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 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int correctLevelPosX_EM(int lx)
199 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
204 int correctLevelPosY_EM(int ly)
207 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
212 int getFieldbufferOffsetX_RND(int dir, int pos)
214 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
215 int dx = (dir & MV_HORIZONTAL ? pos : 0);
216 int dx_var = dx * TILESIZE_VAR / TILESIZE;
219 if (EVEN(SCR_FIELDX))
221 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
222 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
224 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
225 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
227 fx += (dx_var > 0 ? TILEX_VAR : 0);
234 if (full_lev_fieldx <= SCR_FIELDX)
236 if (EVEN(SCR_FIELDX))
237 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
239 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
245 int getFieldbufferOffsetY_RND(int dir, int pos)
247 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
248 int dy = (dir & MV_VERTICAL ? pos : 0);
249 int dy_var = dy * TILESIZE_VAR / TILESIZE;
252 if (EVEN(SCR_FIELDY))
254 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
255 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
257 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
258 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
260 fy += (dy_var > 0 ? TILEY_VAR : 0);
267 if (full_lev_fieldy <= SCR_FIELDY)
269 if (EVEN(SCR_FIELDY))
270 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
272 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
278 static int getLevelFromScreenX_RND(int sx)
280 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
283 int lx = LEVELX((px + dx) / TILESIZE_VAR);
288 static int getLevelFromScreenY_RND(int sy)
290 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
293 int ly = LEVELY((py + dy) / TILESIZE_VAR);
298 static int getLevelFromScreenX_EM(int sx)
300 int level_xsize = level.native_em_level->cav->width;
301 int full_xsize = level_xsize * TILESIZE_VAR;
303 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
305 int fx = getFieldbufferOffsetX_EM();
308 int lx = LEVELX((px + dx) / TILESIZE_VAR);
310 lx = correctLevelPosX_EM(lx);
315 static int getLevelFromScreenY_EM(int sy)
317 int level_ysize = level.native_em_level->cav->height;
318 int full_ysize = level_ysize * TILESIZE_VAR;
320 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
322 int fy = getFieldbufferOffsetY_EM();
325 int ly = LEVELY((py + dy) / TILESIZE_VAR);
327 ly = correctLevelPosY_EM(ly);
332 static int getLevelFromScreenX_SP(int sx)
334 int menBorder = setup.sp_show_border_elements;
335 int level_xsize = level.native_sp_level->width;
336 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
338 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
340 int fx = getFieldbufferOffsetX_SP();
343 int lx = LEVELX((px + dx) / TILESIZE_VAR);
348 static int getLevelFromScreenY_SP(int sy)
350 int menBorder = setup.sp_show_border_elements;
351 int level_ysize = level.native_sp_level->height;
352 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
354 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
356 int fy = getFieldbufferOffsetY_SP();
359 int ly = LEVELY((py + dy) / TILESIZE_VAR);
364 static int getLevelFromScreenX_MM(int sx)
366 int level_xsize = level.native_mm_level->fieldx;
367 int full_xsize = level_xsize * TILESIZE_VAR;
369 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
372 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
377 static int getLevelFromScreenY_MM(int sy)
379 int level_ysize = level.native_mm_level->fieldy;
380 int full_ysize = level_ysize * TILESIZE_VAR;
382 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
385 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
390 int getLevelFromScreenX(int x)
392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
393 return getLevelFromScreenX_EM(x);
394 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
395 return getLevelFromScreenX_SP(x);
396 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
397 return getLevelFromScreenX_MM(x);
399 return getLevelFromScreenX_RND(x);
402 int getLevelFromScreenY(int y)
404 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
405 return getLevelFromScreenY_EM(y);
406 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
407 return getLevelFromScreenY_SP(y);
408 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
409 return getLevelFromScreenY_MM(y);
411 return getLevelFromScreenY_RND(y);
414 void DumpTile(int x, int y)
420 printf_line("-", 79);
421 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
422 printf_line("-", 79);
424 if (!IN_LEV_FIELD(x, y))
426 printf("(not in level field)\n");
432 token_name = element_info[Feld[x][y]].token_name;
434 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
435 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
436 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
437 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
438 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
439 printf(" MovPos: %d\n", MovPos[x][y]);
440 printf(" MovDir: %d\n", MovDir[x][y]);
441 printf(" MovDelay: %d\n", MovDelay[x][y]);
442 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
443 printf(" CustomValue: %d\n", CustomValue[x][y]);
444 printf(" GfxElement: %d\n", GfxElement[x][y]);
445 printf(" GfxAction: %d\n", GfxAction[x][y]);
446 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
447 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
451 void DumpTileFromScreen(int sx, int sy)
453 int lx = getLevelFromScreenX(sx);
454 int ly = getLevelFromScreenY(sy);
459 void SetDrawtoField(int mode)
461 if (mode == DRAW_TO_FIELDBUFFER)
467 BX2 = SCR_FIELDX + 1;
468 BY2 = SCR_FIELDY + 1;
470 drawto_field = fieldbuffer;
472 else // DRAW_TO_BACKBUFFER
478 BX2 = SCR_FIELDX - 1;
479 BY2 = SCR_FIELDY - 1;
481 drawto_field = backbuffer;
485 static void RedrawPlayfield_RND(void)
487 if (game.envelope_active)
490 DrawLevel(REDRAW_ALL);
494 void RedrawPlayfield(void)
496 if (game_status != GAME_MODE_PLAYING)
499 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
500 RedrawPlayfield_EM(TRUE);
501 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
502 RedrawPlayfield_SP(TRUE);
503 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
504 RedrawPlayfield_MM();
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
506 RedrawPlayfield_RND();
508 BlitScreenToBitmap(backbuffer);
510 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
514 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
517 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
518 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
520 if (x == -1 && y == -1)
523 if (draw_target == DRAW_TO_SCREEN)
524 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
526 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
529 static void DrawMaskedBorderExt_FIELD(int draw_target)
531 if (global.border_status >= GAME_MODE_MAIN &&
532 global.border_status <= GAME_MODE_PLAYING &&
533 border.draw_masked[global.border_status])
534 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
538 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
540 // when drawing to backbuffer, never draw border over open doors
541 if (draw_target == DRAW_TO_BACKBUFFER &&
542 (GetDoorState() & DOOR_OPEN_1))
545 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
546 (global.border_status != GAME_MODE_EDITOR ||
547 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
548 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
551 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
553 // when drawing to backbuffer, never draw border over open doors
554 if (draw_target == DRAW_TO_BACKBUFFER &&
555 (GetDoorState() & DOOR_OPEN_2))
558 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
559 global.border_status != GAME_MODE_EDITOR)
560 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
563 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
565 // currently not available
568 static void DrawMaskedBorderExt_ALL(int draw_target)
570 DrawMaskedBorderExt_FIELD(draw_target);
571 DrawMaskedBorderExt_DOOR_1(draw_target);
572 DrawMaskedBorderExt_DOOR_2(draw_target);
573 DrawMaskedBorderExt_DOOR_3(draw_target);
576 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
578 // never draw masked screen borders on borderless screens
579 if (global.border_status == GAME_MODE_LOADING ||
580 global.border_status == GAME_MODE_TITLE)
583 if (redraw_mask & REDRAW_ALL)
584 DrawMaskedBorderExt_ALL(draw_target);
587 if (redraw_mask & REDRAW_FIELD)
588 DrawMaskedBorderExt_FIELD(draw_target);
589 if (redraw_mask & REDRAW_DOOR_1)
590 DrawMaskedBorderExt_DOOR_1(draw_target);
591 if (redraw_mask & REDRAW_DOOR_2)
592 DrawMaskedBorderExt_DOOR_2(draw_target);
593 if (redraw_mask & REDRAW_DOOR_3)
594 DrawMaskedBorderExt_DOOR_3(draw_target);
598 void DrawMaskedBorder_FIELD(void)
600 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorder(int redraw_mask)
605 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
608 void DrawMaskedBorderToTarget(int draw_target)
610 if (draw_target == DRAW_TO_BACKBUFFER ||
611 draw_target == DRAW_TO_SCREEN)
613 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
617 int last_border_status = global.border_status;
619 if (draw_target == DRAW_TO_FADE_SOURCE)
621 global.border_status = gfx.fade_border_source_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
624 else if (draw_target == DRAW_TO_FADE_TARGET)
626 global.border_status = gfx.fade_border_target_status;
627 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
630 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
632 global.border_status = last_border_status;
633 gfx.masked_border_bitmap_ptr = backbuffer;
637 void DrawTileCursor(int draw_target)
643 int graphic = IMG_GLOBAL_TILE_CURSOR;
645 int tilesize = TILESIZE_VAR;
646 int width = tilesize;
647 int height = tilesize;
649 if (game_status != GAME_MODE_PLAYING)
652 if (!tile_cursor.enabled ||
656 if (tile_cursor.moving)
658 int step = TILESIZE_VAR / 4;
659 int dx = tile_cursor.target_x - tile_cursor.x;
660 int dy = tile_cursor.target_y - tile_cursor.y;
663 tile_cursor.x = tile_cursor.target_x;
665 tile_cursor.x += SIGN(dx) * step;
668 tile_cursor.y = tile_cursor.target_y;
670 tile_cursor.y += SIGN(dy) * step;
672 if (tile_cursor.x == tile_cursor.target_x &&
673 tile_cursor.y == tile_cursor.target_y)
674 tile_cursor.moving = FALSE;
677 dst_x = tile_cursor.x;
678 dst_y = tile_cursor.y;
680 frame = getGraphicAnimationFrame(graphic, -1);
682 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
685 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
686 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
688 if (draw_target == DRAW_TO_SCREEN)
689 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
691 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
695 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
702 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
703 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
705 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
708 void BlitScreenToBitmap(Bitmap *target_bitmap)
710 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
711 BlitScreenToBitmap_EM(target_bitmap);
712 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
713 BlitScreenToBitmap_SP(target_bitmap);
714 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
715 BlitScreenToBitmap_MM(target_bitmap);
716 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
717 BlitScreenToBitmap_RND(target_bitmap);
719 redraw_mask |= REDRAW_FIELD;
722 static void DrawFramesPerSecond(void)
725 int font_nr = FONT_TEXT_2;
726 int font_width = getFontWidth(font_nr);
727 int draw_deactivation_mask = GetDrawDeactivationMask();
728 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
730 // draw FPS with leading space (needed if field buffer deactivated)
731 sprintf(text, " %04.1f fps", global.frames_per_second);
733 // override draw deactivation mask (required for invisible warp mode)
734 SetDrawDeactivationMask(REDRAW_NONE);
736 // draw opaque FPS if field buffer deactivated, else draw masked FPS
737 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
738 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
740 // set draw deactivation mask to previous value
741 SetDrawDeactivationMask(draw_deactivation_mask);
743 // force full-screen redraw in this frame
744 redraw_mask = REDRAW_ALL;
748 static void PrintFrameTimeDebugging(void)
750 static unsigned int last_counter = 0;
751 unsigned int counter = Counter();
752 int diff_1 = counter - last_counter;
753 int diff_2 = diff_1 - GAME_FRAME_DELAY;
755 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
756 char diff_bar[2 * diff_2_max + 5];
760 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
762 for (i = 0; i < diff_2_max; i++)
763 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
764 i >= diff_2_max - diff_2_cut ? '-' : ' ');
766 diff_bar[pos++] = '|';
768 for (i = 0; i < diff_2_max; i++)
769 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
771 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
773 diff_bar[pos++] = '\0';
775 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
778 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
781 last_counter = counter;
785 static int unifiedRedrawMask(int mask)
787 if (mask & REDRAW_ALL)
790 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
796 static boolean equalRedrawMasks(int mask_1, int mask_2)
798 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
801 void BackToFront(void)
803 static int last_redraw_mask = REDRAW_NONE;
805 // force screen redraw in every frame to continue drawing global animations
806 // (but always use the last redraw mask to prevent unwanted side effects)
807 if (redraw_mask == REDRAW_NONE)
808 redraw_mask = last_redraw_mask;
810 last_redraw_mask = redraw_mask;
813 // masked border now drawn immediately when blitting backbuffer to window
815 // draw masked border to all viewports, if defined
816 DrawMaskedBorder(redraw_mask);
819 // draw frames per second (only if debug mode is enabled)
820 if (redraw_mask & REDRAW_FPS)
821 DrawFramesPerSecond();
823 // remove playfield redraw before potentially merging with doors redraw
824 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
825 redraw_mask &= ~REDRAW_FIELD;
827 // redraw complete window if both playfield and (some) doors need redraw
828 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
829 redraw_mask = REDRAW_ALL;
831 /* although redrawing the whole window would be fine for normal gameplay,
832 being able to only redraw the playfield is required for deactivating
833 certain drawing areas (mainly playfield) to work, which is needed for
834 warp-forward to be fast enough (by skipping redraw of most frames) */
836 if (redraw_mask & REDRAW_ALL)
838 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
840 else if (redraw_mask & REDRAW_FIELD)
842 BlitBitmap(backbuffer, window,
843 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
845 else if (redraw_mask & REDRAW_DOORS)
847 // merge door areas to prevent calling screen redraw more than once
853 if (redraw_mask & REDRAW_DOOR_1)
857 x2 = MAX(x2, DX + DXSIZE);
858 y2 = MAX(y2, DY + DYSIZE);
861 if (redraw_mask & REDRAW_DOOR_2)
865 x2 = MAX(x2, VX + VXSIZE);
866 y2 = MAX(y2, VY + VYSIZE);
869 if (redraw_mask & REDRAW_DOOR_3)
873 x2 = MAX(x2, EX + EXSIZE);
874 y2 = MAX(y2, EY + EYSIZE);
877 // make sure that at least one pixel is blitted, and inside the screen
878 // (else nothing is blitted, causing the animations not to be updated)
879 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
880 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
881 x2 = MIN(MAX(1, x2), WIN_XSIZE);
882 y2 = MIN(MAX(1, y2), WIN_YSIZE);
884 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
887 redraw_mask = REDRAW_NONE;
890 PrintFrameTimeDebugging();
894 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
896 unsigned int frame_delay_value_old = GetVideoFrameDelay();
898 SetVideoFrameDelay(frame_delay_value);
902 SetVideoFrameDelay(frame_delay_value_old);
905 static int fade_type_skip = FADE_TYPE_NONE;
907 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
909 void (*draw_border_function)(void) = NULL;
910 int x, y, width, height;
911 int fade_delay, post_delay;
913 if (fade_type == FADE_TYPE_FADE_OUT)
915 if (fade_type_skip != FADE_TYPE_NONE)
917 // skip all fade operations until specified fade operation
918 if (fade_type & fade_type_skip)
919 fade_type_skip = FADE_TYPE_NONE;
924 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
928 redraw_mask |= fade_mask;
930 if (fade_type == FADE_TYPE_SKIP)
932 fade_type_skip = fade_mode;
937 fade_delay = fading.fade_delay;
938 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
940 if (fade_type_skip != FADE_TYPE_NONE)
942 // skip all fade operations until specified fade operation
943 if (fade_type & fade_type_skip)
944 fade_type_skip = FADE_TYPE_NONE;
949 if (global.autoplay_leveldir)
954 if (fade_mask == REDRAW_FIELD)
959 height = FADE_SYSIZE;
961 if (border.draw_masked_when_fading)
962 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
964 DrawMaskedBorder_FIELD(); // draw once
974 // when switching screens without fading, set fade delay to zero
975 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
978 // do not display black frame when fading out without fade delay
979 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
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 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
998 // set screen mode for animations back to fading
999 global.anim_status = GAME_MODE_PSEUDO_FADING;
1002 static void SetScreenStates_AfterFadingIn(void)
1004 // store new source screen (to use correct masked border for fading)
1005 gfx.fade_border_source_status = global.border_status;
1007 global.anim_status = global.anim_status_next;
1010 static void SetScreenStates_BeforeFadingOut(void)
1012 // store new target screen (to use correct masked border for fading)
1013 gfx.fade_border_target_status = game_status;
1015 // set screen mode for animations to fading
1016 global.anim_status = GAME_MODE_PSEUDO_FADING;
1018 // store backbuffer with all animations that will be stopped for fading out
1019 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1022 static void SetScreenStates_AfterFadingOut(void)
1024 global.border_status = game_status;
1027 void FadeIn(int fade_mask)
1029 SetScreenStates_BeforeFadingIn();
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1038 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1042 FADE_SXSIZE = FULL_SXSIZE;
1043 FADE_SYSIZE = FULL_SYSIZE;
1045 // activate virtual buttons depending on upcoming game status
1046 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1047 game_status == GAME_MODE_PLAYING && !tape.playing)
1048 SetOverlayActive(TRUE);
1050 SetScreenStates_AfterFadingIn();
1052 // force update of global animation status in case of rapid screen changes
1053 redraw_mask = REDRAW_ALL;
1057 void FadeOut(int fade_mask)
1059 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1060 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1061 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1064 SetScreenStates_BeforeFadingOut();
1066 SetTileCursorActive(FALSE);
1067 SetOverlayActive(FALSE);
1070 DrawMaskedBorder(REDRAW_ALL);
1073 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1074 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1076 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1078 SetScreenStates_AfterFadingOut();
1081 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1083 static struct TitleFadingInfo fading_leave_stored;
1086 fading_leave_stored = fading_leave;
1088 fading = fading_leave_stored;
1091 void FadeSetEnterMenu(void)
1093 fading = menu.enter_menu;
1095 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1098 void FadeSetLeaveMenu(void)
1100 fading = menu.leave_menu;
1102 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1105 void FadeSetEnterScreen(void)
1107 fading = menu.enter_screen[game_status];
1109 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1112 void FadeSetNextScreen(void)
1114 fading = menu.next_screen[game_status];
1116 // (do not overwrite fade mode set by FadeSetEnterScreen)
1117 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1120 void FadeSetLeaveScreen(void)
1122 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1125 void FadeSetFromType(int type)
1127 if (type & TYPE_ENTER_SCREEN)
1128 FadeSetEnterScreen();
1129 else if (type & TYPE_ENTER)
1131 else if (type & TYPE_LEAVE)
1135 void FadeSetDisabled(void)
1137 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1139 fading = fading_none;
1142 void FadeSkipNextFadeIn(void)
1144 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1147 void FadeSkipNextFadeOut(void)
1149 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1152 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1154 if (graphic == IMG_UNDEFINED)
1157 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1159 return (graphic_info[graphic].bitmap != NULL || redefined ?
1160 graphic_info[graphic].bitmap :
1161 graphic_info[default_graphic].bitmap);
1164 static Bitmap *getBackgroundBitmap(int graphic)
1166 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1169 static Bitmap *getGlobalBorderBitmap(int graphic)
1171 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1174 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1177 (status == GAME_MODE_MAIN ||
1178 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1179 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1180 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1181 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1184 return getGlobalBorderBitmap(graphic);
1187 void SetWindowBackgroundImageIfDefined(int graphic)
1189 if (graphic_info[graphic].bitmap)
1190 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1193 void SetMainBackgroundImageIfDefined(int graphic)
1195 if (graphic_info[graphic].bitmap)
1196 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1199 void SetDoorBackgroundImageIfDefined(int graphic)
1201 if (graphic_info[graphic].bitmap)
1202 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1205 void SetWindowBackgroundImage(int graphic)
1207 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1210 void SetMainBackgroundImage(int graphic)
1212 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1215 void SetDoorBackgroundImage(int graphic)
1217 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1220 void SetPanelBackground(void)
1222 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1224 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1225 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1227 SetDoorBackgroundBitmap(bitmap_db_panel);
1230 void DrawBackground(int x, int y, int width, int height)
1232 // "drawto" might still point to playfield buffer here (hall of fame)
1233 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1235 if (IN_GFX_FIELD_FULL(x, y))
1236 redraw_mask |= REDRAW_FIELD;
1237 else if (IN_GFX_DOOR_1(x, y))
1238 redraw_mask |= REDRAW_DOOR_1;
1239 else if (IN_GFX_DOOR_2(x, y))
1240 redraw_mask |= REDRAW_DOOR_2;
1241 else if (IN_GFX_DOOR_3(x, y))
1242 redraw_mask |= REDRAW_DOOR_3;
1245 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1247 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1249 if (font->bitmap == NULL)
1252 DrawBackground(x, y, width, height);
1255 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1257 struct GraphicInfo *g = &graphic_info[graphic];
1259 if (g->bitmap == NULL)
1262 DrawBackground(x, y, width, height);
1265 static int game_status_last = -1;
1266 static Bitmap *global_border_bitmap_last = NULL;
1267 static Bitmap *global_border_bitmap = NULL;
1268 static int real_sx_last = -1, real_sy_last = -1;
1269 static int full_sxsize_last = -1, full_sysize_last = -1;
1270 static int dx_last = -1, dy_last = -1;
1271 static int dxsize_last = -1, dysize_last = -1;
1272 static int vx_last = -1, vy_last = -1;
1273 static int vxsize_last = -1, vysize_last = -1;
1274 static int ex_last = -1, ey_last = -1;
1275 static int exsize_last = -1, eysize_last = -1;
1277 boolean CheckIfGlobalBorderHasChanged(void)
1279 // if game status has not changed, global border has not changed either
1280 if (game_status == game_status_last)
1283 // determine and store new global border bitmap for current game status
1284 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1286 return (global_border_bitmap_last != global_border_bitmap);
1289 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1294 // if game status has not changed, nothing has to be redrawn
1295 if (game_status == game_status_last)
1298 // redraw if last screen was title screen
1299 if (game_status_last == GAME_MODE_TITLE)
1302 // redraw if global screen border has changed
1303 if (CheckIfGlobalBorderHasChanged())
1306 // redraw if position or size of playfield area has changed
1307 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1308 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1311 // redraw if position or size of door area has changed
1312 if (dx_last != DX || dy_last != DY ||
1313 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1316 // redraw if position or size of tape area has changed
1317 if (vx_last != VX || vy_last != VY ||
1318 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1321 // redraw if position or size of editor area has changed
1322 if (ex_last != EX || ey_last != EY ||
1323 exsize_last != EXSIZE || eysize_last != EYSIZE)
1330 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1333 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1335 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1338 void RedrawGlobalBorder(void)
1340 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1342 RedrawGlobalBorderFromBitmap(bitmap);
1344 redraw_mask = REDRAW_ALL;
1347 static void RedrawGlobalBorderIfNeeded(void)
1349 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1350 if (game_status == game_status_last)
1354 // copy current draw buffer to later copy back areas that have not changed
1355 if (game_status_last != GAME_MODE_TITLE)
1356 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1358 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1359 if (CheckIfGlobalBorderRedrawIsNeeded())
1362 // redraw global screen border (or clear, if defined to be empty)
1363 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1365 if (game_status == GAME_MODE_EDITOR)
1366 DrawSpecialEditorDoor();
1368 // copy previous playfield and door areas, if they are defined on both
1369 // previous and current screen and if they still have the same size
1371 if (real_sx_last != -1 && real_sy_last != -1 &&
1372 REAL_SX != -1 && REAL_SY != -1 &&
1373 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1374 BlitBitmap(bitmap_db_store_1, backbuffer,
1375 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1378 if (dx_last != -1 && dy_last != -1 &&
1379 DX != -1 && DY != -1 &&
1380 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1381 BlitBitmap(bitmap_db_store_1, backbuffer,
1382 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1384 if (game_status != GAME_MODE_EDITOR)
1386 if (vx_last != -1 && vy_last != -1 &&
1387 VX != -1 && VY != -1 &&
1388 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1389 BlitBitmap(bitmap_db_store_1, backbuffer,
1390 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1394 if (ex_last != -1 && ey_last != -1 &&
1395 EX != -1 && EY != -1 &&
1396 exsize_last == EXSIZE && eysize_last == EYSIZE)
1397 BlitBitmap(bitmap_db_store_1, backbuffer,
1398 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1401 redraw_mask = REDRAW_ALL;
1404 game_status_last = game_status;
1406 global_border_bitmap_last = global_border_bitmap;
1408 real_sx_last = REAL_SX;
1409 real_sy_last = REAL_SY;
1410 full_sxsize_last = FULL_SXSIZE;
1411 full_sysize_last = FULL_SYSIZE;
1414 dxsize_last = DXSIZE;
1415 dysize_last = DYSIZE;
1418 vxsize_last = VXSIZE;
1419 vysize_last = VYSIZE;
1422 exsize_last = EXSIZE;
1423 eysize_last = EYSIZE;
1426 void ClearField(void)
1428 RedrawGlobalBorderIfNeeded();
1430 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1431 // (when entering hall of fame after playing)
1432 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1434 // !!! maybe this should be done before clearing the background !!!
1435 if (game_status == GAME_MODE_PLAYING)
1437 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1438 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1442 SetDrawtoField(DRAW_TO_BACKBUFFER);
1446 void MarkTileDirty(int x, int y)
1448 redraw_mask |= REDRAW_FIELD;
1451 void SetBorderElement(void)
1455 BorderElement = EL_EMPTY;
1457 // only the R'n'D game engine may use an additional steelwall border
1458 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1461 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1463 for (x = 0; x < lev_fieldx; x++)
1465 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1466 BorderElement = EL_STEELWALL;
1468 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1474 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1475 int max_array_fieldx, int max_array_fieldy,
1476 short field[max_array_fieldx][max_array_fieldy],
1477 int max_fieldx, int max_fieldy)
1481 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1482 static int safety = 0;
1484 // check if starting field still has the desired content
1485 if (field[from_x][from_y] == fill_element)
1490 if (safety > max_fieldx * max_fieldy)
1491 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1493 old_element = field[from_x][from_y];
1494 field[from_x][from_y] = fill_element;
1496 for (i = 0; i < 4; i++)
1498 x = from_x + check[i][0];
1499 y = from_y + check[i][1];
1501 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1502 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1503 field, max_fieldx, max_fieldy);
1509 void FloodFillLevel(int from_x, int from_y, int fill_element,
1510 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1511 int max_fieldx, int max_fieldy)
1513 FloodFillLevelExt(from_x, from_y, fill_element,
1514 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1515 max_fieldx, max_fieldy);
1518 void SetRandomAnimationValue(int x, int y)
1520 gfx.anim_random_frame = GfxRandom[x][y];
1523 int getGraphicAnimationFrame(int graphic, int sync_frame)
1525 // animation synchronized with global frame counter, not move position
1526 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1527 sync_frame = FrameCounter;
1529 return getAnimationFrame(graphic_info[graphic].anim_frames,
1530 graphic_info[graphic].anim_delay,
1531 graphic_info[graphic].anim_mode,
1532 graphic_info[graphic].anim_start_frame,
1536 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1538 struct GraphicInfo *g = &graphic_info[graphic];
1539 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1541 if (tilesize == gfx.standard_tile_size)
1542 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1543 else if (tilesize == game.tile_size)
1544 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1546 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1549 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1550 boolean get_backside)
1552 struct GraphicInfo *g = &graphic_info[graphic];
1553 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1554 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1556 if (g->offset_y == 0) // frames are ordered horizontally
1558 int max_width = g->anim_frames_per_line * g->width;
1559 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1561 *x = pos % max_width;
1562 *y = src_y % g->height + pos / max_width * g->height;
1564 else if (g->offset_x == 0) // frames are ordered vertically
1566 int max_height = g->anim_frames_per_line * g->height;
1567 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1569 *x = src_x % g->width + pos / max_height * g->width;
1570 *y = pos % max_height;
1572 else // frames are ordered diagonally
1574 *x = src_x + frame * g->offset_x;
1575 *y = src_y + frame * g->offset_y;
1579 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1580 Bitmap **bitmap, int *x, int *y,
1581 boolean get_backside)
1583 struct GraphicInfo *g = &graphic_info[graphic];
1585 // if no graphics defined at all, use fallback graphics
1586 if (g->bitmaps == NULL)
1587 *g = graphic_info[IMG_CHAR_EXCLAM];
1589 // if no in-game graphics defined, always use standard graphic size
1590 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1591 tilesize = TILESIZE;
1593 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1594 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1596 *x = *x * tilesize / g->tile_size;
1597 *y = *y * tilesize / g->tile_size;
1600 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1601 Bitmap **bitmap, int *x, int *y)
1603 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1606 void getFixedGraphicSource(int graphic, int frame,
1607 Bitmap **bitmap, int *x, int *y)
1609 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1612 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1614 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1617 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1618 int *x, int *y, boolean get_backside)
1620 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1624 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1626 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1629 void DrawGraphic(int x, int y, int graphic, int frame)
1632 if (!IN_SCR_FIELD(x, y))
1634 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1635 printf("DrawGraphic(): This should never happen!\n");
1640 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1643 MarkTileDirty(x, y);
1646 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1649 if (!IN_SCR_FIELD(x, y))
1651 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1652 printf("DrawGraphic(): This should never happen!\n");
1657 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1659 MarkTileDirty(x, y);
1662 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1668 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1670 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1673 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1679 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1680 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1683 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1686 if (!IN_SCR_FIELD(x, y))
1688 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1689 printf("DrawGraphicThruMask(): This should never happen!\n");
1694 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1697 MarkTileDirty(x, y);
1700 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1703 if (!IN_SCR_FIELD(x, y))
1705 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1706 printf("DrawGraphicThruMask(): This should never happen!\n");
1711 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1713 MarkTileDirty(x, y);
1716 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1722 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1724 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1728 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1729 int graphic, int frame)
1734 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1736 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1740 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1742 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1744 MarkTileDirty(x / tilesize, y / tilesize);
1747 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1750 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1751 graphic, frame, tilesize);
1752 MarkTileDirty(x / tilesize, y / tilesize);
1755 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1761 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1762 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1765 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1766 int frame, int tilesize)
1771 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1772 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1775 void DrawMiniGraphic(int x, int y, int graphic)
1777 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1778 MarkTileDirty(x / 2, y / 2);
1781 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1786 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1787 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1790 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1791 int graphic, int frame,
1792 int cut_mode, int mask_mode)
1797 int width = TILEX, height = TILEY;
1800 if (dx || dy) // shifted graphic
1802 if (x < BX1) // object enters playfield from the left
1809 else if (x > BX2) // object enters playfield from the right
1815 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1821 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1823 else if (dx) // general horizontal movement
1824 MarkTileDirty(x + SIGN(dx), y);
1826 if (y < BY1) // object enters playfield from the top
1828 if (cut_mode == CUT_BELOW) // object completely above top border
1836 else if (y > BY2) // object enters playfield from the bottom
1842 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1848 else if (dy > 0 && cut_mode == CUT_ABOVE)
1850 if (y == BY2) // object completely above bottom border
1856 MarkTileDirty(x, y + 1);
1857 } // object leaves playfield to the bottom
1858 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1860 else if (dy) // general vertical movement
1861 MarkTileDirty(x, y + SIGN(dy));
1865 if (!IN_SCR_FIELD(x, y))
1867 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1868 printf("DrawGraphicShifted(): This should never happen!\n");
1873 width = width * TILESIZE_VAR / TILESIZE;
1874 height = height * TILESIZE_VAR / TILESIZE;
1875 cx = cx * TILESIZE_VAR / TILESIZE;
1876 cy = cy * TILESIZE_VAR / TILESIZE;
1877 dx = dx * TILESIZE_VAR / TILESIZE;
1878 dy = dy * TILESIZE_VAR / TILESIZE;
1880 if (width > 0 && height > 0)
1882 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1887 dst_x = FX + x * TILEX_VAR + dx;
1888 dst_y = FY + y * TILEY_VAR + dy;
1890 if (mask_mode == USE_MASKING)
1891 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1894 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1897 MarkTileDirty(x, y);
1901 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1902 int graphic, int frame,
1903 int cut_mode, int mask_mode)
1908 int width = TILEX_VAR, height = TILEY_VAR;
1911 int x2 = x + SIGN(dx);
1912 int y2 = y + SIGN(dy);
1914 // movement with two-tile animations must be sync'ed with movement position,
1915 // not with current GfxFrame (which can be higher when using slow movement)
1916 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1917 int anim_frames = graphic_info[graphic].anim_frames;
1919 // (we also need anim_delay here for movement animations with less frames)
1920 int anim_delay = graphic_info[graphic].anim_delay;
1921 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1923 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1924 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1926 // re-calculate animation frame for two-tile movement animation
1927 frame = getGraphicAnimationFrame(graphic, sync_frame);
1929 // check if movement start graphic inside screen area and should be drawn
1930 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1932 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1934 dst_x = FX + x1 * TILEX_VAR;
1935 dst_y = FY + y1 * TILEY_VAR;
1937 if (mask_mode == USE_MASKING)
1938 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1941 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1944 MarkTileDirty(x1, y1);
1947 // check if movement end graphic inside screen area and should be drawn
1948 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1950 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1952 dst_x = FX + x2 * TILEX_VAR;
1953 dst_y = FY + y2 * TILEY_VAR;
1955 if (mask_mode == USE_MASKING)
1956 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1959 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1962 MarkTileDirty(x2, y2);
1966 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1967 int graphic, int frame,
1968 int cut_mode, int mask_mode)
1972 DrawGraphic(x, y, graphic, frame);
1977 if (graphic_info[graphic].double_movement) // EM style movement images
1978 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1980 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1983 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1984 int graphic, int frame, int cut_mode)
1986 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1989 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1990 int cut_mode, int mask_mode)
1992 int lx = LEVELX(x), ly = LEVELY(y);
1996 if (IN_LEV_FIELD(lx, ly))
1998 SetRandomAnimationValue(lx, ly);
2000 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2001 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2003 // do not use double (EM style) movement graphic when not moving
2004 if (graphic_info[graphic].double_movement && !dx && !dy)
2006 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2007 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2010 else // border element
2012 graphic = el2img(element);
2013 frame = getGraphicAnimationFrame(graphic, -1);
2016 if (element == EL_EXPANDABLE_WALL)
2018 boolean left_stopped = FALSE, right_stopped = FALSE;
2020 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2021 left_stopped = TRUE;
2022 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2023 right_stopped = TRUE;
2025 if (left_stopped && right_stopped)
2027 else if (left_stopped)
2029 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2030 frame = graphic_info[graphic].anim_frames - 1;
2032 else if (right_stopped)
2034 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2035 frame = graphic_info[graphic].anim_frames - 1;
2040 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2041 else if (mask_mode == USE_MASKING)
2042 DrawGraphicThruMask(x, y, graphic, frame);
2044 DrawGraphic(x, y, graphic, frame);
2047 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2048 int cut_mode, int mask_mode)
2050 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2051 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2052 cut_mode, mask_mode);
2055 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2058 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2061 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2064 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2067 void DrawLevelElementThruMask(int x, int y, int element)
2069 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2072 void DrawLevelFieldThruMask(int x, int y)
2074 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2077 // !!! implementation of quicksand is totally broken !!!
2078 #define IS_CRUMBLED_TILE(x, y, e) \
2079 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2080 !IS_MOVING(x, y) || \
2081 (e) == EL_QUICKSAND_EMPTYING || \
2082 (e) == EL_QUICKSAND_FAST_EMPTYING))
2084 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2089 int width, height, cx, cy;
2090 int sx = SCREENX(x), sy = SCREENY(y);
2091 int crumbled_border_size = graphic_info[graphic].border_size;
2092 int crumbled_tile_size = graphic_info[graphic].tile_size;
2093 int crumbled_border_size_var =
2094 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2097 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2099 for (i = 1; i < 4; i++)
2101 int dxx = (i & 1 ? dx : 0);
2102 int dyy = (i & 2 ? dy : 0);
2105 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2108 // check if neighbour field is of same crumble type
2109 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2110 graphic_info[graphic].class ==
2111 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2113 // return if check prevents inner corner
2114 if (same == (dxx == dx && dyy == dy))
2118 // if we reach this point, we have an inner corner
2120 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2122 width = crumbled_border_size_var;
2123 height = crumbled_border_size_var;
2124 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2125 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2127 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2128 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2131 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2136 int width, height, bx, by, cx, cy;
2137 int sx = SCREENX(x), sy = SCREENY(y);
2138 int crumbled_border_size = graphic_info[graphic].border_size;
2139 int crumbled_tile_size = graphic_info[graphic].tile_size;
2140 int crumbled_border_size_var =
2141 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2142 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2145 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2147 // draw simple, sloppy, non-corner-accurate crumbled border
2149 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2150 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2151 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2152 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2154 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2155 FX + sx * TILEX_VAR + cx,
2156 FY + sy * TILEY_VAR + cy);
2158 // (remaining middle border part must be at least as big as corner part)
2159 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2160 crumbled_border_size_var >= TILESIZE_VAR / 3)
2163 // correct corners of crumbled border, if needed
2165 for (i = -1; i <= 1; i += 2)
2167 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2168 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2169 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2172 // check if neighbour field is of same crumble type
2173 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2174 graphic_info[graphic].class ==
2175 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2177 // no crumbled corner, but continued crumbled border
2179 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2180 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2181 int b1 = (i == 1 ? crumbled_border_size_var :
2182 TILESIZE_VAR - 2 * crumbled_border_size_var);
2184 width = crumbled_border_size_var;
2185 height = crumbled_border_size_var;
2187 if (dir == 1 || dir == 2)
2202 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2204 FX + sx * TILEX_VAR + cx,
2205 FY + sy * TILEY_VAR + cy);
2210 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2212 int sx = SCREENX(x), sy = SCREENY(y);
2215 static int xy[4][2] =
2223 if (!IN_LEV_FIELD(x, y))
2226 element = TILE_GFX_ELEMENT(x, y);
2228 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2230 if (!IN_SCR_FIELD(sx, sy))
2233 // crumble field borders towards direct neighbour fields
2234 for (i = 0; i < 4; i++)
2236 int xx = x + xy[i][0];
2237 int yy = y + xy[i][1];
2239 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2242 // check if neighbour field is of same crumble type
2243 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2244 graphic_info[graphic].class ==
2245 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2248 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2251 // crumble inner field corners towards corner neighbour fields
2252 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2253 graphic_info[graphic].anim_frames == 2)
2255 for (i = 0; i < 4; i++)
2257 int dx = (i & 1 ? +1 : -1);
2258 int dy = (i & 2 ? +1 : -1);
2260 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2264 MarkTileDirty(sx, sy);
2266 else // center field is not crumbled -- crumble neighbour fields
2268 // crumble field borders of direct neighbour fields
2269 for (i = 0; i < 4; i++)
2271 int xx = x + xy[i][0];
2272 int yy = y + xy[i][1];
2273 int sxx = sx + xy[i][0];
2274 int syy = sy + xy[i][1];
2276 if (!IN_LEV_FIELD(xx, yy) ||
2277 !IN_SCR_FIELD(sxx, syy))
2280 // do not crumble fields that are being digged or snapped
2281 if (Feld[xx][yy] == EL_EMPTY ||
2282 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2285 element = TILE_GFX_ELEMENT(xx, yy);
2287 if (!IS_CRUMBLED_TILE(xx, yy, element))
2290 graphic = el_act2crm(element, ACTION_DEFAULT);
2292 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2294 MarkTileDirty(sxx, syy);
2297 // crumble inner field corners of corner neighbour fields
2298 for (i = 0; i < 4; i++)
2300 int dx = (i & 1 ? +1 : -1);
2301 int dy = (i & 2 ? +1 : -1);
2307 if (!IN_LEV_FIELD(xx, yy) ||
2308 !IN_SCR_FIELD(sxx, syy))
2311 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2314 element = TILE_GFX_ELEMENT(xx, yy);
2316 if (!IS_CRUMBLED_TILE(xx, yy, element))
2319 graphic = el_act2crm(element, ACTION_DEFAULT);
2321 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2322 graphic_info[graphic].anim_frames == 2)
2323 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2325 MarkTileDirty(sxx, syy);
2330 void DrawLevelFieldCrumbled(int x, int y)
2334 if (!IN_LEV_FIELD(x, y))
2337 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2338 GfxElement[x][y] != EL_UNDEFINED &&
2339 GFX_CRUMBLED(GfxElement[x][y]))
2341 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2346 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2348 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2351 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2354 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2355 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2356 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2357 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2358 int sx = SCREENX(x), sy = SCREENY(y);
2360 DrawGraphic(sx, sy, graphic1, frame1);
2361 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2364 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2366 int sx = SCREENX(x), sy = SCREENY(y);
2367 static int xy[4][2] =
2376 // crumble direct neighbour fields (required for field borders)
2377 for (i = 0; i < 4; i++)
2379 int xx = x + xy[i][0];
2380 int yy = y + xy[i][1];
2381 int sxx = sx + xy[i][0];
2382 int syy = sy + xy[i][1];
2384 if (!IN_LEV_FIELD(xx, yy) ||
2385 !IN_SCR_FIELD(sxx, syy) ||
2386 !GFX_CRUMBLED(Feld[xx][yy]) ||
2390 DrawLevelField(xx, yy);
2393 // crumble corner neighbour fields (required for inner field corners)
2394 for (i = 0; i < 4; i++)
2396 int dx = (i & 1 ? +1 : -1);
2397 int dy = (i & 2 ? +1 : -1);
2403 if (!IN_LEV_FIELD(xx, yy) ||
2404 !IN_SCR_FIELD(sxx, syy) ||
2405 !GFX_CRUMBLED(Feld[xx][yy]) ||
2409 int element = TILE_GFX_ELEMENT(xx, yy);
2410 int graphic = el_act2crm(element, ACTION_DEFAULT);
2412 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2413 graphic_info[graphic].anim_frames == 2)
2414 DrawLevelField(xx, yy);
2418 static int getBorderElement(int x, int y)
2422 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2423 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2424 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2425 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2426 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2427 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2428 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2430 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2431 int steel_position = (x == -1 && y == -1 ? 0 :
2432 x == lev_fieldx && y == -1 ? 1 :
2433 x == -1 && y == lev_fieldy ? 2 :
2434 x == lev_fieldx && y == lev_fieldy ? 3 :
2435 x == -1 || x == lev_fieldx ? 4 :
2436 y == -1 || y == lev_fieldy ? 5 : 6);
2438 return border[steel_position][steel_type];
2441 void DrawScreenElement(int x, int y, int element)
2443 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2444 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2447 void DrawLevelElement(int x, int y, int element)
2449 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2450 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2453 void DrawScreenField(int x, int y)
2455 int lx = LEVELX(x), ly = LEVELY(y);
2456 int element, content;
2458 if (!IN_LEV_FIELD(lx, ly))
2460 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2463 element = getBorderElement(lx, ly);
2465 DrawScreenElement(x, y, element);
2470 element = Feld[lx][ly];
2471 content = Store[lx][ly];
2473 if (IS_MOVING(lx, ly))
2475 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2476 boolean cut_mode = NO_CUTTING;
2478 if (element == EL_QUICKSAND_EMPTYING ||
2479 element == EL_QUICKSAND_FAST_EMPTYING ||
2480 element == EL_MAGIC_WALL_EMPTYING ||
2481 element == EL_BD_MAGIC_WALL_EMPTYING ||
2482 element == EL_DC_MAGIC_WALL_EMPTYING ||
2483 element == EL_AMOEBA_DROPPING)
2484 cut_mode = CUT_ABOVE;
2485 else if (element == EL_QUICKSAND_FILLING ||
2486 element == EL_QUICKSAND_FAST_FILLING ||
2487 element == EL_MAGIC_WALL_FILLING ||
2488 element == EL_BD_MAGIC_WALL_FILLING ||
2489 element == EL_DC_MAGIC_WALL_FILLING)
2490 cut_mode = CUT_BELOW;
2492 if (cut_mode == CUT_ABOVE)
2493 DrawScreenElement(x, y, element);
2495 DrawScreenElement(x, y, EL_EMPTY);
2498 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2499 else if (cut_mode == NO_CUTTING)
2500 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2503 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2505 if (cut_mode == CUT_BELOW &&
2506 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2507 DrawLevelElement(lx, ly + 1, element);
2510 if (content == EL_ACID)
2512 int dir = MovDir[lx][ly];
2513 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2514 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2516 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2518 // prevent target field from being drawn again (but without masking)
2519 // (this would happen if target field is scanned after moving element)
2520 Stop[newlx][newly] = TRUE;
2523 else if (IS_BLOCKED(lx, ly))
2528 boolean cut_mode = NO_CUTTING;
2529 int element_old, content_old;
2531 Blocked2Moving(lx, ly, &oldx, &oldy);
2534 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2535 MovDir[oldx][oldy] == MV_RIGHT);
2537 element_old = Feld[oldx][oldy];
2538 content_old = Store[oldx][oldy];
2540 if (element_old == EL_QUICKSAND_EMPTYING ||
2541 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2542 element_old == EL_MAGIC_WALL_EMPTYING ||
2543 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2544 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2545 element_old == EL_AMOEBA_DROPPING)
2546 cut_mode = CUT_ABOVE;
2548 DrawScreenElement(x, y, EL_EMPTY);
2551 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2553 else if (cut_mode == NO_CUTTING)
2554 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2557 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2560 else if (IS_DRAWABLE(element))
2561 DrawScreenElement(x, y, element);
2563 DrawScreenElement(x, y, EL_EMPTY);
2566 void DrawLevelField(int x, int y)
2568 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2569 DrawScreenField(SCREENX(x), SCREENY(y));
2570 else if (IS_MOVING(x, y))
2574 Moving2Blocked(x, y, &newx, &newy);
2575 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2576 DrawScreenField(SCREENX(newx), SCREENY(newy));
2578 else if (IS_BLOCKED(x, y))
2582 Blocked2Moving(x, y, &oldx, &oldy);
2583 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2584 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2588 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2589 int (*el2img_function)(int), boolean masked,
2590 int element_bits_draw)
2592 int element_base = map_mm_wall_element(element);
2593 int element_bits = (IS_DF_WALL(element) ?
2594 element - EL_DF_WALL_START :
2595 IS_MM_WALL(element) ?
2596 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2597 int graphic = el2img_function(element_base);
2598 int tilesize_draw = tilesize / 2;
2603 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2605 for (i = 0; i < 4; i++)
2607 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2608 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2610 if (!(element_bits_draw & (1 << i)))
2613 if (element_bits & (1 << i))
2616 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2617 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2619 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2620 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2625 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2626 tilesize_draw, tilesize_draw);
2631 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2632 boolean masked, int element_bits_draw)
2634 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2635 element, tilesize, el2edimg, masked, element_bits_draw);
2638 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2639 int (*el2img_function)(int))
2641 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2645 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2648 if (IS_MM_WALL(element))
2650 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2651 element, tilesize, el2edimg, masked, 0x000f);
2655 int graphic = el2edimg(element);
2658 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2660 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2664 void DrawSizedElement(int x, int y, int element, int tilesize)
2666 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2669 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2671 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2674 void DrawMiniElement(int x, int y, int element)
2678 graphic = el2edimg(element);
2679 DrawMiniGraphic(x, y, graphic);
2682 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2685 int x = sx + scroll_x, y = sy + scroll_y;
2687 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2688 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2689 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2690 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2692 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2695 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2697 int x = sx + scroll_x, y = sy + scroll_y;
2699 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2700 DrawMiniElement(sx, sy, EL_EMPTY);
2701 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2702 DrawMiniElement(sx, sy, Feld[x][y]);
2704 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2707 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2708 int x, int y, int xsize, int ysize,
2709 int tile_width, int tile_height)
2713 int dst_x = startx + x * tile_width;
2714 int dst_y = starty + y * tile_height;
2715 int width = graphic_info[graphic].width;
2716 int height = graphic_info[graphic].height;
2717 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2718 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2719 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2720 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2721 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2722 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2723 boolean draw_masked = graphic_info[graphic].draw_masked;
2725 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2727 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2729 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2733 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2734 inner_sx + (x - 1) * tile_width % inner_width);
2735 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2736 inner_sy + (y - 1) * tile_height % inner_height);
2739 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2742 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2746 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2747 int x, int y, int xsize, int ysize,
2750 int font_width = getFontWidth(font_nr);
2751 int font_height = getFontHeight(font_nr);
2753 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2754 font_width, font_height);
2757 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2759 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2760 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2761 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2762 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2763 boolean no_delay = (tape.warp_forward);
2764 unsigned int anim_delay = 0;
2765 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2766 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2767 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2768 int font_width = getFontWidth(font_nr);
2769 int font_height = getFontHeight(font_nr);
2770 int max_xsize = level.envelope[envelope_nr].xsize;
2771 int max_ysize = level.envelope[envelope_nr].ysize;
2772 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2773 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2774 int xend = max_xsize;
2775 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2776 int xstep = (xstart < xend ? 1 : 0);
2777 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2779 int end = MAX(xend - xstart, yend - ystart);
2782 for (i = start; i <= end; i++)
2784 int last_frame = end; // last frame of this "for" loop
2785 int x = xstart + i * xstep;
2786 int y = ystart + i * ystep;
2787 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2788 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2789 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2790 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2793 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2795 BlitScreenToBitmap(backbuffer);
2797 SetDrawtoField(DRAW_TO_BACKBUFFER);
2799 for (yy = 0; yy < ysize; yy++)
2800 for (xx = 0; xx < xsize; xx++)
2801 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2803 DrawTextBuffer(sx + font_width, sy + font_height,
2804 level.envelope[envelope_nr].text, font_nr, max_xsize,
2805 xsize - 2, ysize - 2, 0, mask_mode,
2806 level.envelope[envelope_nr].autowrap,
2807 level.envelope[envelope_nr].centered, FALSE);
2809 redraw_mask |= REDRAW_FIELD;
2812 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2815 ClearAutoRepeatKeyEvents();
2818 void ShowEnvelope(int envelope_nr)
2820 int element = EL_ENVELOPE_1 + envelope_nr;
2821 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2822 int sound_opening = element_info[element].sound[ACTION_OPENING];
2823 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2824 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2825 boolean no_delay = (tape.warp_forward);
2826 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2827 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2828 int anim_mode = graphic_info[graphic].anim_mode;
2829 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2830 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2832 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2834 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2836 if (anim_mode == ANIM_DEFAULT)
2837 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2839 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2842 Delay_WithScreenUpdates(wait_delay_value);
2844 WaitForEventToContinue();
2846 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2848 if (anim_mode != ANIM_NONE)
2849 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2851 if (anim_mode == ANIM_DEFAULT)
2852 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2854 game.envelope_active = FALSE;
2856 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2858 redraw_mask |= REDRAW_FIELD;
2862 static void setRequestBasePosition(int *x, int *y)
2864 int sx_base, sy_base;
2866 if (request.x != -1)
2867 sx_base = request.x;
2868 else if (request.align == ALIGN_LEFT)
2870 else if (request.align == ALIGN_RIGHT)
2871 sx_base = SX + SXSIZE;
2873 sx_base = SX + SXSIZE / 2;
2875 if (request.y != -1)
2876 sy_base = request.y;
2877 else if (request.valign == VALIGN_TOP)
2879 else if (request.valign == VALIGN_BOTTOM)
2880 sy_base = SY + SYSIZE;
2882 sy_base = SY + SYSIZE / 2;
2888 static void setRequestPositionExt(int *x, int *y, int width, int height,
2889 boolean add_border_size)
2891 int border_size = request.border_size;
2892 int sx_base, sy_base;
2895 setRequestBasePosition(&sx_base, &sy_base);
2897 if (request.align == ALIGN_LEFT)
2899 else if (request.align == ALIGN_RIGHT)
2900 sx = sx_base - width;
2902 sx = sx_base - width / 2;
2904 if (request.valign == VALIGN_TOP)
2906 else if (request.valign == VALIGN_BOTTOM)
2907 sy = sy_base - height;
2909 sy = sy_base - height / 2;
2911 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2912 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2914 if (add_border_size)
2924 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2926 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2929 static void DrawEnvelopeRequest(char *text)
2931 char *text_final = text;
2932 char *text_door_style = NULL;
2933 int graphic = IMG_BACKGROUND_REQUEST;
2934 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2935 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2936 int font_nr = FONT_REQUEST;
2937 int font_width = getFontWidth(font_nr);
2938 int font_height = getFontHeight(font_nr);
2939 int border_size = request.border_size;
2940 int line_spacing = request.line_spacing;
2941 int line_height = font_height + line_spacing;
2942 int max_text_width = request.width - 2 * border_size;
2943 int max_text_height = request.height - 2 * border_size;
2944 int line_length = max_text_width / font_width;
2945 int max_lines = max_text_height / line_height;
2946 int text_width = line_length * font_width;
2947 int width = request.width;
2948 int height = request.height;
2949 int tile_size = MAX(request.step_offset, 1);
2950 int x_steps = width / tile_size;
2951 int y_steps = height / tile_size;
2952 int sx_offset = border_size;
2953 int sy_offset = border_size;
2957 if (request.centered)
2958 sx_offset = (request.width - text_width) / 2;
2960 if (request.wrap_single_words && !request.autowrap)
2962 char *src_text_ptr, *dst_text_ptr;
2964 text_door_style = checked_malloc(2 * strlen(text) + 1);
2966 src_text_ptr = text;
2967 dst_text_ptr = text_door_style;
2969 while (*src_text_ptr)
2971 if (*src_text_ptr == ' ' ||
2972 *src_text_ptr == '?' ||
2973 *src_text_ptr == '!')
2974 *dst_text_ptr++ = '\n';
2976 if (*src_text_ptr != ' ')
2977 *dst_text_ptr++ = *src_text_ptr;
2982 *dst_text_ptr = '\0';
2984 text_final = text_door_style;
2987 setRequestPosition(&sx, &sy, FALSE);
2989 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2991 for (y = 0; y < y_steps; y++)
2992 for (x = 0; x < x_steps; x++)
2993 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2994 x, y, x_steps, y_steps,
2995 tile_size, tile_size);
2997 // force DOOR font inside door area
2998 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3000 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3001 line_length, -1, max_lines, line_spacing, mask_mode,
3002 request.autowrap, request.centered, FALSE);
3006 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3007 RedrawGadget(tool_gadget[i]);
3009 // store readily prepared envelope request for later use when animating
3010 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3012 if (text_door_style)
3013 free(text_door_style);
3016 static void AnimateEnvelopeRequest(int anim_mode, int action)
3018 int graphic = IMG_BACKGROUND_REQUEST;
3019 boolean draw_masked = graphic_info[graphic].draw_masked;
3020 int delay_value_normal = request.step_delay;
3021 int delay_value_fast = delay_value_normal / 2;
3022 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3023 boolean no_delay = (tape.warp_forward);
3024 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3025 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3026 unsigned int anim_delay = 0;
3028 int tile_size = MAX(request.step_offset, 1);
3029 int max_xsize = request.width / tile_size;
3030 int max_ysize = request.height / tile_size;
3031 int max_xsize_inner = max_xsize - 2;
3032 int max_ysize_inner = max_ysize - 2;
3034 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3035 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3036 int xend = max_xsize_inner;
3037 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3038 int xstep = (xstart < xend ? 1 : 0);
3039 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3041 int end = MAX(xend - xstart, yend - ystart);
3044 if (setup.quick_doors)
3051 for (i = start; i <= end; i++)
3053 int last_frame = end; // last frame of this "for" loop
3054 int x = xstart + i * xstep;
3055 int y = ystart + i * ystep;
3056 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3057 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3058 int xsize_size_left = (xsize - 1) * tile_size;
3059 int ysize_size_top = (ysize - 1) * tile_size;
3060 int max_xsize_pos = (max_xsize - 1) * tile_size;
3061 int max_ysize_pos = (max_ysize - 1) * tile_size;
3062 int width = xsize * tile_size;
3063 int height = ysize * tile_size;
3068 setRequestPosition(&src_x, &src_y, FALSE);
3069 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3071 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3073 for (yy = 0; yy < 2; yy++)
3075 for (xx = 0; xx < 2; xx++)
3077 int src_xx = src_x + xx * max_xsize_pos;
3078 int src_yy = src_y + yy * max_ysize_pos;
3079 int dst_xx = dst_x + xx * xsize_size_left;
3080 int dst_yy = dst_y + yy * ysize_size_top;
3081 int xx_size = (xx ? tile_size : xsize_size_left);
3082 int yy_size = (yy ? tile_size : ysize_size_top);
3085 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3086 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3088 BlitBitmap(bitmap_db_store_2, backbuffer,
3089 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3093 redraw_mask |= REDRAW_FIELD;
3097 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3100 ClearAutoRepeatKeyEvents();
3103 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3105 int graphic = IMG_BACKGROUND_REQUEST;
3106 int sound_opening = SND_REQUEST_OPENING;
3107 int sound_closing = SND_REQUEST_CLOSING;
3108 int anim_mode_1 = request.anim_mode; // (higher priority)
3109 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3110 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3111 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3112 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3114 if (game_status == GAME_MODE_PLAYING)
3115 BlitScreenToBitmap(backbuffer);
3117 SetDrawtoField(DRAW_TO_BACKBUFFER);
3119 // SetDrawBackgroundMask(REDRAW_NONE);
3121 if (action == ACTION_OPENING)
3123 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3125 if (req_state & REQ_ASK)
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3132 else if (req_state & REQ_CONFIRM)
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3135 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3137 else if (req_state & REQ_PLAYER)
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3140 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3141 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3142 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3145 DrawEnvelopeRequest(text);
3148 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3150 if (action == ACTION_OPENING)
3152 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3154 if (anim_mode == ANIM_DEFAULT)
3155 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3157 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3161 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3163 if (anim_mode != ANIM_NONE)
3164 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3166 if (anim_mode == ANIM_DEFAULT)
3167 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3170 game.envelope_active = FALSE;
3172 if (action == ACTION_CLOSING)
3173 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3175 // SetDrawBackgroundMask(last_draw_background_mask);
3177 redraw_mask |= REDRAW_FIELD;
3181 if (action == ACTION_CLOSING &&
3182 game_status == GAME_MODE_PLAYING &&
3183 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3184 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3187 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3189 if (IS_MM_WALL(element))
3191 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3197 int graphic = el2preimg(element);
3199 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3200 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3205 void DrawLevel(int draw_background_mask)
3209 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3210 SetDrawBackgroundMask(draw_background_mask);
3214 for (x = BX1; x <= BX2; x++)
3215 for (y = BY1; y <= BY2; y++)
3216 DrawScreenField(x, y);
3218 redraw_mask |= REDRAW_FIELD;
3221 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3226 for (x = 0; x < size_x; x++)
3227 for (y = 0; y < size_y; y++)
3228 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3230 redraw_mask |= REDRAW_FIELD;
3233 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3237 for (x = 0; x < size_x; x++)
3238 for (y = 0; y < size_y; y++)
3239 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3241 redraw_mask |= REDRAW_FIELD;
3244 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3246 boolean show_level_border = (BorderElement != EL_EMPTY);
3247 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3248 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3249 int tile_size = preview.tile_size;
3250 int preview_width = preview.xsize * tile_size;
3251 int preview_height = preview.ysize * tile_size;
3252 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3253 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3254 int real_preview_width = real_preview_xsize * tile_size;
3255 int real_preview_height = real_preview_ysize * tile_size;
3256 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3257 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3260 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3263 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3265 dst_x += (preview_width - real_preview_width) / 2;
3266 dst_y += (preview_height - real_preview_height) / 2;
3268 for (x = 0; x < real_preview_xsize; x++)
3270 for (y = 0; y < real_preview_ysize; y++)
3272 int lx = from_x + x + (show_level_border ? -1 : 0);
3273 int ly = from_y + y + (show_level_border ? -1 : 0);
3274 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3275 getBorderElement(lx, ly));
3277 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3278 element, tile_size);
3282 redraw_mask |= REDRAW_FIELD;
3285 #define MICROLABEL_EMPTY 0
3286 #define MICROLABEL_LEVEL_NAME 1
3287 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3288 #define MICROLABEL_LEVEL_AUTHOR 3
3289 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3290 #define MICROLABEL_IMPORTED_FROM 5
3291 #define MICROLABEL_IMPORTED_BY_HEAD 6
3292 #define MICROLABEL_IMPORTED_BY 7
3294 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3296 int max_text_width = SXSIZE;
3297 int font_width = getFontWidth(font_nr);
3299 if (pos->align == ALIGN_CENTER)
3300 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3301 else if (pos->align == ALIGN_RIGHT)
3302 max_text_width = pos->x;
3304 max_text_width = SXSIZE - pos->x;
3306 return max_text_width / font_width;
3309 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3311 char label_text[MAX_OUTPUT_LINESIZE + 1];
3312 int max_len_label_text;
3313 int font_nr = pos->font;
3316 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3319 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3320 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3321 mode == MICROLABEL_IMPORTED_BY_HEAD)
3322 font_nr = pos->font_alt;
3324 max_len_label_text = getMaxTextLength(pos, font_nr);
3326 if (pos->size != -1)
3327 max_len_label_text = pos->size;
3329 for (i = 0; i < max_len_label_text; i++)
3330 label_text[i] = ' ';
3331 label_text[max_len_label_text] = '\0';
3333 if (strlen(label_text) > 0)
3334 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3337 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3338 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3339 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3340 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3341 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3342 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3343 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3344 max_len_label_text);
3345 label_text[max_len_label_text] = '\0';
3347 if (strlen(label_text) > 0)
3348 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3350 redraw_mask |= REDRAW_FIELD;
3353 static void DrawPreviewLevelLabel(int mode)
3355 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3358 static void DrawPreviewLevelInfo(int mode)
3360 if (mode == MICROLABEL_LEVEL_NAME)
3361 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3362 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3363 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3366 static void DrawPreviewLevelExt(boolean restart)
3368 static unsigned int scroll_delay = 0;
3369 static unsigned int label_delay = 0;
3370 static int from_x, from_y, scroll_direction;
3371 static int label_state, label_counter;
3372 unsigned int scroll_delay_value = preview.step_delay;
3373 boolean show_level_border = (BorderElement != EL_EMPTY);
3374 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3375 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3382 if (preview.anim_mode == ANIM_CENTERED)
3384 if (level_xsize > preview.xsize)
3385 from_x = (level_xsize - preview.xsize) / 2;
3386 if (level_ysize > preview.ysize)
3387 from_y = (level_ysize - preview.ysize) / 2;
3390 from_x += preview.xoffset;
3391 from_y += preview.yoffset;
3393 scroll_direction = MV_RIGHT;
3397 DrawPreviewLevelPlayfield(from_x, from_y);
3398 DrawPreviewLevelLabel(label_state);
3400 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3401 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3403 // initialize delay counters
3404 DelayReached(&scroll_delay, 0);
3405 DelayReached(&label_delay, 0);
3407 if (leveldir_current->name)
3409 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3410 char label_text[MAX_OUTPUT_LINESIZE + 1];
3411 int font_nr = pos->font;
3412 int max_len_label_text = getMaxTextLength(pos, font_nr);
3414 if (pos->size != -1)
3415 max_len_label_text = pos->size;
3417 strncpy(label_text, leveldir_current->name, max_len_label_text);
3418 label_text[max_len_label_text] = '\0';
3420 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3421 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3427 // scroll preview level, if needed
3428 if (preview.anim_mode != ANIM_NONE &&
3429 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3430 DelayReached(&scroll_delay, scroll_delay_value))
3432 switch (scroll_direction)
3437 from_x -= preview.step_offset;
3438 from_x = (from_x < 0 ? 0 : from_x);
3441 scroll_direction = MV_UP;
3445 if (from_x < level_xsize - preview.xsize)
3447 from_x += preview.step_offset;
3448 from_x = (from_x > level_xsize - preview.xsize ?
3449 level_xsize - preview.xsize : from_x);
3452 scroll_direction = MV_DOWN;
3458 from_y -= preview.step_offset;
3459 from_y = (from_y < 0 ? 0 : from_y);
3462 scroll_direction = MV_RIGHT;
3466 if (from_y < level_ysize - preview.ysize)
3468 from_y += preview.step_offset;
3469 from_y = (from_y > level_ysize - preview.ysize ?
3470 level_ysize - preview.ysize : from_y);
3473 scroll_direction = MV_LEFT;
3480 DrawPreviewLevelPlayfield(from_x, from_y);
3483 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3484 // redraw micro level label, if needed
3485 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3486 !strEqual(level.author, ANONYMOUS_NAME) &&
3487 !strEqual(level.author, leveldir_current->name) &&
3488 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3490 int max_label_counter = 23;
3492 if (leveldir_current->imported_from != NULL &&
3493 strlen(leveldir_current->imported_from) > 0)
3494 max_label_counter += 14;
3495 if (leveldir_current->imported_by != NULL &&
3496 strlen(leveldir_current->imported_by) > 0)
3497 max_label_counter += 14;
3499 label_counter = (label_counter + 1) % max_label_counter;
3500 label_state = (label_counter >= 0 && label_counter <= 7 ?
3501 MICROLABEL_LEVEL_NAME :
3502 label_counter >= 9 && label_counter <= 12 ?
3503 MICROLABEL_LEVEL_AUTHOR_HEAD :
3504 label_counter >= 14 && label_counter <= 21 ?
3505 MICROLABEL_LEVEL_AUTHOR :
3506 label_counter >= 23 && label_counter <= 26 ?
3507 MICROLABEL_IMPORTED_FROM_HEAD :
3508 label_counter >= 28 && label_counter <= 35 ?
3509 MICROLABEL_IMPORTED_FROM :
3510 label_counter >= 37 && label_counter <= 40 ?
3511 MICROLABEL_IMPORTED_BY_HEAD :
3512 label_counter >= 42 && label_counter <= 49 ?
3513 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3515 if (leveldir_current->imported_from == NULL &&
3516 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3517 label_state == MICROLABEL_IMPORTED_FROM))
3518 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3519 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3521 DrawPreviewLevelLabel(label_state);
3525 void DrawPreviewPlayers(void)
3527 if (game_status != GAME_MODE_MAIN)
3530 // do not draw preview players if level preview redefined, but players aren't
3531 if (preview.redefined && !menu.main.preview_players.redefined)
3534 boolean player_found[MAX_PLAYERS];
3535 int num_players = 0;
3538 for (i = 0; i < MAX_PLAYERS; i++)
3539 player_found[i] = FALSE;
3541 // check which players can be found in the level (simple approach)
3542 for (x = 0; x < lev_fieldx; x++)
3544 for (y = 0; y < lev_fieldy; y++)
3546 int element = level.field[x][y];
3548 if (ELEM_IS_PLAYER(element))
3550 int player_nr = GET_PLAYER_NR(element);
3552 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3554 if (!player_found[player_nr])
3557 player_found[player_nr] = TRUE;
3562 struct TextPosInfo *pos = &menu.main.preview_players;
3563 int tile_size = pos->tile_size;
3564 int border_size = pos->border_size;
3565 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3566 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3567 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3568 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3569 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3570 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3571 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3572 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3573 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3574 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3575 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3576 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3578 // clear area in which the players will be drawn
3579 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3580 max_players_width, max_players_height);
3582 if (!network.enabled && !setup.team_mode)
3585 // only draw players if level is suited for team mode
3586 if (num_players < 2)
3589 // draw all players that were found in the level
3590 for (i = 0; i < MAX_PLAYERS; i++)
3592 if (player_found[i])
3594 int graphic = el2img(EL_PLAYER_1 + i);
3596 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3598 xpos += player_xoffset;
3599 ypos += player_yoffset;
3604 void DrawPreviewLevelInitial(void)
3606 DrawPreviewLevelExt(TRUE);
3607 DrawPreviewPlayers();
3610 void DrawPreviewLevelAnimation(void)
3612 DrawPreviewLevelExt(FALSE);
3615 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3616 int border_size, int font_nr)
3618 int graphic = el2img(EL_PLAYER_1 + player_nr);
3619 int font_height = getFontHeight(font_nr);
3620 int player_height = MAX(tile_size, font_height);
3621 int xoffset_text = tile_size + border_size;
3622 int yoffset_text = (player_height - font_height) / 2;
3623 int yoffset_graphic = (player_height - tile_size) / 2;
3624 char *player_name = getNetworkPlayerName(player_nr + 1);
3626 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3628 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3631 static void DrawNetworkPlayersExt(boolean force)
3633 if (game_status != GAME_MODE_MAIN)
3636 if (!network.connected && !force)
3639 // do not draw network players if level preview redefined, but players aren't
3640 if (preview.redefined && !menu.main.network_players.redefined)
3643 int num_players = 0;
3646 for (i = 0; i < MAX_PLAYERS; i++)
3647 if (stored_player[i].connected_network)
3650 struct TextPosInfo *pos = &menu.main.network_players;
3651 int tile_size = pos->tile_size;
3652 int border_size = pos->border_size;
3653 int xoffset_text = tile_size + border_size;
3654 int font_nr = pos->font;
3655 int font_width = getFontWidth(font_nr);
3656 int font_height = getFontHeight(font_nr);
3657 int player_height = MAX(tile_size, font_height);
3658 int player_yoffset = player_height + border_size;
3659 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3660 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3661 int all_players_height = num_players * player_yoffset - border_size;
3662 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3663 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3664 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3666 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3667 max_players_width, max_players_height);
3669 // first draw local network player ...
3670 for (i = 0; i < MAX_PLAYERS; i++)
3672 if (stored_player[i].connected_network &&
3673 stored_player[i].connected_locally)
3675 char *player_name = getNetworkPlayerName(i + 1);
3676 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3677 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3679 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3681 ypos += player_yoffset;
3685 // ... then draw all other network players
3686 for (i = 0; i < MAX_PLAYERS; i++)
3688 if (stored_player[i].connected_network &&
3689 !stored_player[i].connected_locally)
3691 char *player_name = getNetworkPlayerName(i + 1);
3692 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3693 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3695 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3697 ypos += player_yoffset;
3702 void DrawNetworkPlayers(void)
3704 DrawNetworkPlayersExt(FALSE);
3707 void ClearNetworkPlayers(void)
3709 DrawNetworkPlayersExt(TRUE);
3712 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3713 int graphic, int sync_frame,
3716 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3718 if (mask_mode == USE_MASKING)
3719 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3721 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3724 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3725 int graphic, int sync_frame, int mask_mode)
3727 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3729 if (mask_mode == USE_MASKING)
3730 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3732 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3735 static void DrawGraphicAnimation(int x, int y, int graphic)
3737 int lx = LEVELX(x), ly = LEVELY(y);
3739 if (!IN_SCR_FIELD(x, y))
3742 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3743 graphic, GfxFrame[lx][ly], NO_MASKING);
3745 MarkTileDirty(x, y);
3748 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3750 int lx = LEVELX(x), ly = LEVELY(y);
3752 if (!IN_SCR_FIELD(x, y))
3755 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3756 graphic, GfxFrame[lx][ly], NO_MASKING);
3757 MarkTileDirty(x, y);
3760 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3762 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3765 void DrawLevelElementAnimation(int x, int y, int element)
3767 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3769 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3772 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3774 int sx = SCREENX(x), sy = SCREENY(y);
3776 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3779 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3782 DrawGraphicAnimation(sx, sy, graphic);
3785 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3786 DrawLevelFieldCrumbled(x, y);
3788 if (GFX_CRUMBLED(Feld[x][y]))
3789 DrawLevelFieldCrumbled(x, y);
3793 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3795 int sx = SCREENX(x), sy = SCREENY(y);
3798 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3801 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3803 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3806 DrawGraphicAnimation(sx, sy, graphic);
3808 if (GFX_CRUMBLED(element))
3809 DrawLevelFieldCrumbled(x, y);
3812 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3814 if (player->use_murphy)
3816 // this works only because currently only one player can be "murphy" ...
3817 static int last_horizontal_dir = MV_LEFT;
3818 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3820 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3821 last_horizontal_dir = move_dir;
3823 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3825 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3827 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3833 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3836 static boolean equalGraphics(int graphic1, int graphic2)
3838 struct GraphicInfo *g1 = &graphic_info[graphic1];
3839 struct GraphicInfo *g2 = &graphic_info[graphic2];
3841 return (g1->bitmap == g2->bitmap &&
3842 g1->src_x == g2->src_x &&
3843 g1->src_y == g2->src_y &&
3844 g1->anim_frames == g2->anim_frames &&
3845 g1->anim_delay == g2->anim_delay &&
3846 g1->anim_mode == g2->anim_mode);
3849 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3853 DRAW_PLAYER_STAGE_INIT = 0,
3854 DRAW_PLAYER_STAGE_LAST_FIELD,
3855 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3856 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3857 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3858 DRAW_PLAYER_STAGE_PLAYER,
3860 DRAW_PLAYER_STAGE_PLAYER,
3861 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3863 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3864 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3866 NUM_DRAW_PLAYER_STAGES
3869 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3871 static int static_last_player_graphic[MAX_PLAYERS];
3872 static int static_last_player_frame[MAX_PLAYERS];
3873 static boolean static_player_is_opaque[MAX_PLAYERS];
3874 static boolean draw_player[MAX_PLAYERS];
3875 int pnr = player->index_nr;
3877 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3879 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3880 static_last_player_frame[pnr] = player->Frame;
3881 static_player_is_opaque[pnr] = FALSE;
3883 draw_player[pnr] = TRUE;
3886 if (!draw_player[pnr])
3890 if (!IN_LEV_FIELD(player->jx, player->jy))
3892 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3893 printf("DrawPlayerField(): This should never happen!\n");
3895 draw_player[pnr] = FALSE;
3901 int last_player_graphic = static_last_player_graphic[pnr];
3902 int last_player_frame = static_last_player_frame[pnr];
3903 boolean player_is_opaque = static_player_is_opaque[pnr];
3905 int jx = player->jx;
3906 int jy = player->jy;
3907 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3908 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3909 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3910 int last_jx = (player->is_moving ? jx - dx : jx);
3911 int last_jy = (player->is_moving ? jy - dy : jy);
3912 int next_jx = jx + dx;
3913 int next_jy = jy + dy;
3914 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3915 int sx = SCREENX(jx);
3916 int sy = SCREENY(jy);
3917 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3918 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3919 int element = Feld[jx][jy];
3920 int last_element = Feld[last_jx][last_jy];
3921 int action = (player->is_pushing ? ACTION_PUSHING :
3922 player->is_digging ? ACTION_DIGGING :
3923 player->is_collecting ? ACTION_COLLECTING :
3924 player->is_moving ? ACTION_MOVING :
3925 player->is_snapping ? ACTION_SNAPPING :
3926 player->is_dropping ? ACTION_DROPPING :
3927 player->is_waiting ? player->action_waiting :
3930 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3932 // ------------------------------------------------------------------------
3933 // initialize drawing the player
3934 // ------------------------------------------------------------------------
3936 draw_player[pnr] = FALSE;
3938 // GfxElement[][] is set to the element the player is digging or collecting;
3939 // remove also for off-screen player if the player is not moving anymore
3940 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3941 GfxElement[jx][jy] = EL_UNDEFINED;
3943 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3946 if (element == EL_EXPLOSION)
3949 InitPlayerGfxAnimation(player, action, move_dir);
3951 draw_player[pnr] = TRUE;
3953 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3955 // ------------------------------------------------------------------------
3956 // draw things in the field the player is leaving, if needed
3957 // ------------------------------------------------------------------------
3959 if (!IN_SCR_FIELD(sx, sy))
3960 draw_player[pnr] = FALSE;
3962 if (!player->is_moving)
3965 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3967 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3969 if (last_element == EL_DYNAMITE_ACTIVE ||
3970 last_element == EL_EM_DYNAMITE_ACTIVE ||
3971 last_element == EL_SP_DISK_RED_ACTIVE)
3972 DrawDynamite(last_jx, last_jy);
3974 DrawLevelFieldThruMask(last_jx, last_jy);
3976 else if (last_element == EL_DYNAMITE_ACTIVE ||
3977 last_element == EL_EM_DYNAMITE_ACTIVE ||
3978 last_element == EL_SP_DISK_RED_ACTIVE)
3979 DrawDynamite(last_jx, last_jy);
3981 DrawLevelField(last_jx, last_jy);
3983 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3984 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3986 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3988 // ------------------------------------------------------------------------
3989 // draw things behind the player, if needed
3990 // ------------------------------------------------------------------------
3994 DrawLevelElement(jx, jy, Back[jx][jy]);
3999 if (IS_ACTIVE_BOMB(element))
4001 DrawLevelElement(jx, jy, EL_EMPTY);
4006 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4008 int old_element = GfxElement[jx][jy];
4009 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4010 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4012 if (GFX_CRUMBLED(old_element))
4013 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4015 DrawGraphic(sx, sy, old_graphic, frame);
4017 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4018 static_player_is_opaque[pnr] = TRUE;
4022 GfxElement[jx][jy] = EL_UNDEFINED;
4024 // make sure that pushed elements are drawn with correct frame rate
4025 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4027 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4028 GfxFrame[jx][jy] = player->StepFrame;
4030 DrawLevelField(jx, jy);
4033 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4035 // ------------------------------------------------------------------------
4036 // draw things the player is pushing, if needed
4037 // ------------------------------------------------------------------------
4039 if (!player->is_pushing || !player->is_moving)
4042 int gfx_frame = GfxFrame[jx][jy];
4044 if (!IS_MOVING(jx, jy)) // push movement already finished
4046 element = Feld[next_jx][next_jy];
4047 gfx_frame = GfxFrame[next_jx][next_jy];
4050 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4051 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4052 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4054 // draw background element under pushed element (like the Sokoban field)
4055 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4057 // this allows transparent pushing animation over non-black background
4060 DrawLevelElement(jx, jy, Back[jx][jy]);
4062 DrawLevelElement(jx, jy, EL_EMPTY);
4064 if (Back[next_jx][next_jy])
4065 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4067 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4069 else if (Back[next_jx][next_jy])
4070 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4072 int px = SCREENX(jx), py = SCREENY(jy);
4073 int pxx = (TILEX - ABS(sxx)) * dx;
4074 int pyy = (TILEY - ABS(syy)) * dy;
4077 // do not draw (EM style) pushing animation when pushing is finished
4078 // (two-tile animations usually do not contain start and end frame)
4079 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4080 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4082 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4084 // masked drawing is needed for EMC style (double) movement graphics
4085 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4086 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4089 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4091 // ------------------------------------------------------------------------
4092 // draw player himself
4093 // ------------------------------------------------------------------------
4095 int graphic = getPlayerGraphic(player, move_dir);
4097 // in the case of changed player action or direction, prevent the current
4098 // animation frame from being restarted for identical animations
4099 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4100 player->Frame = last_player_frame;
4102 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4104 if (player_is_opaque)
4105 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4107 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4109 if (SHIELD_ON(player))
4111 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4112 IMG_SHIELD_NORMAL_ACTIVE);
4113 frame = getGraphicAnimationFrame(graphic, -1);
4115 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4118 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4120 // ------------------------------------------------------------------------
4121 // draw things in front of player (active dynamite or dynabombs)
4122 // ------------------------------------------------------------------------
4124 if (IS_ACTIVE_BOMB(element))
4126 int graphic = el2img(element);
4127 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4129 if (game.emulation == EMU_SUPAPLEX)
4130 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4132 DrawGraphicThruMask(sx, sy, graphic, frame);
4135 if (player_is_moving && last_element == EL_EXPLOSION)
4137 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4138 GfxElement[last_jx][last_jy] : EL_EMPTY);
4139 int graphic = el_act2img(element, ACTION_EXPLODING);
4140 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4141 int phase = ExplodePhase[last_jx][last_jy] - 1;
4142 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4145 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4148 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4150 // ------------------------------------------------------------------------
4151 // draw elements the player is just walking/passing through/under
4152 // ------------------------------------------------------------------------
4154 if (player_is_moving)
4156 // handle the field the player is leaving ...
4157 if (IS_ACCESSIBLE_INSIDE(last_element))
4158 DrawLevelField(last_jx, last_jy);
4159 else if (IS_ACCESSIBLE_UNDER(last_element))
4160 DrawLevelFieldThruMask(last_jx, last_jy);
4163 // do not redraw accessible elements if the player is just pushing them
4164 if (!player_is_moving || !player->is_pushing)
4166 // ... and the field the player is entering
4167 if (IS_ACCESSIBLE_INSIDE(element))
4168 DrawLevelField(jx, jy);
4169 else if (IS_ACCESSIBLE_UNDER(element))
4170 DrawLevelFieldThruMask(jx, jy);
4173 MarkTileDirty(sx, sy);
4177 void DrawPlayer(struct PlayerInfo *player)
4181 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4182 DrawPlayerExt(player, i);
4185 void DrawAllPlayers(void)
4189 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4190 for (j = 0; j < MAX_PLAYERS; j++)
4191 if (stored_player[j].active)
4192 DrawPlayerExt(&stored_player[j], i);
4195 void DrawPlayerField(int x, int y)
4197 if (!IS_PLAYER(x, y))
4200 DrawPlayer(PLAYERINFO(x, y));
4203 // ----------------------------------------------------------------------------
4205 void WaitForEventToContinue(void)
4207 boolean still_wait = TRUE;
4209 if (program.headless)
4212 // simulate releasing mouse button over last gadget, if still pressed
4214 HandleGadgets(-1, -1, 0);
4216 button_status = MB_RELEASED;
4224 if (NextValidEvent(&event))
4228 case EVENT_BUTTONRELEASE:
4229 case EVENT_KEYPRESS:
4230 case SDL_CONTROLLERBUTTONDOWN:
4231 case SDL_JOYBUTTONDOWN:
4235 case EVENT_KEYRELEASE:
4236 ClearPlayerAction();
4240 HandleOtherEvents(&event);
4244 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4253 #define MAX_REQUEST_LINES 13
4254 #define MAX_REQUEST_LINE_FONT1_LEN 7
4255 #define MAX_REQUEST_LINE_FONT2_LEN 10
4257 static int RequestHandleEvents(unsigned int req_state)
4259 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4261 int width = request.width;
4262 int height = request.height;
4266 // when showing request dialog after game ended, deactivate game panel
4267 if (game_just_ended)
4268 game.panel.active = FALSE;
4270 game.request_active = TRUE;
4272 setRequestPosition(&sx, &sy, FALSE);
4274 button_status = MB_RELEASED;
4276 request_gadget_id = -1;
4281 if (game_just_ended)
4283 // the MM game engine does not use a special (scrollable) field buffer
4284 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4285 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4287 HandleGameActions();
4289 SetDrawtoField(DRAW_TO_BACKBUFFER);
4291 if (global.use_envelope_request)
4293 // copy current state of request area to middle of playfield area
4294 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4302 while (NextValidEvent(&event))
4306 case EVENT_BUTTONPRESS:
4307 case EVENT_BUTTONRELEASE:
4308 case EVENT_MOTIONNOTIFY:
4312 if (event.type == EVENT_MOTIONNOTIFY)
4317 motion_status = TRUE;
4318 mx = ((MotionEvent *) &event)->x;
4319 my = ((MotionEvent *) &event)->y;
4323 motion_status = FALSE;
4324 mx = ((ButtonEvent *) &event)->x;
4325 my = ((ButtonEvent *) &event)->y;
4326 if (event.type == EVENT_BUTTONPRESS)
4327 button_status = ((ButtonEvent *) &event)->button;
4329 button_status = MB_RELEASED;
4332 // this sets 'request_gadget_id'
4333 HandleGadgets(mx, my, button_status);
4335 switch (request_gadget_id)
4337 case TOOL_CTRL_ID_YES:
4338 case TOOL_CTRL_ID_TOUCH_YES:
4341 case TOOL_CTRL_ID_NO:
4342 case TOOL_CTRL_ID_TOUCH_NO:
4345 case TOOL_CTRL_ID_CONFIRM:
4346 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4347 result = TRUE | FALSE;
4350 case TOOL_CTRL_ID_PLAYER_1:
4353 case TOOL_CTRL_ID_PLAYER_2:
4356 case TOOL_CTRL_ID_PLAYER_3:
4359 case TOOL_CTRL_ID_PLAYER_4:
4364 // only check clickable animations if no request gadget clicked
4365 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4372 case SDL_WINDOWEVENT:
4373 HandleWindowEvent((WindowEvent *) &event);
4376 case SDL_APP_WILLENTERBACKGROUND:
4377 case SDL_APP_DIDENTERBACKGROUND:
4378 case SDL_APP_WILLENTERFOREGROUND:
4379 case SDL_APP_DIDENTERFOREGROUND:
4380 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4383 case EVENT_KEYPRESS:
4385 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4390 if (req_state & REQ_CONFIRM)
4399 #if defined(KSYM_Rewind)
4400 case KSYM_Rewind: // for Amazon Fire TV remote
4409 #if defined(KSYM_FastForward)
4410 case KSYM_FastForward: // for Amazon Fire TV remote
4416 HandleKeysDebug(key, KEY_PRESSED);
4420 if (req_state & REQ_PLAYER)
4422 int old_player_nr = setup.network_player_nr;
4425 result = old_player_nr + 1;
4430 result = old_player_nr + 1;
4461 case EVENT_KEYRELEASE:
4462 ClearPlayerAction();
4465 case SDL_CONTROLLERBUTTONDOWN:
4466 switch (event.cbutton.button)
4468 case SDL_CONTROLLER_BUTTON_A:
4469 case SDL_CONTROLLER_BUTTON_X:
4470 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4471 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4475 case SDL_CONTROLLER_BUTTON_B:
4476 case SDL_CONTROLLER_BUTTON_Y:
4477 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4478 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4479 case SDL_CONTROLLER_BUTTON_BACK:
4484 if (req_state & REQ_PLAYER)
4486 int old_player_nr = setup.network_player_nr;
4489 result = old_player_nr + 1;
4491 switch (event.cbutton.button)
4493 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4494 case SDL_CONTROLLER_BUTTON_Y:
4498 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4499 case SDL_CONTROLLER_BUTTON_B:
4503 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4504 case SDL_CONTROLLER_BUTTON_A:
4508 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4509 case SDL_CONTROLLER_BUTTON_X:
4520 case SDL_CONTROLLERBUTTONUP:
4521 HandleJoystickEvent(&event);
4522 ClearPlayerAction();
4526 HandleOtherEvents(&event);
4531 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4533 int joy = AnyJoystick();
4535 if (joy & JOY_BUTTON_1)
4537 else if (joy & JOY_BUTTON_2)
4540 else if (AnyJoystick())
4542 int joy = AnyJoystick();
4544 if (req_state & REQ_PLAYER)
4548 else if (joy & JOY_RIGHT)
4550 else if (joy & JOY_DOWN)
4552 else if (joy & JOY_LEFT)
4557 if (game_just_ended)
4559 if (global.use_envelope_request)
4561 // copy back current state of pressed buttons inside request area
4562 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4569 game.request_active = FALSE;
4574 static boolean RequestDoor(char *text, unsigned int req_state)
4576 unsigned int old_door_state;
4577 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4578 int font_nr = FONT_TEXT_2;
4583 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4585 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4586 font_nr = FONT_TEXT_1;
4589 if (game_status == GAME_MODE_PLAYING)
4590 BlitScreenToBitmap(backbuffer);
4592 // disable deactivated drawing when quick-loading level tape recording
4593 if (tape.playing && tape.deactivate_display)
4594 TapeDeactivateDisplayOff(TRUE);
4596 SetMouseCursor(CURSOR_DEFAULT);
4598 // pause network game while waiting for request to answer
4599 if (network.enabled &&
4600 game_status == GAME_MODE_PLAYING &&
4601 !game.all_players_gone &&
4602 req_state & REQUEST_WAIT_FOR_INPUT)
4603 SendToServer_PausePlaying();
4605 old_door_state = GetDoorState();
4607 // simulate releasing mouse button over last gadget, if still pressed
4609 HandleGadgets(-1, -1, 0);
4613 // draw released gadget before proceeding
4616 if (old_door_state & DOOR_OPEN_1)
4618 CloseDoor(DOOR_CLOSE_1);
4620 // save old door content
4621 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4622 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4625 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4626 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4628 // clear door drawing field
4629 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4631 // force DOOR font inside door area
4632 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4634 // write text for request
4635 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4637 char text_line[max_request_line_len + 1];
4643 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4645 tc = *(text_ptr + tx);
4646 // if (!tc || tc == ' ')
4647 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4651 if ((tc == '?' || tc == '!') && tl == 0)
4661 strncpy(text_line, text_ptr, tl);
4664 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4665 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4666 text_line, font_nr);
4668 text_ptr += tl + (tc == ' ' ? 1 : 0);
4669 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4674 if (req_state & REQ_ASK)
4676 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4677 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4678 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4679 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4681 else if (req_state & REQ_CONFIRM)
4683 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4684 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4686 else if (req_state & REQ_PLAYER)
4688 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4689 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4690 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4691 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4694 // copy request gadgets to door backbuffer
4695 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4697 OpenDoor(DOOR_OPEN_1);
4699 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4701 if (game_status == GAME_MODE_PLAYING)
4703 SetPanelBackground();
4704 SetDrawBackgroundMask(REDRAW_DOOR_1);
4708 SetDrawBackgroundMask(REDRAW_FIELD);
4714 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4716 // ---------- handle request buttons ----------
4717 result = RequestHandleEvents(req_state);
4721 if (!(req_state & REQ_STAY_OPEN))
4723 CloseDoor(DOOR_CLOSE_1);
4725 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4726 (req_state & REQ_REOPEN))
4727 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4732 if (game_status == GAME_MODE_PLAYING)
4734 SetPanelBackground();
4735 SetDrawBackgroundMask(REDRAW_DOOR_1);
4739 SetDrawBackgroundMask(REDRAW_FIELD);
4742 // continue network game after request
4743 if (network.enabled &&
4744 game_status == GAME_MODE_PLAYING &&
4745 !game.all_players_gone &&
4746 req_state & REQUEST_WAIT_FOR_INPUT)
4747 SendToServer_ContinuePlaying();
4749 // restore deactivated drawing when quick-loading level tape recording
4750 if (tape.playing && tape.deactivate_display)
4751 TapeDeactivateDisplayOn();
4756 static boolean RequestEnvelope(char *text, unsigned int req_state)
4760 if (game_status == GAME_MODE_PLAYING)
4761 BlitScreenToBitmap(backbuffer);
4763 // disable deactivated drawing when quick-loading level tape recording
4764 if (tape.playing && tape.deactivate_display)
4765 TapeDeactivateDisplayOff(TRUE);
4767 SetMouseCursor(CURSOR_DEFAULT);
4769 // pause network game while waiting for request to answer
4770 if (network.enabled &&
4771 game_status == GAME_MODE_PLAYING &&
4772 !game.all_players_gone &&
4773 req_state & REQUEST_WAIT_FOR_INPUT)
4774 SendToServer_PausePlaying();
4776 // simulate releasing mouse button over last gadget, if still pressed
4778 HandleGadgets(-1, -1, 0);
4782 // (replace with setting corresponding request background)
4783 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4784 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4786 // clear door drawing field
4787 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4789 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4791 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4793 if (game_status == GAME_MODE_PLAYING)
4795 SetPanelBackground();
4796 SetDrawBackgroundMask(REDRAW_DOOR_1);
4800 SetDrawBackgroundMask(REDRAW_FIELD);
4806 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4808 // ---------- handle request buttons ----------
4809 result = RequestHandleEvents(req_state);
4813 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4817 if (game_status == GAME_MODE_PLAYING)
4819 SetPanelBackground();
4820 SetDrawBackgroundMask(REDRAW_DOOR_1);
4824 SetDrawBackgroundMask(REDRAW_FIELD);
4827 // continue network game after request
4828 if (network.enabled &&
4829 game_status == GAME_MODE_PLAYING &&
4830 !game.all_players_gone &&
4831 req_state & REQUEST_WAIT_FOR_INPUT)
4832 SendToServer_ContinuePlaying();
4834 // restore deactivated drawing when quick-loading level tape recording
4835 if (tape.playing && tape.deactivate_display)
4836 TapeDeactivateDisplayOn();
4841 boolean Request(char *text, unsigned int req_state)
4843 boolean overlay_enabled = GetOverlayEnabled();
4846 SetOverlayEnabled(FALSE);
4848 if (global.use_envelope_request)
4849 result = RequestEnvelope(text, req_state);
4851 result = RequestDoor(text, req_state);
4853 SetOverlayEnabled(overlay_enabled);
4858 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4860 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4861 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4864 if (dpo1->sort_priority != dpo2->sort_priority)
4865 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4867 compare_result = dpo1->nr - dpo2->nr;
4869 return compare_result;
4872 void InitGraphicCompatibilityInfo_Doors(void)
4878 struct DoorInfo *door;
4882 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4883 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4885 { -1, -1, -1, NULL }
4887 struct Rect door_rect_list[] =
4889 { DX, DY, DXSIZE, DYSIZE },
4890 { VX, VY, VXSIZE, VYSIZE }
4894 for (i = 0; doors[i].door_token != -1; i++)
4896 int door_token = doors[i].door_token;
4897 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4898 int part_1 = doors[i].part_1;
4899 int part_8 = doors[i].part_8;
4900 int part_2 = part_1 + 1;
4901 int part_3 = part_1 + 2;
4902 struct DoorInfo *door = doors[i].door;
4903 struct Rect *door_rect = &door_rect_list[door_index];
4904 boolean door_gfx_redefined = FALSE;
4906 // check if any door part graphic definitions have been redefined
4908 for (j = 0; door_part_controls[j].door_token != -1; j++)
4910 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4911 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4913 if (dpc->door_token == door_token && fi->redefined)
4914 door_gfx_redefined = TRUE;
4917 // check for old-style door graphic/animation modifications
4919 if (!door_gfx_redefined)
4921 if (door->anim_mode & ANIM_STATIC_PANEL)
4923 door->panel.step_xoffset = 0;
4924 door->panel.step_yoffset = 0;
4927 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4929 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4930 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4931 int num_door_steps, num_panel_steps;
4933 // remove door part graphics other than the two default wings
4935 for (j = 0; door_part_controls[j].door_token != -1; j++)
4937 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4938 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4940 if (dpc->graphic >= part_3 &&
4941 dpc->graphic <= part_8)
4945 // set graphics and screen positions of the default wings
4947 g_part_1->width = door_rect->width;
4948 g_part_1->height = door_rect->height;
4949 g_part_2->width = door_rect->width;
4950 g_part_2->height = door_rect->height;
4951 g_part_2->src_x = door_rect->width;
4952 g_part_2->src_y = g_part_1->src_y;
4954 door->part_2.x = door->part_1.x;
4955 door->part_2.y = door->part_1.y;
4957 if (door->width != -1)
4959 g_part_1->width = door->width;
4960 g_part_2->width = door->width;
4962 // special treatment for graphics and screen position of right wing
4963 g_part_2->src_x += door_rect->width - door->width;
4964 door->part_2.x += door_rect->width - door->width;
4967 if (door->height != -1)
4969 g_part_1->height = door->height;
4970 g_part_2->height = door->height;
4972 // special treatment for graphics and screen position of bottom wing
4973 g_part_2->src_y += door_rect->height - door->height;
4974 door->part_2.y += door_rect->height - door->height;
4977 // set animation delays for the default wings and panels
4979 door->part_1.step_delay = door->step_delay;
4980 door->part_2.step_delay = door->step_delay;
4981 door->panel.step_delay = door->step_delay;
4983 // set animation draw order for the default wings
4985 door->part_1.sort_priority = 2; // draw left wing over ...
4986 door->part_2.sort_priority = 1; // ... right wing
4988 // set animation draw offset for the default wings
4990 if (door->anim_mode & ANIM_HORIZONTAL)
4992 door->part_1.step_xoffset = door->step_offset;
4993 door->part_1.step_yoffset = 0;
4994 door->part_2.step_xoffset = door->step_offset * -1;
4995 door->part_2.step_yoffset = 0;
4997 num_door_steps = g_part_1->width / door->step_offset;
4999 else // ANIM_VERTICAL
5001 door->part_1.step_xoffset = 0;
5002 door->part_1.step_yoffset = door->step_offset;
5003 door->part_2.step_xoffset = 0;
5004 door->part_2.step_yoffset = door->step_offset * -1;
5006 num_door_steps = g_part_1->height / door->step_offset;
5009 // set animation draw offset for the default panels
5011 if (door->step_offset > 1)
5013 num_panel_steps = 2 * door_rect->height / door->step_offset;
5014 door->panel.start_step = num_panel_steps - num_door_steps;
5015 door->panel.start_step_closing = door->panel.start_step;
5019 num_panel_steps = door_rect->height / door->step_offset;
5020 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5021 door->panel.start_step_closing = door->panel.start_step;
5022 door->panel.step_delay *= 2;
5029 void InitDoors(void)
5033 for (i = 0; door_part_controls[i].door_token != -1; i++)
5035 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5036 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5038 // initialize "start_step_opening" and "start_step_closing", if needed
5039 if (dpc->pos->start_step_opening == 0 &&
5040 dpc->pos->start_step_closing == 0)
5042 // dpc->pos->start_step_opening = dpc->pos->start_step;
5043 dpc->pos->start_step_closing = dpc->pos->start_step;
5046 // fill structure for door part draw order (sorted below)
5048 dpo->sort_priority = dpc->pos->sort_priority;
5051 // sort door part controls according to sort_priority and graphic number
5052 qsort(door_part_order, MAX_DOOR_PARTS,
5053 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5056 unsigned int OpenDoor(unsigned int door_state)
5058 if (door_state & DOOR_COPY_BACK)
5060 if (door_state & DOOR_OPEN_1)
5061 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5062 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5064 if (door_state & DOOR_OPEN_2)
5065 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5066 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5068 door_state &= ~DOOR_COPY_BACK;
5071 return MoveDoor(door_state);
5074 unsigned int CloseDoor(unsigned int door_state)
5076 unsigned int old_door_state = GetDoorState();
5078 if (!(door_state & DOOR_NO_COPY_BACK))
5080 if (old_door_state & DOOR_OPEN_1)
5081 BlitBitmap(backbuffer, bitmap_db_door_1,
5082 DX, DY, DXSIZE, DYSIZE, 0, 0);
5084 if (old_door_state & DOOR_OPEN_2)
5085 BlitBitmap(backbuffer, bitmap_db_door_2,
5086 VX, VY, VXSIZE, VYSIZE, 0, 0);
5088 door_state &= ~DOOR_NO_COPY_BACK;
5091 return MoveDoor(door_state);
5094 unsigned int GetDoorState(void)
5096 return MoveDoor(DOOR_GET_STATE);
5099 unsigned int SetDoorState(unsigned int door_state)
5101 return MoveDoor(door_state | DOOR_SET_STATE);
5104 static int euclid(int a, int b)
5106 return (b ? euclid(b, a % b) : a);
5109 unsigned int MoveDoor(unsigned int door_state)
5111 struct Rect door_rect_list[] =
5113 { DX, DY, DXSIZE, DYSIZE },
5114 { VX, VY, VXSIZE, VYSIZE }
5116 static int door1 = DOOR_CLOSE_1;
5117 static int door2 = DOOR_CLOSE_2;
5118 unsigned int door_delay = 0;
5119 unsigned int door_delay_value;
5122 if (door_state == DOOR_GET_STATE)
5123 return (door1 | door2);
5125 if (door_state & DOOR_SET_STATE)
5127 if (door_state & DOOR_ACTION_1)
5128 door1 = door_state & DOOR_ACTION_1;
5129 if (door_state & DOOR_ACTION_2)
5130 door2 = door_state & DOOR_ACTION_2;
5132 return (door1 | door2);
5135 if (!(door_state & DOOR_FORCE_REDRAW))
5137 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5138 door_state &= ~DOOR_OPEN_1;
5139 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5140 door_state &= ~DOOR_CLOSE_1;
5141 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5142 door_state &= ~DOOR_OPEN_2;
5143 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5144 door_state &= ~DOOR_CLOSE_2;
5147 if (global.autoplay_leveldir)
5149 door_state |= DOOR_NO_DELAY;
5150 door_state &= ~DOOR_CLOSE_ALL;
5153 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5154 door_state |= DOOR_NO_DELAY;
5156 if (door_state & DOOR_ACTION)
5158 boolean door_panel_drawn[NUM_DOORS];
5159 boolean panel_has_doors[NUM_DOORS];
5160 boolean door_part_skip[MAX_DOOR_PARTS];
5161 boolean door_part_done[MAX_DOOR_PARTS];
5162 boolean door_part_done_all;
5163 int num_steps[MAX_DOOR_PARTS];
5164 int max_move_delay = 0; // delay for complete animations of all doors
5165 int max_step_delay = 0; // delay (ms) between two animation frames
5166 int num_move_steps = 0; // number of animation steps for all doors
5167 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5168 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5169 int current_move_delay = 0;
5173 for (i = 0; i < NUM_DOORS; i++)
5174 panel_has_doors[i] = FALSE;
5176 for (i = 0; i < MAX_DOOR_PARTS; i++)
5178 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5179 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5180 int door_token = dpc->door_token;
5182 door_part_done[i] = FALSE;
5183 door_part_skip[i] = (!(door_state & door_token) ||
5187 for (i = 0; i < MAX_DOOR_PARTS; i++)
5189 int nr = door_part_order[i].nr;
5190 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5191 struct DoorPartPosInfo *pos = dpc->pos;
5192 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5193 int door_token = dpc->door_token;
5194 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5195 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5196 int step_xoffset = ABS(pos->step_xoffset);
5197 int step_yoffset = ABS(pos->step_yoffset);
5198 int step_delay = pos->step_delay;
5199 int current_door_state = door_state & door_token;
5200 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5201 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5202 boolean part_opening = (is_panel ? door_closing : door_opening);
5203 int start_step = (part_opening ? pos->start_step_opening :
5204 pos->start_step_closing);
5205 float move_xsize = (step_xoffset ? g->width : 0);
5206 float move_ysize = (step_yoffset ? g->height : 0);
5207 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5208 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5209 int move_steps = (move_xsteps && move_ysteps ?
5210 MIN(move_xsteps, move_ysteps) :
5211 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5212 int move_delay = move_steps * step_delay;
5214 if (door_part_skip[nr])
5217 max_move_delay = MAX(max_move_delay, move_delay);
5218 max_step_delay = (max_step_delay == 0 ? step_delay :
5219 euclid(max_step_delay, step_delay));
5220 num_steps[nr] = move_steps;
5224 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5226 panel_has_doors[door_index] = TRUE;
5230 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5232 num_move_steps = max_move_delay / max_step_delay;
5233 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5235 door_delay_value = max_step_delay;
5237 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5239 start = num_move_steps - 1;
5243 // opening door sound has priority over simultaneously closing door
5244 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5246 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5248 if (door_state & DOOR_OPEN_1)
5249 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5250 if (door_state & DOOR_OPEN_2)
5251 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5253 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5255 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5257 if (door_state & DOOR_CLOSE_1)
5258 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5259 if (door_state & DOOR_CLOSE_2)
5260 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5264 for (k = start; k < num_move_steps; k++)
5266 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5268 door_part_done_all = TRUE;
5270 for (i = 0; i < NUM_DOORS; i++)
5271 door_panel_drawn[i] = FALSE;
5273 for (i = 0; i < MAX_DOOR_PARTS; i++)
5275 int nr = door_part_order[i].nr;
5276 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5277 struct DoorPartPosInfo *pos = dpc->pos;
5278 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5279 int door_token = dpc->door_token;
5280 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5281 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5282 boolean is_panel_and_door_has_closed = FALSE;
5283 struct Rect *door_rect = &door_rect_list[door_index];
5284 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5286 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5287 int current_door_state = door_state & door_token;
5288 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5289 boolean door_closing = !door_opening;
5290 boolean part_opening = (is_panel ? door_closing : door_opening);
5291 boolean part_closing = !part_opening;
5292 int start_step = (part_opening ? pos->start_step_opening :
5293 pos->start_step_closing);
5294 int step_delay = pos->step_delay;
5295 int step_factor = step_delay / max_step_delay;
5296 int k1 = (step_factor ? k / step_factor + 1 : k);
5297 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5298 int kk = MAX(0, k2);
5301 int src_x, src_y, src_xx, src_yy;
5302 int dst_x, dst_y, dst_xx, dst_yy;
5305 if (door_part_skip[nr])
5308 if (!(door_state & door_token))
5316 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5317 int kk_door = MAX(0, k2_door);
5318 int sync_frame = kk_door * door_delay_value;
5319 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5321 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5322 &g_src_x, &g_src_y);
5327 if (!door_panel_drawn[door_index])
5329 ClearRectangle(drawto, door_rect->x, door_rect->y,
5330 door_rect->width, door_rect->height);
5332 door_panel_drawn[door_index] = TRUE;
5335 // draw opening or closing door parts
5337 if (pos->step_xoffset < 0) // door part on right side
5340 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5343 if (dst_xx + width > door_rect->width)
5344 width = door_rect->width - dst_xx;
5346 else // door part on left side
5349 dst_xx = pos->x - kk * pos->step_xoffset;
5353 src_xx = ABS(dst_xx);
5357 width = g->width - src_xx;
5359 if (width > door_rect->width)
5360 width = door_rect->width;
5362 // printf("::: k == %d [%d] \n", k, start_step);
5365 if (pos->step_yoffset < 0) // door part on bottom side
5368 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5371 if (dst_yy + height > door_rect->height)
5372 height = door_rect->height - dst_yy;
5374 else // door part on top side
5377 dst_yy = pos->y - kk * pos->step_yoffset;
5381 src_yy = ABS(dst_yy);
5385 height = g->height - src_yy;
5388 src_x = g_src_x + src_xx;
5389 src_y = g_src_y + src_yy;
5391 dst_x = door_rect->x + dst_xx;
5392 dst_y = door_rect->y + dst_yy;
5394 is_panel_and_door_has_closed =
5397 panel_has_doors[door_index] &&
5398 k >= num_move_steps_doors_only - 1);
5400 if (width >= 0 && width <= g->width &&
5401 height >= 0 && height <= g->height &&
5402 !is_panel_and_door_has_closed)
5404 if (is_panel || !pos->draw_masked)
5405 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5408 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5412 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5414 if ((part_opening && (width < 0 || height < 0)) ||
5415 (part_closing && (width >= g->width && height >= g->height)))
5416 door_part_done[nr] = TRUE;
5418 // continue door part animations, but not panel after door has closed
5419 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5420 door_part_done_all = FALSE;
5423 if (!(door_state & DOOR_NO_DELAY))
5427 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5429 current_move_delay += max_step_delay;
5431 // prevent OS (Windows) from complaining about program not responding
5435 if (door_part_done_all)
5439 if (!(door_state & DOOR_NO_DELAY))
5441 // wait for specified door action post delay
5442 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5443 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5444 else if (door_state & DOOR_ACTION_1)
5445 door_delay_value = door_1.post_delay;
5446 else if (door_state & DOOR_ACTION_2)
5447 door_delay_value = door_2.post_delay;
5449 while (!DelayReached(&door_delay, door_delay_value))
5454 if (door_state & DOOR_ACTION_1)
5455 door1 = door_state & DOOR_ACTION_1;
5456 if (door_state & DOOR_ACTION_2)
5457 door2 = door_state & DOOR_ACTION_2;
5459 // draw masked border over door area
5460 DrawMaskedBorder(REDRAW_DOOR_1);
5461 DrawMaskedBorder(REDRAW_DOOR_2);
5463 ClearAutoRepeatKeyEvents();
5465 return (door1 | door2);
5468 static boolean useSpecialEditorDoor(void)
5470 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5471 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5473 // do not draw special editor door if editor border defined or redefined
5474 if (graphic_info[graphic].bitmap != NULL || redefined)
5477 // do not draw special editor door if global border defined to be empty
5478 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5481 // do not draw special editor door if viewport definitions do not match
5485 EY + EYSIZE != VY + VYSIZE)
5491 void DrawSpecialEditorDoor(void)
5493 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5494 int top_border_width = gfx1->width;
5495 int top_border_height = gfx1->height;
5496 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5497 int ex = EX - outer_border;
5498 int ey = EY - outer_border;
5499 int vy = VY - outer_border;
5500 int exsize = EXSIZE + 2 * outer_border;
5502 if (!useSpecialEditorDoor())
5505 // draw bigger level editor toolbox window
5506 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5507 top_border_width, top_border_height, ex, ey - top_border_height);
5508 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5509 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5511 redraw_mask |= REDRAW_ALL;
5514 void UndrawSpecialEditorDoor(void)
5516 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5517 int top_border_width = gfx1->width;
5518 int top_border_height = gfx1->height;
5519 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5520 int ex = EX - outer_border;
5521 int ey = EY - outer_border;
5522 int ey_top = ey - top_border_height;
5523 int exsize = EXSIZE + 2 * outer_border;
5524 int eysize = EYSIZE + 2 * outer_border;
5526 if (!useSpecialEditorDoor())
5529 // draw normal tape recorder window
5530 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5532 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5533 ex, ey_top, top_border_width, top_border_height,
5535 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5536 ex, ey, exsize, eysize, ex, ey);
5540 // if screen background is set to "[NONE]", clear editor toolbox window
5541 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5542 ClearRectangle(drawto, ex, ey, exsize, eysize);
5545 redraw_mask |= REDRAW_ALL;
5549 // ---------- new tool button stuff -------------------------------------------
5554 struct TextPosInfo *pos;
5556 boolean is_touch_button;
5558 } toolbutton_info[NUM_TOOL_BUTTONS] =
5561 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5562 TOOL_CTRL_ID_YES, FALSE, "yes"
5565 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5566 TOOL_CTRL_ID_NO, FALSE, "no"
5569 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5570 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5573 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5574 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5577 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5578 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5581 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5582 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5585 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5586 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5589 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5590 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5593 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5594 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5597 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5598 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5602 void CreateToolButtons(void)
5606 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5608 int graphic = toolbutton_info[i].graphic;
5609 struct GraphicInfo *gfx = &graphic_info[graphic];
5610 struct TextPosInfo *pos = toolbutton_info[i].pos;
5611 struct GadgetInfo *gi;
5612 Bitmap *deco_bitmap = None;
5613 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5614 unsigned int event_mask = GD_EVENT_RELEASED;
5615 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5616 int base_x = (is_touch_button ? 0 : DX);
5617 int base_y = (is_touch_button ? 0 : DY);
5618 int gd_x = gfx->src_x;
5619 int gd_y = gfx->src_y;
5620 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5621 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5626 if (global.use_envelope_request && !is_touch_button)
5628 setRequestPosition(&base_x, &base_y, TRUE);
5630 // check if request buttons are outside of envelope and fix, if needed
5631 if (x < 0 || x + gfx->width > request.width ||
5632 y < 0 || y + gfx->height > request.height)
5634 if (id == TOOL_CTRL_ID_YES)
5637 y = request.height - 2 * request.border_size - gfx->height;
5639 else if (id == TOOL_CTRL_ID_NO)
5641 x = request.width - 2 * request.border_size - gfx->width;
5642 y = request.height - 2 * request.border_size - gfx->height;
5644 else if (id == TOOL_CTRL_ID_CONFIRM)
5646 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5647 y = request.height - 2 * request.border_size - gfx->height;
5649 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5651 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5653 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5654 y = request.height - 2 * request.border_size - gfx->height * 2;
5656 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5657 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5662 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5664 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5666 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5667 pos->size, &deco_bitmap, &deco_x, &deco_y);
5668 deco_xpos = (gfx->width - pos->size) / 2;
5669 deco_ypos = (gfx->height - pos->size) / 2;
5672 gi = CreateGadget(GDI_CUSTOM_ID, id,
5673 GDI_IMAGE_ID, graphic,
5674 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5677 GDI_WIDTH, gfx->width,
5678 GDI_HEIGHT, gfx->height,
5679 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5680 GDI_STATE, GD_BUTTON_UNPRESSED,
5681 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5682 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5683 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5684 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5685 GDI_DECORATION_SIZE, pos->size, pos->size,
5686 GDI_DECORATION_SHIFTING, 1, 1,
5687 GDI_DIRECT_DRAW, FALSE,
5688 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5689 GDI_EVENT_MASK, event_mask,
5690 GDI_CALLBACK_ACTION, HandleToolButtons,
5694 Error(ERR_EXIT, "cannot create gadget");
5696 tool_gadget[id] = gi;
5700 void FreeToolButtons(void)
5704 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5705 FreeGadget(tool_gadget[i]);
5708 static void UnmapToolButtons(void)
5712 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5713 UnmapGadget(tool_gadget[i]);
5716 static void HandleToolButtons(struct GadgetInfo *gi)
5718 request_gadget_id = gi->custom_id;
5721 static struct Mapping_EM_to_RND_object
5724 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5725 boolean is_backside; // backside of moving element
5731 em_object_mapping_list[GAME_TILE_MAX + 1] =
5734 Zborder, FALSE, FALSE,
5738 Zplayer, FALSE, FALSE,
5747 Ztank, FALSE, FALSE,
5751 Zeater, FALSE, FALSE,
5755 Zdynamite, FALSE, FALSE,
5759 Zboom, FALSE, FALSE,
5764 Xchain, FALSE, FALSE,
5765 EL_DEFAULT, ACTION_EXPLODING, -1
5768 Xboom_bug, FALSE, FALSE,
5769 EL_BUG, ACTION_EXPLODING, -1
5772 Xboom_tank, FALSE, FALSE,
5773 EL_SPACESHIP, ACTION_EXPLODING, -1
5776 Xboom_android, FALSE, FALSE,
5777 EL_EMC_ANDROID, ACTION_OTHER, -1
5780 Xboom_1, FALSE, FALSE,
5781 EL_DEFAULT, ACTION_EXPLODING, -1
5784 Xboom_2, FALSE, FALSE,
5785 EL_DEFAULT, ACTION_EXPLODING, -1
5789 Xblank, TRUE, FALSE,
5794 Xsplash_e, FALSE, FALSE,
5795 EL_ACID_SPLASH_RIGHT, -1, -1
5798 Xsplash_w, FALSE, FALSE,
5799 EL_ACID_SPLASH_LEFT, -1, -1
5803 Xplant, TRUE, FALSE,
5804 EL_EMC_PLANT, -1, -1
5807 Yplant, FALSE, FALSE,
5808 EL_EMC_PLANT, -1, -1
5812 Xacid_1, TRUE, FALSE,
5816 Xacid_2, FALSE, FALSE,
5820 Xacid_3, FALSE, FALSE,
5824 Xacid_4, FALSE, FALSE,
5828 Xacid_5, FALSE, FALSE,
5832 Xacid_6, FALSE, FALSE,
5836 Xacid_7, FALSE, FALSE,
5840 Xacid_8, FALSE, FALSE,
5845 Xfake_acid_1, TRUE, FALSE,
5846 EL_EMC_FAKE_ACID, -1, -1
5849 Xfake_acid_2, FALSE, FALSE,
5850 EL_EMC_FAKE_ACID, -1, -1
5853 Xfake_acid_3, FALSE, FALSE,
5854 EL_EMC_FAKE_ACID, -1, -1
5857 Xfake_acid_4, FALSE, FALSE,
5858 EL_EMC_FAKE_ACID, -1, -1
5861 Xfake_acid_5, FALSE, FALSE,
5862 EL_EMC_FAKE_ACID, -1, -1
5865 Xfake_acid_6, FALSE, FALSE,
5866 EL_EMC_FAKE_ACID, -1, -1
5869 Xfake_acid_7, FALSE, FALSE,
5870 EL_EMC_FAKE_ACID, -1, -1
5873 Xfake_acid_8, FALSE, FALSE,
5874 EL_EMC_FAKE_ACID, -1, -1
5878 Xgrass, TRUE, FALSE,
5879 EL_EMC_GRASS, -1, -1
5882 Ygrass_nB, FALSE, FALSE,
5883 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5886 Ygrass_eB, FALSE, FALSE,
5887 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5890 Ygrass_sB, FALSE, FALSE,
5891 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5894 Ygrass_wB, FALSE, FALSE,
5895 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5903 Ydirt_nB, FALSE, FALSE,
5904 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5907 Ydirt_eB, FALSE, FALSE,
5908 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5911 Ydirt_sB, FALSE, FALSE,
5912 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5915 Ydirt_wB, FALSE, FALSE,
5916 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5920 Xandroid, TRUE, FALSE,
5921 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5924 Xandroid_1_n, FALSE, FALSE,
5925 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5928 Xandroid_2_n, FALSE, FALSE,
5929 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5932 Xandroid_1_e, FALSE, FALSE,
5933 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5936 Xandroid_2_e, FALSE, FALSE,
5937 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5940 Xandroid_1_w, FALSE, FALSE,
5941 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5944 Xandroid_2_w, FALSE, FALSE,
5945 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5948 Xandroid_1_s, FALSE, FALSE,
5949 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5952 Xandroid_2_s, FALSE, FALSE,
5953 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5956 Yandroid_n, FALSE, FALSE,
5957 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5960 Yandroid_nB, FALSE, TRUE,
5961 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5964 Yandroid_ne, FALSE, FALSE,
5965 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5968 Yandroid_neB, FALSE, TRUE,
5969 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5972 Yandroid_e, FALSE, FALSE,
5973 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5976 Yandroid_eB, FALSE, TRUE,
5977 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5980 Yandroid_se, FALSE, FALSE,
5981 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5984 Yandroid_seB, FALSE, TRUE,
5985 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5988 Yandroid_s, FALSE, FALSE,
5989 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5992 Yandroid_sB, FALSE, TRUE,
5993 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5996 Yandroid_sw, FALSE, FALSE,
5997 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6000 Yandroid_swB, FALSE, TRUE,
6001 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6004 Yandroid_w, FALSE, FALSE,
6005 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6008 Yandroid_wB, FALSE, TRUE,
6009 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6012 Yandroid_nw, FALSE, FALSE,
6013 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6016 Yandroid_nwB, FALSE, TRUE,
6017 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6021 Xeater_n, TRUE, FALSE,
6022 EL_YAMYAM_UP, -1, -1
6025 Xeater_e, TRUE, FALSE,
6026 EL_YAMYAM_RIGHT, -1, -1
6029 Xeater_w, TRUE, FALSE,
6030 EL_YAMYAM_LEFT, -1, -1
6033 Xeater_s, TRUE, FALSE,
6034 EL_YAMYAM_DOWN, -1, -1
6037 Yeater_n, FALSE, FALSE,
6038 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6041 Yeater_nB, FALSE, TRUE,
6042 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6045 Yeater_e, FALSE, FALSE,
6046 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6049 Yeater_eB, FALSE, TRUE,
6050 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6053 Yeater_s, FALSE, FALSE,
6054 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6057 Yeater_sB, FALSE, TRUE,
6058 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6061 Yeater_w, FALSE, FALSE,
6062 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6065 Yeater_wB, FALSE, TRUE,
6066 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6069 Yeater_stone, FALSE, FALSE,
6070 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6073 Yeater_spring, FALSE, FALSE,
6074 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6078 Xalien, TRUE, FALSE,
6082 Xalien_pause, FALSE, FALSE,
6086 Yalien_n, FALSE, FALSE,
6087 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6090 Yalien_nB, FALSE, TRUE,
6091 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6094 Yalien_e, FALSE, FALSE,
6095 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6098 Yalien_eB, FALSE, TRUE,
6099 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6102 Yalien_s, FALSE, FALSE,
6103 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6106 Yalien_sB, FALSE, TRUE,
6107 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6110 Yalien_w, FALSE, FALSE,
6111 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6114 Yalien_wB, FALSE, TRUE,
6115 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6118 Yalien_stone, FALSE, FALSE,
6119 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6122 Yalien_spring, FALSE, FALSE,
6123 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6127 Xbug_1_n, TRUE, FALSE,
6131 Xbug_1_e, TRUE, FALSE,
6132 EL_BUG_RIGHT, -1, -1
6135 Xbug_1_s, TRUE, FALSE,
6139 Xbug_1_w, TRUE, FALSE,
6143 Xbug_2_n, FALSE, FALSE,
6147 Xbug_2_e, FALSE, FALSE,
6148 EL_BUG_RIGHT, -1, -1
6151 Xbug_2_s, FALSE, FALSE,
6155 Xbug_2_w, FALSE, FALSE,
6159 Ybug_n, FALSE, FALSE,
6160 EL_BUG, ACTION_MOVING, MV_BIT_UP
6163 Ybug_nB, FALSE, TRUE,
6164 EL_BUG, ACTION_MOVING, MV_BIT_UP
6167 Ybug_e, FALSE, FALSE,
6168 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6171 Ybug_eB, FALSE, TRUE,
6172 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6175 Ybug_s, FALSE, FALSE,
6176 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6179 Ybug_sB, FALSE, TRUE,
6180 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6183 Ybug_w, FALSE, FALSE,
6184 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6187 Ybug_wB, FALSE, TRUE,
6188 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6191 Ybug_w_n, FALSE, FALSE,
6192 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6195 Ybug_n_e, FALSE, FALSE,
6196 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6199 Ybug_e_s, FALSE, FALSE,
6200 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6203 Ybug_s_w, FALSE, FALSE,
6204 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6207 Ybug_e_n, FALSE, FALSE,
6208 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6211 Ybug_s_e, FALSE, FALSE,
6212 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6215 Ybug_w_s, FALSE, FALSE,
6216 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6219 Ybug_n_w, FALSE, FALSE,
6220 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6223 Ybug_stone, FALSE, FALSE,
6224 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6227 Ybug_spring, FALSE, FALSE,
6228 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6232 Xtank_1_n, TRUE, FALSE,
6233 EL_SPACESHIP_UP, -1, -1
6236 Xtank_1_e, TRUE, FALSE,
6237 EL_SPACESHIP_RIGHT, -1, -1
6240 Xtank_1_s, TRUE, FALSE,
6241 EL_SPACESHIP_DOWN, -1, -1
6244 Xtank_1_w, TRUE, FALSE,
6245 EL_SPACESHIP_LEFT, -1, -1
6248 Xtank_2_n, FALSE, FALSE,
6249 EL_SPACESHIP_UP, -1, -1
6252 Xtank_2_e, FALSE, FALSE,
6253 EL_SPACESHIP_RIGHT, -1, -1
6256 Xtank_2_s, FALSE, FALSE,
6257 EL_SPACESHIP_DOWN, -1, -1
6260 Xtank_2_w, FALSE, FALSE,
6261 EL_SPACESHIP_LEFT, -1, -1
6264 Ytank_n, FALSE, FALSE,
6265 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6268 Ytank_nB, FALSE, TRUE,
6269 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6272 Ytank_e, FALSE, FALSE,
6273 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6276 Ytank_eB, FALSE, TRUE,
6277 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6280 Ytank_s, FALSE, FALSE,
6281 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6284 Ytank_sB, FALSE, TRUE,
6285 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6288 Ytank_w, FALSE, FALSE,
6289 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6292 Ytank_wB, FALSE, TRUE,
6293 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6296 Ytank_w_n, FALSE, FALSE,
6297 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6300 Ytank_n_e, FALSE, FALSE,
6301 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6304 Ytank_e_s, FALSE, FALSE,
6305 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6308 Ytank_s_w, FALSE, FALSE,
6309 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6312 Ytank_e_n, FALSE, FALSE,
6313 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6316 Ytank_s_e, FALSE, FALSE,
6317 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6320 Ytank_w_s, FALSE, FALSE,
6321 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6324 Ytank_n_w, FALSE, FALSE,
6325 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6328 Ytank_stone, FALSE, FALSE,
6329 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6332 Ytank_spring, FALSE, FALSE,
6333 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6337 Xemerald, TRUE, FALSE,
6341 Xemerald_pause, FALSE, FALSE,
6345 Xemerald_fall, FALSE, FALSE,
6349 Xemerald_shine, FALSE, FALSE,
6350 EL_EMERALD, ACTION_TWINKLING, -1
6353 Yemerald_s, FALSE, FALSE,
6354 EL_EMERALD, ACTION_FALLING, -1
6357 Yemerald_sB, FALSE, TRUE,
6358 EL_EMERALD, ACTION_FALLING, -1
6361 Yemerald_e, FALSE, FALSE,
6362 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6365 Yemerald_eB, FALSE, TRUE,
6366 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6369 Yemerald_w, FALSE, FALSE,
6370 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6373 Yemerald_wB, FALSE, TRUE,
6374 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6377 Yemerald_blank, FALSE, FALSE,
6378 EL_EMERALD, ACTION_COLLECTING, -1
6382 Xdiamond, TRUE, FALSE,
6386 Xdiamond_pause, FALSE, FALSE,
6390 Xdiamond_fall, FALSE, FALSE,
6394 Xdiamond_shine, FALSE, FALSE,
6395 EL_DIAMOND, ACTION_TWINKLING, -1
6398 Ydiamond_s, FALSE, FALSE,
6399 EL_DIAMOND, ACTION_FALLING, -1
6402 Ydiamond_sB, FALSE, TRUE,
6403 EL_DIAMOND, ACTION_FALLING, -1
6406 Ydiamond_e, FALSE, FALSE,
6407 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6410 Ydiamond_eB, FALSE, TRUE,
6411 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6414 Ydiamond_w, FALSE, FALSE,
6415 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6418 Ydiamond_wB, FALSE, TRUE,
6419 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6422 Ydiamond_blank, FALSE, FALSE,
6423 EL_DIAMOND, ACTION_COLLECTING, -1
6426 Ydiamond_stone, FALSE, FALSE,
6427 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6431 Xstone, TRUE, FALSE,
6435 Xstone_pause, FALSE, FALSE,
6439 Xstone_fall, FALSE, FALSE,
6443 Ystone_s, FALSE, FALSE,
6444 EL_ROCK, ACTION_FALLING, -1
6447 Ystone_sB, FALSE, TRUE,
6448 EL_ROCK, ACTION_FALLING, -1
6451 Ystone_e, FALSE, FALSE,
6452 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6455 Ystone_eB, FALSE, TRUE,
6456 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6459 Ystone_w, FALSE, FALSE,
6460 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6463 Ystone_wB, FALSE, TRUE,
6464 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6472 Xbomb_pause, FALSE, FALSE,
6476 Xbomb_fall, FALSE, FALSE,
6480 Ybomb_s, FALSE, FALSE,
6481 EL_BOMB, ACTION_FALLING, -1
6484 Ybomb_sB, FALSE, TRUE,
6485 EL_BOMB, ACTION_FALLING, -1
6488 Ybomb_e, FALSE, FALSE,
6489 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6492 Ybomb_eB, FALSE, TRUE,
6493 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6496 Ybomb_w, FALSE, FALSE,
6497 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6500 Ybomb_wB, FALSE, TRUE,
6501 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6504 Ybomb_blank, FALSE, FALSE,
6505 EL_BOMB, ACTION_ACTIVATING, -1
6513 Xnut_pause, FALSE, FALSE,
6517 Xnut_fall, FALSE, FALSE,
6521 Ynut_s, FALSE, FALSE,
6522 EL_NUT, ACTION_FALLING, -1
6525 Ynut_sB, FALSE, TRUE,
6526 EL_NUT, ACTION_FALLING, -1
6529 Ynut_e, FALSE, FALSE,
6530 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6533 Ynut_eB, FALSE, TRUE,
6534 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6537 Ynut_w, FALSE, FALSE,
6538 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6541 Ynut_wB, FALSE, TRUE,
6542 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6545 Ynut_stone, FALSE, FALSE,
6546 EL_NUT, ACTION_BREAKING, -1
6550 Xspring, TRUE, FALSE,
6554 Xspring_pause, FALSE, FALSE,
6558 Xspring_e, FALSE, FALSE,
6562 Xspring_w, FALSE, FALSE,
6566 Xspring_fall, FALSE, FALSE,
6570 Yspring_s, FALSE, FALSE,
6571 EL_SPRING, ACTION_FALLING, -1
6574 Yspring_sB, FALSE, TRUE,
6575 EL_SPRING, ACTION_FALLING, -1
6578 Yspring_e, FALSE, FALSE,
6579 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6582 Yspring_eB, FALSE, TRUE,
6583 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6586 Yspring_w, FALSE, FALSE,
6587 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6590 Yspring_wB, FALSE, TRUE,
6591 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6594 Yspring_alien_e, FALSE, FALSE,
6595 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6598 Yspring_alien_eB, FALSE, TRUE,
6599 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6602 Yspring_alien_w, FALSE, FALSE,
6603 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6606 Yspring_alien_wB, FALSE, TRUE,
6607 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6611 Xpush_emerald_e, FALSE, FALSE,
6612 EL_EMERALD, -1, MV_BIT_RIGHT
6615 Xpush_emerald_w, FALSE, FALSE,
6616 EL_EMERALD, -1, MV_BIT_LEFT
6619 Xpush_diamond_e, FALSE, FALSE,
6620 EL_DIAMOND, -1, MV_BIT_RIGHT
6623 Xpush_diamond_w, FALSE, FALSE,
6624 EL_DIAMOND, -1, MV_BIT_LEFT
6627 Xpush_stone_e, FALSE, FALSE,
6628 EL_ROCK, -1, MV_BIT_RIGHT
6631 Xpush_stone_w, FALSE, FALSE,
6632 EL_ROCK, -1, MV_BIT_LEFT
6635 Xpush_bomb_e, FALSE, FALSE,
6636 EL_BOMB, -1, MV_BIT_RIGHT
6639 Xpush_bomb_w, FALSE, FALSE,
6640 EL_BOMB, -1, MV_BIT_LEFT
6643 Xpush_nut_e, FALSE, FALSE,
6644 EL_NUT, -1, MV_BIT_RIGHT
6647 Xpush_nut_w, FALSE, FALSE,
6648 EL_NUT, -1, MV_BIT_LEFT
6651 Xpush_spring_e, FALSE, FALSE,
6652 EL_SPRING, -1, MV_BIT_RIGHT
6655 Xpush_spring_w, FALSE, FALSE,
6656 EL_SPRING, -1, MV_BIT_LEFT
6660 Xdynamite, TRUE, FALSE,
6661 EL_EM_DYNAMITE, -1, -1
6664 Ydynamite_blank, FALSE, FALSE,
6665 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6668 Xdynamite_1, TRUE, FALSE,
6669 EL_EM_DYNAMITE_ACTIVE, -1, -1
6672 Xdynamite_2, FALSE, FALSE,
6673 EL_EM_DYNAMITE_ACTIVE, -1, -1
6676 Xdynamite_3, FALSE, FALSE,
6677 EL_EM_DYNAMITE_ACTIVE, -1, -1
6680 Xdynamite_4, FALSE, FALSE,
6681 EL_EM_DYNAMITE_ACTIVE, -1, -1
6685 Xkey_1, TRUE, FALSE,
6689 Xkey_2, TRUE, FALSE,
6693 Xkey_3, TRUE, FALSE,
6697 Xkey_4, TRUE, FALSE,
6701 Xkey_5, TRUE, FALSE,
6702 EL_EMC_KEY_5, -1, -1
6705 Xkey_6, TRUE, FALSE,
6706 EL_EMC_KEY_6, -1, -1
6709 Xkey_7, TRUE, FALSE,
6710 EL_EMC_KEY_7, -1, -1
6713 Xkey_8, TRUE, FALSE,
6714 EL_EMC_KEY_8, -1, -1
6718 Xdoor_1, TRUE, FALSE,
6719 EL_EM_GATE_1, -1, -1
6722 Xdoor_2, TRUE, FALSE,
6723 EL_EM_GATE_2, -1, -1
6726 Xdoor_3, TRUE, FALSE,
6727 EL_EM_GATE_3, -1, -1
6730 Xdoor_4, TRUE, FALSE,
6731 EL_EM_GATE_4, -1, -1
6734 Xdoor_5, TRUE, FALSE,
6735 EL_EMC_GATE_5, -1, -1
6738 Xdoor_6, TRUE, FALSE,
6739 EL_EMC_GATE_6, -1, -1
6742 Xdoor_7, TRUE, FALSE,
6743 EL_EMC_GATE_7, -1, -1
6746 Xdoor_8, TRUE, FALSE,
6747 EL_EMC_GATE_8, -1, -1
6751 Xfake_door_1, TRUE, FALSE,
6752 EL_EM_GATE_1_GRAY, -1, -1
6755 Xfake_door_2, TRUE, FALSE,
6756 EL_EM_GATE_2_GRAY, -1, -1
6759 Xfake_door_3, TRUE, FALSE,
6760 EL_EM_GATE_3_GRAY, -1, -1
6763 Xfake_door_4, TRUE, FALSE,
6764 EL_EM_GATE_4_GRAY, -1, -1
6767 Xfake_door_5, TRUE, FALSE,
6768 EL_EMC_GATE_5_GRAY, -1, -1
6771 Xfake_door_6, TRUE, FALSE,
6772 EL_EMC_GATE_6_GRAY, -1, -1
6775 Xfake_door_7, TRUE, FALSE,
6776 EL_EMC_GATE_7_GRAY, -1, -1
6779 Xfake_door_8, TRUE, FALSE,
6780 EL_EMC_GATE_8_GRAY, -1, -1
6784 Xballoon, TRUE, FALSE,
6788 Yballoon_n, FALSE, FALSE,
6789 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6792 Yballoon_nB, FALSE, TRUE,
6793 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6796 Yballoon_e, FALSE, FALSE,
6797 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6800 Yballoon_eB, FALSE, TRUE,
6801 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6804 Yballoon_s, FALSE, FALSE,
6805 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6808 Yballoon_sB, FALSE, TRUE,
6809 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6812 Yballoon_w, FALSE, FALSE,
6813 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6816 Yballoon_wB, FALSE, TRUE,
6817 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6821 Xball_1, TRUE, FALSE,
6822 EL_EMC_MAGIC_BALL, -1, -1
6825 Yball_1, FALSE, FALSE,
6826 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6829 Xball_2, FALSE, FALSE,
6830 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6833 Yball_2, FALSE, FALSE,
6834 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6837 Yball_blank, FALSE, FALSE,
6838 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6842 Xamoeba_1, TRUE, FALSE,
6843 EL_AMOEBA_DRY, ACTION_OTHER, -1
6846 Xamoeba_2, FALSE, FALSE,
6847 EL_AMOEBA_DRY, ACTION_OTHER, -1
6850 Xamoeba_3, FALSE, FALSE,
6851 EL_AMOEBA_DRY, ACTION_OTHER, -1
6854 Xamoeba_4, FALSE, FALSE,
6855 EL_AMOEBA_DRY, ACTION_OTHER, -1
6858 Xamoeba_5, TRUE, FALSE,
6859 EL_AMOEBA_WET, ACTION_OTHER, -1
6862 Xamoeba_6, FALSE, FALSE,
6863 EL_AMOEBA_WET, ACTION_OTHER, -1
6866 Xamoeba_7, FALSE, FALSE,
6867 EL_AMOEBA_WET, ACTION_OTHER, -1
6870 Xamoeba_8, FALSE, FALSE,
6871 EL_AMOEBA_WET, ACTION_OTHER, -1
6875 Xdrip, FALSE, FALSE,
6876 EL_AMOEBA_DROP, ACTION_GROWING, -1
6879 Xdrip_fall, TRUE, FALSE,
6880 EL_AMOEBA_DROP, -1, -1
6883 Xdrip_stretch, FALSE, FALSE,
6884 EL_AMOEBA_DROP, ACTION_FALLING, -1
6887 Xdrip_stretchB, FALSE, TRUE,
6888 EL_AMOEBA_DROP, ACTION_FALLING, -1
6891 Ydrip_1_s, FALSE, FALSE,
6892 EL_AMOEBA_DROP, ACTION_FALLING, -1
6895 Ydrip_1_sB, FALSE, TRUE,
6896 EL_AMOEBA_DROP, ACTION_FALLING, -1
6899 Ydrip_2_s, FALSE, FALSE,
6900 EL_AMOEBA_DROP, ACTION_FALLING, -1
6903 Ydrip_2_sB, FALSE, TRUE,
6904 EL_AMOEBA_DROP, ACTION_FALLING, -1
6908 Xwonderwall, TRUE, FALSE,
6909 EL_MAGIC_WALL, -1, -1
6912 Ywonderwall, FALSE, FALSE,
6913 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6917 Xwheel, TRUE, FALSE,
6918 EL_ROBOT_WHEEL, -1, -1
6921 Ywheel, FALSE, FALSE,
6922 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6926 Xswitch, TRUE, FALSE,
6927 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6930 Yswitch, FALSE, FALSE,
6931 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6935 Xbumper, TRUE, FALSE,
6936 EL_EMC_SPRING_BUMPER, -1, -1
6939 Ybumper, FALSE, FALSE,
6940 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6944 Xacid_nw, TRUE, FALSE,
6945 EL_ACID_POOL_TOPLEFT, -1, -1
6948 Xacid_ne, TRUE, FALSE,
6949 EL_ACID_POOL_TOPRIGHT, -1, -1
6952 Xacid_sw, TRUE, FALSE,
6953 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6956 Xacid_s, TRUE, FALSE,
6957 EL_ACID_POOL_BOTTOM, -1, -1
6960 Xacid_se, TRUE, FALSE,
6961 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6965 Xfake_blank, TRUE, FALSE,
6966 EL_INVISIBLE_WALL, -1, -1
6969 Yfake_blank, FALSE, FALSE,
6970 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6974 Xfake_grass, TRUE, FALSE,
6975 EL_EMC_FAKE_GRASS, -1, -1
6978 Yfake_grass, FALSE, FALSE,
6979 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6983 Xfake_amoeba, TRUE, FALSE,
6984 EL_EMC_DRIPPER, -1, -1
6987 Yfake_amoeba, FALSE, FALSE,
6988 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6992 Xlenses, TRUE, FALSE,
6993 EL_EMC_LENSES, -1, -1
6997 Xmagnify, TRUE, FALSE,
6998 EL_EMC_MAGNIFIER, -1, -1
7003 EL_QUICKSAND_EMPTY, -1, -1
7006 Xsand_stone, TRUE, FALSE,
7007 EL_QUICKSAND_FULL, -1, -1
7010 Xsand_stonein_1, FALSE, TRUE,
7011 EL_ROCK, ACTION_FILLING, -1
7014 Xsand_stonein_2, FALSE, TRUE,
7015 EL_ROCK, ACTION_FILLING, -1
7018 Xsand_stonein_3, FALSE, TRUE,
7019 EL_ROCK, ACTION_FILLING, -1
7022 Xsand_stonein_4, FALSE, TRUE,
7023 EL_ROCK, ACTION_FILLING, -1
7026 Xsand_sandstone_1, FALSE, FALSE,
7027 EL_QUICKSAND_FILLING, -1, -1
7030 Xsand_sandstone_2, FALSE, FALSE,
7031 EL_QUICKSAND_FILLING, -1, -1
7034 Xsand_sandstone_3, FALSE, FALSE,
7035 EL_QUICKSAND_FILLING, -1, -1
7038 Xsand_sandstone_4, FALSE, FALSE,
7039 EL_QUICKSAND_FILLING, -1, -1
7042 Xsand_stonesand_1, FALSE, FALSE,
7043 EL_QUICKSAND_EMPTYING, -1, -1
7046 Xsand_stonesand_2, FALSE, FALSE,
7047 EL_QUICKSAND_EMPTYING, -1, -1
7050 Xsand_stonesand_3, FALSE, FALSE,
7051 EL_QUICKSAND_EMPTYING, -1, -1
7054 Xsand_stonesand_4, FALSE, FALSE,
7055 EL_QUICKSAND_EMPTYING, -1, -1
7058 Xsand_stoneout_1, FALSE, FALSE,
7059 EL_ROCK, ACTION_EMPTYING, -1
7062 Xsand_stoneout_2, FALSE, FALSE,
7063 EL_ROCK, ACTION_EMPTYING, -1
7066 Xsand_stonesand_quickout_1, FALSE, FALSE,
7067 EL_QUICKSAND_EMPTYING, -1, -1
7070 Xsand_stonesand_quickout_2, FALSE, FALSE,
7071 EL_QUICKSAND_EMPTYING, -1, -1
7075 Xslide_ns, TRUE, FALSE,
7076 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7079 Yslide_ns_blank, FALSE, FALSE,
7080 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7083 Xslide_ew, TRUE, FALSE,
7084 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7087 Yslide_ew_blank, FALSE, FALSE,
7088 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7092 Xwind_n, TRUE, FALSE,
7093 EL_BALLOON_SWITCH_UP, -1, -1
7096 Xwind_e, TRUE, FALSE,
7097 EL_BALLOON_SWITCH_RIGHT, -1, -1
7100 Xwind_s, TRUE, FALSE,
7101 EL_BALLOON_SWITCH_DOWN, -1, -1
7104 Xwind_w, TRUE, FALSE,
7105 EL_BALLOON_SWITCH_LEFT, -1, -1
7108 Xwind_any, TRUE, FALSE,
7109 EL_BALLOON_SWITCH_ANY, -1, -1
7112 Xwind_stop, TRUE, FALSE,
7113 EL_BALLOON_SWITCH_NONE, -1, -1
7118 EL_EM_EXIT_CLOSED, -1, -1
7121 Xexit_1, TRUE, FALSE,
7122 EL_EM_EXIT_OPEN, -1, -1
7125 Xexit_2, FALSE, FALSE,
7126 EL_EM_EXIT_OPEN, -1, -1
7129 Xexit_3, FALSE, FALSE,
7130 EL_EM_EXIT_OPEN, -1, -1
7134 Xpause, FALSE, FALSE,
7139 Xwall_1, TRUE, FALSE,
7143 Xwall_2, TRUE, FALSE,
7144 EL_EMC_WALL_14, -1, -1
7147 Xwall_3, TRUE, FALSE,
7148 EL_EMC_WALL_15, -1, -1
7151 Xwall_4, TRUE, FALSE,
7152 EL_EMC_WALL_16, -1, -1
7156 Xroundwall_1, TRUE, FALSE,
7157 EL_WALL_SLIPPERY, -1, -1
7160 Xroundwall_2, TRUE, FALSE,
7161 EL_EMC_WALL_SLIPPERY_2, -1, -1
7164 Xroundwall_3, TRUE, FALSE,
7165 EL_EMC_WALL_SLIPPERY_3, -1, -1
7168 Xroundwall_4, TRUE, FALSE,
7169 EL_EMC_WALL_SLIPPERY_4, -1, -1
7173 Xsteel_1, TRUE, FALSE,
7174 EL_STEELWALL, -1, -1
7177 Xsteel_2, TRUE, FALSE,
7178 EL_EMC_STEELWALL_2, -1, -1
7181 Xsteel_3, TRUE, FALSE,
7182 EL_EMC_STEELWALL_3, -1, -1
7185 Xsteel_4, TRUE, FALSE,
7186 EL_EMC_STEELWALL_4, -1, -1
7190 Xdecor_1, TRUE, FALSE,
7191 EL_EMC_WALL_8, -1, -1
7194 Xdecor_2, TRUE, FALSE,
7195 EL_EMC_WALL_6, -1, -1
7198 Xdecor_3, TRUE, FALSE,
7199 EL_EMC_WALL_4, -1, -1
7202 Xdecor_4, TRUE, FALSE,
7203 EL_EMC_WALL_7, -1, -1
7206 Xdecor_5, TRUE, FALSE,
7207 EL_EMC_WALL_5, -1, -1
7210 Xdecor_6, TRUE, FALSE,
7211 EL_EMC_WALL_9, -1, -1
7214 Xdecor_7, TRUE, FALSE,
7215 EL_EMC_WALL_10, -1, -1
7218 Xdecor_8, TRUE, FALSE,
7219 EL_EMC_WALL_1, -1, -1
7222 Xdecor_9, TRUE, FALSE,
7223 EL_EMC_WALL_2, -1, -1
7226 Xdecor_10, TRUE, FALSE,
7227 EL_EMC_WALL_3, -1, -1
7230 Xdecor_11, TRUE, FALSE,
7231 EL_EMC_WALL_11, -1, -1
7234 Xdecor_12, TRUE, FALSE,
7235 EL_EMC_WALL_12, -1, -1
7239 Xalpha_0, TRUE, FALSE,
7240 EL_CHAR('0'), -1, -1
7243 Xalpha_1, TRUE, FALSE,
7244 EL_CHAR('1'), -1, -1
7247 Xalpha_2, TRUE, FALSE,
7248 EL_CHAR('2'), -1, -1
7251 Xalpha_3, TRUE, FALSE,
7252 EL_CHAR('3'), -1, -1
7255 Xalpha_4, TRUE, FALSE,
7256 EL_CHAR('4'), -1, -1
7259 Xalpha_5, TRUE, FALSE,
7260 EL_CHAR('5'), -1, -1
7263 Xalpha_6, TRUE, FALSE,
7264 EL_CHAR('6'), -1, -1
7267 Xalpha_7, TRUE, FALSE,
7268 EL_CHAR('7'), -1, -1
7271 Xalpha_8, TRUE, FALSE,
7272 EL_CHAR('8'), -1, -1
7275 Xalpha_9, TRUE, FALSE,
7276 EL_CHAR('9'), -1, -1
7279 Xalpha_excla, TRUE, FALSE,
7280 EL_CHAR('!'), -1, -1
7283 Xalpha_apost, TRUE, FALSE,
7284 EL_CHAR('\''), -1, -1
7287 Xalpha_comma, TRUE, FALSE,
7288 EL_CHAR(','), -1, -1
7291 Xalpha_minus, TRUE, FALSE,
7292 EL_CHAR('-'), -1, -1
7295 Xalpha_perio, TRUE, FALSE,
7296 EL_CHAR('.'), -1, -1
7299 Xalpha_colon, TRUE, FALSE,
7300 EL_CHAR(':'), -1, -1
7303 Xalpha_quest, TRUE, FALSE,
7304 EL_CHAR('?'), -1, -1
7307 Xalpha_a, TRUE, FALSE,
7308 EL_CHAR('A'), -1, -1
7311 Xalpha_b, TRUE, FALSE,
7312 EL_CHAR('B'), -1, -1
7315 Xalpha_c, TRUE, FALSE,
7316 EL_CHAR('C'), -1, -1
7319 Xalpha_d, TRUE, FALSE,
7320 EL_CHAR('D'), -1, -1
7323 Xalpha_e, TRUE, FALSE,
7324 EL_CHAR('E'), -1, -1
7327 Xalpha_f, TRUE, FALSE,
7328 EL_CHAR('F'), -1, -1
7331 Xalpha_g, TRUE, FALSE,
7332 EL_CHAR('G'), -1, -1
7335 Xalpha_h, TRUE, FALSE,
7336 EL_CHAR('H'), -1, -1
7339 Xalpha_i, TRUE, FALSE,
7340 EL_CHAR('I'), -1, -1
7343 Xalpha_j, TRUE, FALSE,
7344 EL_CHAR('J'), -1, -1
7347 Xalpha_k, TRUE, FALSE,
7348 EL_CHAR('K'), -1, -1
7351 Xalpha_l, TRUE, FALSE,
7352 EL_CHAR('L'), -1, -1
7355 Xalpha_m, TRUE, FALSE,
7356 EL_CHAR('M'), -1, -1
7359 Xalpha_n, TRUE, FALSE,
7360 EL_CHAR('N'), -1, -1
7363 Xalpha_o, TRUE, FALSE,
7364 EL_CHAR('O'), -1, -1
7367 Xalpha_p, TRUE, FALSE,
7368 EL_CHAR('P'), -1, -1
7371 Xalpha_q, TRUE, FALSE,
7372 EL_CHAR('Q'), -1, -1
7375 Xalpha_r, TRUE, FALSE,
7376 EL_CHAR('R'), -1, -1
7379 Xalpha_s, TRUE, FALSE,
7380 EL_CHAR('S'), -1, -1
7383 Xalpha_t, TRUE, FALSE,
7384 EL_CHAR('T'), -1, -1
7387 Xalpha_u, TRUE, FALSE,
7388 EL_CHAR('U'), -1, -1
7391 Xalpha_v, TRUE, FALSE,
7392 EL_CHAR('V'), -1, -1
7395 Xalpha_w, TRUE, FALSE,
7396 EL_CHAR('W'), -1, -1
7399 Xalpha_x, TRUE, FALSE,
7400 EL_CHAR('X'), -1, -1
7403 Xalpha_y, TRUE, FALSE,
7404 EL_CHAR('Y'), -1, -1
7407 Xalpha_z, TRUE, FALSE,
7408 EL_CHAR('Z'), -1, -1
7411 Xalpha_arrow_e, TRUE, FALSE,
7412 EL_CHAR('>'), -1, -1
7415 Xalpha_arrow_w, TRUE, FALSE,
7416 EL_CHAR('<'), -1, -1
7419 Xalpha_copyr, TRUE, FALSE,
7420 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7424 Ykey_1_blank, FALSE, FALSE,
7425 EL_EM_KEY_1, ACTION_COLLECTING, -1
7428 Ykey_2_blank, FALSE, FALSE,
7429 EL_EM_KEY_2, ACTION_COLLECTING, -1
7432 Ykey_3_blank, FALSE, FALSE,
7433 EL_EM_KEY_3, ACTION_COLLECTING, -1
7436 Ykey_4_blank, FALSE, FALSE,
7437 EL_EM_KEY_4, ACTION_COLLECTING, -1
7440 Ykey_5_blank, FALSE, FALSE,
7441 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7444 Ykey_6_blank, FALSE, FALSE,
7445 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7448 Ykey_7_blank, FALSE, FALSE,
7449 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7452 Ykey_8_blank, FALSE, FALSE,
7453 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7456 Ylenses_blank, FALSE, FALSE,
7457 EL_EMC_LENSES, ACTION_COLLECTING, -1
7460 Ymagnify_blank, FALSE, FALSE,
7461 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7464 Ygrass_blank, FALSE, FALSE,
7465 EL_EMC_GRASS, ACTION_SNAPPING, -1
7468 Ydirt_blank, FALSE, FALSE,
7469 EL_SAND, ACTION_SNAPPING, -1
7478 static struct Mapping_EM_to_RND_player
7487 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7491 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7495 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7499 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7503 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7507 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7511 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7515 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7519 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7523 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7527 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7531 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7535 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7539 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7543 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7547 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7551 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7555 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7559 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7563 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7567 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7571 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7575 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7579 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7583 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7587 EL_PLAYER_1, ACTION_DEFAULT, -1,
7591 EL_PLAYER_2, ACTION_DEFAULT, -1,
7595 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7599 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7603 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7607 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7611 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7615 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7619 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7623 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7627 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7631 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7635 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7639 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7643 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7647 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7651 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7655 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7659 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7663 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7667 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7671 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7675 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7679 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7683 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7687 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7691 EL_PLAYER_3, ACTION_DEFAULT, -1,
7695 EL_PLAYER_4, ACTION_DEFAULT, -1,
7704 int map_element_RND_to_EM_cave(int element_rnd)
7706 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7707 static boolean mapping_initialized = FALSE;
7709 if (!mapping_initialized)
7713 // return "Xalpha_quest" for all undefined elements in mapping array
7714 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7715 mapping_RND_to_EM[i] = Xalpha_quest;
7717 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7718 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7719 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7720 em_object_mapping_list[i].element_em;
7722 mapping_initialized = TRUE;
7725 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7727 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7732 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7735 int map_element_EM_to_RND_cave(int element_em_cave)
7737 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7738 static boolean mapping_initialized = FALSE;
7740 if (!mapping_initialized)
7744 // return "EL_UNKNOWN" for all undefined elements in mapping array
7745 for (i = 0; i < GAME_TILE_MAX; i++)
7746 mapping_EM_to_RND[i] = EL_UNKNOWN;
7748 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7749 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7750 em_object_mapping_list[i].element_rnd;
7752 mapping_initialized = TRUE;
7755 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7757 Error(ERR_WARN, "invalid EM cave element %d", element_em_cave);
7762 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7765 int map_element_EM_to_RND_game(int element_em_game)
7767 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7768 static boolean mapping_initialized = FALSE;
7770 if (!mapping_initialized)
7774 // return "EL_UNKNOWN" for all undefined elements in mapping array
7775 for (i = 0; i < GAME_TILE_MAX; i++)
7776 mapping_EM_to_RND[i] = EL_UNKNOWN;
7778 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7779 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7780 em_object_mapping_list[i].element_rnd;
7782 mapping_initialized = TRUE;
7785 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7787 Error(ERR_WARN, "invalid EM game element %d", element_em_game);
7792 return mapping_EM_to_RND[element_em_game];
7795 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7797 struct LevelInfo_EM *level_em = level->native_em_level;
7798 struct CAVE *cav = level_em->cav;
7801 for (i = 0; i < GAME_TILE_MAX; i++)
7802 cav->android_array[i] = Cblank;
7804 for (i = 0; i < level->num_android_clone_elements; i++)
7806 int element_rnd = level->android_clone_element[i];
7807 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7809 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7810 if (em_object_mapping_list[j].element_rnd == element_rnd)
7811 cav->android_array[em_object_mapping_list[j].element_em] =
7816 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7818 struct LevelInfo_EM *level_em = level->native_em_level;
7819 struct CAVE *cav = level_em->cav;
7822 level->num_android_clone_elements = 0;
7824 for (i = 0; i < GAME_TILE_MAX; i++)
7826 int element_em_cave = cav->android_array[i];
7828 boolean element_found = FALSE;
7830 if (element_em_cave == Cblank)
7833 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7835 for (j = 0; j < level->num_android_clone_elements; j++)
7836 if (level->android_clone_element[j] == element_rnd)
7837 element_found = TRUE;
7841 level->android_clone_element[level->num_android_clone_elements++] =
7844 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7849 if (level->num_android_clone_elements == 0)
7851 level->num_android_clone_elements = 1;
7852 level->android_clone_element[0] = EL_EMPTY;
7856 int map_direction_RND_to_EM(int direction)
7858 return (direction == MV_UP ? 0 :
7859 direction == MV_RIGHT ? 1 :
7860 direction == MV_DOWN ? 2 :
7861 direction == MV_LEFT ? 3 :
7865 int map_direction_EM_to_RND(int direction)
7867 return (direction == 0 ? MV_UP :
7868 direction == 1 ? MV_RIGHT :
7869 direction == 2 ? MV_DOWN :
7870 direction == 3 ? MV_LEFT :
7874 int map_element_RND_to_SP(int element_rnd)
7876 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7878 if (element_rnd >= EL_SP_START &&
7879 element_rnd <= EL_SP_END)
7880 element_sp = element_rnd - EL_SP_START;
7881 else if (element_rnd == EL_EMPTY_SPACE)
7883 else if (element_rnd == EL_INVISIBLE_WALL)
7889 int map_element_SP_to_RND(int element_sp)
7891 int element_rnd = EL_UNKNOWN;
7893 if (element_sp >= 0x00 &&
7895 element_rnd = EL_SP_START + element_sp;
7896 else if (element_sp == 0x28)
7897 element_rnd = EL_INVISIBLE_WALL;
7902 int map_action_SP_to_RND(int action_sp)
7906 case actActive: return ACTION_ACTIVE;
7907 case actImpact: return ACTION_IMPACT;
7908 case actExploding: return ACTION_EXPLODING;
7909 case actDigging: return ACTION_DIGGING;
7910 case actSnapping: return ACTION_SNAPPING;
7911 case actCollecting: return ACTION_COLLECTING;
7912 case actPassing: return ACTION_PASSING;
7913 case actPushing: return ACTION_PUSHING;
7914 case actDropping: return ACTION_DROPPING;
7916 default: return ACTION_DEFAULT;
7920 int map_element_RND_to_MM(int element_rnd)
7922 return (element_rnd >= EL_MM_START_1 &&
7923 element_rnd <= EL_MM_END_1 ?
7924 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7926 element_rnd >= EL_MM_START_2 &&
7927 element_rnd <= EL_MM_END_2 ?
7928 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7930 element_rnd >= EL_CHAR_START &&
7931 element_rnd <= EL_CHAR_END ?
7932 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7934 element_rnd >= EL_MM_RUNTIME_START &&
7935 element_rnd <= EL_MM_RUNTIME_END ?
7936 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7938 element_rnd >= EL_MM_DUMMY_START &&
7939 element_rnd <= EL_MM_DUMMY_END ?
7940 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7942 EL_MM_EMPTY_NATIVE);
7945 int map_element_MM_to_RND(int element_mm)
7947 return (element_mm == EL_MM_EMPTY_NATIVE ||
7948 element_mm == EL_DF_EMPTY_NATIVE ?
7951 element_mm >= EL_MM_START_1_NATIVE &&
7952 element_mm <= EL_MM_END_1_NATIVE ?
7953 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7955 element_mm >= EL_MM_START_2_NATIVE &&
7956 element_mm <= EL_MM_END_2_NATIVE ?
7957 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7959 element_mm >= EL_MM_CHAR_START_NATIVE &&
7960 element_mm <= EL_MM_CHAR_END_NATIVE ?
7961 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7963 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7964 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7965 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7967 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7968 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7969 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7974 int map_action_MM_to_RND(int action_mm)
7976 // all MM actions are defined to exactly match their RND counterparts
7980 int map_sound_MM_to_RND(int sound_mm)
7984 case SND_MM_GAME_LEVELTIME_CHARGING:
7985 return SND_GAME_LEVELTIME_CHARGING;
7987 case SND_MM_GAME_HEALTH_CHARGING:
7988 return SND_GAME_HEALTH_CHARGING;
7991 return SND_UNDEFINED;
7995 int map_mm_wall_element(int element)
7997 return (element >= EL_MM_STEEL_WALL_START &&
7998 element <= EL_MM_STEEL_WALL_END ?
8001 element >= EL_MM_WOODEN_WALL_START &&
8002 element <= EL_MM_WOODEN_WALL_END ?
8005 element >= EL_MM_ICE_WALL_START &&
8006 element <= EL_MM_ICE_WALL_END ?
8009 element >= EL_MM_AMOEBA_WALL_START &&
8010 element <= EL_MM_AMOEBA_WALL_END ?
8013 element >= EL_DF_STEEL_WALL_START &&
8014 element <= EL_DF_STEEL_WALL_END ?
8017 element >= EL_DF_WOODEN_WALL_START &&
8018 element <= EL_DF_WOODEN_WALL_END ?
8024 int map_mm_wall_element_editor(int element)
8028 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8029 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8030 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8031 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8032 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8033 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8035 default: return element;
8039 int get_next_element(int element)
8043 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8044 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8045 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8046 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8047 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8048 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8049 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8050 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8051 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8052 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8053 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8055 default: return element;
8059 int el2img_mm(int element_mm)
8061 return el2img(map_element_MM_to_RND(element_mm));
8064 int el_act_dir2img(int element, int action, int direction)
8066 element = GFX_ELEMENT(element);
8067 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8069 // direction_graphic[][] == graphic[] for undefined direction graphics
8070 return element_info[element].direction_graphic[action][direction];
8073 static int el_act_dir2crm(int element, int action, int direction)
8075 element = GFX_ELEMENT(element);
8076 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8078 // direction_graphic[][] == graphic[] for undefined direction graphics
8079 return element_info[element].direction_crumbled[action][direction];
8082 int el_act2img(int element, int action)
8084 element = GFX_ELEMENT(element);
8086 return element_info[element].graphic[action];
8089 int el_act2crm(int element, int action)
8091 element = GFX_ELEMENT(element);
8093 return element_info[element].crumbled[action];
8096 int el_dir2img(int element, int direction)
8098 element = GFX_ELEMENT(element);
8100 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8103 int el2baseimg(int element)
8105 return element_info[element].graphic[ACTION_DEFAULT];
8108 int el2img(int element)
8110 element = GFX_ELEMENT(element);
8112 return element_info[element].graphic[ACTION_DEFAULT];
8115 int el2edimg(int element)
8117 element = GFX_ELEMENT(element);
8119 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8122 int el2preimg(int element)
8124 element = GFX_ELEMENT(element);
8126 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8129 int el2panelimg(int element)
8131 element = GFX_ELEMENT(element);
8133 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8136 int font2baseimg(int font_nr)
8138 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8141 int getBeltNrFromBeltElement(int element)
8143 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8144 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8145 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8148 int getBeltNrFromBeltActiveElement(int element)
8150 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8151 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8152 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8155 int getBeltNrFromBeltSwitchElement(int element)
8157 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8158 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8159 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8162 int getBeltDirNrFromBeltElement(int element)
8164 static int belt_base_element[4] =
8166 EL_CONVEYOR_BELT_1_LEFT,
8167 EL_CONVEYOR_BELT_2_LEFT,
8168 EL_CONVEYOR_BELT_3_LEFT,
8169 EL_CONVEYOR_BELT_4_LEFT
8172 int belt_nr = getBeltNrFromBeltElement(element);
8173 int belt_dir_nr = element - belt_base_element[belt_nr];
8175 return (belt_dir_nr % 3);
8178 int getBeltDirNrFromBeltSwitchElement(int element)
8180 static int belt_base_element[4] =
8182 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8183 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8184 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8185 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8188 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8189 int belt_dir_nr = element - belt_base_element[belt_nr];
8191 return (belt_dir_nr % 3);
8194 int getBeltDirFromBeltElement(int element)
8196 static int belt_move_dir[3] =
8203 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8205 return belt_move_dir[belt_dir_nr];
8208 int getBeltDirFromBeltSwitchElement(int element)
8210 static int belt_move_dir[3] =
8217 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8219 return belt_move_dir[belt_dir_nr];
8222 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8224 static int belt_base_element[4] =
8226 EL_CONVEYOR_BELT_1_LEFT,
8227 EL_CONVEYOR_BELT_2_LEFT,
8228 EL_CONVEYOR_BELT_3_LEFT,
8229 EL_CONVEYOR_BELT_4_LEFT
8232 return belt_base_element[belt_nr] + belt_dir_nr;
8235 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8237 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8239 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8242 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8244 static int belt_base_element[4] =
8246 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8247 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8248 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8249 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8252 return belt_base_element[belt_nr] + belt_dir_nr;
8255 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8257 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8259 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8262 boolean swapTiles_EM(boolean is_pre_emc_cave)
8264 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8267 boolean getTeamMode_EM(void)
8269 return game.team_mode || network_playing;
8272 boolean isActivePlayer_EM(int player_nr)
8274 return stored_player[player_nr].active;
8277 unsigned int InitRND(int seed)
8279 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8280 return InitEngineRandom_EM(seed);
8281 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8282 return InitEngineRandom_SP(seed);
8283 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8284 return InitEngineRandom_MM(seed);
8286 return InitEngineRandom_RND(seed);
8289 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8290 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8292 static int get_effective_element_EM(int tile, int frame_em)
8294 int element = object_mapping[tile].element_rnd;
8295 int action = object_mapping[tile].action;
8296 boolean is_backside = object_mapping[tile].is_backside;
8297 boolean action_removing = (action == ACTION_DIGGING ||
8298 action == ACTION_SNAPPING ||
8299 action == ACTION_COLLECTING);
8307 return (frame_em > 5 ? EL_EMPTY : element);
8313 else // frame_em == 7
8324 case Ydiamond_stone:
8328 case Xdrip_stretchB:
8344 case Ymagnify_blank:
8347 case Xsand_stonein_1:
8348 case Xsand_stonein_2:
8349 case Xsand_stonein_3:
8350 case Xsand_stonein_4:
8354 return (is_backside || action_removing ? EL_EMPTY : element);
8359 static boolean check_linear_animation_EM(int tile)
8363 case Xsand_stonesand_1:
8364 case Xsand_stonesand_quickout_1:
8365 case Xsand_sandstone_1:
8366 case Xsand_stonein_1:
8367 case Xsand_stoneout_1:
8395 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8396 boolean has_crumbled_graphics,
8397 int crumbled, int sync_frame)
8399 // if element can be crumbled, but certain action graphics are just empty
8400 // space (like instantly snapping sand to empty space in 1 frame), do not
8401 // treat these empty space graphics as crumbled graphics in EMC engine
8402 if (crumbled == IMG_EMPTY_SPACE)
8403 has_crumbled_graphics = FALSE;
8405 if (has_crumbled_graphics)
8407 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8408 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8409 g_crumbled->anim_delay,
8410 g_crumbled->anim_mode,
8411 g_crumbled->anim_start_frame,
8414 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8415 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8417 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8418 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8420 g_em->has_crumbled_graphics = TRUE;
8424 g_em->crumbled_bitmap = NULL;
8425 g_em->crumbled_src_x = 0;
8426 g_em->crumbled_src_y = 0;
8427 g_em->crumbled_border_size = 0;
8428 g_em->crumbled_tile_size = 0;
8430 g_em->has_crumbled_graphics = FALSE;
8435 void ResetGfxAnimation_EM(int x, int y, int tile)
8441 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8442 int tile, int frame_em, int x, int y)
8444 int action = object_mapping[tile].action;
8445 int direction = object_mapping[tile].direction;
8446 int effective_element = get_effective_element_EM(tile, frame_em);
8447 int graphic = (direction == MV_NONE ?
8448 el_act2img(effective_element, action) :
8449 el_act_dir2img(effective_element, action, direction));
8450 struct GraphicInfo *g = &graphic_info[graphic];
8452 boolean action_removing = (action == ACTION_DIGGING ||
8453 action == ACTION_SNAPPING ||
8454 action == ACTION_COLLECTING);
8455 boolean action_moving = (action == ACTION_FALLING ||
8456 action == ACTION_MOVING ||
8457 action == ACTION_PUSHING ||
8458 action == ACTION_EATING ||
8459 action == ACTION_FILLING ||
8460 action == ACTION_EMPTYING);
8461 boolean action_falling = (action == ACTION_FALLING ||
8462 action == ACTION_FILLING ||
8463 action == ACTION_EMPTYING);
8465 // special case: graphic uses "2nd movement tile" and has defined
8466 // 7 frames for movement animation (or less) => use default graphic
8467 // for last (8th) frame which ends the movement animation
8468 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8470 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8471 graphic = (direction == MV_NONE ?
8472 el_act2img(effective_element, action) :
8473 el_act_dir2img(effective_element, action, direction));
8475 g = &graphic_info[graphic];
8478 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8482 else if (action_moving)
8484 boolean is_backside = object_mapping[tile].is_backside;
8488 int direction = object_mapping[tile].direction;
8489 int move_dir = (action_falling ? MV_DOWN : direction);
8494 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8495 if (g->double_movement && frame_em == 0)
8499 if (move_dir == MV_LEFT)
8500 GfxFrame[x - 1][y] = GfxFrame[x][y];
8501 else if (move_dir == MV_RIGHT)
8502 GfxFrame[x + 1][y] = GfxFrame[x][y];
8503 else if (move_dir == MV_UP)
8504 GfxFrame[x][y - 1] = GfxFrame[x][y];
8505 else if (move_dir == MV_DOWN)
8506 GfxFrame[x][y + 1] = GfxFrame[x][y];
8513 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8514 if (tile == Xsand_stonesand_quickout_1 ||
8515 tile == Xsand_stonesand_quickout_2)
8519 if (graphic_info[graphic].anim_global_sync)
8520 sync_frame = FrameCounter;
8521 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8522 sync_frame = GfxFrame[x][y];
8524 sync_frame = 0; // playfield border (pseudo steel)
8526 SetRandomAnimationValue(x, y);
8528 int frame = getAnimationFrame(g->anim_frames,
8531 g->anim_start_frame,
8534 g_em->unique_identifier =
8535 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8538 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8539 int tile, int frame_em, int x, int y)
8541 int action = object_mapping[tile].action;
8542 int direction = object_mapping[tile].direction;
8543 boolean is_backside = object_mapping[tile].is_backside;
8544 int effective_element = get_effective_element_EM(tile, frame_em);
8545 int effective_action = action;
8546 int graphic = (direction == MV_NONE ?
8547 el_act2img(effective_element, effective_action) :
8548 el_act_dir2img(effective_element, effective_action,
8550 int crumbled = (direction == MV_NONE ?
8551 el_act2crm(effective_element, effective_action) :
8552 el_act_dir2crm(effective_element, effective_action,
8554 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8555 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8556 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8557 struct GraphicInfo *g = &graphic_info[graphic];
8560 // special case: graphic uses "2nd movement tile" and has defined
8561 // 7 frames for movement animation (or less) => use default graphic
8562 // for last (8th) frame which ends the movement animation
8563 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8565 effective_action = ACTION_DEFAULT;
8566 graphic = (direction == MV_NONE ?
8567 el_act2img(effective_element, effective_action) :
8568 el_act_dir2img(effective_element, effective_action,
8570 crumbled = (direction == MV_NONE ?
8571 el_act2crm(effective_element, effective_action) :
8572 el_act_dir2crm(effective_element, effective_action,
8575 g = &graphic_info[graphic];
8578 if (graphic_info[graphic].anim_global_sync)
8579 sync_frame = FrameCounter;
8580 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8581 sync_frame = GfxFrame[x][y];
8583 sync_frame = 0; // playfield border (pseudo steel)
8585 SetRandomAnimationValue(x, y);
8587 int frame = getAnimationFrame(g->anim_frames,
8590 g->anim_start_frame,
8593 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8594 g->double_movement && is_backside);
8596 // (updating the "crumbled" graphic definitions is probably not really needed,
8597 // as animations for crumbled graphics can't be longer than one EMC cycle)
8598 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8602 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8603 int player_nr, int anim, int frame_em)
8605 int element = player_mapping[player_nr][anim].element_rnd;
8606 int action = player_mapping[player_nr][anim].action;
8607 int direction = player_mapping[player_nr][anim].direction;
8608 int graphic = (direction == MV_NONE ?
8609 el_act2img(element, action) :
8610 el_act_dir2img(element, action, direction));
8611 struct GraphicInfo *g = &graphic_info[graphic];
8614 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8616 stored_player[player_nr].StepFrame = frame_em;
8618 sync_frame = stored_player[player_nr].Frame;
8620 int frame = getAnimationFrame(g->anim_frames,
8623 g->anim_start_frame,
8626 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8627 &g_em->src_x, &g_em->src_y, FALSE);
8630 void InitGraphicInfo_EM(void)
8634 // always start with reliable default values
8635 for (i = 0; i < GAME_TILE_MAX; i++)
8637 object_mapping[i].element_rnd = EL_UNKNOWN;
8638 object_mapping[i].is_backside = FALSE;
8639 object_mapping[i].action = ACTION_DEFAULT;
8640 object_mapping[i].direction = MV_NONE;
8643 // always start with reliable default values
8644 for (p = 0; p < MAX_PLAYERS; p++)
8646 for (i = 0; i < PLY_MAX; i++)
8648 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8649 player_mapping[p][i].action = ACTION_DEFAULT;
8650 player_mapping[p][i].direction = MV_NONE;
8654 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8656 int e = em_object_mapping_list[i].element_em;
8658 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8659 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8661 if (em_object_mapping_list[i].action != -1)
8662 object_mapping[e].action = em_object_mapping_list[i].action;
8664 if (em_object_mapping_list[i].direction != -1)
8665 object_mapping[e].direction =
8666 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8669 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8671 int a = em_player_mapping_list[i].action_em;
8672 int p = em_player_mapping_list[i].player_nr;
8674 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8676 if (em_player_mapping_list[i].action != -1)
8677 player_mapping[p][a].action = em_player_mapping_list[i].action;
8679 if (em_player_mapping_list[i].direction != -1)
8680 player_mapping[p][a].direction =
8681 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8684 for (i = 0; i < GAME_TILE_MAX; i++)
8686 int element = object_mapping[i].element_rnd;
8687 int action = object_mapping[i].action;
8688 int direction = object_mapping[i].direction;
8689 boolean is_backside = object_mapping[i].is_backside;
8690 boolean action_exploding = ((action == ACTION_EXPLODING ||
8691 action == ACTION_SMASHED_BY_ROCK ||
8692 action == ACTION_SMASHED_BY_SPRING) &&
8693 element != EL_DIAMOND);
8694 boolean action_active = (action == ACTION_ACTIVE);
8695 boolean action_other = (action == ACTION_OTHER);
8697 for (j = 0; j < 8; j++)
8699 int effective_element = get_effective_element_EM(i, j);
8700 int effective_action = (j < 7 ? action :
8701 i == Xdrip_stretch ? action :
8702 i == Xdrip_stretchB ? action :
8703 i == Ydrip_1_s ? action :
8704 i == Ydrip_1_sB ? action :
8705 i == Yball_1 ? action :
8706 i == Xball_2 ? action :
8707 i == Yball_2 ? action :
8708 i == Yball_blank ? action :
8709 i == Ykey_1_blank ? action :
8710 i == Ykey_2_blank ? action :
8711 i == Ykey_3_blank ? action :
8712 i == Ykey_4_blank ? action :
8713 i == Ykey_5_blank ? action :
8714 i == Ykey_6_blank ? action :
8715 i == Ykey_7_blank ? action :
8716 i == Ykey_8_blank ? action :
8717 i == Ylenses_blank ? action :
8718 i == Ymagnify_blank ? action :
8719 i == Ygrass_blank ? action :
8720 i == Ydirt_blank ? action :
8721 i == Xsand_stonein_1 ? action :
8722 i == Xsand_stonein_2 ? action :
8723 i == Xsand_stonein_3 ? action :
8724 i == Xsand_stonein_4 ? action :
8725 i == Xsand_stoneout_1 ? action :
8726 i == Xsand_stoneout_2 ? action :
8727 i == Xboom_android ? ACTION_EXPLODING :
8728 action_exploding ? ACTION_EXPLODING :
8729 action_active ? action :
8730 action_other ? action :
8732 int graphic = (el_act_dir2img(effective_element, effective_action,
8734 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8736 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8737 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8738 boolean has_action_graphics = (graphic != base_graphic);
8739 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8740 struct GraphicInfo *g = &graphic_info[graphic];
8741 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8744 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8745 boolean special_animation = (action != ACTION_DEFAULT &&
8746 g->anim_frames == 3 &&
8747 g->anim_delay == 2 &&
8748 g->anim_mode & ANIM_LINEAR);
8749 int sync_frame = (i == Xdrip_stretch ? 7 :
8750 i == Xdrip_stretchB ? 7 :
8751 i == Ydrip_2_s ? j + 8 :
8752 i == Ydrip_2_sB ? j + 8 :
8761 i == Xfake_acid_1 ? 0 :
8762 i == Xfake_acid_2 ? 10 :
8763 i == Xfake_acid_3 ? 20 :
8764 i == Xfake_acid_4 ? 30 :
8765 i == Xfake_acid_5 ? 40 :
8766 i == Xfake_acid_6 ? 50 :
8767 i == Xfake_acid_7 ? 60 :
8768 i == Xfake_acid_8 ? 70 :
8770 i == Yball_2 ? j + 8 :
8771 i == Yball_blank ? j + 1 :
8772 i == Ykey_1_blank ? j + 1 :
8773 i == Ykey_2_blank ? j + 1 :
8774 i == Ykey_3_blank ? j + 1 :
8775 i == Ykey_4_blank ? j + 1 :
8776 i == Ykey_5_blank ? j + 1 :
8777 i == Ykey_6_blank ? j + 1 :
8778 i == Ykey_7_blank ? j + 1 :
8779 i == Ykey_8_blank ? j + 1 :
8780 i == Ylenses_blank ? j + 1 :
8781 i == Ymagnify_blank ? j + 1 :
8782 i == Ygrass_blank ? j + 1 :
8783 i == Ydirt_blank ? j + 1 :
8784 i == Xamoeba_1 ? 0 :
8785 i == Xamoeba_2 ? 1 :
8786 i == Xamoeba_3 ? 2 :
8787 i == Xamoeba_4 ? 3 :
8788 i == Xamoeba_5 ? 0 :
8789 i == Xamoeba_6 ? 1 :
8790 i == Xamoeba_7 ? 2 :
8791 i == Xamoeba_8 ? 3 :
8792 i == Xexit_2 ? j + 8 :
8793 i == Xexit_3 ? j + 16 :
8794 i == Xdynamite_1 ? 0 :
8795 i == Xdynamite_2 ? 8 :
8796 i == Xdynamite_3 ? 16 :
8797 i == Xdynamite_4 ? 24 :
8798 i == Xsand_stonein_1 ? j + 1 :
8799 i == Xsand_stonein_2 ? j + 9 :
8800 i == Xsand_stonein_3 ? j + 17 :
8801 i == Xsand_stonein_4 ? j + 25 :
8802 i == Xsand_stoneout_1 && j == 0 ? 0 :
8803 i == Xsand_stoneout_1 && j == 1 ? 0 :
8804 i == Xsand_stoneout_1 && j == 2 ? 1 :
8805 i == Xsand_stoneout_1 && j == 3 ? 2 :
8806 i == Xsand_stoneout_1 && j == 4 ? 2 :
8807 i == Xsand_stoneout_1 && j == 5 ? 3 :
8808 i == Xsand_stoneout_1 && j == 6 ? 4 :
8809 i == Xsand_stoneout_1 && j == 7 ? 4 :
8810 i == Xsand_stoneout_2 && j == 0 ? 5 :
8811 i == Xsand_stoneout_2 && j == 1 ? 6 :
8812 i == Xsand_stoneout_2 && j == 2 ? 7 :
8813 i == Xsand_stoneout_2 && j == 3 ? 8 :
8814 i == Xsand_stoneout_2 && j == 4 ? 9 :
8815 i == Xsand_stoneout_2 && j == 5 ? 11 :
8816 i == Xsand_stoneout_2 && j == 6 ? 13 :
8817 i == Xsand_stoneout_2 && j == 7 ? 15 :
8818 i == Xboom_bug && j == 1 ? 2 :
8819 i == Xboom_bug && j == 2 ? 2 :
8820 i == Xboom_bug && j == 3 ? 4 :
8821 i == Xboom_bug && j == 4 ? 4 :
8822 i == Xboom_bug && j == 5 ? 2 :
8823 i == Xboom_bug && j == 6 ? 2 :
8824 i == Xboom_bug && j == 7 ? 0 :
8825 i == Xboom_tank && j == 1 ? 2 :
8826 i == Xboom_tank && j == 2 ? 2 :
8827 i == Xboom_tank && j == 3 ? 4 :
8828 i == Xboom_tank && j == 4 ? 4 :
8829 i == Xboom_tank && j == 5 ? 2 :
8830 i == Xboom_tank && j == 6 ? 2 :
8831 i == Xboom_tank && j == 7 ? 0 :
8832 i == Xboom_android && j == 7 ? 6 :
8833 i == Xboom_1 && j == 1 ? 2 :
8834 i == Xboom_1 && j == 2 ? 2 :
8835 i == Xboom_1 && j == 3 ? 4 :
8836 i == Xboom_1 && j == 4 ? 4 :
8837 i == Xboom_1 && j == 5 ? 6 :
8838 i == Xboom_1 && j == 6 ? 6 :
8839 i == Xboom_1 && j == 7 ? 8 :
8840 i == Xboom_2 && j == 0 ? 8 :
8841 i == Xboom_2 && j == 1 ? 8 :
8842 i == Xboom_2 && j == 2 ? 10 :
8843 i == Xboom_2 && j == 3 ? 10 :
8844 i == Xboom_2 && j == 4 ? 10 :
8845 i == Xboom_2 && j == 5 ? 12 :
8846 i == Xboom_2 && j == 6 ? 12 :
8847 i == Xboom_2 && j == 7 ? 12 :
8848 special_animation && j == 4 ? 3 :
8849 effective_action != action ? 0 :
8851 int frame = getAnimationFrame(g->anim_frames,
8854 g->anim_start_frame,
8857 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8858 g->double_movement && is_backside);
8860 g_em->bitmap = src_bitmap;
8861 g_em->src_x = src_x;
8862 g_em->src_y = src_y;
8863 g_em->src_offset_x = 0;
8864 g_em->src_offset_y = 0;
8865 g_em->dst_offset_x = 0;
8866 g_em->dst_offset_y = 0;
8867 g_em->width = TILEX;
8868 g_em->height = TILEY;
8870 g_em->preserve_background = FALSE;
8872 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8875 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8876 effective_action == ACTION_MOVING ||
8877 effective_action == ACTION_PUSHING ||
8878 effective_action == ACTION_EATING)) ||
8879 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8880 effective_action == ACTION_EMPTYING)))
8883 (effective_action == ACTION_FALLING ||
8884 effective_action == ACTION_FILLING ||
8885 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8886 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8887 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8888 int num_steps = (i == Ydrip_1_s ? 16 :
8889 i == Ydrip_1_sB ? 16 :
8890 i == Ydrip_2_s ? 16 :
8891 i == Ydrip_2_sB ? 16 :
8892 i == Xsand_stonein_1 ? 32 :
8893 i == Xsand_stonein_2 ? 32 :
8894 i == Xsand_stonein_3 ? 32 :
8895 i == Xsand_stonein_4 ? 32 :
8896 i == Xsand_stoneout_1 ? 16 :
8897 i == Xsand_stoneout_2 ? 16 : 8);
8898 int cx = ABS(dx) * (TILEX / num_steps);
8899 int cy = ABS(dy) * (TILEY / num_steps);
8900 int step_frame = (i == Ydrip_2_s ? j + 8 :
8901 i == Ydrip_2_sB ? j + 8 :
8902 i == Xsand_stonein_2 ? j + 8 :
8903 i == Xsand_stonein_3 ? j + 16 :
8904 i == Xsand_stonein_4 ? j + 24 :
8905 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8906 int step = (is_backside ? step_frame : num_steps - step_frame);
8908 if (is_backside) // tile where movement starts
8910 if (dx < 0 || dy < 0)
8912 g_em->src_offset_x = cx * step;
8913 g_em->src_offset_y = cy * step;
8917 g_em->dst_offset_x = cx * step;
8918 g_em->dst_offset_y = cy * step;
8921 else // tile where movement ends
8923 if (dx < 0 || dy < 0)
8925 g_em->dst_offset_x = cx * step;
8926 g_em->dst_offset_y = cy * step;
8930 g_em->src_offset_x = cx * step;
8931 g_em->src_offset_y = cy * step;
8935 g_em->width = TILEX - cx * step;
8936 g_em->height = TILEY - cy * step;
8939 // create unique graphic identifier to decide if tile must be redrawn
8940 /* bit 31 - 16 (16 bit): EM style graphic
8941 bit 15 - 12 ( 4 bit): EM style frame
8942 bit 11 - 6 ( 6 bit): graphic width
8943 bit 5 - 0 ( 6 bit): graphic height */
8944 g_em->unique_identifier =
8945 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8949 for (i = 0; i < GAME_TILE_MAX; i++)
8951 for (j = 0; j < 8; j++)
8953 int element = object_mapping[i].element_rnd;
8954 int action = object_mapping[i].action;
8955 int direction = object_mapping[i].direction;
8956 boolean is_backside = object_mapping[i].is_backside;
8957 int graphic_action = el_act_dir2img(element, action, direction);
8958 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8960 if ((action == ACTION_SMASHED_BY_ROCK ||
8961 action == ACTION_SMASHED_BY_SPRING ||
8962 action == ACTION_EATING) &&
8963 graphic_action == graphic_default)
8965 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8966 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8967 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8968 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8971 // no separate animation for "smashed by rock" -- use rock instead
8972 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8973 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8975 g_em->bitmap = g_xx->bitmap;
8976 g_em->src_x = g_xx->src_x;
8977 g_em->src_y = g_xx->src_y;
8978 g_em->src_offset_x = g_xx->src_offset_x;
8979 g_em->src_offset_y = g_xx->src_offset_y;
8980 g_em->dst_offset_x = g_xx->dst_offset_x;
8981 g_em->dst_offset_y = g_xx->dst_offset_y;
8982 g_em->width = g_xx->width;
8983 g_em->height = g_xx->height;
8984 g_em->unique_identifier = g_xx->unique_identifier;
8987 g_em->preserve_background = TRUE;
8992 for (p = 0; p < MAX_PLAYERS; p++)
8994 for (i = 0; i < PLY_MAX; i++)
8996 int element = player_mapping[p][i].element_rnd;
8997 int action = player_mapping[p][i].action;
8998 int direction = player_mapping[p][i].direction;
9000 for (j = 0; j < 8; j++)
9002 int effective_element = element;
9003 int effective_action = action;
9004 int graphic = (direction == MV_NONE ?
9005 el_act2img(effective_element, effective_action) :
9006 el_act_dir2img(effective_element, effective_action,
9008 struct GraphicInfo *g = &graphic_info[graphic];
9009 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9013 int frame = getAnimationFrame(g->anim_frames,
9016 g->anim_start_frame,
9019 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9021 g_em->bitmap = src_bitmap;
9022 g_em->src_x = src_x;
9023 g_em->src_y = src_y;
9024 g_em->src_offset_x = 0;
9025 g_em->src_offset_y = 0;
9026 g_em->dst_offset_x = 0;
9027 g_em->dst_offset_y = 0;
9028 g_em->width = TILEX;
9029 g_em->height = TILEY;
9035 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9036 boolean any_player_moving,
9037 boolean any_player_snapping,
9038 boolean any_player_dropping)
9040 if (frame == 7 && !any_player_dropping)
9042 if (!local_player->was_waiting)
9044 if (!CheckSaveEngineSnapshotToList())
9047 local_player->was_waiting = TRUE;
9050 else if (any_player_moving || any_player_snapping || any_player_dropping)
9052 local_player->was_waiting = FALSE;
9056 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9057 boolean murphy_is_dropping)
9059 if (murphy_is_waiting)
9061 if (!local_player->was_waiting)
9063 if (!CheckSaveEngineSnapshotToList())
9066 local_player->was_waiting = TRUE;
9071 local_player->was_waiting = FALSE;
9075 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9076 boolean button_released)
9078 if (button_released)
9080 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9081 CheckSaveEngineSnapshotToList();
9083 else if (element_clicked)
9085 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9086 CheckSaveEngineSnapshotToList();
9088 game.snapshot.changed_action = TRUE;
9092 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9093 boolean any_player_moving,
9094 boolean any_player_snapping,
9095 boolean any_player_dropping)
9097 if (tape.single_step && tape.recording && !tape.pausing)
9098 if (frame == 7 && !any_player_dropping)
9099 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9101 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9102 any_player_snapping, any_player_dropping);
9105 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9106 boolean murphy_is_dropping)
9108 boolean murphy_starts_dropping = FALSE;
9111 for (i = 0; i < MAX_PLAYERS; i++)
9112 if (stored_player[i].force_dropping)
9113 murphy_starts_dropping = TRUE;
9115 if (tape.single_step && tape.recording && !tape.pausing)
9116 if (murphy_is_waiting && !murphy_starts_dropping)
9117 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9119 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9122 void CheckSingleStepMode_MM(boolean element_clicked,
9123 boolean button_released)
9125 if (tape.single_step && tape.recording && !tape.pausing)
9126 if (button_released)
9127 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9129 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9132 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9133 int graphic, int sync_frame, int x, int y)
9135 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9137 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9140 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9142 return (IS_NEXT_FRAME(sync_frame, graphic));
9145 int getGraphicInfo_Delay(int graphic)
9147 return graphic_info[graphic].anim_delay;
9150 void PlayMenuSoundExt(int sound)
9152 if (sound == SND_UNDEFINED)
9155 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9156 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9159 if (IS_LOOP_SOUND(sound))
9160 PlaySoundLoop(sound);
9165 void PlayMenuSound(void)
9167 PlayMenuSoundExt(menu.sound[game_status]);
9170 void PlayMenuSoundStereo(int sound, int stereo_position)
9172 if (sound == SND_UNDEFINED)
9175 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9176 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9179 if (IS_LOOP_SOUND(sound))
9180 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9182 PlaySoundStereo(sound, stereo_position);
9185 void PlayMenuSoundIfLoopExt(int sound)
9187 if (sound == SND_UNDEFINED)
9190 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9191 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9194 if (IS_LOOP_SOUND(sound))
9195 PlaySoundLoop(sound);
9198 void PlayMenuSoundIfLoop(void)
9200 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9203 void PlayMenuMusicExt(int music)
9205 if (music == MUS_UNDEFINED)
9208 if (!setup.sound_music)
9211 if (IS_LOOP_MUSIC(music))
9212 PlayMusicLoop(music);
9217 void PlayMenuMusic(void)
9219 char *curr_music = getCurrentlyPlayingMusicFilename();
9220 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9222 if (!strEqual(curr_music, next_music))
9223 PlayMenuMusicExt(menu.music[game_status]);
9226 void PlayMenuSoundsAndMusic(void)
9232 static void FadeMenuSounds(void)
9237 static void FadeMenuMusic(void)
9239 char *curr_music = getCurrentlyPlayingMusicFilename();
9240 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9242 if (!strEqual(curr_music, next_music))
9246 void FadeMenuSoundsAndMusic(void)
9252 void PlaySoundActivating(void)
9255 PlaySound(SND_MENU_ITEM_ACTIVATING);
9259 void PlaySoundSelecting(void)
9262 PlaySound(SND_MENU_ITEM_SELECTING);
9266 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9268 boolean change_fullscreen = (setup.fullscreen !=
9269 video.fullscreen_enabled);
9270 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9271 setup.window_scaling_percent !=
9272 video.window_scaling_percent);
9274 if (change_window_scaling_percent && video.fullscreen_enabled)
9277 if (!change_window_scaling_percent && !video.fullscreen_available)
9280 if (change_window_scaling_percent)
9282 SDLSetWindowScaling(setup.window_scaling_percent);
9286 else if (change_fullscreen)
9288 SDLSetWindowFullscreen(setup.fullscreen);
9290 // set setup value according to successfully changed fullscreen mode
9291 setup.fullscreen = video.fullscreen_enabled;
9296 if (change_fullscreen ||
9297 change_window_scaling_percent)
9299 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9301 // save backbuffer content which gets lost when toggling fullscreen mode
9302 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9304 if (change_window_scaling_percent)
9306 // keep window mode, but change window scaling
9307 video.fullscreen_enabled = TRUE; // force new window scaling
9310 // toggle fullscreen
9311 ChangeVideoModeIfNeeded(setup.fullscreen);
9313 // set setup value according to successfully changed fullscreen mode
9314 setup.fullscreen = video.fullscreen_enabled;
9316 // restore backbuffer content from temporary backbuffer backup bitmap
9317 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9319 FreeBitmap(tmp_backbuffer);
9321 // update visible window/screen
9322 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9326 static void JoinRectangles(int *x, int *y, int *width, int *height,
9327 int x2, int y2, int width2, int height2)
9329 // do not join with "off-screen" rectangle
9330 if (x2 == -1 || y2 == -1)
9335 *width = MAX(*width, width2);
9336 *height = MAX(*height, height2);
9339 void SetAnimStatus(int anim_status_new)
9341 if (anim_status_new == GAME_MODE_MAIN)
9342 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9343 else if (anim_status_new == GAME_MODE_SCORES)
9344 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9346 global.anim_status_next = anim_status_new;
9348 // directly set screen modes that are entered without fading
9349 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9350 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9351 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9352 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9353 global.anim_status = global.anim_status_next;
9356 void SetGameStatus(int game_status_new)
9358 if (game_status_new != game_status)
9359 game_status_last_screen = game_status;
9361 game_status = game_status_new;
9363 SetAnimStatus(game_status_new);
9366 void SetFontStatus(int game_status_new)
9368 static int last_game_status = -1;
9370 if (game_status_new != -1)
9372 // set game status for font use after storing last game status
9373 last_game_status = game_status;
9374 game_status = game_status_new;
9378 // reset game status after font use from last stored game status
9379 game_status = last_game_status;
9383 void ResetFontStatus(void)
9388 void SetLevelSetInfo(char *identifier, int level_nr)
9390 setString(&levelset.identifier, identifier);
9392 levelset.level_nr = level_nr;
9395 boolean CheckIfAllViewportsHaveChanged(void)
9397 // if game status has not changed, viewports have not changed either
9398 if (game_status == game_status_last)
9401 // check if all viewports have changed with current game status
9403 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9404 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9405 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9406 int new_real_sx = vp_playfield->x;
9407 int new_real_sy = vp_playfield->y;
9408 int new_full_sxsize = vp_playfield->width;
9409 int new_full_sysize = vp_playfield->height;
9410 int new_dx = vp_door_1->x;
9411 int new_dy = vp_door_1->y;
9412 int new_dxsize = vp_door_1->width;
9413 int new_dysize = vp_door_1->height;
9414 int new_vx = vp_door_2->x;
9415 int new_vy = vp_door_2->y;
9416 int new_vxsize = vp_door_2->width;
9417 int new_vysize = vp_door_2->height;
9419 boolean playfield_viewport_has_changed =
9420 (new_real_sx != REAL_SX ||
9421 new_real_sy != REAL_SY ||
9422 new_full_sxsize != FULL_SXSIZE ||
9423 new_full_sysize != FULL_SYSIZE);
9425 boolean door_1_viewport_has_changed =
9428 new_dxsize != DXSIZE ||
9429 new_dysize != DYSIZE);
9431 boolean door_2_viewport_has_changed =
9434 new_vxsize != VXSIZE ||
9435 new_vysize != VYSIZE ||
9436 game_status_last == GAME_MODE_EDITOR);
9438 return (playfield_viewport_has_changed &&
9439 door_1_viewport_has_changed &&
9440 door_2_viewport_has_changed);
9443 boolean CheckFadeAll(void)
9445 return (CheckIfGlobalBorderHasChanged() ||
9446 CheckIfAllViewportsHaveChanged());
9449 void ChangeViewportPropertiesIfNeeded(void)
9451 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9452 FALSE : setup.small_game_graphics);
9453 int gfx_game_mode = game_status;
9454 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9456 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9457 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9458 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9459 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9460 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9461 int new_win_xsize = vp_window->width;
9462 int new_win_ysize = vp_window->height;
9463 int border_left = vp_playfield->border_left;
9464 int border_right = vp_playfield->border_right;
9465 int border_top = vp_playfield->border_top;
9466 int border_bottom = vp_playfield->border_bottom;
9467 int new_sx = vp_playfield->x + border_left;
9468 int new_sy = vp_playfield->y + border_top;
9469 int new_sxsize = vp_playfield->width - border_left - border_right;
9470 int new_sysize = vp_playfield->height - border_top - border_bottom;
9471 int new_real_sx = vp_playfield->x;
9472 int new_real_sy = vp_playfield->y;
9473 int new_full_sxsize = vp_playfield->width;
9474 int new_full_sysize = vp_playfield->height;
9475 int new_dx = vp_door_1->x;
9476 int new_dy = vp_door_1->y;
9477 int new_dxsize = vp_door_1->width;
9478 int new_dysize = vp_door_1->height;
9479 int new_vx = vp_door_2->x;
9480 int new_vy = vp_door_2->y;
9481 int new_vxsize = vp_door_2->width;
9482 int new_vysize = vp_door_2->height;
9483 int new_ex = vp_door_3->x;
9484 int new_ey = vp_door_3->y;
9485 int new_exsize = vp_door_3->width;
9486 int new_eysize = vp_door_3->height;
9487 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9488 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9489 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9490 int new_scr_fieldx = new_sxsize / tilesize;
9491 int new_scr_fieldy = new_sysize / tilesize;
9492 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9493 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9494 boolean init_gfx_buffers = FALSE;
9495 boolean init_video_buffer = FALSE;
9496 boolean init_gadgets_and_anims = FALSE;
9497 boolean init_em_graphics = FALSE;
9499 if (new_win_xsize != WIN_XSIZE ||
9500 new_win_ysize != WIN_YSIZE)
9502 WIN_XSIZE = new_win_xsize;
9503 WIN_YSIZE = new_win_ysize;
9505 init_video_buffer = TRUE;
9506 init_gfx_buffers = TRUE;
9507 init_gadgets_and_anims = TRUE;
9509 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9512 if (new_scr_fieldx != SCR_FIELDX ||
9513 new_scr_fieldy != SCR_FIELDY)
9515 // this always toggles between MAIN and GAME when using small tile size
9517 SCR_FIELDX = new_scr_fieldx;
9518 SCR_FIELDY = new_scr_fieldy;
9520 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9531 new_sxsize != SXSIZE ||
9532 new_sysize != SYSIZE ||
9533 new_dxsize != DXSIZE ||
9534 new_dysize != DYSIZE ||
9535 new_vxsize != VXSIZE ||
9536 new_vysize != VYSIZE ||
9537 new_exsize != EXSIZE ||
9538 new_eysize != EYSIZE ||
9539 new_real_sx != REAL_SX ||
9540 new_real_sy != REAL_SY ||
9541 new_full_sxsize != FULL_SXSIZE ||
9542 new_full_sysize != FULL_SYSIZE ||
9543 new_tilesize_var != TILESIZE_VAR
9546 // ------------------------------------------------------------------------
9547 // determine next fading area for changed viewport definitions
9548 // ------------------------------------------------------------------------
9550 // start with current playfield area (default fading area)
9553 FADE_SXSIZE = FULL_SXSIZE;
9554 FADE_SYSIZE = FULL_SYSIZE;
9556 // add new playfield area if position or size has changed
9557 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9558 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9560 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9561 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9564 // add current and new door 1 area if position or size has changed
9565 if (new_dx != DX || new_dy != DY ||
9566 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9568 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9569 DX, DY, DXSIZE, DYSIZE);
9570 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9571 new_dx, new_dy, new_dxsize, new_dysize);
9574 // add current and new door 2 area if position or size has changed
9575 if (new_vx != VX || new_vy != VY ||
9576 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9578 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9579 VX, VY, VXSIZE, VYSIZE);
9580 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9581 new_vx, new_vy, new_vxsize, new_vysize);
9584 // ------------------------------------------------------------------------
9585 // handle changed tile size
9586 // ------------------------------------------------------------------------
9588 if (new_tilesize_var != TILESIZE_VAR)
9590 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9592 // changing tile size invalidates scroll values of engine snapshots
9593 FreeEngineSnapshotSingle();
9595 // changing tile size requires update of graphic mapping for EM engine
9596 init_em_graphics = TRUE;
9607 SXSIZE = new_sxsize;
9608 SYSIZE = new_sysize;
9609 DXSIZE = new_dxsize;
9610 DYSIZE = new_dysize;
9611 VXSIZE = new_vxsize;
9612 VYSIZE = new_vysize;
9613 EXSIZE = new_exsize;
9614 EYSIZE = new_eysize;
9615 REAL_SX = new_real_sx;
9616 REAL_SY = new_real_sy;
9617 FULL_SXSIZE = new_full_sxsize;
9618 FULL_SYSIZE = new_full_sysize;
9619 TILESIZE_VAR = new_tilesize_var;
9621 init_gfx_buffers = TRUE;
9622 init_gadgets_and_anims = TRUE;
9624 // printf("::: viewports: init_gfx_buffers\n");
9625 // printf("::: viewports: init_gadgets_and_anims\n");
9628 if (init_gfx_buffers)
9630 // printf("::: init_gfx_buffers\n");
9632 SCR_FIELDX = new_scr_fieldx_buffers;
9633 SCR_FIELDY = new_scr_fieldy_buffers;
9637 SCR_FIELDX = new_scr_fieldx;
9638 SCR_FIELDY = new_scr_fieldy;
9640 SetDrawDeactivationMask(REDRAW_NONE);
9641 SetDrawBackgroundMask(REDRAW_FIELD);
9644 if (init_video_buffer)
9646 // printf("::: init_video_buffer\n");
9648 FreeAllImageTextures(); // needs old renderer to free the textures
9650 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9651 InitImageTextures();
9654 if (init_gadgets_and_anims)
9656 // printf("::: init_gadgets_and_anims\n");
9659 InitGlobalAnimations();
9662 if (init_em_graphics)
9664 InitGraphicInfo_EM();