1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int correctLevelPosX_EM(int lx)
199 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
204 int correctLevelPosY_EM(int ly)
207 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
212 int getFieldbufferOffsetX_RND(int dir, int pos)
214 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
215 int dx = (dir & MV_HORIZONTAL ? pos : 0);
216 int dx_var = dx * TILESIZE_VAR / TILESIZE;
219 if (EVEN(SCR_FIELDX))
221 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
222 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
224 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
225 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
227 fx += (dx_var > 0 ? TILEX_VAR : 0);
234 if (full_lev_fieldx <= SCR_FIELDX)
236 if (EVEN(SCR_FIELDX))
237 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
239 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
245 int getFieldbufferOffsetY_RND(int dir, int pos)
247 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
248 int dy = (dir & MV_VERTICAL ? pos : 0);
249 int dy_var = dy * TILESIZE_VAR / TILESIZE;
252 if (EVEN(SCR_FIELDY))
254 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
255 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
257 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
258 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
260 fy += (dy_var > 0 ? TILEY_VAR : 0);
267 if (full_lev_fieldy <= SCR_FIELDY)
269 if (EVEN(SCR_FIELDY))
270 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
272 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
278 static int getLevelFromScreenX_RND(int sx)
280 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
283 int lx = LEVELX((px + dx) / TILESIZE_VAR);
288 static int getLevelFromScreenY_RND(int sy)
290 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
293 int ly = LEVELY((py + dy) / TILESIZE_VAR);
298 static int getLevelFromScreenX_EM(int sx)
300 int level_xsize = level.native_em_level->cav->width;
301 int full_xsize = level_xsize * TILESIZE_VAR;
303 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
305 int fx = getFieldbufferOffsetX_EM();
308 int lx = LEVELX((px + dx) / TILESIZE_VAR);
310 lx = correctLevelPosX_EM(lx);
315 static int getLevelFromScreenY_EM(int sy)
317 int level_ysize = level.native_em_level->cav->height;
318 int full_ysize = level_ysize * TILESIZE_VAR;
320 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
322 int fy = getFieldbufferOffsetY_EM();
325 int ly = LEVELY((py + dy) / TILESIZE_VAR);
327 ly = correctLevelPosY_EM(ly);
332 static int getLevelFromScreenX_SP(int sx)
334 int menBorder = setup.sp_show_border_elements;
335 int level_xsize = level.native_sp_level->width;
336 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
338 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
340 int fx = getFieldbufferOffsetX_SP();
343 int lx = LEVELX((px + dx) / TILESIZE_VAR);
348 static int getLevelFromScreenY_SP(int sy)
350 int menBorder = setup.sp_show_border_elements;
351 int level_ysize = level.native_sp_level->height;
352 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
354 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
356 int fy = getFieldbufferOffsetY_SP();
359 int ly = LEVELY((py + dy) / TILESIZE_VAR);
364 static int getLevelFromScreenX_MM(int sx)
366 int level_xsize = level.native_mm_level->fieldx;
367 int full_xsize = level_xsize * TILESIZE_VAR;
369 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
372 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
377 static int getLevelFromScreenY_MM(int sy)
379 int level_ysize = level.native_mm_level->fieldy;
380 int full_ysize = level_ysize * TILESIZE_VAR;
382 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
385 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
390 int getLevelFromScreenX(int x)
392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
393 return getLevelFromScreenX_EM(x);
394 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
395 return getLevelFromScreenX_SP(x);
396 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
397 return getLevelFromScreenX_MM(x);
399 return getLevelFromScreenX_RND(x);
402 int getLevelFromScreenY(int y)
404 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
405 return getLevelFromScreenY_EM(y);
406 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
407 return getLevelFromScreenY_SP(y);
408 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
409 return getLevelFromScreenY_MM(y);
411 return getLevelFromScreenY_RND(y);
414 void DumpTile(int x, int y)
420 printf_line("-", 79);
421 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
422 printf_line("-", 79);
424 if (!IN_LEV_FIELD(x, y))
426 printf("(not in level field)\n");
432 token_name = element_info[Feld[x][y]].token_name;
434 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
435 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
436 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
437 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
438 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
439 printf(" MovPos: %d\n", MovPos[x][y]);
440 printf(" MovDir: %d\n", MovDir[x][y]);
441 printf(" MovDelay: %d\n", MovDelay[x][y]);
442 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
443 printf(" CustomValue: %d\n", CustomValue[x][y]);
444 printf(" GfxElement: %d\n", GfxElement[x][y]);
445 printf(" GfxAction: %d\n", GfxAction[x][y]);
446 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
447 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
451 void DumpTileFromScreen(int sx, int sy)
453 int lx = getLevelFromScreenX(sx);
454 int ly = getLevelFromScreenY(sy);
459 void SetDrawtoField(int mode)
461 if (mode == DRAW_TO_FIELDBUFFER)
467 BX2 = SCR_FIELDX + 1;
468 BY2 = SCR_FIELDY + 1;
470 drawto_field = fieldbuffer;
472 else // DRAW_TO_BACKBUFFER
478 BX2 = SCR_FIELDX - 1;
479 BY2 = SCR_FIELDY - 1;
481 drawto_field = backbuffer;
485 static void RedrawPlayfield_RND(void)
487 if (game.envelope_active)
490 DrawLevel(REDRAW_ALL);
494 void RedrawPlayfield(void)
496 if (game_status != GAME_MODE_PLAYING)
499 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
500 RedrawPlayfield_EM(TRUE);
501 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
502 RedrawPlayfield_SP(TRUE);
503 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
504 RedrawPlayfield_MM();
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
506 RedrawPlayfield_RND();
508 BlitScreenToBitmap(backbuffer);
510 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
514 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
517 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
518 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
520 if (x == -1 && y == -1)
523 if (draw_target == DRAW_TO_SCREEN)
524 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
526 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
529 static void DrawMaskedBorderExt_FIELD(int draw_target)
531 if (global.border_status >= GAME_MODE_MAIN &&
532 global.border_status <= GAME_MODE_PLAYING &&
533 border.draw_masked[global.border_status])
534 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
538 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
540 // when drawing to backbuffer, never draw border over open doors
541 if (draw_target == DRAW_TO_BACKBUFFER &&
542 (GetDoorState() & DOOR_OPEN_1))
545 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
546 (global.border_status != GAME_MODE_EDITOR ||
547 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
548 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
551 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
553 // when drawing to backbuffer, never draw border over open doors
554 if (draw_target == DRAW_TO_BACKBUFFER &&
555 (GetDoorState() & DOOR_OPEN_2))
558 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
559 global.border_status != GAME_MODE_EDITOR)
560 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
563 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
565 // currently not available
568 static void DrawMaskedBorderExt_ALL(int draw_target)
570 DrawMaskedBorderExt_FIELD(draw_target);
571 DrawMaskedBorderExt_DOOR_1(draw_target);
572 DrawMaskedBorderExt_DOOR_2(draw_target);
573 DrawMaskedBorderExt_DOOR_3(draw_target);
576 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
578 // never draw masked screen borders on borderless screens
579 if (global.border_status == GAME_MODE_LOADING ||
580 global.border_status == GAME_MODE_TITLE)
583 if (redraw_mask & REDRAW_ALL)
584 DrawMaskedBorderExt_ALL(draw_target);
587 if (redraw_mask & REDRAW_FIELD)
588 DrawMaskedBorderExt_FIELD(draw_target);
589 if (redraw_mask & REDRAW_DOOR_1)
590 DrawMaskedBorderExt_DOOR_1(draw_target);
591 if (redraw_mask & REDRAW_DOOR_2)
592 DrawMaskedBorderExt_DOOR_2(draw_target);
593 if (redraw_mask & REDRAW_DOOR_3)
594 DrawMaskedBorderExt_DOOR_3(draw_target);
598 void DrawMaskedBorder_FIELD(void)
600 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorder(int redraw_mask)
605 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
608 void DrawMaskedBorderToTarget(int draw_target)
610 if (draw_target == DRAW_TO_BACKBUFFER ||
611 draw_target == DRAW_TO_SCREEN)
613 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
617 int last_border_status = global.border_status;
619 if (draw_target == DRAW_TO_FADE_SOURCE)
621 global.border_status = gfx.fade_border_source_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
624 else if (draw_target == DRAW_TO_FADE_TARGET)
626 global.border_status = gfx.fade_border_target_status;
627 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
630 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
632 global.border_status = last_border_status;
633 gfx.masked_border_bitmap_ptr = backbuffer;
637 void DrawTileCursor(int draw_target)
643 int graphic = IMG_GLOBAL_TILE_CURSOR;
645 int tilesize = TILESIZE_VAR;
646 int width = tilesize;
647 int height = tilesize;
649 if (game_status != GAME_MODE_PLAYING)
652 if (!tile_cursor.enabled ||
656 if (tile_cursor.moving)
658 int step = TILESIZE_VAR / 4;
659 int dx = tile_cursor.target_x - tile_cursor.x;
660 int dy = tile_cursor.target_y - tile_cursor.y;
663 tile_cursor.x = tile_cursor.target_x;
665 tile_cursor.x += SIGN(dx) * step;
668 tile_cursor.y = tile_cursor.target_y;
670 tile_cursor.y += SIGN(dy) * step;
672 if (tile_cursor.x == tile_cursor.target_x &&
673 tile_cursor.y == tile_cursor.target_y)
674 tile_cursor.moving = FALSE;
677 dst_x = tile_cursor.x;
678 dst_y = tile_cursor.y;
680 frame = getGraphicAnimationFrame(graphic, -1);
682 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
685 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
686 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
688 if (draw_target == DRAW_TO_SCREEN)
689 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
691 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
695 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
702 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
703 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
705 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
708 void BlitScreenToBitmap(Bitmap *target_bitmap)
710 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
711 BlitScreenToBitmap_EM(target_bitmap);
712 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
713 BlitScreenToBitmap_SP(target_bitmap);
714 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
715 BlitScreenToBitmap_MM(target_bitmap);
716 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
717 BlitScreenToBitmap_RND(target_bitmap);
719 redraw_mask |= REDRAW_FIELD;
722 static void DrawFramesPerSecond(void)
725 int font_nr = FONT_TEXT_2;
726 int font_width = getFontWidth(font_nr);
727 int draw_deactivation_mask = GetDrawDeactivationMask();
728 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
730 // draw FPS with leading space (needed if field buffer deactivated)
731 sprintf(text, " %04.1f fps", global.frames_per_second);
733 // override draw deactivation mask (required for invisible warp mode)
734 SetDrawDeactivationMask(REDRAW_NONE);
736 // draw opaque FPS if field buffer deactivated, else draw masked FPS
737 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
738 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
740 // set draw deactivation mask to previous value
741 SetDrawDeactivationMask(draw_deactivation_mask);
743 // force full-screen redraw in this frame
744 redraw_mask = REDRAW_ALL;
748 static void PrintFrameTimeDebugging(void)
750 static unsigned int last_counter = 0;
751 unsigned int counter = Counter();
752 int diff_1 = counter - last_counter;
753 int diff_2 = diff_1 - GAME_FRAME_DELAY;
755 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
756 char diff_bar[2 * diff_2_max + 5];
760 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
762 for (i = 0; i < diff_2_max; i++)
763 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
764 i >= diff_2_max - diff_2_cut ? '-' : ' ');
766 diff_bar[pos++] = '|';
768 for (i = 0; i < diff_2_max; i++)
769 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
771 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
773 diff_bar[pos++] = '\0';
775 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
778 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
781 last_counter = counter;
785 static int unifiedRedrawMask(int mask)
787 if (mask & REDRAW_ALL)
790 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
796 static boolean equalRedrawMasks(int mask_1, int mask_2)
798 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
801 void BackToFront(void)
803 static int last_redraw_mask = REDRAW_NONE;
805 // force screen redraw in every frame to continue drawing global animations
806 // (but always use the last redraw mask to prevent unwanted side effects)
807 if (redraw_mask == REDRAW_NONE)
808 redraw_mask = last_redraw_mask;
810 last_redraw_mask = redraw_mask;
813 // masked border now drawn immediately when blitting backbuffer to window
815 // draw masked border to all viewports, if defined
816 DrawMaskedBorder(redraw_mask);
819 // draw frames per second (only if debug mode is enabled)
820 if (redraw_mask & REDRAW_FPS)
821 DrawFramesPerSecond();
823 // remove playfield redraw before potentially merging with doors redraw
824 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
825 redraw_mask &= ~REDRAW_FIELD;
827 // redraw complete window if both playfield and (some) doors need redraw
828 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
829 redraw_mask = REDRAW_ALL;
831 /* although redrawing the whole window would be fine for normal gameplay,
832 being able to only redraw the playfield is required for deactivating
833 certain drawing areas (mainly playfield) to work, which is needed for
834 warp-forward to be fast enough (by skipping redraw of most frames) */
836 if (redraw_mask & REDRAW_ALL)
838 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
840 else if (redraw_mask & REDRAW_FIELD)
842 BlitBitmap(backbuffer, window,
843 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
845 else if (redraw_mask & REDRAW_DOORS)
847 // merge door areas to prevent calling screen redraw more than once
853 if (redraw_mask & REDRAW_DOOR_1)
857 x2 = MAX(x2, DX + DXSIZE);
858 y2 = MAX(y2, DY + DYSIZE);
861 if (redraw_mask & REDRAW_DOOR_2)
865 x2 = MAX(x2, VX + VXSIZE);
866 y2 = MAX(y2, VY + VYSIZE);
869 if (redraw_mask & REDRAW_DOOR_3)
873 x2 = MAX(x2, EX + EXSIZE);
874 y2 = MAX(y2, EY + EYSIZE);
877 // make sure that at least one pixel is blitted, and inside the screen
878 // (else nothing is blitted, causing the animations not to be updated)
879 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
880 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
881 x2 = MIN(MAX(1, x2), WIN_XSIZE);
882 y2 = MIN(MAX(1, y2), WIN_YSIZE);
884 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
887 redraw_mask = REDRAW_NONE;
890 PrintFrameTimeDebugging();
894 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
896 unsigned int frame_delay_value_old = GetVideoFrameDelay();
898 SetVideoFrameDelay(frame_delay_value);
902 SetVideoFrameDelay(frame_delay_value_old);
905 static int fade_type_skip = FADE_TYPE_NONE;
907 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
909 void (*draw_border_function)(void) = NULL;
910 int x, y, width, height;
911 int fade_delay, post_delay;
913 if (fade_type == FADE_TYPE_FADE_OUT)
915 if (fade_type_skip != FADE_TYPE_NONE)
917 // skip all fade operations until specified fade operation
918 if (fade_type & fade_type_skip)
919 fade_type_skip = FADE_TYPE_NONE;
924 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
928 redraw_mask |= fade_mask;
930 if (fade_type == FADE_TYPE_SKIP)
932 fade_type_skip = fade_mode;
937 fade_delay = fading.fade_delay;
938 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
940 if (fade_type_skip != FADE_TYPE_NONE)
942 // skip all fade operations until specified fade operation
943 if (fade_type & fade_type_skip)
944 fade_type_skip = FADE_TYPE_NONE;
949 if (global.autoplay_leveldir)
954 if (fade_mask == REDRAW_FIELD)
959 height = FADE_SYSIZE;
961 if (border.draw_masked_when_fading)
962 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
964 DrawMaskedBorder_FIELD(); // draw once
974 // when switching screens without fading, set fade delay to zero
975 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
978 // do not display black frame when fading out without fade delay
979 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
982 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
983 draw_border_function);
985 redraw_mask &= ~fade_mask;
987 ClearAutoRepeatKeyEvents();
990 static void SetScreenStates_BeforeFadingIn(void)
992 // temporarily set screen mode for animations to screen after fading in
993 global.anim_status = global.anim_status_next;
995 // store backbuffer with all animations that will be started after fading in
996 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
997 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
999 // set screen mode for animations back to fading
1000 global.anim_status = GAME_MODE_PSEUDO_FADING;
1003 static void SetScreenStates_AfterFadingIn(void)
1005 // store new source screen (to use correct masked border for fading)
1006 gfx.fade_border_source_status = global.border_status;
1008 global.anim_status = global.anim_status_next;
1011 static void SetScreenStates_BeforeFadingOut(void)
1013 // store new target screen (to use correct masked border for fading)
1014 gfx.fade_border_target_status = game_status;
1016 // set screen mode for animations to fading
1017 global.anim_status = GAME_MODE_PSEUDO_FADING;
1019 // store backbuffer with all animations that will be stopped for fading out
1020 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1021 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1024 static void SetScreenStates_AfterFadingOut(void)
1026 global.border_status = game_status;
1029 void FadeIn(int fade_mask)
1031 SetScreenStates_BeforeFadingIn();
1034 DrawMaskedBorder(REDRAW_ALL);
1037 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1038 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1040 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1044 FADE_SXSIZE = FULL_SXSIZE;
1045 FADE_SYSIZE = FULL_SYSIZE;
1047 // activate virtual buttons depending on upcoming game status
1048 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1049 game_status == GAME_MODE_PLAYING && !tape.playing)
1050 SetOverlayActive(TRUE);
1052 SetScreenStates_AfterFadingIn();
1054 // force update of global animation status in case of rapid screen changes
1055 redraw_mask = REDRAW_ALL;
1059 void FadeOut(int fade_mask)
1061 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1062 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1063 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1066 SetScreenStates_BeforeFadingOut();
1068 SetTileCursorActive(FALSE);
1069 SetOverlayActive(FALSE);
1072 DrawMaskedBorder(REDRAW_ALL);
1075 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1076 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1078 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1080 SetScreenStates_AfterFadingOut();
1083 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1085 static struct TitleFadingInfo fading_leave_stored;
1088 fading_leave_stored = fading_leave;
1090 fading = fading_leave_stored;
1093 void FadeSetEnterMenu(void)
1095 fading = menu.enter_menu;
1097 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1100 void FadeSetLeaveMenu(void)
1102 fading = menu.leave_menu;
1104 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1107 void FadeSetEnterScreen(void)
1109 fading = menu.enter_screen[game_status];
1111 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1114 void FadeSetNextScreen(void)
1116 fading = menu.next_screen[game_status];
1118 // (do not overwrite fade mode set by FadeSetEnterScreen)
1119 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1122 void FadeSetLeaveScreen(void)
1124 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1127 void FadeSetFromType(int type)
1129 if (type & TYPE_ENTER_SCREEN)
1130 FadeSetEnterScreen();
1131 else if (type & TYPE_ENTER)
1133 else if (type & TYPE_LEAVE)
1137 void FadeSetDisabled(void)
1139 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1141 fading = fading_none;
1144 void FadeSkipNextFadeIn(void)
1146 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1149 void FadeSkipNextFadeOut(void)
1151 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1154 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1156 if (graphic == IMG_UNDEFINED)
1159 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1161 return (graphic_info[graphic].bitmap != NULL || redefined ?
1162 graphic_info[graphic].bitmap :
1163 graphic_info[default_graphic].bitmap);
1166 static Bitmap *getBackgroundBitmap(int graphic)
1168 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1171 static Bitmap *getGlobalBorderBitmap(int graphic)
1173 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1176 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1179 (status == GAME_MODE_MAIN ||
1180 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1181 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1182 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1183 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1186 return getGlobalBorderBitmap(graphic);
1189 void SetWindowBackgroundImageIfDefined(int graphic)
1191 if (graphic_info[graphic].bitmap)
1192 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1195 void SetMainBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1201 void SetDoorBackgroundImageIfDefined(int graphic)
1203 if (graphic_info[graphic].bitmap)
1204 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1207 void SetWindowBackgroundImage(int graphic)
1209 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1212 void SetMainBackgroundImage(int graphic)
1214 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1217 void SetDoorBackgroundImage(int graphic)
1219 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1222 void SetPanelBackground(void)
1224 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1226 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1227 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1229 SetDoorBackgroundBitmap(bitmap_db_panel);
1232 void DrawBackground(int x, int y, int width, int height)
1234 // "drawto" might still point to playfield buffer here (hall of fame)
1235 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1237 if (IN_GFX_FIELD_FULL(x, y))
1238 redraw_mask |= REDRAW_FIELD;
1239 else if (IN_GFX_DOOR_1(x, y))
1240 redraw_mask |= REDRAW_DOOR_1;
1241 else if (IN_GFX_DOOR_2(x, y))
1242 redraw_mask |= REDRAW_DOOR_2;
1243 else if (IN_GFX_DOOR_3(x, y))
1244 redraw_mask |= REDRAW_DOOR_3;
1247 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1249 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1251 if (font->bitmap == NULL)
1254 DrawBackground(x, y, width, height);
1257 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1259 struct GraphicInfo *g = &graphic_info[graphic];
1261 if (g->bitmap == NULL)
1264 DrawBackground(x, y, width, height);
1267 static int game_status_last = -1;
1268 static Bitmap *global_border_bitmap_last = NULL;
1269 static Bitmap *global_border_bitmap = NULL;
1270 static int real_sx_last = -1, real_sy_last = -1;
1271 static int full_sxsize_last = -1, full_sysize_last = -1;
1272 static int dx_last = -1, dy_last = -1;
1273 static int dxsize_last = -1, dysize_last = -1;
1274 static int vx_last = -1, vy_last = -1;
1275 static int vxsize_last = -1, vysize_last = -1;
1276 static int ex_last = -1, ey_last = -1;
1277 static int exsize_last = -1, eysize_last = -1;
1279 boolean CheckIfGlobalBorderHasChanged(void)
1281 // if game status has not changed, global border has not changed either
1282 if (game_status == game_status_last)
1285 // determine and store new global border bitmap for current game status
1286 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1288 return (global_border_bitmap_last != global_border_bitmap);
1291 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1293 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1294 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1296 // if game status has not changed, nothing has to be redrawn
1297 if (game_status == game_status_last)
1300 // redraw if last screen was title screen
1301 if (game_status_last == GAME_MODE_TITLE)
1304 // redraw if global screen border has changed
1305 if (CheckIfGlobalBorderHasChanged())
1308 // redraw if position or size of playfield area has changed
1309 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1310 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1313 // redraw if position or size of door area has changed
1314 if (dx_last != DX || dy_last != DY ||
1315 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1318 // redraw if position or size of tape area has changed
1319 if (vx_last != VX || vy_last != VY ||
1320 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1323 // redraw if position or size of editor area has changed
1324 if (ex_last != EX || ey_last != EY ||
1325 exsize_last != EXSIZE || eysize_last != EYSIZE)
1332 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1335 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1337 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1340 void RedrawGlobalBorder(void)
1342 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1344 RedrawGlobalBorderFromBitmap(bitmap);
1346 redraw_mask = REDRAW_ALL;
1349 static void RedrawGlobalBorderIfNeeded(void)
1351 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1352 if (game_status == game_status_last)
1356 // copy current draw buffer to later copy back areas that have not changed
1357 if (game_status_last != GAME_MODE_TITLE)
1358 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1360 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1361 if (CheckIfGlobalBorderRedrawIsNeeded())
1364 // redraw global screen border (or clear, if defined to be empty)
1365 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1367 if (game_status == GAME_MODE_EDITOR)
1368 DrawSpecialEditorDoor();
1370 // copy previous playfield and door areas, if they are defined on both
1371 // previous and current screen and if they still have the same size
1373 if (real_sx_last != -1 && real_sy_last != -1 &&
1374 REAL_SX != -1 && REAL_SY != -1 &&
1375 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1376 BlitBitmap(bitmap_db_store_1, backbuffer,
1377 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1380 if (dx_last != -1 && dy_last != -1 &&
1381 DX != -1 && DY != -1 &&
1382 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1386 if (game_status != GAME_MODE_EDITOR)
1388 if (vx_last != -1 && vy_last != -1 &&
1389 VX != -1 && VY != -1 &&
1390 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1396 if (ex_last != -1 && ey_last != -1 &&
1397 EX != -1 && EY != -1 &&
1398 exsize_last == EXSIZE && eysize_last == EYSIZE)
1399 BlitBitmap(bitmap_db_store_1, backbuffer,
1400 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1403 redraw_mask = REDRAW_ALL;
1406 game_status_last = game_status;
1408 global_border_bitmap_last = global_border_bitmap;
1410 real_sx_last = REAL_SX;
1411 real_sy_last = REAL_SY;
1412 full_sxsize_last = FULL_SXSIZE;
1413 full_sysize_last = FULL_SYSIZE;
1416 dxsize_last = DXSIZE;
1417 dysize_last = DYSIZE;
1420 vxsize_last = VXSIZE;
1421 vysize_last = VYSIZE;
1424 exsize_last = EXSIZE;
1425 eysize_last = EYSIZE;
1428 void ClearField(void)
1430 RedrawGlobalBorderIfNeeded();
1432 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1433 // (when entering hall of fame after playing)
1434 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1436 // !!! maybe this should be done before clearing the background !!!
1437 if (game_status == GAME_MODE_PLAYING)
1439 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1440 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1444 SetDrawtoField(DRAW_TO_BACKBUFFER);
1448 void MarkTileDirty(int x, int y)
1450 redraw_mask |= REDRAW_FIELD;
1453 void SetBorderElement(void)
1457 BorderElement = EL_EMPTY;
1459 // only the R'n'D game engine may use an additional steelwall border
1460 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1463 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1465 for (x = 0; x < lev_fieldx; x++)
1467 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1468 BorderElement = EL_STEELWALL;
1470 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1476 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1477 int max_array_fieldx, int max_array_fieldy,
1478 short field[max_array_fieldx][max_array_fieldy],
1479 int max_fieldx, int max_fieldy)
1483 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1484 static int safety = 0;
1486 // check if starting field still has the desired content
1487 if (field[from_x][from_y] == fill_element)
1492 if (safety > max_fieldx * max_fieldy)
1493 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1495 old_element = field[from_x][from_y];
1496 field[from_x][from_y] = fill_element;
1498 for (i = 0; i < 4; i++)
1500 x = from_x + check[i][0];
1501 y = from_y + check[i][1];
1503 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1504 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1505 field, max_fieldx, max_fieldy);
1511 void FloodFillLevel(int from_x, int from_y, int fill_element,
1512 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1513 int max_fieldx, int max_fieldy)
1515 FloodFillLevelExt(from_x, from_y, fill_element,
1516 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1517 max_fieldx, max_fieldy);
1520 void SetRandomAnimationValue(int x, int y)
1522 gfx.anim_random_frame = GfxRandom[x][y];
1525 int getGraphicAnimationFrame(int graphic, int sync_frame)
1527 // animation synchronized with global frame counter, not move position
1528 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1529 sync_frame = FrameCounter;
1531 return getAnimationFrame(graphic_info[graphic].anim_frames,
1532 graphic_info[graphic].anim_delay,
1533 graphic_info[graphic].anim_mode,
1534 graphic_info[graphic].anim_start_frame,
1538 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1540 struct GraphicInfo *g = &graphic_info[graphic];
1541 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1543 if (tilesize == gfx.standard_tile_size)
1544 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1545 else if (tilesize == game.tile_size)
1546 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1548 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1551 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1552 boolean get_backside)
1554 struct GraphicInfo *g = &graphic_info[graphic];
1555 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1556 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1558 if (g->offset_y == 0) // frames are ordered horizontally
1560 int max_width = g->anim_frames_per_line * g->width;
1561 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1563 *x = pos % max_width;
1564 *y = src_y % g->height + pos / max_width * g->height;
1566 else if (g->offset_x == 0) // frames are ordered vertically
1568 int max_height = g->anim_frames_per_line * g->height;
1569 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1571 *x = src_x % g->width + pos / max_height * g->width;
1572 *y = pos % max_height;
1574 else // frames are ordered diagonally
1576 *x = src_x + frame * g->offset_x;
1577 *y = src_y + frame * g->offset_y;
1581 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1582 Bitmap **bitmap, int *x, int *y,
1583 boolean get_backside)
1585 struct GraphicInfo *g = &graphic_info[graphic];
1587 // if no graphics defined at all, use fallback graphics
1588 if (g->bitmaps == NULL)
1589 *g = graphic_info[IMG_CHAR_EXCLAM];
1591 // if no in-game graphics defined, always use standard graphic size
1592 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1593 tilesize = TILESIZE;
1595 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1596 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1598 *x = *x * tilesize / g->tile_size;
1599 *y = *y * tilesize / g->tile_size;
1602 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1603 Bitmap **bitmap, int *x, int *y)
1605 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1608 void getFixedGraphicSource(int graphic, int frame,
1609 Bitmap **bitmap, int *x, int *y)
1611 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1614 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1616 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1619 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1620 int *x, int *y, boolean get_backside)
1622 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1626 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1628 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1631 void DrawGraphic(int x, int y, int graphic, int frame)
1634 if (!IN_SCR_FIELD(x, y))
1636 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1637 printf("DrawGraphic(): This should never happen!\n");
1642 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1645 MarkTileDirty(x, y);
1648 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1651 if (!IN_SCR_FIELD(x, y))
1653 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1654 printf("DrawGraphic(): This should never happen!\n");
1659 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1661 MarkTileDirty(x, y);
1664 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1670 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1672 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1675 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1681 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1682 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1685 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1688 if (!IN_SCR_FIELD(x, y))
1690 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1691 printf("DrawGraphicThruMask(): This should never happen!\n");
1696 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1699 MarkTileDirty(x, y);
1702 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1705 if (!IN_SCR_FIELD(x, y))
1707 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1708 printf("DrawGraphicThruMask(): This should never happen!\n");
1713 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1715 MarkTileDirty(x, y);
1718 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1724 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1726 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1730 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1731 int graphic, int frame)
1736 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1738 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1742 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1744 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1746 MarkTileDirty(x / tilesize, y / tilesize);
1749 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1752 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1753 graphic, frame, tilesize);
1754 MarkTileDirty(x / tilesize, y / tilesize);
1757 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1763 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1764 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1767 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1768 int frame, int tilesize)
1773 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1774 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1777 void DrawMiniGraphic(int x, int y, int graphic)
1779 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1780 MarkTileDirty(x / 2, y / 2);
1783 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1788 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1789 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1792 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1793 int graphic, int frame,
1794 int cut_mode, int mask_mode)
1799 int width = TILEX, height = TILEY;
1802 if (dx || dy) // shifted graphic
1804 if (x < BX1) // object enters playfield from the left
1811 else if (x > BX2) // object enters playfield from the right
1817 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1823 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1825 else if (dx) // general horizontal movement
1826 MarkTileDirty(x + SIGN(dx), y);
1828 if (y < BY1) // object enters playfield from the top
1830 if (cut_mode == CUT_BELOW) // object completely above top border
1838 else if (y > BY2) // object enters playfield from the bottom
1844 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1850 else if (dy > 0 && cut_mode == CUT_ABOVE)
1852 if (y == BY2) // object completely above bottom border
1858 MarkTileDirty(x, y + 1);
1859 } // object leaves playfield to the bottom
1860 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1862 else if (dy) // general vertical movement
1863 MarkTileDirty(x, y + SIGN(dy));
1867 if (!IN_SCR_FIELD(x, y))
1869 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1870 printf("DrawGraphicShifted(): This should never happen!\n");
1875 width = width * TILESIZE_VAR / TILESIZE;
1876 height = height * TILESIZE_VAR / TILESIZE;
1877 cx = cx * TILESIZE_VAR / TILESIZE;
1878 cy = cy * TILESIZE_VAR / TILESIZE;
1879 dx = dx * TILESIZE_VAR / TILESIZE;
1880 dy = dy * TILESIZE_VAR / TILESIZE;
1882 if (width > 0 && height > 0)
1884 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1889 dst_x = FX + x * TILEX_VAR + dx;
1890 dst_y = FY + y * TILEY_VAR + dy;
1892 if (mask_mode == USE_MASKING)
1893 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1896 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1899 MarkTileDirty(x, y);
1903 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1904 int graphic, int frame,
1905 int cut_mode, int mask_mode)
1910 int width = TILEX_VAR, height = TILEY_VAR;
1913 int x2 = x + SIGN(dx);
1914 int y2 = y + SIGN(dy);
1916 // movement with two-tile animations must be sync'ed with movement position,
1917 // not with current GfxFrame (which can be higher when using slow movement)
1918 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1919 int anim_frames = graphic_info[graphic].anim_frames;
1921 // (we also need anim_delay here for movement animations with less frames)
1922 int anim_delay = graphic_info[graphic].anim_delay;
1923 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1925 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1926 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1928 // re-calculate animation frame for two-tile movement animation
1929 frame = getGraphicAnimationFrame(graphic, sync_frame);
1931 // check if movement start graphic inside screen area and should be drawn
1932 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1934 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1936 dst_x = FX + x1 * TILEX_VAR;
1937 dst_y = FY + y1 * TILEY_VAR;
1939 if (mask_mode == USE_MASKING)
1940 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1943 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1946 MarkTileDirty(x1, y1);
1949 // check if movement end graphic inside screen area and should be drawn
1950 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1952 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1954 dst_x = FX + x2 * TILEX_VAR;
1955 dst_y = FY + y2 * TILEY_VAR;
1957 if (mask_mode == USE_MASKING)
1958 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1961 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1964 MarkTileDirty(x2, y2);
1968 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1969 int graphic, int frame,
1970 int cut_mode, int mask_mode)
1974 DrawGraphic(x, y, graphic, frame);
1979 if (graphic_info[graphic].double_movement) // EM style movement images
1980 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1982 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1985 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1986 int graphic, int frame, int cut_mode)
1988 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1991 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1992 int cut_mode, int mask_mode)
1994 int lx = LEVELX(x), ly = LEVELY(y);
1998 if (IN_LEV_FIELD(lx, ly))
2000 SetRandomAnimationValue(lx, ly);
2002 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2003 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2005 // do not use double (EM style) movement graphic when not moving
2006 if (graphic_info[graphic].double_movement && !dx && !dy)
2008 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2009 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2012 else // border element
2014 graphic = el2img(element);
2015 frame = getGraphicAnimationFrame(graphic, -1);
2018 if (element == EL_EXPANDABLE_WALL)
2020 boolean left_stopped = FALSE, right_stopped = FALSE;
2022 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2023 left_stopped = TRUE;
2024 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2025 right_stopped = TRUE;
2027 if (left_stopped && right_stopped)
2029 else if (left_stopped)
2031 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2032 frame = graphic_info[graphic].anim_frames - 1;
2034 else if (right_stopped)
2036 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2037 frame = graphic_info[graphic].anim_frames - 1;
2042 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2043 else if (mask_mode == USE_MASKING)
2044 DrawGraphicThruMask(x, y, graphic, frame);
2046 DrawGraphic(x, y, graphic, frame);
2049 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2050 int cut_mode, int mask_mode)
2052 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2053 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2054 cut_mode, mask_mode);
2057 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2060 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2063 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2066 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2069 void DrawLevelElementThruMask(int x, int y, int element)
2071 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2074 void DrawLevelFieldThruMask(int x, int y)
2076 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2079 // !!! implementation of quicksand is totally broken !!!
2080 #define IS_CRUMBLED_TILE(x, y, e) \
2081 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2082 !IS_MOVING(x, y) || \
2083 (e) == EL_QUICKSAND_EMPTYING || \
2084 (e) == EL_QUICKSAND_FAST_EMPTYING))
2086 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2091 int width, height, cx, cy;
2092 int sx = SCREENX(x), sy = SCREENY(y);
2093 int crumbled_border_size = graphic_info[graphic].border_size;
2094 int crumbled_tile_size = graphic_info[graphic].tile_size;
2095 int crumbled_border_size_var =
2096 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2099 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2101 for (i = 1; i < 4; i++)
2103 int dxx = (i & 1 ? dx : 0);
2104 int dyy = (i & 2 ? dy : 0);
2107 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2110 // check if neighbour field is of same crumble type
2111 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2112 graphic_info[graphic].class ==
2113 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2115 // return if check prevents inner corner
2116 if (same == (dxx == dx && dyy == dy))
2120 // if we reach this point, we have an inner corner
2122 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2124 width = crumbled_border_size_var;
2125 height = crumbled_border_size_var;
2126 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2127 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2129 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2130 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2133 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2138 int width, height, bx, by, cx, cy;
2139 int sx = SCREENX(x), sy = SCREENY(y);
2140 int crumbled_border_size = graphic_info[graphic].border_size;
2141 int crumbled_tile_size = graphic_info[graphic].tile_size;
2142 int crumbled_border_size_var =
2143 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2144 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2147 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2149 // draw simple, sloppy, non-corner-accurate crumbled border
2151 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2152 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2153 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2154 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2156 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2157 FX + sx * TILEX_VAR + cx,
2158 FY + sy * TILEY_VAR + cy);
2160 // (remaining middle border part must be at least as big as corner part)
2161 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2162 crumbled_border_size_var >= TILESIZE_VAR / 3)
2165 // correct corners of crumbled border, if needed
2167 for (i = -1; i <= 1; i += 2)
2169 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2170 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2171 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2174 // check if neighbour field is of same crumble type
2175 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2176 graphic_info[graphic].class ==
2177 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2179 // no crumbled corner, but continued crumbled border
2181 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2182 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2183 int b1 = (i == 1 ? crumbled_border_size_var :
2184 TILESIZE_VAR - 2 * crumbled_border_size_var);
2186 width = crumbled_border_size_var;
2187 height = crumbled_border_size_var;
2189 if (dir == 1 || dir == 2)
2204 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2206 FX + sx * TILEX_VAR + cx,
2207 FY + sy * TILEY_VAR + cy);
2212 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2214 int sx = SCREENX(x), sy = SCREENY(y);
2217 static int xy[4][2] =
2225 if (!IN_LEV_FIELD(x, y))
2228 element = TILE_GFX_ELEMENT(x, y);
2230 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2232 if (!IN_SCR_FIELD(sx, sy))
2235 // crumble field borders towards direct neighbour fields
2236 for (i = 0; i < 4; i++)
2238 int xx = x + xy[i][0];
2239 int yy = y + xy[i][1];
2241 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2244 // check if neighbour field is of same crumble type
2245 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2246 graphic_info[graphic].class ==
2247 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2250 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2253 // crumble inner field corners towards corner neighbour fields
2254 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2255 graphic_info[graphic].anim_frames == 2)
2257 for (i = 0; i < 4; i++)
2259 int dx = (i & 1 ? +1 : -1);
2260 int dy = (i & 2 ? +1 : -1);
2262 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2266 MarkTileDirty(sx, sy);
2268 else // center field is not crumbled -- crumble neighbour fields
2270 // crumble field borders of direct neighbour fields
2271 for (i = 0; i < 4; i++)
2273 int xx = x + xy[i][0];
2274 int yy = y + xy[i][1];
2275 int sxx = sx + xy[i][0];
2276 int syy = sy + xy[i][1];
2278 if (!IN_LEV_FIELD(xx, yy) ||
2279 !IN_SCR_FIELD(sxx, syy))
2282 // do not crumble fields that are being digged or snapped
2283 if (Feld[xx][yy] == EL_EMPTY ||
2284 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2287 element = TILE_GFX_ELEMENT(xx, yy);
2289 if (!IS_CRUMBLED_TILE(xx, yy, element))
2292 graphic = el_act2crm(element, ACTION_DEFAULT);
2294 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2296 MarkTileDirty(sxx, syy);
2299 // crumble inner field corners of corner neighbour fields
2300 for (i = 0; i < 4; i++)
2302 int dx = (i & 1 ? +1 : -1);
2303 int dy = (i & 2 ? +1 : -1);
2309 if (!IN_LEV_FIELD(xx, yy) ||
2310 !IN_SCR_FIELD(sxx, syy))
2313 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2316 element = TILE_GFX_ELEMENT(xx, yy);
2318 if (!IS_CRUMBLED_TILE(xx, yy, element))
2321 graphic = el_act2crm(element, ACTION_DEFAULT);
2323 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2324 graphic_info[graphic].anim_frames == 2)
2325 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2327 MarkTileDirty(sxx, syy);
2332 void DrawLevelFieldCrumbled(int x, int y)
2336 if (!IN_LEV_FIELD(x, y))
2339 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2340 GfxElement[x][y] != EL_UNDEFINED &&
2341 GFX_CRUMBLED(GfxElement[x][y]))
2343 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2348 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2350 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2353 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2356 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2357 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2358 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2359 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2360 int sx = SCREENX(x), sy = SCREENY(y);
2362 DrawGraphic(sx, sy, graphic1, frame1);
2363 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2366 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2368 int sx = SCREENX(x), sy = SCREENY(y);
2369 static int xy[4][2] =
2378 // crumble direct neighbour fields (required for field borders)
2379 for (i = 0; i < 4; i++)
2381 int xx = x + xy[i][0];
2382 int yy = y + xy[i][1];
2383 int sxx = sx + xy[i][0];
2384 int syy = sy + xy[i][1];
2386 if (!IN_LEV_FIELD(xx, yy) ||
2387 !IN_SCR_FIELD(sxx, syy) ||
2388 !GFX_CRUMBLED(Feld[xx][yy]) ||
2392 DrawLevelField(xx, yy);
2395 // crumble corner neighbour fields (required for inner field corners)
2396 for (i = 0; i < 4; i++)
2398 int dx = (i & 1 ? +1 : -1);
2399 int dy = (i & 2 ? +1 : -1);
2405 if (!IN_LEV_FIELD(xx, yy) ||
2406 !IN_SCR_FIELD(sxx, syy) ||
2407 !GFX_CRUMBLED(Feld[xx][yy]) ||
2411 int element = TILE_GFX_ELEMENT(xx, yy);
2412 int graphic = el_act2crm(element, ACTION_DEFAULT);
2414 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2415 graphic_info[graphic].anim_frames == 2)
2416 DrawLevelField(xx, yy);
2420 static int getBorderElement(int x, int y)
2424 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2425 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2426 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2427 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2428 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2429 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2430 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2432 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2433 int steel_position = (x == -1 && y == -1 ? 0 :
2434 x == lev_fieldx && y == -1 ? 1 :
2435 x == -1 && y == lev_fieldy ? 2 :
2436 x == lev_fieldx && y == lev_fieldy ? 3 :
2437 x == -1 || x == lev_fieldx ? 4 :
2438 y == -1 || y == lev_fieldy ? 5 : 6);
2440 return border[steel_position][steel_type];
2443 void DrawScreenElement(int x, int y, int element)
2445 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2446 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2449 void DrawLevelElement(int x, int y, int element)
2451 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2452 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2455 void DrawScreenField(int x, int y)
2457 int lx = LEVELX(x), ly = LEVELY(y);
2458 int element, content;
2460 if (!IN_LEV_FIELD(lx, ly))
2462 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2465 element = getBorderElement(lx, ly);
2467 DrawScreenElement(x, y, element);
2472 element = Feld[lx][ly];
2473 content = Store[lx][ly];
2475 if (IS_MOVING(lx, ly))
2477 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2478 boolean cut_mode = NO_CUTTING;
2480 if (element == EL_QUICKSAND_EMPTYING ||
2481 element == EL_QUICKSAND_FAST_EMPTYING ||
2482 element == EL_MAGIC_WALL_EMPTYING ||
2483 element == EL_BD_MAGIC_WALL_EMPTYING ||
2484 element == EL_DC_MAGIC_WALL_EMPTYING ||
2485 element == EL_AMOEBA_DROPPING)
2486 cut_mode = CUT_ABOVE;
2487 else if (element == EL_QUICKSAND_FILLING ||
2488 element == EL_QUICKSAND_FAST_FILLING ||
2489 element == EL_MAGIC_WALL_FILLING ||
2490 element == EL_BD_MAGIC_WALL_FILLING ||
2491 element == EL_DC_MAGIC_WALL_FILLING)
2492 cut_mode = CUT_BELOW;
2494 if (cut_mode == CUT_ABOVE)
2495 DrawScreenElement(x, y, element);
2497 DrawScreenElement(x, y, EL_EMPTY);
2500 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2501 else if (cut_mode == NO_CUTTING)
2502 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2505 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2507 if (cut_mode == CUT_BELOW &&
2508 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2509 DrawLevelElement(lx, ly + 1, element);
2512 if (content == EL_ACID)
2514 int dir = MovDir[lx][ly];
2515 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2516 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2518 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2520 // prevent target field from being drawn again (but without masking)
2521 // (this would happen if target field is scanned after moving element)
2522 Stop[newlx][newly] = TRUE;
2525 else if (IS_BLOCKED(lx, ly))
2530 boolean cut_mode = NO_CUTTING;
2531 int element_old, content_old;
2533 Blocked2Moving(lx, ly, &oldx, &oldy);
2536 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2537 MovDir[oldx][oldy] == MV_RIGHT);
2539 element_old = Feld[oldx][oldy];
2540 content_old = Store[oldx][oldy];
2542 if (element_old == EL_QUICKSAND_EMPTYING ||
2543 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2544 element_old == EL_MAGIC_WALL_EMPTYING ||
2545 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2546 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2547 element_old == EL_AMOEBA_DROPPING)
2548 cut_mode = CUT_ABOVE;
2550 DrawScreenElement(x, y, EL_EMPTY);
2553 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2555 else if (cut_mode == NO_CUTTING)
2556 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2559 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2562 else if (IS_DRAWABLE(element))
2563 DrawScreenElement(x, y, element);
2565 DrawScreenElement(x, y, EL_EMPTY);
2568 void DrawLevelField(int x, int y)
2570 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2571 DrawScreenField(SCREENX(x), SCREENY(y));
2572 else if (IS_MOVING(x, y))
2576 Moving2Blocked(x, y, &newx, &newy);
2577 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2578 DrawScreenField(SCREENX(newx), SCREENY(newy));
2580 else if (IS_BLOCKED(x, y))
2584 Blocked2Moving(x, y, &oldx, &oldy);
2585 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2586 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2590 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2591 int (*el2img_function)(int), boolean masked,
2592 int element_bits_draw)
2594 int element_base = map_mm_wall_element(element);
2595 int element_bits = (IS_DF_WALL(element) ?
2596 element - EL_DF_WALL_START :
2597 IS_MM_WALL(element) ?
2598 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2599 int graphic = el2img_function(element_base);
2600 int tilesize_draw = tilesize / 2;
2605 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2607 for (i = 0; i < 4; i++)
2609 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2610 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2612 if (!(element_bits_draw & (1 << i)))
2615 if (element_bits & (1 << i))
2618 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2619 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2621 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2622 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2627 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2628 tilesize_draw, tilesize_draw);
2633 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2634 boolean masked, int element_bits_draw)
2636 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2637 element, tilesize, el2edimg, masked, element_bits_draw);
2640 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2641 int (*el2img_function)(int))
2643 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2647 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2650 if (IS_MM_WALL(element))
2652 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2653 element, tilesize, el2edimg, masked, 0x000f);
2657 int graphic = el2edimg(element);
2660 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2662 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2666 void DrawSizedElement(int x, int y, int element, int tilesize)
2668 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2671 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2673 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2676 void DrawMiniElement(int x, int y, int element)
2680 graphic = el2edimg(element);
2681 DrawMiniGraphic(x, y, graphic);
2684 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2687 int x = sx + scroll_x, y = sy + scroll_y;
2689 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2690 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2691 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2692 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2694 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2697 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2699 int x = sx + scroll_x, y = sy + scroll_y;
2701 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2702 DrawMiniElement(sx, sy, EL_EMPTY);
2703 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2704 DrawMiniElement(sx, sy, Feld[x][y]);
2706 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2709 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2710 int x, int y, int xsize, int ysize,
2711 int tile_width, int tile_height)
2715 int dst_x = startx + x * tile_width;
2716 int dst_y = starty + y * tile_height;
2717 int width = graphic_info[graphic].width;
2718 int height = graphic_info[graphic].height;
2719 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2720 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2721 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2722 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2723 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2724 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2725 boolean draw_masked = graphic_info[graphic].draw_masked;
2727 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2729 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2731 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2735 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2736 inner_sx + (x - 1) * tile_width % inner_width);
2737 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2738 inner_sy + (y - 1) * tile_height % inner_height);
2741 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2744 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2748 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2749 int x, int y, int xsize, int ysize,
2752 int font_width = getFontWidth(font_nr);
2753 int font_height = getFontHeight(font_nr);
2755 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2756 font_width, font_height);
2759 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2761 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2762 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2763 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2764 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2765 boolean no_delay = (tape.warp_forward);
2766 unsigned int anim_delay = 0;
2767 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2768 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2769 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2770 int font_width = getFontWidth(font_nr);
2771 int font_height = getFontHeight(font_nr);
2772 int max_xsize = level.envelope[envelope_nr].xsize;
2773 int max_ysize = level.envelope[envelope_nr].ysize;
2774 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2775 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2776 int xend = max_xsize;
2777 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2778 int xstep = (xstart < xend ? 1 : 0);
2779 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2781 int end = MAX(xend - xstart, yend - ystart);
2784 for (i = start; i <= end; i++)
2786 int last_frame = end; // last frame of this "for" loop
2787 int x = xstart + i * xstep;
2788 int y = ystart + i * ystep;
2789 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2790 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2791 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2792 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2795 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2797 BlitScreenToBitmap(backbuffer);
2799 SetDrawtoField(DRAW_TO_BACKBUFFER);
2801 for (yy = 0; yy < ysize; yy++)
2802 for (xx = 0; xx < xsize; xx++)
2803 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2805 DrawTextBuffer(sx + font_width, sy + font_height,
2806 level.envelope[envelope_nr].text, font_nr, max_xsize,
2807 xsize - 2, ysize - 2, 0, mask_mode,
2808 level.envelope[envelope_nr].autowrap,
2809 level.envelope[envelope_nr].centered, FALSE);
2811 redraw_mask |= REDRAW_FIELD;
2814 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2817 ClearAutoRepeatKeyEvents();
2820 void ShowEnvelope(int envelope_nr)
2822 int element = EL_ENVELOPE_1 + envelope_nr;
2823 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2824 int sound_opening = element_info[element].sound[ACTION_OPENING];
2825 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2826 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2827 boolean no_delay = (tape.warp_forward);
2828 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2829 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2830 int anim_mode = graphic_info[graphic].anim_mode;
2831 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2832 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2834 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2836 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2838 if (anim_mode == ANIM_DEFAULT)
2839 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2841 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2844 Delay_WithScreenUpdates(wait_delay_value);
2846 WaitForEventToContinue();
2848 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2850 if (anim_mode != ANIM_NONE)
2851 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2853 if (anim_mode == ANIM_DEFAULT)
2854 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2856 game.envelope_active = FALSE;
2858 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2860 redraw_mask |= REDRAW_FIELD;
2864 static void setRequestBasePosition(int *x, int *y)
2866 int sx_base, sy_base;
2868 if (request.x != -1)
2869 sx_base = request.x;
2870 else if (request.align == ALIGN_LEFT)
2872 else if (request.align == ALIGN_RIGHT)
2873 sx_base = SX + SXSIZE;
2875 sx_base = SX + SXSIZE / 2;
2877 if (request.y != -1)
2878 sy_base = request.y;
2879 else if (request.valign == VALIGN_TOP)
2881 else if (request.valign == VALIGN_BOTTOM)
2882 sy_base = SY + SYSIZE;
2884 sy_base = SY + SYSIZE / 2;
2890 static void setRequestPositionExt(int *x, int *y, int width, int height,
2891 boolean add_border_size)
2893 int border_size = request.border_size;
2894 int sx_base, sy_base;
2897 setRequestBasePosition(&sx_base, &sy_base);
2899 if (request.align == ALIGN_LEFT)
2901 else if (request.align == ALIGN_RIGHT)
2902 sx = sx_base - width;
2904 sx = sx_base - width / 2;
2906 if (request.valign == VALIGN_TOP)
2908 else if (request.valign == VALIGN_BOTTOM)
2909 sy = sy_base - height;
2911 sy = sy_base - height / 2;
2913 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2914 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2916 if (add_border_size)
2926 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2928 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2931 static void DrawEnvelopeRequest(char *text)
2933 char *text_final = text;
2934 char *text_door_style = NULL;
2935 int graphic = IMG_BACKGROUND_REQUEST;
2936 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2937 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2938 int font_nr = FONT_REQUEST;
2939 int font_width = getFontWidth(font_nr);
2940 int font_height = getFontHeight(font_nr);
2941 int border_size = request.border_size;
2942 int line_spacing = request.line_spacing;
2943 int line_height = font_height + line_spacing;
2944 int max_text_width = request.width - 2 * border_size;
2945 int max_text_height = request.height - 2 * border_size;
2946 int line_length = max_text_width / font_width;
2947 int max_lines = max_text_height / line_height;
2948 int text_width = line_length * font_width;
2949 int width = request.width;
2950 int height = request.height;
2951 int tile_size = MAX(request.step_offset, 1);
2952 int x_steps = width / tile_size;
2953 int y_steps = height / tile_size;
2954 int sx_offset = border_size;
2955 int sy_offset = border_size;
2959 if (request.centered)
2960 sx_offset = (request.width - text_width) / 2;
2962 if (request.wrap_single_words && !request.autowrap)
2964 char *src_text_ptr, *dst_text_ptr;
2966 text_door_style = checked_malloc(2 * strlen(text) + 1);
2968 src_text_ptr = text;
2969 dst_text_ptr = text_door_style;
2971 while (*src_text_ptr)
2973 if (*src_text_ptr == ' ' ||
2974 *src_text_ptr == '?' ||
2975 *src_text_ptr == '!')
2976 *dst_text_ptr++ = '\n';
2978 if (*src_text_ptr != ' ')
2979 *dst_text_ptr++ = *src_text_ptr;
2984 *dst_text_ptr = '\0';
2986 text_final = text_door_style;
2989 setRequestPosition(&sx, &sy, FALSE);
2991 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2993 for (y = 0; y < y_steps; y++)
2994 for (x = 0; x < x_steps; x++)
2995 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2996 x, y, x_steps, y_steps,
2997 tile_size, tile_size);
2999 // force DOOR font inside door area
3000 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3002 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3003 line_length, -1, max_lines, line_spacing, mask_mode,
3004 request.autowrap, request.centered, FALSE);
3008 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3009 RedrawGadget(tool_gadget[i]);
3011 // store readily prepared envelope request for later use when animating
3012 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3014 if (text_door_style)
3015 free(text_door_style);
3018 static void AnimateEnvelopeRequest(int anim_mode, int action)
3020 int graphic = IMG_BACKGROUND_REQUEST;
3021 boolean draw_masked = graphic_info[graphic].draw_masked;
3022 int delay_value_normal = request.step_delay;
3023 int delay_value_fast = delay_value_normal / 2;
3024 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3025 boolean no_delay = (tape.warp_forward);
3026 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3027 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3028 unsigned int anim_delay = 0;
3030 int tile_size = MAX(request.step_offset, 1);
3031 int max_xsize = request.width / tile_size;
3032 int max_ysize = request.height / tile_size;
3033 int max_xsize_inner = max_xsize - 2;
3034 int max_ysize_inner = max_ysize - 2;
3036 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3037 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3038 int xend = max_xsize_inner;
3039 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3040 int xstep = (xstart < xend ? 1 : 0);
3041 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3043 int end = MAX(xend - xstart, yend - ystart);
3046 if (setup.quick_doors)
3053 for (i = start; i <= end; i++)
3055 int last_frame = end; // last frame of this "for" loop
3056 int x = xstart + i * xstep;
3057 int y = ystart + i * ystep;
3058 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3059 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3060 int xsize_size_left = (xsize - 1) * tile_size;
3061 int ysize_size_top = (ysize - 1) * tile_size;
3062 int max_xsize_pos = (max_xsize - 1) * tile_size;
3063 int max_ysize_pos = (max_ysize - 1) * tile_size;
3064 int width = xsize * tile_size;
3065 int height = ysize * tile_size;
3070 setRequestPosition(&src_x, &src_y, FALSE);
3071 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3073 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3075 for (yy = 0; yy < 2; yy++)
3077 for (xx = 0; xx < 2; xx++)
3079 int src_xx = src_x + xx * max_xsize_pos;
3080 int src_yy = src_y + yy * max_ysize_pos;
3081 int dst_xx = dst_x + xx * xsize_size_left;
3082 int dst_yy = dst_y + yy * ysize_size_top;
3083 int xx_size = (xx ? tile_size : xsize_size_left);
3084 int yy_size = (yy ? tile_size : ysize_size_top);
3087 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3088 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3090 BlitBitmap(bitmap_db_store_2, backbuffer,
3091 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3095 redraw_mask |= REDRAW_FIELD;
3099 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3102 ClearAutoRepeatKeyEvents();
3105 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3107 int graphic = IMG_BACKGROUND_REQUEST;
3108 int sound_opening = SND_REQUEST_OPENING;
3109 int sound_closing = SND_REQUEST_CLOSING;
3110 int anim_mode_1 = request.anim_mode; // (higher priority)
3111 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3112 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3113 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3114 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3116 if (game_status == GAME_MODE_PLAYING)
3117 BlitScreenToBitmap(backbuffer);
3119 SetDrawtoField(DRAW_TO_BACKBUFFER);
3121 // SetDrawBackgroundMask(REDRAW_NONE);
3123 if (action == ACTION_OPENING)
3125 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3127 if (req_state & REQ_ASK)
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3132 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3134 else if (req_state & REQ_CONFIRM)
3136 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3139 else if (req_state & REQ_PLAYER)
3141 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3142 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3143 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3144 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3147 DrawEnvelopeRequest(text);
3150 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3152 if (action == ACTION_OPENING)
3154 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3156 if (anim_mode == ANIM_DEFAULT)
3157 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3159 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3163 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3165 if (anim_mode != ANIM_NONE)
3166 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3168 if (anim_mode == ANIM_DEFAULT)
3169 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3172 game.envelope_active = FALSE;
3174 if (action == ACTION_CLOSING)
3175 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3177 // SetDrawBackgroundMask(last_draw_background_mask);
3179 redraw_mask |= REDRAW_FIELD;
3183 if (action == ACTION_CLOSING &&
3184 game_status == GAME_MODE_PLAYING &&
3185 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3186 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3189 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3191 if (IS_MM_WALL(element))
3193 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3199 int graphic = el2preimg(element);
3201 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3202 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3207 void DrawLevel(int draw_background_mask)
3211 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3212 SetDrawBackgroundMask(draw_background_mask);
3216 for (x = BX1; x <= BX2; x++)
3217 for (y = BY1; y <= BY2; y++)
3218 DrawScreenField(x, y);
3220 redraw_mask |= REDRAW_FIELD;
3223 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3228 for (x = 0; x < size_x; x++)
3229 for (y = 0; y < size_y; y++)
3230 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3232 redraw_mask |= REDRAW_FIELD;
3235 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3239 for (x = 0; x < size_x; x++)
3240 for (y = 0; y < size_y; y++)
3241 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3243 redraw_mask |= REDRAW_FIELD;
3246 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3248 boolean show_level_border = (BorderElement != EL_EMPTY);
3249 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3250 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3251 int tile_size = preview.tile_size;
3252 int preview_width = preview.xsize * tile_size;
3253 int preview_height = preview.ysize * tile_size;
3254 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3255 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3256 int real_preview_width = real_preview_xsize * tile_size;
3257 int real_preview_height = real_preview_ysize * tile_size;
3258 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3259 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3262 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3265 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3267 dst_x += (preview_width - real_preview_width) / 2;
3268 dst_y += (preview_height - real_preview_height) / 2;
3270 for (x = 0; x < real_preview_xsize; x++)
3272 for (y = 0; y < real_preview_ysize; y++)
3274 int lx = from_x + x + (show_level_border ? -1 : 0);
3275 int ly = from_y + y + (show_level_border ? -1 : 0);
3276 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3277 getBorderElement(lx, ly));
3279 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3280 element, tile_size);
3284 redraw_mask |= REDRAW_FIELD;
3287 #define MICROLABEL_EMPTY 0
3288 #define MICROLABEL_LEVEL_NAME 1
3289 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3290 #define MICROLABEL_LEVEL_AUTHOR 3
3291 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3292 #define MICROLABEL_IMPORTED_FROM 5
3293 #define MICROLABEL_IMPORTED_BY_HEAD 6
3294 #define MICROLABEL_IMPORTED_BY 7
3296 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3298 int max_text_width = SXSIZE;
3299 int font_width = getFontWidth(font_nr);
3301 if (pos->align == ALIGN_CENTER)
3302 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3303 else if (pos->align == ALIGN_RIGHT)
3304 max_text_width = pos->x;
3306 max_text_width = SXSIZE - pos->x;
3308 return max_text_width / font_width;
3311 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3313 char label_text[MAX_OUTPUT_LINESIZE + 1];
3314 int max_len_label_text;
3315 int font_nr = pos->font;
3318 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3321 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3322 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3323 mode == MICROLABEL_IMPORTED_BY_HEAD)
3324 font_nr = pos->font_alt;
3326 max_len_label_text = getMaxTextLength(pos, font_nr);
3328 if (pos->size != -1)
3329 max_len_label_text = pos->size;
3331 for (i = 0; i < max_len_label_text; i++)
3332 label_text[i] = ' ';
3333 label_text[max_len_label_text] = '\0';
3335 if (strlen(label_text) > 0)
3336 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3339 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3340 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3341 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3342 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3343 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3344 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3345 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3346 max_len_label_text);
3347 label_text[max_len_label_text] = '\0';
3349 if (strlen(label_text) > 0)
3350 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3352 redraw_mask |= REDRAW_FIELD;
3355 static void DrawPreviewLevelLabel(int mode)
3357 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3360 static void DrawPreviewLevelInfo(int mode)
3362 if (mode == MICROLABEL_LEVEL_NAME)
3363 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3364 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3365 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3368 static void DrawPreviewLevelExt(boolean restart)
3370 static unsigned int scroll_delay = 0;
3371 static unsigned int label_delay = 0;
3372 static int from_x, from_y, scroll_direction;
3373 static int label_state, label_counter;
3374 unsigned int scroll_delay_value = preview.step_delay;
3375 boolean show_level_border = (BorderElement != EL_EMPTY);
3376 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3377 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3384 if (preview.anim_mode == ANIM_CENTERED)
3386 if (level_xsize > preview.xsize)
3387 from_x = (level_xsize - preview.xsize) / 2;
3388 if (level_ysize > preview.ysize)
3389 from_y = (level_ysize - preview.ysize) / 2;
3392 from_x += preview.xoffset;
3393 from_y += preview.yoffset;
3395 scroll_direction = MV_RIGHT;
3399 DrawPreviewLevelPlayfield(from_x, from_y);
3400 DrawPreviewLevelLabel(label_state);
3402 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3403 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3405 // initialize delay counters
3406 DelayReached(&scroll_delay, 0);
3407 DelayReached(&label_delay, 0);
3409 if (leveldir_current->name)
3411 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3412 char label_text[MAX_OUTPUT_LINESIZE + 1];
3413 int font_nr = pos->font;
3414 int max_len_label_text = getMaxTextLength(pos, font_nr);
3416 if (pos->size != -1)
3417 max_len_label_text = pos->size;
3419 strncpy(label_text, leveldir_current->name, max_len_label_text);
3420 label_text[max_len_label_text] = '\0';
3422 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3423 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3429 // scroll preview level, if needed
3430 if (preview.anim_mode != ANIM_NONE &&
3431 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3432 DelayReached(&scroll_delay, scroll_delay_value))
3434 switch (scroll_direction)
3439 from_x -= preview.step_offset;
3440 from_x = (from_x < 0 ? 0 : from_x);
3443 scroll_direction = MV_UP;
3447 if (from_x < level_xsize - preview.xsize)
3449 from_x += preview.step_offset;
3450 from_x = (from_x > level_xsize - preview.xsize ?
3451 level_xsize - preview.xsize : from_x);
3454 scroll_direction = MV_DOWN;
3460 from_y -= preview.step_offset;
3461 from_y = (from_y < 0 ? 0 : from_y);
3464 scroll_direction = MV_RIGHT;
3468 if (from_y < level_ysize - preview.ysize)
3470 from_y += preview.step_offset;
3471 from_y = (from_y > level_ysize - preview.ysize ?
3472 level_ysize - preview.ysize : from_y);
3475 scroll_direction = MV_LEFT;
3482 DrawPreviewLevelPlayfield(from_x, from_y);
3485 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3486 // redraw micro level label, if needed
3487 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3488 !strEqual(level.author, ANONYMOUS_NAME) &&
3489 !strEqual(level.author, leveldir_current->name) &&
3490 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3492 int max_label_counter = 23;
3494 if (leveldir_current->imported_from != NULL &&
3495 strlen(leveldir_current->imported_from) > 0)
3496 max_label_counter += 14;
3497 if (leveldir_current->imported_by != NULL &&
3498 strlen(leveldir_current->imported_by) > 0)
3499 max_label_counter += 14;
3501 label_counter = (label_counter + 1) % max_label_counter;
3502 label_state = (label_counter >= 0 && label_counter <= 7 ?
3503 MICROLABEL_LEVEL_NAME :
3504 label_counter >= 9 && label_counter <= 12 ?
3505 MICROLABEL_LEVEL_AUTHOR_HEAD :
3506 label_counter >= 14 && label_counter <= 21 ?
3507 MICROLABEL_LEVEL_AUTHOR :
3508 label_counter >= 23 && label_counter <= 26 ?
3509 MICROLABEL_IMPORTED_FROM_HEAD :
3510 label_counter >= 28 && label_counter <= 35 ?
3511 MICROLABEL_IMPORTED_FROM :
3512 label_counter >= 37 && label_counter <= 40 ?
3513 MICROLABEL_IMPORTED_BY_HEAD :
3514 label_counter >= 42 && label_counter <= 49 ?
3515 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3517 if (leveldir_current->imported_from == NULL &&
3518 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3519 label_state == MICROLABEL_IMPORTED_FROM))
3520 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3521 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3523 DrawPreviewLevelLabel(label_state);
3527 void DrawPreviewPlayers(void)
3529 if (game_status != GAME_MODE_MAIN)
3532 // do not draw preview players if level preview redefined, but players aren't
3533 if (preview.redefined && !menu.main.preview_players.redefined)
3536 boolean player_found[MAX_PLAYERS];
3537 int num_players = 0;
3540 for (i = 0; i < MAX_PLAYERS; i++)
3541 player_found[i] = FALSE;
3543 // check which players can be found in the level (simple approach)
3544 for (x = 0; x < lev_fieldx; x++)
3546 for (y = 0; y < lev_fieldy; y++)
3548 int element = level.field[x][y];
3550 if (ELEM_IS_PLAYER(element))
3552 int player_nr = GET_PLAYER_NR(element);
3554 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3556 if (!player_found[player_nr])
3559 player_found[player_nr] = TRUE;
3564 struct TextPosInfo *pos = &menu.main.preview_players;
3565 int tile_size = pos->tile_size;
3566 int border_size = pos->border_size;
3567 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3568 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3569 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3570 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3571 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3572 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3573 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3574 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3575 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3576 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3577 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3578 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3580 // clear area in which the players will be drawn
3581 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3582 max_players_width, max_players_height);
3584 if (!network.enabled && !setup.team_mode)
3587 // only draw players if level is suited for team mode
3588 if (num_players < 2)
3591 // draw all players that were found in the level
3592 for (i = 0; i < MAX_PLAYERS; i++)
3594 if (player_found[i])
3596 int graphic = el2img(EL_PLAYER_1 + i);
3598 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3600 xpos += player_xoffset;
3601 ypos += player_yoffset;
3606 void DrawPreviewLevelInitial(void)
3608 DrawPreviewLevelExt(TRUE);
3609 DrawPreviewPlayers();
3612 void DrawPreviewLevelAnimation(void)
3614 DrawPreviewLevelExt(FALSE);
3617 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3618 int border_size, int font_nr)
3620 int graphic = el2img(EL_PLAYER_1 + player_nr);
3621 int font_height = getFontHeight(font_nr);
3622 int player_height = MAX(tile_size, font_height);
3623 int xoffset_text = tile_size + border_size;
3624 int yoffset_text = (player_height - font_height) / 2;
3625 int yoffset_graphic = (player_height - tile_size) / 2;
3626 char *player_name = getNetworkPlayerName(player_nr + 1);
3628 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3630 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3633 static void DrawNetworkPlayersExt(boolean force)
3635 if (game_status != GAME_MODE_MAIN)
3638 if (!network.connected && !force)
3641 // do not draw network players if level preview redefined, but players aren't
3642 if (preview.redefined && !menu.main.network_players.redefined)
3645 int num_players = 0;
3648 for (i = 0; i < MAX_PLAYERS; i++)
3649 if (stored_player[i].connected_network)
3652 struct TextPosInfo *pos = &menu.main.network_players;
3653 int tile_size = pos->tile_size;
3654 int border_size = pos->border_size;
3655 int xoffset_text = tile_size + border_size;
3656 int font_nr = pos->font;
3657 int font_width = getFontWidth(font_nr);
3658 int font_height = getFontHeight(font_nr);
3659 int player_height = MAX(tile_size, font_height);
3660 int player_yoffset = player_height + border_size;
3661 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3662 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3663 int all_players_height = num_players * player_yoffset - border_size;
3664 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3665 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3666 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3668 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3669 max_players_width, max_players_height);
3671 // first draw local network player ...
3672 for (i = 0; i < MAX_PLAYERS; i++)
3674 if (stored_player[i].connected_network &&
3675 stored_player[i].connected_locally)
3677 char *player_name = getNetworkPlayerName(i + 1);
3678 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3679 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3681 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3683 ypos += player_yoffset;
3687 // ... then draw all other network players
3688 for (i = 0; i < MAX_PLAYERS; i++)
3690 if (stored_player[i].connected_network &&
3691 !stored_player[i].connected_locally)
3693 char *player_name = getNetworkPlayerName(i + 1);
3694 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3695 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3697 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3699 ypos += player_yoffset;
3704 void DrawNetworkPlayers(void)
3706 DrawNetworkPlayersExt(FALSE);
3709 void ClearNetworkPlayers(void)
3711 DrawNetworkPlayersExt(TRUE);
3714 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3715 int graphic, int sync_frame,
3718 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3720 if (mask_mode == USE_MASKING)
3721 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3723 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3726 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3727 int graphic, int sync_frame, int mask_mode)
3729 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3731 if (mask_mode == USE_MASKING)
3732 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3734 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3737 static void DrawGraphicAnimation(int x, int y, int graphic)
3739 int lx = LEVELX(x), ly = LEVELY(y);
3741 if (!IN_SCR_FIELD(x, y))
3744 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3745 graphic, GfxFrame[lx][ly], NO_MASKING);
3747 MarkTileDirty(x, y);
3750 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3752 int lx = LEVELX(x), ly = LEVELY(y);
3754 if (!IN_SCR_FIELD(x, y))
3757 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3758 graphic, GfxFrame[lx][ly], NO_MASKING);
3759 MarkTileDirty(x, y);
3762 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3764 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3767 void DrawLevelElementAnimation(int x, int y, int element)
3769 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3771 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3774 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3776 int sx = SCREENX(x), sy = SCREENY(y);
3778 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3781 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3784 DrawGraphicAnimation(sx, sy, graphic);
3787 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3788 DrawLevelFieldCrumbled(x, y);
3790 if (GFX_CRUMBLED(Feld[x][y]))
3791 DrawLevelFieldCrumbled(x, y);
3795 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3797 int sx = SCREENX(x), sy = SCREENY(y);
3800 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3803 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3805 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3808 DrawGraphicAnimation(sx, sy, graphic);
3810 if (GFX_CRUMBLED(element))
3811 DrawLevelFieldCrumbled(x, y);
3814 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3816 if (player->use_murphy)
3818 // this works only because currently only one player can be "murphy" ...
3819 static int last_horizontal_dir = MV_LEFT;
3820 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3822 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3823 last_horizontal_dir = move_dir;
3825 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3827 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3829 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3835 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3838 static boolean equalGraphics(int graphic1, int graphic2)
3840 struct GraphicInfo *g1 = &graphic_info[graphic1];
3841 struct GraphicInfo *g2 = &graphic_info[graphic2];
3843 return (g1->bitmap == g2->bitmap &&
3844 g1->src_x == g2->src_x &&
3845 g1->src_y == g2->src_y &&
3846 g1->anim_frames == g2->anim_frames &&
3847 g1->anim_delay == g2->anim_delay &&
3848 g1->anim_mode == g2->anim_mode);
3851 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3855 DRAW_PLAYER_STAGE_INIT = 0,
3856 DRAW_PLAYER_STAGE_LAST_FIELD,
3857 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3858 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3859 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3860 DRAW_PLAYER_STAGE_PLAYER,
3862 DRAW_PLAYER_STAGE_PLAYER,
3863 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3865 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3866 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3868 NUM_DRAW_PLAYER_STAGES
3871 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3873 static int static_last_player_graphic[MAX_PLAYERS];
3874 static int static_last_player_frame[MAX_PLAYERS];
3875 static boolean static_player_is_opaque[MAX_PLAYERS];
3876 static boolean draw_player[MAX_PLAYERS];
3877 int pnr = player->index_nr;
3879 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3881 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3882 static_last_player_frame[pnr] = player->Frame;
3883 static_player_is_opaque[pnr] = FALSE;
3885 draw_player[pnr] = TRUE;
3888 if (!draw_player[pnr])
3892 if (!IN_LEV_FIELD(player->jx, player->jy))
3894 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3895 printf("DrawPlayerField(): This should never happen!\n");
3897 draw_player[pnr] = FALSE;
3903 int last_player_graphic = static_last_player_graphic[pnr];
3904 int last_player_frame = static_last_player_frame[pnr];
3905 boolean player_is_opaque = static_player_is_opaque[pnr];
3907 int jx = player->jx;
3908 int jy = player->jy;
3909 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3910 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3911 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3912 int last_jx = (player->is_moving ? jx - dx : jx);
3913 int last_jy = (player->is_moving ? jy - dy : jy);
3914 int next_jx = jx + dx;
3915 int next_jy = jy + dy;
3916 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3917 int sx = SCREENX(jx);
3918 int sy = SCREENY(jy);
3919 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3920 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3921 int element = Feld[jx][jy];
3922 int last_element = Feld[last_jx][last_jy];
3923 int action = (player->is_pushing ? ACTION_PUSHING :
3924 player->is_digging ? ACTION_DIGGING :
3925 player->is_collecting ? ACTION_COLLECTING :
3926 player->is_moving ? ACTION_MOVING :
3927 player->is_snapping ? ACTION_SNAPPING :
3928 player->is_dropping ? ACTION_DROPPING :
3929 player->is_waiting ? player->action_waiting :
3932 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3934 // ------------------------------------------------------------------------
3935 // initialize drawing the player
3936 // ------------------------------------------------------------------------
3938 draw_player[pnr] = FALSE;
3940 // GfxElement[][] is set to the element the player is digging or collecting;
3941 // remove also for off-screen player if the player is not moving anymore
3942 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3943 GfxElement[jx][jy] = EL_UNDEFINED;
3945 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3948 if (element == EL_EXPLOSION)
3951 InitPlayerGfxAnimation(player, action, move_dir);
3953 draw_player[pnr] = TRUE;
3955 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3957 // ------------------------------------------------------------------------
3958 // draw things in the field the player is leaving, if needed
3959 // ------------------------------------------------------------------------
3961 if (!IN_SCR_FIELD(sx, sy))
3962 draw_player[pnr] = FALSE;
3964 if (!player->is_moving)
3967 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3969 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3971 if (last_element == EL_DYNAMITE_ACTIVE ||
3972 last_element == EL_EM_DYNAMITE_ACTIVE ||
3973 last_element == EL_SP_DISK_RED_ACTIVE)
3974 DrawDynamite(last_jx, last_jy);
3976 DrawLevelFieldThruMask(last_jx, last_jy);
3978 else if (last_element == EL_DYNAMITE_ACTIVE ||
3979 last_element == EL_EM_DYNAMITE_ACTIVE ||
3980 last_element == EL_SP_DISK_RED_ACTIVE)
3981 DrawDynamite(last_jx, last_jy);
3983 DrawLevelField(last_jx, last_jy);
3985 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3986 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3988 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3990 // ------------------------------------------------------------------------
3991 // draw things behind the player, if needed
3992 // ------------------------------------------------------------------------
3996 DrawLevelElement(jx, jy, Back[jx][jy]);
4001 if (IS_ACTIVE_BOMB(element))
4003 DrawLevelElement(jx, jy, EL_EMPTY);
4008 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4010 int old_element = GfxElement[jx][jy];
4011 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4012 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4014 if (GFX_CRUMBLED(old_element))
4015 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4017 DrawGraphic(sx, sy, old_graphic, frame);
4019 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4020 static_player_is_opaque[pnr] = TRUE;
4024 GfxElement[jx][jy] = EL_UNDEFINED;
4026 // make sure that pushed elements are drawn with correct frame rate
4027 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4029 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4030 GfxFrame[jx][jy] = player->StepFrame;
4032 DrawLevelField(jx, jy);
4035 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4037 // ------------------------------------------------------------------------
4038 // draw things the player is pushing, if needed
4039 // ------------------------------------------------------------------------
4041 if (!player->is_pushing || !player->is_moving)
4044 int gfx_frame = GfxFrame[jx][jy];
4046 if (!IS_MOVING(jx, jy)) // push movement already finished
4048 element = Feld[next_jx][next_jy];
4049 gfx_frame = GfxFrame[next_jx][next_jy];
4052 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4053 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4054 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4056 // draw background element under pushed element (like the Sokoban field)
4057 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4059 // this allows transparent pushing animation over non-black background
4062 DrawLevelElement(jx, jy, Back[jx][jy]);
4064 DrawLevelElement(jx, jy, EL_EMPTY);
4066 if (Back[next_jx][next_jy])
4067 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4069 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4071 else if (Back[next_jx][next_jy])
4072 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4074 int px = SCREENX(jx), py = SCREENY(jy);
4075 int pxx = (TILEX - ABS(sxx)) * dx;
4076 int pyy = (TILEY - ABS(syy)) * dy;
4079 // do not draw (EM style) pushing animation when pushing is finished
4080 // (two-tile animations usually do not contain start and end frame)
4081 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4082 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4084 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4086 // masked drawing is needed for EMC style (double) movement graphics
4087 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4088 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4091 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4093 // ------------------------------------------------------------------------
4094 // draw player himself
4095 // ------------------------------------------------------------------------
4097 int graphic = getPlayerGraphic(player, move_dir);
4099 // in the case of changed player action or direction, prevent the current
4100 // animation frame from being restarted for identical animations
4101 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4102 player->Frame = last_player_frame;
4104 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4106 if (player_is_opaque)
4107 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4109 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4111 if (SHIELD_ON(player))
4113 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4114 IMG_SHIELD_NORMAL_ACTIVE);
4115 frame = getGraphicAnimationFrame(graphic, -1);
4117 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4120 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4122 // ------------------------------------------------------------------------
4123 // draw things in front of player (active dynamite or dynabombs)
4124 // ------------------------------------------------------------------------
4126 if (IS_ACTIVE_BOMB(element))
4128 int graphic = el2img(element);
4129 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4131 if (game.emulation == EMU_SUPAPLEX)
4132 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4134 DrawGraphicThruMask(sx, sy, graphic, frame);
4137 if (player_is_moving && last_element == EL_EXPLOSION)
4139 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4140 GfxElement[last_jx][last_jy] : EL_EMPTY);
4141 int graphic = el_act2img(element, ACTION_EXPLODING);
4142 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4143 int phase = ExplodePhase[last_jx][last_jy] - 1;
4144 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4147 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4150 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4152 // ------------------------------------------------------------------------
4153 // draw elements the player is just walking/passing through/under
4154 // ------------------------------------------------------------------------
4156 if (player_is_moving)
4158 // handle the field the player is leaving ...
4159 if (IS_ACCESSIBLE_INSIDE(last_element))
4160 DrawLevelField(last_jx, last_jy);
4161 else if (IS_ACCESSIBLE_UNDER(last_element))
4162 DrawLevelFieldThruMask(last_jx, last_jy);
4165 // do not redraw accessible elements if the player is just pushing them
4166 if (!player_is_moving || !player->is_pushing)
4168 // ... and the field the player is entering
4169 if (IS_ACCESSIBLE_INSIDE(element))
4170 DrawLevelField(jx, jy);
4171 else if (IS_ACCESSIBLE_UNDER(element))
4172 DrawLevelFieldThruMask(jx, jy);
4175 MarkTileDirty(sx, sy);
4179 void DrawPlayer(struct PlayerInfo *player)
4183 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4184 DrawPlayerExt(player, i);
4187 void DrawAllPlayers(void)
4191 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4192 for (j = 0; j < MAX_PLAYERS; j++)
4193 if (stored_player[j].active)
4194 DrawPlayerExt(&stored_player[j], i);
4197 void DrawPlayerField(int x, int y)
4199 if (!IS_PLAYER(x, y))
4202 DrawPlayer(PLAYERINFO(x, y));
4205 // ----------------------------------------------------------------------------
4207 void WaitForEventToContinue(void)
4209 boolean still_wait = TRUE;
4211 if (program.headless)
4214 // simulate releasing mouse button over last gadget, if still pressed
4216 HandleGadgets(-1, -1, 0);
4218 button_status = MB_RELEASED;
4226 if (NextValidEvent(&event))
4230 case EVENT_BUTTONRELEASE:
4231 case EVENT_KEYPRESS:
4232 case SDL_CONTROLLERBUTTONDOWN:
4233 case SDL_JOYBUTTONDOWN:
4237 case EVENT_KEYRELEASE:
4238 ClearPlayerAction();
4242 HandleOtherEvents(&event);
4246 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4255 #define MAX_REQUEST_LINES 13
4256 #define MAX_REQUEST_LINE_FONT1_LEN 7
4257 #define MAX_REQUEST_LINE_FONT2_LEN 10
4259 static int RequestHandleEvents(unsigned int req_state)
4261 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4263 int width = request.width;
4264 int height = request.height;
4268 // when showing request dialog after game ended, deactivate game panel
4269 if (game_just_ended)
4270 game.panel.active = FALSE;
4272 game.request_active = TRUE;
4274 setRequestPosition(&sx, &sy, FALSE);
4276 button_status = MB_RELEASED;
4278 request_gadget_id = -1;
4283 if (game_just_ended)
4285 // the MM game engine does not use a special (scrollable) field buffer
4286 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4287 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4289 HandleGameActions();
4291 SetDrawtoField(DRAW_TO_BACKBUFFER);
4293 if (global.use_envelope_request)
4295 // copy current state of request area to middle of playfield area
4296 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4304 while (NextValidEvent(&event))
4308 case EVENT_BUTTONPRESS:
4309 case EVENT_BUTTONRELEASE:
4310 case EVENT_MOTIONNOTIFY:
4314 if (event.type == EVENT_MOTIONNOTIFY)
4319 motion_status = TRUE;
4320 mx = ((MotionEvent *) &event)->x;
4321 my = ((MotionEvent *) &event)->y;
4325 motion_status = FALSE;
4326 mx = ((ButtonEvent *) &event)->x;
4327 my = ((ButtonEvent *) &event)->y;
4328 if (event.type == EVENT_BUTTONPRESS)
4329 button_status = ((ButtonEvent *) &event)->button;
4331 button_status = MB_RELEASED;
4334 // this sets 'request_gadget_id'
4335 HandleGadgets(mx, my, button_status);
4337 switch (request_gadget_id)
4339 case TOOL_CTRL_ID_YES:
4340 case TOOL_CTRL_ID_TOUCH_YES:
4343 case TOOL_CTRL_ID_NO:
4344 case TOOL_CTRL_ID_TOUCH_NO:
4347 case TOOL_CTRL_ID_CONFIRM:
4348 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4349 result = TRUE | FALSE;
4352 case TOOL_CTRL_ID_PLAYER_1:
4355 case TOOL_CTRL_ID_PLAYER_2:
4358 case TOOL_CTRL_ID_PLAYER_3:
4361 case TOOL_CTRL_ID_PLAYER_4:
4366 // only check clickable animations if no request gadget clicked
4367 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4374 case SDL_WINDOWEVENT:
4375 HandleWindowEvent((WindowEvent *) &event);
4378 case SDL_APP_WILLENTERBACKGROUND:
4379 case SDL_APP_DIDENTERBACKGROUND:
4380 case SDL_APP_WILLENTERFOREGROUND:
4381 case SDL_APP_DIDENTERFOREGROUND:
4382 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4385 case EVENT_KEYPRESS:
4387 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4392 if (req_state & REQ_CONFIRM)
4401 #if defined(KSYM_Rewind)
4402 case KSYM_Rewind: // for Amazon Fire TV remote
4411 #if defined(KSYM_FastForward)
4412 case KSYM_FastForward: // for Amazon Fire TV remote
4418 HandleKeysDebug(key, KEY_PRESSED);
4422 if (req_state & REQ_PLAYER)
4424 int old_player_nr = setup.network_player_nr;
4427 result = old_player_nr + 1;
4432 result = old_player_nr + 1;
4463 case EVENT_KEYRELEASE:
4464 ClearPlayerAction();
4467 case SDL_CONTROLLERBUTTONDOWN:
4468 switch (event.cbutton.button)
4470 case SDL_CONTROLLER_BUTTON_A:
4471 case SDL_CONTROLLER_BUTTON_X:
4472 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4473 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4477 case SDL_CONTROLLER_BUTTON_B:
4478 case SDL_CONTROLLER_BUTTON_Y:
4479 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4480 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4481 case SDL_CONTROLLER_BUTTON_BACK:
4486 if (req_state & REQ_PLAYER)
4488 int old_player_nr = setup.network_player_nr;
4491 result = old_player_nr + 1;
4493 switch (event.cbutton.button)
4495 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4496 case SDL_CONTROLLER_BUTTON_Y:
4500 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4501 case SDL_CONTROLLER_BUTTON_B:
4505 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4506 case SDL_CONTROLLER_BUTTON_A:
4510 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4511 case SDL_CONTROLLER_BUTTON_X:
4522 case SDL_CONTROLLERBUTTONUP:
4523 HandleJoystickEvent(&event);
4524 ClearPlayerAction();
4528 HandleOtherEvents(&event);
4533 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4535 int joy = AnyJoystick();
4537 if (joy & JOY_BUTTON_1)
4539 else if (joy & JOY_BUTTON_2)
4542 else if (AnyJoystick())
4544 int joy = AnyJoystick();
4546 if (req_state & REQ_PLAYER)
4550 else if (joy & JOY_RIGHT)
4552 else if (joy & JOY_DOWN)
4554 else if (joy & JOY_LEFT)
4559 if (game_just_ended)
4561 if (global.use_envelope_request)
4563 // copy back current state of pressed buttons inside request area
4564 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4571 game.request_active = FALSE;
4576 static boolean RequestDoor(char *text, unsigned int req_state)
4578 unsigned int old_door_state;
4579 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4580 int font_nr = FONT_TEXT_2;
4585 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4587 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4588 font_nr = FONT_TEXT_1;
4591 if (game_status == GAME_MODE_PLAYING)
4592 BlitScreenToBitmap(backbuffer);
4594 // disable deactivated drawing when quick-loading level tape recording
4595 if (tape.playing && tape.deactivate_display)
4596 TapeDeactivateDisplayOff(TRUE);
4598 SetMouseCursor(CURSOR_DEFAULT);
4600 // pause network game while waiting for request to answer
4601 if (network.enabled &&
4602 game_status == GAME_MODE_PLAYING &&
4603 !game.all_players_gone &&
4604 req_state & REQUEST_WAIT_FOR_INPUT)
4605 SendToServer_PausePlaying();
4607 old_door_state = GetDoorState();
4609 // simulate releasing mouse button over last gadget, if still pressed
4611 HandleGadgets(-1, -1, 0);
4615 // draw released gadget before proceeding
4618 if (old_door_state & DOOR_OPEN_1)
4620 CloseDoor(DOOR_CLOSE_1);
4622 // save old door content
4623 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4624 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4627 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4628 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4630 // clear door drawing field
4631 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4633 // force DOOR font inside door area
4634 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4636 // write text for request
4637 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4639 char text_line[max_request_line_len + 1];
4645 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4647 tc = *(text_ptr + tx);
4648 // if (!tc || tc == ' ')
4649 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4653 if ((tc == '?' || tc == '!') && tl == 0)
4663 strncpy(text_line, text_ptr, tl);
4666 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4667 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4668 text_line, font_nr);
4670 text_ptr += tl + (tc == ' ' ? 1 : 0);
4671 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4676 if (req_state & REQ_ASK)
4678 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4679 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4680 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4681 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4683 else if (req_state & REQ_CONFIRM)
4685 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4688 else if (req_state & REQ_PLAYER)
4690 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4691 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4692 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4693 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4696 // copy request gadgets to door backbuffer
4697 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4699 OpenDoor(DOOR_OPEN_1);
4701 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4703 if (game_status == GAME_MODE_PLAYING)
4705 SetPanelBackground();
4706 SetDrawBackgroundMask(REDRAW_DOOR_1);
4710 SetDrawBackgroundMask(REDRAW_FIELD);
4716 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4718 // ---------- handle request buttons ----------
4719 result = RequestHandleEvents(req_state);
4723 if (!(req_state & REQ_STAY_OPEN))
4725 CloseDoor(DOOR_CLOSE_1);
4727 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4728 (req_state & REQ_REOPEN))
4729 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4734 if (game_status == GAME_MODE_PLAYING)
4736 SetPanelBackground();
4737 SetDrawBackgroundMask(REDRAW_DOOR_1);
4741 SetDrawBackgroundMask(REDRAW_FIELD);
4744 // continue network game after request
4745 if (network.enabled &&
4746 game_status == GAME_MODE_PLAYING &&
4747 !game.all_players_gone &&
4748 req_state & REQUEST_WAIT_FOR_INPUT)
4749 SendToServer_ContinuePlaying();
4751 // restore deactivated drawing when quick-loading level tape recording
4752 if (tape.playing && tape.deactivate_display)
4753 TapeDeactivateDisplayOn();
4758 static boolean RequestEnvelope(char *text, unsigned int req_state)
4762 if (game_status == GAME_MODE_PLAYING)
4763 BlitScreenToBitmap(backbuffer);
4765 // disable deactivated drawing when quick-loading level tape recording
4766 if (tape.playing && tape.deactivate_display)
4767 TapeDeactivateDisplayOff(TRUE);
4769 SetMouseCursor(CURSOR_DEFAULT);
4771 // pause network game while waiting for request to answer
4772 if (network.enabled &&
4773 game_status == GAME_MODE_PLAYING &&
4774 !game.all_players_gone &&
4775 req_state & REQUEST_WAIT_FOR_INPUT)
4776 SendToServer_PausePlaying();
4778 // simulate releasing mouse button over last gadget, if still pressed
4780 HandleGadgets(-1, -1, 0);
4784 // (replace with setting corresponding request background)
4785 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4786 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4788 // clear door drawing field
4789 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4791 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4793 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4795 if (game_status == GAME_MODE_PLAYING)
4797 SetPanelBackground();
4798 SetDrawBackgroundMask(REDRAW_DOOR_1);
4802 SetDrawBackgroundMask(REDRAW_FIELD);
4808 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4810 // ---------- handle request buttons ----------
4811 result = RequestHandleEvents(req_state);
4815 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4819 if (game_status == GAME_MODE_PLAYING)
4821 SetPanelBackground();
4822 SetDrawBackgroundMask(REDRAW_DOOR_1);
4826 SetDrawBackgroundMask(REDRAW_FIELD);
4829 // continue network game after request
4830 if (network.enabled &&
4831 game_status == GAME_MODE_PLAYING &&
4832 !game.all_players_gone &&
4833 req_state & REQUEST_WAIT_FOR_INPUT)
4834 SendToServer_ContinuePlaying();
4836 // restore deactivated drawing when quick-loading level tape recording
4837 if (tape.playing && tape.deactivate_display)
4838 TapeDeactivateDisplayOn();
4843 boolean Request(char *text, unsigned int req_state)
4845 boolean overlay_enabled = GetOverlayEnabled();
4848 SetOverlayEnabled(FALSE);
4850 if (global.use_envelope_request)
4851 result = RequestEnvelope(text, req_state);
4853 result = RequestDoor(text, req_state);
4855 SetOverlayEnabled(overlay_enabled);
4860 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4862 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4863 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4866 if (dpo1->sort_priority != dpo2->sort_priority)
4867 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4869 compare_result = dpo1->nr - dpo2->nr;
4871 return compare_result;
4874 void InitGraphicCompatibilityInfo_Doors(void)
4880 struct DoorInfo *door;
4884 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4885 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4887 { -1, -1, -1, NULL }
4889 struct Rect door_rect_list[] =
4891 { DX, DY, DXSIZE, DYSIZE },
4892 { VX, VY, VXSIZE, VYSIZE }
4896 for (i = 0; doors[i].door_token != -1; i++)
4898 int door_token = doors[i].door_token;
4899 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4900 int part_1 = doors[i].part_1;
4901 int part_8 = doors[i].part_8;
4902 int part_2 = part_1 + 1;
4903 int part_3 = part_1 + 2;
4904 struct DoorInfo *door = doors[i].door;
4905 struct Rect *door_rect = &door_rect_list[door_index];
4906 boolean door_gfx_redefined = FALSE;
4908 // check if any door part graphic definitions have been redefined
4910 for (j = 0; door_part_controls[j].door_token != -1; j++)
4912 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4913 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4915 if (dpc->door_token == door_token && fi->redefined)
4916 door_gfx_redefined = TRUE;
4919 // check for old-style door graphic/animation modifications
4921 if (!door_gfx_redefined)
4923 if (door->anim_mode & ANIM_STATIC_PANEL)
4925 door->panel.step_xoffset = 0;
4926 door->panel.step_yoffset = 0;
4929 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4931 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4932 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4933 int num_door_steps, num_panel_steps;
4935 // remove door part graphics other than the two default wings
4937 for (j = 0; door_part_controls[j].door_token != -1; j++)
4939 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4940 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4942 if (dpc->graphic >= part_3 &&
4943 dpc->graphic <= part_8)
4947 // set graphics and screen positions of the default wings
4949 g_part_1->width = door_rect->width;
4950 g_part_1->height = door_rect->height;
4951 g_part_2->width = door_rect->width;
4952 g_part_2->height = door_rect->height;
4953 g_part_2->src_x = door_rect->width;
4954 g_part_2->src_y = g_part_1->src_y;
4956 door->part_2.x = door->part_1.x;
4957 door->part_2.y = door->part_1.y;
4959 if (door->width != -1)
4961 g_part_1->width = door->width;
4962 g_part_2->width = door->width;
4964 // special treatment for graphics and screen position of right wing
4965 g_part_2->src_x += door_rect->width - door->width;
4966 door->part_2.x += door_rect->width - door->width;
4969 if (door->height != -1)
4971 g_part_1->height = door->height;
4972 g_part_2->height = door->height;
4974 // special treatment for graphics and screen position of bottom wing
4975 g_part_2->src_y += door_rect->height - door->height;
4976 door->part_2.y += door_rect->height - door->height;
4979 // set animation delays for the default wings and panels
4981 door->part_1.step_delay = door->step_delay;
4982 door->part_2.step_delay = door->step_delay;
4983 door->panel.step_delay = door->step_delay;
4985 // set animation draw order for the default wings
4987 door->part_1.sort_priority = 2; // draw left wing over ...
4988 door->part_2.sort_priority = 1; // ... right wing
4990 // set animation draw offset for the default wings
4992 if (door->anim_mode & ANIM_HORIZONTAL)
4994 door->part_1.step_xoffset = door->step_offset;
4995 door->part_1.step_yoffset = 0;
4996 door->part_2.step_xoffset = door->step_offset * -1;
4997 door->part_2.step_yoffset = 0;
4999 num_door_steps = g_part_1->width / door->step_offset;
5001 else // ANIM_VERTICAL
5003 door->part_1.step_xoffset = 0;
5004 door->part_1.step_yoffset = door->step_offset;
5005 door->part_2.step_xoffset = 0;
5006 door->part_2.step_yoffset = door->step_offset * -1;
5008 num_door_steps = g_part_1->height / door->step_offset;
5011 // set animation draw offset for the default panels
5013 if (door->step_offset > 1)
5015 num_panel_steps = 2 * door_rect->height / door->step_offset;
5016 door->panel.start_step = num_panel_steps - num_door_steps;
5017 door->panel.start_step_closing = door->panel.start_step;
5021 num_panel_steps = door_rect->height / door->step_offset;
5022 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5023 door->panel.start_step_closing = door->panel.start_step;
5024 door->panel.step_delay *= 2;
5031 void InitDoors(void)
5035 for (i = 0; door_part_controls[i].door_token != -1; i++)
5037 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5038 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5040 // initialize "start_step_opening" and "start_step_closing", if needed
5041 if (dpc->pos->start_step_opening == 0 &&
5042 dpc->pos->start_step_closing == 0)
5044 // dpc->pos->start_step_opening = dpc->pos->start_step;
5045 dpc->pos->start_step_closing = dpc->pos->start_step;
5048 // fill structure for door part draw order (sorted below)
5050 dpo->sort_priority = dpc->pos->sort_priority;
5053 // sort door part controls according to sort_priority and graphic number
5054 qsort(door_part_order, MAX_DOOR_PARTS,
5055 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5058 unsigned int OpenDoor(unsigned int door_state)
5060 if (door_state & DOOR_COPY_BACK)
5062 if (door_state & DOOR_OPEN_1)
5063 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5064 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5066 if (door_state & DOOR_OPEN_2)
5067 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5068 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5070 door_state &= ~DOOR_COPY_BACK;
5073 return MoveDoor(door_state);
5076 unsigned int CloseDoor(unsigned int door_state)
5078 unsigned int old_door_state = GetDoorState();
5080 if (!(door_state & DOOR_NO_COPY_BACK))
5082 if (old_door_state & DOOR_OPEN_1)
5083 BlitBitmap(backbuffer, bitmap_db_door_1,
5084 DX, DY, DXSIZE, DYSIZE, 0, 0);
5086 if (old_door_state & DOOR_OPEN_2)
5087 BlitBitmap(backbuffer, bitmap_db_door_2,
5088 VX, VY, VXSIZE, VYSIZE, 0, 0);
5090 door_state &= ~DOOR_NO_COPY_BACK;
5093 return MoveDoor(door_state);
5096 unsigned int GetDoorState(void)
5098 return MoveDoor(DOOR_GET_STATE);
5101 unsigned int SetDoorState(unsigned int door_state)
5103 return MoveDoor(door_state | DOOR_SET_STATE);
5106 static int euclid(int a, int b)
5108 return (b ? euclid(b, a % b) : a);
5111 unsigned int MoveDoor(unsigned int door_state)
5113 struct Rect door_rect_list[] =
5115 { DX, DY, DXSIZE, DYSIZE },
5116 { VX, VY, VXSIZE, VYSIZE }
5118 static int door1 = DOOR_CLOSE_1;
5119 static int door2 = DOOR_CLOSE_2;
5120 unsigned int door_delay = 0;
5121 unsigned int door_delay_value;
5124 if (door_state == DOOR_GET_STATE)
5125 return (door1 | door2);
5127 if (door_state & DOOR_SET_STATE)
5129 if (door_state & DOOR_ACTION_1)
5130 door1 = door_state & DOOR_ACTION_1;
5131 if (door_state & DOOR_ACTION_2)
5132 door2 = door_state & DOOR_ACTION_2;
5134 return (door1 | door2);
5137 if (!(door_state & DOOR_FORCE_REDRAW))
5139 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5140 door_state &= ~DOOR_OPEN_1;
5141 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5142 door_state &= ~DOOR_CLOSE_1;
5143 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5144 door_state &= ~DOOR_OPEN_2;
5145 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5146 door_state &= ~DOOR_CLOSE_2;
5149 if (global.autoplay_leveldir)
5151 door_state |= DOOR_NO_DELAY;
5152 door_state &= ~DOOR_CLOSE_ALL;
5155 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5156 door_state |= DOOR_NO_DELAY;
5158 if (door_state & DOOR_ACTION)
5160 boolean door_panel_drawn[NUM_DOORS];
5161 boolean panel_has_doors[NUM_DOORS];
5162 boolean door_part_skip[MAX_DOOR_PARTS];
5163 boolean door_part_done[MAX_DOOR_PARTS];
5164 boolean door_part_done_all;
5165 int num_steps[MAX_DOOR_PARTS];
5166 int max_move_delay = 0; // delay for complete animations of all doors
5167 int max_step_delay = 0; // delay (ms) between two animation frames
5168 int num_move_steps = 0; // number of animation steps for all doors
5169 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5170 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5171 int current_move_delay = 0;
5175 for (i = 0; i < NUM_DOORS; i++)
5176 panel_has_doors[i] = FALSE;
5178 for (i = 0; i < MAX_DOOR_PARTS; i++)
5180 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5181 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5182 int door_token = dpc->door_token;
5184 door_part_done[i] = FALSE;
5185 door_part_skip[i] = (!(door_state & door_token) ||
5189 for (i = 0; i < MAX_DOOR_PARTS; i++)
5191 int nr = door_part_order[i].nr;
5192 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5193 struct DoorPartPosInfo *pos = dpc->pos;
5194 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5195 int door_token = dpc->door_token;
5196 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5197 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5198 int step_xoffset = ABS(pos->step_xoffset);
5199 int step_yoffset = ABS(pos->step_yoffset);
5200 int step_delay = pos->step_delay;
5201 int current_door_state = door_state & door_token;
5202 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5203 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5204 boolean part_opening = (is_panel ? door_closing : door_opening);
5205 int start_step = (part_opening ? pos->start_step_opening :
5206 pos->start_step_closing);
5207 float move_xsize = (step_xoffset ? g->width : 0);
5208 float move_ysize = (step_yoffset ? g->height : 0);
5209 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5210 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5211 int move_steps = (move_xsteps && move_ysteps ?
5212 MIN(move_xsteps, move_ysteps) :
5213 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5214 int move_delay = move_steps * step_delay;
5216 if (door_part_skip[nr])
5219 max_move_delay = MAX(max_move_delay, move_delay);
5220 max_step_delay = (max_step_delay == 0 ? step_delay :
5221 euclid(max_step_delay, step_delay));
5222 num_steps[nr] = move_steps;
5226 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5228 panel_has_doors[door_index] = TRUE;
5232 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5234 num_move_steps = max_move_delay / max_step_delay;
5235 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5237 door_delay_value = max_step_delay;
5239 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5241 start = num_move_steps - 1;
5245 // opening door sound has priority over simultaneously closing door
5246 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5248 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5250 if (door_state & DOOR_OPEN_1)
5251 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5252 if (door_state & DOOR_OPEN_2)
5253 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5255 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5257 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5259 if (door_state & DOOR_CLOSE_1)
5260 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5261 if (door_state & DOOR_CLOSE_2)
5262 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5266 for (k = start; k < num_move_steps; k++)
5268 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5270 door_part_done_all = TRUE;
5272 for (i = 0; i < NUM_DOORS; i++)
5273 door_panel_drawn[i] = FALSE;
5275 for (i = 0; i < MAX_DOOR_PARTS; i++)
5277 int nr = door_part_order[i].nr;
5278 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5279 struct DoorPartPosInfo *pos = dpc->pos;
5280 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5281 int door_token = dpc->door_token;
5282 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5283 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5284 boolean is_panel_and_door_has_closed = FALSE;
5285 struct Rect *door_rect = &door_rect_list[door_index];
5286 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5288 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5289 int current_door_state = door_state & door_token;
5290 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5291 boolean door_closing = !door_opening;
5292 boolean part_opening = (is_panel ? door_closing : door_opening);
5293 boolean part_closing = !part_opening;
5294 int start_step = (part_opening ? pos->start_step_opening :
5295 pos->start_step_closing);
5296 int step_delay = pos->step_delay;
5297 int step_factor = step_delay / max_step_delay;
5298 int k1 = (step_factor ? k / step_factor + 1 : k);
5299 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5300 int kk = MAX(0, k2);
5303 int src_x, src_y, src_xx, src_yy;
5304 int dst_x, dst_y, dst_xx, dst_yy;
5307 if (door_part_skip[nr])
5310 if (!(door_state & door_token))
5318 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5319 int kk_door = MAX(0, k2_door);
5320 int sync_frame = kk_door * door_delay_value;
5321 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5323 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5324 &g_src_x, &g_src_y);
5329 if (!door_panel_drawn[door_index])
5331 ClearRectangle(drawto, door_rect->x, door_rect->y,
5332 door_rect->width, door_rect->height);
5334 door_panel_drawn[door_index] = TRUE;
5337 // draw opening or closing door parts
5339 if (pos->step_xoffset < 0) // door part on right side
5342 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5345 if (dst_xx + width > door_rect->width)
5346 width = door_rect->width - dst_xx;
5348 else // door part on left side
5351 dst_xx = pos->x - kk * pos->step_xoffset;
5355 src_xx = ABS(dst_xx);
5359 width = g->width - src_xx;
5361 if (width > door_rect->width)
5362 width = door_rect->width;
5364 // printf("::: k == %d [%d] \n", k, start_step);
5367 if (pos->step_yoffset < 0) // door part on bottom side
5370 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5373 if (dst_yy + height > door_rect->height)
5374 height = door_rect->height - dst_yy;
5376 else // door part on top side
5379 dst_yy = pos->y - kk * pos->step_yoffset;
5383 src_yy = ABS(dst_yy);
5387 height = g->height - src_yy;
5390 src_x = g_src_x + src_xx;
5391 src_y = g_src_y + src_yy;
5393 dst_x = door_rect->x + dst_xx;
5394 dst_y = door_rect->y + dst_yy;
5396 is_panel_and_door_has_closed =
5399 panel_has_doors[door_index] &&
5400 k >= num_move_steps_doors_only - 1);
5402 if (width >= 0 && width <= g->width &&
5403 height >= 0 && height <= g->height &&
5404 !is_panel_and_door_has_closed)
5406 if (is_panel || !pos->draw_masked)
5407 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5410 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5414 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5416 if ((part_opening && (width < 0 || height < 0)) ||
5417 (part_closing && (width >= g->width && height >= g->height)))
5418 door_part_done[nr] = TRUE;
5420 // continue door part animations, but not panel after door has closed
5421 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5422 door_part_done_all = FALSE;
5425 if (!(door_state & DOOR_NO_DELAY))
5429 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5431 current_move_delay += max_step_delay;
5433 // prevent OS (Windows) from complaining about program not responding
5437 if (door_part_done_all)
5441 if (!(door_state & DOOR_NO_DELAY))
5443 // wait for specified door action post delay
5444 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5445 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5446 else if (door_state & DOOR_ACTION_1)
5447 door_delay_value = door_1.post_delay;
5448 else if (door_state & DOOR_ACTION_2)
5449 door_delay_value = door_2.post_delay;
5451 while (!DelayReached(&door_delay, door_delay_value))
5456 if (door_state & DOOR_ACTION_1)
5457 door1 = door_state & DOOR_ACTION_1;
5458 if (door_state & DOOR_ACTION_2)
5459 door2 = door_state & DOOR_ACTION_2;
5461 // draw masked border over door area
5462 DrawMaskedBorder(REDRAW_DOOR_1);
5463 DrawMaskedBorder(REDRAW_DOOR_2);
5465 ClearAutoRepeatKeyEvents();
5467 return (door1 | door2);
5470 static boolean useSpecialEditorDoor(void)
5472 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5473 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5475 // do not draw special editor door if editor border defined or redefined
5476 if (graphic_info[graphic].bitmap != NULL || redefined)
5479 // do not draw special editor door if global border defined to be empty
5480 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5483 // do not draw special editor door if viewport definitions do not match
5487 EY + EYSIZE != VY + VYSIZE)
5493 void DrawSpecialEditorDoor(void)
5495 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5496 int top_border_width = gfx1->width;
5497 int top_border_height = gfx1->height;
5498 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5499 int ex = EX - outer_border;
5500 int ey = EY - outer_border;
5501 int vy = VY - outer_border;
5502 int exsize = EXSIZE + 2 * outer_border;
5504 if (!useSpecialEditorDoor())
5507 // draw bigger level editor toolbox window
5508 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5509 top_border_width, top_border_height, ex, ey - top_border_height);
5510 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5511 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5513 redraw_mask |= REDRAW_ALL;
5516 void UndrawSpecialEditorDoor(void)
5518 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5519 int top_border_width = gfx1->width;
5520 int top_border_height = gfx1->height;
5521 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5522 int ex = EX - outer_border;
5523 int ey = EY - outer_border;
5524 int ey_top = ey - top_border_height;
5525 int exsize = EXSIZE + 2 * outer_border;
5526 int eysize = EYSIZE + 2 * outer_border;
5528 if (!useSpecialEditorDoor())
5531 // draw normal tape recorder window
5532 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5534 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5535 ex, ey_top, top_border_width, top_border_height,
5537 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5538 ex, ey, exsize, eysize, ex, ey);
5542 // if screen background is set to "[NONE]", clear editor toolbox window
5543 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5544 ClearRectangle(drawto, ex, ey, exsize, eysize);
5547 redraw_mask |= REDRAW_ALL;
5551 // ---------- new tool button stuff -------------------------------------------
5556 struct TextPosInfo *pos;
5558 boolean is_touch_button;
5560 } toolbutton_info[NUM_TOOL_BUTTONS] =
5563 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5564 TOOL_CTRL_ID_YES, FALSE, "yes"
5567 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5568 TOOL_CTRL_ID_NO, FALSE, "no"
5571 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5572 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5575 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5576 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5579 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5580 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5583 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5584 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5587 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5588 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5591 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5592 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5595 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5596 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5599 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5600 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5604 void CreateToolButtons(void)
5608 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5610 int graphic = toolbutton_info[i].graphic;
5611 struct GraphicInfo *gfx = &graphic_info[graphic];
5612 struct TextPosInfo *pos = toolbutton_info[i].pos;
5613 struct GadgetInfo *gi;
5614 Bitmap *deco_bitmap = None;
5615 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5616 unsigned int event_mask = GD_EVENT_RELEASED;
5617 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5618 int base_x = (is_touch_button ? 0 : DX);
5619 int base_y = (is_touch_button ? 0 : DY);
5620 int gd_x = gfx->src_x;
5621 int gd_y = gfx->src_y;
5622 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5623 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5628 if (global.use_envelope_request && !is_touch_button)
5630 setRequestPosition(&base_x, &base_y, TRUE);
5632 // check if request buttons are outside of envelope and fix, if needed
5633 if (x < 0 || x + gfx->width > request.width ||
5634 y < 0 || y + gfx->height > request.height)
5636 if (id == TOOL_CTRL_ID_YES)
5639 y = request.height - 2 * request.border_size - gfx->height;
5641 else if (id == TOOL_CTRL_ID_NO)
5643 x = request.width - 2 * request.border_size - gfx->width;
5644 y = request.height - 2 * request.border_size - gfx->height;
5646 else if (id == TOOL_CTRL_ID_CONFIRM)
5648 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5649 y = request.height - 2 * request.border_size - gfx->height;
5651 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5653 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5655 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5656 y = request.height - 2 * request.border_size - gfx->height * 2;
5658 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5659 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5664 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5666 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5668 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5669 pos->size, &deco_bitmap, &deco_x, &deco_y);
5670 deco_xpos = (gfx->width - pos->size) / 2;
5671 deco_ypos = (gfx->height - pos->size) / 2;
5674 gi = CreateGadget(GDI_CUSTOM_ID, id,
5675 GDI_IMAGE_ID, graphic,
5676 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5679 GDI_WIDTH, gfx->width,
5680 GDI_HEIGHT, gfx->height,
5681 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5682 GDI_STATE, GD_BUTTON_UNPRESSED,
5683 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5684 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5685 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5686 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5687 GDI_DECORATION_SIZE, pos->size, pos->size,
5688 GDI_DECORATION_SHIFTING, 1, 1,
5689 GDI_DIRECT_DRAW, FALSE,
5690 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5691 GDI_EVENT_MASK, event_mask,
5692 GDI_CALLBACK_ACTION, HandleToolButtons,
5696 Error(ERR_EXIT, "cannot create gadget");
5698 tool_gadget[id] = gi;
5702 void FreeToolButtons(void)
5706 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5707 FreeGadget(tool_gadget[i]);
5710 static void UnmapToolButtons(void)
5714 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5715 UnmapGadget(tool_gadget[i]);
5718 static void HandleToolButtons(struct GadgetInfo *gi)
5720 request_gadget_id = gi->custom_id;
5723 static struct Mapping_EM_to_RND_object
5726 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5727 boolean is_backside; // backside of moving element
5733 em_object_mapping_list[GAME_TILE_MAX + 1] =
5736 Zborder, FALSE, FALSE,
5740 Zplayer, FALSE, FALSE,
5749 Ztank, FALSE, FALSE,
5753 Zeater, FALSE, FALSE,
5757 Zdynamite, FALSE, FALSE,
5761 Zboom, FALSE, FALSE,
5766 Xchain, FALSE, FALSE,
5767 EL_DEFAULT, ACTION_EXPLODING, -1
5770 Xboom_bug, FALSE, FALSE,
5771 EL_BUG, ACTION_EXPLODING, -1
5774 Xboom_tank, FALSE, FALSE,
5775 EL_SPACESHIP, ACTION_EXPLODING, -1
5778 Xboom_android, FALSE, FALSE,
5779 EL_EMC_ANDROID, ACTION_OTHER, -1
5782 Xboom_1, FALSE, FALSE,
5783 EL_DEFAULT, ACTION_EXPLODING, -1
5786 Xboom_2, FALSE, FALSE,
5787 EL_DEFAULT, ACTION_EXPLODING, -1
5791 Xblank, TRUE, FALSE,
5796 Xsplash_e, FALSE, FALSE,
5797 EL_ACID_SPLASH_RIGHT, -1, -1
5800 Xsplash_w, FALSE, FALSE,
5801 EL_ACID_SPLASH_LEFT, -1, -1
5805 Xplant, TRUE, FALSE,
5806 EL_EMC_PLANT, -1, -1
5809 Yplant, FALSE, FALSE,
5810 EL_EMC_PLANT, -1, -1
5814 Xacid_1, TRUE, FALSE,
5818 Xacid_2, FALSE, FALSE,
5822 Xacid_3, FALSE, FALSE,
5826 Xacid_4, FALSE, FALSE,
5830 Xacid_5, FALSE, FALSE,
5834 Xacid_6, FALSE, FALSE,
5838 Xacid_7, FALSE, FALSE,
5842 Xacid_8, FALSE, FALSE,
5847 Xfake_acid_1, TRUE, FALSE,
5848 EL_EMC_FAKE_ACID, -1, -1
5851 Xfake_acid_2, FALSE, FALSE,
5852 EL_EMC_FAKE_ACID, -1, -1
5855 Xfake_acid_3, FALSE, FALSE,
5856 EL_EMC_FAKE_ACID, -1, -1
5859 Xfake_acid_4, FALSE, FALSE,
5860 EL_EMC_FAKE_ACID, -1, -1
5863 Xfake_acid_5, FALSE, FALSE,
5864 EL_EMC_FAKE_ACID, -1, -1
5867 Xfake_acid_6, FALSE, FALSE,
5868 EL_EMC_FAKE_ACID, -1, -1
5871 Xfake_acid_7, FALSE, FALSE,
5872 EL_EMC_FAKE_ACID, -1, -1
5875 Xfake_acid_8, FALSE, FALSE,
5876 EL_EMC_FAKE_ACID, -1, -1
5880 Xgrass, TRUE, FALSE,
5881 EL_EMC_GRASS, -1, -1
5884 Ygrass_nB, FALSE, FALSE,
5885 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5888 Ygrass_eB, FALSE, FALSE,
5889 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5892 Ygrass_sB, FALSE, FALSE,
5893 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5896 Ygrass_wB, FALSE, FALSE,
5897 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5905 Ydirt_nB, FALSE, FALSE,
5906 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5909 Ydirt_eB, FALSE, FALSE,
5910 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5913 Ydirt_sB, FALSE, FALSE,
5914 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5917 Ydirt_wB, FALSE, FALSE,
5918 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5922 Xandroid, TRUE, FALSE,
5923 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5926 Xandroid_1_n, FALSE, FALSE,
5927 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5930 Xandroid_2_n, FALSE, FALSE,
5931 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5934 Xandroid_1_e, FALSE, FALSE,
5935 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5938 Xandroid_2_e, FALSE, FALSE,
5939 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5942 Xandroid_1_w, FALSE, FALSE,
5943 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5946 Xandroid_2_w, FALSE, FALSE,
5947 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5950 Xandroid_1_s, FALSE, FALSE,
5951 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5954 Xandroid_2_s, FALSE, FALSE,
5955 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5958 Yandroid_n, FALSE, FALSE,
5959 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5962 Yandroid_nB, FALSE, TRUE,
5963 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5966 Yandroid_ne, FALSE, FALSE,
5967 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5970 Yandroid_neB, FALSE, TRUE,
5971 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5974 Yandroid_e, FALSE, FALSE,
5975 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5978 Yandroid_eB, FALSE, TRUE,
5979 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5982 Yandroid_se, FALSE, FALSE,
5983 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5986 Yandroid_seB, FALSE, TRUE,
5987 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5990 Yandroid_s, FALSE, FALSE,
5991 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5994 Yandroid_sB, FALSE, TRUE,
5995 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5998 Yandroid_sw, FALSE, FALSE,
5999 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6002 Yandroid_swB, FALSE, TRUE,
6003 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6006 Yandroid_w, FALSE, FALSE,
6007 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6010 Yandroid_wB, FALSE, TRUE,
6011 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6014 Yandroid_nw, FALSE, FALSE,
6015 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6018 Yandroid_nwB, FALSE, TRUE,
6019 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6023 Xeater_n, TRUE, FALSE,
6024 EL_YAMYAM_UP, -1, -1
6027 Xeater_e, TRUE, FALSE,
6028 EL_YAMYAM_RIGHT, -1, -1
6031 Xeater_w, TRUE, FALSE,
6032 EL_YAMYAM_LEFT, -1, -1
6035 Xeater_s, TRUE, FALSE,
6036 EL_YAMYAM_DOWN, -1, -1
6039 Yeater_n, FALSE, FALSE,
6040 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6043 Yeater_nB, FALSE, TRUE,
6044 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6047 Yeater_e, FALSE, FALSE,
6048 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6051 Yeater_eB, FALSE, TRUE,
6052 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6055 Yeater_s, FALSE, FALSE,
6056 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6059 Yeater_sB, FALSE, TRUE,
6060 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6063 Yeater_w, FALSE, FALSE,
6064 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6067 Yeater_wB, FALSE, TRUE,
6068 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6071 Yeater_stone, FALSE, FALSE,
6072 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6075 Yeater_spring, FALSE, FALSE,
6076 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6080 Xalien, TRUE, FALSE,
6084 Xalien_pause, FALSE, FALSE,
6088 Yalien_n, FALSE, FALSE,
6089 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6092 Yalien_nB, FALSE, TRUE,
6093 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6096 Yalien_e, FALSE, FALSE,
6097 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6100 Yalien_eB, FALSE, TRUE,
6101 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6104 Yalien_s, FALSE, FALSE,
6105 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6108 Yalien_sB, FALSE, TRUE,
6109 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6112 Yalien_w, FALSE, FALSE,
6113 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6116 Yalien_wB, FALSE, TRUE,
6117 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6120 Yalien_stone, FALSE, FALSE,
6121 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6124 Yalien_spring, FALSE, FALSE,
6125 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6129 Xbug_1_n, TRUE, FALSE,
6133 Xbug_1_e, TRUE, FALSE,
6134 EL_BUG_RIGHT, -1, -1
6137 Xbug_1_s, TRUE, FALSE,
6141 Xbug_1_w, TRUE, FALSE,
6145 Xbug_2_n, FALSE, FALSE,
6149 Xbug_2_e, FALSE, FALSE,
6150 EL_BUG_RIGHT, -1, -1
6153 Xbug_2_s, FALSE, FALSE,
6157 Xbug_2_w, FALSE, FALSE,
6161 Ybug_n, FALSE, FALSE,
6162 EL_BUG, ACTION_MOVING, MV_BIT_UP
6165 Ybug_nB, FALSE, TRUE,
6166 EL_BUG, ACTION_MOVING, MV_BIT_UP
6169 Ybug_e, FALSE, FALSE,
6170 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6173 Ybug_eB, FALSE, TRUE,
6174 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6177 Ybug_s, FALSE, FALSE,
6178 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6181 Ybug_sB, FALSE, TRUE,
6182 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6185 Ybug_w, FALSE, FALSE,
6186 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6189 Ybug_wB, FALSE, TRUE,
6190 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6193 Ybug_w_n, FALSE, FALSE,
6194 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6197 Ybug_n_e, FALSE, FALSE,
6198 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6201 Ybug_e_s, FALSE, FALSE,
6202 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6205 Ybug_s_w, FALSE, FALSE,
6206 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6209 Ybug_e_n, FALSE, FALSE,
6210 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6213 Ybug_s_e, FALSE, FALSE,
6214 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6217 Ybug_w_s, FALSE, FALSE,
6218 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6221 Ybug_n_w, FALSE, FALSE,
6222 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6225 Ybug_stone, FALSE, FALSE,
6226 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6229 Ybug_spring, FALSE, FALSE,
6230 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6234 Xtank_1_n, TRUE, FALSE,
6235 EL_SPACESHIP_UP, -1, -1
6238 Xtank_1_e, TRUE, FALSE,
6239 EL_SPACESHIP_RIGHT, -1, -1
6242 Xtank_1_s, TRUE, FALSE,
6243 EL_SPACESHIP_DOWN, -1, -1
6246 Xtank_1_w, TRUE, FALSE,
6247 EL_SPACESHIP_LEFT, -1, -1
6250 Xtank_2_n, FALSE, FALSE,
6251 EL_SPACESHIP_UP, -1, -1
6254 Xtank_2_e, FALSE, FALSE,
6255 EL_SPACESHIP_RIGHT, -1, -1
6258 Xtank_2_s, FALSE, FALSE,
6259 EL_SPACESHIP_DOWN, -1, -1
6262 Xtank_2_w, FALSE, FALSE,
6263 EL_SPACESHIP_LEFT, -1, -1
6266 Ytank_n, FALSE, FALSE,
6267 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6270 Ytank_nB, FALSE, TRUE,
6271 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6274 Ytank_e, FALSE, FALSE,
6275 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6278 Ytank_eB, FALSE, TRUE,
6279 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6282 Ytank_s, FALSE, FALSE,
6283 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6286 Ytank_sB, FALSE, TRUE,
6287 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6290 Ytank_w, FALSE, FALSE,
6291 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6294 Ytank_wB, FALSE, TRUE,
6295 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6298 Ytank_w_n, FALSE, FALSE,
6299 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6302 Ytank_n_e, FALSE, FALSE,
6303 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6306 Ytank_e_s, FALSE, FALSE,
6307 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6310 Ytank_s_w, FALSE, FALSE,
6311 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6314 Ytank_e_n, FALSE, FALSE,
6315 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6318 Ytank_s_e, FALSE, FALSE,
6319 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6322 Ytank_w_s, FALSE, FALSE,
6323 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6326 Ytank_n_w, FALSE, FALSE,
6327 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6330 Ytank_stone, FALSE, FALSE,
6331 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6334 Ytank_spring, FALSE, FALSE,
6335 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6339 Xemerald, TRUE, FALSE,
6343 Xemerald_pause, FALSE, FALSE,
6347 Xemerald_fall, FALSE, FALSE,
6351 Xemerald_shine, FALSE, FALSE,
6352 EL_EMERALD, ACTION_TWINKLING, -1
6355 Yemerald_s, FALSE, FALSE,
6356 EL_EMERALD, ACTION_FALLING, -1
6359 Yemerald_sB, FALSE, TRUE,
6360 EL_EMERALD, ACTION_FALLING, -1
6363 Yemerald_e, FALSE, FALSE,
6364 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6367 Yemerald_eB, FALSE, TRUE,
6368 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6371 Yemerald_w, FALSE, FALSE,
6372 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6375 Yemerald_wB, FALSE, TRUE,
6376 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6379 Yemerald_blank, FALSE, FALSE,
6380 EL_EMERALD, ACTION_COLLECTING, -1
6384 Xdiamond, TRUE, FALSE,
6388 Xdiamond_pause, FALSE, FALSE,
6392 Xdiamond_fall, FALSE, FALSE,
6396 Xdiamond_shine, FALSE, FALSE,
6397 EL_DIAMOND, ACTION_TWINKLING, -1
6400 Ydiamond_s, FALSE, FALSE,
6401 EL_DIAMOND, ACTION_FALLING, -1
6404 Ydiamond_sB, FALSE, TRUE,
6405 EL_DIAMOND, ACTION_FALLING, -1
6408 Ydiamond_e, FALSE, FALSE,
6409 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6412 Ydiamond_eB, FALSE, TRUE,
6413 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6416 Ydiamond_w, FALSE, FALSE,
6417 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6420 Ydiamond_wB, FALSE, TRUE,
6421 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6424 Ydiamond_blank, FALSE, FALSE,
6425 EL_DIAMOND, ACTION_COLLECTING, -1
6428 Ydiamond_stone, FALSE, FALSE,
6429 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6433 Xstone, TRUE, FALSE,
6437 Xstone_pause, FALSE, FALSE,
6441 Xstone_fall, FALSE, FALSE,
6445 Ystone_s, FALSE, FALSE,
6446 EL_ROCK, ACTION_FALLING, -1
6449 Ystone_sB, FALSE, TRUE,
6450 EL_ROCK, ACTION_FALLING, -1
6453 Ystone_e, FALSE, FALSE,
6454 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6457 Ystone_eB, FALSE, TRUE,
6458 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6461 Ystone_w, FALSE, FALSE,
6462 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6465 Ystone_wB, FALSE, TRUE,
6466 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6474 Xbomb_pause, FALSE, FALSE,
6478 Xbomb_fall, FALSE, FALSE,
6482 Ybomb_s, FALSE, FALSE,
6483 EL_BOMB, ACTION_FALLING, -1
6486 Ybomb_sB, FALSE, TRUE,
6487 EL_BOMB, ACTION_FALLING, -1
6490 Ybomb_e, FALSE, FALSE,
6491 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6494 Ybomb_eB, FALSE, TRUE,
6495 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6498 Ybomb_w, FALSE, FALSE,
6499 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6502 Ybomb_wB, FALSE, TRUE,
6503 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6506 Ybomb_blank, FALSE, FALSE,
6507 EL_BOMB, ACTION_ACTIVATING, -1
6515 Xnut_pause, FALSE, FALSE,
6519 Xnut_fall, FALSE, FALSE,
6523 Ynut_s, FALSE, FALSE,
6524 EL_NUT, ACTION_FALLING, -1
6527 Ynut_sB, FALSE, TRUE,
6528 EL_NUT, ACTION_FALLING, -1
6531 Ynut_e, FALSE, FALSE,
6532 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6535 Ynut_eB, FALSE, TRUE,
6536 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6539 Ynut_w, FALSE, FALSE,
6540 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6543 Ynut_wB, FALSE, TRUE,
6544 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6547 Ynut_stone, FALSE, FALSE,
6548 EL_NUT, ACTION_BREAKING, -1
6552 Xspring, TRUE, FALSE,
6556 Xspring_pause, FALSE, FALSE,
6560 Xspring_e, FALSE, FALSE,
6564 Xspring_w, FALSE, FALSE,
6568 Xspring_fall, FALSE, FALSE,
6572 Yspring_s, FALSE, FALSE,
6573 EL_SPRING, ACTION_FALLING, -1
6576 Yspring_sB, FALSE, TRUE,
6577 EL_SPRING, ACTION_FALLING, -1
6580 Yspring_e, FALSE, FALSE,
6581 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6584 Yspring_eB, FALSE, TRUE,
6585 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6588 Yspring_w, FALSE, FALSE,
6589 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6592 Yspring_wB, FALSE, TRUE,
6593 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6596 Yspring_alien_e, FALSE, FALSE,
6597 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6600 Yspring_alien_eB, FALSE, TRUE,
6601 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6604 Yspring_alien_w, FALSE, FALSE,
6605 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6608 Yspring_alien_wB, FALSE, TRUE,
6609 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6613 Xpush_emerald_e, FALSE, FALSE,
6614 EL_EMERALD, -1, MV_BIT_RIGHT
6617 Xpush_emerald_w, FALSE, FALSE,
6618 EL_EMERALD, -1, MV_BIT_LEFT
6621 Xpush_diamond_e, FALSE, FALSE,
6622 EL_DIAMOND, -1, MV_BIT_RIGHT
6625 Xpush_diamond_w, FALSE, FALSE,
6626 EL_DIAMOND, -1, MV_BIT_LEFT
6629 Xpush_stone_e, FALSE, FALSE,
6630 EL_ROCK, -1, MV_BIT_RIGHT
6633 Xpush_stone_w, FALSE, FALSE,
6634 EL_ROCK, -1, MV_BIT_LEFT
6637 Xpush_bomb_e, FALSE, FALSE,
6638 EL_BOMB, -1, MV_BIT_RIGHT
6641 Xpush_bomb_w, FALSE, FALSE,
6642 EL_BOMB, -1, MV_BIT_LEFT
6645 Xpush_nut_e, FALSE, FALSE,
6646 EL_NUT, -1, MV_BIT_RIGHT
6649 Xpush_nut_w, FALSE, FALSE,
6650 EL_NUT, -1, MV_BIT_LEFT
6653 Xpush_spring_e, FALSE, FALSE,
6654 EL_SPRING, -1, MV_BIT_RIGHT
6657 Xpush_spring_w, FALSE, FALSE,
6658 EL_SPRING, -1, MV_BIT_LEFT
6662 Xdynamite, TRUE, FALSE,
6663 EL_EM_DYNAMITE, -1, -1
6666 Ydynamite_blank, FALSE, FALSE,
6667 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6670 Xdynamite_1, TRUE, FALSE,
6671 EL_EM_DYNAMITE_ACTIVE, -1, -1
6674 Xdynamite_2, FALSE, FALSE,
6675 EL_EM_DYNAMITE_ACTIVE, -1, -1
6678 Xdynamite_3, FALSE, FALSE,
6679 EL_EM_DYNAMITE_ACTIVE, -1, -1
6682 Xdynamite_4, FALSE, FALSE,
6683 EL_EM_DYNAMITE_ACTIVE, -1, -1
6687 Xkey_1, TRUE, FALSE,
6691 Xkey_2, TRUE, FALSE,
6695 Xkey_3, TRUE, FALSE,
6699 Xkey_4, TRUE, FALSE,
6703 Xkey_5, TRUE, FALSE,
6704 EL_EMC_KEY_5, -1, -1
6707 Xkey_6, TRUE, FALSE,
6708 EL_EMC_KEY_6, -1, -1
6711 Xkey_7, TRUE, FALSE,
6712 EL_EMC_KEY_7, -1, -1
6715 Xkey_8, TRUE, FALSE,
6716 EL_EMC_KEY_8, -1, -1
6720 Xdoor_1, TRUE, FALSE,
6721 EL_EM_GATE_1, -1, -1
6724 Xdoor_2, TRUE, FALSE,
6725 EL_EM_GATE_2, -1, -1
6728 Xdoor_3, TRUE, FALSE,
6729 EL_EM_GATE_3, -1, -1
6732 Xdoor_4, TRUE, FALSE,
6733 EL_EM_GATE_4, -1, -1
6736 Xdoor_5, TRUE, FALSE,
6737 EL_EMC_GATE_5, -1, -1
6740 Xdoor_6, TRUE, FALSE,
6741 EL_EMC_GATE_6, -1, -1
6744 Xdoor_7, TRUE, FALSE,
6745 EL_EMC_GATE_7, -1, -1
6748 Xdoor_8, TRUE, FALSE,
6749 EL_EMC_GATE_8, -1, -1
6753 Xfake_door_1, TRUE, FALSE,
6754 EL_EM_GATE_1_GRAY, -1, -1
6757 Xfake_door_2, TRUE, FALSE,
6758 EL_EM_GATE_2_GRAY, -1, -1
6761 Xfake_door_3, TRUE, FALSE,
6762 EL_EM_GATE_3_GRAY, -1, -1
6765 Xfake_door_4, TRUE, FALSE,
6766 EL_EM_GATE_4_GRAY, -1, -1
6769 Xfake_door_5, TRUE, FALSE,
6770 EL_EMC_GATE_5_GRAY, -1, -1
6773 Xfake_door_6, TRUE, FALSE,
6774 EL_EMC_GATE_6_GRAY, -1, -1
6777 Xfake_door_7, TRUE, FALSE,
6778 EL_EMC_GATE_7_GRAY, -1, -1
6781 Xfake_door_8, TRUE, FALSE,
6782 EL_EMC_GATE_8_GRAY, -1, -1
6786 Xballoon, TRUE, FALSE,
6790 Yballoon_n, FALSE, FALSE,
6791 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6794 Yballoon_nB, FALSE, TRUE,
6795 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6798 Yballoon_e, FALSE, FALSE,
6799 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6802 Yballoon_eB, FALSE, TRUE,
6803 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6806 Yballoon_s, FALSE, FALSE,
6807 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6810 Yballoon_sB, FALSE, TRUE,
6811 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6814 Yballoon_w, FALSE, FALSE,
6815 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6818 Yballoon_wB, FALSE, TRUE,
6819 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6823 Xball_1, TRUE, FALSE,
6824 EL_EMC_MAGIC_BALL, -1, -1
6827 Yball_1, FALSE, FALSE,
6828 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6831 Xball_2, FALSE, FALSE,
6832 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6835 Yball_2, FALSE, FALSE,
6836 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6839 Yball_blank, FALSE, FALSE,
6840 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6844 Xamoeba_1, TRUE, FALSE,
6845 EL_AMOEBA_DRY, ACTION_OTHER, -1
6848 Xamoeba_2, FALSE, FALSE,
6849 EL_AMOEBA_DRY, ACTION_OTHER, -1
6852 Xamoeba_3, FALSE, FALSE,
6853 EL_AMOEBA_DRY, ACTION_OTHER, -1
6856 Xamoeba_4, FALSE, FALSE,
6857 EL_AMOEBA_DRY, ACTION_OTHER, -1
6860 Xamoeba_5, TRUE, FALSE,
6861 EL_AMOEBA_WET, ACTION_OTHER, -1
6864 Xamoeba_6, FALSE, FALSE,
6865 EL_AMOEBA_WET, ACTION_OTHER, -1
6868 Xamoeba_7, FALSE, FALSE,
6869 EL_AMOEBA_WET, ACTION_OTHER, -1
6872 Xamoeba_8, FALSE, FALSE,
6873 EL_AMOEBA_WET, ACTION_OTHER, -1
6877 Xdrip, FALSE, FALSE,
6878 EL_AMOEBA_DROP, ACTION_GROWING, -1
6881 Xdrip_fall, TRUE, FALSE,
6882 EL_AMOEBA_DROP, -1, -1
6885 Xdrip_stretch, FALSE, FALSE,
6886 EL_AMOEBA_DROP, ACTION_FALLING, -1
6889 Xdrip_stretchB, FALSE, TRUE,
6890 EL_AMOEBA_DROP, ACTION_FALLING, -1
6893 Ydrip_1_s, FALSE, FALSE,
6894 EL_AMOEBA_DROP, ACTION_FALLING, -1
6897 Ydrip_1_sB, FALSE, TRUE,
6898 EL_AMOEBA_DROP, ACTION_FALLING, -1
6901 Ydrip_2_s, FALSE, FALSE,
6902 EL_AMOEBA_DROP, ACTION_FALLING, -1
6905 Ydrip_2_sB, FALSE, TRUE,
6906 EL_AMOEBA_DROP, ACTION_FALLING, -1
6910 Xwonderwall, TRUE, FALSE,
6911 EL_MAGIC_WALL, -1, -1
6914 Ywonderwall, FALSE, FALSE,
6915 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6919 Xwheel, TRUE, FALSE,
6920 EL_ROBOT_WHEEL, -1, -1
6923 Ywheel, FALSE, FALSE,
6924 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6928 Xswitch, TRUE, FALSE,
6929 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6932 Yswitch, FALSE, FALSE,
6933 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6937 Xbumper, TRUE, FALSE,
6938 EL_EMC_SPRING_BUMPER, -1, -1
6941 Ybumper, FALSE, FALSE,
6942 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6946 Xacid_nw, TRUE, FALSE,
6947 EL_ACID_POOL_TOPLEFT, -1, -1
6950 Xacid_ne, TRUE, FALSE,
6951 EL_ACID_POOL_TOPRIGHT, -1, -1
6954 Xacid_sw, TRUE, FALSE,
6955 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6958 Xacid_s, TRUE, FALSE,
6959 EL_ACID_POOL_BOTTOM, -1, -1
6962 Xacid_se, TRUE, FALSE,
6963 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6967 Xfake_blank, TRUE, FALSE,
6968 EL_INVISIBLE_WALL, -1, -1
6971 Yfake_blank, FALSE, FALSE,
6972 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6976 Xfake_grass, TRUE, FALSE,
6977 EL_EMC_FAKE_GRASS, -1, -1
6980 Yfake_grass, FALSE, FALSE,
6981 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6985 Xfake_amoeba, TRUE, FALSE,
6986 EL_EMC_DRIPPER, -1, -1
6989 Yfake_amoeba, FALSE, FALSE,
6990 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6994 Xlenses, TRUE, FALSE,
6995 EL_EMC_LENSES, -1, -1
6999 Xmagnify, TRUE, FALSE,
7000 EL_EMC_MAGNIFIER, -1, -1
7005 EL_QUICKSAND_EMPTY, -1, -1
7008 Xsand_stone, TRUE, FALSE,
7009 EL_QUICKSAND_FULL, -1, -1
7012 Xsand_stonein_1, FALSE, TRUE,
7013 EL_ROCK, ACTION_FILLING, -1
7016 Xsand_stonein_2, FALSE, TRUE,
7017 EL_ROCK, ACTION_FILLING, -1
7020 Xsand_stonein_3, FALSE, TRUE,
7021 EL_ROCK, ACTION_FILLING, -1
7024 Xsand_stonein_4, FALSE, TRUE,
7025 EL_ROCK, ACTION_FILLING, -1
7028 Xsand_sandstone_1, FALSE, FALSE,
7029 EL_QUICKSAND_FILLING, -1, -1
7032 Xsand_sandstone_2, FALSE, FALSE,
7033 EL_QUICKSAND_FILLING, -1, -1
7036 Xsand_sandstone_3, FALSE, FALSE,
7037 EL_QUICKSAND_FILLING, -1, -1
7040 Xsand_sandstone_4, FALSE, FALSE,
7041 EL_QUICKSAND_FILLING, -1, -1
7044 Xsand_stonesand_1, FALSE, FALSE,
7045 EL_QUICKSAND_EMPTYING, -1, -1
7048 Xsand_stonesand_2, FALSE, FALSE,
7049 EL_QUICKSAND_EMPTYING, -1, -1
7052 Xsand_stonesand_3, FALSE, FALSE,
7053 EL_QUICKSAND_EMPTYING, -1, -1
7056 Xsand_stonesand_4, FALSE, FALSE,
7057 EL_QUICKSAND_EMPTYING, -1, -1
7060 Xsand_stoneout_1, FALSE, FALSE,
7061 EL_ROCK, ACTION_EMPTYING, -1
7064 Xsand_stoneout_2, FALSE, FALSE,
7065 EL_ROCK, ACTION_EMPTYING, -1
7068 Xsand_stonesand_quickout_1, FALSE, FALSE,
7069 EL_QUICKSAND_EMPTYING, -1, -1
7072 Xsand_stonesand_quickout_2, FALSE, FALSE,
7073 EL_QUICKSAND_EMPTYING, -1, -1
7077 Xslide_ns, TRUE, FALSE,
7078 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7081 Yslide_ns_blank, FALSE, FALSE,
7082 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7085 Xslide_ew, TRUE, FALSE,
7086 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7089 Yslide_ew_blank, FALSE, FALSE,
7090 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7094 Xwind_n, TRUE, FALSE,
7095 EL_BALLOON_SWITCH_UP, -1, -1
7098 Xwind_e, TRUE, FALSE,
7099 EL_BALLOON_SWITCH_RIGHT, -1, -1
7102 Xwind_s, TRUE, FALSE,
7103 EL_BALLOON_SWITCH_DOWN, -1, -1
7106 Xwind_w, TRUE, FALSE,
7107 EL_BALLOON_SWITCH_LEFT, -1, -1
7110 Xwind_any, TRUE, FALSE,
7111 EL_BALLOON_SWITCH_ANY, -1, -1
7114 Xwind_stop, TRUE, FALSE,
7115 EL_BALLOON_SWITCH_NONE, -1, -1
7120 EL_EM_EXIT_CLOSED, -1, -1
7123 Xexit_1, TRUE, FALSE,
7124 EL_EM_EXIT_OPEN, -1, -1
7127 Xexit_2, FALSE, FALSE,
7128 EL_EM_EXIT_OPEN, -1, -1
7131 Xexit_3, FALSE, FALSE,
7132 EL_EM_EXIT_OPEN, -1, -1
7136 Xpause, FALSE, FALSE,
7141 Xwall_1, TRUE, FALSE,
7145 Xwall_2, TRUE, FALSE,
7146 EL_EMC_WALL_14, -1, -1
7149 Xwall_3, TRUE, FALSE,
7150 EL_EMC_WALL_15, -1, -1
7153 Xwall_4, TRUE, FALSE,
7154 EL_EMC_WALL_16, -1, -1
7158 Xroundwall_1, TRUE, FALSE,
7159 EL_WALL_SLIPPERY, -1, -1
7162 Xroundwall_2, TRUE, FALSE,
7163 EL_EMC_WALL_SLIPPERY_2, -1, -1
7166 Xroundwall_3, TRUE, FALSE,
7167 EL_EMC_WALL_SLIPPERY_3, -1, -1
7170 Xroundwall_4, TRUE, FALSE,
7171 EL_EMC_WALL_SLIPPERY_4, -1, -1
7175 Xsteel_1, TRUE, FALSE,
7176 EL_STEELWALL, -1, -1
7179 Xsteel_2, TRUE, FALSE,
7180 EL_EMC_STEELWALL_2, -1, -1
7183 Xsteel_3, TRUE, FALSE,
7184 EL_EMC_STEELWALL_3, -1, -1
7187 Xsteel_4, TRUE, FALSE,
7188 EL_EMC_STEELWALL_4, -1, -1
7192 Xdecor_1, TRUE, FALSE,
7193 EL_EMC_WALL_8, -1, -1
7196 Xdecor_2, TRUE, FALSE,
7197 EL_EMC_WALL_6, -1, -1
7200 Xdecor_3, TRUE, FALSE,
7201 EL_EMC_WALL_4, -1, -1
7204 Xdecor_4, TRUE, FALSE,
7205 EL_EMC_WALL_7, -1, -1
7208 Xdecor_5, TRUE, FALSE,
7209 EL_EMC_WALL_5, -1, -1
7212 Xdecor_6, TRUE, FALSE,
7213 EL_EMC_WALL_9, -1, -1
7216 Xdecor_7, TRUE, FALSE,
7217 EL_EMC_WALL_10, -1, -1
7220 Xdecor_8, TRUE, FALSE,
7221 EL_EMC_WALL_1, -1, -1
7224 Xdecor_9, TRUE, FALSE,
7225 EL_EMC_WALL_2, -1, -1
7228 Xdecor_10, TRUE, FALSE,
7229 EL_EMC_WALL_3, -1, -1
7232 Xdecor_11, TRUE, FALSE,
7233 EL_EMC_WALL_11, -1, -1
7236 Xdecor_12, TRUE, FALSE,
7237 EL_EMC_WALL_12, -1, -1
7241 Xalpha_0, TRUE, FALSE,
7242 EL_CHAR('0'), -1, -1
7245 Xalpha_1, TRUE, FALSE,
7246 EL_CHAR('1'), -1, -1
7249 Xalpha_2, TRUE, FALSE,
7250 EL_CHAR('2'), -1, -1
7253 Xalpha_3, TRUE, FALSE,
7254 EL_CHAR('3'), -1, -1
7257 Xalpha_4, TRUE, FALSE,
7258 EL_CHAR('4'), -1, -1
7261 Xalpha_5, TRUE, FALSE,
7262 EL_CHAR('5'), -1, -1
7265 Xalpha_6, TRUE, FALSE,
7266 EL_CHAR('6'), -1, -1
7269 Xalpha_7, TRUE, FALSE,
7270 EL_CHAR('7'), -1, -1
7273 Xalpha_8, TRUE, FALSE,
7274 EL_CHAR('8'), -1, -1
7277 Xalpha_9, TRUE, FALSE,
7278 EL_CHAR('9'), -1, -1
7281 Xalpha_excla, TRUE, FALSE,
7282 EL_CHAR('!'), -1, -1
7285 Xalpha_apost, TRUE, FALSE,
7286 EL_CHAR('\''), -1, -1
7289 Xalpha_comma, TRUE, FALSE,
7290 EL_CHAR(','), -1, -1
7293 Xalpha_minus, TRUE, FALSE,
7294 EL_CHAR('-'), -1, -1
7297 Xalpha_perio, TRUE, FALSE,
7298 EL_CHAR('.'), -1, -1
7301 Xalpha_colon, TRUE, FALSE,
7302 EL_CHAR(':'), -1, -1
7305 Xalpha_quest, TRUE, FALSE,
7306 EL_CHAR('?'), -1, -1
7309 Xalpha_a, TRUE, FALSE,
7310 EL_CHAR('A'), -1, -1
7313 Xalpha_b, TRUE, FALSE,
7314 EL_CHAR('B'), -1, -1
7317 Xalpha_c, TRUE, FALSE,
7318 EL_CHAR('C'), -1, -1
7321 Xalpha_d, TRUE, FALSE,
7322 EL_CHAR('D'), -1, -1
7325 Xalpha_e, TRUE, FALSE,
7326 EL_CHAR('E'), -1, -1
7329 Xalpha_f, TRUE, FALSE,
7330 EL_CHAR('F'), -1, -1
7333 Xalpha_g, TRUE, FALSE,
7334 EL_CHAR('G'), -1, -1
7337 Xalpha_h, TRUE, FALSE,
7338 EL_CHAR('H'), -1, -1
7341 Xalpha_i, TRUE, FALSE,
7342 EL_CHAR('I'), -1, -1
7345 Xalpha_j, TRUE, FALSE,
7346 EL_CHAR('J'), -1, -1
7349 Xalpha_k, TRUE, FALSE,
7350 EL_CHAR('K'), -1, -1
7353 Xalpha_l, TRUE, FALSE,
7354 EL_CHAR('L'), -1, -1
7357 Xalpha_m, TRUE, FALSE,
7358 EL_CHAR('M'), -1, -1
7361 Xalpha_n, TRUE, FALSE,
7362 EL_CHAR('N'), -1, -1
7365 Xalpha_o, TRUE, FALSE,
7366 EL_CHAR('O'), -1, -1
7369 Xalpha_p, TRUE, FALSE,
7370 EL_CHAR('P'), -1, -1
7373 Xalpha_q, TRUE, FALSE,
7374 EL_CHAR('Q'), -1, -1
7377 Xalpha_r, TRUE, FALSE,
7378 EL_CHAR('R'), -1, -1
7381 Xalpha_s, TRUE, FALSE,
7382 EL_CHAR('S'), -1, -1
7385 Xalpha_t, TRUE, FALSE,
7386 EL_CHAR('T'), -1, -1
7389 Xalpha_u, TRUE, FALSE,
7390 EL_CHAR('U'), -1, -1
7393 Xalpha_v, TRUE, FALSE,
7394 EL_CHAR('V'), -1, -1
7397 Xalpha_w, TRUE, FALSE,
7398 EL_CHAR('W'), -1, -1
7401 Xalpha_x, TRUE, FALSE,
7402 EL_CHAR('X'), -1, -1
7405 Xalpha_y, TRUE, FALSE,
7406 EL_CHAR('Y'), -1, -1
7409 Xalpha_z, TRUE, FALSE,
7410 EL_CHAR('Z'), -1, -1
7413 Xalpha_arrow_e, TRUE, FALSE,
7414 EL_CHAR('>'), -1, -1
7417 Xalpha_arrow_w, TRUE, FALSE,
7418 EL_CHAR('<'), -1, -1
7421 Xalpha_copyr, TRUE, FALSE,
7422 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7426 Ykey_1_blank, FALSE, FALSE,
7427 EL_EM_KEY_1, ACTION_COLLECTING, -1
7430 Ykey_2_blank, FALSE, FALSE,
7431 EL_EM_KEY_2, ACTION_COLLECTING, -1
7434 Ykey_3_blank, FALSE, FALSE,
7435 EL_EM_KEY_3, ACTION_COLLECTING, -1
7438 Ykey_4_blank, FALSE, FALSE,
7439 EL_EM_KEY_4, ACTION_COLLECTING, -1
7442 Ykey_5_blank, FALSE, FALSE,
7443 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7446 Ykey_6_blank, FALSE, FALSE,
7447 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7450 Ykey_7_blank, FALSE, FALSE,
7451 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7454 Ykey_8_blank, FALSE, FALSE,
7455 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7458 Ylenses_blank, FALSE, FALSE,
7459 EL_EMC_LENSES, ACTION_COLLECTING, -1
7462 Ymagnify_blank, FALSE, FALSE,
7463 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7466 Ygrass_blank, FALSE, FALSE,
7467 EL_EMC_GRASS, ACTION_SNAPPING, -1
7470 Ydirt_blank, FALSE, FALSE,
7471 EL_SAND, ACTION_SNAPPING, -1
7480 static struct Mapping_EM_to_RND_player
7489 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7493 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7497 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7501 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7505 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7509 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7513 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7517 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7521 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7525 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7529 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7533 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7537 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7541 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7545 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7549 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7553 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7557 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7561 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7565 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7569 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7573 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7577 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7581 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7585 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7589 EL_PLAYER_1, ACTION_DEFAULT, -1,
7593 EL_PLAYER_2, ACTION_DEFAULT, -1,
7597 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7601 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7605 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7609 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7613 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7617 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7621 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7625 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7629 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7633 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7637 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7641 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7645 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7649 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7653 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7657 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7661 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7665 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7669 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7673 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7677 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7681 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7685 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7689 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7693 EL_PLAYER_3, ACTION_DEFAULT, -1,
7697 EL_PLAYER_4, ACTION_DEFAULT, -1,
7706 int map_element_RND_to_EM_cave(int element_rnd)
7708 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7709 static boolean mapping_initialized = FALSE;
7711 if (!mapping_initialized)
7715 // return "Xalpha_quest" for all undefined elements in mapping array
7716 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7717 mapping_RND_to_EM[i] = Xalpha_quest;
7719 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7720 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7721 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7722 em_object_mapping_list[i].element_em;
7724 mapping_initialized = TRUE;
7727 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7729 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7734 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7737 int map_element_EM_to_RND_cave(int element_em_cave)
7739 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7740 static boolean mapping_initialized = FALSE;
7742 if (!mapping_initialized)
7746 // return "EL_UNKNOWN" for all undefined elements in mapping array
7747 for (i = 0; i < GAME_TILE_MAX; i++)
7748 mapping_EM_to_RND[i] = EL_UNKNOWN;
7750 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7751 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7752 em_object_mapping_list[i].element_rnd;
7754 mapping_initialized = TRUE;
7757 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7759 Error(ERR_WARN, "invalid EM cave element %d", element_em_cave);
7764 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7767 int map_element_EM_to_RND_game(int element_em_game)
7769 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7770 static boolean mapping_initialized = FALSE;
7772 if (!mapping_initialized)
7776 // return "EL_UNKNOWN" for all undefined elements in mapping array
7777 for (i = 0; i < GAME_TILE_MAX; i++)
7778 mapping_EM_to_RND[i] = EL_UNKNOWN;
7780 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7781 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7782 em_object_mapping_list[i].element_rnd;
7784 mapping_initialized = TRUE;
7787 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7789 Error(ERR_WARN, "invalid EM game element %d", element_em_game);
7794 return mapping_EM_to_RND[element_em_game];
7797 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7799 struct LevelInfo_EM *level_em = level->native_em_level;
7800 struct CAVE *cav = level_em->cav;
7803 for (i = 0; i < GAME_TILE_MAX; i++)
7804 cav->android_array[i] = Cblank;
7806 for (i = 0; i < level->num_android_clone_elements; i++)
7808 int element_rnd = level->android_clone_element[i];
7809 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7811 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7812 if (em_object_mapping_list[j].element_rnd == element_rnd)
7813 cav->android_array[em_object_mapping_list[j].element_em] =
7818 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7820 struct LevelInfo_EM *level_em = level->native_em_level;
7821 struct CAVE *cav = level_em->cav;
7824 level->num_android_clone_elements = 0;
7826 for (i = 0; i < GAME_TILE_MAX; i++)
7828 int element_em_cave = cav->android_array[i];
7830 boolean element_found = FALSE;
7832 if (element_em_cave == Cblank)
7835 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7837 for (j = 0; j < level->num_android_clone_elements; j++)
7838 if (level->android_clone_element[j] == element_rnd)
7839 element_found = TRUE;
7843 level->android_clone_element[level->num_android_clone_elements++] =
7846 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7851 if (level->num_android_clone_elements == 0)
7853 level->num_android_clone_elements = 1;
7854 level->android_clone_element[0] = EL_EMPTY;
7858 int map_direction_RND_to_EM(int direction)
7860 return (direction == MV_UP ? 0 :
7861 direction == MV_RIGHT ? 1 :
7862 direction == MV_DOWN ? 2 :
7863 direction == MV_LEFT ? 3 :
7867 int map_direction_EM_to_RND(int direction)
7869 return (direction == 0 ? MV_UP :
7870 direction == 1 ? MV_RIGHT :
7871 direction == 2 ? MV_DOWN :
7872 direction == 3 ? MV_LEFT :
7876 int map_element_RND_to_SP(int element_rnd)
7878 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7880 if (element_rnd >= EL_SP_START &&
7881 element_rnd <= EL_SP_END)
7882 element_sp = element_rnd - EL_SP_START;
7883 else if (element_rnd == EL_EMPTY_SPACE)
7885 else if (element_rnd == EL_INVISIBLE_WALL)
7891 int map_element_SP_to_RND(int element_sp)
7893 int element_rnd = EL_UNKNOWN;
7895 if (element_sp >= 0x00 &&
7897 element_rnd = EL_SP_START + element_sp;
7898 else if (element_sp == 0x28)
7899 element_rnd = EL_INVISIBLE_WALL;
7904 int map_action_SP_to_RND(int action_sp)
7908 case actActive: return ACTION_ACTIVE;
7909 case actImpact: return ACTION_IMPACT;
7910 case actExploding: return ACTION_EXPLODING;
7911 case actDigging: return ACTION_DIGGING;
7912 case actSnapping: return ACTION_SNAPPING;
7913 case actCollecting: return ACTION_COLLECTING;
7914 case actPassing: return ACTION_PASSING;
7915 case actPushing: return ACTION_PUSHING;
7916 case actDropping: return ACTION_DROPPING;
7918 default: return ACTION_DEFAULT;
7922 int map_element_RND_to_MM(int element_rnd)
7924 return (element_rnd >= EL_MM_START_1 &&
7925 element_rnd <= EL_MM_END_1 ?
7926 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7928 element_rnd >= EL_MM_START_2 &&
7929 element_rnd <= EL_MM_END_2 ?
7930 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7932 element_rnd >= EL_CHAR_START &&
7933 element_rnd <= EL_CHAR_END ?
7934 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7936 element_rnd >= EL_MM_RUNTIME_START &&
7937 element_rnd <= EL_MM_RUNTIME_END ?
7938 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7940 element_rnd >= EL_MM_DUMMY_START &&
7941 element_rnd <= EL_MM_DUMMY_END ?
7942 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7944 EL_MM_EMPTY_NATIVE);
7947 int map_element_MM_to_RND(int element_mm)
7949 return (element_mm == EL_MM_EMPTY_NATIVE ||
7950 element_mm == EL_DF_EMPTY_NATIVE ?
7953 element_mm >= EL_MM_START_1_NATIVE &&
7954 element_mm <= EL_MM_END_1_NATIVE ?
7955 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7957 element_mm >= EL_MM_START_2_NATIVE &&
7958 element_mm <= EL_MM_END_2_NATIVE ?
7959 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7961 element_mm >= EL_MM_CHAR_START_NATIVE &&
7962 element_mm <= EL_MM_CHAR_END_NATIVE ?
7963 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7965 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7966 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7967 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7969 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7970 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7971 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7976 int map_action_MM_to_RND(int action_mm)
7978 // all MM actions are defined to exactly match their RND counterparts
7982 int map_sound_MM_to_RND(int sound_mm)
7986 case SND_MM_GAME_LEVELTIME_CHARGING:
7987 return SND_GAME_LEVELTIME_CHARGING;
7989 case SND_MM_GAME_HEALTH_CHARGING:
7990 return SND_GAME_HEALTH_CHARGING;
7993 return SND_UNDEFINED;
7997 int map_mm_wall_element(int element)
7999 return (element >= EL_MM_STEEL_WALL_START &&
8000 element <= EL_MM_STEEL_WALL_END ?
8003 element >= EL_MM_WOODEN_WALL_START &&
8004 element <= EL_MM_WOODEN_WALL_END ?
8007 element >= EL_MM_ICE_WALL_START &&
8008 element <= EL_MM_ICE_WALL_END ?
8011 element >= EL_MM_AMOEBA_WALL_START &&
8012 element <= EL_MM_AMOEBA_WALL_END ?
8015 element >= EL_DF_STEEL_WALL_START &&
8016 element <= EL_DF_STEEL_WALL_END ?
8019 element >= EL_DF_WOODEN_WALL_START &&
8020 element <= EL_DF_WOODEN_WALL_END ?
8026 int map_mm_wall_element_editor(int element)
8030 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8031 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8032 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8033 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8034 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8035 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8037 default: return element;
8041 int get_next_element(int element)
8045 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8046 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8047 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8048 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8049 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8050 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8051 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8052 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8053 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8054 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8055 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8057 default: return element;
8061 int el2img_mm(int element_mm)
8063 return el2img(map_element_MM_to_RND(element_mm));
8066 int el_act_dir2img(int element, int action, int direction)
8068 element = GFX_ELEMENT(element);
8069 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8071 // direction_graphic[][] == graphic[] for undefined direction graphics
8072 return element_info[element].direction_graphic[action][direction];
8075 static int el_act_dir2crm(int element, int action, int direction)
8077 element = GFX_ELEMENT(element);
8078 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8080 // direction_graphic[][] == graphic[] for undefined direction graphics
8081 return element_info[element].direction_crumbled[action][direction];
8084 int el_act2img(int element, int action)
8086 element = GFX_ELEMENT(element);
8088 return element_info[element].graphic[action];
8091 int el_act2crm(int element, int action)
8093 element = GFX_ELEMENT(element);
8095 return element_info[element].crumbled[action];
8098 int el_dir2img(int element, int direction)
8100 element = GFX_ELEMENT(element);
8102 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8105 int el2baseimg(int element)
8107 return element_info[element].graphic[ACTION_DEFAULT];
8110 int el2img(int element)
8112 element = GFX_ELEMENT(element);
8114 return element_info[element].graphic[ACTION_DEFAULT];
8117 int el2edimg(int element)
8119 element = GFX_ELEMENT(element);
8121 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8124 int el2preimg(int element)
8126 element = GFX_ELEMENT(element);
8128 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8131 int el2panelimg(int element)
8133 element = GFX_ELEMENT(element);
8135 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8138 int font2baseimg(int font_nr)
8140 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8143 int getBeltNrFromBeltElement(int element)
8145 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8146 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8147 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8150 int getBeltNrFromBeltActiveElement(int element)
8152 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8153 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8154 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8157 int getBeltNrFromBeltSwitchElement(int element)
8159 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8160 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8161 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8164 int getBeltDirNrFromBeltElement(int element)
8166 static int belt_base_element[4] =
8168 EL_CONVEYOR_BELT_1_LEFT,
8169 EL_CONVEYOR_BELT_2_LEFT,
8170 EL_CONVEYOR_BELT_3_LEFT,
8171 EL_CONVEYOR_BELT_4_LEFT
8174 int belt_nr = getBeltNrFromBeltElement(element);
8175 int belt_dir_nr = element - belt_base_element[belt_nr];
8177 return (belt_dir_nr % 3);
8180 int getBeltDirNrFromBeltSwitchElement(int element)
8182 static int belt_base_element[4] =
8184 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8185 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8186 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8187 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8190 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8191 int belt_dir_nr = element - belt_base_element[belt_nr];
8193 return (belt_dir_nr % 3);
8196 int getBeltDirFromBeltElement(int element)
8198 static int belt_move_dir[3] =
8205 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8207 return belt_move_dir[belt_dir_nr];
8210 int getBeltDirFromBeltSwitchElement(int element)
8212 static int belt_move_dir[3] =
8219 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8221 return belt_move_dir[belt_dir_nr];
8224 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8226 static int belt_base_element[4] =
8228 EL_CONVEYOR_BELT_1_LEFT,
8229 EL_CONVEYOR_BELT_2_LEFT,
8230 EL_CONVEYOR_BELT_3_LEFT,
8231 EL_CONVEYOR_BELT_4_LEFT
8234 return belt_base_element[belt_nr] + belt_dir_nr;
8237 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8239 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8241 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8244 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8246 static int belt_base_element[4] =
8248 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8249 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8250 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8251 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8254 return belt_base_element[belt_nr] + belt_dir_nr;
8257 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8259 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8261 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8264 boolean swapTiles_EM(boolean is_pre_emc_cave)
8266 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8269 boolean getTeamMode_EM(void)
8271 return game.team_mode || network_playing;
8274 boolean isActivePlayer_EM(int player_nr)
8276 return stored_player[player_nr].active;
8279 unsigned int InitRND(int seed)
8281 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8282 return InitEngineRandom_EM(seed);
8283 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8284 return InitEngineRandom_SP(seed);
8285 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8286 return InitEngineRandom_MM(seed);
8288 return InitEngineRandom_RND(seed);
8291 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8292 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8294 static int get_effective_element_EM(int tile, int frame_em)
8296 int element = object_mapping[tile].element_rnd;
8297 int action = object_mapping[tile].action;
8298 boolean is_backside = object_mapping[tile].is_backside;
8299 boolean action_removing = (action == ACTION_DIGGING ||
8300 action == ACTION_SNAPPING ||
8301 action == ACTION_COLLECTING);
8309 return (frame_em > 5 ? EL_EMPTY : element);
8315 else // frame_em == 7
8326 case Ydiamond_stone:
8330 case Xdrip_stretchB:
8346 case Ymagnify_blank:
8349 case Xsand_stonein_1:
8350 case Xsand_stonein_2:
8351 case Xsand_stonein_3:
8352 case Xsand_stonein_4:
8356 return (is_backside || action_removing ? EL_EMPTY : element);
8361 static boolean check_linear_animation_EM(int tile)
8365 case Xsand_stonesand_1:
8366 case Xsand_stonesand_quickout_1:
8367 case Xsand_sandstone_1:
8368 case Xsand_stonein_1:
8369 case Xsand_stoneout_1:
8397 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8398 boolean has_crumbled_graphics,
8399 int crumbled, int sync_frame)
8401 // if element can be crumbled, but certain action graphics are just empty
8402 // space (like instantly snapping sand to empty space in 1 frame), do not
8403 // treat these empty space graphics as crumbled graphics in EMC engine
8404 if (crumbled == IMG_EMPTY_SPACE)
8405 has_crumbled_graphics = FALSE;
8407 if (has_crumbled_graphics)
8409 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8410 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8411 g_crumbled->anim_delay,
8412 g_crumbled->anim_mode,
8413 g_crumbled->anim_start_frame,
8416 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8417 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8419 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8420 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8422 g_em->has_crumbled_graphics = TRUE;
8426 g_em->crumbled_bitmap = NULL;
8427 g_em->crumbled_src_x = 0;
8428 g_em->crumbled_src_y = 0;
8429 g_em->crumbled_border_size = 0;
8430 g_em->crumbled_tile_size = 0;
8432 g_em->has_crumbled_graphics = FALSE;
8437 void ResetGfxAnimation_EM(int x, int y, int tile)
8443 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8444 int tile, int frame_em, int x, int y)
8446 int action = object_mapping[tile].action;
8447 int direction = object_mapping[tile].direction;
8448 int effective_element = get_effective_element_EM(tile, frame_em);
8449 int graphic = (direction == MV_NONE ?
8450 el_act2img(effective_element, action) :
8451 el_act_dir2img(effective_element, action, direction));
8452 struct GraphicInfo *g = &graphic_info[graphic];
8454 boolean action_removing = (action == ACTION_DIGGING ||
8455 action == ACTION_SNAPPING ||
8456 action == ACTION_COLLECTING);
8457 boolean action_moving = (action == ACTION_FALLING ||
8458 action == ACTION_MOVING ||
8459 action == ACTION_PUSHING ||
8460 action == ACTION_EATING ||
8461 action == ACTION_FILLING ||
8462 action == ACTION_EMPTYING);
8463 boolean action_falling = (action == ACTION_FALLING ||
8464 action == ACTION_FILLING ||
8465 action == ACTION_EMPTYING);
8467 // special case: graphic uses "2nd movement tile" and has defined
8468 // 7 frames for movement animation (or less) => use default graphic
8469 // for last (8th) frame which ends the movement animation
8470 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8472 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8473 graphic = (direction == MV_NONE ?
8474 el_act2img(effective_element, action) :
8475 el_act_dir2img(effective_element, action, direction));
8477 g = &graphic_info[graphic];
8480 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8484 else if (action_moving)
8486 boolean is_backside = object_mapping[tile].is_backside;
8490 int direction = object_mapping[tile].direction;
8491 int move_dir = (action_falling ? MV_DOWN : direction);
8496 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8497 if (g->double_movement && frame_em == 0)
8501 if (move_dir == MV_LEFT)
8502 GfxFrame[x - 1][y] = GfxFrame[x][y];
8503 else if (move_dir == MV_RIGHT)
8504 GfxFrame[x + 1][y] = GfxFrame[x][y];
8505 else if (move_dir == MV_UP)
8506 GfxFrame[x][y - 1] = GfxFrame[x][y];
8507 else if (move_dir == MV_DOWN)
8508 GfxFrame[x][y + 1] = GfxFrame[x][y];
8515 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8516 if (tile == Xsand_stonesand_quickout_1 ||
8517 tile == Xsand_stonesand_quickout_2)
8521 if (graphic_info[graphic].anim_global_sync)
8522 sync_frame = FrameCounter;
8523 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8524 sync_frame = GfxFrame[x][y];
8526 sync_frame = 0; // playfield border (pseudo steel)
8528 SetRandomAnimationValue(x, y);
8530 int frame = getAnimationFrame(g->anim_frames,
8533 g->anim_start_frame,
8536 g_em->unique_identifier =
8537 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8540 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8541 int tile, int frame_em, int x, int y)
8543 int action = object_mapping[tile].action;
8544 int direction = object_mapping[tile].direction;
8545 boolean is_backside = object_mapping[tile].is_backside;
8546 int effective_element = get_effective_element_EM(tile, frame_em);
8547 int effective_action = action;
8548 int graphic = (direction == MV_NONE ?
8549 el_act2img(effective_element, effective_action) :
8550 el_act_dir2img(effective_element, effective_action,
8552 int crumbled = (direction == MV_NONE ?
8553 el_act2crm(effective_element, effective_action) :
8554 el_act_dir2crm(effective_element, effective_action,
8556 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8557 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8558 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8559 struct GraphicInfo *g = &graphic_info[graphic];
8562 // special case: graphic uses "2nd movement tile" and has defined
8563 // 7 frames for movement animation (or less) => use default graphic
8564 // for last (8th) frame which ends the movement animation
8565 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8567 effective_action = ACTION_DEFAULT;
8568 graphic = (direction == MV_NONE ?
8569 el_act2img(effective_element, effective_action) :
8570 el_act_dir2img(effective_element, effective_action,
8572 crumbled = (direction == MV_NONE ?
8573 el_act2crm(effective_element, effective_action) :
8574 el_act_dir2crm(effective_element, effective_action,
8577 g = &graphic_info[graphic];
8580 if (graphic_info[graphic].anim_global_sync)
8581 sync_frame = FrameCounter;
8582 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8583 sync_frame = GfxFrame[x][y];
8585 sync_frame = 0; // playfield border (pseudo steel)
8587 SetRandomAnimationValue(x, y);
8589 int frame = getAnimationFrame(g->anim_frames,
8592 g->anim_start_frame,
8595 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8596 g->double_movement && is_backside);
8598 // (updating the "crumbled" graphic definitions is probably not really needed,
8599 // as animations for crumbled graphics can't be longer than one EMC cycle)
8600 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8604 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8605 int player_nr, int anim, int frame_em)
8607 int element = player_mapping[player_nr][anim].element_rnd;
8608 int action = player_mapping[player_nr][anim].action;
8609 int direction = player_mapping[player_nr][anim].direction;
8610 int graphic = (direction == MV_NONE ?
8611 el_act2img(element, action) :
8612 el_act_dir2img(element, action, direction));
8613 struct GraphicInfo *g = &graphic_info[graphic];
8616 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8618 stored_player[player_nr].StepFrame = frame_em;
8620 sync_frame = stored_player[player_nr].Frame;
8622 int frame = getAnimationFrame(g->anim_frames,
8625 g->anim_start_frame,
8628 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8629 &g_em->src_x, &g_em->src_y, FALSE);
8632 void InitGraphicInfo_EM(void)
8636 // always start with reliable default values
8637 for (i = 0; i < GAME_TILE_MAX; i++)
8639 object_mapping[i].element_rnd = EL_UNKNOWN;
8640 object_mapping[i].is_backside = FALSE;
8641 object_mapping[i].action = ACTION_DEFAULT;
8642 object_mapping[i].direction = MV_NONE;
8645 // always start with reliable default values
8646 for (p = 0; p < MAX_PLAYERS; p++)
8648 for (i = 0; i < PLY_MAX; i++)
8650 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8651 player_mapping[p][i].action = ACTION_DEFAULT;
8652 player_mapping[p][i].direction = MV_NONE;
8656 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8658 int e = em_object_mapping_list[i].element_em;
8660 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8661 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8663 if (em_object_mapping_list[i].action != -1)
8664 object_mapping[e].action = em_object_mapping_list[i].action;
8666 if (em_object_mapping_list[i].direction != -1)
8667 object_mapping[e].direction =
8668 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8671 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8673 int a = em_player_mapping_list[i].action_em;
8674 int p = em_player_mapping_list[i].player_nr;
8676 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8678 if (em_player_mapping_list[i].action != -1)
8679 player_mapping[p][a].action = em_player_mapping_list[i].action;
8681 if (em_player_mapping_list[i].direction != -1)
8682 player_mapping[p][a].direction =
8683 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8686 for (i = 0; i < GAME_TILE_MAX; i++)
8688 int element = object_mapping[i].element_rnd;
8689 int action = object_mapping[i].action;
8690 int direction = object_mapping[i].direction;
8691 boolean is_backside = object_mapping[i].is_backside;
8692 boolean action_exploding = ((action == ACTION_EXPLODING ||
8693 action == ACTION_SMASHED_BY_ROCK ||
8694 action == ACTION_SMASHED_BY_SPRING) &&
8695 element != EL_DIAMOND);
8696 boolean action_active = (action == ACTION_ACTIVE);
8697 boolean action_other = (action == ACTION_OTHER);
8699 for (j = 0; j < 8; j++)
8701 int effective_element = get_effective_element_EM(i, j);
8702 int effective_action = (j < 7 ? action :
8703 i == Xdrip_stretch ? action :
8704 i == Xdrip_stretchB ? action :
8705 i == Ydrip_1_s ? action :
8706 i == Ydrip_1_sB ? action :
8707 i == Yball_1 ? action :
8708 i == Xball_2 ? action :
8709 i == Yball_2 ? action :
8710 i == Yball_blank ? action :
8711 i == Ykey_1_blank ? action :
8712 i == Ykey_2_blank ? action :
8713 i == Ykey_3_blank ? action :
8714 i == Ykey_4_blank ? action :
8715 i == Ykey_5_blank ? action :
8716 i == Ykey_6_blank ? action :
8717 i == Ykey_7_blank ? action :
8718 i == Ykey_8_blank ? action :
8719 i == Ylenses_blank ? action :
8720 i == Ymagnify_blank ? action :
8721 i == Ygrass_blank ? action :
8722 i == Ydirt_blank ? action :
8723 i == Xsand_stonein_1 ? action :
8724 i == Xsand_stonein_2 ? action :
8725 i == Xsand_stonein_3 ? action :
8726 i == Xsand_stonein_4 ? action :
8727 i == Xsand_stoneout_1 ? action :
8728 i == Xsand_stoneout_2 ? action :
8729 i == Xboom_android ? ACTION_EXPLODING :
8730 action_exploding ? ACTION_EXPLODING :
8731 action_active ? action :
8732 action_other ? action :
8734 int graphic = (el_act_dir2img(effective_element, effective_action,
8736 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8738 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8739 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8740 boolean has_action_graphics = (graphic != base_graphic);
8741 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8742 struct GraphicInfo *g = &graphic_info[graphic];
8743 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8746 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8747 boolean special_animation = (action != ACTION_DEFAULT &&
8748 g->anim_frames == 3 &&
8749 g->anim_delay == 2 &&
8750 g->anim_mode & ANIM_LINEAR);
8751 int sync_frame = (i == Xdrip_stretch ? 7 :
8752 i == Xdrip_stretchB ? 7 :
8753 i == Ydrip_2_s ? j + 8 :
8754 i == Ydrip_2_sB ? j + 8 :
8763 i == Xfake_acid_1 ? 0 :
8764 i == Xfake_acid_2 ? 10 :
8765 i == Xfake_acid_3 ? 20 :
8766 i == Xfake_acid_4 ? 30 :
8767 i == Xfake_acid_5 ? 40 :
8768 i == Xfake_acid_6 ? 50 :
8769 i == Xfake_acid_7 ? 60 :
8770 i == Xfake_acid_8 ? 70 :
8772 i == Yball_2 ? j + 8 :
8773 i == Yball_blank ? j + 1 :
8774 i == Ykey_1_blank ? j + 1 :
8775 i == Ykey_2_blank ? j + 1 :
8776 i == Ykey_3_blank ? j + 1 :
8777 i == Ykey_4_blank ? j + 1 :
8778 i == Ykey_5_blank ? j + 1 :
8779 i == Ykey_6_blank ? j + 1 :
8780 i == Ykey_7_blank ? j + 1 :
8781 i == Ykey_8_blank ? j + 1 :
8782 i == Ylenses_blank ? j + 1 :
8783 i == Ymagnify_blank ? j + 1 :
8784 i == Ygrass_blank ? j + 1 :
8785 i == Ydirt_blank ? j + 1 :
8786 i == Xamoeba_1 ? 0 :
8787 i == Xamoeba_2 ? 1 :
8788 i == Xamoeba_3 ? 2 :
8789 i == Xamoeba_4 ? 3 :
8790 i == Xamoeba_5 ? 0 :
8791 i == Xamoeba_6 ? 1 :
8792 i == Xamoeba_7 ? 2 :
8793 i == Xamoeba_8 ? 3 :
8794 i == Xexit_2 ? j + 8 :
8795 i == Xexit_3 ? j + 16 :
8796 i == Xdynamite_1 ? 0 :
8797 i == Xdynamite_2 ? 8 :
8798 i == Xdynamite_3 ? 16 :
8799 i == Xdynamite_4 ? 24 :
8800 i == Xsand_stonein_1 ? j + 1 :
8801 i == Xsand_stonein_2 ? j + 9 :
8802 i == Xsand_stonein_3 ? j + 17 :
8803 i == Xsand_stonein_4 ? j + 25 :
8804 i == Xsand_stoneout_1 && j == 0 ? 0 :
8805 i == Xsand_stoneout_1 && j == 1 ? 0 :
8806 i == Xsand_stoneout_1 && j == 2 ? 1 :
8807 i == Xsand_stoneout_1 && j == 3 ? 2 :
8808 i == Xsand_stoneout_1 && j == 4 ? 2 :
8809 i == Xsand_stoneout_1 && j == 5 ? 3 :
8810 i == Xsand_stoneout_1 && j == 6 ? 4 :
8811 i == Xsand_stoneout_1 && j == 7 ? 4 :
8812 i == Xsand_stoneout_2 && j == 0 ? 5 :
8813 i == Xsand_stoneout_2 && j == 1 ? 6 :
8814 i == Xsand_stoneout_2 && j == 2 ? 7 :
8815 i == Xsand_stoneout_2 && j == 3 ? 8 :
8816 i == Xsand_stoneout_2 && j == 4 ? 9 :
8817 i == Xsand_stoneout_2 && j == 5 ? 11 :
8818 i == Xsand_stoneout_2 && j == 6 ? 13 :
8819 i == Xsand_stoneout_2 && j == 7 ? 15 :
8820 i == Xboom_bug && j == 1 ? 2 :
8821 i == Xboom_bug && j == 2 ? 2 :
8822 i == Xboom_bug && j == 3 ? 4 :
8823 i == Xboom_bug && j == 4 ? 4 :
8824 i == Xboom_bug && j == 5 ? 2 :
8825 i == Xboom_bug && j == 6 ? 2 :
8826 i == Xboom_bug && j == 7 ? 0 :
8827 i == Xboom_tank && j == 1 ? 2 :
8828 i == Xboom_tank && j == 2 ? 2 :
8829 i == Xboom_tank && j == 3 ? 4 :
8830 i == Xboom_tank && j == 4 ? 4 :
8831 i == Xboom_tank && j == 5 ? 2 :
8832 i == Xboom_tank && j == 6 ? 2 :
8833 i == Xboom_tank && j == 7 ? 0 :
8834 i == Xboom_android && j == 7 ? 6 :
8835 i == Xboom_1 && j == 1 ? 2 :
8836 i == Xboom_1 && j == 2 ? 2 :
8837 i == Xboom_1 && j == 3 ? 4 :
8838 i == Xboom_1 && j == 4 ? 4 :
8839 i == Xboom_1 && j == 5 ? 6 :
8840 i == Xboom_1 && j == 6 ? 6 :
8841 i == Xboom_1 && j == 7 ? 8 :
8842 i == Xboom_2 && j == 0 ? 8 :
8843 i == Xboom_2 && j == 1 ? 8 :
8844 i == Xboom_2 && j == 2 ? 10 :
8845 i == Xboom_2 && j == 3 ? 10 :
8846 i == Xboom_2 && j == 4 ? 10 :
8847 i == Xboom_2 && j == 5 ? 12 :
8848 i == Xboom_2 && j == 6 ? 12 :
8849 i == Xboom_2 && j == 7 ? 12 :
8850 special_animation && j == 4 ? 3 :
8851 effective_action != action ? 0 :
8853 int frame = getAnimationFrame(g->anim_frames,
8856 g->anim_start_frame,
8859 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8860 g->double_movement && is_backside);
8862 g_em->bitmap = src_bitmap;
8863 g_em->src_x = src_x;
8864 g_em->src_y = src_y;
8865 g_em->src_offset_x = 0;
8866 g_em->src_offset_y = 0;
8867 g_em->dst_offset_x = 0;
8868 g_em->dst_offset_y = 0;
8869 g_em->width = TILEX;
8870 g_em->height = TILEY;
8872 g_em->preserve_background = FALSE;
8874 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8877 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8878 effective_action == ACTION_MOVING ||
8879 effective_action == ACTION_PUSHING ||
8880 effective_action == ACTION_EATING)) ||
8881 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8882 effective_action == ACTION_EMPTYING)))
8885 (effective_action == ACTION_FALLING ||
8886 effective_action == ACTION_FILLING ||
8887 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8888 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8889 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8890 int num_steps = (i == Ydrip_1_s ? 16 :
8891 i == Ydrip_1_sB ? 16 :
8892 i == Ydrip_2_s ? 16 :
8893 i == Ydrip_2_sB ? 16 :
8894 i == Xsand_stonein_1 ? 32 :
8895 i == Xsand_stonein_2 ? 32 :
8896 i == Xsand_stonein_3 ? 32 :
8897 i == Xsand_stonein_4 ? 32 :
8898 i == Xsand_stoneout_1 ? 16 :
8899 i == Xsand_stoneout_2 ? 16 : 8);
8900 int cx = ABS(dx) * (TILEX / num_steps);
8901 int cy = ABS(dy) * (TILEY / num_steps);
8902 int step_frame = (i == Ydrip_2_s ? j + 8 :
8903 i == Ydrip_2_sB ? j + 8 :
8904 i == Xsand_stonein_2 ? j + 8 :
8905 i == Xsand_stonein_3 ? j + 16 :
8906 i == Xsand_stonein_4 ? j + 24 :
8907 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8908 int step = (is_backside ? step_frame : num_steps - step_frame);
8910 if (is_backside) // tile where movement starts
8912 if (dx < 0 || dy < 0)
8914 g_em->src_offset_x = cx * step;
8915 g_em->src_offset_y = cy * step;
8919 g_em->dst_offset_x = cx * step;
8920 g_em->dst_offset_y = cy * step;
8923 else // tile where movement ends
8925 if (dx < 0 || dy < 0)
8927 g_em->dst_offset_x = cx * step;
8928 g_em->dst_offset_y = cy * step;
8932 g_em->src_offset_x = cx * step;
8933 g_em->src_offset_y = cy * step;
8937 g_em->width = TILEX - cx * step;
8938 g_em->height = TILEY - cy * step;
8941 // create unique graphic identifier to decide if tile must be redrawn
8942 /* bit 31 - 16 (16 bit): EM style graphic
8943 bit 15 - 12 ( 4 bit): EM style frame
8944 bit 11 - 6 ( 6 bit): graphic width
8945 bit 5 - 0 ( 6 bit): graphic height */
8946 g_em->unique_identifier =
8947 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8951 for (i = 0; i < GAME_TILE_MAX; i++)
8953 for (j = 0; j < 8; j++)
8955 int element = object_mapping[i].element_rnd;
8956 int action = object_mapping[i].action;
8957 int direction = object_mapping[i].direction;
8958 boolean is_backside = object_mapping[i].is_backside;
8959 int graphic_action = el_act_dir2img(element, action, direction);
8960 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8962 if ((action == ACTION_SMASHED_BY_ROCK ||
8963 action == ACTION_SMASHED_BY_SPRING ||
8964 action == ACTION_EATING) &&
8965 graphic_action == graphic_default)
8967 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8968 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8969 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8970 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8973 // no separate animation for "smashed by rock" -- use rock instead
8974 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8975 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8977 g_em->bitmap = g_xx->bitmap;
8978 g_em->src_x = g_xx->src_x;
8979 g_em->src_y = g_xx->src_y;
8980 g_em->src_offset_x = g_xx->src_offset_x;
8981 g_em->src_offset_y = g_xx->src_offset_y;
8982 g_em->dst_offset_x = g_xx->dst_offset_x;
8983 g_em->dst_offset_y = g_xx->dst_offset_y;
8984 g_em->width = g_xx->width;
8985 g_em->height = g_xx->height;
8986 g_em->unique_identifier = g_xx->unique_identifier;
8989 g_em->preserve_background = TRUE;
8994 for (p = 0; p < MAX_PLAYERS; p++)
8996 for (i = 0; i < PLY_MAX; i++)
8998 int element = player_mapping[p][i].element_rnd;
8999 int action = player_mapping[p][i].action;
9000 int direction = player_mapping[p][i].direction;
9002 for (j = 0; j < 8; j++)
9004 int effective_element = element;
9005 int effective_action = action;
9006 int graphic = (direction == MV_NONE ?
9007 el_act2img(effective_element, effective_action) :
9008 el_act_dir2img(effective_element, effective_action,
9010 struct GraphicInfo *g = &graphic_info[graphic];
9011 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9015 int frame = getAnimationFrame(g->anim_frames,
9018 g->anim_start_frame,
9021 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9023 g_em->bitmap = src_bitmap;
9024 g_em->src_x = src_x;
9025 g_em->src_y = src_y;
9026 g_em->src_offset_x = 0;
9027 g_em->src_offset_y = 0;
9028 g_em->dst_offset_x = 0;
9029 g_em->dst_offset_y = 0;
9030 g_em->width = TILEX;
9031 g_em->height = TILEY;
9037 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9038 boolean any_player_moving,
9039 boolean any_player_snapping,
9040 boolean any_player_dropping)
9042 if (frame == 7 && !any_player_dropping)
9044 if (!local_player->was_waiting)
9046 if (!CheckSaveEngineSnapshotToList())
9049 local_player->was_waiting = TRUE;
9052 else if (any_player_moving || any_player_snapping || any_player_dropping)
9054 local_player->was_waiting = FALSE;
9058 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9059 boolean murphy_is_dropping)
9061 if (murphy_is_waiting)
9063 if (!local_player->was_waiting)
9065 if (!CheckSaveEngineSnapshotToList())
9068 local_player->was_waiting = TRUE;
9073 local_player->was_waiting = FALSE;
9077 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9078 boolean button_released)
9080 if (button_released)
9082 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9083 CheckSaveEngineSnapshotToList();
9085 else if (element_clicked)
9087 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9088 CheckSaveEngineSnapshotToList();
9090 game.snapshot.changed_action = TRUE;
9094 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9095 boolean any_player_moving,
9096 boolean any_player_snapping,
9097 boolean any_player_dropping)
9099 if (tape.single_step && tape.recording && !tape.pausing)
9100 if (frame == 7 && !any_player_dropping)
9101 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9103 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9104 any_player_snapping, any_player_dropping);
9107 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9108 boolean murphy_is_dropping)
9110 boolean murphy_starts_dropping = FALSE;
9113 for (i = 0; i < MAX_PLAYERS; i++)
9114 if (stored_player[i].force_dropping)
9115 murphy_starts_dropping = TRUE;
9117 if (tape.single_step && tape.recording && !tape.pausing)
9118 if (murphy_is_waiting && !murphy_starts_dropping)
9119 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9121 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9124 void CheckSingleStepMode_MM(boolean element_clicked,
9125 boolean button_released)
9127 if (tape.single_step && tape.recording && !tape.pausing)
9128 if (button_released)
9129 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9131 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9134 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9135 int graphic, int sync_frame, int x, int y)
9137 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9139 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9142 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9144 return (IS_NEXT_FRAME(sync_frame, graphic));
9147 int getGraphicInfo_Delay(int graphic)
9149 return graphic_info[graphic].anim_delay;
9152 void PlayMenuSoundExt(int sound)
9154 if (sound == SND_UNDEFINED)
9157 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9158 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9161 if (IS_LOOP_SOUND(sound))
9162 PlaySoundLoop(sound);
9167 void PlayMenuSound(void)
9169 PlayMenuSoundExt(menu.sound[game_status]);
9172 void PlayMenuSoundStereo(int sound, int stereo_position)
9174 if (sound == SND_UNDEFINED)
9177 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9178 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9181 if (IS_LOOP_SOUND(sound))
9182 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9184 PlaySoundStereo(sound, stereo_position);
9187 void PlayMenuSoundIfLoopExt(int sound)
9189 if (sound == SND_UNDEFINED)
9192 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9193 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9196 if (IS_LOOP_SOUND(sound))
9197 PlaySoundLoop(sound);
9200 void PlayMenuSoundIfLoop(void)
9202 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9205 void PlayMenuMusicExt(int music)
9207 if (music == MUS_UNDEFINED)
9210 if (!setup.sound_music)
9213 if (IS_LOOP_MUSIC(music))
9214 PlayMusicLoop(music);
9219 void PlayMenuMusic(void)
9221 char *curr_music = getCurrentlyPlayingMusicFilename();
9222 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9224 if (!strEqual(curr_music, next_music))
9225 PlayMenuMusicExt(menu.music[game_status]);
9228 void PlayMenuSoundsAndMusic(void)
9234 static void FadeMenuSounds(void)
9239 static void FadeMenuMusic(void)
9241 char *curr_music = getCurrentlyPlayingMusicFilename();
9242 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9244 if (!strEqual(curr_music, next_music))
9248 void FadeMenuSoundsAndMusic(void)
9254 void PlaySoundActivating(void)
9257 PlaySound(SND_MENU_ITEM_ACTIVATING);
9261 void PlaySoundSelecting(void)
9264 PlaySound(SND_MENU_ITEM_SELECTING);
9268 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9270 boolean change_fullscreen = (setup.fullscreen !=
9271 video.fullscreen_enabled);
9272 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9273 setup.window_scaling_percent !=
9274 video.window_scaling_percent);
9276 if (change_window_scaling_percent && video.fullscreen_enabled)
9279 if (!change_window_scaling_percent && !video.fullscreen_available)
9282 if (change_window_scaling_percent)
9284 SDLSetWindowScaling(setup.window_scaling_percent);
9288 else if (change_fullscreen)
9290 SDLSetWindowFullscreen(setup.fullscreen);
9292 // set setup value according to successfully changed fullscreen mode
9293 setup.fullscreen = video.fullscreen_enabled;
9298 if (change_fullscreen ||
9299 change_window_scaling_percent)
9301 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9303 // save backbuffer content which gets lost when toggling fullscreen mode
9304 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9306 if (change_window_scaling_percent)
9308 // keep window mode, but change window scaling
9309 video.fullscreen_enabled = TRUE; // force new window scaling
9312 // toggle fullscreen
9313 ChangeVideoModeIfNeeded(setup.fullscreen);
9315 // set setup value according to successfully changed fullscreen mode
9316 setup.fullscreen = video.fullscreen_enabled;
9318 // restore backbuffer content from temporary backbuffer backup bitmap
9319 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9321 FreeBitmap(tmp_backbuffer);
9323 // update visible window/screen
9324 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9328 static void JoinRectangles(int *x, int *y, int *width, int *height,
9329 int x2, int y2, int width2, int height2)
9331 // do not join with "off-screen" rectangle
9332 if (x2 == -1 || y2 == -1)
9337 *width = MAX(*width, width2);
9338 *height = MAX(*height, height2);
9341 void SetAnimStatus(int anim_status_new)
9343 if (anim_status_new == GAME_MODE_MAIN)
9344 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9345 else if (anim_status_new == GAME_MODE_SCORES)
9346 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9348 global.anim_status_next = anim_status_new;
9350 // directly set screen modes that are entered without fading
9351 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9352 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9353 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9354 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9355 global.anim_status = global.anim_status_next;
9358 void SetGameStatus(int game_status_new)
9360 if (game_status_new != game_status)
9361 game_status_last_screen = game_status;
9363 game_status = game_status_new;
9365 SetAnimStatus(game_status_new);
9368 void SetFontStatus(int game_status_new)
9370 static int last_game_status = -1;
9372 if (game_status_new != -1)
9374 // set game status for font use after storing last game status
9375 last_game_status = game_status;
9376 game_status = game_status_new;
9380 // reset game status after font use from last stored game status
9381 game_status = last_game_status;
9385 void ResetFontStatus(void)
9390 void SetLevelSetInfo(char *identifier, int level_nr)
9392 setString(&levelset.identifier, identifier);
9394 levelset.level_nr = level_nr;
9397 boolean CheckIfAllViewportsHaveChanged(void)
9399 // if game status has not changed, viewports have not changed either
9400 if (game_status == game_status_last)
9403 // check if all viewports have changed with current game status
9405 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9406 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9407 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9408 int new_real_sx = vp_playfield->x;
9409 int new_real_sy = vp_playfield->y;
9410 int new_full_sxsize = vp_playfield->width;
9411 int new_full_sysize = vp_playfield->height;
9412 int new_dx = vp_door_1->x;
9413 int new_dy = vp_door_1->y;
9414 int new_dxsize = vp_door_1->width;
9415 int new_dysize = vp_door_1->height;
9416 int new_vx = vp_door_2->x;
9417 int new_vy = vp_door_2->y;
9418 int new_vxsize = vp_door_2->width;
9419 int new_vysize = vp_door_2->height;
9421 boolean playfield_viewport_has_changed =
9422 (new_real_sx != REAL_SX ||
9423 new_real_sy != REAL_SY ||
9424 new_full_sxsize != FULL_SXSIZE ||
9425 new_full_sysize != FULL_SYSIZE);
9427 boolean door_1_viewport_has_changed =
9430 new_dxsize != DXSIZE ||
9431 new_dysize != DYSIZE);
9433 boolean door_2_viewport_has_changed =
9436 new_vxsize != VXSIZE ||
9437 new_vysize != VYSIZE ||
9438 game_status_last == GAME_MODE_EDITOR);
9440 return (playfield_viewport_has_changed &&
9441 door_1_viewport_has_changed &&
9442 door_2_viewport_has_changed);
9445 boolean CheckFadeAll(void)
9447 return (CheckIfGlobalBorderHasChanged() ||
9448 CheckIfAllViewportsHaveChanged());
9451 void ChangeViewportPropertiesIfNeeded(void)
9453 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9454 FALSE : setup.small_game_graphics);
9455 int gfx_game_mode = game_status;
9456 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9458 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9459 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9460 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9461 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9462 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9463 int new_win_xsize = vp_window->width;
9464 int new_win_ysize = vp_window->height;
9465 int border_left = vp_playfield->border_left;
9466 int border_right = vp_playfield->border_right;
9467 int border_top = vp_playfield->border_top;
9468 int border_bottom = vp_playfield->border_bottom;
9469 int new_sx = vp_playfield->x + border_left;
9470 int new_sy = vp_playfield->y + border_top;
9471 int new_sxsize = vp_playfield->width - border_left - border_right;
9472 int new_sysize = vp_playfield->height - border_top - border_bottom;
9473 int new_real_sx = vp_playfield->x;
9474 int new_real_sy = vp_playfield->y;
9475 int new_full_sxsize = vp_playfield->width;
9476 int new_full_sysize = vp_playfield->height;
9477 int new_dx = vp_door_1->x;
9478 int new_dy = vp_door_1->y;
9479 int new_dxsize = vp_door_1->width;
9480 int new_dysize = vp_door_1->height;
9481 int new_vx = vp_door_2->x;
9482 int new_vy = vp_door_2->y;
9483 int new_vxsize = vp_door_2->width;
9484 int new_vysize = vp_door_2->height;
9485 int new_ex = vp_door_3->x;
9486 int new_ey = vp_door_3->y;
9487 int new_exsize = vp_door_3->width;
9488 int new_eysize = vp_door_3->height;
9489 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9490 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9491 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9492 int new_scr_fieldx = new_sxsize / tilesize;
9493 int new_scr_fieldy = new_sysize / tilesize;
9494 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9495 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9496 boolean init_gfx_buffers = FALSE;
9497 boolean init_video_buffer = FALSE;
9498 boolean init_gadgets_and_anims = FALSE;
9499 boolean init_em_graphics = FALSE;
9501 if (new_win_xsize != WIN_XSIZE ||
9502 new_win_ysize != WIN_YSIZE)
9504 WIN_XSIZE = new_win_xsize;
9505 WIN_YSIZE = new_win_ysize;
9507 init_video_buffer = TRUE;
9508 init_gfx_buffers = TRUE;
9509 init_gadgets_and_anims = TRUE;
9511 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9514 if (new_scr_fieldx != SCR_FIELDX ||
9515 new_scr_fieldy != SCR_FIELDY)
9517 // this always toggles between MAIN and GAME when using small tile size
9519 SCR_FIELDX = new_scr_fieldx;
9520 SCR_FIELDY = new_scr_fieldy;
9522 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9533 new_sxsize != SXSIZE ||
9534 new_sysize != SYSIZE ||
9535 new_dxsize != DXSIZE ||
9536 new_dysize != DYSIZE ||
9537 new_vxsize != VXSIZE ||
9538 new_vysize != VYSIZE ||
9539 new_exsize != EXSIZE ||
9540 new_eysize != EYSIZE ||
9541 new_real_sx != REAL_SX ||
9542 new_real_sy != REAL_SY ||
9543 new_full_sxsize != FULL_SXSIZE ||
9544 new_full_sysize != FULL_SYSIZE ||
9545 new_tilesize_var != TILESIZE_VAR
9548 // ------------------------------------------------------------------------
9549 // determine next fading area for changed viewport definitions
9550 // ------------------------------------------------------------------------
9552 // start with current playfield area (default fading area)
9555 FADE_SXSIZE = FULL_SXSIZE;
9556 FADE_SYSIZE = FULL_SYSIZE;
9558 // add new playfield area if position or size has changed
9559 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9560 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9562 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9563 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9566 // add current and new door 1 area if position or size has changed
9567 if (new_dx != DX || new_dy != DY ||
9568 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9570 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9571 DX, DY, DXSIZE, DYSIZE);
9572 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9573 new_dx, new_dy, new_dxsize, new_dysize);
9576 // add current and new door 2 area if position or size has changed
9577 if (new_vx != VX || new_vy != VY ||
9578 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9580 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9581 VX, VY, VXSIZE, VYSIZE);
9582 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9583 new_vx, new_vy, new_vxsize, new_vysize);
9586 // ------------------------------------------------------------------------
9587 // handle changed tile size
9588 // ------------------------------------------------------------------------
9590 if (new_tilesize_var != TILESIZE_VAR)
9592 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9594 // changing tile size invalidates scroll values of engine snapshots
9595 FreeEngineSnapshotSingle();
9597 // changing tile size requires update of graphic mapping for EM engine
9598 init_em_graphics = TRUE;
9609 SXSIZE = new_sxsize;
9610 SYSIZE = new_sysize;
9611 DXSIZE = new_dxsize;
9612 DYSIZE = new_dysize;
9613 VXSIZE = new_vxsize;
9614 VYSIZE = new_vysize;
9615 EXSIZE = new_exsize;
9616 EYSIZE = new_eysize;
9617 REAL_SX = new_real_sx;
9618 REAL_SY = new_real_sy;
9619 FULL_SXSIZE = new_full_sxsize;
9620 FULL_SYSIZE = new_full_sysize;
9621 TILESIZE_VAR = new_tilesize_var;
9623 init_gfx_buffers = TRUE;
9624 init_gadgets_and_anims = TRUE;
9626 // printf("::: viewports: init_gfx_buffers\n");
9627 // printf("::: viewports: init_gadgets_and_anims\n");
9630 if (init_gfx_buffers)
9632 // printf("::: init_gfx_buffers\n");
9634 SCR_FIELDX = new_scr_fieldx_buffers;
9635 SCR_FIELDY = new_scr_fieldy_buffers;
9639 SCR_FIELDX = new_scr_fieldx;
9640 SCR_FIELDY = new_scr_fieldy;
9642 SetDrawDeactivationMask(REDRAW_NONE);
9643 SetDrawBackgroundMask(REDRAW_FIELD);
9646 if (init_video_buffer)
9648 // printf("::: init_video_buffer\n");
9650 FreeAllImageTextures(); // needs old renderer to free the textures
9652 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9653 InitImageTextures();
9656 if (init_gadgets_and_anims)
9658 // printf("::: init_gadgets_and_anims\n");
9661 InitGlobalAnimations();
9664 if (init_em_graphics)
9666 InitGraphicInfo_EM();