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 if (!setup.fade_screens ||
976 fading.fade_mode == FADE_MODE_NONE)
978 if (fade_mode == FADE_MODE_FADE_OUT)
981 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
983 redraw_mask &= ~fade_mask;
988 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
989 draw_border_function);
991 redraw_mask &= ~fade_mask;
993 ClearAutoRepeatKeyEvents();
996 static void SetScreenStates_BeforeFadingIn(void)
998 // temporarily set screen mode for animations to screen after fading in
999 global.anim_status = global.anim_status_next;
1001 // store backbuffer with all animations that will be started after fading in
1002 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
1003 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
1005 // set screen mode for animations back to fading
1006 global.anim_status = GAME_MODE_PSEUDO_FADING;
1009 static void SetScreenStates_AfterFadingIn(void)
1011 // store new source screen (to use correct masked border for fading)
1012 gfx.fade_border_source_status = global.border_status;
1014 global.anim_status = global.anim_status_next;
1017 static void SetScreenStates_BeforeFadingOut(void)
1019 // store new target screen (to use correct masked border for fading)
1020 gfx.fade_border_target_status = game_status;
1022 // set screen mode for animations to fading
1023 global.anim_status = GAME_MODE_PSEUDO_FADING;
1025 // store backbuffer with all animations that will be stopped for fading out
1026 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1027 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1030 static void SetScreenStates_AfterFadingOut(void)
1032 global.border_status = game_status;
1035 void FadeIn(int fade_mask)
1037 SetScreenStates_BeforeFadingIn();
1040 DrawMaskedBorder(REDRAW_ALL);
1043 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1044 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1046 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1050 FADE_SXSIZE = FULL_SXSIZE;
1051 FADE_SYSIZE = FULL_SYSIZE;
1053 // activate virtual buttons depending on upcoming game status
1054 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1055 game_status == GAME_MODE_PLAYING && !tape.playing)
1056 SetOverlayActive(TRUE);
1058 SetScreenStates_AfterFadingIn();
1060 // force update of global animation status in case of rapid screen changes
1061 redraw_mask = REDRAW_ALL;
1065 void FadeOut(int fade_mask)
1067 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1068 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1069 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1072 SetScreenStates_BeforeFadingOut();
1074 SetTileCursorActive(FALSE);
1075 SetOverlayActive(FALSE);
1078 DrawMaskedBorder(REDRAW_ALL);
1081 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1082 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1084 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1086 SetScreenStates_AfterFadingOut();
1089 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1091 static struct TitleFadingInfo fading_leave_stored;
1094 fading_leave_stored = fading_leave;
1096 fading = fading_leave_stored;
1099 void FadeSetEnterMenu(void)
1101 fading = menu.enter_menu;
1103 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1106 void FadeSetLeaveMenu(void)
1108 fading = menu.leave_menu;
1110 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1113 void FadeSetEnterScreen(void)
1115 fading = menu.enter_screen[game_status];
1117 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1120 void FadeSetNextScreen(void)
1122 fading = menu.next_screen[game_status];
1124 // (do not overwrite fade mode set by FadeSetEnterScreen)
1125 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1128 void FadeSetLeaveScreen(void)
1130 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1133 void FadeSetFromType(int type)
1135 if (type & TYPE_ENTER_SCREEN)
1136 FadeSetEnterScreen();
1137 else if (type & TYPE_ENTER)
1139 else if (type & TYPE_LEAVE)
1143 void FadeSetDisabled(void)
1145 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1147 fading = fading_none;
1150 void FadeSkipNextFadeIn(void)
1152 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1155 void FadeSkipNextFadeOut(void)
1157 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1160 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1162 if (graphic == IMG_UNDEFINED)
1165 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1167 return (graphic_info[graphic].bitmap != NULL || redefined ?
1168 graphic_info[graphic].bitmap :
1169 graphic_info[default_graphic].bitmap);
1172 static Bitmap *getBackgroundBitmap(int graphic)
1174 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1177 static Bitmap *getGlobalBorderBitmap(int graphic)
1179 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1182 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1185 (status == GAME_MODE_MAIN ||
1186 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1187 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1188 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1189 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1192 return getGlobalBorderBitmap(graphic);
1195 void SetWindowBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1201 void SetMainBackgroundImageIfDefined(int graphic)
1203 if (graphic_info[graphic].bitmap)
1204 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1207 void SetDoorBackgroundImageIfDefined(int graphic)
1209 if (graphic_info[graphic].bitmap)
1210 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1213 void SetWindowBackgroundImage(int graphic)
1215 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1218 void SetMainBackgroundImage(int graphic)
1220 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1223 void SetDoorBackgroundImage(int graphic)
1225 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1228 void SetPanelBackground(void)
1230 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1232 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1233 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1235 SetDoorBackgroundBitmap(bitmap_db_panel);
1238 void DrawBackground(int x, int y, int width, int height)
1240 // "drawto" might still point to playfield buffer here (hall of fame)
1241 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1243 if (IN_GFX_FIELD_FULL(x, y))
1244 redraw_mask |= REDRAW_FIELD;
1245 else if (IN_GFX_DOOR_1(x, y))
1246 redraw_mask |= REDRAW_DOOR_1;
1247 else if (IN_GFX_DOOR_2(x, y))
1248 redraw_mask |= REDRAW_DOOR_2;
1249 else if (IN_GFX_DOOR_3(x, y))
1250 redraw_mask |= REDRAW_DOOR_3;
1253 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1255 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1257 if (font->bitmap == NULL)
1260 DrawBackground(x, y, width, height);
1263 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1265 struct GraphicInfo *g = &graphic_info[graphic];
1267 if (g->bitmap == NULL)
1270 DrawBackground(x, y, width, height);
1273 static int game_status_last = -1;
1274 static Bitmap *global_border_bitmap_last = NULL;
1275 static Bitmap *global_border_bitmap = NULL;
1276 static int real_sx_last = -1, real_sy_last = -1;
1277 static int full_sxsize_last = -1, full_sysize_last = -1;
1278 static int dx_last = -1, dy_last = -1;
1279 static int dxsize_last = -1, dysize_last = -1;
1280 static int vx_last = -1, vy_last = -1;
1281 static int vxsize_last = -1, vysize_last = -1;
1282 static int ex_last = -1, ey_last = -1;
1283 static int exsize_last = -1, eysize_last = -1;
1285 boolean CheckIfGlobalBorderHasChanged(void)
1287 // if game status has not changed, global border has not changed either
1288 if (game_status == game_status_last)
1291 // determine and store new global border bitmap for current game status
1292 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1294 return (global_border_bitmap_last != global_border_bitmap);
1297 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1299 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1300 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1302 // if game status has not changed, nothing has to be redrawn
1303 if (game_status == game_status_last)
1306 // redraw if last screen was title screen
1307 if (game_status_last == GAME_MODE_TITLE)
1310 // redraw if global screen border has changed
1311 if (CheckIfGlobalBorderHasChanged())
1314 // redraw if position or size of playfield area has changed
1315 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1316 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1319 // redraw if position or size of door area has changed
1320 if (dx_last != DX || dy_last != DY ||
1321 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1324 // redraw if position or size of tape area has changed
1325 if (vx_last != VX || vy_last != VY ||
1326 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1329 // redraw if position or size of editor area has changed
1330 if (ex_last != EX || ey_last != EY ||
1331 exsize_last != EXSIZE || eysize_last != EYSIZE)
1338 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1341 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1343 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1346 void RedrawGlobalBorder(void)
1348 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1350 RedrawGlobalBorderFromBitmap(bitmap);
1352 redraw_mask = REDRAW_ALL;
1355 static void RedrawGlobalBorderIfNeeded(void)
1357 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1358 if (game_status == game_status_last)
1362 // copy current draw buffer to later copy back areas that have not changed
1363 if (game_status_last != GAME_MODE_TITLE)
1364 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1366 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1367 if (CheckIfGlobalBorderRedrawIsNeeded())
1370 // redraw global screen border (or clear, if defined to be empty)
1371 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1373 if (game_status == GAME_MODE_EDITOR)
1374 DrawSpecialEditorDoor();
1376 // copy previous playfield and door areas, if they are defined on both
1377 // previous and current screen and if they still have the same size
1379 if (real_sx_last != -1 && real_sy_last != -1 &&
1380 REAL_SX != -1 && REAL_SY != -1 &&
1381 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1382 BlitBitmap(bitmap_db_store_1, backbuffer,
1383 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1386 if (dx_last != -1 && dy_last != -1 &&
1387 DX != -1 && DY != -1 &&
1388 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1389 BlitBitmap(bitmap_db_store_1, backbuffer,
1390 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1392 if (game_status != GAME_MODE_EDITOR)
1394 if (vx_last != -1 && vy_last != -1 &&
1395 VX != -1 && VY != -1 &&
1396 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1397 BlitBitmap(bitmap_db_store_1, backbuffer,
1398 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1402 if (ex_last != -1 && ey_last != -1 &&
1403 EX != -1 && EY != -1 &&
1404 exsize_last == EXSIZE && eysize_last == EYSIZE)
1405 BlitBitmap(bitmap_db_store_1, backbuffer,
1406 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1409 redraw_mask = REDRAW_ALL;
1412 game_status_last = game_status;
1414 global_border_bitmap_last = global_border_bitmap;
1416 real_sx_last = REAL_SX;
1417 real_sy_last = REAL_SY;
1418 full_sxsize_last = FULL_SXSIZE;
1419 full_sysize_last = FULL_SYSIZE;
1422 dxsize_last = DXSIZE;
1423 dysize_last = DYSIZE;
1426 vxsize_last = VXSIZE;
1427 vysize_last = VYSIZE;
1430 exsize_last = EXSIZE;
1431 eysize_last = EYSIZE;
1434 void ClearField(void)
1436 RedrawGlobalBorderIfNeeded();
1438 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1439 // (when entering hall of fame after playing)
1440 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1442 // !!! maybe this should be done before clearing the background !!!
1443 if (game_status == GAME_MODE_PLAYING)
1445 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1446 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1450 SetDrawtoField(DRAW_TO_BACKBUFFER);
1454 void MarkTileDirty(int x, int y)
1456 redraw_mask |= REDRAW_FIELD;
1459 void SetBorderElement(void)
1463 BorderElement = EL_EMPTY;
1465 // only the R'n'D game engine may use an additional steelwall border
1466 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1469 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1471 for (x = 0; x < lev_fieldx; x++)
1473 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1474 BorderElement = EL_STEELWALL;
1476 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1482 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1483 int max_array_fieldx, int max_array_fieldy,
1484 short field[max_array_fieldx][max_array_fieldy],
1485 int max_fieldx, int max_fieldy)
1489 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1490 static int safety = 0;
1492 // check if starting field still has the desired content
1493 if (field[from_x][from_y] == fill_element)
1498 if (safety > max_fieldx * max_fieldy)
1499 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1501 old_element = field[from_x][from_y];
1502 field[from_x][from_y] = fill_element;
1504 for (i = 0; i < 4; i++)
1506 x = from_x + check[i][0];
1507 y = from_y + check[i][1];
1509 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1510 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1511 field, max_fieldx, max_fieldy);
1517 void FloodFillLevel(int from_x, int from_y, int fill_element,
1518 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1519 int max_fieldx, int max_fieldy)
1521 FloodFillLevelExt(from_x, from_y, fill_element,
1522 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1523 max_fieldx, max_fieldy);
1526 void SetRandomAnimationValue(int x, int y)
1528 gfx.anim_random_frame = GfxRandom[x][y];
1531 int getGraphicAnimationFrame(int graphic, int sync_frame)
1533 // animation synchronized with global frame counter, not move position
1534 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1535 sync_frame = FrameCounter;
1537 return getAnimationFrame(graphic_info[graphic].anim_frames,
1538 graphic_info[graphic].anim_delay,
1539 graphic_info[graphic].anim_mode,
1540 graphic_info[graphic].anim_start_frame,
1544 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1546 struct GraphicInfo *g = &graphic_info[graphic];
1547 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1549 if (tilesize == gfx.standard_tile_size)
1550 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1551 else if (tilesize == game.tile_size)
1552 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1554 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1557 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1558 boolean get_backside)
1560 struct GraphicInfo *g = &graphic_info[graphic];
1561 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1562 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1564 if (g->offset_y == 0) // frames are ordered horizontally
1566 int max_width = g->anim_frames_per_line * g->width;
1567 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1569 *x = pos % max_width;
1570 *y = src_y % g->height + pos / max_width * g->height;
1572 else if (g->offset_x == 0) // frames are ordered vertically
1574 int max_height = g->anim_frames_per_line * g->height;
1575 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1577 *x = src_x % g->width + pos / max_height * g->width;
1578 *y = pos % max_height;
1580 else // frames are ordered diagonally
1582 *x = src_x + frame * g->offset_x;
1583 *y = src_y + frame * g->offset_y;
1587 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1588 Bitmap **bitmap, int *x, int *y,
1589 boolean get_backside)
1591 struct GraphicInfo *g = &graphic_info[graphic];
1593 // if no graphics defined at all, use fallback graphics
1594 if (g->bitmaps == NULL)
1595 *g = graphic_info[IMG_CHAR_EXCLAM];
1597 // if no in-game graphics defined, always use standard graphic size
1598 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1599 tilesize = TILESIZE;
1601 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1602 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1604 *x = *x * tilesize / g->tile_size;
1605 *y = *y * tilesize / g->tile_size;
1608 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1609 Bitmap **bitmap, int *x, int *y)
1611 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1614 void getFixedGraphicSource(int graphic, int frame,
1615 Bitmap **bitmap, int *x, int *y)
1617 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1620 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1622 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1625 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1626 int *x, int *y, boolean get_backside)
1628 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1632 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1634 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1637 void DrawGraphic(int x, int y, int graphic, int frame)
1640 if (!IN_SCR_FIELD(x, y))
1642 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1643 printf("DrawGraphic(): This should never happen!\n");
1648 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1651 MarkTileDirty(x, y);
1654 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1657 if (!IN_SCR_FIELD(x, y))
1659 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1660 printf("DrawGraphic(): This should never happen!\n");
1665 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1667 MarkTileDirty(x, y);
1670 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1676 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1678 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1681 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1687 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1688 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1691 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1694 if (!IN_SCR_FIELD(x, y))
1696 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1697 printf("DrawGraphicThruMask(): This should never happen!\n");
1702 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1705 MarkTileDirty(x, y);
1708 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1711 if (!IN_SCR_FIELD(x, y))
1713 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1714 printf("DrawGraphicThruMask(): This should never happen!\n");
1719 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1721 MarkTileDirty(x, y);
1724 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1730 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1732 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1736 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1737 int graphic, int frame)
1742 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1744 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1748 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1750 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1752 MarkTileDirty(x / tilesize, y / tilesize);
1755 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1758 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1759 graphic, frame, tilesize);
1760 MarkTileDirty(x / tilesize, y / tilesize);
1763 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1769 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1770 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1773 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1774 int frame, int tilesize)
1779 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1780 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1783 void DrawMiniGraphic(int x, int y, int graphic)
1785 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1786 MarkTileDirty(x / 2, y / 2);
1789 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1794 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1795 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1798 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1799 int graphic, int frame,
1800 int cut_mode, int mask_mode)
1805 int width = TILEX, height = TILEY;
1808 if (dx || dy) // shifted graphic
1810 if (x < BX1) // object enters playfield from the left
1817 else if (x > BX2) // object enters playfield from the right
1823 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1829 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1831 else if (dx) // general horizontal movement
1832 MarkTileDirty(x + SIGN(dx), y);
1834 if (y < BY1) // object enters playfield from the top
1836 if (cut_mode == CUT_BELOW) // object completely above top border
1844 else if (y > BY2) // object enters playfield from the bottom
1850 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1856 else if (dy > 0 && cut_mode == CUT_ABOVE)
1858 if (y == BY2) // object completely above bottom border
1864 MarkTileDirty(x, y + 1);
1865 } // object leaves playfield to the bottom
1866 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1868 else if (dy) // general vertical movement
1869 MarkTileDirty(x, y + SIGN(dy));
1873 if (!IN_SCR_FIELD(x, y))
1875 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1876 printf("DrawGraphicShifted(): This should never happen!\n");
1881 width = width * TILESIZE_VAR / TILESIZE;
1882 height = height * TILESIZE_VAR / TILESIZE;
1883 cx = cx * TILESIZE_VAR / TILESIZE;
1884 cy = cy * TILESIZE_VAR / TILESIZE;
1885 dx = dx * TILESIZE_VAR / TILESIZE;
1886 dy = dy * TILESIZE_VAR / TILESIZE;
1888 if (width > 0 && height > 0)
1890 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1895 dst_x = FX + x * TILEX_VAR + dx;
1896 dst_y = FY + y * TILEY_VAR + dy;
1898 if (mask_mode == USE_MASKING)
1899 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1902 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1905 MarkTileDirty(x, y);
1909 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1910 int graphic, int frame,
1911 int cut_mode, int mask_mode)
1916 int width = TILEX_VAR, height = TILEY_VAR;
1919 int x2 = x + SIGN(dx);
1920 int y2 = y + SIGN(dy);
1922 // movement with two-tile animations must be sync'ed with movement position,
1923 // not with current GfxFrame (which can be higher when using slow movement)
1924 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1925 int anim_frames = graphic_info[graphic].anim_frames;
1927 // (we also need anim_delay here for movement animations with less frames)
1928 int anim_delay = graphic_info[graphic].anim_delay;
1929 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1931 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1932 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1934 // re-calculate animation frame for two-tile movement animation
1935 frame = getGraphicAnimationFrame(graphic, sync_frame);
1937 // check if movement start graphic inside screen area and should be drawn
1938 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1940 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1942 dst_x = FX + x1 * TILEX_VAR;
1943 dst_y = FY + y1 * TILEY_VAR;
1945 if (mask_mode == USE_MASKING)
1946 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1949 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1952 MarkTileDirty(x1, y1);
1955 // check if movement end graphic inside screen area and should be drawn
1956 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1958 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1960 dst_x = FX + x2 * TILEX_VAR;
1961 dst_y = FY + y2 * TILEY_VAR;
1963 if (mask_mode == USE_MASKING)
1964 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1967 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1970 MarkTileDirty(x2, y2);
1974 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1975 int graphic, int frame,
1976 int cut_mode, int mask_mode)
1980 DrawGraphic(x, y, graphic, frame);
1985 if (graphic_info[graphic].double_movement) // EM style movement images
1986 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1988 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1991 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1992 int graphic, int frame, int cut_mode)
1994 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1997 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1998 int cut_mode, int mask_mode)
2000 int lx = LEVELX(x), ly = LEVELY(y);
2004 if (IN_LEV_FIELD(lx, ly))
2006 SetRandomAnimationValue(lx, ly);
2008 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2009 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2011 // do not use double (EM style) movement graphic when not moving
2012 if (graphic_info[graphic].double_movement && !dx && !dy)
2014 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2015 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2018 else // border element
2020 graphic = el2img(element);
2021 frame = getGraphicAnimationFrame(graphic, -1);
2024 if (element == EL_EXPANDABLE_WALL)
2026 boolean left_stopped = FALSE, right_stopped = FALSE;
2028 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2029 left_stopped = TRUE;
2030 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2031 right_stopped = TRUE;
2033 if (left_stopped && right_stopped)
2035 else if (left_stopped)
2037 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2038 frame = graphic_info[graphic].anim_frames - 1;
2040 else if (right_stopped)
2042 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2043 frame = graphic_info[graphic].anim_frames - 1;
2048 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2049 else if (mask_mode == USE_MASKING)
2050 DrawGraphicThruMask(x, y, graphic, frame);
2052 DrawGraphic(x, y, graphic, frame);
2055 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2056 int cut_mode, int mask_mode)
2058 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2059 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2060 cut_mode, mask_mode);
2063 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2066 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2069 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2072 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2075 void DrawLevelElementThruMask(int x, int y, int element)
2077 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2080 void DrawLevelFieldThruMask(int x, int y)
2082 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2085 // !!! implementation of quicksand is totally broken !!!
2086 #define IS_CRUMBLED_TILE(x, y, e) \
2087 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2088 !IS_MOVING(x, y) || \
2089 (e) == EL_QUICKSAND_EMPTYING || \
2090 (e) == EL_QUICKSAND_FAST_EMPTYING))
2092 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2097 int width, height, cx, cy;
2098 int sx = SCREENX(x), sy = SCREENY(y);
2099 int crumbled_border_size = graphic_info[graphic].border_size;
2100 int crumbled_tile_size = graphic_info[graphic].tile_size;
2101 int crumbled_border_size_var =
2102 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2105 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2107 for (i = 1; i < 4; i++)
2109 int dxx = (i & 1 ? dx : 0);
2110 int dyy = (i & 2 ? dy : 0);
2113 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2116 // check if neighbour field is of same crumble type
2117 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2118 graphic_info[graphic].class ==
2119 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2121 // return if check prevents inner corner
2122 if (same == (dxx == dx && dyy == dy))
2126 // if we reach this point, we have an inner corner
2128 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2130 width = crumbled_border_size_var;
2131 height = crumbled_border_size_var;
2132 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2133 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2135 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2136 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2139 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2144 int width, height, bx, by, cx, cy;
2145 int sx = SCREENX(x), sy = SCREENY(y);
2146 int crumbled_border_size = graphic_info[graphic].border_size;
2147 int crumbled_tile_size = graphic_info[graphic].tile_size;
2148 int crumbled_border_size_var =
2149 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2150 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2153 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2155 // draw simple, sloppy, non-corner-accurate crumbled border
2157 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2158 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2159 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2160 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2162 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2163 FX + sx * TILEX_VAR + cx,
2164 FY + sy * TILEY_VAR + cy);
2166 // (remaining middle border part must be at least as big as corner part)
2167 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2168 crumbled_border_size_var >= TILESIZE_VAR / 3)
2171 // correct corners of crumbled border, if needed
2173 for (i = -1; i <= 1; i += 2)
2175 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2176 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2177 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2180 // check if neighbour field is of same crumble type
2181 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2182 graphic_info[graphic].class ==
2183 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2185 // no crumbled corner, but continued crumbled border
2187 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2188 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2189 int b1 = (i == 1 ? crumbled_border_size_var :
2190 TILESIZE_VAR - 2 * crumbled_border_size_var);
2192 width = crumbled_border_size_var;
2193 height = crumbled_border_size_var;
2195 if (dir == 1 || dir == 2)
2210 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2212 FX + sx * TILEX_VAR + cx,
2213 FY + sy * TILEY_VAR + cy);
2218 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2220 int sx = SCREENX(x), sy = SCREENY(y);
2223 static int xy[4][2] =
2231 if (!IN_LEV_FIELD(x, y))
2234 element = TILE_GFX_ELEMENT(x, y);
2236 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2238 if (!IN_SCR_FIELD(sx, sy))
2241 // crumble field borders towards direct neighbour fields
2242 for (i = 0; i < 4; i++)
2244 int xx = x + xy[i][0];
2245 int yy = y + xy[i][1];
2247 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2250 // check if neighbour field is of same crumble type
2251 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2252 graphic_info[graphic].class ==
2253 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2256 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2259 // crumble inner field corners towards corner neighbour fields
2260 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2261 graphic_info[graphic].anim_frames == 2)
2263 for (i = 0; i < 4; i++)
2265 int dx = (i & 1 ? +1 : -1);
2266 int dy = (i & 2 ? +1 : -1);
2268 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2272 MarkTileDirty(sx, sy);
2274 else // center field is not crumbled -- crumble neighbour fields
2276 // crumble field borders of direct neighbour fields
2277 for (i = 0; i < 4; i++)
2279 int xx = x + xy[i][0];
2280 int yy = y + xy[i][1];
2281 int sxx = sx + xy[i][0];
2282 int syy = sy + xy[i][1];
2284 if (!IN_LEV_FIELD(xx, yy) ||
2285 !IN_SCR_FIELD(sxx, syy))
2288 // do not crumble fields that are being digged or snapped
2289 if (Feld[xx][yy] == EL_EMPTY ||
2290 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2293 element = TILE_GFX_ELEMENT(xx, yy);
2295 if (!IS_CRUMBLED_TILE(xx, yy, element))
2298 graphic = el_act2crm(element, ACTION_DEFAULT);
2300 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2302 MarkTileDirty(sxx, syy);
2305 // crumble inner field corners of corner neighbour fields
2306 for (i = 0; i < 4; i++)
2308 int dx = (i & 1 ? +1 : -1);
2309 int dy = (i & 2 ? +1 : -1);
2315 if (!IN_LEV_FIELD(xx, yy) ||
2316 !IN_SCR_FIELD(sxx, syy))
2319 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2322 element = TILE_GFX_ELEMENT(xx, yy);
2324 if (!IS_CRUMBLED_TILE(xx, yy, element))
2327 graphic = el_act2crm(element, ACTION_DEFAULT);
2329 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2330 graphic_info[graphic].anim_frames == 2)
2331 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2333 MarkTileDirty(sxx, syy);
2338 void DrawLevelFieldCrumbled(int x, int y)
2342 if (!IN_LEV_FIELD(x, y))
2345 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2346 GfxElement[x][y] != EL_UNDEFINED &&
2347 GFX_CRUMBLED(GfxElement[x][y]))
2349 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2354 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2356 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2359 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2362 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2363 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2364 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2365 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2366 int sx = SCREENX(x), sy = SCREENY(y);
2368 DrawGraphic(sx, sy, graphic1, frame1);
2369 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2372 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2374 int sx = SCREENX(x), sy = SCREENY(y);
2375 static int xy[4][2] =
2384 // crumble direct neighbour fields (required for field borders)
2385 for (i = 0; i < 4; i++)
2387 int xx = x + xy[i][0];
2388 int yy = y + xy[i][1];
2389 int sxx = sx + xy[i][0];
2390 int syy = sy + xy[i][1];
2392 if (!IN_LEV_FIELD(xx, yy) ||
2393 !IN_SCR_FIELD(sxx, syy) ||
2394 !GFX_CRUMBLED(Feld[xx][yy]) ||
2398 DrawLevelField(xx, yy);
2401 // crumble corner neighbour fields (required for inner field corners)
2402 for (i = 0; i < 4; i++)
2404 int dx = (i & 1 ? +1 : -1);
2405 int dy = (i & 2 ? +1 : -1);
2411 if (!IN_LEV_FIELD(xx, yy) ||
2412 !IN_SCR_FIELD(sxx, syy) ||
2413 !GFX_CRUMBLED(Feld[xx][yy]) ||
2417 int element = TILE_GFX_ELEMENT(xx, yy);
2418 int graphic = el_act2crm(element, ACTION_DEFAULT);
2420 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2421 graphic_info[graphic].anim_frames == 2)
2422 DrawLevelField(xx, yy);
2426 static int getBorderElement(int x, int y)
2430 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2431 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2432 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2433 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2434 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2435 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2436 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2438 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2439 int steel_position = (x == -1 && y == -1 ? 0 :
2440 x == lev_fieldx && y == -1 ? 1 :
2441 x == -1 && y == lev_fieldy ? 2 :
2442 x == lev_fieldx && y == lev_fieldy ? 3 :
2443 x == -1 || x == lev_fieldx ? 4 :
2444 y == -1 || y == lev_fieldy ? 5 : 6);
2446 return border[steel_position][steel_type];
2449 void DrawScreenElement(int x, int y, int element)
2451 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2452 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2455 void DrawLevelElement(int x, int y, int element)
2457 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2458 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2461 void DrawScreenField(int x, int y)
2463 int lx = LEVELX(x), ly = LEVELY(y);
2464 int element, content;
2466 if (!IN_LEV_FIELD(lx, ly))
2468 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2471 element = getBorderElement(lx, ly);
2473 DrawScreenElement(x, y, element);
2478 element = Feld[lx][ly];
2479 content = Store[lx][ly];
2481 if (IS_MOVING(lx, ly))
2483 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2484 boolean cut_mode = NO_CUTTING;
2486 if (element == EL_QUICKSAND_EMPTYING ||
2487 element == EL_QUICKSAND_FAST_EMPTYING ||
2488 element == EL_MAGIC_WALL_EMPTYING ||
2489 element == EL_BD_MAGIC_WALL_EMPTYING ||
2490 element == EL_DC_MAGIC_WALL_EMPTYING ||
2491 element == EL_AMOEBA_DROPPING)
2492 cut_mode = CUT_ABOVE;
2493 else if (element == EL_QUICKSAND_FILLING ||
2494 element == EL_QUICKSAND_FAST_FILLING ||
2495 element == EL_MAGIC_WALL_FILLING ||
2496 element == EL_BD_MAGIC_WALL_FILLING ||
2497 element == EL_DC_MAGIC_WALL_FILLING)
2498 cut_mode = CUT_BELOW;
2500 if (cut_mode == CUT_ABOVE)
2501 DrawScreenElement(x, y, element);
2503 DrawScreenElement(x, y, EL_EMPTY);
2506 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2507 else if (cut_mode == NO_CUTTING)
2508 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2511 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2513 if (cut_mode == CUT_BELOW &&
2514 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2515 DrawLevelElement(lx, ly + 1, element);
2518 if (content == EL_ACID)
2520 int dir = MovDir[lx][ly];
2521 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2522 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2524 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2526 // prevent target field from being drawn again (but without masking)
2527 // (this would happen if target field is scanned after moving element)
2528 Stop[newlx][newly] = TRUE;
2531 else if (IS_BLOCKED(lx, ly))
2536 boolean cut_mode = NO_CUTTING;
2537 int element_old, content_old;
2539 Blocked2Moving(lx, ly, &oldx, &oldy);
2542 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2543 MovDir[oldx][oldy] == MV_RIGHT);
2545 element_old = Feld[oldx][oldy];
2546 content_old = Store[oldx][oldy];
2548 if (element_old == EL_QUICKSAND_EMPTYING ||
2549 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2550 element_old == EL_MAGIC_WALL_EMPTYING ||
2551 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2552 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2553 element_old == EL_AMOEBA_DROPPING)
2554 cut_mode = CUT_ABOVE;
2556 DrawScreenElement(x, y, EL_EMPTY);
2559 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2561 else if (cut_mode == NO_CUTTING)
2562 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2565 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2568 else if (IS_DRAWABLE(element))
2569 DrawScreenElement(x, y, element);
2571 DrawScreenElement(x, y, EL_EMPTY);
2574 void DrawLevelField(int x, int y)
2576 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2577 DrawScreenField(SCREENX(x), SCREENY(y));
2578 else if (IS_MOVING(x, y))
2582 Moving2Blocked(x, y, &newx, &newy);
2583 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2584 DrawScreenField(SCREENX(newx), SCREENY(newy));
2586 else if (IS_BLOCKED(x, y))
2590 Blocked2Moving(x, y, &oldx, &oldy);
2591 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2592 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2596 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2597 int (*el2img_function)(int), boolean masked,
2598 int element_bits_draw)
2600 int element_base = map_mm_wall_element(element);
2601 int element_bits = (IS_DF_WALL(element) ?
2602 element - EL_DF_WALL_START :
2603 IS_MM_WALL(element) ?
2604 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2605 int graphic = el2img_function(element_base);
2606 int tilesize_draw = tilesize / 2;
2611 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2613 for (i = 0; i < 4; i++)
2615 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2616 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2618 if (!(element_bits_draw & (1 << i)))
2621 if (element_bits & (1 << i))
2624 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2625 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2627 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2628 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2633 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2634 tilesize_draw, tilesize_draw);
2639 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2640 boolean masked, int element_bits_draw)
2642 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2643 element, tilesize, el2edimg, masked, element_bits_draw);
2646 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2647 int (*el2img_function)(int))
2649 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2653 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2656 if (IS_MM_WALL(element))
2658 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2659 element, tilesize, el2edimg, masked, 0x000f);
2663 int graphic = el2edimg(element);
2666 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2668 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2672 void DrawSizedElement(int x, int y, int element, int tilesize)
2674 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2677 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2679 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2682 void DrawMiniElement(int x, int y, int element)
2686 graphic = el2edimg(element);
2687 DrawMiniGraphic(x, y, graphic);
2690 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2693 int x = sx + scroll_x, y = sy + scroll_y;
2695 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2696 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2697 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2698 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2700 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2703 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2705 int x = sx + scroll_x, y = sy + scroll_y;
2707 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2708 DrawMiniElement(sx, sy, EL_EMPTY);
2709 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2710 DrawMiniElement(sx, sy, Feld[x][y]);
2712 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2715 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2716 int x, int y, int xsize, int ysize,
2717 int tile_width, int tile_height)
2721 int dst_x = startx + x * tile_width;
2722 int dst_y = starty + y * tile_height;
2723 int width = graphic_info[graphic].width;
2724 int height = graphic_info[graphic].height;
2725 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2726 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2727 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2728 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2729 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2730 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2731 boolean draw_masked = graphic_info[graphic].draw_masked;
2733 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2735 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2737 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2741 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2742 inner_sx + (x - 1) * tile_width % inner_width);
2743 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2744 inner_sy + (y - 1) * tile_height % inner_height);
2747 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2750 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2754 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2755 int x, int y, int xsize, int ysize,
2758 int font_width = getFontWidth(font_nr);
2759 int font_height = getFontHeight(font_nr);
2761 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2762 font_width, font_height);
2765 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2767 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2768 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2769 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2770 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2771 boolean no_delay = (tape.warp_forward);
2772 unsigned int anim_delay = 0;
2773 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2774 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2775 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2776 int font_width = getFontWidth(font_nr);
2777 int font_height = getFontHeight(font_nr);
2778 int max_xsize = level.envelope[envelope_nr].xsize;
2779 int max_ysize = level.envelope[envelope_nr].ysize;
2780 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2781 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2782 int xend = max_xsize;
2783 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2784 int xstep = (xstart < xend ? 1 : 0);
2785 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2787 int end = MAX(xend - xstart, yend - ystart);
2790 for (i = start; i <= end; i++)
2792 int last_frame = end; // last frame of this "for" loop
2793 int x = xstart + i * xstep;
2794 int y = ystart + i * ystep;
2795 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2796 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2797 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2798 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2801 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2803 BlitScreenToBitmap(backbuffer);
2805 SetDrawtoField(DRAW_TO_BACKBUFFER);
2807 for (yy = 0; yy < ysize; yy++)
2808 for (xx = 0; xx < xsize; xx++)
2809 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2811 DrawTextBuffer(sx + font_width, sy + font_height,
2812 level.envelope[envelope_nr].text, font_nr, max_xsize,
2813 xsize - 2, ysize - 2, 0, mask_mode,
2814 level.envelope[envelope_nr].autowrap,
2815 level.envelope[envelope_nr].centered, FALSE);
2817 redraw_mask |= REDRAW_FIELD;
2820 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2823 ClearAutoRepeatKeyEvents();
2826 void ShowEnvelope(int envelope_nr)
2828 int element = EL_ENVELOPE_1 + envelope_nr;
2829 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2830 int sound_opening = element_info[element].sound[ACTION_OPENING];
2831 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2832 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2833 boolean no_delay = (tape.warp_forward);
2834 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2835 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2836 int anim_mode = graphic_info[graphic].anim_mode;
2837 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2838 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2840 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2842 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2844 if (anim_mode == ANIM_DEFAULT)
2845 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2847 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2850 Delay_WithScreenUpdates(wait_delay_value);
2852 WaitForEventToContinue();
2854 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2856 if (anim_mode != ANIM_NONE)
2857 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2859 if (anim_mode == ANIM_DEFAULT)
2860 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2862 game.envelope_active = FALSE;
2864 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2866 redraw_mask |= REDRAW_FIELD;
2870 static void setRequestBasePosition(int *x, int *y)
2872 int sx_base, sy_base;
2874 if (request.x != -1)
2875 sx_base = request.x;
2876 else if (request.align == ALIGN_LEFT)
2878 else if (request.align == ALIGN_RIGHT)
2879 sx_base = SX + SXSIZE;
2881 sx_base = SX + SXSIZE / 2;
2883 if (request.y != -1)
2884 sy_base = request.y;
2885 else if (request.valign == VALIGN_TOP)
2887 else if (request.valign == VALIGN_BOTTOM)
2888 sy_base = SY + SYSIZE;
2890 sy_base = SY + SYSIZE / 2;
2896 static void setRequestPositionExt(int *x, int *y, int width, int height,
2897 boolean add_border_size)
2899 int border_size = request.border_size;
2900 int sx_base, sy_base;
2903 setRequestBasePosition(&sx_base, &sy_base);
2905 if (request.align == ALIGN_LEFT)
2907 else if (request.align == ALIGN_RIGHT)
2908 sx = sx_base - width;
2910 sx = sx_base - width / 2;
2912 if (request.valign == VALIGN_TOP)
2914 else if (request.valign == VALIGN_BOTTOM)
2915 sy = sy_base - height;
2917 sy = sy_base - height / 2;
2919 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2920 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2922 if (add_border_size)
2932 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2934 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2937 static void DrawEnvelopeRequest(char *text)
2939 char *text_final = text;
2940 char *text_door_style = NULL;
2941 int graphic = IMG_BACKGROUND_REQUEST;
2942 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2943 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2944 int font_nr = FONT_REQUEST;
2945 int font_width = getFontWidth(font_nr);
2946 int font_height = getFontHeight(font_nr);
2947 int border_size = request.border_size;
2948 int line_spacing = request.line_spacing;
2949 int line_height = font_height + line_spacing;
2950 int max_text_width = request.width - 2 * border_size;
2951 int max_text_height = request.height - 2 * border_size;
2952 int line_length = max_text_width / font_width;
2953 int max_lines = max_text_height / line_height;
2954 int text_width = line_length * font_width;
2955 int width = request.width;
2956 int height = request.height;
2957 int tile_size = MAX(request.step_offset, 1);
2958 int x_steps = width / tile_size;
2959 int y_steps = height / tile_size;
2960 int sx_offset = border_size;
2961 int sy_offset = border_size;
2965 if (request.centered)
2966 sx_offset = (request.width - text_width) / 2;
2968 if (request.wrap_single_words && !request.autowrap)
2970 char *src_text_ptr, *dst_text_ptr;
2972 text_door_style = checked_malloc(2 * strlen(text) + 1);
2974 src_text_ptr = text;
2975 dst_text_ptr = text_door_style;
2977 while (*src_text_ptr)
2979 if (*src_text_ptr == ' ' ||
2980 *src_text_ptr == '?' ||
2981 *src_text_ptr == '!')
2982 *dst_text_ptr++ = '\n';
2984 if (*src_text_ptr != ' ')
2985 *dst_text_ptr++ = *src_text_ptr;
2990 *dst_text_ptr = '\0';
2992 text_final = text_door_style;
2995 setRequestPosition(&sx, &sy, FALSE);
2997 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2999 for (y = 0; y < y_steps; y++)
3000 for (x = 0; x < x_steps; x++)
3001 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3002 x, y, x_steps, y_steps,
3003 tile_size, tile_size);
3005 // force DOOR font inside door area
3006 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3008 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3009 line_length, -1, max_lines, line_spacing, mask_mode,
3010 request.autowrap, request.centered, FALSE);
3014 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3015 RedrawGadget(tool_gadget[i]);
3017 // store readily prepared envelope request for later use when animating
3018 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3020 if (text_door_style)
3021 free(text_door_style);
3024 static void AnimateEnvelopeRequest(int anim_mode, int action)
3026 int graphic = IMG_BACKGROUND_REQUEST;
3027 boolean draw_masked = graphic_info[graphic].draw_masked;
3028 int delay_value_normal = request.step_delay;
3029 int delay_value_fast = delay_value_normal / 2;
3030 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3031 boolean no_delay = (tape.warp_forward);
3032 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3033 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3034 unsigned int anim_delay = 0;
3036 int tile_size = MAX(request.step_offset, 1);
3037 int max_xsize = request.width / tile_size;
3038 int max_ysize = request.height / tile_size;
3039 int max_xsize_inner = max_xsize - 2;
3040 int max_ysize_inner = max_ysize - 2;
3042 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3043 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3044 int xend = max_xsize_inner;
3045 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3046 int xstep = (xstart < xend ? 1 : 0);
3047 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3049 int end = MAX(xend - xstart, yend - ystart);
3052 if (setup.quick_doors)
3059 for (i = start; i <= end; i++)
3061 int last_frame = end; // last frame of this "for" loop
3062 int x = xstart + i * xstep;
3063 int y = ystart + i * ystep;
3064 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3065 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3066 int xsize_size_left = (xsize - 1) * tile_size;
3067 int ysize_size_top = (ysize - 1) * tile_size;
3068 int max_xsize_pos = (max_xsize - 1) * tile_size;
3069 int max_ysize_pos = (max_ysize - 1) * tile_size;
3070 int width = xsize * tile_size;
3071 int height = ysize * tile_size;
3076 setRequestPosition(&src_x, &src_y, FALSE);
3077 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3079 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3081 for (yy = 0; yy < 2; yy++)
3083 for (xx = 0; xx < 2; xx++)
3085 int src_xx = src_x + xx * max_xsize_pos;
3086 int src_yy = src_y + yy * max_ysize_pos;
3087 int dst_xx = dst_x + xx * xsize_size_left;
3088 int dst_yy = dst_y + yy * ysize_size_top;
3089 int xx_size = (xx ? tile_size : xsize_size_left);
3090 int yy_size = (yy ? tile_size : ysize_size_top);
3093 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3094 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3096 BlitBitmap(bitmap_db_store_2, backbuffer,
3097 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3101 redraw_mask |= REDRAW_FIELD;
3105 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3108 ClearAutoRepeatKeyEvents();
3111 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3113 int graphic = IMG_BACKGROUND_REQUEST;
3114 int sound_opening = SND_REQUEST_OPENING;
3115 int sound_closing = SND_REQUEST_CLOSING;
3116 int anim_mode_1 = request.anim_mode; // (higher priority)
3117 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3118 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3119 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3120 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3122 if (game_status == GAME_MODE_PLAYING)
3123 BlitScreenToBitmap(backbuffer);
3125 SetDrawtoField(DRAW_TO_BACKBUFFER);
3127 // SetDrawBackgroundMask(REDRAW_NONE);
3129 if (action == ACTION_OPENING)
3131 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3133 if (req_state & REQ_ASK)
3135 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3136 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3140 else if (req_state & REQ_CONFIRM)
3142 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3143 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3145 else if (req_state & REQ_PLAYER)
3147 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3148 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3149 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3150 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3153 DrawEnvelopeRequest(text);
3156 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3158 if (action == ACTION_OPENING)
3160 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3162 if (anim_mode == ANIM_DEFAULT)
3163 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3165 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3169 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3171 if (anim_mode != ANIM_NONE)
3172 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3174 if (anim_mode == ANIM_DEFAULT)
3175 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3178 game.envelope_active = FALSE;
3180 if (action == ACTION_CLOSING)
3181 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3183 // SetDrawBackgroundMask(last_draw_background_mask);
3185 redraw_mask |= REDRAW_FIELD;
3189 if (action == ACTION_CLOSING &&
3190 game_status == GAME_MODE_PLAYING &&
3191 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3192 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3195 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3197 if (IS_MM_WALL(element))
3199 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3205 int graphic = el2preimg(element);
3207 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3208 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3213 void DrawLevel(int draw_background_mask)
3217 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3218 SetDrawBackgroundMask(draw_background_mask);
3222 for (x = BX1; x <= BX2; x++)
3223 for (y = BY1; y <= BY2; y++)
3224 DrawScreenField(x, y);
3226 redraw_mask |= REDRAW_FIELD;
3229 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3234 for (x = 0; x < size_x; x++)
3235 for (y = 0; y < size_y; y++)
3236 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3238 redraw_mask |= REDRAW_FIELD;
3241 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3245 for (x = 0; x < size_x; x++)
3246 for (y = 0; y < size_y; y++)
3247 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3249 redraw_mask |= REDRAW_FIELD;
3252 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3254 boolean show_level_border = (BorderElement != EL_EMPTY);
3255 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3256 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3257 int tile_size = preview.tile_size;
3258 int preview_width = preview.xsize * tile_size;
3259 int preview_height = preview.ysize * tile_size;
3260 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3261 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3262 int real_preview_width = real_preview_xsize * tile_size;
3263 int real_preview_height = real_preview_ysize * tile_size;
3264 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3265 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3268 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3271 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3273 dst_x += (preview_width - real_preview_width) / 2;
3274 dst_y += (preview_height - real_preview_height) / 2;
3276 for (x = 0; x < real_preview_xsize; x++)
3278 for (y = 0; y < real_preview_ysize; y++)
3280 int lx = from_x + x + (show_level_border ? -1 : 0);
3281 int ly = from_y + y + (show_level_border ? -1 : 0);
3282 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3283 getBorderElement(lx, ly));
3285 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3286 element, tile_size);
3290 redraw_mask |= REDRAW_FIELD;
3293 #define MICROLABEL_EMPTY 0
3294 #define MICROLABEL_LEVEL_NAME 1
3295 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3296 #define MICROLABEL_LEVEL_AUTHOR 3
3297 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3298 #define MICROLABEL_IMPORTED_FROM 5
3299 #define MICROLABEL_IMPORTED_BY_HEAD 6
3300 #define MICROLABEL_IMPORTED_BY 7
3302 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3304 int max_text_width = SXSIZE;
3305 int font_width = getFontWidth(font_nr);
3307 if (pos->align == ALIGN_CENTER)
3308 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3309 else if (pos->align == ALIGN_RIGHT)
3310 max_text_width = pos->x;
3312 max_text_width = SXSIZE - pos->x;
3314 return max_text_width / font_width;
3317 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3319 char label_text[MAX_OUTPUT_LINESIZE + 1];
3320 int max_len_label_text;
3321 int font_nr = pos->font;
3324 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3327 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3328 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3329 mode == MICROLABEL_IMPORTED_BY_HEAD)
3330 font_nr = pos->font_alt;
3332 max_len_label_text = getMaxTextLength(pos, font_nr);
3334 if (pos->size != -1)
3335 max_len_label_text = pos->size;
3337 for (i = 0; i < max_len_label_text; i++)
3338 label_text[i] = ' ';
3339 label_text[max_len_label_text] = '\0';
3341 if (strlen(label_text) > 0)
3342 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3345 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3346 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3347 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3348 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3349 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3350 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3351 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3352 max_len_label_text);
3353 label_text[max_len_label_text] = '\0';
3355 if (strlen(label_text) > 0)
3356 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3358 redraw_mask |= REDRAW_FIELD;
3361 static void DrawPreviewLevelLabel(int mode)
3363 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3366 static void DrawPreviewLevelInfo(int mode)
3368 if (mode == MICROLABEL_LEVEL_NAME)
3369 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3370 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3371 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3374 static void DrawPreviewLevelExt(boolean restart)
3376 static unsigned int scroll_delay = 0;
3377 static unsigned int label_delay = 0;
3378 static int from_x, from_y, scroll_direction;
3379 static int label_state, label_counter;
3380 unsigned int scroll_delay_value = preview.step_delay;
3381 boolean show_level_border = (BorderElement != EL_EMPTY);
3382 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3383 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3390 if (preview.anim_mode == ANIM_CENTERED)
3392 if (level_xsize > preview.xsize)
3393 from_x = (level_xsize - preview.xsize) / 2;
3394 if (level_ysize > preview.ysize)
3395 from_y = (level_ysize - preview.ysize) / 2;
3398 from_x += preview.xoffset;
3399 from_y += preview.yoffset;
3401 scroll_direction = MV_RIGHT;
3405 DrawPreviewLevelPlayfield(from_x, from_y);
3406 DrawPreviewLevelLabel(label_state);
3408 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3409 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3411 // initialize delay counters
3412 DelayReached(&scroll_delay, 0);
3413 DelayReached(&label_delay, 0);
3415 if (leveldir_current->name)
3417 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3418 char label_text[MAX_OUTPUT_LINESIZE + 1];
3419 int font_nr = pos->font;
3420 int max_len_label_text = getMaxTextLength(pos, font_nr);
3422 if (pos->size != -1)
3423 max_len_label_text = pos->size;
3425 strncpy(label_text, leveldir_current->name, max_len_label_text);
3426 label_text[max_len_label_text] = '\0';
3428 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3429 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3435 // scroll preview level, if needed
3436 if (preview.anim_mode != ANIM_NONE &&
3437 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3438 DelayReached(&scroll_delay, scroll_delay_value))
3440 switch (scroll_direction)
3445 from_x -= preview.step_offset;
3446 from_x = (from_x < 0 ? 0 : from_x);
3449 scroll_direction = MV_UP;
3453 if (from_x < level_xsize - preview.xsize)
3455 from_x += preview.step_offset;
3456 from_x = (from_x > level_xsize - preview.xsize ?
3457 level_xsize - preview.xsize : from_x);
3460 scroll_direction = MV_DOWN;
3466 from_y -= preview.step_offset;
3467 from_y = (from_y < 0 ? 0 : from_y);
3470 scroll_direction = MV_RIGHT;
3474 if (from_y < level_ysize - preview.ysize)
3476 from_y += preview.step_offset;
3477 from_y = (from_y > level_ysize - preview.ysize ?
3478 level_ysize - preview.ysize : from_y);
3481 scroll_direction = MV_LEFT;
3488 DrawPreviewLevelPlayfield(from_x, from_y);
3491 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3492 // redraw micro level label, if needed
3493 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3494 !strEqual(level.author, ANONYMOUS_NAME) &&
3495 !strEqual(level.author, leveldir_current->name) &&
3496 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3498 int max_label_counter = 23;
3500 if (leveldir_current->imported_from != NULL &&
3501 strlen(leveldir_current->imported_from) > 0)
3502 max_label_counter += 14;
3503 if (leveldir_current->imported_by != NULL &&
3504 strlen(leveldir_current->imported_by) > 0)
3505 max_label_counter += 14;
3507 label_counter = (label_counter + 1) % max_label_counter;
3508 label_state = (label_counter >= 0 && label_counter <= 7 ?
3509 MICROLABEL_LEVEL_NAME :
3510 label_counter >= 9 && label_counter <= 12 ?
3511 MICROLABEL_LEVEL_AUTHOR_HEAD :
3512 label_counter >= 14 && label_counter <= 21 ?
3513 MICROLABEL_LEVEL_AUTHOR :
3514 label_counter >= 23 && label_counter <= 26 ?
3515 MICROLABEL_IMPORTED_FROM_HEAD :
3516 label_counter >= 28 && label_counter <= 35 ?
3517 MICROLABEL_IMPORTED_FROM :
3518 label_counter >= 37 && label_counter <= 40 ?
3519 MICROLABEL_IMPORTED_BY_HEAD :
3520 label_counter >= 42 && label_counter <= 49 ?
3521 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3523 if (leveldir_current->imported_from == NULL &&
3524 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3525 label_state == MICROLABEL_IMPORTED_FROM))
3526 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3527 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3529 DrawPreviewLevelLabel(label_state);
3533 void DrawPreviewPlayers(void)
3535 if (game_status != GAME_MODE_MAIN)
3538 // do not draw preview players if level preview redefined, but players aren't
3539 if (preview.redefined && !menu.main.preview_players.redefined)
3542 boolean player_found[MAX_PLAYERS];
3543 int num_players = 0;
3546 for (i = 0; i < MAX_PLAYERS; i++)
3547 player_found[i] = FALSE;
3549 // check which players can be found in the level (simple approach)
3550 for (x = 0; x < lev_fieldx; x++)
3552 for (y = 0; y < lev_fieldy; y++)
3554 int element = level.field[x][y];
3556 if (ELEM_IS_PLAYER(element))
3558 int player_nr = GET_PLAYER_NR(element);
3560 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3562 if (!player_found[player_nr])
3565 player_found[player_nr] = TRUE;
3570 struct TextPosInfo *pos = &menu.main.preview_players;
3571 int tile_size = pos->tile_size;
3572 int border_size = pos->border_size;
3573 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3574 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3575 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3576 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3577 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3578 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3579 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3580 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3581 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3582 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3583 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3584 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3586 // clear area in which the players will be drawn
3587 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3588 max_players_width, max_players_height);
3590 if (!network.enabled && !setup.team_mode)
3593 // only draw players if level is suited for team mode
3594 if (num_players < 2)
3597 // draw all players that were found in the level
3598 for (i = 0; i < MAX_PLAYERS; i++)
3600 if (player_found[i])
3602 int graphic = el2img(EL_PLAYER_1 + i);
3604 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3606 xpos += player_xoffset;
3607 ypos += player_yoffset;
3612 void DrawPreviewLevelInitial(void)
3614 DrawPreviewLevelExt(TRUE);
3615 DrawPreviewPlayers();
3618 void DrawPreviewLevelAnimation(void)
3620 DrawPreviewLevelExt(FALSE);
3623 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3624 int border_size, int font_nr)
3626 int graphic = el2img(EL_PLAYER_1 + player_nr);
3627 int font_height = getFontHeight(font_nr);
3628 int player_height = MAX(tile_size, font_height);
3629 int xoffset_text = tile_size + border_size;
3630 int yoffset_text = (player_height - font_height) / 2;
3631 int yoffset_graphic = (player_height - tile_size) / 2;
3632 char *player_name = getNetworkPlayerName(player_nr + 1);
3634 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3636 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3639 static void DrawNetworkPlayersExt(boolean force)
3641 if (game_status != GAME_MODE_MAIN)
3644 if (!network.connected && !force)
3647 // do not draw network players if level preview redefined, but players aren't
3648 if (preview.redefined && !menu.main.network_players.redefined)
3651 int num_players = 0;
3654 for (i = 0; i < MAX_PLAYERS; i++)
3655 if (stored_player[i].connected_network)
3658 struct TextPosInfo *pos = &menu.main.network_players;
3659 int tile_size = pos->tile_size;
3660 int border_size = pos->border_size;
3661 int xoffset_text = tile_size + border_size;
3662 int font_nr = pos->font;
3663 int font_width = getFontWidth(font_nr);
3664 int font_height = getFontHeight(font_nr);
3665 int player_height = MAX(tile_size, font_height);
3666 int player_yoffset = player_height + border_size;
3667 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3668 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3669 int all_players_height = num_players * player_yoffset - border_size;
3670 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3671 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3672 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3674 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3675 max_players_width, max_players_height);
3677 // first draw local network player ...
3678 for (i = 0; i < MAX_PLAYERS; i++)
3680 if (stored_player[i].connected_network &&
3681 stored_player[i].connected_locally)
3683 char *player_name = getNetworkPlayerName(i + 1);
3684 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3685 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3687 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3689 ypos += player_yoffset;
3693 // ... then draw all other network players
3694 for (i = 0; i < MAX_PLAYERS; i++)
3696 if (stored_player[i].connected_network &&
3697 !stored_player[i].connected_locally)
3699 char *player_name = getNetworkPlayerName(i + 1);
3700 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3701 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3703 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3705 ypos += player_yoffset;
3710 void DrawNetworkPlayers(void)
3712 DrawNetworkPlayersExt(FALSE);
3715 void ClearNetworkPlayers(void)
3717 DrawNetworkPlayersExt(TRUE);
3720 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3721 int graphic, int sync_frame,
3724 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3726 if (mask_mode == USE_MASKING)
3727 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3729 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3732 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3733 int graphic, int sync_frame, int mask_mode)
3735 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3737 if (mask_mode == USE_MASKING)
3738 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3740 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3743 static void DrawGraphicAnimation(int x, int y, int graphic)
3745 int lx = LEVELX(x), ly = LEVELY(y);
3747 if (!IN_SCR_FIELD(x, y))
3750 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3751 graphic, GfxFrame[lx][ly], NO_MASKING);
3753 MarkTileDirty(x, y);
3756 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3758 int lx = LEVELX(x), ly = LEVELY(y);
3760 if (!IN_SCR_FIELD(x, y))
3763 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3764 graphic, GfxFrame[lx][ly], NO_MASKING);
3765 MarkTileDirty(x, y);
3768 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3770 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3773 void DrawLevelElementAnimation(int x, int y, int element)
3775 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3777 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3780 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3782 int sx = SCREENX(x), sy = SCREENY(y);
3784 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3787 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3790 DrawGraphicAnimation(sx, sy, graphic);
3793 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3794 DrawLevelFieldCrumbled(x, y);
3796 if (GFX_CRUMBLED(Feld[x][y]))
3797 DrawLevelFieldCrumbled(x, y);
3801 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3803 int sx = SCREENX(x), sy = SCREENY(y);
3806 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3809 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3811 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3814 DrawGraphicAnimation(sx, sy, graphic);
3816 if (GFX_CRUMBLED(element))
3817 DrawLevelFieldCrumbled(x, y);
3820 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3822 if (player->use_murphy)
3824 // this works only because currently only one player can be "murphy" ...
3825 static int last_horizontal_dir = MV_LEFT;
3826 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3828 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3829 last_horizontal_dir = move_dir;
3831 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3833 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3835 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3841 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3844 static boolean equalGraphics(int graphic1, int graphic2)
3846 struct GraphicInfo *g1 = &graphic_info[graphic1];
3847 struct GraphicInfo *g2 = &graphic_info[graphic2];
3849 return (g1->bitmap == g2->bitmap &&
3850 g1->src_x == g2->src_x &&
3851 g1->src_y == g2->src_y &&
3852 g1->anim_frames == g2->anim_frames &&
3853 g1->anim_delay == g2->anim_delay &&
3854 g1->anim_mode == g2->anim_mode);
3857 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3861 DRAW_PLAYER_STAGE_INIT = 0,
3862 DRAW_PLAYER_STAGE_LAST_FIELD,
3863 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3864 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3865 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3866 DRAW_PLAYER_STAGE_PLAYER,
3868 DRAW_PLAYER_STAGE_PLAYER,
3869 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3871 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3872 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3874 NUM_DRAW_PLAYER_STAGES
3877 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3879 static int static_last_player_graphic[MAX_PLAYERS];
3880 static int static_last_player_frame[MAX_PLAYERS];
3881 static boolean static_player_is_opaque[MAX_PLAYERS];
3882 static boolean draw_player[MAX_PLAYERS];
3883 int pnr = player->index_nr;
3885 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3887 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3888 static_last_player_frame[pnr] = player->Frame;
3889 static_player_is_opaque[pnr] = FALSE;
3891 draw_player[pnr] = TRUE;
3894 if (!draw_player[pnr])
3898 if (!IN_LEV_FIELD(player->jx, player->jy))
3900 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3901 printf("DrawPlayerField(): This should never happen!\n");
3903 draw_player[pnr] = FALSE;
3909 int last_player_graphic = static_last_player_graphic[pnr];
3910 int last_player_frame = static_last_player_frame[pnr];
3911 boolean player_is_opaque = static_player_is_opaque[pnr];
3913 int jx = player->jx;
3914 int jy = player->jy;
3915 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3916 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3917 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3918 int last_jx = (player->is_moving ? jx - dx : jx);
3919 int last_jy = (player->is_moving ? jy - dy : jy);
3920 int next_jx = jx + dx;
3921 int next_jy = jy + dy;
3922 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3923 int sx = SCREENX(jx);
3924 int sy = SCREENY(jy);
3925 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3926 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3927 int element = Feld[jx][jy];
3928 int last_element = Feld[last_jx][last_jy];
3929 int action = (player->is_pushing ? ACTION_PUSHING :
3930 player->is_digging ? ACTION_DIGGING :
3931 player->is_collecting ? ACTION_COLLECTING :
3932 player->is_moving ? ACTION_MOVING :
3933 player->is_snapping ? ACTION_SNAPPING :
3934 player->is_dropping ? ACTION_DROPPING :
3935 player->is_waiting ? player->action_waiting :
3938 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3940 // ------------------------------------------------------------------------
3941 // initialize drawing the player
3942 // ------------------------------------------------------------------------
3944 draw_player[pnr] = FALSE;
3946 // GfxElement[][] is set to the element the player is digging or collecting;
3947 // remove also for off-screen player if the player is not moving anymore
3948 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3949 GfxElement[jx][jy] = EL_UNDEFINED;
3951 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3954 if (element == EL_EXPLOSION)
3957 InitPlayerGfxAnimation(player, action, move_dir);
3959 draw_player[pnr] = TRUE;
3961 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3963 // ------------------------------------------------------------------------
3964 // draw things in the field the player is leaving, if needed
3965 // ------------------------------------------------------------------------
3967 if (!IN_SCR_FIELD(sx, sy))
3968 draw_player[pnr] = FALSE;
3970 if (!player->is_moving)
3973 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3975 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3977 if (last_element == EL_DYNAMITE_ACTIVE ||
3978 last_element == EL_EM_DYNAMITE_ACTIVE ||
3979 last_element == EL_SP_DISK_RED_ACTIVE)
3980 DrawDynamite(last_jx, last_jy);
3982 DrawLevelFieldThruMask(last_jx, last_jy);
3984 else if (last_element == EL_DYNAMITE_ACTIVE ||
3985 last_element == EL_EM_DYNAMITE_ACTIVE ||
3986 last_element == EL_SP_DISK_RED_ACTIVE)
3987 DrawDynamite(last_jx, last_jy);
3989 DrawLevelField(last_jx, last_jy);
3991 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3992 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3994 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3996 // ------------------------------------------------------------------------
3997 // draw things behind the player, if needed
3998 // ------------------------------------------------------------------------
4002 DrawLevelElement(jx, jy, Back[jx][jy]);
4007 if (IS_ACTIVE_BOMB(element))
4009 DrawLevelElement(jx, jy, EL_EMPTY);
4014 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4016 int old_element = GfxElement[jx][jy];
4017 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4018 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4020 if (GFX_CRUMBLED(old_element))
4021 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4023 DrawGraphic(sx, sy, old_graphic, frame);
4025 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4026 static_player_is_opaque[pnr] = TRUE;
4030 GfxElement[jx][jy] = EL_UNDEFINED;
4032 // make sure that pushed elements are drawn with correct frame rate
4033 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4035 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4036 GfxFrame[jx][jy] = player->StepFrame;
4038 DrawLevelField(jx, jy);
4041 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4043 // ------------------------------------------------------------------------
4044 // draw things the player is pushing, if needed
4045 // ------------------------------------------------------------------------
4047 if (!player->is_pushing || !player->is_moving)
4050 int gfx_frame = GfxFrame[jx][jy];
4052 if (!IS_MOVING(jx, jy)) // push movement already finished
4054 element = Feld[next_jx][next_jy];
4055 gfx_frame = GfxFrame[next_jx][next_jy];
4058 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4059 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4060 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4062 // draw background element under pushed element (like the Sokoban field)
4063 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4065 // this allows transparent pushing animation over non-black background
4068 DrawLevelElement(jx, jy, Back[jx][jy]);
4070 DrawLevelElement(jx, jy, EL_EMPTY);
4072 if (Back[next_jx][next_jy])
4073 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4075 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4077 else if (Back[next_jx][next_jy])
4078 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4080 int px = SCREENX(jx), py = SCREENY(jy);
4081 int pxx = (TILEX - ABS(sxx)) * dx;
4082 int pyy = (TILEY - ABS(syy)) * dy;
4085 // do not draw (EM style) pushing animation when pushing is finished
4086 // (two-tile animations usually do not contain start and end frame)
4087 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4088 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4090 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4092 // masked drawing is needed for EMC style (double) movement graphics
4093 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4094 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4097 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4099 // ------------------------------------------------------------------------
4100 // draw player himself
4101 // ------------------------------------------------------------------------
4103 int graphic = getPlayerGraphic(player, move_dir);
4105 // in the case of changed player action or direction, prevent the current
4106 // animation frame from being restarted for identical animations
4107 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4108 player->Frame = last_player_frame;
4110 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4112 if (player_is_opaque)
4113 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4115 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4117 if (SHIELD_ON(player))
4119 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4120 IMG_SHIELD_NORMAL_ACTIVE);
4121 frame = getGraphicAnimationFrame(graphic, -1);
4123 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4126 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4128 // ------------------------------------------------------------------------
4129 // draw things in front of player (active dynamite or dynabombs)
4130 // ------------------------------------------------------------------------
4132 if (IS_ACTIVE_BOMB(element))
4134 int graphic = el2img(element);
4135 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4137 if (game.emulation == EMU_SUPAPLEX)
4138 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4140 DrawGraphicThruMask(sx, sy, graphic, frame);
4143 if (player_is_moving && last_element == EL_EXPLOSION)
4145 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4146 GfxElement[last_jx][last_jy] : EL_EMPTY);
4147 int graphic = el_act2img(element, ACTION_EXPLODING);
4148 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4149 int phase = ExplodePhase[last_jx][last_jy] - 1;
4150 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4153 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4156 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4158 // ------------------------------------------------------------------------
4159 // draw elements the player is just walking/passing through/under
4160 // ------------------------------------------------------------------------
4162 if (player_is_moving)
4164 // handle the field the player is leaving ...
4165 if (IS_ACCESSIBLE_INSIDE(last_element))
4166 DrawLevelField(last_jx, last_jy);
4167 else if (IS_ACCESSIBLE_UNDER(last_element))
4168 DrawLevelFieldThruMask(last_jx, last_jy);
4171 // do not redraw accessible elements if the player is just pushing them
4172 if (!player_is_moving || !player->is_pushing)
4174 // ... and the field the player is entering
4175 if (IS_ACCESSIBLE_INSIDE(element))
4176 DrawLevelField(jx, jy);
4177 else if (IS_ACCESSIBLE_UNDER(element))
4178 DrawLevelFieldThruMask(jx, jy);
4181 MarkTileDirty(sx, sy);
4185 void DrawPlayer(struct PlayerInfo *player)
4189 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4190 DrawPlayerExt(player, i);
4193 void DrawAllPlayers(void)
4197 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4198 for (j = 0; j < MAX_PLAYERS; j++)
4199 if (stored_player[j].active)
4200 DrawPlayerExt(&stored_player[j], i);
4203 void DrawPlayerField(int x, int y)
4205 if (!IS_PLAYER(x, y))
4208 DrawPlayer(PLAYERINFO(x, y));
4211 // ----------------------------------------------------------------------------
4213 void WaitForEventToContinue(void)
4215 boolean still_wait = TRUE;
4217 if (program.headless)
4220 // simulate releasing mouse button over last gadget, if still pressed
4222 HandleGadgets(-1, -1, 0);
4224 button_status = MB_RELEASED;
4232 if (NextValidEvent(&event))
4236 case EVENT_BUTTONRELEASE:
4237 case EVENT_KEYPRESS:
4238 case SDL_CONTROLLERBUTTONDOWN:
4239 case SDL_JOYBUTTONDOWN:
4243 case EVENT_KEYRELEASE:
4244 ClearPlayerAction();
4248 HandleOtherEvents(&event);
4252 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4261 #define MAX_REQUEST_LINES 13
4262 #define MAX_REQUEST_LINE_FONT1_LEN 7
4263 #define MAX_REQUEST_LINE_FONT2_LEN 10
4265 static int RequestHandleEvents(unsigned int req_state)
4267 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4269 int width = request.width;
4270 int height = request.height;
4274 // when showing request dialog after game ended, deactivate game panel
4275 if (game_just_ended)
4276 game.panel.active = FALSE;
4278 game.request_active = TRUE;
4280 setRequestPosition(&sx, &sy, FALSE);
4282 button_status = MB_RELEASED;
4284 request_gadget_id = -1;
4289 if (game_just_ended)
4291 // the MM game engine does not use a special (scrollable) field buffer
4292 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4293 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4295 HandleGameActions();
4297 SetDrawtoField(DRAW_TO_BACKBUFFER);
4299 if (global.use_envelope_request)
4301 // copy current state of request area to middle of playfield area
4302 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4310 while (NextValidEvent(&event))
4314 case EVENT_BUTTONPRESS:
4315 case EVENT_BUTTONRELEASE:
4316 case EVENT_MOTIONNOTIFY:
4320 if (event.type == EVENT_MOTIONNOTIFY)
4325 motion_status = TRUE;
4326 mx = ((MotionEvent *) &event)->x;
4327 my = ((MotionEvent *) &event)->y;
4331 motion_status = FALSE;
4332 mx = ((ButtonEvent *) &event)->x;
4333 my = ((ButtonEvent *) &event)->y;
4334 if (event.type == EVENT_BUTTONPRESS)
4335 button_status = ((ButtonEvent *) &event)->button;
4337 button_status = MB_RELEASED;
4340 // this sets 'request_gadget_id'
4341 HandleGadgets(mx, my, button_status);
4343 switch (request_gadget_id)
4345 case TOOL_CTRL_ID_YES:
4346 case TOOL_CTRL_ID_TOUCH_YES:
4349 case TOOL_CTRL_ID_NO:
4350 case TOOL_CTRL_ID_TOUCH_NO:
4353 case TOOL_CTRL_ID_CONFIRM:
4354 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4355 result = TRUE | FALSE;
4358 case TOOL_CTRL_ID_PLAYER_1:
4361 case TOOL_CTRL_ID_PLAYER_2:
4364 case TOOL_CTRL_ID_PLAYER_3:
4367 case TOOL_CTRL_ID_PLAYER_4:
4372 // only check clickable animations if no request gadget clicked
4373 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4380 case SDL_WINDOWEVENT:
4381 HandleWindowEvent((WindowEvent *) &event);
4384 case SDL_APP_WILLENTERBACKGROUND:
4385 case SDL_APP_DIDENTERBACKGROUND:
4386 case SDL_APP_WILLENTERFOREGROUND:
4387 case SDL_APP_DIDENTERFOREGROUND:
4388 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4391 case EVENT_KEYPRESS:
4393 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4398 if (req_state & REQ_CONFIRM)
4407 #if defined(KSYM_Rewind)
4408 case KSYM_Rewind: // for Amazon Fire TV remote
4417 #if defined(KSYM_FastForward)
4418 case KSYM_FastForward: // for Amazon Fire TV remote
4424 HandleKeysDebug(key, KEY_PRESSED);
4428 if (req_state & REQ_PLAYER)
4430 int old_player_nr = setup.network_player_nr;
4433 result = old_player_nr + 1;
4438 result = old_player_nr + 1;
4469 case EVENT_KEYRELEASE:
4470 ClearPlayerAction();
4473 case SDL_CONTROLLERBUTTONDOWN:
4474 switch (event.cbutton.button)
4476 case SDL_CONTROLLER_BUTTON_A:
4477 case SDL_CONTROLLER_BUTTON_X:
4478 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4479 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4483 case SDL_CONTROLLER_BUTTON_B:
4484 case SDL_CONTROLLER_BUTTON_Y:
4485 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4486 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4487 case SDL_CONTROLLER_BUTTON_BACK:
4492 if (req_state & REQ_PLAYER)
4494 int old_player_nr = setup.network_player_nr;
4497 result = old_player_nr + 1;
4499 switch (event.cbutton.button)
4501 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4502 case SDL_CONTROLLER_BUTTON_Y:
4506 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4507 case SDL_CONTROLLER_BUTTON_B:
4511 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4512 case SDL_CONTROLLER_BUTTON_A:
4516 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4517 case SDL_CONTROLLER_BUTTON_X:
4528 case SDL_CONTROLLERBUTTONUP:
4529 HandleJoystickEvent(&event);
4530 ClearPlayerAction();
4534 HandleOtherEvents(&event);
4539 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4541 int joy = AnyJoystick();
4543 if (joy & JOY_BUTTON_1)
4545 else if (joy & JOY_BUTTON_2)
4548 else if (AnyJoystick())
4550 int joy = AnyJoystick();
4552 if (req_state & REQ_PLAYER)
4556 else if (joy & JOY_RIGHT)
4558 else if (joy & JOY_DOWN)
4560 else if (joy & JOY_LEFT)
4565 if (game_just_ended)
4567 if (global.use_envelope_request)
4569 // copy back current state of pressed buttons inside request area
4570 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4577 game.request_active = FALSE;
4582 static boolean RequestDoor(char *text, unsigned int req_state)
4584 unsigned int old_door_state;
4585 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4586 int font_nr = FONT_TEXT_2;
4591 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4593 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4594 font_nr = FONT_TEXT_1;
4597 if (game_status == GAME_MODE_PLAYING)
4598 BlitScreenToBitmap(backbuffer);
4600 // disable deactivated drawing when quick-loading level tape recording
4601 if (tape.playing && tape.deactivate_display)
4602 TapeDeactivateDisplayOff(TRUE);
4604 SetMouseCursor(CURSOR_DEFAULT);
4606 // pause network game while waiting for request to answer
4607 if (network.enabled &&
4608 game_status == GAME_MODE_PLAYING &&
4609 !game.all_players_gone &&
4610 req_state & REQUEST_WAIT_FOR_INPUT)
4611 SendToServer_PausePlaying();
4613 old_door_state = GetDoorState();
4615 // simulate releasing mouse button over last gadget, if still pressed
4617 HandleGadgets(-1, -1, 0);
4621 // draw released gadget before proceeding
4624 if (old_door_state & DOOR_OPEN_1)
4626 CloseDoor(DOOR_CLOSE_1);
4628 // save old door content
4629 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4630 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4633 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4634 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4636 // clear door drawing field
4637 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4639 // force DOOR font inside door area
4640 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4642 // write text for request
4643 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4645 char text_line[max_request_line_len + 1];
4651 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4653 tc = *(text_ptr + tx);
4654 // if (!tc || tc == ' ')
4655 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4659 if ((tc == '?' || tc == '!') && tl == 0)
4669 strncpy(text_line, text_ptr, tl);
4672 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4673 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4674 text_line, font_nr);
4676 text_ptr += tl + (tc == ' ' ? 1 : 0);
4677 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4682 if (req_state & REQ_ASK)
4684 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4685 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4687 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4689 else if (req_state & REQ_CONFIRM)
4691 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4692 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4694 else if (req_state & REQ_PLAYER)
4696 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4697 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4698 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4699 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4702 // copy request gadgets to door backbuffer
4703 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4705 OpenDoor(DOOR_OPEN_1);
4707 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4709 if (game_status == GAME_MODE_PLAYING)
4711 SetPanelBackground();
4712 SetDrawBackgroundMask(REDRAW_DOOR_1);
4716 SetDrawBackgroundMask(REDRAW_FIELD);
4722 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4724 // ---------- handle request buttons ----------
4725 result = RequestHandleEvents(req_state);
4729 if (!(req_state & REQ_STAY_OPEN))
4731 CloseDoor(DOOR_CLOSE_1);
4733 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4734 (req_state & REQ_REOPEN))
4735 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4740 if (game_status == GAME_MODE_PLAYING)
4742 SetPanelBackground();
4743 SetDrawBackgroundMask(REDRAW_DOOR_1);
4747 SetDrawBackgroundMask(REDRAW_FIELD);
4750 // continue network game after request
4751 if (network.enabled &&
4752 game_status == GAME_MODE_PLAYING &&
4753 !game.all_players_gone &&
4754 req_state & REQUEST_WAIT_FOR_INPUT)
4755 SendToServer_ContinuePlaying();
4757 // restore deactivated drawing when quick-loading level tape recording
4758 if (tape.playing && tape.deactivate_display)
4759 TapeDeactivateDisplayOn();
4764 static boolean RequestEnvelope(char *text, unsigned int req_state)
4768 if (game_status == GAME_MODE_PLAYING)
4769 BlitScreenToBitmap(backbuffer);
4771 // disable deactivated drawing when quick-loading level tape recording
4772 if (tape.playing && tape.deactivate_display)
4773 TapeDeactivateDisplayOff(TRUE);
4775 SetMouseCursor(CURSOR_DEFAULT);
4777 // pause network game while waiting for request to answer
4778 if (network.enabled &&
4779 game_status == GAME_MODE_PLAYING &&
4780 !game.all_players_gone &&
4781 req_state & REQUEST_WAIT_FOR_INPUT)
4782 SendToServer_PausePlaying();
4784 // simulate releasing mouse button over last gadget, if still pressed
4786 HandleGadgets(-1, -1, 0);
4790 // (replace with setting corresponding request background)
4791 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4792 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4794 // clear door drawing field
4795 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4797 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4799 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4801 if (game_status == GAME_MODE_PLAYING)
4803 SetPanelBackground();
4804 SetDrawBackgroundMask(REDRAW_DOOR_1);
4808 SetDrawBackgroundMask(REDRAW_FIELD);
4814 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4816 // ---------- handle request buttons ----------
4817 result = RequestHandleEvents(req_state);
4821 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4825 if (game_status == GAME_MODE_PLAYING)
4827 SetPanelBackground();
4828 SetDrawBackgroundMask(REDRAW_DOOR_1);
4832 SetDrawBackgroundMask(REDRAW_FIELD);
4835 // continue network game after request
4836 if (network.enabled &&
4837 game_status == GAME_MODE_PLAYING &&
4838 !game.all_players_gone &&
4839 req_state & REQUEST_WAIT_FOR_INPUT)
4840 SendToServer_ContinuePlaying();
4842 // restore deactivated drawing when quick-loading level tape recording
4843 if (tape.playing && tape.deactivate_display)
4844 TapeDeactivateDisplayOn();
4849 boolean Request(char *text, unsigned int req_state)
4851 boolean overlay_enabled = GetOverlayEnabled();
4854 SetOverlayEnabled(FALSE);
4856 if (global.use_envelope_request)
4857 result = RequestEnvelope(text, req_state);
4859 result = RequestDoor(text, req_state);
4861 SetOverlayEnabled(overlay_enabled);
4866 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4868 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4869 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4872 if (dpo1->sort_priority != dpo2->sort_priority)
4873 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4875 compare_result = dpo1->nr - dpo2->nr;
4877 return compare_result;
4880 void InitGraphicCompatibilityInfo_Doors(void)
4886 struct DoorInfo *door;
4890 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4891 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4893 { -1, -1, -1, NULL }
4895 struct Rect door_rect_list[] =
4897 { DX, DY, DXSIZE, DYSIZE },
4898 { VX, VY, VXSIZE, VYSIZE }
4902 for (i = 0; doors[i].door_token != -1; i++)
4904 int door_token = doors[i].door_token;
4905 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4906 int part_1 = doors[i].part_1;
4907 int part_8 = doors[i].part_8;
4908 int part_2 = part_1 + 1;
4909 int part_3 = part_1 + 2;
4910 struct DoorInfo *door = doors[i].door;
4911 struct Rect *door_rect = &door_rect_list[door_index];
4912 boolean door_gfx_redefined = FALSE;
4914 // check if any door part graphic definitions have been redefined
4916 for (j = 0; door_part_controls[j].door_token != -1; j++)
4918 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4919 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4921 if (dpc->door_token == door_token && fi->redefined)
4922 door_gfx_redefined = TRUE;
4925 // check for old-style door graphic/animation modifications
4927 if (!door_gfx_redefined)
4929 if (door->anim_mode & ANIM_STATIC_PANEL)
4931 door->panel.step_xoffset = 0;
4932 door->panel.step_yoffset = 0;
4935 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4937 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4938 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4939 int num_door_steps, num_panel_steps;
4941 // remove door part graphics other than the two default wings
4943 for (j = 0; door_part_controls[j].door_token != -1; j++)
4945 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4946 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4948 if (dpc->graphic >= part_3 &&
4949 dpc->graphic <= part_8)
4953 // set graphics and screen positions of the default wings
4955 g_part_1->width = door_rect->width;
4956 g_part_1->height = door_rect->height;
4957 g_part_2->width = door_rect->width;
4958 g_part_2->height = door_rect->height;
4959 g_part_2->src_x = door_rect->width;
4960 g_part_2->src_y = g_part_1->src_y;
4962 door->part_2.x = door->part_1.x;
4963 door->part_2.y = door->part_1.y;
4965 if (door->width != -1)
4967 g_part_1->width = door->width;
4968 g_part_2->width = door->width;
4970 // special treatment for graphics and screen position of right wing
4971 g_part_2->src_x += door_rect->width - door->width;
4972 door->part_2.x += door_rect->width - door->width;
4975 if (door->height != -1)
4977 g_part_1->height = door->height;
4978 g_part_2->height = door->height;
4980 // special treatment for graphics and screen position of bottom wing
4981 g_part_2->src_y += door_rect->height - door->height;
4982 door->part_2.y += door_rect->height - door->height;
4985 // set animation delays for the default wings and panels
4987 door->part_1.step_delay = door->step_delay;
4988 door->part_2.step_delay = door->step_delay;
4989 door->panel.step_delay = door->step_delay;
4991 // set animation draw order for the default wings
4993 door->part_1.sort_priority = 2; // draw left wing over ...
4994 door->part_2.sort_priority = 1; // ... right wing
4996 // set animation draw offset for the default wings
4998 if (door->anim_mode & ANIM_HORIZONTAL)
5000 door->part_1.step_xoffset = door->step_offset;
5001 door->part_1.step_yoffset = 0;
5002 door->part_2.step_xoffset = door->step_offset * -1;
5003 door->part_2.step_yoffset = 0;
5005 num_door_steps = g_part_1->width / door->step_offset;
5007 else // ANIM_VERTICAL
5009 door->part_1.step_xoffset = 0;
5010 door->part_1.step_yoffset = door->step_offset;
5011 door->part_2.step_xoffset = 0;
5012 door->part_2.step_yoffset = door->step_offset * -1;
5014 num_door_steps = g_part_1->height / door->step_offset;
5017 // set animation draw offset for the default panels
5019 if (door->step_offset > 1)
5021 num_panel_steps = 2 * door_rect->height / door->step_offset;
5022 door->panel.start_step = num_panel_steps - num_door_steps;
5023 door->panel.start_step_closing = door->panel.start_step;
5027 num_panel_steps = door_rect->height / door->step_offset;
5028 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5029 door->panel.start_step_closing = door->panel.start_step;
5030 door->panel.step_delay *= 2;
5037 void InitDoors(void)
5041 for (i = 0; door_part_controls[i].door_token != -1; i++)
5043 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5044 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5046 // initialize "start_step_opening" and "start_step_closing", if needed
5047 if (dpc->pos->start_step_opening == 0 &&
5048 dpc->pos->start_step_closing == 0)
5050 // dpc->pos->start_step_opening = dpc->pos->start_step;
5051 dpc->pos->start_step_closing = dpc->pos->start_step;
5054 // fill structure for door part draw order (sorted below)
5056 dpo->sort_priority = dpc->pos->sort_priority;
5059 // sort door part controls according to sort_priority and graphic number
5060 qsort(door_part_order, MAX_DOOR_PARTS,
5061 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5064 unsigned int OpenDoor(unsigned int door_state)
5066 if (door_state & DOOR_COPY_BACK)
5068 if (door_state & DOOR_OPEN_1)
5069 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5070 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5072 if (door_state & DOOR_OPEN_2)
5073 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5074 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5076 door_state &= ~DOOR_COPY_BACK;
5079 return MoveDoor(door_state);
5082 unsigned int CloseDoor(unsigned int door_state)
5084 unsigned int old_door_state = GetDoorState();
5086 if (!(door_state & DOOR_NO_COPY_BACK))
5088 if (old_door_state & DOOR_OPEN_1)
5089 BlitBitmap(backbuffer, bitmap_db_door_1,
5090 DX, DY, DXSIZE, DYSIZE, 0, 0);
5092 if (old_door_state & DOOR_OPEN_2)
5093 BlitBitmap(backbuffer, bitmap_db_door_2,
5094 VX, VY, VXSIZE, VYSIZE, 0, 0);
5096 door_state &= ~DOOR_NO_COPY_BACK;
5099 return MoveDoor(door_state);
5102 unsigned int GetDoorState(void)
5104 return MoveDoor(DOOR_GET_STATE);
5107 unsigned int SetDoorState(unsigned int door_state)
5109 return MoveDoor(door_state | DOOR_SET_STATE);
5112 static int euclid(int a, int b)
5114 return (b ? euclid(b, a % b) : a);
5117 unsigned int MoveDoor(unsigned int door_state)
5119 struct Rect door_rect_list[] =
5121 { DX, DY, DXSIZE, DYSIZE },
5122 { VX, VY, VXSIZE, VYSIZE }
5124 static int door1 = DOOR_CLOSE_1;
5125 static int door2 = DOOR_CLOSE_2;
5126 unsigned int door_delay = 0;
5127 unsigned int door_delay_value;
5130 if (door_state == DOOR_GET_STATE)
5131 return (door1 | door2);
5133 if (door_state & DOOR_SET_STATE)
5135 if (door_state & DOOR_ACTION_1)
5136 door1 = door_state & DOOR_ACTION_1;
5137 if (door_state & DOOR_ACTION_2)
5138 door2 = door_state & DOOR_ACTION_2;
5140 return (door1 | door2);
5143 if (!(door_state & DOOR_FORCE_REDRAW))
5145 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5146 door_state &= ~DOOR_OPEN_1;
5147 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5148 door_state &= ~DOOR_CLOSE_1;
5149 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5150 door_state &= ~DOOR_OPEN_2;
5151 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5152 door_state &= ~DOOR_CLOSE_2;
5155 if (global.autoplay_leveldir)
5157 door_state |= DOOR_NO_DELAY;
5158 door_state &= ~DOOR_CLOSE_ALL;
5161 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5162 door_state |= DOOR_NO_DELAY;
5164 if (door_state & DOOR_ACTION)
5166 boolean door_panel_drawn[NUM_DOORS];
5167 boolean panel_has_doors[NUM_DOORS];
5168 boolean door_part_skip[MAX_DOOR_PARTS];
5169 boolean door_part_done[MAX_DOOR_PARTS];
5170 boolean door_part_done_all;
5171 int num_steps[MAX_DOOR_PARTS];
5172 int max_move_delay = 0; // delay for complete animations of all doors
5173 int max_step_delay = 0; // delay (ms) between two animation frames
5174 int num_move_steps = 0; // number of animation steps for all doors
5175 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5176 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5177 int current_move_delay = 0;
5181 for (i = 0; i < NUM_DOORS; i++)
5182 panel_has_doors[i] = FALSE;
5184 for (i = 0; i < MAX_DOOR_PARTS; i++)
5186 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5187 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5188 int door_token = dpc->door_token;
5190 door_part_done[i] = FALSE;
5191 door_part_skip[i] = (!(door_state & door_token) ||
5195 for (i = 0; i < MAX_DOOR_PARTS; i++)
5197 int nr = door_part_order[i].nr;
5198 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5199 struct DoorPartPosInfo *pos = dpc->pos;
5200 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5201 int door_token = dpc->door_token;
5202 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5203 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5204 int step_xoffset = ABS(pos->step_xoffset);
5205 int step_yoffset = ABS(pos->step_yoffset);
5206 int step_delay = pos->step_delay;
5207 int current_door_state = door_state & door_token;
5208 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5209 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5210 boolean part_opening = (is_panel ? door_closing : door_opening);
5211 int start_step = (part_opening ? pos->start_step_opening :
5212 pos->start_step_closing);
5213 float move_xsize = (step_xoffset ? g->width : 0);
5214 float move_ysize = (step_yoffset ? g->height : 0);
5215 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5216 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5217 int move_steps = (move_xsteps && move_ysteps ?
5218 MIN(move_xsteps, move_ysteps) :
5219 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5220 int move_delay = move_steps * step_delay;
5222 if (door_part_skip[nr])
5225 max_move_delay = MAX(max_move_delay, move_delay);
5226 max_step_delay = (max_step_delay == 0 ? step_delay :
5227 euclid(max_step_delay, step_delay));
5228 num_steps[nr] = move_steps;
5232 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5234 panel_has_doors[door_index] = TRUE;
5238 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5240 num_move_steps = max_move_delay / max_step_delay;
5241 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5243 door_delay_value = max_step_delay;
5245 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5247 start = num_move_steps - 1;
5251 // opening door sound has priority over simultaneously closing door
5252 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5254 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5256 if (door_state & DOOR_OPEN_1)
5257 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5258 if (door_state & DOOR_OPEN_2)
5259 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5261 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5263 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5265 if (door_state & DOOR_CLOSE_1)
5266 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5267 if (door_state & DOOR_CLOSE_2)
5268 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5272 for (k = start; k < num_move_steps; k++)
5274 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5276 door_part_done_all = TRUE;
5278 for (i = 0; i < NUM_DOORS; i++)
5279 door_panel_drawn[i] = FALSE;
5281 for (i = 0; i < MAX_DOOR_PARTS; i++)
5283 int nr = door_part_order[i].nr;
5284 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5285 struct DoorPartPosInfo *pos = dpc->pos;
5286 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5287 int door_token = dpc->door_token;
5288 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5289 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5290 boolean is_panel_and_door_has_closed = FALSE;
5291 struct Rect *door_rect = &door_rect_list[door_index];
5292 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5294 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5295 int current_door_state = door_state & door_token;
5296 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5297 boolean door_closing = !door_opening;
5298 boolean part_opening = (is_panel ? door_closing : door_opening);
5299 boolean part_closing = !part_opening;
5300 int start_step = (part_opening ? pos->start_step_opening :
5301 pos->start_step_closing);
5302 int step_delay = pos->step_delay;
5303 int step_factor = step_delay / max_step_delay;
5304 int k1 = (step_factor ? k / step_factor + 1 : k);
5305 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5306 int kk = MAX(0, k2);
5309 int src_x, src_y, src_xx, src_yy;
5310 int dst_x, dst_y, dst_xx, dst_yy;
5313 if (door_part_skip[nr])
5316 if (!(door_state & door_token))
5324 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5325 int kk_door = MAX(0, k2_door);
5326 int sync_frame = kk_door * door_delay_value;
5327 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5329 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5330 &g_src_x, &g_src_y);
5335 if (!door_panel_drawn[door_index])
5337 ClearRectangle(drawto, door_rect->x, door_rect->y,
5338 door_rect->width, door_rect->height);
5340 door_panel_drawn[door_index] = TRUE;
5343 // draw opening or closing door parts
5345 if (pos->step_xoffset < 0) // door part on right side
5348 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5351 if (dst_xx + width > door_rect->width)
5352 width = door_rect->width - dst_xx;
5354 else // door part on left side
5357 dst_xx = pos->x - kk * pos->step_xoffset;
5361 src_xx = ABS(dst_xx);
5365 width = g->width - src_xx;
5367 if (width > door_rect->width)
5368 width = door_rect->width;
5370 // printf("::: k == %d [%d] \n", k, start_step);
5373 if (pos->step_yoffset < 0) // door part on bottom side
5376 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5379 if (dst_yy + height > door_rect->height)
5380 height = door_rect->height - dst_yy;
5382 else // door part on top side
5385 dst_yy = pos->y - kk * pos->step_yoffset;
5389 src_yy = ABS(dst_yy);
5393 height = g->height - src_yy;
5396 src_x = g_src_x + src_xx;
5397 src_y = g_src_y + src_yy;
5399 dst_x = door_rect->x + dst_xx;
5400 dst_y = door_rect->y + dst_yy;
5402 is_panel_and_door_has_closed =
5405 panel_has_doors[door_index] &&
5406 k >= num_move_steps_doors_only - 1);
5408 if (width >= 0 && width <= g->width &&
5409 height >= 0 && height <= g->height &&
5410 !is_panel_and_door_has_closed)
5412 if (is_panel || !pos->draw_masked)
5413 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5416 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5420 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5422 if ((part_opening && (width < 0 || height < 0)) ||
5423 (part_closing && (width >= g->width && height >= g->height)))
5424 door_part_done[nr] = TRUE;
5426 // continue door part animations, but not panel after door has closed
5427 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5428 door_part_done_all = FALSE;
5431 if (!(door_state & DOOR_NO_DELAY))
5435 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5437 current_move_delay += max_step_delay;
5439 // prevent OS (Windows) from complaining about program not responding
5443 if (door_part_done_all)
5447 if (!(door_state & DOOR_NO_DELAY))
5449 // wait for specified door action post delay
5450 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5451 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5452 else if (door_state & DOOR_ACTION_1)
5453 door_delay_value = door_1.post_delay;
5454 else if (door_state & DOOR_ACTION_2)
5455 door_delay_value = door_2.post_delay;
5457 while (!DelayReached(&door_delay, door_delay_value))
5462 if (door_state & DOOR_ACTION_1)
5463 door1 = door_state & DOOR_ACTION_1;
5464 if (door_state & DOOR_ACTION_2)
5465 door2 = door_state & DOOR_ACTION_2;
5467 // draw masked border over door area
5468 DrawMaskedBorder(REDRAW_DOOR_1);
5469 DrawMaskedBorder(REDRAW_DOOR_2);
5471 ClearAutoRepeatKeyEvents();
5473 return (door1 | door2);
5476 static boolean useSpecialEditorDoor(void)
5478 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5479 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5481 // do not draw special editor door if editor border defined or redefined
5482 if (graphic_info[graphic].bitmap != NULL || redefined)
5485 // do not draw special editor door if global border defined to be empty
5486 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5489 // do not draw special editor door if viewport definitions do not match
5493 EY + EYSIZE != VY + VYSIZE)
5499 void DrawSpecialEditorDoor(void)
5501 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5502 int top_border_width = gfx1->width;
5503 int top_border_height = gfx1->height;
5504 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5505 int ex = EX - outer_border;
5506 int ey = EY - outer_border;
5507 int vy = VY - outer_border;
5508 int exsize = EXSIZE + 2 * outer_border;
5510 if (!useSpecialEditorDoor())
5513 // draw bigger level editor toolbox window
5514 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5515 top_border_width, top_border_height, ex, ey - top_border_height);
5516 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5517 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5519 redraw_mask |= REDRAW_ALL;
5522 void UndrawSpecialEditorDoor(void)
5524 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5525 int top_border_width = gfx1->width;
5526 int top_border_height = gfx1->height;
5527 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5528 int ex = EX - outer_border;
5529 int ey = EY - outer_border;
5530 int ey_top = ey - top_border_height;
5531 int exsize = EXSIZE + 2 * outer_border;
5532 int eysize = EYSIZE + 2 * outer_border;
5534 if (!useSpecialEditorDoor())
5537 // draw normal tape recorder window
5538 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5540 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5541 ex, ey_top, top_border_width, top_border_height,
5543 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5544 ex, ey, exsize, eysize, ex, ey);
5548 // if screen background is set to "[NONE]", clear editor toolbox window
5549 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5550 ClearRectangle(drawto, ex, ey, exsize, eysize);
5553 redraw_mask |= REDRAW_ALL;
5557 // ---------- new tool button stuff -------------------------------------------
5562 struct TextPosInfo *pos;
5564 boolean is_touch_button;
5566 } toolbutton_info[NUM_TOOL_BUTTONS] =
5569 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5570 TOOL_CTRL_ID_YES, FALSE, "yes"
5573 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5574 TOOL_CTRL_ID_NO, FALSE, "no"
5577 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5578 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5581 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5582 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5585 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5586 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5589 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5590 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5593 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5594 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5597 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5598 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5601 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5602 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5605 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5606 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5610 void CreateToolButtons(void)
5614 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5616 int graphic = toolbutton_info[i].graphic;
5617 struct GraphicInfo *gfx = &graphic_info[graphic];
5618 struct TextPosInfo *pos = toolbutton_info[i].pos;
5619 struct GadgetInfo *gi;
5620 Bitmap *deco_bitmap = None;
5621 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5622 unsigned int event_mask = GD_EVENT_RELEASED;
5623 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5624 int base_x = (is_touch_button ? 0 : DX);
5625 int base_y = (is_touch_button ? 0 : DY);
5626 int gd_x = gfx->src_x;
5627 int gd_y = gfx->src_y;
5628 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5629 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5634 if (global.use_envelope_request && !is_touch_button)
5636 setRequestPosition(&base_x, &base_y, TRUE);
5638 // check if request buttons are outside of envelope and fix, if needed
5639 if (x < 0 || x + gfx->width > request.width ||
5640 y < 0 || y + gfx->height > request.height)
5642 if (id == TOOL_CTRL_ID_YES)
5645 y = request.height - 2 * request.border_size - gfx->height;
5647 else if (id == TOOL_CTRL_ID_NO)
5649 x = request.width - 2 * request.border_size - gfx->width;
5650 y = request.height - 2 * request.border_size - gfx->height;
5652 else if (id == TOOL_CTRL_ID_CONFIRM)
5654 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5655 y = request.height - 2 * request.border_size - gfx->height;
5657 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5659 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5661 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5662 y = request.height - 2 * request.border_size - gfx->height * 2;
5664 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5665 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5670 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5672 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5674 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5675 pos->size, &deco_bitmap, &deco_x, &deco_y);
5676 deco_xpos = (gfx->width - pos->size) / 2;
5677 deco_ypos = (gfx->height - pos->size) / 2;
5680 gi = CreateGadget(GDI_CUSTOM_ID, id,
5681 GDI_IMAGE_ID, graphic,
5682 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5685 GDI_WIDTH, gfx->width,
5686 GDI_HEIGHT, gfx->height,
5687 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5688 GDI_STATE, GD_BUTTON_UNPRESSED,
5689 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5690 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5691 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5692 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5693 GDI_DECORATION_SIZE, pos->size, pos->size,
5694 GDI_DECORATION_SHIFTING, 1, 1,
5695 GDI_DIRECT_DRAW, FALSE,
5696 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5697 GDI_EVENT_MASK, event_mask,
5698 GDI_CALLBACK_ACTION, HandleToolButtons,
5702 Error(ERR_EXIT, "cannot create gadget");
5704 tool_gadget[id] = gi;
5708 void FreeToolButtons(void)
5712 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5713 FreeGadget(tool_gadget[i]);
5716 static void UnmapToolButtons(void)
5720 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5721 UnmapGadget(tool_gadget[i]);
5724 static void HandleToolButtons(struct GadgetInfo *gi)
5726 request_gadget_id = gi->custom_id;
5729 static struct Mapping_EM_to_RND_object
5732 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5733 boolean is_backside; // backside of moving element
5739 em_object_mapping_list[GAME_TILE_MAX + 1] =
5742 Zborder, FALSE, FALSE,
5746 Zplayer, FALSE, FALSE,
5755 Ztank, FALSE, FALSE,
5759 Zeater, FALSE, FALSE,
5763 Zdynamite, FALSE, FALSE,
5767 Zboom, FALSE, FALSE,
5772 Xchain, FALSE, FALSE,
5773 EL_DEFAULT, ACTION_EXPLODING, -1
5776 Xboom_bug, FALSE, FALSE,
5777 EL_BUG, ACTION_EXPLODING, -1
5780 Xboom_tank, FALSE, FALSE,
5781 EL_SPACESHIP, ACTION_EXPLODING, -1
5784 Xboom_android, FALSE, FALSE,
5785 EL_EMC_ANDROID, ACTION_OTHER, -1
5788 Xboom_1, FALSE, FALSE,
5789 EL_DEFAULT, ACTION_EXPLODING, -1
5792 Xboom_2, FALSE, FALSE,
5793 EL_DEFAULT, ACTION_EXPLODING, -1
5797 Xblank, TRUE, FALSE,
5802 Xsplash_e, FALSE, FALSE,
5803 EL_ACID_SPLASH_RIGHT, -1, -1
5806 Xsplash_w, FALSE, FALSE,
5807 EL_ACID_SPLASH_LEFT, -1, -1
5811 Xplant, TRUE, FALSE,
5812 EL_EMC_PLANT, -1, -1
5815 Yplant, FALSE, FALSE,
5816 EL_EMC_PLANT, -1, -1
5820 Xacid_1, TRUE, FALSE,
5824 Xacid_2, FALSE, FALSE,
5828 Xacid_3, FALSE, FALSE,
5832 Xacid_4, FALSE, FALSE,
5836 Xacid_5, FALSE, FALSE,
5840 Xacid_6, FALSE, FALSE,
5844 Xacid_7, FALSE, FALSE,
5848 Xacid_8, FALSE, FALSE,
5853 Xfake_acid_1, TRUE, FALSE,
5854 EL_EMC_FAKE_ACID, -1, -1
5857 Xfake_acid_2, FALSE, FALSE,
5858 EL_EMC_FAKE_ACID, -1, -1
5861 Xfake_acid_3, FALSE, FALSE,
5862 EL_EMC_FAKE_ACID, -1, -1
5865 Xfake_acid_4, FALSE, FALSE,
5866 EL_EMC_FAKE_ACID, -1, -1
5869 Xfake_acid_5, FALSE, FALSE,
5870 EL_EMC_FAKE_ACID, -1, -1
5873 Xfake_acid_6, FALSE, FALSE,
5874 EL_EMC_FAKE_ACID, -1, -1
5877 Xfake_acid_7, FALSE, FALSE,
5878 EL_EMC_FAKE_ACID, -1, -1
5881 Xfake_acid_8, FALSE, FALSE,
5882 EL_EMC_FAKE_ACID, -1, -1
5886 Xgrass, TRUE, FALSE,
5887 EL_EMC_GRASS, -1, -1
5890 Ygrass_nB, FALSE, FALSE,
5891 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5894 Ygrass_eB, FALSE, FALSE,
5895 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5898 Ygrass_sB, FALSE, FALSE,
5899 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5902 Ygrass_wB, FALSE, FALSE,
5903 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5911 Ydirt_nB, FALSE, FALSE,
5912 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5915 Ydirt_eB, FALSE, FALSE,
5916 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5919 Ydirt_sB, FALSE, FALSE,
5920 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5923 Ydirt_wB, FALSE, FALSE,
5924 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5928 Xandroid, TRUE, FALSE,
5929 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5932 Xandroid_1_n, FALSE, FALSE,
5933 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5936 Xandroid_2_n, FALSE, FALSE,
5937 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5940 Xandroid_1_e, FALSE, FALSE,
5941 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5944 Xandroid_2_e, FALSE, FALSE,
5945 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5948 Xandroid_1_w, FALSE, FALSE,
5949 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5952 Xandroid_2_w, FALSE, FALSE,
5953 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5956 Xandroid_1_s, FALSE, FALSE,
5957 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5960 Xandroid_2_s, FALSE, FALSE,
5961 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5964 Yandroid_n, FALSE, FALSE,
5965 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5968 Yandroid_nB, FALSE, TRUE,
5969 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5972 Yandroid_ne, FALSE, FALSE,
5973 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5976 Yandroid_neB, FALSE, TRUE,
5977 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5980 Yandroid_e, FALSE, FALSE,
5981 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5984 Yandroid_eB, FALSE, TRUE,
5985 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5988 Yandroid_se, FALSE, FALSE,
5989 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5992 Yandroid_seB, FALSE, TRUE,
5993 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5996 Yandroid_s, FALSE, FALSE,
5997 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6000 Yandroid_sB, FALSE, TRUE,
6001 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6004 Yandroid_sw, FALSE, FALSE,
6005 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6008 Yandroid_swB, FALSE, TRUE,
6009 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6012 Yandroid_w, FALSE, FALSE,
6013 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6016 Yandroid_wB, FALSE, TRUE,
6017 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6020 Yandroid_nw, FALSE, FALSE,
6021 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6024 Yandroid_nwB, FALSE, TRUE,
6025 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6029 Xeater_n, TRUE, FALSE,
6030 EL_YAMYAM_UP, -1, -1
6033 Xeater_e, TRUE, FALSE,
6034 EL_YAMYAM_RIGHT, -1, -1
6037 Xeater_w, TRUE, FALSE,
6038 EL_YAMYAM_LEFT, -1, -1
6041 Xeater_s, TRUE, FALSE,
6042 EL_YAMYAM_DOWN, -1, -1
6045 Yeater_n, FALSE, FALSE,
6046 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6049 Yeater_nB, FALSE, TRUE,
6050 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6053 Yeater_e, FALSE, FALSE,
6054 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6057 Yeater_eB, FALSE, TRUE,
6058 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6061 Yeater_s, FALSE, FALSE,
6062 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6065 Yeater_sB, FALSE, TRUE,
6066 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6069 Yeater_w, FALSE, FALSE,
6070 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6073 Yeater_wB, FALSE, TRUE,
6074 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6077 Yeater_stone, FALSE, FALSE,
6078 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6081 Yeater_spring, FALSE, FALSE,
6082 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6086 Xalien, TRUE, FALSE,
6090 Xalien_pause, FALSE, FALSE,
6094 Yalien_n, FALSE, FALSE,
6095 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6098 Yalien_nB, FALSE, TRUE,
6099 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6102 Yalien_e, FALSE, FALSE,
6103 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6106 Yalien_eB, FALSE, TRUE,
6107 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6110 Yalien_s, FALSE, FALSE,
6111 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6114 Yalien_sB, FALSE, TRUE,
6115 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6118 Yalien_w, FALSE, FALSE,
6119 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6122 Yalien_wB, FALSE, TRUE,
6123 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6126 Yalien_stone, FALSE, FALSE,
6127 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6130 Yalien_spring, FALSE, FALSE,
6131 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6135 Xbug_1_n, TRUE, FALSE,
6139 Xbug_1_e, TRUE, FALSE,
6140 EL_BUG_RIGHT, -1, -1
6143 Xbug_1_s, TRUE, FALSE,
6147 Xbug_1_w, TRUE, FALSE,
6151 Xbug_2_n, FALSE, FALSE,
6155 Xbug_2_e, FALSE, FALSE,
6156 EL_BUG_RIGHT, -1, -1
6159 Xbug_2_s, FALSE, FALSE,
6163 Xbug_2_w, FALSE, FALSE,
6167 Ybug_n, FALSE, FALSE,
6168 EL_BUG, ACTION_MOVING, MV_BIT_UP
6171 Ybug_nB, FALSE, TRUE,
6172 EL_BUG, ACTION_MOVING, MV_BIT_UP
6175 Ybug_e, FALSE, FALSE,
6176 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6179 Ybug_eB, FALSE, TRUE,
6180 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6183 Ybug_s, FALSE, FALSE,
6184 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6187 Ybug_sB, FALSE, TRUE,
6188 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6191 Ybug_w, FALSE, FALSE,
6192 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6195 Ybug_wB, FALSE, TRUE,
6196 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6199 Ybug_w_n, FALSE, FALSE,
6200 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6203 Ybug_n_e, FALSE, FALSE,
6204 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6207 Ybug_e_s, FALSE, FALSE,
6208 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6211 Ybug_s_w, FALSE, FALSE,
6212 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6215 Ybug_e_n, FALSE, FALSE,
6216 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6219 Ybug_s_e, FALSE, FALSE,
6220 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6223 Ybug_w_s, FALSE, FALSE,
6224 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6227 Ybug_n_w, FALSE, FALSE,
6228 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6231 Ybug_stone, FALSE, FALSE,
6232 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6235 Ybug_spring, FALSE, FALSE,
6236 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6240 Xtank_1_n, TRUE, FALSE,
6241 EL_SPACESHIP_UP, -1, -1
6244 Xtank_1_e, TRUE, FALSE,
6245 EL_SPACESHIP_RIGHT, -1, -1
6248 Xtank_1_s, TRUE, FALSE,
6249 EL_SPACESHIP_DOWN, -1, -1
6252 Xtank_1_w, TRUE, FALSE,
6253 EL_SPACESHIP_LEFT, -1, -1
6256 Xtank_2_n, FALSE, FALSE,
6257 EL_SPACESHIP_UP, -1, -1
6260 Xtank_2_e, FALSE, FALSE,
6261 EL_SPACESHIP_RIGHT, -1, -1
6264 Xtank_2_s, FALSE, FALSE,
6265 EL_SPACESHIP_DOWN, -1, -1
6268 Xtank_2_w, FALSE, FALSE,
6269 EL_SPACESHIP_LEFT, -1, -1
6272 Ytank_n, FALSE, FALSE,
6273 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6276 Ytank_nB, FALSE, TRUE,
6277 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6280 Ytank_e, FALSE, FALSE,
6281 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6284 Ytank_eB, FALSE, TRUE,
6285 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6288 Ytank_s, FALSE, FALSE,
6289 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6292 Ytank_sB, FALSE, TRUE,
6293 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6296 Ytank_w, FALSE, FALSE,
6297 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6300 Ytank_wB, FALSE, TRUE,
6301 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6304 Ytank_w_n, FALSE, FALSE,
6305 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6308 Ytank_n_e, FALSE, FALSE,
6309 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6312 Ytank_e_s, FALSE, FALSE,
6313 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6316 Ytank_s_w, FALSE, FALSE,
6317 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6320 Ytank_e_n, FALSE, FALSE,
6321 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6324 Ytank_s_e, FALSE, FALSE,
6325 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6328 Ytank_w_s, FALSE, FALSE,
6329 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6332 Ytank_n_w, FALSE, FALSE,
6333 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6336 Ytank_stone, FALSE, FALSE,
6337 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6340 Ytank_spring, FALSE, FALSE,
6341 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6345 Xemerald, TRUE, FALSE,
6349 Xemerald_pause, FALSE, FALSE,
6353 Xemerald_fall, FALSE, FALSE,
6357 Xemerald_shine, FALSE, FALSE,
6358 EL_EMERALD, ACTION_TWINKLING, -1
6361 Yemerald_s, FALSE, FALSE,
6362 EL_EMERALD, ACTION_FALLING, -1
6365 Yemerald_sB, FALSE, TRUE,
6366 EL_EMERALD, ACTION_FALLING, -1
6369 Yemerald_e, FALSE, FALSE,
6370 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6373 Yemerald_eB, FALSE, TRUE,
6374 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6377 Yemerald_w, FALSE, FALSE,
6378 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6381 Yemerald_wB, FALSE, TRUE,
6382 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6385 Yemerald_blank, FALSE, FALSE,
6386 EL_EMERALD, ACTION_COLLECTING, -1
6390 Xdiamond, TRUE, FALSE,
6394 Xdiamond_pause, FALSE, FALSE,
6398 Xdiamond_fall, FALSE, FALSE,
6402 Xdiamond_shine, FALSE, FALSE,
6403 EL_DIAMOND, ACTION_TWINKLING, -1
6406 Ydiamond_s, FALSE, FALSE,
6407 EL_DIAMOND, ACTION_FALLING, -1
6410 Ydiamond_sB, FALSE, TRUE,
6411 EL_DIAMOND, ACTION_FALLING, -1
6414 Ydiamond_e, FALSE, FALSE,
6415 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6418 Ydiamond_eB, FALSE, TRUE,
6419 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6422 Ydiamond_w, FALSE, FALSE,
6423 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6426 Ydiamond_wB, FALSE, TRUE,
6427 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6430 Ydiamond_blank, FALSE, FALSE,
6431 EL_DIAMOND, ACTION_COLLECTING, -1
6434 Ydiamond_stone, FALSE, FALSE,
6435 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6439 Xstone, TRUE, FALSE,
6443 Xstone_pause, FALSE, FALSE,
6447 Xstone_fall, FALSE, FALSE,
6451 Ystone_s, FALSE, FALSE,
6452 EL_ROCK, ACTION_FALLING, -1
6455 Ystone_sB, FALSE, TRUE,
6456 EL_ROCK, ACTION_FALLING, -1
6459 Ystone_e, FALSE, FALSE,
6460 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6463 Ystone_eB, FALSE, TRUE,
6464 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6467 Ystone_w, FALSE, FALSE,
6468 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6471 Ystone_wB, FALSE, TRUE,
6472 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6480 Xbomb_pause, FALSE, FALSE,
6484 Xbomb_fall, FALSE, FALSE,
6488 Ybomb_s, FALSE, FALSE,
6489 EL_BOMB, ACTION_FALLING, -1
6492 Ybomb_sB, FALSE, TRUE,
6493 EL_BOMB, ACTION_FALLING, -1
6496 Ybomb_e, FALSE, FALSE,
6497 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6500 Ybomb_eB, FALSE, TRUE,
6501 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6504 Ybomb_w, FALSE, FALSE,
6505 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6508 Ybomb_wB, FALSE, TRUE,
6509 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6512 Ybomb_blank, FALSE, FALSE,
6513 EL_BOMB, ACTION_ACTIVATING, -1
6521 Xnut_pause, FALSE, FALSE,
6525 Xnut_fall, FALSE, FALSE,
6529 Ynut_s, FALSE, FALSE,
6530 EL_NUT, ACTION_FALLING, -1
6533 Ynut_sB, FALSE, TRUE,
6534 EL_NUT, ACTION_FALLING, -1
6537 Ynut_e, FALSE, FALSE,
6538 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6541 Ynut_eB, FALSE, TRUE,
6542 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6545 Ynut_w, FALSE, FALSE,
6546 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6549 Ynut_wB, FALSE, TRUE,
6550 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6553 Ynut_stone, FALSE, FALSE,
6554 EL_NUT, ACTION_BREAKING, -1
6558 Xspring, TRUE, FALSE,
6562 Xspring_pause, FALSE, FALSE,
6566 Xspring_e, FALSE, FALSE,
6570 Xspring_w, FALSE, FALSE,
6574 Xspring_fall, FALSE, FALSE,
6578 Yspring_s, FALSE, FALSE,
6579 EL_SPRING, ACTION_FALLING, -1
6582 Yspring_sB, FALSE, TRUE,
6583 EL_SPRING, ACTION_FALLING, -1
6586 Yspring_e, FALSE, FALSE,
6587 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6590 Yspring_eB, FALSE, TRUE,
6591 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6594 Yspring_w, FALSE, FALSE,
6595 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6598 Yspring_wB, FALSE, TRUE,
6599 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6602 Yspring_alien_e, FALSE, FALSE,
6603 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6606 Yspring_alien_eB, FALSE, TRUE,
6607 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6610 Yspring_alien_w, FALSE, FALSE,
6611 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6614 Yspring_alien_wB, FALSE, TRUE,
6615 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6619 Xpush_emerald_e, FALSE, FALSE,
6620 EL_EMERALD, -1, MV_BIT_RIGHT
6623 Xpush_emerald_w, FALSE, FALSE,
6624 EL_EMERALD, -1, MV_BIT_LEFT
6627 Xpush_diamond_e, FALSE, FALSE,
6628 EL_DIAMOND, -1, MV_BIT_RIGHT
6631 Xpush_diamond_w, FALSE, FALSE,
6632 EL_DIAMOND, -1, MV_BIT_LEFT
6635 Xpush_stone_e, FALSE, FALSE,
6636 EL_ROCK, -1, MV_BIT_RIGHT
6639 Xpush_stone_w, FALSE, FALSE,
6640 EL_ROCK, -1, MV_BIT_LEFT
6643 Xpush_bomb_e, FALSE, FALSE,
6644 EL_BOMB, -1, MV_BIT_RIGHT
6647 Xpush_bomb_w, FALSE, FALSE,
6648 EL_BOMB, -1, MV_BIT_LEFT
6651 Xpush_nut_e, FALSE, FALSE,
6652 EL_NUT, -1, MV_BIT_RIGHT
6655 Xpush_nut_w, FALSE, FALSE,
6656 EL_NUT, -1, MV_BIT_LEFT
6659 Xpush_spring_e, FALSE, FALSE,
6660 EL_SPRING, -1, MV_BIT_RIGHT
6663 Xpush_spring_w, FALSE, FALSE,
6664 EL_SPRING, -1, MV_BIT_LEFT
6668 Xdynamite, TRUE, FALSE,
6669 EL_EM_DYNAMITE, -1, -1
6672 Ydynamite_blank, FALSE, FALSE,
6673 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6676 Xdynamite_1, TRUE, FALSE,
6677 EL_EM_DYNAMITE_ACTIVE, -1, -1
6680 Xdynamite_2, FALSE, FALSE,
6681 EL_EM_DYNAMITE_ACTIVE, -1, -1
6684 Xdynamite_3, FALSE, FALSE,
6685 EL_EM_DYNAMITE_ACTIVE, -1, -1
6688 Xdynamite_4, FALSE, FALSE,
6689 EL_EM_DYNAMITE_ACTIVE, -1, -1
6693 Xkey_1, TRUE, FALSE,
6697 Xkey_2, TRUE, FALSE,
6701 Xkey_3, TRUE, FALSE,
6705 Xkey_4, TRUE, FALSE,
6709 Xkey_5, TRUE, FALSE,
6710 EL_EMC_KEY_5, -1, -1
6713 Xkey_6, TRUE, FALSE,
6714 EL_EMC_KEY_6, -1, -1
6717 Xkey_7, TRUE, FALSE,
6718 EL_EMC_KEY_7, -1, -1
6721 Xkey_8, TRUE, FALSE,
6722 EL_EMC_KEY_8, -1, -1
6726 Xdoor_1, TRUE, FALSE,
6727 EL_EM_GATE_1, -1, -1
6730 Xdoor_2, TRUE, FALSE,
6731 EL_EM_GATE_2, -1, -1
6734 Xdoor_3, TRUE, FALSE,
6735 EL_EM_GATE_3, -1, -1
6738 Xdoor_4, TRUE, FALSE,
6739 EL_EM_GATE_4, -1, -1
6742 Xdoor_5, TRUE, FALSE,
6743 EL_EMC_GATE_5, -1, -1
6746 Xdoor_6, TRUE, FALSE,
6747 EL_EMC_GATE_6, -1, -1
6750 Xdoor_7, TRUE, FALSE,
6751 EL_EMC_GATE_7, -1, -1
6754 Xdoor_8, TRUE, FALSE,
6755 EL_EMC_GATE_8, -1, -1
6759 Xfake_door_1, TRUE, FALSE,
6760 EL_EM_GATE_1_GRAY, -1, -1
6763 Xfake_door_2, TRUE, FALSE,
6764 EL_EM_GATE_2_GRAY, -1, -1
6767 Xfake_door_3, TRUE, FALSE,
6768 EL_EM_GATE_3_GRAY, -1, -1
6771 Xfake_door_4, TRUE, FALSE,
6772 EL_EM_GATE_4_GRAY, -1, -1
6775 Xfake_door_5, TRUE, FALSE,
6776 EL_EMC_GATE_5_GRAY, -1, -1
6779 Xfake_door_6, TRUE, FALSE,
6780 EL_EMC_GATE_6_GRAY, -1, -1
6783 Xfake_door_7, TRUE, FALSE,
6784 EL_EMC_GATE_7_GRAY, -1, -1
6787 Xfake_door_8, TRUE, FALSE,
6788 EL_EMC_GATE_8_GRAY, -1, -1
6792 Xballoon, TRUE, FALSE,
6796 Yballoon_n, FALSE, FALSE,
6797 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6800 Yballoon_nB, FALSE, TRUE,
6801 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6804 Yballoon_e, FALSE, FALSE,
6805 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6808 Yballoon_eB, FALSE, TRUE,
6809 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6812 Yballoon_s, FALSE, FALSE,
6813 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6816 Yballoon_sB, FALSE, TRUE,
6817 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6820 Yballoon_w, FALSE, FALSE,
6821 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6824 Yballoon_wB, FALSE, TRUE,
6825 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6829 Xball_1, TRUE, FALSE,
6830 EL_EMC_MAGIC_BALL, -1, -1
6833 Yball_1, FALSE, FALSE,
6834 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6837 Xball_2, FALSE, FALSE,
6838 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6841 Yball_2, FALSE, FALSE,
6842 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6845 Yball_blank, FALSE, FALSE,
6846 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6850 Xamoeba_1, TRUE, FALSE,
6851 EL_AMOEBA_DRY, ACTION_OTHER, -1
6854 Xamoeba_2, FALSE, FALSE,
6855 EL_AMOEBA_DRY, ACTION_OTHER, -1
6858 Xamoeba_3, FALSE, FALSE,
6859 EL_AMOEBA_DRY, ACTION_OTHER, -1
6862 Xamoeba_4, FALSE, FALSE,
6863 EL_AMOEBA_DRY, ACTION_OTHER, -1
6866 Xamoeba_5, TRUE, FALSE,
6867 EL_AMOEBA_WET, ACTION_OTHER, -1
6870 Xamoeba_6, FALSE, FALSE,
6871 EL_AMOEBA_WET, ACTION_OTHER, -1
6874 Xamoeba_7, FALSE, FALSE,
6875 EL_AMOEBA_WET, ACTION_OTHER, -1
6878 Xamoeba_8, FALSE, FALSE,
6879 EL_AMOEBA_WET, ACTION_OTHER, -1
6883 Xdrip, FALSE, FALSE,
6884 EL_AMOEBA_DROP, ACTION_GROWING, -1
6887 Xdrip_fall, TRUE, FALSE,
6888 EL_AMOEBA_DROP, -1, -1
6891 Xdrip_stretch, FALSE, FALSE,
6892 EL_AMOEBA_DROP, ACTION_FALLING, -1
6895 Xdrip_stretchB, FALSE, TRUE,
6896 EL_AMOEBA_DROP, ACTION_FALLING, -1
6899 Ydrip_1_s, FALSE, FALSE,
6900 EL_AMOEBA_DROP, ACTION_FALLING, -1
6903 Ydrip_1_sB, FALSE, TRUE,
6904 EL_AMOEBA_DROP, ACTION_FALLING, -1
6907 Ydrip_2_s, FALSE, FALSE,
6908 EL_AMOEBA_DROP, ACTION_FALLING, -1
6911 Ydrip_2_sB, FALSE, TRUE,
6912 EL_AMOEBA_DROP, ACTION_FALLING, -1
6916 Xwonderwall, TRUE, FALSE,
6917 EL_MAGIC_WALL, -1, -1
6920 Ywonderwall, FALSE, FALSE,
6921 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6925 Xwheel, TRUE, FALSE,
6926 EL_ROBOT_WHEEL, -1, -1
6929 Ywheel, FALSE, FALSE,
6930 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6934 Xswitch, TRUE, FALSE,
6935 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6938 Yswitch, FALSE, FALSE,
6939 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6943 Xbumper, TRUE, FALSE,
6944 EL_EMC_SPRING_BUMPER, -1, -1
6947 Ybumper, FALSE, FALSE,
6948 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6952 Xacid_nw, TRUE, FALSE,
6953 EL_ACID_POOL_TOPLEFT, -1, -1
6956 Xacid_ne, TRUE, FALSE,
6957 EL_ACID_POOL_TOPRIGHT, -1, -1
6960 Xacid_sw, TRUE, FALSE,
6961 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6964 Xacid_s, TRUE, FALSE,
6965 EL_ACID_POOL_BOTTOM, -1, -1
6968 Xacid_se, TRUE, FALSE,
6969 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6973 Xfake_blank, TRUE, FALSE,
6974 EL_INVISIBLE_WALL, -1, -1
6977 Yfake_blank, FALSE, FALSE,
6978 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6982 Xfake_grass, TRUE, FALSE,
6983 EL_EMC_FAKE_GRASS, -1, -1
6986 Yfake_grass, FALSE, FALSE,
6987 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6991 Xfake_amoeba, TRUE, FALSE,
6992 EL_EMC_DRIPPER, -1, -1
6995 Yfake_amoeba, FALSE, FALSE,
6996 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7000 Xlenses, TRUE, FALSE,
7001 EL_EMC_LENSES, -1, -1
7005 Xmagnify, TRUE, FALSE,
7006 EL_EMC_MAGNIFIER, -1, -1
7011 EL_QUICKSAND_EMPTY, -1, -1
7014 Xsand_stone, TRUE, FALSE,
7015 EL_QUICKSAND_FULL, -1, -1
7018 Xsand_stonein_1, FALSE, TRUE,
7019 EL_ROCK, ACTION_FILLING, -1
7022 Xsand_stonein_2, FALSE, TRUE,
7023 EL_ROCK, ACTION_FILLING, -1
7026 Xsand_stonein_3, FALSE, TRUE,
7027 EL_ROCK, ACTION_FILLING, -1
7030 Xsand_stonein_4, FALSE, TRUE,
7031 EL_ROCK, ACTION_FILLING, -1
7034 Xsand_sandstone_1, FALSE, FALSE,
7035 EL_QUICKSAND_FILLING, -1, -1
7038 Xsand_sandstone_2, FALSE, FALSE,
7039 EL_QUICKSAND_FILLING, -1, -1
7042 Xsand_sandstone_3, FALSE, FALSE,
7043 EL_QUICKSAND_FILLING, -1, -1
7046 Xsand_sandstone_4, FALSE, FALSE,
7047 EL_QUICKSAND_FILLING, -1, -1
7050 Xsand_stonesand_1, FALSE, FALSE,
7051 EL_QUICKSAND_EMPTYING, -1, -1
7054 Xsand_stonesand_2, FALSE, FALSE,
7055 EL_QUICKSAND_EMPTYING, -1, -1
7058 Xsand_stonesand_3, FALSE, FALSE,
7059 EL_QUICKSAND_EMPTYING, -1, -1
7062 Xsand_stonesand_4, FALSE, FALSE,
7063 EL_QUICKSAND_EMPTYING, -1, -1
7066 Xsand_stoneout_1, FALSE, FALSE,
7067 EL_ROCK, ACTION_EMPTYING, -1
7070 Xsand_stoneout_2, FALSE, FALSE,
7071 EL_ROCK, ACTION_EMPTYING, -1
7074 Xsand_stonesand_quickout_1, FALSE, FALSE,
7075 EL_QUICKSAND_EMPTYING, -1, -1
7078 Xsand_stonesand_quickout_2, FALSE, FALSE,
7079 EL_QUICKSAND_EMPTYING, -1, -1
7083 Xslide_ns, TRUE, FALSE,
7084 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7087 Yslide_ns_blank, FALSE, FALSE,
7088 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7091 Xslide_ew, TRUE, FALSE,
7092 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7095 Yslide_ew_blank, FALSE, FALSE,
7096 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7100 Xwind_n, TRUE, FALSE,
7101 EL_BALLOON_SWITCH_UP, -1, -1
7104 Xwind_e, TRUE, FALSE,
7105 EL_BALLOON_SWITCH_RIGHT, -1, -1
7108 Xwind_s, TRUE, FALSE,
7109 EL_BALLOON_SWITCH_DOWN, -1, -1
7112 Xwind_w, TRUE, FALSE,
7113 EL_BALLOON_SWITCH_LEFT, -1, -1
7116 Xwind_any, TRUE, FALSE,
7117 EL_BALLOON_SWITCH_ANY, -1, -1
7120 Xwind_stop, TRUE, FALSE,
7121 EL_BALLOON_SWITCH_NONE, -1, -1
7126 EL_EM_EXIT_CLOSED, -1, -1
7129 Xexit_1, TRUE, FALSE,
7130 EL_EM_EXIT_OPEN, -1, -1
7133 Xexit_2, FALSE, FALSE,
7134 EL_EM_EXIT_OPEN, -1, -1
7137 Xexit_3, FALSE, FALSE,
7138 EL_EM_EXIT_OPEN, -1, -1
7142 Xpause, FALSE, FALSE,
7147 Xwall_1, TRUE, FALSE,
7151 Xwall_2, TRUE, FALSE,
7152 EL_EMC_WALL_14, -1, -1
7155 Xwall_3, TRUE, FALSE,
7156 EL_EMC_WALL_15, -1, -1
7159 Xwall_4, TRUE, FALSE,
7160 EL_EMC_WALL_16, -1, -1
7164 Xroundwall_1, TRUE, FALSE,
7165 EL_WALL_SLIPPERY, -1, -1
7168 Xroundwall_2, TRUE, FALSE,
7169 EL_EMC_WALL_SLIPPERY_2, -1, -1
7172 Xroundwall_3, TRUE, FALSE,
7173 EL_EMC_WALL_SLIPPERY_3, -1, -1
7176 Xroundwall_4, TRUE, FALSE,
7177 EL_EMC_WALL_SLIPPERY_4, -1, -1
7181 Xsteel_1, TRUE, FALSE,
7182 EL_STEELWALL, -1, -1
7185 Xsteel_2, TRUE, FALSE,
7186 EL_EMC_STEELWALL_2, -1, -1
7189 Xsteel_3, TRUE, FALSE,
7190 EL_EMC_STEELWALL_3, -1, -1
7193 Xsteel_4, TRUE, FALSE,
7194 EL_EMC_STEELWALL_4, -1, -1
7198 Xdecor_1, TRUE, FALSE,
7199 EL_EMC_WALL_8, -1, -1
7202 Xdecor_2, TRUE, FALSE,
7203 EL_EMC_WALL_6, -1, -1
7206 Xdecor_3, TRUE, FALSE,
7207 EL_EMC_WALL_4, -1, -1
7210 Xdecor_4, TRUE, FALSE,
7211 EL_EMC_WALL_7, -1, -1
7214 Xdecor_5, TRUE, FALSE,
7215 EL_EMC_WALL_5, -1, -1
7218 Xdecor_6, TRUE, FALSE,
7219 EL_EMC_WALL_9, -1, -1
7222 Xdecor_7, TRUE, FALSE,
7223 EL_EMC_WALL_10, -1, -1
7226 Xdecor_8, TRUE, FALSE,
7227 EL_EMC_WALL_1, -1, -1
7230 Xdecor_9, TRUE, FALSE,
7231 EL_EMC_WALL_2, -1, -1
7234 Xdecor_10, TRUE, FALSE,
7235 EL_EMC_WALL_3, -1, -1
7238 Xdecor_11, TRUE, FALSE,
7239 EL_EMC_WALL_11, -1, -1
7242 Xdecor_12, TRUE, FALSE,
7243 EL_EMC_WALL_12, -1, -1
7247 Xalpha_0, TRUE, FALSE,
7248 EL_CHAR('0'), -1, -1
7251 Xalpha_1, TRUE, FALSE,
7252 EL_CHAR('1'), -1, -1
7255 Xalpha_2, TRUE, FALSE,
7256 EL_CHAR('2'), -1, -1
7259 Xalpha_3, TRUE, FALSE,
7260 EL_CHAR('3'), -1, -1
7263 Xalpha_4, TRUE, FALSE,
7264 EL_CHAR('4'), -1, -1
7267 Xalpha_5, TRUE, FALSE,
7268 EL_CHAR('5'), -1, -1
7271 Xalpha_6, TRUE, FALSE,
7272 EL_CHAR('6'), -1, -1
7275 Xalpha_7, TRUE, FALSE,
7276 EL_CHAR('7'), -1, -1
7279 Xalpha_8, TRUE, FALSE,
7280 EL_CHAR('8'), -1, -1
7283 Xalpha_9, TRUE, FALSE,
7284 EL_CHAR('9'), -1, -1
7287 Xalpha_excla, TRUE, FALSE,
7288 EL_CHAR('!'), -1, -1
7291 Xalpha_apost, TRUE, FALSE,
7292 EL_CHAR('\''), -1, -1
7295 Xalpha_comma, TRUE, FALSE,
7296 EL_CHAR(','), -1, -1
7299 Xalpha_minus, TRUE, FALSE,
7300 EL_CHAR('-'), -1, -1
7303 Xalpha_perio, TRUE, FALSE,
7304 EL_CHAR('.'), -1, -1
7307 Xalpha_colon, TRUE, FALSE,
7308 EL_CHAR(':'), -1, -1
7311 Xalpha_quest, TRUE, FALSE,
7312 EL_CHAR('?'), -1, -1
7315 Xalpha_a, TRUE, FALSE,
7316 EL_CHAR('A'), -1, -1
7319 Xalpha_b, TRUE, FALSE,
7320 EL_CHAR('B'), -1, -1
7323 Xalpha_c, TRUE, FALSE,
7324 EL_CHAR('C'), -1, -1
7327 Xalpha_d, TRUE, FALSE,
7328 EL_CHAR('D'), -1, -1
7331 Xalpha_e, TRUE, FALSE,
7332 EL_CHAR('E'), -1, -1
7335 Xalpha_f, TRUE, FALSE,
7336 EL_CHAR('F'), -1, -1
7339 Xalpha_g, TRUE, FALSE,
7340 EL_CHAR('G'), -1, -1
7343 Xalpha_h, TRUE, FALSE,
7344 EL_CHAR('H'), -1, -1
7347 Xalpha_i, TRUE, FALSE,
7348 EL_CHAR('I'), -1, -1
7351 Xalpha_j, TRUE, FALSE,
7352 EL_CHAR('J'), -1, -1
7355 Xalpha_k, TRUE, FALSE,
7356 EL_CHAR('K'), -1, -1
7359 Xalpha_l, TRUE, FALSE,
7360 EL_CHAR('L'), -1, -1
7363 Xalpha_m, TRUE, FALSE,
7364 EL_CHAR('M'), -1, -1
7367 Xalpha_n, TRUE, FALSE,
7368 EL_CHAR('N'), -1, -1
7371 Xalpha_o, TRUE, FALSE,
7372 EL_CHAR('O'), -1, -1
7375 Xalpha_p, TRUE, FALSE,
7376 EL_CHAR('P'), -1, -1
7379 Xalpha_q, TRUE, FALSE,
7380 EL_CHAR('Q'), -1, -1
7383 Xalpha_r, TRUE, FALSE,
7384 EL_CHAR('R'), -1, -1
7387 Xalpha_s, TRUE, FALSE,
7388 EL_CHAR('S'), -1, -1
7391 Xalpha_t, TRUE, FALSE,
7392 EL_CHAR('T'), -1, -1
7395 Xalpha_u, TRUE, FALSE,
7396 EL_CHAR('U'), -1, -1
7399 Xalpha_v, TRUE, FALSE,
7400 EL_CHAR('V'), -1, -1
7403 Xalpha_w, TRUE, FALSE,
7404 EL_CHAR('W'), -1, -1
7407 Xalpha_x, TRUE, FALSE,
7408 EL_CHAR('X'), -1, -1
7411 Xalpha_y, TRUE, FALSE,
7412 EL_CHAR('Y'), -1, -1
7415 Xalpha_z, TRUE, FALSE,
7416 EL_CHAR('Z'), -1, -1
7419 Xalpha_arrow_e, TRUE, FALSE,
7420 EL_CHAR('>'), -1, -1
7423 Xalpha_arrow_w, TRUE, FALSE,
7424 EL_CHAR('<'), -1, -1
7427 Xalpha_copyr, TRUE, FALSE,
7428 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7432 Ykey_1_blank, FALSE, FALSE,
7433 EL_EM_KEY_1, ACTION_COLLECTING, -1
7436 Ykey_2_blank, FALSE, FALSE,
7437 EL_EM_KEY_2, ACTION_COLLECTING, -1
7440 Ykey_3_blank, FALSE, FALSE,
7441 EL_EM_KEY_3, ACTION_COLLECTING, -1
7444 Ykey_4_blank, FALSE, FALSE,
7445 EL_EM_KEY_4, ACTION_COLLECTING, -1
7448 Ykey_5_blank, FALSE, FALSE,
7449 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7452 Ykey_6_blank, FALSE, FALSE,
7453 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7456 Ykey_7_blank, FALSE, FALSE,
7457 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7460 Ykey_8_blank, FALSE, FALSE,
7461 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7464 Ylenses_blank, FALSE, FALSE,
7465 EL_EMC_LENSES, ACTION_COLLECTING, -1
7468 Ymagnify_blank, FALSE, FALSE,
7469 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7472 Ygrass_blank, FALSE, FALSE,
7473 EL_EMC_GRASS, ACTION_SNAPPING, -1
7476 Ydirt_blank, FALSE, FALSE,
7477 EL_SAND, ACTION_SNAPPING, -1
7486 static struct Mapping_EM_to_RND_player
7495 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7499 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7503 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7507 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7511 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7515 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7519 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7523 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7527 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7531 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7535 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7539 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7543 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7547 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7551 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7555 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7559 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7563 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7567 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7571 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7575 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7579 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7583 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7587 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7591 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7595 EL_PLAYER_1, ACTION_DEFAULT, -1,
7599 EL_PLAYER_2, ACTION_DEFAULT, -1,
7603 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7607 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7611 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7615 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7619 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7623 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7627 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7631 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7635 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7639 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7643 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7647 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7651 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7655 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7659 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7663 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7667 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7671 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7675 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7679 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7683 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7687 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7691 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7695 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7699 EL_PLAYER_3, ACTION_DEFAULT, -1,
7703 EL_PLAYER_4, ACTION_DEFAULT, -1,
7712 int map_element_RND_to_EM_cave(int element_rnd)
7714 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7715 static boolean mapping_initialized = FALSE;
7717 if (!mapping_initialized)
7721 // return "Xalpha_quest" for all undefined elements in mapping array
7722 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7723 mapping_RND_to_EM[i] = Xalpha_quest;
7725 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7726 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7727 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7728 em_object_mapping_list[i].element_em;
7730 mapping_initialized = TRUE;
7733 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7735 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7740 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7743 int map_element_EM_to_RND_cave(int element_em_cave)
7745 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7746 static boolean mapping_initialized = FALSE;
7748 if (!mapping_initialized)
7752 // return "EL_UNKNOWN" for all undefined elements in mapping array
7753 for (i = 0; i < GAME_TILE_MAX; i++)
7754 mapping_EM_to_RND[i] = EL_UNKNOWN;
7756 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7757 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7758 em_object_mapping_list[i].element_rnd;
7760 mapping_initialized = TRUE;
7763 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7765 Error(ERR_WARN, "invalid EM cave element %d", element_em_cave);
7770 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7773 int map_element_EM_to_RND_game(int element_em_game)
7775 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7776 static boolean mapping_initialized = FALSE;
7778 if (!mapping_initialized)
7782 // return "EL_UNKNOWN" for all undefined elements in mapping array
7783 for (i = 0; i < GAME_TILE_MAX; i++)
7784 mapping_EM_to_RND[i] = EL_UNKNOWN;
7786 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7787 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7788 em_object_mapping_list[i].element_rnd;
7790 mapping_initialized = TRUE;
7793 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7795 Error(ERR_WARN, "invalid EM game element %d", element_em_game);
7800 return mapping_EM_to_RND[element_em_game];
7803 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7805 struct LevelInfo_EM *level_em = level->native_em_level;
7806 struct CAVE *cav = level_em->cav;
7809 for (i = 0; i < GAME_TILE_MAX; i++)
7810 cav->android_array[i] = Cblank;
7812 for (i = 0; i < level->num_android_clone_elements; i++)
7814 int element_rnd = level->android_clone_element[i];
7815 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7817 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7818 if (em_object_mapping_list[j].element_rnd == element_rnd)
7819 cav->android_array[em_object_mapping_list[j].element_em] =
7824 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7826 struct LevelInfo_EM *level_em = level->native_em_level;
7827 struct CAVE *cav = level_em->cav;
7830 level->num_android_clone_elements = 0;
7832 for (i = 0; i < GAME_TILE_MAX; i++)
7834 int element_em_cave = cav->android_array[i];
7836 boolean element_found = FALSE;
7838 if (element_em_cave == Cblank)
7841 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7843 for (j = 0; j < level->num_android_clone_elements; j++)
7844 if (level->android_clone_element[j] == element_rnd)
7845 element_found = TRUE;
7849 level->android_clone_element[level->num_android_clone_elements++] =
7852 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7857 if (level->num_android_clone_elements == 0)
7859 level->num_android_clone_elements = 1;
7860 level->android_clone_element[0] = EL_EMPTY;
7864 int map_direction_RND_to_EM(int direction)
7866 return (direction == MV_UP ? 0 :
7867 direction == MV_RIGHT ? 1 :
7868 direction == MV_DOWN ? 2 :
7869 direction == MV_LEFT ? 3 :
7873 int map_direction_EM_to_RND(int direction)
7875 return (direction == 0 ? MV_UP :
7876 direction == 1 ? MV_RIGHT :
7877 direction == 2 ? MV_DOWN :
7878 direction == 3 ? MV_LEFT :
7882 int map_element_RND_to_SP(int element_rnd)
7884 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7886 if (element_rnd >= EL_SP_START &&
7887 element_rnd <= EL_SP_END)
7888 element_sp = element_rnd - EL_SP_START;
7889 else if (element_rnd == EL_EMPTY_SPACE)
7891 else if (element_rnd == EL_INVISIBLE_WALL)
7897 int map_element_SP_to_RND(int element_sp)
7899 int element_rnd = EL_UNKNOWN;
7901 if (element_sp >= 0x00 &&
7903 element_rnd = EL_SP_START + element_sp;
7904 else if (element_sp == 0x28)
7905 element_rnd = EL_INVISIBLE_WALL;
7910 int map_action_SP_to_RND(int action_sp)
7914 case actActive: return ACTION_ACTIVE;
7915 case actImpact: return ACTION_IMPACT;
7916 case actExploding: return ACTION_EXPLODING;
7917 case actDigging: return ACTION_DIGGING;
7918 case actSnapping: return ACTION_SNAPPING;
7919 case actCollecting: return ACTION_COLLECTING;
7920 case actPassing: return ACTION_PASSING;
7921 case actPushing: return ACTION_PUSHING;
7922 case actDropping: return ACTION_DROPPING;
7924 default: return ACTION_DEFAULT;
7928 int map_element_RND_to_MM(int element_rnd)
7930 return (element_rnd >= EL_MM_START_1 &&
7931 element_rnd <= EL_MM_END_1 ?
7932 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7934 element_rnd >= EL_MM_START_2 &&
7935 element_rnd <= EL_MM_END_2 ?
7936 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7938 element_rnd >= EL_CHAR_START &&
7939 element_rnd <= EL_CHAR_END ?
7940 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7942 element_rnd >= EL_MM_RUNTIME_START &&
7943 element_rnd <= EL_MM_RUNTIME_END ?
7944 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7946 element_rnd >= EL_MM_DUMMY_START &&
7947 element_rnd <= EL_MM_DUMMY_END ?
7948 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7950 EL_MM_EMPTY_NATIVE);
7953 int map_element_MM_to_RND(int element_mm)
7955 return (element_mm == EL_MM_EMPTY_NATIVE ||
7956 element_mm == EL_DF_EMPTY_NATIVE ?
7959 element_mm >= EL_MM_START_1_NATIVE &&
7960 element_mm <= EL_MM_END_1_NATIVE ?
7961 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7963 element_mm >= EL_MM_START_2_NATIVE &&
7964 element_mm <= EL_MM_END_2_NATIVE ?
7965 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7967 element_mm >= EL_MM_CHAR_START_NATIVE &&
7968 element_mm <= EL_MM_CHAR_END_NATIVE ?
7969 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7971 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7972 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7973 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7975 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7976 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7977 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7982 int map_action_MM_to_RND(int action_mm)
7984 // all MM actions are defined to exactly match their RND counterparts
7988 int map_sound_MM_to_RND(int sound_mm)
7992 case SND_MM_GAME_LEVELTIME_CHARGING:
7993 return SND_GAME_LEVELTIME_CHARGING;
7995 case SND_MM_GAME_HEALTH_CHARGING:
7996 return SND_GAME_HEALTH_CHARGING;
7999 return SND_UNDEFINED;
8003 int map_mm_wall_element(int element)
8005 return (element >= EL_MM_STEEL_WALL_START &&
8006 element <= EL_MM_STEEL_WALL_END ?
8009 element >= EL_MM_WOODEN_WALL_START &&
8010 element <= EL_MM_WOODEN_WALL_END ?
8013 element >= EL_MM_ICE_WALL_START &&
8014 element <= EL_MM_ICE_WALL_END ?
8017 element >= EL_MM_AMOEBA_WALL_START &&
8018 element <= EL_MM_AMOEBA_WALL_END ?
8021 element >= EL_DF_STEEL_WALL_START &&
8022 element <= EL_DF_STEEL_WALL_END ?
8025 element >= EL_DF_WOODEN_WALL_START &&
8026 element <= EL_DF_WOODEN_WALL_END ?
8032 int map_mm_wall_element_editor(int element)
8036 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8037 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8038 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8039 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8040 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8041 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8043 default: return element;
8047 int get_next_element(int element)
8051 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8052 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8053 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8054 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8055 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8056 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8057 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8058 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8059 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8060 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8061 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8063 default: return element;
8067 int el2img_mm(int element_mm)
8069 return el2img(map_element_MM_to_RND(element_mm));
8072 int el_act_dir2img(int element, int action, int direction)
8074 element = GFX_ELEMENT(element);
8075 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8077 // direction_graphic[][] == graphic[] for undefined direction graphics
8078 return element_info[element].direction_graphic[action][direction];
8081 static int el_act_dir2crm(int element, int action, int direction)
8083 element = GFX_ELEMENT(element);
8084 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8086 // direction_graphic[][] == graphic[] for undefined direction graphics
8087 return element_info[element].direction_crumbled[action][direction];
8090 int el_act2img(int element, int action)
8092 element = GFX_ELEMENT(element);
8094 return element_info[element].graphic[action];
8097 int el_act2crm(int element, int action)
8099 element = GFX_ELEMENT(element);
8101 return element_info[element].crumbled[action];
8104 int el_dir2img(int element, int direction)
8106 element = GFX_ELEMENT(element);
8108 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8111 int el2baseimg(int element)
8113 return element_info[element].graphic[ACTION_DEFAULT];
8116 int el2img(int element)
8118 element = GFX_ELEMENT(element);
8120 return element_info[element].graphic[ACTION_DEFAULT];
8123 int el2edimg(int element)
8125 element = GFX_ELEMENT(element);
8127 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8130 int el2preimg(int element)
8132 element = GFX_ELEMENT(element);
8134 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8137 int el2panelimg(int element)
8139 element = GFX_ELEMENT(element);
8141 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8144 int font2baseimg(int font_nr)
8146 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8149 int getBeltNrFromBeltElement(int element)
8151 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8152 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8153 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8156 int getBeltNrFromBeltActiveElement(int element)
8158 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8159 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8160 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8163 int getBeltNrFromBeltSwitchElement(int element)
8165 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8166 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8167 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8170 int getBeltDirNrFromBeltElement(int element)
8172 static int belt_base_element[4] =
8174 EL_CONVEYOR_BELT_1_LEFT,
8175 EL_CONVEYOR_BELT_2_LEFT,
8176 EL_CONVEYOR_BELT_3_LEFT,
8177 EL_CONVEYOR_BELT_4_LEFT
8180 int belt_nr = getBeltNrFromBeltElement(element);
8181 int belt_dir_nr = element - belt_base_element[belt_nr];
8183 return (belt_dir_nr % 3);
8186 int getBeltDirNrFromBeltSwitchElement(int element)
8188 static int belt_base_element[4] =
8190 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8191 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8192 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8193 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8196 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8197 int belt_dir_nr = element - belt_base_element[belt_nr];
8199 return (belt_dir_nr % 3);
8202 int getBeltDirFromBeltElement(int element)
8204 static int belt_move_dir[3] =
8211 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8213 return belt_move_dir[belt_dir_nr];
8216 int getBeltDirFromBeltSwitchElement(int element)
8218 static int belt_move_dir[3] =
8225 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8227 return belt_move_dir[belt_dir_nr];
8230 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8232 static int belt_base_element[4] =
8234 EL_CONVEYOR_BELT_1_LEFT,
8235 EL_CONVEYOR_BELT_2_LEFT,
8236 EL_CONVEYOR_BELT_3_LEFT,
8237 EL_CONVEYOR_BELT_4_LEFT
8240 return belt_base_element[belt_nr] + belt_dir_nr;
8243 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8245 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8247 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8250 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8252 static int belt_base_element[4] =
8254 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8255 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8256 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8257 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8260 return belt_base_element[belt_nr] + belt_dir_nr;
8263 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8265 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8267 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8270 boolean getTeamMode_EM(void)
8272 return game.team_mode || network_playing;
8275 unsigned int InitRND(int seed)
8277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8278 return InitEngineRandom_EM(seed);
8279 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8280 return InitEngineRandom_SP(seed);
8281 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8282 return InitEngineRandom_MM(seed);
8284 return InitEngineRandom_RND(seed);
8287 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8288 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8290 static int get_effective_element_EM(int tile, int frame_em)
8292 int element = object_mapping[tile].element_rnd;
8293 int action = object_mapping[tile].action;
8294 boolean is_backside = object_mapping[tile].is_backside;
8295 boolean action_removing = (action == ACTION_DIGGING ||
8296 action == ACTION_SNAPPING ||
8297 action == ACTION_COLLECTING);
8305 return (frame_em > 5 ? EL_EMPTY : element);
8311 else // frame_em == 7
8322 case Ydiamond_stone:
8326 case Xdrip_stretchB:
8342 case Ymagnify_blank:
8345 case Xsand_stonein_1:
8346 case Xsand_stonein_2:
8347 case Xsand_stonein_3:
8348 case Xsand_stonein_4:
8352 return (is_backside || action_removing ? EL_EMPTY : element);
8357 static boolean check_linear_animation_EM(int tile)
8361 case Xsand_stonesand_1:
8362 case Xsand_stonesand_quickout_1:
8363 case Xsand_sandstone_1:
8364 case Xsand_stonein_1:
8365 case Xsand_stoneout_1:
8393 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8394 boolean has_crumbled_graphics,
8395 int crumbled, int sync_frame)
8397 // if element can be crumbled, but certain action graphics are just empty
8398 // space (like instantly snapping sand to empty space in 1 frame), do not
8399 // treat these empty space graphics as crumbled graphics in EMC engine
8400 if (crumbled == IMG_EMPTY_SPACE)
8401 has_crumbled_graphics = FALSE;
8403 if (has_crumbled_graphics)
8405 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8406 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8407 g_crumbled->anim_delay,
8408 g_crumbled->anim_mode,
8409 g_crumbled->anim_start_frame,
8412 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8413 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8415 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8416 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8418 g_em->has_crumbled_graphics = TRUE;
8422 g_em->crumbled_bitmap = NULL;
8423 g_em->crumbled_src_x = 0;
8424 g_em->crumbled_src_y = 0;
8425 g_em->crumbled_border_size = 0;
8426 g_em->crumbled_tile_size = 0;
8428 g_em->has_crumbled_graphics = FALSE;
8433 void ResetGfxAnimation_EM(int x, int y, int tile)
8439 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8440 int tile, int frame_em, int x, int y)
8442 int action = object_mapping[tile].action;
8443 int direction = object_mapping[tile].direction;
8444 int effective_element = get_effective_element_EM(tile, frame_em);
8445 int graphic = (direction == MV_NONE ?
8446 el_act2img(effective_element, action) :
8447 el_act_dir2img(effective_element, action, direction));
8448 struct GraphicInfo *g = &graphic_info[graphic];
8450 boolean action_removing = (action == ACTION_DIGGING ||
8451 action == ACTION_SNAPPING ||
8452 action == ACTION_COLLECTING);
8453 boolean action_moving = (action == ACTION_FALLING ||
8454 action == ACTION_MOVING ||
8455 action == ACTION_PUSHING ||
8456 action == ACTION_EATING ||
8457 action == ACTION_FILLING ||
8458 action == ACTION_EMPTYING);
8459 boolean action_falling = (action == ACTION_FALLING ||
8460 action == ACTION_FILLING ||
8461 action == ACTION_EMPTYING);
8463 // special case: graphic uses "2nd movement tile" and has defined
8464 // 7 frames for movement animation (or less) => use default graphic
8465 // for last (8th) frame which ends the movement animation
8466 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8468 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8469 graphic = (direction == MV_NONE ?
8470 el_act2img(effective_element, action) :
8471 el_act_dir2img(effective_element, action, direction));
8473 g = &graphic_info[graphic];
8476 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8480 else if (action_moving)
8482 boolean is_backside = object_mapping[tile].is_backside;
8486 int direction = object_mapping[tile].direction;
8487 int move_dir = (action_falling ? MV_DOWN : direction);
8492 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8493 if (g->double_movement && frame_em == 0)
8497 if (move_dir == MV_LEFT)
8498 GfxFrame[x - 1][y] = GfxFrame[x][y];
8499 else if (move_dir == MV_RIGHT)
8500 GfxFrame[x + 1][y] = GfxFrame[x][y];
8501 else if (move_dir == MV_UP)
8502 GfxFrame[x][y - 1] = GfxFrame[x][y];
8503 else if (move_dir == MV_DOWN)
8504 GfxFrame[x][y + 1] = GfxFrame[x][y];
8511 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8512 if (tile == Xsand_stonesand_quickout_1 ||
8513 tile == Xsand_stonesand_quickout_2)
8517 if (graphic_info[graphic].anim_global_sync)
8518 sync_frame = FrameCounter;
8519 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8520 sync_frame = GfxFrame[x][y];
8522 sync_frame = 0; // playfield border (pseudo steel)
8524 SetRandomAnimationValue(x, y);
8526 int frame = getAnimationFrame(g->anim_frames,
8529 g->anim_start_frame,
8532 g_em->unique_identifier =
8533 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8536 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8537 int tile, int frame_em, int x, int y)
8539 int action = object_mapping[tile].action;
8540 int direction = object_mapping[tile].direction;
8541 boolean is_backside = object_mapping[tile].is_backside;
8542 int effective_element = get_effective_element_EM(tile, frame_em);
8543 int effective_action = action;
8544 int graphic = (direction == MV_NONE ?
8545 el_act2img(effective_element, effective_action) :
8546 el_act_dir2img(effective_element, effective_action,
8548 int crumbled = (direction == MV_NONE ?
8549 el_act2crm(effective_element, effective_action) :
8550 el_act_dir2crm(effective_element, effective_action,
8552 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8553 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8554 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8555 struct GraphicInfo *g = &graphic_info[graphic];
8558 // special case: graphic uses "2nd movement tile" and has defined
8559 // 7 frames for movement animation (or less) => use default graphic
8560 // for last (8th) frame which ends the movement animation
8561 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8563 effective_action = ACTION_DEFAULT;
8564 graphic = (direction == MV_NONE ?
8565 el_act2img(effective_element, effective_action) :
8566 el_act_dir2img(effective_element, effective_action,
8568 crumbled = (direction == MV_NONE ?
8569 el_act2crm(effective_element, effective_action) :
8570 el_act_dir2crm(effective_element, effective_action,
8573 g = &graphic_info[graphic];
8576 if (graphic_info[graphic].anim_global_sync)
8577 sync_frame = FrameCounter;
8578 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8579 sync_frame = GfxFrame[x][y];
8581 sync_frame = 0; // playfield border (pseudo steel)
8583 SetRandomAnimationValue(x, y);
8585 int frame = getAnimationFrame(g->anim_frames,
8588 g->anim_start_frame,
8591 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8592 g->double_movement && is_backside);
8594 // (updating the "crumbled" graphic definitions is probably not really needed,
8595 // as animations for crumbled graphics can't be longer than one EMC cycle)
8596 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8600 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8601 int player_nr, int anim, int frame_em)
8603 int element = player_mapping[player_nr][anim].element_rnd;
8604 int action = player_mapping[player_nr][anim].action;
8605 int direction = player_mapping[player_nr][anim].direction;
8606 int graphic = (direction == MV_NONE ?
8607 el_act2img(element, action) :
8608 el_act_dir2img(element, action, direction));
8609 struct GraphicInfo *g = &graphic_info[graphic];
8612 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8614 stored_player[player_nr].StepFrame = frame_em;
8616 sync_frame = stored_player[player_nr].Frame;
8618 int frame = getAnimationFrame(g->anim_frames,
8621 g->anim_start_frame,
8624 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8625 &g_em->src_x, &g_em->src_y, FALSE);
8628 void InitGraphicInfo_EM(void)
8632 // always start with reliable default values
8633 for (i = 0; i < GAME_TILE_MAX; i++)
8635 object_mapping[i].element_rnd = EL_UNKNOWN;
8636 object_mapping[i].is_backside = FALSE;
8637 object_mapping[i].action = ACTION_DEFAULT;
8638 object_mapping[i].direction = MV_NONE;
8641 // always start with reliable default values
8642 for (p = 0; p < MAX_PLAYERS; p++)
8644 for (i = 0; i < PLY_MAX; i++)
8646 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8647 player_mapping[p][i].action = ACTION_DEFAULT;
8648 player_mapping[p][i].direction = MV_NONE;
8652 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8654 int e = em_object_mapping_list[i].element_em;
8656 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8657 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8659 if (em_object_mapping_list[i].action != -1)
8660 object_mapping[e].action = em_object_mapping_list[i].action;
8662 if (em_object_mapping_list[i].direction != -1)
8663 object_mapping[e].direction =
8664 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8667 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8669 int a = em_player_mapping_list[i].action_em;
8670 int p = em_player_mapping_list[i].player_nr;
8672 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8674 if (em_player_mapping_list[i].action != -1)
8675 player_mapping[p][a].action = em_player_mapping_list[i].action;
8677 if (em_player_mapping_list[i].direction != -1)
8678 player_mapping[p][a].direction =
8679 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8682 for (i = 0; i < GAME_TILE_MAX; i++)
8684 int element = object_mapping[i].element_rnd;
8685 int action = object_mapping[i].action;
8686 int direction = object_mapping[i].direction;
8687 boolean is_backside = object_mapping[i].is_backside;
8688 boolean action_exploding = ((action == ACTION_EXPLODING ||
8689 action == ACTION_SMASHED_BY_ROCK ||
8690 action == ACTION_SMASHED_BY_SPRING) &&
8691 element != EL_DIAMOND);
8692 boolean action_active = (action == ACTION_ACTIVE);
8693 boolean action_other = (action == ACTION_OTHER);
8695 for (j = 0; j < 8; j++)
8697 int effective_element = get_effective_element_EM(i, j);
8698 int effective_action = (j < 7 ? action :
8699 i == Xdrip_stretch ? action :
8700 i == Xdrip_stretchB ? action :
8701 i == Ydrip_1_s ? action :
8702 i == Ydrip_1_sB ? action :
8703 i == Yball_1 ? action :
8704 i == Xball_2 ? action :
8705 i == Yball_2 ? action :
8706 i == Yball_blank ? action :
8707 i == Ykey_1_blank ? action :
8708 i == Ykey_2_blank ? action :
8709 i == Ykey_3_blank ? action :
8710 i == Ykey_4_blank ? action :
8711 i == Ykey_5_blank ? action :
8712 i == Ykey_6_blank ? action :
8713 i == Ykey_7_blank ? action :
8714 i == Ykey_8_blank ? action :
8715 i == Ylenses_blank ? action :
8716 i == Ymagnify_blank ? action :
8717 i == Ygrass_blank ? action :
8718 i == Ydirt_blank ? action :
8719 i == Xsand_stonein_1 ? action :
8720 i == Xsand_stonein_2 ? action :
8721 i == Xsand_stonein_3 ? action :
8722 i == Xsand_stonein_4 ? action :
8723 i == Xsand_stoneout_1 ? action :
8724 i == Xsand_stoneout_2 ? action :
8725 i == Xboom_android ? ACTION_EXPLODING :
8726 action_exploding ? ACTION_EXPLODING :
8727 action_active ? action :
8728 action_other ? action :
8730 int graphic = (el_act_dir2img(effective_element, effective_action,
8732 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8734 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8735 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8736 boolean has_action_graphics = (graphic != base_graphic);
8737 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8738 struct GraphicInfo *g = &graphic_info[graphic];
8739 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8742 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8743 boolean special_animation = (action != ACTION_DEFAULT &&
8744 g->anim_frames == 3 &&
8745 g->anim_delay == 2 &&
8746 g->anim_mode & ANIM_LINEAR);
8747 int sync_frame = (i == Xdrip_stretch ? 7 :
8748 i == Xdrip_stretchB ? 7 :
8749 i == Ydrip_2_s ? j + 8 :
8750 i == Ydrip_2_sB ? j + 8 :
8759 i == Xfake_acid_1 ? 0 :
8760 i == Xfake_acid_2 ? 10 :
8761 i == Xfake_acid_3 ? 20 :
8762 i == Xfake_acid_4 ? 30 :
8763 i == Xfake_acid_5 ? 40 :
8764 i == Xfake_acid_6 ? 50 :
8765 i == Xfake_acid_7 ? 60 :
8766 i == Xfake_acid_8 ? 70 :
8768 i == Yball_2 ? j + 8 :
8769 i == Yball_blank ? j + 1 :
8770 i == Ykey_1_blank ? j + 1 :
8771 i == Ykey_2_blank ? j + 1 :
8772 i == Ykey_3_blank ? j + 1 :
8773 i == Ykey_4_blank ? j + 1 :
8774 i == Ykey_5_blank ? j + 1 :
8775 i == Ykey_6_blank ? j + 1 :
8776 i == Ykey_7_blank ? j + 1 :
8777 i == Ykey_8_blank ? j + 1 :
8778 i == Ylenses_blank ? j + 1 :
8779 i == Ymagnify_blank ? j + 1 :
8780 i == Ygrass_blank ? j + 1 :
8781 i == Ydirt_blank ? j + 1 :
8782 i == Xamoeba_1 ? 0 :
8783 i == Xamoeba_2 ? 1 :
8784 i == Xamoeba_3 ? 2 :
8785 i == Xamoeba_4 ? 3 :
8786 i == Xamoeba_5 ? 0 :
8787 i == Xamoeba_6 ? 1 :
8788 i == Xamoeba_7 ? 2 :
8789 i == Xamoeba_8 ? 3 :
8790 i == Xexit_2 ? j + 8 :
8791 i == Xexit_3 ? j + 16 :
8792 i == Xdynamite_1 ? 0 :
8793 i == Xdynamite_2 ? 8 :
8794 i == Xdynamite_3 ? 16 :
8795 i == Xdynamite_4 ? 24 :
8796 i == Xsand_stonein_1 ? j + 1 :
8797 i == Xsand_stonein_2 ? j + 9 :
8798 i == Xsand_stonein_3 ? j + 17 :
8799 i == Xsand_stonein_4 ? j + 25 :
8800 i == Xsand_stoneout_1 && j == 0 ? 0 :
8801 i == Xsand_stoneout_1 && j == 1 ? 0 :
8802 i == Xsand_stoneout_1 && j == 2 ? 1 :
8803 i == Xsand_stoneout_1 && j == 3 ? 2 :
8804 i == Xsand_stoneout_1 && j == 4 ? 2 :
8805 i == Xsand_stoneout_1 && j == 5 ? 3 :
8806 i == Xsand_stoneout_1 && j == 6 ? 4 :
8807 i == Xsand_stoneout_1 && j == 7 ? 4 :
8808 i == Xsand_stoneout_2 && j == 0 ? 5 :
8809 i == Xsand_stoneout_2 && j == 1 ? 6 :
8810 i == Xsand_stoneout_2 && j == 2 ? 7 :
8811 i == Xsand_stoneout_2 && j == 3 ? 8 :
8812 i == Xsand_stoneout_2 && j == 4 ? 9 :
8813 i == Xsand_stoneout_2 && j == 5 ? 11 :
8814 i == Xsand_stoneout_2 && j == 6 ? 13 :
8815 i == Xsand_stoneout_2 && j == 7 ? 15 :
8816 i == Xboom_bug && j == 1 ? 2 :
8817 i == Xboom_bug && j == 2 ? 2 :
8818 i == Xboom_bug && j == 3 ? 4 :
8819 i == Xboom_bug && j == 4 ? 4 :
8820 i == Xboom_bug && j == 5 ? 2 :
8821 i == Xboom_bug && j == 6 ? 2 :
8822 i == Xboom_bug && j == 7 ? 0 :
8823 i == Xboom_tank && j == 1 ? 2 :
8824 i == Xboom_tank && j == 2 ? 2 :
8825 i == Xboom_tank && j == 3 ? 4 :
8826 i == Xboom_tank && j == 4 ? 4 :
8827 i == Xboom_tank && j == 5 ? 2 :
8828 i == Xboom_tank && j == 6 ? 2 :
8829 i == Xboom_tank && j == 7 ? 0 :
8830 i == Xboom_android && j == 7 ? 6 :
8831 i == Xboom_1 && j == 1 ? 2 :
8832 i == Xboom_1 && j == 2 ? 2 :
8833 i == Xboom_1 && j == 3 ? 4 :
8834 i == Xboom_1 && j == 4 ? 4 :
8835 i == Xboom_1 && j == 5 ? 6 :
8836 i == Xboom_1 && j == 6 ? 6 :
8837 i == Xboom_1 && j == 7 ? 8 :
8838 i == Xboom_2 && j == 0 ? 8 :
8839 i == Xboom_2 && j == 1 ? 8 :
8840 i == Xboom_2 && j == 2 ? 10 :
8841 i == Xboom_2 && j == 3 ? 10 :
8842 i == Xboom_2 && j == 4 ? 10 :
8843 i == Xboom_2 && j == 5 ? 12 :
8844 i == Xboom_2 && j == 6 ? 12 :
8845 i == Xboom_2 && j == 7 ? 12 :
8846 special_animation && j == 4 ? 3 :
8847 effective_action != action ? 0 :
8849 int frame = getAnimationFrame(g->anim_frames,
8852 g->anim_start_frame,
8855 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8856 g->double_movement && is_backside);
8858 g_em->bitmap = src_bitmap;
8859 g_em->src_x = src_x;
8860 g_em->src_y = src_y;
8861 g_em->src_offset_x = 0;
8862 g_em->src_offset_y = 0;
8863 g_em->dst_offset_x = 0;
8864 g_em->dst_offset_y = 0;
8865 g_em->width = TILEX;
8866 g_em->height = TILEY;
8868 g_em->preserve_background = FALSE;
8870 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8873 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8874 effective_action == ACTION_MOVING ||
8875 effective_action == ACTION_PUSHING ||
8876 effective_action == ACTION_EATING)) ||
8877 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8878 effective_action == ACTION_EMPTYING)))
8881 (effective_action == ACTION_FALLING ||
8882 effective_action == ACTION_FILLING ||
8883 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8884 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8885 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8886 int num_steps = (i == Ydrip_1_s ? 16 :
8887 i == Ydrip_1_sB ? 16 :
8888 i == Ydrip_2_s ? 16 :
8889 i == Ydrip_2_sB ? 16 :
8890 i == Xsand_stonein_1 ? 32 :
8891 i == Xsand_stonein_2 ? 32 :
8892 i == Xsand_stonein_3 ? 32 :
8893 i == Xsand_stonein_4 ? 32 :
8894 i == Xsand_stoneout_1 ? 16 :
8895 i == Xsand_stoneout_2 ? 16 : 8);
8896 int cx = ABS(dx) * (TILEX / num_steps);
8897 int cy = ABS(dy) * (TILEY / num_steps);
8898 int step_frame = (i == Ydrip_2_s ? j + 8 :
8899 i == Ydrip_2_sB ? j + 8 :
8900 i == Xsand_stonein_2 ? j + 8 :
8901 i == Xsand_stonein_3 ? j + 16 :
8902 i == Xsand_stonein_4 ? j + 24 :
8903 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8904 int step = (is_backside ? step_frame : num_steps - step_frame);
8906 if (is_backside) // tile where movement starts
8908 if (dx < 0 || dy < 0)
8910 g_em->src_offset_x = cx * step;
8911 g_em->src_offset_y = cy * step;
8915 g_em->dst_offset_x = cx * step;
8916 g_em->dst_offset_y = cy * step;
8919 else // tile where movement ends
8921 if (dx < 0 || dy < 0)
8923 g_em->dst_offset_x = cx * step;
8924 g_em->dst_offset_y = cy * step;
8928 g_em->src_offset_x = cx * step;
8929 g_em->src_offset_y = cy * step;
8933 g_em->width = TILEX - cx * step;
8934 g_em->height = TILEY - cy * step;
8937 // create unique graphic identifier to decide if tile must be redrawn
8938 /* bit 31 - 16 (16 bit): EM style graphic
8939 bit 15 - 12 ( 4 bit): EM style frame
8940 bit 11 - 6 ( 6 bit): graphic width
8941 bit 5 - 0 ( 6 bit): graphic height */
8942 g_em->unique_identifier =
8943 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8947 for (i = 0; i < GAME_TILE_MAX; i++)
8949 for (j = 0; j < 8; j++)
8951 int element = object_mapping[i].element_rnd;
8952 int action = object_mapping[i].action;
8953 int direction = object_mapping[i].direction;
8954 boolean is_backside = object_mapping[i].is_backside;
8955 int graphic_action = el_act_dir2img(element, action, direction);
8956 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8958 if ((action == ACTION_SMASHED_BY_ROCK ||
8959 action == ACTION_SMASHED_BY_SPRING ||
8960 action == ACTION_EATING) &&
8961 graphic_action == graphic_default)
8963 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8964 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8965 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8966 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8969 // no separate animation for "smashed by rock" -- use rock instead
8970 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8971 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8973 g_em->bitmap = g_xx->bitmap;
8974 g_em->src_x = g_xx->src_x;
8975 g_em->src_y = g_xx->src_y;
8976 g_em->src_offset_x = g_xx->src_offset_x;
8977 g_em->src_offset_y = g_xx->src_offset_y;
8978 g_em->dst_offset_x = g_xx->dst_offset_x;
8979 g_em->dst_offset_y = g_xx->dst_offset_y;
8980 g_em->width = g_xx->width;
8981 g_em->height = g_xx->height;
8982 g_em->unique_identifier = g_xx->unique_identifier;
8985 g_em->preserve_background = TRUE;
8990 for (p = 0; p < MAX_PLAYERS; p++)
8992 for (i = 0; i < PLY_MAX; i++)
8994 int element = player_mapping[p][i].element_rnd;
8995 int action = player_mapping[p][i].action;
8996 int direction = player_mapping[p][i].direction;
8998 for (j = 0; j < 8; j++)
9000 int effective_element = element;
9001 int effective_action = action;
9002 int graphic = (direction == MV_NONE ?
9003 el_act2img(effective_element, effective_action) :
9004 el_act_dir2img(effective_element, effective_action,
9006 struct GraphicInfo *g = &graphic_info[graphic];
9007 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9011 int frame = getAnimationFrame(g->anim_frames,
9014 g->anim_start_frame,
9017 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9019 g_em->bitmap = src_bitmap;
9020 g_em->src_x = src_x;
9021 g_em->src_y = src_y;
9022 g_em->src_offset_x = 0;
9023 g_em->src_offset_y = 0;
9024 g_em->dst_offset_x = 0;
9025 g_em->dst_offset_y = 0;
9026 g_em->width = TILEX;
9027 g_em->height = TILEY;
9033 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9034 boolean any_player_moving,
9035 boolean any_player_snapping,
9036 boolean any_player_dropping)
9038 if (frame == 0 && !any_player_dropping)
9040 if (!local_player->was_waiting)
9042 if (!CheckSaveEngineSnapshotToList())
9045 local_player->was_waiting = TRUE;
9048 else if (any_player_moving || any_player_snapping || any_player_dropping)
9050 local_player->was_waiting = FALSE;
9054 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9055 boolean murphy_is_dropping)
9057 if (murphy_is_waiting)
9059 if (!local_player->was_waiting)
9061 if (!CheckSaveEngineSnapshotToList())
9064 local_player->was_waiting = TRUE;
9069 local_player->was_waiting = FALSE;
9073 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9074 boolean button_released)
9076 if (button_released)
9078 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9079 CheckSaveEngineSnapshotToList();
9081 else if (element_clicked)
9083 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9084 CheckSaveEngineSnapshotToList();
9086 game.snapshot.changed_action = TRUE;
9090 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9091 boolean any_player_moving,
9092 boolean any_player_snapping,
9093 boolean any_player_dropping)
9095 if (tape.single_step && tape.recording && !tape.pausing)
9096 if (frame == 0 && !any_player_dropping)
9097 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9099 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9100 any_player_snapping, any_player_dropping);
9103 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9104 boolean murphy_is_dropping)
9106 boolean murphy_starts_dropping = FALSE;
9109 for (i = 0; i < MAX_PLAYERS; i++)
9110 if (stored_player[i].force_dropping)
9111 murphy_starts_dropping = TRUE;
9113 if (tape.single_step && tape.recording && !tape.pausing)
9114 if (murphy_is_waiting && !murphy_starts_dropping)
9115 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9117 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9120 void CheckSingleStepMode_MM(boolean element_clicked,
9121 boolean button_released)
9123 if (tape.single_step && tape.recording && !tape.pausing)
9124 if (button_released)
9125 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9127 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9130 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9131 int graphic, int sync_frame, int x, int y)
9133 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9135 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9138 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9140 return (IS_NEXT_FRAME(sync_frame, graphic));
9143 int getGraphicInfo_Delay(int graphic)
9145 return graphic_info[graphic].anim_delay;
9148 void PlayMenuSoundExt(int sound)
9150 if (sound == SND_UNDEFINED)
9153 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9154 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9157 if (IS_LOOP_SOUND(sound))
9158 PlaySoundLoop(sound);
9163 void PlayMenuSound(void)
9165 PlayMenuSoundExt(menu.sound[game_status]);
9168 void PlayMenuSoundStereo(int sound, int stereo_position)
9170 if (sound == SND_UNDEFINED)
9173 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9174 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9177 if (IS_LOOP_SOUND(sound))
9178 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9180 PlaySoundStereo(sound, stereo_position);
9183 void PlayMenuSoundIfLoopExt(int sound)
9185 if (sound == SND_UNDEFINED)
9188 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9189 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9192 if (IS_LOOP_SOUND(sound))
9193 PlaySoundLoop(sound);
9196 void PlayMenuSoundIfLoop(void)
9198 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9201 void PlayMenuMusicExt(int music)
9203 if (music == MUS_UNDEFINED)
9206 if (!setup.sound_music)
9209 if (IS_LOOP_MUSIC(music))
9210 PlayMusicLoop(music);
9215 void PlayMenuMusic(void)
9217 char *curr_music = getCurrentlyPlayingMusicFilename();
9218 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9220 if (!strEqual(curr_music, next_music))
9221 PlayMenuMusicExt(menu.music[game_status]);
9224 void PlayMenuSoundsAndMusic(void)
9230 static void FadeMenuSounds(void)
9235 static void FadeMenuMusic(void)
9237 char *curr_music = getCurrentlyPlayingMusicFilename();
9238 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9240 if (!strEqual(curr_music, next_music))
9244 void FadeMenuSoundsAndMusic(void)
9250 void PlaySoundActivating(void)
9253 PlaySound(SND_MENU_ITEM_ACTIVATING);
9257 void PlaySoundSelecting(void)
9260 PlaySound(SND_MENU_ITEM_SELECTING);
9264 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9266 boolean change_fullscreen = (setup.fullscreen !=
9267 video.fullscreen_enabled);
9268 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9269 setup.window_scaling_percent !=
9270 video.window_scaling_percent);
9272 if (change_window_scaling_percent && video.fullscreen_enabled)
9275 if (!change_window_scaling_percent && !video.fullscreen_available)
9278 if (change_window_scaling_percent)
9280 SDLSetWindowScaling(setup.window_scaling_percent);
9284 else if (change_fullscreen)
9286 SDLSetWindowFullscreen(setup.fullscreen);
9288 // set setup value according to successfully changed fullscreen mode
9289 setup.fullscreen = video.fullscreen_enabled;
9294 if (change_fullscreen ||
9295 change_window_scaling_percent)
9297 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9299 // save backbuffer content which gets lost when toggling fullscreen mode
9300 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9302 if (change_window_scaling_percent)
9304 // keep window mode, but change window scaling
9305 video.fullscreen_enabled = TRUE; // force new window scaling
9308 // toggle fullscreen
9309 ChangeVideoModeIfNeeded(setup.fullscreen);
9311 // set setup value according to successfully changed fullscreen mode
9312 setup.fullscreen = video.fullscreen_enabled;
9314 // restore backbuffer content from temporary backbuffer backup bitmap
9315 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9317 FreeBitmap(tmp_backbuffer);
9319 // update visible window/screen
9320 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9324 static void JoinRectangles(int *x, int *y, int *width, int *height,
9325 int x2, int y2, int width2, int height2)
9327 // do not join with "off-screen" rectangle
9328 if (x2 == -1 || y2 == -1)
9333 *width = MAX(*width, width2);
9334 *height = MAX(*height, height2);
9337 void SetAnimStatus(int anim_status_new)
9339 if (anim_status_new == GAME_MODE_MAIN)
9340 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9341 else if (anim_status_new == GAME_MODE_SCORES)
9342 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9344 global.anim_status_next = anim_status_new;
9346 // directly set screen modes that are entered without fading
9347 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9348 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9349 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9350 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9351 global.anim_status = global.anim_status_next;
9354 void SetGameStatus(int game_status_new)
9356 if (game_status_new != game_status)
9357 game_status_last_screen = game_status;
9359 game_status = game_status_new;
9361 SetAnimStatus(game_status_new);
9364 void SetFontStatus(int game_status_new)
9366 static int last_game_status = -1;
9368 if (game_status_new != -1)
9370 // set game status for font use after storing last game status
9371 last_game_status = game_status;
9372 game_status = game_status_new;
9376 // reset game status after font use from last stored game status
9377 game_status = last_game_status;
9381 void ResetFontStatus(void)
9386 void SetLevelSetInfo(char *identifier, int level_nr)
9388 setString(&levelset.identifier, identifier);
9390 levelset.level_nr = level_nr;
9393 boolean CheckIfAllViewportsHaveChanged(void)
9395 // if game status has not changed, viewports have not changed either
9396 if (game_status == game_status_last)
9399 // check if all viewports have changed with current game status
9401 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9402 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9403 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9404 int new_real_sx = vp_playfield->x;
9405 int new_real_sy = vp_playfield->y;
9406 int new_full_sxsize = vp_playfield->width;
9407 int new_full_sysize = vp_playfield->height;
9408 int new_dx = vp_door_1->x;
9409 int new_dy = vp_door_1->y;
9410 int new_dxsize = vp_door_1->width;
9411 int new_dysize = vp_door_1->height;
9412 int new_vx = vp_door_2->x;
9413 int new_vy = vp_door_2->y;
9414 int new_vxsize = vp_door_2->width;
9415 int new_vysize = vp_door_2->height;
9417 boolean playfield_viewport_has_changed =
9418 (new_real_sx != REAL_SX ||
9419 new_real_sy != REAL_SY ||
9420 new_full_sxsize != FULL_SXSIZE ||
9421 new_full_sysize != FULL_SYSIZE);
9423 boolean door_1_viewport_has_changed =
9426 new_dxsize != DXSIZE ||
9427 new_dysize != DYSIZE);
9429 boolean door_2_viewport_has_changed =
9432 new_vxsize != VXSIZE ||
9433 new_vysize != VYSIZE ||
9434 game_status_last == GAME_MODE_EDITOR);
9436 return (playfield_viewport_has_changed &&
9437 door_1_viewport_has_changed &&
9438 door_2_viewport_has_changed);
9441 boolean CheckFadeAll(void)
9443 return (CheckIfGlobalBorderHasChanged() ||
9444 CheckIfAllViewportsHaveChanged());
9447 void ChangeViewportPropertiesIfNeeded(void)
9449 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9450 FALSE : setup.small_game_graphics);
9451 int gfx_game_mode = game_status;
9452 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9454 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9455 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9456 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9457 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9458 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9459 int new_win_xsize = vp_window->width;
9460 int new_win_ysize = vp_window->height;
9461 int border_left = vp_playfield->border_left;
9462 int border_right = vp_playfield->border_right;
9463 int border_top = vp_playfield->border_top;
9464 int border_bottom = vp_playfield->border_bottom;
9465 int new_sx = vp_playfield->x + border_left;
9466 int new_sy = vp_playfield->y + border_top;
9467 int new_sxsize = vp_playfield->width - border_left - border_right;
9468 int new_sysize = vp_playfield->height - border_top - border_bottom;
9469 int new_real_sx = vp_playfield->x;
9470 int new_real_sy = vp_playfield->y;
9471 int new_full_sxsize = vp_playfield->width;
9472 int new_full_sysize = vp_playfield->height;
9473 int new_dx = vp_door_1->x;
9474 int new_dy = vp_door_1->y;
9475 int new_dxsize = vp_door_1->width;
9476 int new_dysize = vp_door_1->height;
9477 int new_vx = vp_door_2->x;
9478 int new_vy = vp_door_2->y;
9479 int new_vxsize = vp_door_2->width;
9480 int new_vysize = vp_door_2->height;
9481 int new_ex = vp_door_3->x;
9482 int new_ey = vp_door_3->y;
9483 int new_exsize = vp_door_3->width;
9484 int new_eysize = vp_door_3->height;
9485 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9486 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9487 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9488 int new_scr_fieldx = new_sxsize / tilesize;
9489 int new_scr_fieldy = new_sysize / tilesize;
9490 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9491 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9492 boolean init_gfx_buffers = FALSE;
9493 boolean init_video_buffer = FALSE;
9494 boolean init_gadgets_and_anims = FALSE;
9495 boolean init_em_graphics = FALSE;
9497 if (new_win_xsize != WIN_XSIZE ||
9498 new_win_ysize != WIN_YSIZE)
9500 WIN_XSIZE = new_win_xsize;
9501 WIN_YSIZE = new_win_ysize;
9503 init_video_buffer = TRUE;
9504 init_gfx_buffers = TRUE;
9505 init_gadgets_and_anims = TRUE;
9507 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9510 if (new_scr_fieldx != SCR_FIELDX ||
9511 new_scr_fieldy != SCR_FIELDY)
9513 // this always toggles between MAIN and GAME when using small tile size
9515 SCR_FIELDX = new_scr_fieldx;
9516 SCR_FIELDY = new_scr_fieldy;
9518 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9529 new_sxsize != SXSIZE ||
9530 new_sysize != SYSIZE ||
9531 new_dxsize != DXSIZE ||
9532 new_dysize != DYSIZE ||
9533 new_vxsize != VXSIZE ||
9534 new_vysize != VYSIZE ||
9535 new_exsize != EXSIZE ||
9536 new_eysize != EYSIZE ||
9537 new_real_sx != REAL_SX ||
9538 new_real_sy != REAL_SY ||
9539 new_full_sxsize != FULL_SXSIZE ||
9540 new_full_sysize != FULL_SYSIZE ||
9541 new_tilesize_var != TILESIZE_VAR
9544 // ------------------------------------------------------------------------
9545 // determine next fading area for changed viewport definitions
9546 // ------------------------------------------------------------------------
9548 // start with current playfield area (default fading area)
9551 FADE_SXSIZE = FULL_SXSIZE;
9552 FADE_SYSIZE = FULL_SYSIZE;
9554 // add new playfield area if position or size has changed
9555 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9556 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9558 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9559 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9562 // add current and new door 1 area if position or size has changed
9563 if (new_dx != DX || new_dy != DY ||
9564 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9566 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9567 DX, DY, DXSIZE, DYSIZE);
9568 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9569 new_dx, new_dy, new_dxsize, new_dysize);
9572 // add current and new door 2 area if position or size has changed
9573 if (new_vx != VX || new_vy != VY ||
9574 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9576 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9577 VX, VY, VXSIZE, VYSIZE);
9578 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9579 new_vx, new_vy, new_vxsize, new_vysize);
9582 // ------------------------------------------------------------------------
9583 // handle changed tile size
9584 // ------------------------------------------------------------------------
9586 if (new_tilesize_var != TILESIZE_VAR)
9588 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9590 // changing tile size invalidates scroll values of engine snapshots
9591 FreeEngineSnapshotSingle();
9593 // changing tile size requires update of graphic mapping for EM engine
9594 init_em_graphics = TRUE;
9605 SXSIZE = new_sxsize;
9606 SYSIZE = new_sysize;
9607 DXSIZE = new_dxsize;
9608 DYSIZE = new_dysize;
9609 VXSIZE = new_vxsize;
9610 VYSIZE = new_vysize;
9611 EXSIZE = new_exsize;
9612 EYSIZE = new_eysize;
9613 REAL_SX = new_real_sx;
9614 REAL_SY = new_real_sy;
9615 FULL_SXSIZE = new_full_sxsize;
9616 FULL_SYSIZE = new_full_sysize;
9617 TILESIZE_VAR = new_tilesize_var;
9619 init_gfx_buffers = TRUE;
9620 init_gadgets_and_anims = TRUE;
9622 // printf("::: viewports: init_gfx_buffers\n");
9623 // printf("::: viewports: init_gadgets_and_anims\n");
9626 if (init_gfx_buffers)
9628 // printf("::: init_gfx_buffers\n");
9630 SCR_FIELDX = new_scr_fieldx_buffers;
9631 SCR_FIELDY = new_scr_fieldy_buffers;
9635 SCR_FIELDX = new_scr_fieldx;
9636 SCR_FIELDY = new_scr_fieldy;
9638 SetDrawDeactivationMask(REDRAW_NONE);
9639 SetDrawBackgroundMask(REDRAW_FIELD);
9642 if (init_video_buffer)
9644 // printf("::: init_video_buffer\n");
9646 FreeAllImageTextures(); // needs old renderer to free the textures
9648 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9649 InitImageTextures();
9652 if (init_gadgets_and_anims)
9654 // printf("::: init_gadgets_and_anims\n");
9657 InitGlobalAnimations();
9660 if (init_em_graphics)
9662 InitGraphicInfo_EM();