1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 void DumpTile(int x, int y)
401 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
404 if (!IN_LEV_FIELD(x, y))
406 Info("(not in level field)");
412 token_name = element_info[Tile[x][y]].token_name;
414 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
415 Info("Back: %s", print_if_not_empty(Back[x][y]));
416 Info("Store: %s", print_if_not_empty(Store[x][y]));
417 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
418 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
419 Info("MovPos: %d", MovPos[x][y]);
420 Info("MovDir: %d", MovDir[x][y]);
421 Info("MovDelay: %d", MovDelay[x][y]);
422 Info("ChangeDelay: %d", ChangeDelay[x][y]);
423 Info("CustomValue: %d", CustomValue[x][y]);
424 Info("GfxElement: %d", GfxElement[x][y]);
425 Info("GfxAction: %d", GfxAction[x][y]);
426 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
427 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
431 void DumpTileFromScreen(int sx, int sy)
433 int lx = getLevelFromScreenX(sx);
434 int ly = getLevelFromScreenY(sy);
439 void SetDrawtoField(int mode)
441 if (mode == DRAW_TO_FIELDBUFFER)
447 BX2 = SCR_FIELDX + 1;
448 BY2 = SCR_FIELDY + 1;
450 drawto_field = fieldbuffer;
452 else // DRAW_TO_BACKBUFFER
458 BX2 = SCR_FIELDX - 1;
459 BY2 = SCR_FIELDY - 1;
461 drawto_field = backbuffer;
465 static void RedrawPlayfield_RND(void)
467 if (game.envelope_active)
470 DrawLevel(REDRAW_ALL);
474 void RedrawPlayfield(void)
476 if (game_status != GAME_MODE_PLAYING)
479 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
480 RedrawPlayfield_EM(TRUE);
481 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
482 RedrawPlayfield_SP(TRUE);
483 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
484 RedrawPlayfield_MM();
485 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
486 RedrawPlayfield_RND();
488 BlitScreenToBitmap(backbuffer);
490 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
494 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
497 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
498 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
500 if (x == -1 && y == -1)
503 if (draw_target == DRAW_TO_SCREEN)
504 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
506 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
509 static void DrawMaskedBorderExt_FIELD(int draw_target)
511 if (global.border_status >= GAME_MODE_MAIN &&
512 global.border_status <= GAME_MODE_PLAYING &&
513 border.draw_masked[global.border_status])
514 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
518 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
520 // when drawing to backbuffer, never draw border over open doors
521 if (draw_target == DRAW_TO_BACKBUFFER &&
522 (GetDoorState() & DOOR_OPEN_1))
525 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
526 (global.border_status != GAME_MODE_EDITOR ||
527 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
528 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
531 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
533 // when drawing to backbuffer, never draw border over open doors
534 if (draw_target == DRAW_TO_BACKBUFFER &&
535 (GetDoorState() & DOOR_OPEN_2))
538 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
539 global.border_status != GAME_MODE_EDITOR)
540 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
543 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
545 // currently not available
548 static void DrawMaskedBorderExt_ALL(int draw_target)
550 DrawMaskedBorderExt_FIELD(draw_target);
551 DrawMaskedBorderExt_DOOR_1(draw_target);
552 DrawMaskedBorderExt_DOOR_2(draw_target);
553 DrawMaskedBorderExt_DOOR_3(draw_target);
556 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
558 // never draw masked screen borders on borderless screens
559 if (global.border_status == GAME_MODE_LOADING ||
560 global.border_status == GAME_MODE_TITLE)
563 if (redraw_mask & REDRAW_ALL)
564 DrawMaskedBorderExt_ALL(draw_target);
567 if (redraw_mask & REDRAW_FIELD)
568 DrawMaskedBorderExt_FIELD(draw_target);
569 if (redraw_mask & REDRAW_DOOR_1)
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 if (redraw_mask & REDRAW_DOOR_2)
572 DrawMaskedBorderExt_DOOR_2(draw_target);
573 if (redraw_mask & REDRAW_DOOR_3)
574 DrawMaskedBorderExt_DOOR_3(draw_target);
578 void DrawMaskedBorder_FIELD(void)
580 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
583 void DrawMaskedBorder(int redraw_mask)
585 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
588 void DrawMaskedBorderToTarget(int draw_target)
590 if (draw_target == DRAW_TO_BACKBUFFER ||
591 draw_target == DRAW_TO_SCREEN)
593 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
597 int last_border_status = global.border_status;
599 if (draw_target == DRAW_TO_FADE_SOURCE)
601 global.border_status = gfx.fade_border_source_status;
602 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
604 else if (draw_target == DRAW_TO_FADE_TARGET)
606 global.border_status = gfx.fade_border_target_status;
607 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 global.border_status = last_border_status;
613 gfx.masked_border_bitmap_ptr = backbuffer;
617 void DrawTileCursor(int draw_target)
623 int graphic = IMG_GLOBAL_TILE_CURSOR;
625 int tilesize = TILESIZE_VAR;
626 int width = tilesize;
627 int height = tilesize;
629 if (game_status != GAME_MODE_PLAYING)
632 if (!tile_cursor.enabled ||
636 if (tile_cursor.moving)
638 int step = TILESIZE_VAR / 4;
639 int dx = tile_cursor.target_x - tile_cursor.x;
640 int dy = tile_cursor.target_y - tile_cursor.y;
643 tile_cursor.x = tile_cursor.target_x;
645 tile_cursor.x += SIGN(dx) * step;
648 tile_cursor.y = tile_cursor.target_y;
650 tile_cursor.y += SIGN(dy) * step;
652 if (tile_cursor.x == tile_cursor.target_x &&
653 tile_cursor.y == tile_cursor.target_y)
654 tile_cursor.moving = FALSE;
657 dst_x = tile_cursor.x;
658 dst_y = tile_cursor.y;
660 frame = getGraphicAnimationFrame(graphic, -1);
662 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
665 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
666 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
668 if (draw_target == DRAW_TO_SCREEN)
669 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
671 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
675 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
677 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
680 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
682 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
683 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
685 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
688 void BlitScreenToBitmap(Bitmap *target_bitmap)
690 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
691 BlitScreenToBitmap_EM(target_bitmap);
692 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
693 BlitScreenToBitmap_SP(target_bitmap);
694 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
695 BlitScreenToBitmap_MM(target_bitmap);
696 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
697 BlitScreenToBitmap_RND(target_bitmap);
699 redraw_mask |= REDRAW_FIELD;
702 static void DrawFramesPerSecond(void)
705 int font_nr = FONT_TEXT_2;
706 int font_width = getFontWidth(font_nr);
707 int draw_deactivation_mask = GetDrawDeactivationMask();
708 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
710 // draw FPS with leading space (needed if field buffer deactivated)
711 sprintf(text, " %04.1f fps", global.frames_per_second);
713 // override draw deactivation mask (required for invisible warp mode)
714 SetDrawDeactivationMask(REDRAW_NONE);
716 // draw opaque FPS if field buffer deactivated, else draw masked FPS
717 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
718 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
720 // set draw deactivation mask to previous value
721 SetDrawDeactivationMask(draw_deactivation_mask);
723 // force full-screen redraw in this frame
724 redraw_mask = REDRAW_ALL;
728 static void PrintFrameTimeDebugging(void)
730 static unsigned int last_counter = 0;
731 unsigned int counter = Counter();
732 int diff_1 = counter - last_counter;
733 int diff_2 = diff_1 - GAME_FRAME_DELAY;
735 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
736 char diff_bar[2 * diff_2_max + 5];
740 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
742 for (i = 0; i < diff_2_max; i++)
743 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
744 i >= diff_2_max - diff_2_cut ? '-' : ' ');
746 diff_bar[pos++] = '|';
748 for (i = 0; i < diff_2_max; i++)
749 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
751 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
753 diff_bar[pos++] = '\0';
755 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
758 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
761 last_counter = counter;
765 static int unifiedRedrawMask(int mask)
767 if (mask & REDRAW_ALL)
770 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
776 static boolean equalRedrawMasks(int mask_1, int mask_2)
778 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
781 void BackToFront(void)
783 static int last_redraw_mask = REDRAW_NONE;
785 // force screen redraw in every frame to continue drawing global animations
786 // (but always use the last redraw mask to prevent unwanted side effects)
787 if (redraw_mask == REDRAW_NONE)
788 redraw_mask = last_redraw_mask;
790 last_redraw_mask = redraw_mask;
793 // masked border now drawn immediately when blitting backbuffer to window
795 // draw masked border to all viewports, if defined
796 DrawMaskedBorder(redraw_mask);
799 // draw frames per second (only if debug mode is enabled)
800 if (redraw_mask & REDRAW_FPS)
801 DrawFramesPerSecond();
803 // remove playfield redraw before potentially merging with doors redraw
804 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
805 redraw_mask &= ~REDRAW_FIELD;
807 // redraw complete window if both playfield and (some) doors need redraw
808 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
809 redraw_mask = REDRAW_ALL;
811 /* although redrawing the whole window would be fine for normal gameplay,
812 being able to only redraw the playfield is required for deactivating
813 certain drawing areas (mainly playfield) to work, which is needed for
814 warp-forward to be fast enough (by skipping redraw of most frames) */
816 if (redraw_mask & REDRAW_ALL)
818 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
820 else if (redraw_mask & REDRAW_FIELD)
822 BlitBitmap(backbuffer, window,
823 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
825 else if (redraw_mask & REDRAW_DOORS)
827 // merge door areas to prevent calling screen redraw more than once
833 if (redraw_mask & REDRAW_DOOR_1)
837 x2 = MAX(x2, DX + DXSIZE);
838 y2 = MAX(y2, DY + DYSIZE);
841 if (redraw_mask & REDRAW_DOOR_2)
845 x2 = MAX(x2, VX + VXSIZE);
846 y2 = MAX(y2, VY + VYSIZE);
849 if (redraw_mask & REDRAW_DOOR_3)
853 x2 = MAX(x2, EX + EXSIZE);
854 y2 = MAX(y2, EY + EYSIZE);
857 // make sure that at least one pixel is blitted, and inside the screen
858 // (else nothing is blitted, causing the animations not to be updated)
859 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
860 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
861 x2 = MIN(MAX(1, x2), WIN_XSIZE);
862 y2 = MIN(MAX(1, y2), WIN_YSIZE);
864 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
867 redraw_mask = REDRAW_NONE;
870 PrintFrameTimeDebugging();
874 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
876 unsigned int frame_delay_value_old = GetVideoFrameDelay();
878 SetVideoFrameDelay(frame_delay_value);
882 SetVideoFrameDelay(frame_delay_value_old);
885 static int fade_type_skip = FADE_TYPE_NONE;
887 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
889 void (*draw_border_function)(void) = NULL;
890 int x, y, width, height;
891 int fade_delay, post_delay;
893 if (fade_type == FADE_TYPE_FADE_OUT)
895 if (fade_type_skip != FADE_TYPE_NONE)
897 // skip all fade operations until specified fade operation
898 if (fade_type & fade_type_skip)
899 fade_type_skip = FADE_TYPE_NONE;
904 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
908 redraw_mask |= fade_mask;
910 if (fade_type == FADE_TYPE_SKIP)
912 fade_type_skip = fade_mode;
917 fade_delay = fading.fade_delay;
918 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
920 if (fade_type_skip != FADE_TYPE_NONE)
922 // skip all fade operations until specified fade operation
923 if (fade_type & fade_type_skip)
924 fade_type_skip = FADE_TYPE_NONE;
929 if (global.autoplay_leveldir)
934 if (fade_mask == REDRAW_FIELD)
939 height = FADE_SYSIZE;
941 if (border.draw_masked_when_fading)
942 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
944 DrawMaskedBorder_FIELD(); // draw once
954 // when switching screens without fading, set fade delay to zero
955 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
958 // do not display black frame when fading out without fade delay
959 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
962 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
963 draw_border_function);
965 redraw_mask &= ~fade_mask;
967 ClearAutoRepeatKeyEvents();
970 static void SetScreenStates_BeforeFadingIn(void)
972 // temporarily set screen mode for animations to screen after fading in
973 global.anim_status = global.anim_status_next;
975 // store backbuffer with all animations that will be started after fading in
976 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
978 // set screen mode for animations back to fading
979 global.anim_status = GAME_MODE_PSEUDO_FADING;
982 static void SetScreenStates_AfterFadingIn(void)
984 // store new source screen (to use correct masked border for fading)
985 gfx.fade_border_source_status = global.border_status;
987 global.anim_status = global.anim_status_next;
990 static void SetScreenStates_BeforeFadingOut(void)
992 // store new target screen (to use correct masked border for fading)
993 gfx.fade_border_target_status = game_status;
995 // set screen mode for animations to fading
996 global.anim_status = GAME_MODE_PSEUDO_FADING;
998 // store backbuffer with all animations that will be stopped for fading out
999 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1002 static void SetScreenStates_AfterFadingOut(void)
1004 global.border_status = game_status;
1007 void FadeIn(int fade_mask)
1009 SetScreenStates_BeforeFadingIn();
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1018 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1022 FADE_SXSIZE = FULL_SXSIZE;
1023 FADE_SYSIZE = FULL_SYSIZE;
1025 // activate virtual buttons depending on upcoming game status
1026 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1027 game_status == GAME_MODE_PLAYING && !tape.playing)
1028 SetOverlayActive(TRUE);
1030 SetScreenStates_AfterFadingIn();
1032 // force update of global animation status in case of rapid screen changes
1033 redraw_mask = REDRAW_ALL;
1037 void FadeOut(int fade_mask)
1039 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1040 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1041 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1044 SetScreenStates_BeforeFadingOut();
1046 SetTileCursorActive(FALSE);
1047 SetOverlayActive(FALSE);
1050 DrawMaskedBorder(REDRAW_ALL);
1053 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1054 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1056 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1058 SetScreenStates_AfterFadingOut();
1061 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1063 static struct TitleFadingInfo fading_leave_stored;
1066 fading_leave_stored = fading_leave;
1068 fading = fading_leave_stored;
1071 void FadeSetEnterMenu(void)
1073 fading = menu.enter_menu;
1075 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1078 void FadeSetLeaveMenu(void)
1080 fading = menu.leave_menu;
1082 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1085 void FadeSetEnterScreen(void)
1087 fading = menu.enter_screen[game_status];
1089 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1092 void FadeSetNextScreen(void)
1094 fading = menu.next_screen[game_status];
1096 // (do not overwrite fade mode set by FadeSetEnterScreen)
1097 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1100 void FadeSetLeaveScreen(void)
1102 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1105 void FadeSetFromType(int type)
1107 if (type & TYPE_ENTER_SCREEN)
1108 FadeSetEnterScreen();
1109 else if (type & TYPE_ENTER)
1111 else if (type & TYPE_LEAVE)
1115 void FadeSetDisabled(void)
1117 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1119 fading = fading_none;
1122 void FadeSkipNextFadeIn(void)
1124 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1127 void FadeSkipNextFadeOut(void)
1129 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1132 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1134 if (graphic == IMG_UNDEFINED)
1137 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1139 return (graphic_info[graphic].bitmap != NULL || redefined ?
1140 graphic_info[graphic].bitmap :
1141 graphic_info[default_graphic].bitmap);
1144 static Bitmap *getBackgroundBitmap(int graphic)
1146 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1149 static Bitmap *getGlobalBorderBitmap(int graphic)
1151 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1154 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1157 (status == GAME_MODE_MAIN ||
1158 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1159 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1160 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1161 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1164 return getGlobalBorderBitmap(graphic);
1167 void SetWindowBackgroundImageIfDefined(int graphic)
1169 if (graphic_info[graphic].bitmap)
1170 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1173 void SetMainBackgroundImageIfDefined(int graphic)
1175 if (graphic_info[graphic].bitmap)
1176 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1179 void SetDoorBackgroundImageIfDefined(int graphic)
1181 if (graphic_info[graphic].bitmap)
1182 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1185 void SetWindowBackgroundImage(int graphic)
1187 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1190 void SetMainBackgroundImage(int graphic)
1192 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1195 void SetDoorBackgroundImage(int graphic)
1197 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1200 void SetPanelBackground(void)
1202 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1204 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1205 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1207 SetDoorBackgroundBitmap(bitmap_db_panel);
1210 void DrawBackground(int x, int y, int width, int height)
1212 // "drawto" might still point to playfield buffer here (hall of fame)
1213 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1215 if (IN_GFX_FIELD_FULL(x, y))
1216 redraw_mask |= REDRAW_FIELD;
1217 else if (IN_GFX_DOOR_1(x, y))
1218 redraw_mask |= REDRAW_DOOR_1;
1219 else if (IN_GFX_DOOR_2(x, y))
1220 redraw_mask |= REDRAW_DOOR_2;
1221 else if (IN_GFX_DOOR_3(x, y))
1222 redraw_mask |= REDRAW_DOOR_3;
1225 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1227 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1229 if (font->bitmap == NULL)
1232 DrawBackground(x, y, width, height);
1235 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1237 struct GraphicInfo *g = &graphic_info[graphic];
1239 if (g->bitmap == NULL)
1242 DrawBackground(x, y, width, height);
1245 static int game_status_last = -1;
1246 static Bitmap *global_border_bitmap_last = NULL;
1247 static Bitmap *global_border_bitmap = NULL;
1248 static int real_sx_last = -1, real_sy_last = -1;
1249 static int full_sxsize_last = -1, full_sysize_last = -1;
1250 static int dx_last = -1, dy_last = -1;
1251 static int dxsize_last = -1, dysize_last = -1;
1252 static int vx_last = -1, vy_last = -1;
1253 static int vxsize_last = -1, vysize_last = -1;
1254 static int ex_last = -1, ey_last = -1;
1255 static int exsize_last = -1, eysize_last = -1;
1257 boolean CheckIfGlobalBorderHasChanged(void)
1259 // if game status has not changed, global border has not changed either
1260 if (game_status == game_status_last)
1263 // determine and store new global border bitmap for current game status
1264 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1266 return (global_border_bitmap_last != global_border_bitmap);
1269 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1271 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1272 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1274 // if game status has not changed, nothing has to be redrawn
1275 if (game_status == game_status_last)
1278 // redraw if last screen was title screen
1279 if (game_status_last == GAME_MODE_TITLE)
1282 // redraw if global screen border has changed
1283 if (CheckIfGlobalBorderHasChanged())
1286 // redraw if position or size of playfield area has changed
1287 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1288 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1291 // redraw if position or size of door area has changed
1292 if (dx_last != DX || dy_last != DY ||
1293 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1296 // redraw if position or size of tape area has changed
1297 if (vx_last != VX || vy_last != VY ||
1298 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1301 // redraw if position or size of editor area has changed
1302 if (ex_last != EX || ey_last != EY ||
1303 exsize_last != EXSIZE || eysize_last != EYSIZE)
1310 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1313 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1315 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1318 void RedrawGlobalBorder(void)
1320 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1322 RedrawGlobalBorderFromBitmap(bitmap);
1324 redraw_mask = REDRAW_ALL;
1327 static void RedrawGlobalBorderIfNeeded(void)
1329 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1330 if (game_status == game_status_last)
1334 // copy current draw buffer to later copy back areas that have not changed
1335 if (game_status_last != GAME_MODE_TITLE)
1336 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1338 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1339 if (CheckIfGlobalBorderRedrawIsNeeded())
1341 // determine and store new global border bitmap for current game status
1342 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1345 // redraw global screen border (or clear, if defined to be empty)
1346 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1348 if (game_status == GAME_MODE_EDITOR)
1349 DrawSpecialEditorDoor();
1351 // copy previous playfield and door areas, if they are defined on both
1352 // previous and current screen and if they still have the same size
1354 if (real_sx_last != -1 && real_sy_last != -1 &&
1355 REAL_SX != -1 && REAL_SY != -1 &&
1356 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1357 BlitBitmap(bitmap_db_store_1, backbuffer,
1358 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1361 if (dx_last != -1 && dy_last != -1 &&
1362 DX != -1 && DY != -1 &&
1363 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1364 BlitBitmap(bitmap_db_store_1, backbuffer,
1365 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1367 if (game_status != GAME_MODE_EDITOR)
1369 if (vx_last != -1 && vy_last != -1 &&
1370 VX != -1 && VY != -1 &&
1371 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1377 if (ex_last != -1 && ey_last != -1 &&
1378 EX != -1 && EY != -1 &&
1379 exsize_last == EXSIZE && eysize_last == EYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1384 redraw_mask = REDRAW_ALL;
1387 game_status_last = game_status;
1389 global_border_bitmap_last = global_border_bitmap;
1391 real_sx_last = REAL_SX;
1392 real_sy_last = REAL_SY;
1393 full_sxsize_last = FULL_SXSIZE;
1394 full_sysize_last = FULL_SYSIZE;
1397 dxsize_last = DXSIZE;
1398 dysize_last = DYSIZE;
1401 vxsize_last = VXSIZE;
1402 vysize_last = VYSIZE;
1405 exsize_last = EXSIZE;
1406 eysize_last = EYSIZE;
1409 void ClearField(void)
1411 RedrawGlobalBorderIfNeeded();
1413 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1414 // (when entering hall of fame after playing)
1415 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1417 // !!! maybe this should be done before clearing the background !!!
1418 if (game_status == GAME_MODE_PLAYING)
1420 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1421 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1425 SetDrawtoField(DRAW_TO_BACKBUFFER);
1429 void MarkTileDirty(int x, int y)
1431 redraw_mask |= REDRAW_FIELD;
1434 void SetBorderElement(void)
1438 BorderElement = EL_EMPTY;
1440 // only the R'n'D game engine may use an additional steelwall border
1441 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1444 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1446 for (x = 0; x < lev_fieldx; x++)
1448 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1449 BorderElement = EL_STEELWALL;
1451 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1457 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1458 int max_array_fieldx, int max_array_fieldy,
1459 short field[max_array_fieldx][max_array_fieldy],
1460 int max_fieldx, int max_fieldy)
1464 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1465 static int safety = 0;
1467 // check if starting field still has the desired content
1468 if (field[from_x][from_y] == fill_element)
1473 if (safety > max_fieldx * max_fieldy)
1474 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1476 old_element = field[from_x][from_y];
1477 field[from_x][from_y] = fill_element;
1479 for (i = 0; i < 4; i++)
1481 x = from_x + check[i][0];
1482 y = from_y + check[i][1];
1484 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1485 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1486 field, max_fieldx, max_fieldy);
1492 void FloodFillLevel(int from_x, int from_y, int fill_element,
1493 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1494 int max_fieldx, int max_fieldy)
1496 FloodFillLevelExt(from_x, from_y, fill_element,
1497 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1498 max_fieldx, max_fieldy);
1501 void SetRandomAnimationValue(int x, int y)
1503 gfx.anim_random_frame = GfxRandom[x][y];
1506 int getGraphicAnimationFrame(int graphic, int sync_frame)
1508 // animation synchronized with global frame counter, not move position
1509 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1510 sync_frame = FrameCounter;
1512 return getAnimationFrame(graphic_info[graphic].anim_frames,
1513 graphic_info[graphic].anim_delay,
1514 graphic_info[graphic].anim_mode,
1515 graphic_info[graphic].anim_start_frame,
1519 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1521 struct GraphicInfo *g = &graphic_info[graphic];
1522 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1524 if (tilesize == gfx.standard_tile_size)
1525 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1526 else if (tilesize == game.tile_size)
1527 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1529 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1532 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1533 boolean get_backside)
1535 struct GraphicInfo *g = &graphic_info[graphic];
1536 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1537 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1539 if (g->offset_y == 0) // frames are ordered horizontally
1541 int max_width = g->anim_frames_per_line * g->width;
1542 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1544 *x = pos % max_width;
1545 *y = src_y % g->height + pos / max_width * g->height;
1547 else if (g->offset_x == 0) // frames are ordered vertically
1549 int max_height = g->anim_frames_per_line * g->height;
1550 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1552 *x = src_x % g->width + pos / max_height * g->width;
1553 *y = pos % max_height;
1555 else // frames are ordered diagonally
1557 *x = src_x + frame * g->offset_x;
1558 *y = src_y + frame * g->offset_y;
1562 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1563 Bitmap **bitmap, int *x, int *y,
1564 boolean get_backside)
1566 struct GraphicInfo *g = &graphic_info[graphic];
1568 // if no graphics defined at all, use fallback graphics
1569 if (g->bitmaps == NULL)
1570 *g = graphic_info[IMG_CHAR_EXCLAM];
1572 // if no in-game graphics defined, always use standard graphic size
1573 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1574 tilesize = TILESIZE;
1576 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1577 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1579 *x = *x * tilesize / g->tile_size;
1580 *y = *y * tilesize / g->tile_size;
1583 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1584 Bitmap **bitmap, int *x, int *y)
1586 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1589 void getFixedGraphicSource(int graphic, int frame,
1590 Bitmap **bitmap, int *x, int *y)
1592 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1595 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1597 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1600 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1601 int *x, int *y, boolean get_backside)
1603 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1607 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1609 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1612 void DrawGraphic(int x, int y, int graphic, int frame)
1615 if (!IN_SCR_FIELD(x, y))
1617 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1618 Debug("draw:DrawGraphic", "This should never happen!");
1624 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1627 MarkTileDirty(x, y);
1630 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1633 if (!IN_SCR_FIELD(x, y))
1635 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1637 Debug("draw:DrawFixedGraphic", "This should never happen!");
1643 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1645 MarkTileDirty(x, y);
1648 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1654 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1656 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1659 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1665 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1666 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1669 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1672 if (!IN_SCR_FIELD(x, y))
1674 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1676 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1682 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1685 MarkTileDirty(x, y);
1688 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1691 if (!IN_SCR_FIELD(x, y))
1693 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1695 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1701 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1703 MarkTileDirty(x, y);
1706 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1712 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1714 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1718 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1719 int graphic, int frame)
1724 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1726 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1730 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1732 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1734 MarkTileDirty(x / tilesize, y / tilesize);
1737 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1740 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1741 graphic, frame, tilesize);
1742 MarkTileDirty(x / tilesize, y / tilesize);
1745 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1751 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1752 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1755 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1756 int frame, int tilesize)
1761 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1762 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1765 void DrawMiniGraphic(int x, int y, int graphic)
1767 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1768 MarkTileDirty(x / 2, y / 2);
1771 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1776 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1777 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1780 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1781 int graphic, int frame,
1782 int cut_mode, int mask_mode)
1787 int width = TILEX, height = TILEY;
1790 if (dx || dy) // shifted graphic
1792 if (x < BX1) // object enters playfield from the left
1799 else if (x > BX2) // object enters playfield from the right
1805 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1811 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1813 else if (dx) // general horizontal movement
1814 MarkTileDirty(x + SIGN(dx), y);
1816 if (y < BY1) // object enters playfield from the top
1818 if (cut_mode == CUT_BELOW) // object completely above top border
1826 else if (y > BY2) // object enters playfield from the bottom
1832 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1838 else if (dy > 0 && cut_mode == CUT_ABOVE)
1840 if (y == BY2) // object completely above bottom border
1846 MarkTileDirty(x, y + 1);
1847 } // object leaves playfield to the bottom
1848 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1850 else if (dy) // general vertical movement
1851 MarkTileDirty(x, y + SIGN(dy));
1855 if (!IN_SCR_FIELD(x, y))
1857 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1859 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1865 width = width * TILESIZE_VAR / TILESIZE;
1866 height = height * TILESIZE_VAR / TILESIZE;
1867 cx = cx * TILESIZE_VAR / TILESIZE;
1868 cy = cy * TILESIZE_VAR / TILESIZE;
1869 dx = dx * TILESIZE_VAR / TILESIZE;
1870 dy = dy * TILESIZE_VAR / TILESIZE;
1872 if (width > 0 && height > 0)
1874 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1879 dst_x = FX + x * TILEX_VAR + dx;
1880 dst_y = FY + y * TILEY_VAR + dy;
1882 if (mask_mode == USE_MASKING)
1883 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1886 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1889 MarkTileDirty(x, y);
1893 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1894 int graphic, int frame,
1895 int cut_mode, int mask_mode)
1900 int width = TILEX_VAR, height = TILEY_VAR;
1903 int x2 = x + SIGN(dx);
1904 int y2 = y + SIGN(dy);
1906 // movement with two-tile animations must be sync'ed with movement position,
1907 // not with current GfxFrame (which can be higher when using slow movement)
1908 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1909 int anim_frames = graphic_info[graphic].anim_frames;
1911 // (we also need anim_delay here for movement animations with less frames)
1912 int anim_delay = graphic_info[graphic].anim_delay;
1913 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1915 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1916 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1918 // re-calculate animation frame for two-tile movement animation
1919 frame = getGraphicAnimationFrame(graphic, sync_frame);
1921 // check if movement start graphic inside screen area and should be drawn
1922 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1924 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1926 dst_x = FX + x1 * TILEX_VAR;
1927 dst_y = FY + y1 * TILEY_VAR;
1929 if (mask_mode == USE_MASKING)
1930 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1933 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1936 MarkTileDirty(x1, y1);
1939 // check if movement end graphic inside screen area and should be drawn
1940 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1942 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1944 dst_x = FX + x2 * TILEX_VAR;
1945 dst_y = FY + y2 * TILEY_VAR;
1947 if (mask_mode == USE_MASKING)
1948 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 MarkTileDirty(x2, y2);
1958 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1959 int graphic, int frame,
1960 int cut_mode, int mask_mode)
1964 DrawGraphic(x, y, graphic, frame);
1969 if (graphic_info[graphic].double_movement) // EM style movement images
1970 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1972 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1975 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1976 int graphic, int frame, int cut_mode)
1978 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1981 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1982 int cut_mode, int mask_mode)
1984 int lx = LEVELX(x), ly = LEVELY(y);
1988 if (IN_LEV_FIELD(lx, ly))
1990 SetRandomAnimationValue(lx, ly);
1992 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1993 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1995 // do not use double (EM style) movement graphic when not moving
1996 if (graphic_info[graphic].double_movement && !dx && !dy)
1998 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1999 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2002 else // border element
2004 graphic = el2img(element);
2005 frame = getGraphicAnimationFrame(graphic, -1);
2008 if (element == EL_EXPANDABLE_WALL)
2010 boolean left_stopped = FALSE, right_stopped = FALSE;
2012 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2013 left_stopped = TRUE;
2014 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2015 right_stopped = TRUE;
2017 if (left_stopped && right_stopped)
2019 else if (left_stopped)
2021 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2022 frame = graphic_info[graphic].anim_frames - 1;
2024 else if (right_stopped)
2026 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2027 frame = graphic_info[graphic].anim_frames - 1;
2032 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2033 else if (mask_mode == USE_MASKING)
2034 DrawGraphicThruMask(x, y, graphic, frame);
2036 DrawGraphic(x, y, graphic, frame);
2039 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2040 int cut_mode, int mask_mode)
2042 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2043 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2044 cut_mode, mask_mode);
2047 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2050 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2053 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2056 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2059 void DrawLevelElementThruMask(int x, int y, int element)
2061 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2064 void DrawLevelFieldThruMask(int x, int y)
2066 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2069 // !!! implementation of quicksand is totally broken !!!
2070 #define IS_CRUMBLED_TILE(x, y, e) \
2071 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2072 !IS_MOVING(x, y) || \
2073 (e) == EL_QUICKSAND_EMPTYING || \
2074 (e) == EL_QUICKSAND_FAST_EMPTYING))
2076 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2081 int width, height, cx, cy;
2082 int sx = SCREENX(x), sy = SCREENY(y);
2083 int crumbled_border_size = graphic_info[graphic].border_size;
2084 int crumbled_tile_size = graphic_info[graphic].tile_size;
2085 int crumbled_border_size_var =
2086 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2089 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2091 for (i = 1; i < 4; i++)
2093 int dxx = (i & 1 ? dx : 0);
2094 int dyy = (i & 2 ? dy : 0);
2097 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2100 // check if neighbour field is of same crumble type
2101 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2102 graphic_info[graphic].class ==
2103 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2105 // return if check prevents inner corner
2106 if (same == (dxx == dx && dyy == dy))
2110 // if we reach this point, we have an inner corner
2112 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2114 width = crumbled_border_size_var;
2115 height = crumbled_border_size_var;
2116 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2117 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2119 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2120 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2123 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2128 int width, height, bx, by, cx, cy;
2129 int sx = SCREENX(x), sy = SCREENY(y);
2130 int crumbled_border_size = graphic_info[graphic].border_size;
2131 int crumbled_tile_size = graphic_info[graphic].tile_size;
2132 int crumbled_border_size_var =
2133 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2134 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2137 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2139 // draw simple, sloppy, non-corner-accurate crumbled border
2141 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2142 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2143 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2144 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2146 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2147 FX + sx * TILEX_VAR + cx,
2148 FY + sy * TILEY_VAR + cy);
2150 // (remaining middle border part must be at least as big as corner part)
2151 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2152 crumbled_border_size_var >= TILESIZE_VAR / 3)
2155 // correct corners of crumbled border, if needed
2157 for (i = -1; i <= 1; i += 2)
2159 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2160 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2161 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2164 // check if neighbour field is of same crumble type
2165 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2166 graphic_info[graphic].class ==
2167 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2169 // no crumbled corner, but continued crumbled border
2171 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2172 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2173 int b1 = (i == 1 ? crumbled_border_size_var :
2174 TILESIZE_VAR - 2 * crumbled_border_size_var);
2176 width = crumbled_border_size_var;
2177 height = crumbled_border_size_var;
2179 if (dir == 1 || dir == 2)
2194 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2196 FX + sx * TILEX_VAR + cx,
2197 FY + sy * TILEY_VAR + cy);
2202 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2204 int sx = SCREENX(x), sy = SCREENY(y);
2207 static int xy[4][2] =
2215 if (!IN_LEV_FIELD(x, y))
2218 element = TILE_GFX_ELEMENT(x, y);
2220 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2222 if (!IN_SCR_FIELD(sx, sy))
2225 // crumble field borders towards direct neighbour fields
2226 for (i = 0; i < 4; i++)
2228 int xx = x + xy[i][0];
2229 int yy = y + xy[i][1];
2231 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2234 // check if neighbour field is of same crumble type
2235 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2236 graphic_info[graphic].class ==
2237 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2240 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2243 // crumble inner field corners towards corner neighbour fields
2244 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2245 graphic_info[graphic].anim_frames == 2)
2247 for (i = 0; i < 4; i++)
2249 int dx = (i & 1 ? +1 : -1);
2250 int dy = (i & 2 ? +1 : -1);
2252 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2256 MarkTileDirty(sx, sy);
2258 else // center field is not crumbled -- crumble neighbour fields
2260 // crumble field borders of direct neighbour fields
2261 for (i = 0; i < 4; i++)
2263 int xx = x + xy[i][0];
2264 int yy = y + xy[i][1];
2265 int sxx = sx + xy[i][0];
2266 int syy = sy + xy[i][1];
2268 if (!IN_LEV_FIELD(xx, yy) ||
2269 !IN_SCR_FIELD(sxx, syy))
2272 // do not crumble fields that are being digged or snapped
2273 if (Tile[xx][yy] == EL_EMPTY ||
2274 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2277 element = TILE_GFX_ELEMENT(xx, yy);
2279 if (!IS_CRUMBLED_TILE(xx, yy, element))
2282 graphic = el_act2crm(element, ACTION_DEFAULT);
2284 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2286 MarkTileDirty(sxx, syy);
2289 // crumble inner field corners of corner neighbour fields
2290 for (i = 0; i < 4; i++)
2292 int dx = (i & 1 ? +1 : -1);
2293 int dy = (i & 2 ? +1 : -1);
2299 if (!IN_LEV_FIELD(xx, yy) ||
2300 !IN_SCR_FIELD(sxx, syy))
2303 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2306 element = TILE_GFX_ELEMENT(xx, yy);
2308 if (!IS_CRUMBLED_TILE(xx, yy, element))
2311 graphic = el_act2crm(element, ACTION_DEFAULT);
2313 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2314 graphic_info[graphic].anim_frames == 2)
2315 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2317 MarkTileDirty(sxx, syy);
2322 void DrawLevelFieldCrumbled(int x, int y)
2326 if (!IN_LEV_FIELD(x, y))
2329 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2330 GfxElement[x][y] != EL_UNDEFINED &&
2331 GFX_CRUMBLED(GfxElement[x][y]))
2333 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2338 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2340 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2343 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2346 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2347 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2348 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2349 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2350 int sx = SCREENX(x), sy = SCREENY(y);
2352 DrawGraphic(sx, sy, graphic1, frame1);
2353 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2356 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2358 int sx = SCREENX(x), sy = SCREENY(y);
2359 static int xy[4][2] =
2368 // crumble direct neighbour fields (required for field borders)
2369 for (i = 0; i < 4; i++)
2371 int xx = x + xy[i][0];
2372 int yy = y + xy[i][1];
2373 int sxx = sx + xy[i][0];
2374 int syy = sy + xy[i][1];
2376 if (!IN_LEV_FIELD(xx, yy) ||
2377 !IN_SCR_FIELD(sxx, syy) ||
2378 !GFX_CRUMBLED(Tile[xx][yy]) ||
2382 DrawLevelField(xx, yy);
2385 // crumble corner neighbour fields (required for inner field corners)
2386 for (i = 0; i < 4; i++)
2388 int dx = (i & 1 ? +1 : -1);
2389 int dy = (i & 2 ? +1 : -1);
2395 if (!IN_LEV_FIELD(xx, yy) ||
2396 !IN_SCR_FIELD(sxx, syy) ||
2397 !GFX_CRUMBLED(Tile[xx][yy]) ||
2401 int element = TILE_GFX_ELEMENT(xx, yy);
2402 int graphic = el_act2crm(element, ACTION_DEFAULT);
2404 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2405 graphic_info[graphic].anim_frames == 2)
2406 DrawLevelField(xx, yy);
2410 static int getBorderElement(int x, int y)
2414 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2415 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2416 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2417 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2418 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2419 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2420 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2422 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2423 int steel_position = (x == -1 && y == -1 ? 0 :
2424 x == lev_fieldx && y == -1 ? 1 :
2425 x == -1 && y == lev_fieldy ? 2 :
2426 x == lev_fieldx && y == lev_fieldy ? 3 :
2427 x == -1 || x == lev_fieldx ? 4 :
2428 y == -1 || y == lev_fieldy ? 5 : 6);
2430 return border[steel_position][steel_type];
2433 void DrawScreenElement(int x, int y, int element)
2435 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2436 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2439 void DrawLevelElement(int x, int y, int element)
2441 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2442 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2445 void DrawScreenField(int x, int y)
2447 int lx = LEVELX(x), ly = LEVELY(y);
2448 int element, content;
2450 if (!IN_LEV_FIELD(lx, ly))
2452 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2455 element = getBorderElement(lx, ly);
2457 DrawScreenElement(x, y, element);
2462 element = Tile[lx][ly];
2463 content = Store[lx][ly];
2465 if (IS_MOVING(lx, ly))
2467 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2468 boolean cut_mode = NO_CUTTING;
2470 if (element == EL_QUICKSAND_EMPTYING ||
2471 element == EL_QUICKSAND_FAST_EMPTYING ||
2472 element == EL_MAGIC_WALL_EMPTYING ||
2473 element == EL_BD_MAGIC_WALL_EMPTYING ||
2474 element == EL_DC_MAGIC_WALL_EMPTYING ||
2475 element == EL_AMOEBA_DROPPING)
2476 cut_mode = CUT_ABOVE;
2477 else if (element == EL_QUICKSAND_FILLING ||
2478 element == EL_QUICKSAND_FAST_FILLING ||
2479 element == EL_MAGIC_WALL_FILLING ||
2480 element == EL_BD_MAGIC_WALL_FILLING ||
2481 element == EL_DC_MAGIC_WALL_FILLING)
2482 cut_mode = CUT_BELOW;
2484 if (cut_mode == CUT_ABOVE)
2485 DrawScreenElement(x, y, element);
2487 DrawScreenElement(x, y, EL_EMPTY);
2490 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2491 else if (cut_mode == NO_CUTTING)
2492 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2495 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2497 if (cut_mode == CUT_BELOW &&
2498 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2499 DrawLevelElement(lx, ly + 1, element);
2502 if (content == EL_ACID)
2504 int dir = MovDir[lx][ly];
2505 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2506 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2508 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2510 // prevent target field from being drawn again (but without masking)
2511 // (this would happen if target field is scanned after moving element)
2512 Stop[newlx][newly] = TRUE;
2515 else if (IS_BLOCKED(lx, ly))
2520 boolean cut_mode = NO_CUTTING;
2521 int element_old, content_old;
2523 Blocked2Moving(lx, ly, &oldx, &oldy);
2526 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2527 MovDir[oldx][oldy] == MV_RIGHT);
2529 element_old = Tile[oldx][oldy];
2530 content_old = Store[oldx][oldy];
2532 if (element_old == EL_QUICKSAND_EMPTYING ||
2533 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2534 element_old == EL_MAGIC_WALL_EMPTYING ||
2535 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2536 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2537 element_old == EL_AMOEBA_DROPPING)
2538 cut_mode = CUT_ABOVE;
2540 DrawScreenElement(x, y, EL_EMPTY);
2543 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2545 else if (cut_mode == NO_CUTTING)
2546 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2549 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2552 else if (IS_DRAWABLE(element))
2553 DrawScreenElement(x, y, element);
2555 DrawScreenElement(x, y, EL_EMPTY);
2558 void DrawLevelField(int x, int y)
2560 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2561 DrawScreenField(SCREENX(x), SCREENY(y));
2562 else if (IS_MOVING(x, y))
2566 Moving2Blocked(x, y, &newx, &newy);
2567 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2568 DrawScreenField(SCREENX(newx), SCREENY(newy));
2570 else if (IS_BLOCKED(x, y))
2574 Blocked2Moving(x, y, &oldx, &oldy);
2575 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2576 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2580 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2581 int (*el2img_function)(int), boolean masked,
2582 int element_bits_draw)
2584 int element_base = map_mm_wall_element(element);
2585 int element_bits = (IS_DF_WALL(element) ?
2586 element - EL_DF_WALL_START :
2587 IS_MM_WALL(element) ?
2588 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2589 int graphic = el2img_function(element_base);
2590 int tilesize_draw = tilesize / 2;
2595 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2597 for (i = 0; i < 4; i++)
2599 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2600 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2602 if (!(element_bits_draw & (1 << i)))
2605 if (element_bits & (1 << i))
2608 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2609 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2611 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2612 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2617 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2618 tilesize_draw, tilesize_draw);
2623 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2624 boolean masked, int element_bits_draw)
2626 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2627 element, tilesize, el2edimg, masked, element_bits_draw);
2630 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2631 int (*el2img_function)(int))
2633 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2637 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2640 if (IS_MM_WALL(element))
2642 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2643 element, tilesize, el2edimg, masked, 0x000f);
2647 int graphic = el2edimg(element);
2650 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2652 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2656 void DrawSizedElement(int x, int y, int element, int tilesize)
2658 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2661 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2663 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2666 void DrawMiniElement(int x, int y, int element)
2670 graphic = el2edimg(element);
2671 DrawMiniGraphic(x, y, graphic);
2674 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2677 int x = sx + scroll_x, y = sy + scroll_y;
2679 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2680 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2681 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2682 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2684 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2687 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2689 int x = sx + scroll_x, y = sy + scroll_y;
2691 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2692 DrawMiniElement(sx, sy, EL_EMPTY);
2693 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2694 DrawMiniElement(sx, sy, Tile[x][y]);
2696 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2699 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2700 int x, int y, int xsize, int ysize,
2701 int tile_width, int tile_height)
2705 int dst_x = startx + x * tile_width;
2706 int dst_y = starty + y * tile_height;
2707 int width = graphic_info[graphic].width;
2708 int height = graphic_info[graphic].height;
2709 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2710 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2711 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2712 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2713 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2714 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2715 boolean draw_masked = graphic_info[graphic].draw_masked;
2717 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2719 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2721 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2725 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2726 inner_sx + (x - 1) * tile_width % inner_width);
2727 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2728 inner_sy + (y - 1) * tile_height % inner_height);
2731 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2734 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2738 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2739 int x, int y, int xsize, int ysize,
2742 int font_width = getFontWidth(font_nr);
2743 int font_height = getFontHeight(font_nr);
2745 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2746 font_width, font_height);
2749 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2751 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2752 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2753 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2754 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2755 boolean no_delay = (tape.warp_forward);
2756 unsigned int anim_delay = 0;
2757 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2758 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2759 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2760 int font_width = getFontWidth(font_nr);
2761 int font_height = getFontHeight(font_nr);
2762 int max_xsize = level.envelope[envelope_nr].xsize;
2763 int max_ysize = level.envelope[envelope_nr].ysize;
2764 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2765 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2766 int xend = max_xsize;
2767 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2768 int xstep = (xstart < xend ? 1 : 0);
2769 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2771 int end = MAX(xend - xstart, yend - ystart);
2774 for (i = start; i <= end; i++)
2776 int last_frame = end; // last frame of this "for" loop
2777 int x = xstart + i * xstep;
2778 int y = ystart + i * ystep;
2779 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2780 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2781 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2782 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2785 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2787 BlitScreenToBitmap(backbuffer);
2789 SetDrawtoField(DRAW_TO_BACKBUFFER);
2791 for (yy = 0; yy < ysize; yy++)
2792 for (xx = 0; xx < xsize; xx++)
2793 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2795 DrawTextBuffer(sx + font_width, sy + font_height,
2796 level.envelope[envelope_nr].text, font_nr, max_xsize,
2797 xsize - 2, ysize - 2, 0, mask_mode,
2798 level.envelope[envelope_nr].autowrap,
2799 level.envelope[envelope_nr].centered, FALSE);
2801 redraw_mask |= REDRAW_FIELD;
2804 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2807 ClearAutoRepeatKeyEvents();
2810 void ShowEnvelope(int envelope_nr)
2812 int element = EL_ENVELOPE_1 + envelope_nr;
2813 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2814 int sound_opening = element_info[element].sound[ACTION_OPENING];
2815 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2816 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2817 boolean no_delay = (tape.warp_forward);
2818 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2819 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2820 int anim_mode = graphic_info[graphic].anim_mode;
2821 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2822 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2824 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2826 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2828 if (anim_mode == ANIM_DEFAULT)
2829 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2831 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2834 Delay_WithScreenUpdates(wait_delay_value);
2836 WaitForEventToContinue();
2838 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2840 if (anim_mode != ANIM_NONE)
2841 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2843 if (anim_mode == ANIM_DEFAULT)
2844 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2846 game.envelope_active = FALSE;
2848 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2850 redraw_mask |= REDRAW_FIELD;
2854 static void setRequestBasePosition(int *x, int *y)
2856 int sx_base, sy_base;
2858 if (request.x != -1)
2859 sx_base = request.x;
2860 else if (request.align == ALIGN_LEFT)
2862 else if (request.align == ALIGN_RIGHT)
2863 sx_base = SX + SXSIZE;
2865 sx_base = SX + SXSIZE / 2;
2867 if (request.y != -1)
2868 sy_base = request.y;
2869 else if (request.valign == VALIGN_TOP)
2871 else if (request.valign == VALIGN_BOTTOM)
2872 sy_base = SY + SYSIZE;
2874 sy_base = SY + SYSIZE / 2;
2880 static void setRequestPositionExt(int *x, int *y, int width, int height,
2881 boolean add_border_size)
2883 int border_size = request.border_size;
2884 int sx_base, sy_base;
2887 setRequestBasePosition(&sx_base, &sy_base);
2889 if (request.align == ALIGN_LEFT)
2891 else if (request.align == ALIGN_RIGHT)
2892 sx = sx_base - width;
2894 sx = sx_base - width / 2;
2896 if (request.valign == VALIGN_TOP)
2898 else if (request.valign == VALIGN_BOTTOM)
2899 sy = sy_base - height;
2901 sy = sy_base - height / 2;
2903 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2904 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2906 if (add_border_size)
2916 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2918 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2921 static void DrawEnvelopeRequest(char *text)
2923 char *text_final = text;
2924 char *text_door_style = NULL;
2925 int graphic = IMG_BACKGROUND_REQUEST;
2926 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2927 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2928 int font_nr = FONT_REQUEST;
2929 int font_width = getFontWidth(font_nr);
2930 int font_height = getFontHeight(font_nr);
2931 int border_size = request.border_size;
2932 int line_spacing = request.line_spacing;
2933 int line_height = font_height + line_spacing;
2934 int max_text_width = request.width - 2 * border_size;
2935 int max_text_height = request.height - 2 * border_size;
2936 int line_length = max_text_width / font_width;
2937 int max_lines = max_text_height / line_height;
2938 int text_width = line_length * font_width;
2939 int width = request.width;
2940 int height = request.height;
2941 int tile_size = MAX(request.step_offset, 1);
2942 int x_steps = width / tile_size;
2943 int y_steps = height / tile_size;
2944 int sx_offset = border_size;
2945 int sy_offset = border_size;
2949 if (request.centered)
2950 sx_offset = (request.width - text_width) / 2;
2952 if (request.wrap_single_words && !request.autowrap)
2954 char *src_text_ptr, *dst_text_ptr;
2956 text_door_style = checked_malloc(2 * strlen(text) + 1);
2958 src_text_ptr = text;
2959 dst_text_ptr = text_door_style;
2961 while (*src_text_ptr)
2963 if (*src_text_ptr == ' ' ||
2964 *src_text_ptr == '?' ||
2965 *src_text_ptr == '!')
2966 *dst_text_ptr++ = '\n';
2968 if (*src_text_ptr != ' ')
2969 *dst_text_ptr++ = *src_text_ptr;
2974 *dst_text_ptr = '\0';
2976 text_final = text_door_style;
2979 setRequestPosition(&sx, &sy, FALSE);
2981 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2983 for (y = 0; y < y_steps; y++)
2984 for (x = 0; x < x_steps; x++)
2985 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2986 x, y, x_steps, y_steps,
2987 tile_size, tile_size);
2989 // force DOOR font inside door area
2990 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2992 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2993 line_length, -1, max_lines, line_spacing, mask_mode,
2994 request.autowrap, request.centered, FALSE);
2998 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2999 RedrawGadget(tool_gadget[i]);
3001 // store readily prepared envelope request for later use when animating
3002 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3004 if (text_door_style)
3005 free(text_door_style);
3008 static void AnimateEnvelopeRequest(int anim_mode, int action)
3010 int graphic = IMG_BACKGROUND_REQUEST;
3011 boolean draw_masked = graphic_info[graphic].draw_masked;
3012 int delay_value_normal = request.step_delay;
3013 int delay_value_fast = delay_value_normal / 2;
3014 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3015 boolean no_delay = (tape.warp_forward);
3016 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3017 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3018 unsigned int anim_delay = 0;
3020 int tile_size = MAX(request.step_offset, 1);
3021 int max_xsize = request.width / tile_size;
3022 int max_ysize = request.height / tile_size;
3023 int max_xsize_inner = max_xsize - 2;
3024 int max_ysize_inner = max_ysize - 2;
3026 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3027 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3028 int xend = max_xsize_inner;
3029 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3030 int xstep = (xstart < xend ? 1 : 0);
3031 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3033 int end = MAX(xend - xstart, yend - ystart);
3036 if (setup.quick_doors)
3043 for (i = start; i <= end; i++)
3045 int last_frame = end; // last frame of this "for" loop
3046 int x = xstart + i * xstep;
3047 int y = ystart + i * ystep;
3048 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3049 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3050 int xsize_size_left = (xsize - 1) * tile_size;
3051 int ysize_size_top = (ysize - 1) * tile_size;
3052 int max_xsize_pos = (max_xsize - 1) * tile_size;
3053 int max_ysize_pos = (max_ysize - 1) * tile_size;
3054 int width = xsize * tile_size;
3055 int height = ysize * tile_size;
3060 setRequestPosition(&src_x, &src_y, FALSE);
3061 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3063 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3065 for (yy = 0; yy < 2; yy++)
3067 for (xx = 0; xx < 2; xx++)
3069 int src_xx = src_x + xx * max_xsize_pos;
3070 int src_yy = src_y + yy * max_ysize_pos;
3071 int dst_xx = dst_x + xx * xsize_size_left;
3072 int dst_yy = dst_y + yy * ysize_size_top;
3073 int xx_size = (xx ? tile_size : xsize_size_left);
3074 int yy_size = (yy ? tile_size : ysize_size_top);
3077 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3078 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3080 BlitBitmap(bitmap_db_store_2, backbuffer,
3081 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3085 redraw_mask |= REDRAW_FIELD;
3089 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3092 ClearAutoRepeatKeyEvents();
3095 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3097 int graphic = IMG_BACKGROUND_REQUEST;
3098 int sound_opening = SND_REQUEST_OPENING;
3099 int sound_closing = SND_REQUEST_CLOSING;
3100 int anim_mode_1 = request.anim_mode; // (higher priority)
3101 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3102 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3103 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3104 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3106 if (game_status == GAME_MODE_PLAYING)
3107 BlitScreenToBitmap(backbuffer);
3109 SetDrawtoField(DRAW_TO_BACKBUFFER);
3111 // SetDrawBackgroundMask(REDRAW_NONE);
3113 if (action == ACTION_OPENING)
3115 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3117 if (req_state & REQ_ASK)
3119 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3120 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3121 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3122 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3124 else if (req_state & REQ_CONFIRM)
3126 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3129 else if (req_state & REQ_PLAYER)
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3132 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3137 DrawEnvelopeRequest(text);
3140 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3142 if (action == ACTION_OPENING)
3144 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3146 if (anim_mode == ANIM_DEFAULT)
3147 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3149 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3153 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3155 if (anim_mode != ANIM_NONE)
3156 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3158 if (anim_mode == ANIM_DEFAULT)
3159 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3162 game.envelope_active = FALSE;
3164 if (action == ACTION_CLOSING)
3165 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3167 // SetDrawBackgroundMask(last_draw_background_mask);
3169 redraw_mask |= REDRAW_FIELD;
3173 if (action == ACTION_CLOSING &&
3174 game_status == GAME_MODE_PLAYING &&
3175 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3176 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3179 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3181 if (IS_MM_WALL(element))
3183 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3189 int graphic = el2preimg(element);
3191 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3192 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3197 void DrawLevel(int draw_background_mask)
3201 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3202 SetDrawBackgroundMask(draw_background_mask);
3206 for (x = BX1; x <= BX2; x++)
3207 for (y = BY1; y <= BY2; y++)
3208 DrawScreenField(x, y);
3210 redraw_mask |= REDRAW_FIELD;
3213 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3218 for (x = 0; x < size_x; x++)
3219 for (y = 0; y < size_y; y++)
3220 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3222 redraw_mask |= REDRAW_FIELD;
3225 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3229 for (x = 0; x < size_x; x++)
3230 for (y = 0; y < size_y; y++)
3231 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3233 redraw_mask |= REDRAW_FIELD;
3236 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3238 boolean show_level_border = (BorderElement != EL_EMPTY);
3239 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3240 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3241 int tile_size = preview.tile_size;
3242 int preview_width = preview.xsize * tile_size;
3243 int preview_height = preview.ysize * tile_size;
3244 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3245 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3246 int real_preview_width = real_preview_xsize * tile_size;
3247 int real_preview_height = real_preview_ysize * tile_size;
3248 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3249 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3252 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3255 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3257 dst_x += (preview_width - real_preview_width) / 2;
3258 dst_y += (preview_height - real_preview_height) / 2;
3260 for (x = 0; x < real_preview_xsize; x++)
3262 for (y = 0; y < real_preview_ysize; y++)
3264 int lx = from_x + x + (show_level_border ? -1 : 0);
3265 int ly = from_y + y + (show_level_border ? -1 : 0);
3266 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3267 getBorderElement(lx, ly));
3269 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3270 element, tile_size);
3274 redraw_mask |= REDRAW_FIELD;
3277 #define MICROLABEL_EMPTY 0
3278 #define MICROLABEL_LEVEL_NAME 1
3279 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3280 #define MICROLABEL_LEVEL_AUTHOR 3
3281 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3282 #define MICROLABEL_IMPORTED_FROM 5
3283 #define MICROLABEL_IMPORTED_BY_HEAD 6
3284 #define MICROLABEL_IMPORTED_BY 7
3286 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3288 int max_text_width = SXSIZE;
3289 int font_width = getFontWidth(font_nr);
3291 if (pos->align == ALIGN_CENTER)
3292 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3293 else if (pos->align == ALIGN_RIGHT)
3294 max_text_width = pos->x;
3296 max_text_width = SXSIZE - pos->x;
3298 return max_text_width / font_width;
3301 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3303 char label_text[MAX_OUTPUT_LINESIZE + 1];
3304 int max_len_label_text;
3305 int font_nr = pos->font;
3308 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3311 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3312 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3313 mode == MICROLABEL_IMPORTED_BY_HEAD)
3314 font_nr = pos->font_alt;
3316 max_len_label_text = getMaxTextLength(pos, font_nr);
3318 if (pos->size != -1)
3319 max_len_label_text = pos->size;
3321 for (i = 0; i < max_len_label_text; i++)
3322 label_text[i] = ' ';
3323 label_text[max_len_label_text] = '\0';
3325 if (strlen(label_text) > 0)
3326 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3329 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3330 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3331 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3332 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3333 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3334 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3335 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3336 max_len_label_text);
3337 label_text[max_len_label_text] = '\0';
3339 if (strlen(label_text) > 0)
3340 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3342 redraw_mask |= REDRAW_FIELD;
3345 static void DrawPreviewLevelLabel(int mode)
3347 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3350 static void DrawPreviewLevelInfo(int mode)
3352 if (mode == MICROLABEL_LEVEL_NAME)
3353 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3354 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3355 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3358 static void DrawPreviewLevelExt(boolean restart)
3360 static unsigned int scroll_delay = 0;
3361 static unsigned int label_delay = 0;
3362 static int from_x, from_y, scroll_direction;
3363 static int label_state, label_counter;
3364 unsigned int scroll_delay_value = preview.step_delay;
3365 boolean show_level_border = (BorderElement != EL_EMPTY);
3366 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3367 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3374 if (preview.anim_mode == ANIM_CENTERED)
3376 if (level_xsize > preview.xsize)
3377 from_x = (level_xsize - preview.xsize) / 2;
3378 if (level_ysize > preview.ysize)
3379 from_y = (level_ysize - preview.ysize) / 2;
3382 from_x += preview.xoffset;
3383 from_y += preview.yoffset;
3385 scroll_direction = MV_RIGHT;
3389 DrawPreviewLevelPlayfield(from_x, from_y);
3390 DrawPreviewLevelLabel(label_state);
3392 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3393 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3395 // initialize delay counters
3396 DelayReached(&scroll_delay, 0);
3397 DelayReached(&label_delay, 0);
3399 if (leveldir_current->name)
3401 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3402 char label_text[MAX_OUTPUT_LINESIZE + 1];
3403 int font_nr = pos->font;
3404 int max_len_label_text = getMaxTextLength(pos, font_nr);
3406 if (pos->size != -1)
3407 max_len_label_text = pos->size;
3409 strncpy(label_text, leveldir_current->name, max_len_label_text);
3410 label_text[max_len_label_text] = '\0';
3412 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3413 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3419 // scroll preview level, if needed
3420 if (preview.anim_mode != ANIM_NONE &&
3421 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3422 DelayReached(&scroll_delay, scroll_delay_value))
3424 switch (scroll_direction)
3429 from_x -= preview.step_offset;
3430 from_x = (from_x < 0 ? 0 : from_x);
3433 scroll_direction = MV_UP;
3437 if (from_x < level_xsize - preview.xsize)
3439 from_x += preview.step_offset;
3440 from_x = (from_x > level_xsize - preview.xsize ?
3441 level_xsize - preview.xsize : from_x);
3444 scroll_direction = MV_DOWN;
3450 from_y -= preview.step_offset;
3451 from_y = (from_y < 0 ? 0 : from_y);
3454 scroll_direction = MV_RIGHT;
3458 if (from_y < level_ysize - preview.ysize)
3460 from_y += preview.step_offset;
3461 from_y = (from_y > level_ysize - preview.ysize ?
3462 level_ysize - preview.ysize : from_y);
3465 scroll_direction = MV_LEFT;
3472 DrawPreviewLevelPlayfield(from_x, from_y);
3475 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3476 // redraw micro level label, if needed
3477 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3478 !strEqual(level.author, ANONYMOUS_NAME) &&
3479 !strEqual(level.author, leveldir_current->name) &&
3480 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3482 int max_label_counter = 23;
3484 if (leveldir_current->imported_from != NULL &&
3485 strlen(leveldir_current->imported_from) > 0)
3486 max_label_counter += 14;
3487 if (leveldir_current->imported_by != NULL &&
3488 strlen(leveldir_current->imported_by) > 0)
3489 max_label_counter += 14;
3491 label_counter = (label_counter + 1) % max_label_counter;
3492 label_state = (label_counter >= 0 && label_counter <= 7 ?
3493 MICROLABEL_LEVEL_NAME :
3494 label_counter >= 9 && label_counter <= 12 ?
3495 MICROLABEL_LEVEL_AUTHOR_HEAD :
3496 label_counter >= 14 && label_counter <= 21 ?
3497 MICROLABEL_LEVEL_AUTHOR :
3498 label_counter >= 23 && label_counter <= 26 ?
3499 MICROLABEL_IMPORTED_FROM_HEAD :
3500 label_counter >= 28 && label_counter <= 35 ?
3501 MICROLABEL_IMPORTED_FROM :
3502 label_counter >= 37 && label_counter <= 40 ?
3503 MICROLABEL_IMPORTED_BY_HEAD :
3504 label_counter >= 42 && label_counter <= 49 ?
3505 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3507 if (leveldir_current->imported_from == NULL &&
3508 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3509 label_state == MICROLABEL_IMPORTED_FROM))
3510 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3511 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3513 DrawPreviewLevelLabel(label_state);
3517 void DrawPreviewPlayers(void)
3519 if (game_status != GAME_MODE_MAIN)
3522 // do not draw preview players if level preview redefined, but players aren't
3523 if (preview.redefined && !menu.main.preview_players.redefined)
3526 boolean player_found[MAX_PLAYERS];
3527 int num_players = 0;
3530 for (i = 0; i < MAX_PLAYERS; i++)
3531 player_found[i] = FALSE;
3533 // check which players can be found in the level (simple approach)
3534 for (x = 0; x < lev_fieldx; x++)
3536 for (y = 0; y < lev_fieldy; y++)
3538 int element = level.field[x][y];
3540 if (ELEM_IS_PLAYER(element))
3542 int player_nr = GET_PLAYER_NR(element);
3544 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3546 if (!player_found[player_nr])
3549 player_found[player_nr] = TRUE;
3554 struct TextPosInfo *pos = &menu.main.preview_players;
3555 int tile_size = pos->tile_size;
3556 int border_size = pos->border_size;
3557 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3558 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3559 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3560 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3561 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3562 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3563 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3564 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3565 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3566 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3567 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3568 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3570 // clear area in which the players will be drawn
3571 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3572 max_players_width, max_players_height);
3574 if (!network.enabled && !setup.team_mode)
3577 // only draw players if level is suited for team mode
3578 if (num_players < 2)
3581 // draw all players that were found in the level
3582 for (i = 0; i < MAX_PLAYERS; i++)
3584 if (player_found[i])
3586 int graphic = el2img(EL_PLAYER_1 + i);
3588 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3590 xpos += player_xoffset;
3591 ypos += player_yoffset;
3596 void DrawPreviewLevelInitial(void)
3598 DrawPreviewLevelExt(TRUE);
3599 DrawPreviewPlayers();
3602 void DrawPreviewLevelAnimation(void)
3604 DrawPreviewLevelExt(FALSE);
3607 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3608 int border_size, int font_nr)
3610 int graphic = el2img(EL_PLAYER_1 + player_nr);
3611 int font_height = getFontHeight(font_nr);
3612 int player_height = MAX(tile_size, font_height);
3613 int xoffset_text = tile_size + border_size;
3614 int yoffset_text = (player_height - font_height) / 2;
3615 int yoffset_graphic = (player_height - tile_size) / 2;
3616 char *player_name = getNetworkPlayerName(player_nr + 1);
3618 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3620 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3623 static void DrawNetworkPlayersExt(boolean force)
3625 if (game_status != GAME_MODE_MAIN)
3628 if (!network.connected && !force)
3631 // do not draw network players if level preview redefined, but players aren't
3632 if (preview.redefined && !menu.main.network_players.redefined)
3635 int num_players = 0;
3638 for (i = 0; i < MAX_PLAYERS; i++)
3639 if (stored_player[i].connected_network)
3642 struct TextPosInfo *pos = &menu.main.network_players;
3643 int tile_size = pos->tile_size;
3644 int border_size = pos->border_size;
3645 int xoffset_text = tile_size + border_size;
3646 int font_nr = pos->font;
3647 int font_width = getFontWidth(font_nr);
3648 int font_height = getFontHeight(font_nr);
3649 int player_height = MAX(tile_size, font_height);
3650 int player_yoffset = player_height + border_size;
3651 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3652 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3653 int all_players_height = num_players * player_yoffset - border_size;
3654 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3655 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3656 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3658 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3659 max_players_width, max_players_height);
3661 // first draw local network player ...
3662 for (i = 0; i < MAX_PLAYERS; i++)
3664 if (stored_player[i].connected_network &&
3665 stored_player[i].connected_locally)
3667 char *player_name = getNetworkPlayerName(i + 1);
3668 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3669 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3671 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3673 ypos += player_yoffset;
3677 // ... then draw all other network players
3678 for (i = 0; i < MAX_PLAYERS; i++)
3680 if (stored_player[i].connected_network &&
3681 !stored_player[i].connected_locally)
3683 char *player_name = getNetworkPlayerName(i + 1);
3684 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3685 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3687 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3689 ypos += player_yoffset;
3694 void DrawNetworkPlayers(void)
3696 DrawNetworkPlayersExt(FALSE);
3699 void ClearNetworkPlayers(void)
3701 DrawNetworkPlayersExt(TRUE);
3704 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3705 int graphic, int sync_frame,
3708 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3710 if (mask_mode == USE_MASKING)
3711 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3713 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3716 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3717 int graphic, int sync_frame, int mask_mode)
3719 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3721 if (mask_mode == USE_MASKING)
3722 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3724 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3727 static void DrawGraphicAnimation(int x, int y, int graphic)
3729 int lx = LEVELX(x), ly = LEVELY(y);
3731 if (!IN_SCR_FIELD(x, y))
3734 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3735 graphic, GfxFrame[lx][ly], NO_MASKING);
3737 MarkTileDirty(x, y);
3740 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3742 int lx = LEVELX(x), ly = LEVELY(y);
3744 if (!IN_SCR_FIELD(x, y))
3747 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3748 graphic, GfxFrame[lx][ly], NO_MASKING);
3749 MarkTileDirty(x, y);
3752 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3754 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3757 void DrawLevelElementAnimation(int x, int y, int element)
3759 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3761 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3764 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3766 int sx = SCREENX(x), sy = SCREENY(y);
3768 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3771 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3774 DrawGraphicAnimation(sx, sy, graphic);
3777 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3778 DrawLevelFieldCrumbled(x, y);
3780 if (GFX_CRUMBLED(Tile[x][y]))
3781 DrawLevelFieldCrumbled(x, y);
3785 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3787 int sx = SCREENX(x), sy = SCREENY(y);
3790 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3793 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3795 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3798 DrawGraphicAnimation(sx, sy, graphic);
3800 if (GFX_CRUMBLED(element))
3801 DrawLevelFieldCrumbled(x, y);
3804 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3806 if (player->use_murphy)
3808 // this works only because currently only one player can be "murphy" ...
3809 static int last_horizontal_dir = MV_LEFT;
3810 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3812 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3813 last_horizontal_dir = move_dir;
3815 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3817 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3819 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3825 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3828 static boolean equalGraphics(int graphic1, int graphic2)
3830 struct GraphicInfo *g1 = &graphic_info[graphic1];
3831 struct GraphicInfo *g2 = &graphic_info[graphic2];
3833 return (g1->bitmap == g2->bitmap &&
3834 g1->src_x == g2->src_x &&
3835 g1->src_y == g2->src_y &&
3836 g1->anim_frames == g2->anim_frames &&
3837 g1->anim_delay == g2->anim_delay &&
3838 g1->anim_mode == g2->anim_mode);
3841 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3845 DRAW_PLAYER_STAGE_INIT = 0,
3846 DRAW_PLAYER_STAGE_LAST_FIELD,
3847 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3848 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3849 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3850 DRAW_PLAYER_STAGE_PLAYER,
3852 DRAW_PLAYER_STAGE_PLAYER,
3853 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3855 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3856 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3858 NUM_DRAW_PLAYER_STAGES
3861 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3863 static int static_last_player_graphic[MAX_PLAYERS];
3864 static int static_last_player_frame[MAX_PLAYERS];
3865 static boolean static_player_is_opaque[MAX_PLAYERS];
3866 static boolean draw_player[MAX_PLAYERS];
3867 int pnr = player->index_nr;
3869 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3871 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3872 static_last_player_frame[pnr] = player->Frame;
3873 static_player_is_opaque[pnr] = FALSE;
3875 draw_player[pnr] = TRUE;
3878 if (!draw_player[pnr])
3882 if (!IN_LEV_FIELD(player->jx, player->jy))
3884 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3885 Debug("draw:DrawPlayerExt", "This should never happen!");
3887 draw_player[pnr] = FALSE;
3893 int last_player_graphic = static_last_player_graphic[pnr];
3894 int last_player_frame = static_last_player_frame[pnr];
3895 boolean player_is_opaque = static_player_is_opaque[pnr];
3897 int jx = player->jx;
3898 int jy = player->jy;
3899 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3900 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3901 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3902 int last_jx = (player->is_moving ? jx - dx : jx);
3903 int last_jy = (player->is_moving ? jy - dy : jy);
3904 int next_jx = jx + dx;
3905 int next_jy = jy + dy;
3906 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3907 int sx = SCREENX(jx);
3908 int sy = SCREENY(jy);
3909 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3910 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3911 int element = Tile[jx][jy];
3912 int last_element = Tile[last_jx][last_jy];
3913 int action = (player->is_pushing ? ACTION_PUSHING :
3914 player->is_digging ? ACTION_DIGGING :
3915 player->is_collecting ? ACTION_COLLECTING :
3916 player->is_moving ? ACTION_MOVING :
3917 player->is_snapping ? ACTION_SNAPPING :
3918 player->is_dropping ? ACTION_DROPPING :
3919 player->is_waiting ? player->action_waiting :
3922 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3924 // ------------------------------------------------------------------------
3925 // initialize drawing the player
3926 // ------------------------------------------------------------------------
3928 draw_player[pnr] = FALSE;
3930 // GfxElement[][] is set to the element the player is digging or collecting;
3931 // remove also for off-screen player if the player is not moving anymore
3932 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3933 GfxElement[jx][jy] = EL_UNDEFINED;
3935 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3938 if (element == EL_EXPLOSION)
3941 InitPlayerGfxAnimation(player, action, move_dir);
3943 draw_player[pnr] = TRUE;
3945 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3947 // ------------------------------------------------------------------------
3948 // draw things in the field the player is leaving, if needed
3949 // ------------------------------------------------------------------------
3951 if (!IN_SCR_FIELD(sx, sy))
3952 draw_player[pnr] = FALSE;
3954 if (!player->is_moving)
3957 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3959 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3961 if (last_element == EL_DYNAMITE_ACTIVE ||
3962 last_element == EL_EM_DYNAMITE_ACTIVE ||
3963 last_element == EL_SP_DISK_RED_ACTIVE)
3964 DrawDynamite(last_jx, last_jy);
3966 DrawLevelFieldThruMask(last_jx, last_jy);
3968 else if (last_element == EL_DYNAMITE_ACTIVE ||
3969 last_element == EL_EM_DYNAMITE_ACTIVE ||
3970 last_element == EL_SP_DISK_RED_ACTIVE)
3971 DrawDynamite(last_jx, last_jy);
3973 DrawLevelField(last_jx, last_jy);
3975 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3976 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3978 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3980 // ------------------------------------------------------------------------
3981 // draw things behind the player, if needed
3982 // ------------------------------------------------------------------------
3986 DrawLevelElement(jx, jy, Back[jx][jy]);
3991 if (IS_ACTIVE_BOMB(element))
3993 DrawLevelElement(jx, jy, EL_EMPTY);
3998 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4000 int old_element = GfxElement[jx][jy];
4001 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4002 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4004 if (GFX_CRUMBLED(old_element))
4005 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4007 DrawGraphic(sx, sy, old_graphic, frame);
4009 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4010 static_player_is_opaque[pnr] = TRUE;
4014 GfxElement[jx][jy] = EL_UNDEFINED;
4016 // make sure that pushed elements are drawn with correct frame rate
4017 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4019 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4020 GfxFrame[jx][jy] = player->StepFrame;
4022 DrawLevelField(jx, jy);
4025 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4027 // ------------------------------------------------------------------------
4028 // draw things the player is pushing, if needed
4029 // ------------------------------------------------------------------------
4031 if (!player->is_pushing || !player->is_moving)
4034 int gfx_frame = GfxFrame[jx][jy];
4036 if (!IS_MOVING(jx, jy)) // push movement already finished
4038 element = Tile[next_jx][next_jy];
4039 gfx_frame = GfxFrame[next_jx][next_jy];
4042 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4043 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4044 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4046 // draw background element under pushed element (like the Sokoban field)
4047 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4049 // this allows transparent pushing animation over non-black background
4052 DrawLevelElement(jx, jy, Back[jx][jy]);
4054 DrawLevelElement(jx, jy, EL_EMPTY);
4056 if (Back[next_jx][next_jy])
4057 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4059 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4061 else if (Back[next_jx][next_jy])
4062 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4064 int px = SCREENX(jx), py = SCREENY(jy);
4065 int pxx = (TILEX - ABS(sxx)) * dx;
4066 int pyy = (TILEY - ABS(syy)) * dy;
4069 // do not draw (EM style) pushing animation when pushing is finished
4070 // (two-tile animations usually do not contain start and end frame)
4071 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4072 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4074 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4076 // masked drawing is needed for EMC style (double) movement graphics
4077 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4078 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4081 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4083 // ------------------------------------------------------------------------
4084 // draw player himself
4085 // ------------------------------------------------------------------------
4087 int graphic = getPlayerGraphic(player, move_dir);
4089 // in the case of changed player action or direction, prevent the current
4090 // animation frame from being restarted for identical animations
4091 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4092 player->Frame = last_player_frame;
4094 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4096 if (player_is_opaque)
4097 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4099 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4101 if (SHIELD_ON(player))
4103 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4104 IMG_SHIELD_NORMAL_ACTIVE);
4105 frame = getGraphicAnimationFrame(graphic, -1);
4107 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4110 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4112 // ------------------------------------------------------------------------
4113 // draw things in front of player (active dynamite or dynabombs)
4114 // ------------------------------------------------------------------------
4116 if (IS_ACTIVE_BOMB(element))
4118 int graphic = el2img(element);
4119 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4121 if (game.emulation == EMU_SUPAPLEX)
4122 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4124 DrawGraphicThruMask(sx, sy, graphic, frame);
4127 if (player_is_moving && last_element == EL_EXPLOSION)
4129 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4130 GfxElement[last_jx][last_jy] : EL_EMPTY);
4131 int graphic = el_act2img(element, ACTION_EXPLODING);
4132 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4133 int phase = ExplodePhase[last_jx][last_jy] - 1;
4134 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4137 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4140 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4142 // ------------------------------------------------------------------------
4143 // draw elements the player is just walking/passing through/under
4144 // ------------------------------------------------------------------------
4146 if (player_is_moving)
4148 // handle the field the player is leaving ...
4149 if (IS_ACCESSIBLE_INSIDE(last_element))
4150 DrawLevelField(last_jx, last_jy);
4151 else if (IS_ACCESSIBLE_UNDER(last_element))
4152 DrawLevelFieldThruMask(last_jx, last_jy);
4155 // do not redraw accessible elements if the player is just pushing them
4156 if (!player_is_moving || !player->is_pushing)
4158 // ... and the field the player is entering
4159 if (IS_ACCESSIBLE_INSIDE(element))
4160 DrawLevelField(jx, jy);
4161 else if (IS_ACCESSIBLE_UNDER(element))
4162 DrawLevelFieldThruMask(jx, jy);
4165 MarkTileDirty(sx, sy);
4169 void DrawPlayer(struct PlayerInfo *player)
4173 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4174 DrawPlayerExt(player, i);
4177 void DrawAllPlayers(void)
4181 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4182 for (j = 0; j < MAX_PLAYERS; j++)
4183 if (stored_player[j].active)
4184 DrawPlayerExt(&stored_player[j], i);
4187 void DrawPlayerField(int x, int y)
4189 if (!IS_PLAYER(x, y))
4192 DrawPlayer(PLAYERINFO(x, y));
4195 // ----------------------------------------------------------------------------
4197 void WaitForEventToContinue(void)
4199 boolean first_wait = TRUE;
4200 boolean still_wait = TRUE;
4202 if (program.headless)
4205 // simulate releasing mouse button over last gadget, if still pressed
4207 HandleGadgets(-1, -1, 0);
4209 button_status = MB_RELEASED;
4212 ClearPlayerAction();
4218 if (NextValidEvent(&event))
4222 case EVENT_BUTTONPRESS:
4223 case EVENT_FINGERPRESS:
4227 case EVENT_BUTTONRELEASE:
4228 case EVENT_FINGERRELEASE:
4229 still_wait = first_wait;
4232 case EVENT_KEYPRESS:
4233 case SDL_CONTROLLERBUTTONDOWN:
4234 case SDL_JOYBUTTONDOWN:
4239 HandleOtherEvents(&event);
4243 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4248 if (!PendingEvent())
4253 #define MAX_REQUEST_LINES 13
4254 #define MAX_REQUEST_LINE_FONT1_LEN 7
4255 #define MAX_REQUEST_LINE_FONT2_LEN 10
4257 static int RequestHandleEvents(unsigned int req_state)
4259 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4261 int width = request.width;
4262 int height = request.height;
4266 // when showing request dialog after game ended, deactivate game panel
4267 if (game_just_ended)
4268 game.panel.active = FALSE;
4270 game.request_active = TRUE;
4272 setRequestPosition(&sx, &sy, FALSE);
4274 button_status = MB_RELEASED;
4276 request_gadget_id = -1;
4281 if (game_just_ended)
4283 // the MM game engine does not use a special (scrollable) field buffer
4284 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4285 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4287 HandleGameActions();
4289 SetDrawtoField(DRAW_TO_BACKBUFFER);
4291 if (global.use_envelope_request)
4293 // copy current state of request area to middle of playfield area
4294 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4302 while (NextValidEvent(&event))
4306 case EVENT_BUTTONPRESS:
4307 case EVENT_BUTTONRELEASE:
4308 case EVENT_MOTIONNOTIFY:
4312 if (event.type == EVENT_MOTIONNOTIFY)
4317 motion_status = TRUE;
4318 mx = ((MotionEvent *) &event)->x;
4319 my = ((MotionEvent *) &event)->y;
4323 motion_status = FALSE;
4324 mx = ((ButtonEvent *) &event)->x;
4325 my = ((ButtonEvent *) &event)->y;
4326 if (event.type == EVENT_BUTTONPRESS)
4327 button_status = ((ButtonEvent *) &event)->button;
4329 button_status = MB_RELEASED;
4332 // this sets 'request_gadget_id'
4333 HandleGadgets(mx, my, button_status);
4335 switch (request_gadget_id)
4337 case TOOL_CTRL_ID_YES:
4338 case TOOL_CTRL_ID_TOUCH_YES:
4341 case TOOL_CTRL_ID_NO:
4342 case TOOL_CTRL_ID_TOUCH_NO:
4345 case TOOL_CTRL_ID_CONFIRM:
4346 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4347 result = TRUE | FALSE;
4350 case TOOL_CTRL_ID_PLAYER_1:
4353 case TOOL_CTRL_ID_PLAYER_2:
4356 case TOOL_CTRL_ID_PLAYER_3:
4359 case TOOL_CTRL_ID_PLAYER_4:
4364 // only check clickable animations if no request gadget clicked
4365 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4372 case SDL_WINDOWEVENT:
4373 HandleWindowEvent((WindowEvent *) &event);
4376 case SDL_APP_WILLENTERBACKGROUND:
4377 case SDL_APP_DIDENTERBACKGROUND:
4378 case SDL_APP_WILLENTERFOREGROUND:
4379 case SDL_APP_DIDENTERFOREGROUND:
4380 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4383 case EVENT_KEYPRESS:
4385 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4390 if (req_state & REQ_CONFIRM)
4399 #if defined(KSYM_Rewind)
4400 case KSYM_Rewind: // for Amazon Fire TV remote
4409 #if defined(KSYM_FastForward)
4410 case KSYM_FastForward: // for Amazon Fire TV remote
4416 HandleKeysDebug(key, KEY_PRESSED);
4420 if (req_state & REQ_PLAYER)
4422 int old_player_nr = setup.network_player_nr;
4425 result = old_player_nr + 1;
4430 result = old_player_nr + 1;
4461 case EVENT_FINGERRELEASE:
4462 case EVENT_KEYRELEASE:
4463 ClearPlayerAction();
4466 case SDL_CONTROLLERBUTTONDOWN:
4467 switch (event.cbutton.button)
4469 case SDL_CONTROLLER_BUTTON_A:
4470 case SDL_CONTROLLER_BUTTON_X:
4471 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4472 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4476 case SDL_CONTROLLER_BUTTON_B:
4477 case SDL_CONTROLLER_BUTTON_Y:
4478 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4479 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4480 case SDL_CONTROLLER_BUTTON_BACK:
4485 if (req_state & REQ_PLAYER)
4487 int old_player_nr = setup.network_player_nr;
4490 result = old_player_nr + 1;
4492 switch (event.cbutton.button)
4494 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4495 case SDL_CONTROLLER_BUTTON_Y:
4499 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4500 case SDL_CONTROLLER_BUTTON_B:
4504 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4505 case SDL_CONTROLLER_BUTTON_A:
4509 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4510 case SDL_CONTROLLER_BUTTON_X:
4521 case SDL_CONTROLLERBUTTONUP:
4522 HandleJoystickEvent(&event);
4523 ClearPlayerAction();
4527 HandleOtherEvents(&event);
4532 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4534 int joy = AnyJoystick();
4536 if (joy & JOY_BUTTON_1)
4538 else if (joy & JOY_BUTTON_2)
4541 else if (AnyJoystick())
4543 int joy = AnyJoystick();
4545 if (req_state & REQ_PLAYER)
4549 else if (joy & JOY_RIGHT)
4551 else if (joy & JOY_DOWN)
4553 else if (joy & JOY_LEFT)
4558 if (game_just_ended)
4560 if (global.use_envelope_request)
4562 // copy back current state of pressed buttons inside request area
4563 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4570 game.request_active = FALSE;
4575 static boolean RequestDoor(char *text, unsigned int req_state)
4577 unsigned int old_door_state;
4578 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4579 int font_nr = FONT_TEXT_2;
4584 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4586 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4587 font_nr = FONT_TEXT_1;
4590 if (game_status == GAME_MODE_PLAYING)
4591 BlitScreenToBitmap(backbuffer);
4593 // disable deactivated drawing when quick-loading level tape recording
4594 if (tape.playing && tape.deactivate_display)
4595 TapeDeactivateDisplayOff(TRUE);
4597 SetMouseCursor(CURSOR_DEFAULT);
4599 // pause network game while waiting for request to answer
4600 if (network.enabled &&
4601 game_status == GAME_MODE_PLAYING &&
4602 !game.all_players_gone &&
4603 req_state & REQUEST_WAIT_FOR_INPUT)
4604 SendToServer_PausePlaying();
4606 old_door_state = GetDoorState();
4608 // simulate releasing mouse button over last gadget, if still pressed
4610 HandleGadgets(-1, -1, 0);
4614 // draw released gadget before proceeding
4617 if (old_door_state & DOOR_OPEN_1)
4619 CloseDoor(DOOR_CLOSE_1);
4621 // save old door content
4622 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4623 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4626 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4627 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4629 // clear door drawing field
4630 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4632 // force DOOR font inside door area
4633 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4635 // write text for request
4636 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4638 char text_line[max_request_line_len + 1];
4644 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4646 tc = *(text_ptr + tx);
4647 // if (!tc || tc == ' ')
4648 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4652 if ((tc == '?' || tc == '!') && tl == 0)
4662 strncpy(text_line, text_ptr, tl);
4665 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4666 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4667 text_line, font_nr);
4669 text_ptr += tl + (tc == ' ' ? 1 : 0);
4670 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4675 if (req_state & REQ_ASK)
4677 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4678 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4679 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4680 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4682 else if (req_state & REQ_CONFIRM)
4684 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4685 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4687 else if (req_state & REQ_PLAYER)
4689 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4690 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4691 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4692 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4695 // copy request gadgets to door backbuffer
4696 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4698 OpenDoor(DOOR_OPEN_1);
4700 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4702 if (game_status == GAME_MODE_PLAYING)
4704 SetPanelBackground();
4705 SetDrawBackgroundMask(REDRAW_DOOR_1);
4709 SetDrawBackgroundMask(REDRAW_FIELD);
4715 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4717 // ---------- handle request buttons ----------
4718 result = RequestHandleEvents(req_state);
4722 if (!(req_state & REQ_STAY_OPEN))
4724 CloseDoor(DOOR_CLOSE_1);
4726 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4727 (req_state & REQ_REOPEN))
4728 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4733 if (game_status == GAME_MODE_PLAYING)
4735 SetPanelBackground();
4736 SetDrawBackgroundMask(REDRAW_DOOR_1);
4740 SetDrawBackgroundMask(REDRAW_FIELD);
4743 // continue network game after request
4744 if (network.enabled &&
4745 game_status == GAME_MODE_PLAYING &&
4746 !game.all_players_gone &&
4747 req_state & REQUEST_WAIT_FOR_INPUT)
4748 SendToServer_ContinuePlaying();
4750 // restore deactivated drawing when quick-loading level tape recording
4751 if (tape.playing && tape.deactivate_display)
4752 TapeDeactivateDisplayOn();
4757 static boolean RequestEnvelope(char *text, unsigned int req_state)
4761 if (game_status == GAME_MODE_PLAYING)
4762 BlitScreenToBitmap(backbuffer);
4764 // disable deactivated drawing when quick-loading level tape recording
4765 if (tape.playing && tape.deactivate_display)
4766 TapeDeactivateDisplayOff(TRUE);
4768 SetMouseCursor(CURSOR_DEFAULT);
4770 // pause network game while waiting for request to answer
4771 if (network.enabled &&
4772 game_status == GAME_MODE_PLAYING &&
4773 !game.all_players_gone &&
4774 req_state & REQUEST_WAIT_FOR_INPUT)
4775 SendToServer_PausePlaying();
4777 // simulate releasing mouse button over last gadget, if still pressed
4779 HandleGadgets(-1, -1, 0);
4783 // (replace with setting corresponding request background)
4784 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4785 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4787 // clear door drawing field
4788 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4790 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4792 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4794 if (game_status == GAME_MODE_PLAYING)
4796 SetPanelBackground();
4797 SetDrawBackgroundMask(REDRAW_DOOR_1);
4801 SetDrawBackgroundMask(REDRAW_FIELD);
4807 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4809 // ---------- handle request buttons ----------
4810 result = RequestHandleEvents(req_state);
4814 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4818 if (game_status == GAME_MODE_PLAYING)
4820 SetPanelBackground();
4821 SetDrawBackgroundMask(REDRAW_DOOR_1);
4825 SetDrawBackgroundMask(REDRAW_FIELD);
4828 // continue network game after request
4829 if (network.enabled &&
4830 game_status == GAME_MODE_PLAYING &&
4831 !game.all_players_gone &&
4832 req_state & REQUEST_WAIT_FOR_INPUT)
4833 SendToServer_ContinuePlaying();
4835 // restore deactivated drawing when quick-loading level tape recording
4836 if (tape.playing && tape.deactivate_display)
4837 TapeDeactivateDisplayOn();
4842 boolean Request(char *text, unsigned int req_state)
4844 boolean overlay_enabled = GetOverlayEnabled();
4847 SetOverlayEnabled(FALSE);
4849 if (global.use_envelope_request)
4850 result = RequestEnvelope(text, req_state);
4852 result = RequestDoor(text, req_state);
4854 SetOverlayEnabled(overlay_enabled);
4859 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4861 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4862 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4865 if (dpo1->sort_priority != dpo2->sort_priority)
4866 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4868 compare_result = dpo1->nr - dpo2->nr;
4870 return compare_result;
4873 void InitGraphicCompatibilityInfo_Doors(void)
4879 struct DoorInfo *door;
4883 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4884 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4886 { -1, -1, -1, NULL }
4888 struct Rect door_rect_list[] =
4890 { DX, DY, DXSIZE, DYSIZE },
4891 { VX, VY, VXSIZE, VYSIZE }
4895 for (i = 0; doors[i].door_token != -1; i++)
4897 int door_token = doors[i].door_token;
4898 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4899 int part_1 = doors[i].part_1;
4900 int part_8 = doors[i].part_8;
4901 int part_2 = part_1 + 1;
4902 int part_3 = part_1 + 2;
4903 struct DoorInfo *door = doors[i].door;
4904 struct Rect *door_rect = &door_rect_list[door_index];
4905 boolean door_gfx_redefined = FALSE;
4907 // check if any door part graphic definitions have been redefined
4909 for (j = 0; door_part_controls[j].door_token != -1; j++)
4911 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4912 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4914 if (dpc->door_token == door_token && fi->redefined)
4915 door_gfx_redefined = TRUE;
4918 // check for old-style door graphic/animation modifications
4920 if (!door_gfx_redefined)
4922 if (door->anim_mode & ANIM_STATIC_PANEL)
4924 door->panel.step_xoffset = 0;
4925 door->panel.step_yoffset = 0;
4928 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4930 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4931 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4932 int num_door_steps, num_panel_steps;
4934 // remove door part graphics other than the two default wings
4936 for (j = 0; door_part_controls[j].door_token != -1; j++)
4938 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4939 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4941 if (dpc->graphic >= part_3 &&
4942 dpc->graphic <= part_8)
4946 // set graphics and screen positions of the default wings
4948 g_part_1->width = door_rect->width;
4949 g_part_1->height = door_rect->height;
4950 g_part_2->width = door_rect->width;
4951 g_part_2->height = door_rect->height;
4952 g_part_2->src_x = door_rect->width;
4953 g_part_2->src_y = g_part_1->src_y;
4955 door->part_2.x = door->part_1.x;
4956 door->part_2.y = door->part_1.y;
4958 if (door->width != -1)
4960 g_part_1->width = door->width;
4961 g_part_2->width = door->width;
4963 // special treatment for graphics and screen position of right wing
4964 g_part_2->src_x += door_rect->width - door->width;
4965 door->part_2.x += door_rect->width - door->width;
4968 if (door->height != -1)
4970 g_part_1->height = door->height;
4971 g_part_2->height = door->height;
4973 // special treatment for graphics and screen position of bottom wing
4974 g_part_2->src_y += door_rect->height - door->height;
4975 door->part_2.y += door_rect->height - door->height;
4978 // set animation delays for the default wings and panels
4980 door->part_1.step_delay = door->step_delay;
4981 door->part_2.step_delay = door->step_delay;
4982 door->panel.step_delay = door->step_delay;
4984 // set animation draw order for the default wings
4986 door->part_1.sort_priority = 2; // draw left wing over ...
4987 door->part_2.sort_priority = 1; // ... right wing
4989 // set animation draw offset for the default wings
4991 if (door->anim_mode & ANIM_HORIZONTAL)
4993 door->part_1.step_xoffset = door->step_offset;
4994 door->part_1.step_yoffset = 0;
4995 door->part_2.step_xoffset = door->step_offset * -1;
4996 door->part_2.step_yoffset = 0;
4998 num_door_steps = g_part_1->width / door->step_offset;
5000 else // ANIM_VERTICAL
5002 door->part_1.step_xoffset = 0;
5003 door->part_1.step_yoffset = door->step_offset;
5004 door->part_2.step_xoffset = 0;
5005 door->part_2.step_yoffset = door->step_offset * -1;
5007 num_door_steps = g_part_1->height / door->step_offset;
5010 // set animation draw offset for the default panels
5012 if (door->step_offset > 1)
5014 num_panel_steps = 2 * door_rect->height / door->step_offset;
5015 door->panel.start_step = num_panel_steps - num_door_steps;
5016 door->panel.start_step_closing = door->panel.start_step;
5020 num_panel_steps = door_rect->height / door->step_offset;
5021 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5022 door->panel.start_step_closing = door->panel.start_step;
5023 door->panel.step_delay *= 2;
5030 void InitDoors(void)
5034 for (i = 0; door_part_controls[i].door_token != -1; i++)
5036 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5037 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5039 // initialize "start_step_opening" and "start_step_closing", if needed
5040 if (dpc->pos->start_step_opening == 0 &&
5041 dpc->pos->start_step_closing == 0)
5043 // dpc->pos->start_step_opening = dpc->pos->start_step;
5044 dpc->pos->start_step_closing = dpc->pos->start_step;
5047 // fill structure for door part draw order (sorted below)
5049 dpo->sort_priority = dpc->pos->sort_priority;
5052 // sort door part controls according to sort_priority and graphic number
5053 qsort(door_part_order, MAX_DOOR_PARTS,
5054 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5057 unsigned int OpenDoor(unsigned int door_state)
5059 if (door_state & DOOR_COPY_BACK)
5061 if (door_state & DOOR_OPEN_1)
5062 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5063 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5065 if (door_state & DOOR_OPEN_2)
5066 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5067 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5069 door_state &= ~DOOR_COPY_BACK;
5072 return MoveDoor(door_state);
5075 unsigned int CloseDoor(unsigned int door_state)
5077 unsigned int old_door_state = GetDoorState();
5079 if (!(door_state & DOOR_NO_COPY_BACK))
5081 if (old_door_state & DOOR_OPEN_1)
5082 BlitBitmap(backbuffer, bitmap_db_door_1,
5083 DX, DY, DXSIZE, DYSIZE, 0, 0);
5085 if (old_door_state & DOOR_OPEN_2)
5086 BlitBitmap(backbuffer, bitmap_db_door_2,
5087 VX, VY, VXSIZE, VYSIZE, 0, 0);
5089 door_state &= ~DOOR_NO_COPY_BACK;
5092 return MoveDoor(door_state);
5095 unsigned int GetDoorState(void)
5097 return MoveDoor(DOOR_GET_STATE);
5100 unsigned int SetDoorState(unsigned int door_state)
5102 return MoveDoor(door_state | DOOR_SET_STATE);
5105 static int euclid(int a, int b)
5107 return (b ? euclid(b, a % b) : a);
5110 unsigned int MoveDoor(unsigned int door_state)
5112 struct Rect door_rect_list[] =
5114 { DX, DY, DXSIZE, DYSIZE },
5115 { VX, VY, VXSIZE, VYSIZE }
5117 static int door1 = DOOR_CLOSE_1;
5118 static int door2 = DOOR_CLOSE_2;
5119 unsigned int door_delay = 0;
5120 unsigned int door_delay_value;
5123 if (door_state == DOOR_GET_STATE)
5124 return (door1 | door2);
5126 if (door_state & DOOR_SET_STATE)
5128 if (door_state & DOOR_ACTION_1)
5129 door1 = door_state & DOOR_ACTION_1;
5130 if (door_state & DOOR_ACTION_2)
5131 door2 = door_state & DOOR_ACTION_2;
5133 return (door1 | door2);
5136 if (!(door_state & DOOR_FORCE_REDRAW))
5138 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5139 door_state &= ~DOOR_OPEN_1;
5140 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5141 door_state &= ~DOOR_CLOSE_1;
5142 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5143 door_state &= ~DOOR_OPEN_2;
5144 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5145 door_state &= ~DOOR_CLOSE_2;
5148 if (global.autoplay_leveldir)
5150 door_state |= DOOR_NO_DELAY;
5151 door_state &= ~DOOR_CLOSE_ALL;
5154 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5155 door_state |= DOOR_NO_DELAY;
5157 if (door_state & DOOR_ACTION)
5159 boolean door_panel_drawn[NUM_DOORS];
5160 boolean panel_has_doors[NUM_DOORS];
5161 boolean door_part_skip[MAX_DOOR_PARTS];
5162 boolean door_part_done[MAX_DOOR_PARTS];
5163 boolean door_part_done_all;
5164 int num_steps[MAX_DOOR_PARTS];
5165 int max_move_delay = 0; // delay for complete animations of all doors
5166 int max_step_delay = 0; // delay (ms) between two animation frames
5167 int num_move_steps = 0; // number of animation steps for all doors
5168 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5169 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5170 int current_move_delay = 0;
5174 for (i = 0; i < NUM_DOORS; i++)
5175 panel_has_doors[i] = FALSE;
5177 for (i = 0; i < MAX_DOOR_PARTS; i++)
5179 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5180 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5181 int door_token = dpc->door_token;
5183 door_part_done[i] = FALSE;
5184 door_part_skip[i] = (!(door_state & door_token) ||
5188 for (i = 0; i < MAX_DOOR_PARTS; i++)
5190 int nr = door_part_order[i].nr;
5191 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5192 struct DoorPartPosInfo *pos = dpc->pos;
5193 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5194 int door_token = dpc->door_token;
5195 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5196 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5197 int step_xoffset = ABS(pos->step_xoffset);
5198 int step_yoffset = ABS(pos->step_yoffset);
5199 int step_delay = pos->step_delay;
5200 int current_door_state = door_state & door_token;
5201 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5202 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5203 boolean part_opening = (is_panel ? door_closing : door_opening);
5204 int start_step = (part_opening ? pos->start_step_opening :
5205 pos->start_step_closing);
5206 float move_xsize = (step_xoffset ? g->width : 0);
5207 float move_ysize = (step_yoffset ? g->height : 0);
5208 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5209 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5210 int move_steps = (move_xsteps && move_ysteps ?
5211 MIN(move_xsteps, move_ysteps) :
5212 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5213 int move_delay = move_steps * step_delay;
5215 if (door_part_skip[nr])
5218 max_move_delay = MAX(max_move_delay, move_delay);
5219 max_step_delay = (max_step_delay == 0 ? step_delay :
5220 euclid(max_step_delay, step_delay));
5221 num_steps[nr] = move_steps;
5225 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5227 panel_has_doors[door_index] = TRUE;
5231 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5233 num_move_steps = max_move_delay / max_step_delay;
5234 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5236 door_delay_value = max_step_delay;
5238 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5240 start = num_move_steps - 1;
5244 // opening door sound has priority over simultaneously closing door
5245 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5247 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5249 if (door_state & DOOR_OPEN_1)
5250 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5251 if (door_state & DOOR_OPEN_2)
5252 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5254 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5256 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5258 if (door_state & DOOR_CLOSE_1)
5259 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5260 if (door_state & DOOR_CLOSE_2)
5261 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5265 for (k = start; k < num_move_steps; k++)
5267 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5269 door_part_done_all = TRUE;
5271 for (i = 0; i < NUM_DOORS; i++)
5272 door_panel_drawn[i] = FALSE;
5274 for (i = 0; i < MAX_DOOR_PARTS; i++)
5276 int nr = door_part_order[i].nr;
5277 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5278 struct DoorPartPosInfo *pos = dpc->pos;
5279 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5280 int door_token = dpc->door_token;
5281 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5282 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5283 boolean is_panel_and_door_has_closed = FALSE;
5284 struct Rect *door_rect = &door_rect_list[door_index];
5285 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5287 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5288 int current_door_state = door_state & door_token;
5289 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5290 boolean door_closing = !door_opening;
5291 boolean part_opening = (is_panel ? door_closing : door_opening);
5292 boolean part_closing = !part_opening;
5293 int start_step = (part_opening ? pos->start_step_opening :
5294 pos->start_step_closing);
5295 int step_delay = pos->step_delay;
5296 int step_factor = step_delay / max_step_delay;
5297 int k1 = (step_factor ? k / step_factor + 1 : k);
5298 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5299 int kk = MAX(0, k2);
5302 int src_x, src_y, src_xx, src_yy;
5303 int dst_x, dst_y, dst_xx, dst_yy;
5306 if (door_part_skip[nr])
5309 if (!(door_state & door_token))
5317 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5318 int kk_door = MAX(0, k2_door);
5319 int sync_frame = kk_door * door_delay_value;
5320 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5322 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5323 &g_src_x, &g_src_y);
5328 if (!door_panel_drawn[door_index])
5330 ClearRectangle(drawto, door_rect->x, door_rect->y,
5331 door_rect->width, door_rect->height);
5333 door_panel_drawn[door_index] = TRUE;
5336 // draw opening or closing door parts
5338 if (pos->step_xoffset < 0) // door part on right side
5341 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5344 if (dst_xx + width > door_rect->width)
5345 width = door_rect->width - dst_xx;
5347 else // door part on left side
5350 dst_xx = pos->x - kk * pos->step_xoffset;
5354 src_xx = ABS(dst_xx);
5358 width = g->width - src_xx;
5360 if (width > door_rect->width)
5361 width = door_rect->width;
5363 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5366 if (pos->step_yoffset < 0) // door part on bottom side
5369 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5372 if (dst_yy + height > door_rect->height)
5373 height = door_rect->height - dst_yy;
5375 else // door part on top side
5378 dst_yy = pos->y - kk * pos->step_yoffset;
5382 src_yy = ABS(dst_yy);
5386 height = g->height - src_yy;
5389 src_x = g_src_x + src_xx;
5390 src_y = g_src_y + src_yy;
5392 dst_x = door_rect->x + dst_xx;
5393 dst_y = door_rect->y + dst_yy;
5395 is_panel_and_door_has_closed =
5398 panel_has_doors[door_index] &&
5399 k >= num_move_steps_doors_only - 1);
5401 if (width >= 0 && width <= g->width &&
5402 height >= 0 && height <= g->height &&
5403 !is_panel_and_door_has_closed)
5405 if (is_panel || !pos->draw_masked)
5406 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5409 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5413 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5415 if ((part_opening && (width < 0 || height < 0)) ||
5416 (part_closing && (width >= g->width && height >= g->height)))
5417 door_part_done[nr] = TRUE;
5419 // continue door part animations, but not panel after door has closed
5420 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5421 door_part_done_all = FALSE;
5424 if (!(door_state & DOOR_NO_DELAY))
5428 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5430 current_move_delay += max_step_delay;
5432 // prevent OS (Windows) from complaining about program not responding
5436 if (door_part_done_all)
5440 if (!(door_state & DOOR_NO_DELAY))
5442 // wait for specified door action post delay
5443 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5444 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5445 else if (door_state & DOOR_ACTION_1)
5446 door_delay_value = door_1.post_delay;
5447 else if (door_state & DOOR_ACTION_2)
5448 door_delay_value = door_2.post_delay;
5450 while (!DelayReached(&door_delay, door_delay_value))
5455 if (door_state & DOOR_ACTION_1)
5456 door1 = door_state & DOOR_ACTION_1;
5457 if (door_state & DOOR_ACTION_2)
5458 door2 = door_state & DOOR_ACTION_2;
5460 // draw masked border over door area
5461 DrawMaskedBorder(REDRAW_DOOR_1);
5462 DrawMaskedBorder(REDRAW_DOOR_2);
5464 ClearAutoRepeatKeyEvents();
5466 return (door1 | door2);
5469 static boolean useSpecialEditorDoor(void)
5471 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5472 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5474 // do not draw special editor door if editor border defined or redefined
5475 if (graphic_info[graphic].bitmap != NULL || redefined)
5478 // do not draw special editor door if global border defined to be empty
5479 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5482 // do not draw special editor door if viewport definitions do not match
5486 EY + EYSIZE != VY + VYSIZE)
5492 void DrawSpecialEditorDoor(void)
5494 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5495 int top_border_width = gfx1->width;
5496 int top_border_height = gfx1->height;
5497 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5498 int ex = EX - outer_border;
5499 int ey = EY - outer_border;
5500 int vy = VY - outer_border;
5501 int exsize = EXSIZE + 2 * outer_border;
5503 if (!useSpecialEditorDoor())
5506 // draw bigger level editor toolbox window
5507 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5508 top_border_width, top_border_height, ex, ey - top_border_height);
5509 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5510 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5512 redraw_mask |= REDRAW_ALL;
5515 void UndrawSpecialEditorDoor(void)
5517 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5518 int top_border_width = gfx1->width;
5519 int top_border_height = gfx1->height;
5520 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5521 int ex = EX - outer_border;
5522 int ey = EY - outer_border;
5523 int ey_top = ey - top_border_height;
5524 int exsize = EXSIZE + 2 * outer_border;
5525 int eysize = EYSIZE + 2 * outer_border;
5527 if (!useSpecialEditorDoor())
5530 // draw normal tape recorder window
5531 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5533 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5534 ex, ey_top, top_border_width, top_border_height,
5536 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5537 ex, ey, exsize, eysize, ex, ey);
5541 // if screen background is set to "[NONE]", clear editor toolbox window
5542 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5543 ClearRectangle(drawto, ex, ey, exsize, eysize);
5546 redraw_mask |= REDRAW_ALL;
5550 // ---------- new tool button stuff -------------------------------------------
5555 struct TextPosInfo *pos;
5557 boolean is_touch_button;
5559 } toolbutton_info[NUM_TOOL_BUTTONS] =
5562 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5563 TOOL_CTRL_ID_YES, FALSE, "yes"
5566 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5567 TOOL_CTRL_ID_NO, FALSE, "no"
5570 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5571 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5574 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5575 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5578 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5579 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5582 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5583 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5586 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5587 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5590 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5591 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5594 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5595 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5598 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5599 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5603 void CreateToolButtons(void)
5607 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5609 int graphic = toolbutton_info[i].graphic;
5610 struct GraphicInfo *gfx = &graphic_info[graphic];
5611 struct TextPosInfo *pos = toolbutton_info[i].pos;
5612 struct GadgetInfo *gi;
5613 Bitmap *deco_bitmap = None;
5614 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5615 unsigned int event_mask = GD_EVENT_RELEASED;
5616 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5617 int base_x = (is_touch_button ? 0 : DX);
5618 int base_y = (is_touch_button ? 0 : DY);
5619 int gd_x = gfx->src_x;
5620 int gd_y = gfx->src_y;
5621 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5622 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5627 if (global.use_envelope_request && !is_touch_button)
5629 setRequestPosition(&base_x, &base_y, TRUE);
5631 // check if request buttons are outside of envelope and fix, if needed
5632 if (x < 0 || x + gfx->width > request.width ||
5633 y < 0 || y + gfx->height > request.height)
5635 if (id == TOOL_CTRL_ID_YES)
5638 y = request.height - 2 * request.border_size - gfx->height;
5640 else if (id == TOOL_CTRL_ID_NO)
5642 x = request.width - 2 * request.border_size - gfx->width;
5643 y = request.height - 2 * request.border_size - gfx->height;
5645 else if (id == TOOL_CTRL_ID_CONFIRM)
5647 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5648 y = request.height - 2 * request.border_size - gfx->height;
5650 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5652 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5654 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5655 y = request.height - 2 * request.border_size - gfx->height * 2;
5657 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5658 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5663 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5665 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5667 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5668 pos->size, &deco_bitmap, &deco_x, &deco_y);
5669 deco_xpos = (gfx->width - pos->size) / 2;
5670 deco_ypos = (gfx->height - pos->size) / 2;
5673 gi = CreateGadget(GDI_CUSTOM_ID, id,
5674 GDI_IMAGE_ID, graphic,
5675 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5678 GDI_WIDTH, gfx->width,
5679 GDI_HEIGHT, gfx->height,
5680 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5681 GDI_STATE, GD_BUTTON_UNPRESSED,
5682 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5683 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5684 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5685 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5686 GDI_DECORATION_SIZE, pos->size, pos->size,
5687 GDI_DECORATION_SHIFTING, 1, 1,
5688 GDI_DIRECT_DRAW, FALSE,
5689 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5690 GDI_EVENT_MASK, event_mask,
5691 GDI_CALLBACK_ACTION, HandleToolButtons,
5695 Fail("cannot create gadget");
5697 tool_gadget[id] = gi;
5701 void FreeToolButtons(void)
5705 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5706 FreeGadget(tool_gadget[i]);
5709 static void UnmapToolButtons(void)
5713 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5714 UnmapGadget(tool_gadget[i]);
5717 static void HandleToolButtons(struct GadgetInfo *gi)
5719 request_gadget_id = gi->custom_id;
5722 static struct Mapping_EM_to_RND_object
5725 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5726 boolean is_backside; // backside of moving element
5732 em_object_mapping_list[GAME_TILE_MAX + 1] =
5735 Zborder, FALSE, FALSE,
5739 Zplayer, FALSE, FALSE,
5748 Ztank, FALSE, FALSE,
5752 Zeater, FALSE, FALSE,
5756 Zdynamite, FALSE, FALSE,
5760 Zboom, FALSE, FALSE,
5765 Xchain, FALSE, FALSE,
5766 EL_DEFAULT, ACTION_EXPLODING, -1
5769 Xboom_bug, FALSE, FALSE,
5770 EL_BUG, ACTION_EXPLODING, -1
5773 Xboom_tank, FALSE, FALSE,
5774 EL_SPACESHIP, ACTION_EXPLODING, -1
5777 Xboom_android, FALSE, FALSE,
5778 EL_EMC_ANDROID, ACTION_OTHER, -1
5781 Xboom_1, FALSE, FALSE,
5782 EL_DEFAULT, ACTION_EXPLODING, -1
5785 Xboom_2, FALSE, FALSE,
5786 EL_DEFAULT, ACTION_EXPLODING, -1
5790 Xblank, TRUE, FALSE,
5795 Xsplash_e, FALSE, FALSE,
5796 EL_ACID_SPLASH_RIGHT, -1, -1
5799 Xsplash_w, FALSE, FALSE,
5800 EL_ACID_SPLASH_LEFT, -1, -1
5804 Xplant, TRUE, FALSE,
5805 EL_EMC_PLANT, -1, -1
5808 Yplant, FALSE, FALSE,
5809 EL_EMC_PLANT, -1, -1
5813 Xacid_1, TRUE, FALSE,
5817 Xacid_2, FALSE, FALSE,
5821 Xacid_3, FALSE, FALSE,
5825 Xacid_4, FALSE, FALSE,
5829 Xacid_5, FALSE, FALSE,
5833 Xacid_6, FALSE, FALSE,
5837 Xacid_7, FALSE, FALSE,
5841 Xacid_8, FALSE, FALSE,
5846 Xfake_acid_1, TRUE, FALSE,
5847 EL_EMC_FAKE_ACID, -1, -1
5850 Xfake_acid_2, FALSE, FALSE,
5851 EL_EMC_FAKE_ACID, -1, -1
5854 Xfake_acid_3, FALSE, FALSE,
5855 EL_EMC_FAKE_ACID, -1, -1
5858 Xfake_acid_4, FALSE, FALSE,
5859 EL_EMC_FAKE_ACID, -1, -1
5862 Xfake_acid_5, FALSE, FALSE,
5863 EL_EMC_FAKE_ACID, -1, -1
5866 Xfake_acid_6, FALSE, FALSE,
5867 EL_EMC_FAKE_ACID, -1, -1
5870 Xfake_acid_7, FALSE, FALSE,
5871 EL_EMC_FAKE_ACID, -1, -1
5874 Xfake_acid_8, FALSE, FALSE,
5875 EL_EMC_FAKE_ACID, -1, -1
5879 Xgrass, TRUE, FALSE,
5880 EL_EMC_GRASS, -1, -1
5883 Ygrass_nB, FALSE, FALSE,
5884 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5887 Ygrass_eB, FALSE, FALSE,
5888 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5891 Ygrass_sB, FALSE, FALSE,
5892 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5895 Ygrass_wB, FALSE, FALSE,
5896 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5904 Ydirt_nB, FALSE, FALSE,
5905 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5908 Ydirt_eB, FALSE, FALSE,
5909 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5912 Ydirt_sB, FALSE, FALSE,
5913 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5916 Ydirt_wB, FALSE, FALSE,
5917 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5921 Xandroid, TRUE, FALSE,
5922 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5925 Xandroid_1_n, FALSE, FALSE,
5926 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5929 Xandroid_2_n, FALSE, FALSE,
5930 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5933 Xandroid_1_e, FALSE, FALSE,
5934 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5937 Xandroid_2_e, FALSE, FALSE,
5938 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5941 Xandroid_1_w, FALSE, FALSE,
5942 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5945 Xandroid_2_w, FALSE, FALSE,
5946 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5949 Xandroid_1_s, FALSE, FALSE,
5950 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5953 Xandroid_2_s, FALSE, FALSE,
5954 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5957 Yandroid_n, FALSE, FALSE,
5958 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5961 Yandroid_nB, FALSE, TRUE,
5962 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5965 Yandroid_ne, FALSE, FALSE,
5966 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5969 Yandroid_neB, FALSE, TRUE,
5970 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5973 Yandroid_e, FALSE, FALSE,
5974 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5977 Yandroid_eB, FALSE, TRUE,
5978 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5981 Yandroid_se, FALSE, FALSE,
5982 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5985 Yandroid_seB, FALSE, TRUE,
5986 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5989 Yandroid_s, FALSE, FALSE,
5990 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5993 Yandroid_sB, FALSE, TRUE,
5994 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5997 Yandroid_sw, FALSE, FALSE,
5998 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6001 Yandroid_swB, FALSE, TRUE,
6002 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6005 Yandroid_w, FALSE, FALSE,
6006 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6009 Yandroid_wB, FALSE, TRUE,
6010 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6013 Yandroid_nw, FALSE, FALSE,
6014 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6017 Yandroid_nwB, FALSE, TRUE,
6018 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6022 Xeater_n, TRUE, FALSE,
6023 EL_YAMYAM_UP, -1, -1
6026 Xeater_e, TRUE, FALSE,
6027 EL_YAMYAM_RIGHT, -1, -1
6030 Xeater_w, TRUE, FALSE,
6031 EL_YAMYAM_LEFT, -1, -1
6034 Xeater_s, TRUE, FALSE,
6035 EL_YAMYAM_DOWN, -1, -1
6038 Yeater_n, FALSE, FALSE,
6039 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6042 Yeater_nB, FALSE, TRUE,
6043 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6046 Yeater_e, FALSE, FALSE,
6047 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6050 Yeater_eB, FALSE, TRUE,
6051 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6054 Yeater_s, FALSE, FALSE,
6055 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6058 Yeater_sB, FALSE, TRUE,
6059 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6062 Yeater_w, FALSE, FALSE,
6063 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6066 Yeater_wB, FALSE, TRUE,
6067 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6070 Yeater_stone, FALSE, FALSE,
6071 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6074 Yeater_spring, FALSE, FALSE,
6075 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6079 Xalien, TRUE, FALSE,
6083 Xalien_pause, FALSE, FALSE,
6087 Yalien_n, FALSE, FALSE,
6088 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6091 Yalien_nB, FALSE, TRUE,
6092 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6095 Yalien_e, FALSE, FALSE,
6096 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6099 Yalien_eB, FALSE, TRUE,
6100 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6103 Yalien_s, FALSE, FALSE,
6104 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6107 Yalien_sB, FALSE, TRUE,
6108 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6111 Yalien_w, FALSE, FALSE,
6112 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6115 Yalien_wB, FALSE, TRUE,
6116 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6119 Yalien_stone, FALSE, FALSE,
6120 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6123 Yalien_spring, FALSE, FALSE,
6124 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6128 Xbug_1_n, TRUE, FALSE,
6132 Xbug_1_e, TRUE, FALSE,
6133 EL_BUG_RIGHT, -1, -1
6136 Xbug_1_s, TRUE, FALSE,
6140 Xbug_1_w, TRUE, FALSE,
6144 Xbug_2_n, FALSE, FALSE,
6148 Xbug_2_e, FALSE, FALSE,
6149 EL_BUG_RIGHT, -1, -1
6152 Xbug_2_s, FALSE, FALSE,
6156 Xbug_2_w, FALSE, FALSE,
6160 Ybug_n, FALSE, FALSE,
6161 EL_BUG, ACTION_MOVING, MV_BIT_UP
6164 Ybug_nB, FALSE, TRUE,
6165 EL_BUG, ACTION_MOVING, MV_BIT_UP
6168 Ybug_e, FALSE, FALSE,
6169 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6172 Ybug_eB, FALSE, TRUE,
6173 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6176 Ybug_s, FALSE, FALSE,
6177 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6180 Ybug_sB, FALSE, TRUE,
6181 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6184 Ybug_w, FALSE, FALSE,
6185 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6188 Ybug_wB, FALSE, TRUE,
6189 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6192 Ybug_w_n, FALSE, FALSE,
6193 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6196 Ybug_n_e, FALSE, FALSE,
6197 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6200 Ybug_e_s, FALSE, FALSE,
6201 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6204 Ybug_s_w, FALSE, FALSE,
6205 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6208 Ybug_e_n, FALSE, FALSE,
6209 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6212 Ybug_s_e, FALSE, FALSE,
6213 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6216 Ybug_w_s, FALSE, FALSE,
6217 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6220 Ybug_n_w, FALSE, FALSE,
6221 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6224 Ybug_stone, FALSE, FALSE,
6225 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6228 Ybug_spring, FALSE, FALSE,
6229 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6233 Xtank_1_n, TRUE, FALSE,
6234 EL_SPACESHIP_UP, -1, -1
6237 Xtank_1_e, TRUE, FALSE,
6238 EL_SPACESHIP_RIGHT, -1, -1
6241 Xtank_1_s, TRUE, FALSE,
6242 EL_SPACESHIP_DOWN, -1, -1
6245 Xtank_1_w, TRUE, FALSE,
6246 EL_SPACESHIP_LEFT, -1, -1
6249 Xtank_2_n, FALSE, FALSE,
6250 EL_SPACESHIP_UP, -1, -1
6253 Xtank_2_e, FALSE, FALSE,
6254 EL_SPACESHIP_RIGHT, -1, -1
6257 Xtank_2_s, FALSE, FALSE,
6258 EL_SPACESHIP_DOWN, -1, -1
6261 Xtank_2_w, FALSE, FALSE,
6262 EL_SPACESHIP_LEFT, -1, -1
6265 Ytank_n, FALSE, FALSE,
6266 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6269 Ytank_nB, FALSE, TRUE,
6270 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6273 Ytank_e, FALSE, FALSE,
6274 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6277 Ytank_eB, FALSE, TRUE,
6278 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6281 Ytank_s, FALSE, FALSE,
6282 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6285 Ytank_sB, FALSE, TRUE,
6286 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6289 Ytank_w, FALSE, FALSE,
6290 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6293 Ytank_wB, FALSE, TRUE,
6294 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6297 Ytank_w_n, FALSE, FALSE,
6298 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6301 Ytank_n_e, FALSE, FALSE,
6302 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6305 Ytank_e_s, FALSE, FALSE,
6306 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6309 Ytank_s_w, FALSE, FALSE,
6310 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6313 Ytank_e_n, FALSE, FALSE,
6314 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6317 Ytank_s_e, FALSE, FALSE,
6318 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6321 Ytank_w_s, FALSE, FALSE,
6322 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6325 Ytank_n_w, FALSE, FALSE,
6326 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6329 Ytank_stone, FALSE, FALSE,
6330 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6333 Ytank_spring, FALSE, FALSE,
6334 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6338 Xemerald, TRUE, FALSE,
6342 Xemerald_pause, FALSE, FALSE,
6346 Xemerald_fall, FALSE, FALSE,
6350 Xemerald_shine, FALSE, FALSE,
6351 EL_EMERALD, ACTION_TWINKLING, -1
6354 Yemerald_s, FALSE, FALSE,
6355 EL_EMERALD, ACTION_FALLING, -1
6358 Yemerald_sB, FALSE, TRUE,
6359 EL_EMERALD, ACTION_FALLING, -1
6362 Yemerald_e, FALSE, FALSE,
6363 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6366 Yemerald_eB, FALSE, TRUE,
6367 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6370 Yemerald_w, FALSE, FALSE,
6371 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6374 Yemerald_wB, FALSE, TRUE,
6375 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6378 Yemerald_blank, FALSE, FALSE,
6379 EL_EMERALD, ACTION_COLLECTING, -1
6383 Xdiamond, TRUE, FALSE,
6387 Xdiamond_pause, FALSE, FALSE,
6391 Xdiamond_fall, FALSE, FALSE,
6395 Xdiamond_shine, FALSE, FALSE,
6396 EL_DIAMOND, ACTION_TWINKLING, -1
6399 Ydiamond_s, FALSE, FALSE,
6400 EL_DIAMOND, ACTION_FALLING, -1
6403 Ydiamond_sB, FALSE, TRUE,
6404 EL_DIAMOND, ACTION_FALLING, -1
6407 Ydiamond_e, FALSE, FALSE,
6408 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6411 Ydiamond_eB, FALSE, TRUE,
6412 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6415 Ydiamond_w, FALSE, FALSE,
6416 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6419 Ydiamond_wB, FALSE, TRUE,
6420 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6423 Ydiamond_blank, FALSE, FALSE,
6424 EL_DIAMOND, ACTION_COLLECTING, -1
6427 Ydiamond_stone, FALSE, FALSE,
6428 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6432 Xstone, TRUE, FALSE,
6436 Xstone_pause, FALSE, FALSE,
6440 Xstone_fall, FALSE, FALSE,
6444 Ystone_s, FALSE, FALSE,
6445 EL_ROCK, ACTION_FALLING, -1
6448 Ystone_sB, FALSE, TRUE,
6449 EL_ROCK, ACTION_FALLING, -1
6452 Ystone_e, FALSE, FALSE,
6453 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6456 Ystone_eB, FALSE, TRUE,
6457 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6460 Ystone_w, FALSE, FALSE,
6461 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6464 Ystone_wB, FALSE, TRUE,
6465 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6473 Xbomb_pause, FALSE, FALSE,
6477 Xbomb_fall, FALSE, FALSE,
6481 Ybomb_s, FALSE, FALSE,
6482 EL_BOMB, ACTION_FALLING, -1
6485 Ybomb_sB, FALSE, TRUE,
6486 EL_BOMB, ACTION_FALLING, -1
6489 Ybomb_e, FALSE, FALSE,
6490 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6493 Ybomb_eB, FALSE, TRUE,
6494 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6497 Ybomb_w, FALSE, FALSE,
6498 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6501 Ybomb_wB, FALSE, TRUE,
6502 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6505 Ybomb_blank, FALSE, FALSE,
6506 EL_BOMB, ACTION_ACTIVATING, -1
6514 Xnut_pause, FALSE, FALSE,
6518 Xnut_fall, FALSE, FALSE,
6522 Ynut_s, FALSE, FALSE,
6523 EL_NUT, ACTION_FALLING, -1
6526 Ynut_sB, FALSE, TRUE,
6527 EL_NUT, ACTION_FALLING, -1
6530 Ynut_e, FALSE, FALSE,
6531 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6534 Ynut_eB, FALSE, TRUE,
6535 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6538 Ynut_w, FALSE, FALSE,
6539 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6542 Ynut_wB, FALSE, TRUE,
6543 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6546 Ynut_stone, FALSE, FALSE,
6547 EL_NUT, ACTION_BREAKING, -1
6551 Xspring, TRUE, FALSE,
6555 Xspring_pause, FALSE, FALSE,
6559 Xspring_e, TRUE, FALSE,
6560 EL_SPRING_RIGHT, -1, -1
6563 Xspring_w, TRUE, FALSE,
6564 EL_SPRING_LEFT, -1, -1
6567 Xspring_fall, FALSE, FALSE,
6571 Yspring_s, FALSE, FALSE,
6572 EL_SPRING, ACTION_FALLING, -1
6575 Yspring_sB, FALSE, TRUE,
6576 EL_SPRING, ACTION_FALLING, -1
6579 Yspring_e, FALSE, FALSE,
6580 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6583 Yspring_eB, FALSE, TRUE,
6584 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6587 Yspring_w, FALSE, FALSE,
6588 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6591 Yspring_wB, FALSE, TRUE,
6592 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6595 Yspring_alien_e, FALSE, FALSE,
6596 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6599 Yspring_alien_eB, FALSE, TRUE,
6600 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6603 Yspring_alien_w, FALSE, FALSE,
6604 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6607 Yspring_alien_wB, FALSE, TRUE,
6608 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6612 Xpush_emerald_e, FALSE, FALSE,
6613 EL_EMERALD, -1, MV_BIT_RIGHT
6616 Xpush_emerald_w, FALSE, FALSE,
6617 EL_EMERALD, -1, MV_BIT_LEFT
6620 Xpush_diamond_e, FALSE, FALSE,
6621 EL_DIAMOND, -1, MV_BIT_RIGHT
6624 Xpush_diamond_w, FALSE, FALSE,
6625 EL_DIAMOND, -1, MV_BIT_LEFT
6628 Xpush_stone_e, FALSE, FALSE,
6629 EL_ROCK, -1, MV_BIT_RIGHT
6632 Xpush_stone_w, FALSE, FALSE,
6633 EL_ROCK, -1, MV_BIT_LEFT
6636 Xpush_bomb_e, FALSE, FALSE,
6637 EL_BOMB, -1, MV_BIT_RIGHT
6640 Xpush_bomb_w, FALSE, FALSE,
6641 EL_BOMB, -1, MV_BIT_LEFT
6644 Xpush_nut_e, FALSE, FALSE,
6645 EL_NUT, -1, MV_BIT_RIGHT
6648 Xpush_nut_w, FALSE, FALSE,
6649 EL_NUT, -1, MV_BIT_LEFT
6652 Xpush_spring_e, FALSE, FALSE,
6653 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6656 Xpush_spring_w, FALSE, FALSE,
6657 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6661 Xdynamite, TRUE, FALSE,
6662 EL_EM_DYNAMITE, -1, -1
6665 Ydynamite_blank, FALSE, FALSE,
6666 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6669 Xdynamite_1, TRUE, FALSE,
6670 EL_EM_DYNAMITE_ACTIVE, -1, -1
6673 Xdynamite_2, FALSE, FALSE,
6674 EL_EM_DYNAMITE_ACTIVE, -1, -1
6677 Xdynamite_3, FALSE, FALSE,
6678 EL_EM_DYNAMITE_ACTIVE, -1, -1
6681 Xdynamite_4, FALSE, FALSE,
6682 EL_EM_DYNAMITE_ACTIVE, -1, -1
6686 Xkey_1, TRUE, FALSE,
6690 Xkey_2, TRUE, FALSE,
6694 Xkey_3, TRUE, FALSE,
6698 Xkey_4, TRUE, FALSE,
6702 Xkey_5, TRUE, FALSE,
6703 EL_EMC_KEY_5, -1, -1
6706 Xkey_6, TRUE, FALSE,
6707 EL_EMC_KEY_6, -1, -1
6710 Xkey_7, TRUE, FALSE,
6711 EL_EMC_KEY_7, -1, -1
6714 Xkey_8, TRUE, FALSE,
6715 EL_EMC_KEY_8, -1, -1
6719 Xdoor_1, TRUE, FALSE,
6720 EL_EM_GATE_1, -1, -1
6723 Xdoor_2, TRUE, FALSE,
6724 EL_EM_GATE_2, -1, -1
6727 Xdoor_3, TRUE, FALSE,
6728 EL_EM_GATE_3, -1, -1
6731 Xdoor_4, TRUE, FALSE,
6732 EL_EM_GATE_4, -1, -1
6735 Xdoor_5, TRUE, FALSE,
6736 EL_EMC_GATE_5, -1, -1
6739 Xdoor_6, TRUE, FALSE,
6740 EL_EMC_GATE_6, -1, -1
6743 Xdoor_7, TRUE, FALSE,
6744 EL_EMC_GATE_7, -1, -1
6747 Xdoor_8, TRUE, FALSE,
6748 EL_EMC_GATE_8, -1, -1
6752 Xfake_door_1, TRUE, FALSE,
6753 EL_EM_GATE_1_GRAY, -1, -1
6756 Xfake_door_2, TRUE, FALSE,
6757 EL_EM_GATE_2_GRAY, -1, -1
6760 Xfake_door_3, TRUE, FALSE,
6761 EL_EM_GATE_3_GRAY, -1, -1
6764 Xfake_door_4, TRUE, FALSE,
6765 EL_EM_GATE_4_GRAY, -1, -1
6768 Xfake_door_5, TRUE, FALSE,
6769 EL_EMC_GATE_5_GRAY, -1, -1
6772 Xfake_door_6, TRUE, FALSE,
6773 EL_EMC_GATE_6_GRAY, -1, -1
6776 Xfake_door_7, TRUE, FALSE,
6777 EL_EMC_GATE_7_GRAY, -1, -1
6780 Xfake_door_8, TRUE, FALSE,
6781 EL_EMC_GATE_8_GRAY, -1, -1
6785 Xballoon, TRUE, FALSE,
6789 Yballoon_n, FALSE, FALSE,
6790 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6793 Yballoon_nB, FALSE, TRUE,
6794 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6797 Yballoon_e, FALSE, FALSE,
6798 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6801 Yballoon_eB, FALSE, TRUE,
6802 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6805 Yballoon_s, FALSE, FALSE,
6806 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6809 Yballoon_sB, FALSE, TRUE,
6810 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6813 Yballoon_w, FALSE, FALSE,
6814 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6817 Yballoon_wB, FALSE, TRUE,
6818 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6822 Xball_1, TRUE, FALSE,
6823 EL_EMC_MAGIC_BALL, -1, -1
6826 Yball_1, FALSE, FALSE,
6827 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6830 Xball_2, FALSE, FALSE,
6831 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6834 Yball_2, FALSE, FALSE,
6835 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6838 Yball_blank, FALSE, FALSE,
6839 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6843 Xamoeba_1, TRUE, FALSE,
6844 EL_AMOEBA_DRY, ACTION_OTHER, -1
6847 Xamoeba_2, FALSE, FALSE,
6848 EL_AMOEBA_DRY, ACTION_OTHER, -1
6851 Xamoeba_3, FALSE, FALSE,
6852 EL_AMOEBA_DRY, ACTION_OTHER, -1
6855 Xamoeba_4, FALSE, FALSE,
6856 EL_AMOEBA_DRY, ACTION_OTHER, -1
6859 Xamoeba_5, TRUE, FALSE,
6860 EL_AMOEBA_WET, ACTION_OTHER, -1
6863 Xamoeba_6, FALSE, FALSE,
6864 EL_AMOEBA_WET, ACTION_OTHER, -1
6867 Xamoeba_7, FALSE, FALSE,
6868 EL_AMOEBA_WET, ACTION_OTHER, -1
6871 Xamoeba_8, FALSE, FALSE,
6872 EL_AMOEBA_WET, ACTION_OTHER, -1
6877 EL_AMOEBA_DROP, ACTION_GROWING, -1
6880 Xdrip_fall, FALSE, FALSE,
6881 EL_AMOEBA_DROP, -1, -1
6884 Xdrip_stretch, FALSE, FALSE,
6885 EL_AMOEBA_DROP, ACTION_FALLING, -1
6888 Xdrip_stretchB, FALSE, TRUE,
6889 EL_AMOEBA_DROP, ACTION_FALLING, -1
6892 Ydrip_1_s, FALSE, FALSE,
6893 EL_AMOEBA_DROP, ACTION_FALLING, -1
6896 Ydrip_1_sB, FALSE, TRUE,
6897 EL_AMOEBA_DROP, ACTION_FALLING, -1
6900 Ydrip_2_s, FALSE, FALSE,
6901 EL_AMOEBA_DROP, ACTION_FALLING, -1
6904 Ydrip_2_sB, FALSE, TRUE,
6905 EL_AMOEBA_DROP, ACTION_FALLING, -1
6909 Xwonderwall, TRUE, FALSE,
6910 EL_MAGIC_WALL, -1, -1
6913 Ywonderwall, FALSE, FALSE,
6914 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6918 Xwheel, TRUE, FALSE,
6919 EL_ROBOT_WHEEL, -1, -1
6922 Ywheel, FALSE, FALSE,
6923 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6927 Xswitch, TRUE, FALSE,
6928 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6931 Yswitch, FALSE, FALSE,
6932 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6936 Xbumper, TRUE, FALSE,
6937 EL_EMC_SPRING_BUMPER, -1, -1
6940 Ybumper, FALSE, FALSE,
6941 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6945 Xacid_nw, TRUE, FALSE,
6946 EL_ACID_POOL_TOPLEFT, -1, -1
6949 Xacid_ne, TRUE, FALSE,
6950 EL_ACID_POOL_TOPRIGHT, -1, -1
6953 Xacid_sw, TRUE, FALSE,
6954 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6957 Xacid_s, TRUE, FALSE,
6958 EL_ACID_POOL_BOTTOM, -1, -1
6961 Xacid_se, TRUE, FALSE,
6962 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6966 Xfake_blank, TRUE, FALSE,
6967 EL_INVISIBLE_WALL, -1, -1
6970 Yfake_blank, FALSE, FALSE,
6971 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6975 Xfake_grass, TRUE, FALSE,
6976 EL_EMC_FAKE_GRASS, -1, -1
6979 Yfake_grass, FALSE, FALSE,
6980 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6984 Xfake_amoeba, TRUE, FALSE,
6985 EL_EMC_DRIPPER, -1, -1
6988 Yfake_amoeba, FALSE, FALSE,
6989 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6993 Xlenses, TRUE, FALSE,
6994 EL_EMC_LENSES, -1, -1
6998 Xmagnify, TRUE, FALSE,
6999 EL_EMC_MAGNIFIER, -1, -1
7004 EL_QUICKSAND_EMPTY, -1, -1
7007 Xsand_stone, TRUE, FALSE,
7008 EL_QUICKSAND_FULL, -1, -1
7011 Xsand_stonein_1, FALSE, TRUE,
7012 EL_ROCK, ACTION_FILLING, -1
7015 Xsand_stonein_2, FALSE, TRUE,
7016 EL_ROCK, ACTION_FILLING, -1
7019 Xsand_stonein_3, FALSE, TRUE,
7020 EL_ROCK, ACTION_FILLING, -1
7023 Xsand_stonein_4, FALSE, TRUE,
7024 EL_ROCK, ACTION_FILLING, -1
7027 Xsand_sandstone_1, FALSE, FALSE,
7028 EL_QUICKSAND_FILLING, -1, -1
7031 Xsand_sandstone_2, FALSE, FALSE,
7032 EL_QUICKSAND_FILLING, -1, -1
7035 Xsand_sandstone_3, FALSE, FALSE,
7036 EL_QUICKSAND_FILLING, -1, -1
7039 Xsand_sandstone_4, FALSE, FALSE,
7040 EL_QUICKSAND_FILLING, -1, -1
7043 Xsand_stonesand_1, FALSE, FALSE,
7044 EL_QUICKSAND_EMPTYING, -1, -1
7047 Xsand_stonesand_2, FALSE, FALSE,
7048 EL_QUICKSAND_EMPTYING, -1, -1
7051 Xsand_stonesand_3, FALSE, FALSE,
7052 EL_QUICKSAND_EMPTYING, -1, -1
7055 Xsand_stonesand_4, FALSE, FALSE,
7056 EL_QUICKSAND_EMPTYING, -1, -1
7059 Xsand_stoneout_1, FALSE, FALSE,
7060 EL_ROCK, ACTION_EMPTYING, -1
7063 Xsand_stoneout_2, FALSE, FALSE,
7064 EL_ROCK, ACTION_EMPTYING, -1
7067 Xsand_stonesand_quickout_1, FALSE, FALSE,
7068 EL_QUICKSAND_EMPTYING, -1, -1
7071 Xsand_stonesand_quickout_2, FALSE, FALSE,
7072 EL_QUICKSAND_EMPTYING, -1, -1
7076 Xslide_ns, TRUE, FALSE,
7077 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7080 Yslide_ns_blank, FALSE, FALSE,
7081 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7084 Xslide_ew, TRUE, FALSE,
7085 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7088 Yslide_ew_blank, FALSE, FALSE,
7089 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7093 Xwind_n, TRUE, FALSE,
7094 EL_BALLOON_SWITCH_UP, -1, -1
7097 Xwind_e, TRUE, FALSE,
7098 EL_BALLOON_SWITCH_RIGHT, -1, -1
7101 Xwind_s, TRUE, FALSE,
7102 EL_BALLOON_SWITCH_DOWN, -1, -1
7105 Xwind_w, TRUE, FALSE,
7106 EL_BALLOON_SWITCH_LEFT, -1, -1
7109 Xwind_any, TRUE, FALSE,
7110 EL_BALLOON_SWITCH_ANY, -1, -1
7113 Xwind_stop, TRUE, FALSE,
7114 EL_BALLOON_SWITCH_NONE, -1, -1
7119 EL_EM_EXIT_CLOSED, -1, -1
7122 Xexit_1, TRUE, FALSE,
7123 EL_EM_EXIT_OPEN, -1, -1
7126 Xexit_2, FALSE, FALSE,
7127 EL_EM_EXIT_OPEN, -1, -1
7130 Xexit_3, FALSE, FALSE,
7131 EL_EM_EXIT_OPEN, -1, -1
7135 Xpause, FALSE, FALSE,
7140 Xwall_1, TRUE, FALSE,
7144 Xwall_2, TRUE, FALSE,
7145 EL_EMC_WALL_14, -1, -1
7148 Xwall_3, TRUE, FALSE,
7149 EL_EMC_WALL_15, -1, -1
7152 Xwall_4, TRUE, FALSE,
7153 EL_EMC_WALL_16, -1, -1
7157 Xroundwall_1, TRUE, FALSE,
7158 EL_WALL_SLIPPERY, -1, -1
7161 Xroundwall_2, TRUE, FALSE,
7162 EL_EMC_WALL_SLIPPERY_2, -1, -1
7165 Xroundwall_3, TRUE, FALSE,
7166 EL_EMC_WALL_SLIPPERY_3, -1, -1
7169 Xroundwall_4, TRUE, FALSE,
7170 EL_EMC_WALL_SLIPPERY_4, -1, -1
7174 Xsteel_1, TRUE, FALSE,
7175 EL_STEELWALL, -1, -1
7178 Xsteel_2, TRUE, FALSE,
7179 EL_EMC_STEELWALL_2, -1, -1
7182 Xsteel_3, TRUE, FALSE,
7183 EL_EMC_STEELWALL_3, -1, -1
7186 Xsteel_4, TRUE, FALSE,
7187 EL_EMC_STEELWALL_4, -1, -1
7191 Xdecor_1, TRUE, FALSE,
7192 EL_EMC_WALL_8, -1, -1
7195 Xdecor_2, TRUE, FALSE,
7196 EL_EMC_WALL_6, -1, -1
7199 Xdecor_3, TRUE, FALSE,
7200 EL_EMC_WALL_4, -1, -1
7203 Xdecor_4, TRUE, FALSE,
7204 EL_EMC_WALL_7, -1, -1
7207 Xdecor_5, TRUE, FALSE,
7208 EL_EMC_WALL_5, -1, -1
7211 Xdecor_6, TRUE, FALSE,
7212 EL_EMC_WALL_9, -1, -1
7215 Xdecor_7, TRUE, FALSE,
7216 EL_EMC_WALL_10, -1, -1
7219 Xdecor_8, TRUE, FALSE,
7220 EL_EMC_WALL_1, -1, -1
7223 Xdecor_9, TRUE, FALSE,
7224 EL_EMC_WALL_2, -1, -1
7227 Xdecor_10, TRUE, FALSE,
7228 EL_EMC_WALL_3, -1, -1
7231 Xdecor_11, TRUE, FALSE,
7232 EL_EMC_WALL_11, -1, -1
7235 Xdecor_12, TRUE, FALSE,
7236 EL_EMC_WALL_12, -1, -1
7240 Xalpha_0, TRUE, FALSE,
7241 EL_CHAR('0'), -1, -1
7244 Xalpha_1, TRUE, FALSE,
7245 EL_CHAR('1'), -1, -1
7248 Xalpha_2, TRUE, FALSE,
7249 EL_CHAR('2'), -1, -1
7252 Xalpha_3, TRUE, FALSE,
7253 EL_CHAR('3'), -1, -1
7256 Xalpha_4, TRUE, FALSE,
7257 EL_CHAR('4'), -1, -1
7260 Xalpha_5, TRUE, FALSE,
7261 EL_CHAR('5'), -1, -1
7264 Xalpha_6, TRUE, FALSE,
7265 EL_CHAR('6'), -1, -1
7268 Xalpha_7, TRUE, FALSE,
7269 EL_CHAR('7'), -1, -1
7272 Xalpha_8, TRUE, FALSE,
7273 EL_CHAR('8'), -1, -1
7276 Xalpha_9, TRUE, FALSE,
7277 EL_CHAR('9'), -1, -1
7280 Xalpha_excla, TRUE, FALSE,
7281 EL_CHAR('!'), -1, -1
7284 Xalpha_apost, TRUE, FALSE,
7285 EL_CHAR('\''), -1, -1
7288 Xalpha_comma, TRUE, FALSE,
7289 EL_CHAR(','), -1, -1
7292 Xalpha_minus, TRUE, FALSE,
7293 EL_CHAR('-'), -1, -1
7296 Xalpha_perio, TRUE, FALSE,
7297 EL_CHAR('.'), -1, -1
7300 Xalpha_colon, TRUE, FALSE,
7301 EL_CHAR(':'), -1, -1
7304 Xalpha_quest, TRUE, FALSE,
7305 EL_CHAR('?'), -1, -1
7308 Xalpha_a, TRUE, FALSE,
7309 EL_CHAR('A'), -1, -1
7312 Xalpha_b, TRUE, FALSE,
7313 EL_CHAR('B'), -1, -1
7316 Xalpha_c, TRUE, FALSE,
7317 EL_CHAR('C'), -1, -1
7320 Xalpha_d, TRUE, FALSE,
7321 EL_CHAR('D'), -1, -1
7324 Xalpha_e, TRUE, FALSE,
7325 EL_CHAR('E'), -1, -1
7328 Xalpha_f, TRUE, FALSE,
7329 EL_CHAR('F'), -1, -1
7332 Xalpha_g, TRUE, FALSE,
7333 EL_CHAR('G'), -1, -1
7336 Xalpha_h, TRUE, FALSE,
7337 EL_CHAR('H'), -1, -1
7340 Xalpha_i, TRUE, FALSE,
7341 EL_CHAR('I'), -1, -1
7344 Xalpha_j, TRUE, FALSE,
7345 EL_CHAR('J'), -1, -1
7348 Xalpha_k, TRUE, FALSE,
7349 EL_CHAR('K'), -1, -1
7352 Xalpha_l, TRUE, FALSE,
7353 EL_CHAR('L'), -1, -1
7356 Xalpha_m, TRUE, FALSE,
7357 EL_CHAR('M'), -1, -1
7360 Xalpha_n, TRUE, FALSE,
7361 EL_CHAR('N'), -1, -1
7364 Xalpha_o, TRUE, FALSE,
7365 EL_CHAR('O'), -1, -1
7368 Xalpha_p, TRUE, FALSE,
7369 EL_CHAR('P'), -1, -1
7372 Xalpha_q, TRUE, FALSE,
7373 EL_CHAR('Q'), -1, -1
7376 Xalpha_r, TRUE, FALSE,
7377 EL_CHAR('R'), -1, -1
7380 Xalpha_s, TRUE, FALSE,
7381 EL_CHAR('S'), -1, -1
7384 Xalpha_t, TRUE, FALSE,
7385 EL_CHAR('T'), -1, -1
7388 Xalpha_u, TRUE, FALSE,
7389 EL_CHAR('U'), -1, -1
7392 Xalpha_v, TRUE, FALSE,
7393 EL_CHAR('V'), -1, -1
7396 Xalpha_w, TRUE, FALSE,
7397 EL_CHAR('W'), -1, -1
7400 Xalpha_x, TRUE, FALSE,
7401 EL_CHAR('X'), -1, -1
7404 Xalpha_y, TRUE, FALSE,
7405 EL_CHAR('Y'), -1, -1
7408 Xalpha_z, TRUE, FALSE,
7409 EL_CHAR('Z'), -1, -1
7412 Xalpha_arrow_e, TRUE, FALSE,
7413 EL_CHAR('>'), -1, -1
7416 Xalpha_arrow_w, TRUE, FALSE,
7417 EL_CHAR('<'), -1, -1
7420 Xalpha_copyr, TRUE, FALSE,
7421 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7425 Ykey_1_blank, FALSE, FALSE,
7426 EL_EM_KEY_1, ACTION_COLLECTING, -1
7429 Ykey_2_blank, FALSE, FALSE,
7430 EL_EM_KEY_2, ACTION_COLLECTING, -1
7433 Ykey_3_blank, FALSE, FALSE,
7434 EL_EM_KEY_3, ACTION_COLLECTING, -1
7437 Ykey_4_blank, FALSE, FALSE,
7438 EL_EM_KEY_4, ACTION_COLLECTING, -1
7441 Ykey_5_blank, FALSE, FALSE,
7442 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7445 Ykey_6_blank, FALSE, FALSE,
7446 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7449 Ykey_7_blank, FALSE, FALSE,
7450 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7453 Ykey_8_blank, FALSE, FALSE,
7454 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7457 Ylenses_blank, FALSE, FALSE,
7458 EL_EMC_LENSES, ACTION_COLLECTING, -1
7461 Ymagnify_blank, FALSE, FALSE,
7462 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7465 Ygrass_blank, FALSE, FALSE,
7466 EL_EMC_GRASS, ACTION_SNAPPING, -1
7469 Ydirt_blank, FALSE, FALSE,
7470 EL_SAND, ACTION_SNAPPING, -1
7479 static struct Mapping_EM_to_RND_player
7488 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7492 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7496 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7500 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7504 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7508 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7512 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7516 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7520 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7524 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7528 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7532 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7536 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7540 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7544 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7548 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7552 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7556 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7560 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7564 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7568 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7572 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7576 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7580 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7584 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7588 EL_PLAYER_1, ACTION_DEFAULT, -1,
7592 EL_PLAYER_2, ACTION_DEFAULT, -1,
7596 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7600 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7604 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7608 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7612 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7616 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7620 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7624 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7628 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7632 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7636 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7640 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7644 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7648 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7652 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7656 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7660 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7664 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7668 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7672 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7676 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7680 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7684 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7688 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7692 EL_PLAYER_3, ACTION_DEFAULT, -1,
7696 EL_PLAYER_4, ACTION_DEFAULT, -1,
7705 int map_element_RND_to_EM_cave(int element_rnd)
7707 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7708 static boolean mapping_initialized = FALSE;
7710 if (!mapping_initialized)
7714 // return "Xalpha_quest" for all undefined elements in mapping array
7715 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7716 mapping_RND_to_EM[i] = Xalpha_quest;
7718 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7719 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7720 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7721 em_object_mapping_list[i].element_em;
7723 mapping_initialized = TRUE;
7726 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7728 Warn("invalid RND level element %d", element_rnd);
7733 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7736 int map_element_EM_to_RND_cave(int element_em_cave)
7738 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7739 static boolean mapping_initialized = FALSE;
7741 if (!mapping_initialized)
7745 // return "EL_UNKNOWN" for all undefined elements in mapping array
7746 for (i = 0; i < GAME_TILE_MAX; i++)
7747 mapping_EM_to_RND[i] = EL_UNKNOWN;
7749 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7750 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7751 em_object_mapping_list[i].element_rnd;
7753 mapping_initialized = TRUE;
7756 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7758 Warn("invalid EM cave element %d", element_em_cave);
7763 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7766 int map_element_EM_to_RND_game(int element_em_game)
7768 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7769 static boolean mapping_initialized = FALSE;
7771 if (!mapping_initialized)
7775 // return "EL_UNKNOWN" for all undefined elements in mapping array
7776 for (i = 0; i < GAME_TILE_MAX; i++)
7777 mapping_EM_to_RND[i] = EL_UNKNOWN;
7779 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7780 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7781 em_object_mapping_list[i].element_rnd;
7783 mapping_initialized = TRUE;
7786 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7788 Warn("invalid EM game element %d", element_em_game);
7793 return mapping_EM_to_RND[element_em_game];
7796 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7798 struct LevelInfo_EM *level_em = level->native_em_level;
7799 struct CAVE *cav = level_em->cav;
7802 for (i = 0; i < GAME_TILE_MAX; i++)
7803 cav->android_array[i] = Cblank;
7805 for (i = 0; i < level->num_android_clone_elements; i++)
7807 int element_rnd = level->android_clone_element[i];
7808 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7810 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7811 if (em_object_mapping_list[j].element_rnd == element_rnd)
7812 cav->android_array[em_object_mapping_list[j].element_em] =
7817 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7819 struct LevelInfo_EM *level_em = level->native_em_level;
7820 struct CAVE *cav = level_em->cav;
7823 level->num_android_clone_elements = 0;
7825 for (i = 0; i < GAME_TILE_MAX; i++)
7827 int element_em_cave = cav->android_array[i];
7829 boolean element_found = FALSE;
7831 if (element_em_cave == Cblank)
7834 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7836 for (j = 0; j < level->num_android_clone_elements; j++)
7837 if (level->android_clone_element[j] == element_rnd)
7838 element_found = TRUE;
7842 level->android_clone_element[level->num_android_clone_elements++] =
7845 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7850 if (level->num_android_clone_elements == 0)
7852 level->num_android_clone_elements = 1;
7853 level->android_clone_element[0] = EL_EMPTY;
7857 int map_direction_RND_to_EM(int direction)
7859 return (direction == MV_UP ? 0 :
7860 direction == MV_RIGHT ? 1 :
7861 direction == MV_DOWN ? 2 :
7862 direction == MV_LEFT ? 3 :
7866 int map_direction_EM_to_RND(int direction)
7868 return (direction == 0 ? MV_UP :
7869 direction == 1 ? MV_RIGHT :
7870 direction == 2 ? MV_DOWN :
7871 direction == 3 ? MV_LEFT :
7875 int map_element_RND_to_SP(int element_rnd)
7877 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7879 if (element_rnd >= EL_SP_START &&
7880 element_rnd <= EL_SP_END)
7881 element_sp = element_rnd - EL_SP_START;
7882 else if (element_rnd == EL_EMPTY_SPACE)
7884 else if (element_rnd == EL_INVISIBLE_WALL)
7890 int map_element_SP_to_RND(int element_sp)
7892 int element_rnd = EL_UNKNOWN;
7894 if (element_sp >= 0x00 &&
7896 element_rnd = EL_SP_START + element_sp;
7897 else if (element_sp == 0x28)
7898 element_rnd = EL_INVISIBLE_WALL;
7903 int map_action_SP_to_RND(int action_sp)
7907 case actActive: return ACTION_ACTIVE;
7908 case actImpact: return ACTION_IMPACT;
7909 case actExploding: return ACTION_EXPLODING;
7910 case actDigging: return ACTION_DIGGING;
7911 case actSnapping: return ACTION_SNAPPING;
7912 case actCollecting: return ACTION_COLLECTING;
7913 case actPassing: return ACTION_PASSING;
7914 case actPushing: return ACTION_PUSHING;
7915 case actDropping: return ACTION_DROPPING;
7917 default: return ACTION_DEFAULT;
7921 int map_element_RND_to_MM(int element_rnd)
7923 return (element_rnd >= EL_MM_START_1 &&
7924 element_rnd <= EL_MM_END_1 ?
7925 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7927 element_rnd >= EL_MM_START_2 &&
7928 element_rnd <= EL_MM_END_2 ?
7929 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7931 element_rnd >= EL_CHAR_START &&
7932 element_rnd <= EL_CHAR_END ?
7933 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7935 element_rnd >= EL_MM_RUNTIME_START &&
7936 element_rnd <= EL_MM_RUNTIME_END ?
7937 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7939 element_rnd >= EL_MM_DUMMY_START &&
7940 element_rnd <= EL_MM_DUMMY_END ?
7941 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7943 EL_MM_EMPTY_NATIVE);
7946 int map_element_MM_to_RND(int element_mm)
7948 return (element_mm == EL_MM_EMPTY_NATIVE ||
7949 element_mm == EL_DF_EMPTY_NATIVE ?
7952 element_mm >= EL_MM_START_1_NATIVE &&
7953 element_mm <= EL_MM_END_1_NATIVE ?
7954 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7956 element_mm >= EL_MM_START_2_NATIVE &&
7957 element_mm <= EL_MM_END_2_NATIVE ?
7958 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7960 element_mm >= EL_MM_CHAR_START_NATIVE &&
7961 element_mm <= EL_MM_CHAR_END_NATIVE ?
7962 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7964 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7965 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7966 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7968 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7969 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7970 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7975 int map_action_MM_to_RND(int action_mm)
7977 // all MM actions are defined to exactly match their RND counterparts
7981 int map_sound_MM_to_RND(int sound_mm)
7985 case SND_MM_GAME_LEVELTIME_CHARGING:
7986 return SND_GAME_LEVELTIME_CHARGING;
7988 case SND_MM_GAME_HEALTH_CHARGING:
7989 return SND_GAME_HEALTH_CHARGING;
7992 return SND_UNDEFINED;
7996 int map_mm_wall_element(int element)
7998 return (element >= EL_MM_STEEL_WALL_START &&
7999 element <= EL_MM_STEEL_WALL_END ?
8002 element >= EL_MM_WOODEN_WALL_START &&
8003 element <= EL_MM_WOODEN_WALL_END ?
8006 element >= EL_MM_ICE_WALL_START &&
8007 element <= EL_MM_ICE_WALL_END ?
8010 element >= EL_MM_AMOEBA_WALL_START &&
8011 element <= EL_MM_AMOEBA_WALL_END ?
8014 element >= EL_DF_STEEL_WALL_START &&
8015 element <= EL_DF_STEEL_WALL_END ?
8018 element >= EL_DF_WOODEN_WALL_START &&
8019 element <= EL_DF_WOODEN_WALL_END ?
8025 int map_mm_wall_element_editor(int element)
8029 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8030 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8031 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8032 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8033 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8034 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8036 default: return element;
8040 int get_next_element(int element)
8044 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8045 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8046 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8047 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8048 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8049 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8050 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8051 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8052 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8053 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8054 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8056 default: return element;
8060 int el2img_mm(int element_mm)
8062 return el2img(map_element_MM_to_RND(element_mm));
8065 int el_act_dir2img(int element, int action, int direction)
8067 element = GFX_ELEMENT(element);
8068 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8070 // direction_graphic[][] == graphic[] for undefined direction graphics
8071 return element_info[element].direction_graphic[action][direction];
8074 static int el_act_dir2crm(int element, int action, int direction)
8076 element = GFX_ELEMENT(element);
8077 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8079 // direction_graphic[][] == graphic[] for undefined direction graphics
8080 return element_info[element].direction_crumbled[action][direction];
8083 int el_act2img(int element, int action)
8085 element = GFX_ELEMENT(element);
8087 return element_info[element].graphic[action];
8090 int el_act2crm(int element, int action)
8092 element = GFX_ELEMENT(element);
8094 return element_info[element].crumbled[action];
8097 int el_dir2img(int element, int direction)
8099 element = GFX_ELEMENT(element);
8101 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8104 int el2baseimg(int element)
8106 return element_info[element].graphic[ACTION_DEFAULT];
8109 int el2img(int element)
8111 element = GFX_ELEMENT(element);
8113 return element_info[element].graphic[ACTION_DEFAULT];
8116 int el2edimg(int element)
8118 element = GFX_ELEMENT(element);
8120 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8123 int el2preimg(int element)
8125 element = GFX_ELEMENT(element);
8127 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8130 int el2panelimg(int element)
8132 element = GFX_ELEMENT(element);
8134 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8137 int font2baseimg(int font_nr)
8139 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8142 int getBeltNrFromBeltElement(int element)
8144 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8145 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8146 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8149 int getBeltNrFromBeltActiveElement(int element)
8151 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8152 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8153 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8156 int getBeltNrFromBeltSwitchElement(int element)
8158 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8159 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8160 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8163 int getBeltDirNrFromBeltElement(int element)
8165 static int belt_base_element[4] =
8167 EL_CONVEYOR_BELT_1_LEFT,
8168 EL_CONVEYOR_BELT_2_LEFT,
8169 EL_CONVEYOR_BELT_3_LEFT,
8170 EL_CONVEYOR_BELT_4_LEFT
8173 int belt_nr = getBeltNrFromBeltElement(element);
8174 int belt_dir_nr = element - belt_base_element[belt_nr];
8176 return (belt_dir_nr % 3);
8179 int getBeltDirNrFromBeltSwitchElement(int element)
8181 static int belt_base_element[4] =
8183 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8184 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8185 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8186 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8189 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8190 int belt_dir_nr = element - belt_base_element[belt_nr];
8192 return (belt_dir_nr % 3);
8195 int getBeltDirFromBeltElement(int element)
8197 static int belt_move_dir[3] =
8204 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8206 return belt_move_dir[belt_dir_nr];
8209 int getBeltDirFromBeltSwitchElement(int element)
8211 static int belt_move_dir[3] =
8218 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8220 return belt_move_dir[belt_dir_nr];
8223 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8225 static int belt_base_element[4] =
8227 EL_CONVEYOR_BELT_1_LEFT,
8228 EL_CONVEYOR_BELT_2_LEFT,
8229 EL_CONVEYOR_BELT_3_LEFT,
8230 EL_CONVEYOR_BELT_4_LEFT
8233 return belt_base_element[belt_nr] + belt_dir_nr;
8236 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8238 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8240 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8243 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8245 static int belt_base_element[4] =
8247 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8248 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8249 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8250 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8253 return belt_base_element[belt_nr] + belt_dir_nr;
8256 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8258 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8260 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8263 boolean swapTiles_EM(boolean is_pre_emc_cave)
8265 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8268 boolean getTeamMode_EM(void)
8270 return game.team_mode || network_playing;
8273 boolean isActivePlayer_EM(int player_nr)
8275 return stored_player[player_nr].active;
8278 unsigned int InitRND(int seed)
8280 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8281 return InitEngineRandom_EM(seed);
8282 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8283 return InitEngineRandom_SP(seed);
8284 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8285 return InitEngineRandom_MM(seed);
8287 return InitEngineRandom_RND(seed);
8290 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8291 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8293 static int get_effective_element_EM(int tile, int frame_em)
8295 int element = object_mapping[tile].element_rnd;
8296 int action = object_mapping[tile].action;
8297 boolean is_backside = object_mapping[tile].is_backside;
8298 boolean action_removing = (action == ACTION_DIGGING ||
8299 action == ACTION_SNAPPING ||
8300 action == ACTION_COLLECTING);
8308 return (frame_em > 5 ? EL_EMPTY : element);
8314 else // frame_em == 7
8325 case Ydiamond_stone:
8329 case Xdrip_stretchB:
8345 case Ymagnify_blank:
8348 case Xsand_stonein_1:
8349 case Xsand_stonein_2:
8350 case Xsand_stonein_3:
8351 case Xsand_stonein_4:
8355 return (is_backside || action_removing ? EL_EMPTY : element);
8360 static boolean check_linear_animation_EM(int tile)
8364 case Xsand_stonesand_1:
8365 case Xsand_stonesand_quickout_1:
8366 case Xsand_sandstone_1:
8367 case Xsand_stonein_1:
8368 case Xsand_stoneout_1:
8396 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8397 boolean has_crumbled_graphics,
8398 int crumbled, int sync_frame)
8400 // if element can be crumbled, but certain action graphics are just empty
8401 // space (like instantly snapping sand to empty space in 1 frame), do not
8402 // treat these empty space graphics as crumbled graphics in EMC engine
8403 if (crumbled == IMG_EMPTY_SPACE)
8404 has_crumbled_graphics = FALSE;
8406 if (has_crumbled_graphics)
8408 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8409 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8410 g_crumbled->anim_delay,
8411 g_crumbled->anim_mode,
8412 g_crumbled->anim_start_frame,
8415 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8416 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8418 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8419 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8421 g_em->has_crumbled_graphics = TRUE;
8425 g_em->crumbled_bitmap = NULL;
8426 g_em->crumbled_src_x = 0;
8427 g_em->crumbled_src_y = 0;
8428 g_em->crumbled_border_size = 0;
8429 g_em->crumbled_tile_size = 0;
8431 g_em->has_crumbled_graphics = FALSE;
8436 void ResetGfxAnimation_EM(int x, int y, int tile)
8442 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8443 int tile, int frame_em, int x, int y)
8445 int action = object_mapping[tile].action;
8446 int direction = object_mapping[tile].direction;
8447 int effective_element = get_effective_element_EM(tile, frame_em);
8448 int graphic = (direction == MV_NONE ?
8449 el_act2img(effective_element, action) :
8450 el_act_dir2img(effective_element, action, direction));
8451 struct GraphicInfo *g = &graphic_info[graphic];
8453 boolean action_removing = (action == ACTION_DIGGING ||
8454 action == ACTION_SNAPPING ||
8455 action == ACTION_COLLECTING);
8456 boolean action_moving = (action == ACTION_FALLING ||
8457 action == ACTION_MOVING ||
8458 action == ACTION_PUSHING ||
8459 action == ACTION_EATING ||
8460 action == ACTION_FILLING ||
8461 action == ACTION_EMPTYING);
8462 boolean action_falling = (action == ACTION_FALLING ||
8463 action == ACTION_FILLING ||
8464 action == ACTION_EMPTYING);
8466 // special case: graphic uses "2nd movement tile" and has defined
8467 // 7 frames for movement animation (or less) => use default graphic
8468 // for last (8th) frame which ends the movement animation
8469 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8471 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8472 graphic = (direction == MV_NONE ?
8473 el_act2img(effective_element, action) :
8474 el_act_dir2img(effective_element, action, direction));
8476 g = &graphic_info[graphic];
8479 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8483 else if (action_moving)
8485 boolean is_backside = object_mapping[tile].is_backside;
8489 int direction = object_mapping[tile].direction;
8490 int move_dir = (action_falling ? MV_DOWN : direction);
8495 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8496 if (g->double_movement && frame_em == 0)
8500 if (move_dir == MV_LEFT)
8501 GfxFrame[x - 1][y] = GfxFrame[x][y];
8502 else if (move_dir == MV_RIGHT)
8503 GfxFrame[x + 1][y] = GfxFrame[x][y];
8504 else if (move_dir == MV_UP)
8505 GfxFrame[x][y - 1] = GfxFrame[x][y];
8506 else if (move_dir == MV_DOWN)
8507 GfxFrame[x][y + 1] = GfxFrame[x][y];
8514 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8515 if (tile == Xsand_stonesand_quickout_1 ||
8516 tile == Xsand_stonesand_quickout_2)
8520 if (graphic_info[graphic].anim_global_sync)
8521 sync_frame = FrameCounter;
8522 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8523 sync_frame = GfxFrame[x][y];
8525 sync_frame = 0; // playfield border (pseudo steel)
8527 SetRandomAnimationValue(x, y);
8529 int frame = getAnimationFrame(g->anim_frames,
8532 g->anim_start_frame,
8535 g_em->unique_identifier =
8536 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8539 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8540 int tile, int frame_em, int x, int y)
8542 int action = object_mapping[tile].action;
8543 int direction = object_mapping[tile].direction;
8544 boolean is_backside = object_mapping[tile].is_backside;
8545 int effective_element = get_effective_element_EM(tile, frame_em);
8546 int effective_action = action;
8547 int graphic = (direction == MV_NONE ?
8548 el_act2img(effective_element, effective_action) :
8549 el_act_dir2img(effective_element, effective_action,
8551 int crumbled = (direction == MV_NONE ?
8552 el_act2crm(effective_element, effective_action) :
8553 el_act_dir2crm(effective_element, effective_action,
8555 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8556 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8557 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8558 struct GraphicInfo *g = &graphic_info[graphic];
8561 // special case: graphic uses "2nd movement tile" and has defined
8562 // 7 frames for movement animation (or less) => use default graphic
8563 // for last (8th) frame which ends the movement animation
8564 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8566 effective_action = ACTION_DEFAULT;
8567 graphic = (direction == MV_NONE ?
8568 el_act2img(effective_element, effective_action) :
8569 el_act_dir2img(effective_element, effective_action,
8571 crumbled = (direction == MV_NONE ?
8572 el_act2crm(effective_element, effective_action) :
8573 el_act_dir2crm(effective_element, effective_action,
8576 g = &graphic_info[graphic];
8579 if (graphic_info[graphic].anim_global_sync)
8580 sync_frame = FrameCounter;
8581 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8582 sync_frame = GfxFrame[x][y];
8584 sync_frame = 0; // playfield border (pseudo steel)
8586 SetRandomAnimationValue(x, y);
8588 int frame = getAnimationFrame(g->anim_frames,
8591 g->anim_start_frame,
8594 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8595 g->double_movement && is_backside);
8597 // (updating the "crumbled" graphic definitions is probably not really needed,
8598 // as animations for crumbled graphics can't be longer than one EMC cycle)
8599 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8603 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8604 int player_nr, int anim, int frame_em)
8606 int element = player_mapping[player_nr][anim].element_rnd;
8607 int action = player_mapping[player_nr][anim].action;
8608 int direction = player_mapping[player_nr][anim].direction;
8609 int graphic = (direction == MV_NONE ?
8610 el_act2img(element, action) :
8611 el_act_dir2img(element, action, direction));
8612 struct GraphicInfo *g = &graphic_info[graphic];
8615 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8617 stored_player[player_nr].StepFrame = frame_em;
8619 sync_frame = stored_player[player_nr].Frame;
8621 int frame = getAnimationFrame(g->anim_frames,
8624 g->anim_start_frame,
8627 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8628 &g_em->src_x, &g_em->src_y, FALSE);
8631 void InitGraphicInfo_EM(void)
8635 // always start with reliable default values
8636 for (i = 0; i < GAME_TILE_MAX; i++)
8638 object_mapping[i].element_rnd = EL_UNKNOWN;
8639 object_mapping[i].is_backside = FALSE;
8640 object_mapping[i].action = ACTION_DEFAULT;
8641 object_mapping[i].direction = MV_NONE;
8644 // always start with reliable default values
8645 for (p = 0; p < MAX_PLAYERS; p++)
8647 for (i = 0; i < PLY_MAX; i++)
8649 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8650 player_mapping[p][i].action = ACTION_DEFAULT;
8651 player_mapping[p][i].direction = MV_NONE;
8655 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8657 int e = em_object_mapping_list[i].element_em;
8659 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8660 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8662 if (em_object_mapping_list[i].action != -1)
8663 object_mapping[e].action = em_object_mapping_list[i].action;
8665 if (em_object_mapping_list[i].direction != -1)
8666 object_mapping[e].direction =
8667 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8670 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8672 int a = em_player_mapping_list[i].action_em;
8673 int p = em_player_mapping_list[i].player_nr;
8675 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8677 if (em_player_mapping_list[i].action != -1)
8678 player_mapping[p][a].action = em_player_mapping_list[i].action;
8680 if (em_player_mapping_list[i].direction != -1)
8681 player_mapping[p][a].direction =
8682 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8685 for (i = 0; i < GAME_TILE_MAX; i++)
8687 int element = object_mapping[i].element_rnd;
8688 int action = object_mapping[i].action;
8689 int direction = object_mapping[i].direction;
8690 boolean is_backside = object_mapping[i].is_backside;
8691 boolean action_exploding = ((action == ACTION_EXPLODING ||
8692 action == ACTION_SMASHED_BY_ROCK ||
8693 action == ACTION_SMASHED_BY_SPRING) &&
8694 element != EL_DIAMOND);
8695 boolean action_active = (action == ACTION_ACTIVE);
8696 boolean action_other = (action == ACTION_OTHER);
8698 for (j = 0; j < 8; j++)
8700 int effective_element = get_effective_element_EM(i, j);
8701 int effective_action = (j < 7 ? action :
8702 i == Xdrip_stretch ? action :
8703 i == Xdrip_stretchB ? action :
8704 i == Ydrip_1_s ? action :
8705 i == Ydrip_1_sB ? action :
8706 i == Yball_1 ? action :
8707 i == Xball_2 ? action :
8708 i == Yball_2 ? action :
8709 i == Yball_blank ? action :
8710 i == Ykey_1_blank ? action :
8711 i == Ykey_2_blank ? action :
8712 i == Ykey_3_blank ? action :
8713 i == Ykey_4_blank ? action :
8714 i == Ykey_5_blank ? action :
8715 i == Ykey_6_blank ? action :
8716 i == Ykey_7_blank ? action :
8717 i == Ykey_8_blank ? action :
8718 i == Ylenses_blank ? action :
8719 i == Ymagnify_blank ? action :
8720 i == Ygrass_blank ? action :
8721 i == Ydirt_blank ? action :
8722 i == Xsand_stonein_1 ? action :
8723 i == Xsand_stonein_2 ? action :
8724 i == Xsand_stonein_3 ? action :
8725 i == Xsand_stonein_4 ? action :
8726 i == Xsand_stoneout_1 ? action :
8727 i == Xsand_stoneout_2 ? action :
8728 i == Xboom_android ? ACTION_EXPLODING :
8729 action_exploding ? ACTION_EXPLODING :
8730 action_active ? action :
8731 action_other ? action :
8733 int graphic = (el_act_dir2img(effective_element, effective_action,
8735 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8737 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8738 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8739 boolean has_action_graphics = (graphic != base_graphic);
8740 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8741 struct GraphicInfo *g = &graphic_info[graphic];
8742 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8745 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8746 boolean special_animation = (action != ACTION_DEFAULT &&
8747 g->anim_frames == 3 &&
8748 g->anim_delay == 2 &&
8749 g->anim_mode & ANIM_LINEAR);
8750 int sync_frame = (i == Xdrip_stretch ? 7 :
8751 i == Xdrip_stretchB ? 7 :
8752 i == Ydrip_2_s ? j + 8 :
8753 i == Ydrip_2_sB ? j + 8 :
8762 i == Xfake_acid_1 ? 0 :
8763 i == Xfake_acid_2 ? 10 :
8764 i == Xfake_acid_3 ? 20 :
8765 i == Xfake_acid_4 ? 30 :
8766 i == Xfake_acid_5 ? 40 :
8767 i == Xfake_acid_6 ? 50 :
8768 i == Xfake_acid_7 ? 60 :
8769 i == Xfake_acid_8 ? 70 :
8771 i == Yball_2 ? j + 8 :
8772 i == Yball_blank ? j + 1 :
8773 i == Ykey_1_blank ? j + 1 :
8774 i == Ykey_2_blank ? j + 1 :
8775 i == Ykey_3_blank ? j + 1 :
8776 i == Ykey_4_blank ? j + 1 :
8777 i == Ykey_5_blank ? j + 1 :
8778 i == Ykey_6_blank ? j + 1 :
8779 i == Ykey_7_blank ? j + 1 :
8780 i == Ykey_8_blank ? j + 1 :
8781 i == Ylenses_blank ? j + 1 :
8782 i == Ymagnify_blank ? j + 1 :
8783 i == Ygrass_blank ? j + 1 :
8784 i == Ydirt_blank ? j + 1 :
8785 i == Xamoeba_1 ? 0 :
8786 i == Xamoeba_2 ? 1 :
8787 i == Xamoeba_3 ? 2 :
8788 i == Xamoeba_4 ? 3 :
8789 i == Xamoeba_5 ? 0 :
8790 i == Xamoeba_6 ? 1 :
8791 i == Xamoeba_7 ? 2 :
8792 i == Xamoeba_8 ? 3 :
8793 i == Xexit_2 ? j + 8 :
8794 i == Xexit_3 ? j + 16 :
8795 i == Xdynamite_1 ? 0 :
8796 i == Xdynamite_2 ? 8 :
8797 i == Xdynamite_3 ? 16 :
8798 i == Xdynamite_4 ? 24 :
8799 i == Xsand_stonein_1 ? j + 1 :
8800 i == Xsand_stonein_2 ? j + 9 :
8801 i == Xsand_stonein_3 ? j + 17 :
8802 i == Xsand_stonein_4 ? j + 25 :
8803 i == Xsand_stoneout_1 && j == 0 ? 0 :
8804 i == Xsand_stoneout_1 && j == 1 ? 0 :
8805 i == Xsand_stoneout_1 && j == 2 ? 1 :
8806 i == Xsand_stoneout_1 && j == 3 ? 2 :
8807 i == Xsand_stoneout_1 && j == 4 ? 2 :
8808 i == Xsand_stoneout_1 && j == 5 ? 3 :
8809 i == Xsand_stoneout_1 && j == 6 ? 4 :
8810 i == Xsand_stoneout_1 && j == 7 ? 4 :
8811 i == Xsand_stoneout_2 && j == 0 ? 5 :
8812 i == Xsand_stoneout_2 && j == 1 ? 6 :
8813 i == Xsand_stoneout_2 && j == 2 ? 7 :
8814 i == Xsand_stoneout_2 && j == 3 ? 8 :
8815 i == Xsand_stoneout_2 && j == 4 ? 9 :
8816 i == Xsand_stoneout_2 && j == 5 ? 11 :
8817 i == Xsand_stoneout_2 && j == 6 ? 13 :
8818 i == Xsand_stoneout_2 && j == 7 ? 15 :
8819 i == Xboom_bug && j == 1 ? 2 :
8820 i == Xboom_bug && j == 2 ? 2 :
8821 i == Xboom_bug && j == 3 ? 4 :
8822 i == Xboom_bug && j == 4 ? 4 :
8823 i == Xboom_bug && j == 5 ? 2 :
8824 i == Xboom_bug && j == 6 ? 2 :
8825 i == Xboom_bug && j == 7 ? 0 :
8826 i == Xboom_tank && j == 1 ? 2 :
8827 i == Xboom_tank && j == 2 ? 2 :
8828 i == Xboom_tank && j == 3 ? 4 :
8829 i == Xboom_tank && j == 4 ? 4 :
8830 i == Xboom_tank && j == 5 ? 2 :
8831 i == Xboom_tank && j == 6 ? 2 :
8832 i == Xboom_tank && j == 7 ? 0 :
8833 i == Xboom_android && j == 7 ? 6 :
8834 i == Xboom_1 && j == 1 ? 2 :
8835 i == Xboom_1 && j == 2 ? 2 :
8836 i == Xboom_1 && j == 3 ? 4 :
8837 i == Xboom_1 && j == 4 ? 4 :
8838 i == Xboom_1 && j == 5 ? 6 :
8839 i == Xboom_1 && j == 6 ? 6 :
8840 i == Xboom_1 && j == 7 ? 8 :
8841 i == Xboom_2 && j == 0 ? 8 :
8842 i == Xboom_2 && j == 1 ? 8 :
8843 i == Xboom_2 && j == 2 ? 10 :
8844 i == Xboom_2 && j == 3 ? 10 :
8845 i == Xboom_2 && j == 4 ? 10 :
8846 i == Xboom_2 && j == 5 ? 12 :
8847 i == Xboom_2 && j == 6 ? 12 :
8848 i == Xboom_2 && j == 7 ? 12 :
8849 special_animation && j == 4 ? 3 :
8850 effective_action != action ? 0 :
8852 int frame = getAnimationFrame(g->anim_frames,
8855 g->anim_start_frame,
8858 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8859 g->double_movement && is_backside);
8861 g_em->bitmap = src_bitmap;
8862 g_em->src_x = src_x;
8863 g_em->src_y = src_y;
8864 g_em->src_offset_x = 0;
8865 g_em->src_offset_y = 0;
8866 g_em->dst_offset_x = 0;
8867 g_em->dst_offset_y = 0;
8868 g_em->width = TILEX;
8869 g_em->height = TILEY;
8871 g_em->preserve_background = FALSE;
8873 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8876 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8877 effective_action == ACTION_MOVING ||
8878 effective_action == ACTION_PUSHING ||
8879 effective_action == ACTION_EATING)) ||
8880 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8881 effective_action == ACTION_EMPTYING)))
8884 (effective_action == ACTION_FALLING ||
8885 effective_action == ACTION_FILLING ||
8886 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8887 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8888 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8889 int num_steps = (i == Ydrip_1_s ? 16 :
8890 i == Ydrip_1_sB ? 16 :
8891 i == Ydrip_2_s ? 16 :
8892 i == Ydrip_2_sB ? 16 :
8893 i == Xsand_stonein_1 ? 32 :
8894 i == Xsand_stonein_2 ? 32 :
8895 i == Xsand_stonein_3 ? 32 :
8896 i == Xsand_stonein_4 ? 32 :
8897 i == Xsand_stoneout_1 ? 16 :
8898 i == Xsand_stoneout_2 ? 16 : 8);
8899 int cx = ABS(dx) * (TILEX / num_steps);
8900 int cy = ABS(dy) * (TILEY / num_steps);
8901 int step_frame = (i == Ydrip_2_s ? j + 8 :
8902 i == Ydrip_2_sB ? j + 8 :
8903 i == Xsand_stonein_2 ? j + 8 :
8904 i == Xsand_stonein_3 ? j + 16 :
8905 i == Xsand_stonein_4 ? j + 24 :
8906 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8907 int step = (is_backside ? step_frame : num_steps - step_frame);
8909 if (is_backside) // tile where movement starts
8911 if (dx < 0 || dy < 0)
8913 g_em->src_offset_x = cx * step;
8914 g_em->src_offset_y = cy * step;
8918 g_em->dst_offset_x = cx * step;
8919 g_em->dst_offset_y = cy * step;
8922 else // tile where movement ends
8924 if (dx < 0 || dy < 0)
8926 g_em->dst_offset_x = cx * step;
8927 g_em->dst_offset_y = cy * step;
8931 g_em->src_offset_x = cx * step;
8932 g_em->src_offset_y = cy * step;
8936 g_em->width = TILEX - cx * step;
8937 g_em->height = TILEY - cy * step;
8940 // create unique graphic identifier to decide if tile must be redrawn
8941 /* bit 31 - 16 (16 bit): EM style graphic
8942 bit 15 - 12 ( 4 bit): EM style frame
8943 bit 11 - 6 ( 6 bit): graphic width
8944 bit 5 - 0 ( 6 bit): graphic height */
8945 g_em->unique_identifier =
8946 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8950 for (i = 0; i < GAME_TILE_MAX; i++)
8952 for (j = 0; j < 8; j++)
8954 int element = object_mapping[i].element_rnd;
8955 int action = object_mapping[i].action;
8956 int direction = object_mapping[i].direction;
8957 boolean is_backside = object_mapping[i].is_backside;
8958 int graphic_action = el_act_dir2img(element, action, direction);
8959 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8961 if ((action == ACTION_SMASHED_BY_ROCK ||
8962 action == ACTION_SMASHED_BY_SPRING ||
8963 action == ACTION_EATING) &&
8964 graphic_action == graphic_default)
8966 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8967 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8968 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8969 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8972 // no separate animation for "smashed by rock" -- use rock instead
8973 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8974 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8976 g_em->bitmap = g_xx->bitmap;
8977 g_em->src_x = g_xx->src_x;
8978 g_em->src_y = g_xx->src_y;
8979 g_em->src_offset_x = g_xx->src_offset_x;
8980 g_em->src_offset_y = g_xx->src_offset_y;
8981 g_em->dst_offset_x = g_xx->dst_offset_x;
8982 g_em->dst_offset_y = g_xx->dst_offset_y;
8983 g_em->width = g_xx->width;
8984 g_em->height = g_xx->height;
8985 g_em->unique_identifier = g_xx->unique_identifier;
8988 g_em->preserve_background = TRUE;
8993 for (p = 0; p < MAX_PLAYERS; p++)
8995 for (i = 0; i < PLY_MAX; i++)
8997 int element = player_mapping[p][i].element_rnd;
8998 int action = player_mapping[p][i].action;
8999 int direction = player_mapping[p][i].direction;
9001 for (j = 0; j < 8; j++)
9003 int effective_element = element;
9004 int effective_action = action;
9005 int graphic = (direction == MV_NONE ?
9006 el_act2img(effective_element, effective_action) :
9007 el_act_dir2img(effective_element, effective_action,
9009 struct GraphicInfo *g = &graphic_info[graphic];
9010 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9014 int frame = getAnimationFrame(g->anim_frames,
9017 g->anim_start_frame,
9020 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9022 g_em->bitmap = src_bitmap;
9023 g_em->src_x = src_x;
9024 g_em->src_y = src_y;
9025 g_em->src_offset_x = 0;
9026 g_em->src_offset_y = 0;
9027 g_em->dst_offset_x = 0;
9028 g_em->dst_offset_y = 0;
9029 g_em->width = TILEX;
9030 g_em->height = TILEY;
9036 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9037 boolean any_player_moving,
9038 boolean any_player_snapping,
9039 boolean any_player_dropping)
9041 if (frame == 7 && !any_player_dropping)
9043 if (!local_player->was_waiting)
9045 if (!CheckSaveEngineSnapshotToList())
9048 local_player->was_waiting = TRUE;
9051 else if (any_player_moving || any_player_snapping || any_player_dropping)
9053 local_player->was_waiting = FALSE;
9057 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9058 boolean murphy_is_dropping)
9060 if (murphy_is_waiting)
9062 if (!local_player->was_waiting)
9064 if (!CheckSaveEngineSnapshotToList())
9067 local_player->was_waiting = TRUE;
9072 local_player->was_waiting = FALSE;
9076 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9077 boolean button_released)
9079 if (button_released)
9081 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9082 CheckSaveEngineSnapshotToList();
9084 else if (element_clicked)
9086 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9087 CheckSaveEngineSnapshotToList();
9089 game.snapshot.changed_action = TRUE;
9093 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9094 boolean any_player_moving,
9095 boolean any_player_snapping,
9096 boolean any_player_dropping)
9098 if (tape.single_step && tape.recording && !tape.pausing)
9099 if (frame == 7 && !any_player_dropping)
9100 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9102 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9103 any_player_snapping, any_player_dropping);
9105 return tape.pausing;
9108 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9109 boolean murphy_is_dropping)
9111 boolean murphy_starts_dropping = FALSE;
9114 for (i = 0; i < MAX_PLAYERS; i++)
9115 if (stored_player[i].force_dropping)
9116 murphy_starts_dropping = TRUE;
9118 if (tape.single_step && tape.recording && !tape.pausing)
9119 if (murphy_is_waiting && !murphy_starts_dropping)
9120 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9122 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9125 void CheckSingleStepMode_MM(boolean element_clicked,
9126 boolean button_released)
9128 if (tape.single_step && tape.recording && !tape.pausing)
9129 if (button_released)
9130 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9132 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9135 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9136 int graphic, int sync_frame, int x, int y)
9138 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9140 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9143 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9145 return (IS_NEXT_FRAME(sync_frame, graphic));
9148 int getGraphicInfo_Delay(int graphic)
9150 return graphic_info[graphic].anim_delay;
9153 void PlayMenuSoundExt(int sound)
9155 if (sound == SND_UNDEFINED)
9158 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9159 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9162 if (IS_LOOP_SOUND(sound))
9163 PlaySoundLoop(sound);
9168 void PlayMenuSound(void)
9170 PlayMenuSoundExt(menu.sound[game_status]);
9173 void PlayMenuSoundStereo(int sound, int stereo_position)
9175 if (sound == SND_UNDEFINED)
9178 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9179 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9182 if (IS_LOOP_SOUND(sound))
9183 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9185 PlaySoundStereo(sound, stereo_position);
9188 void PlayMenuSoundIfLoopExt(int sound)
9190 if (sound == SND_UNDEFINED)
9193 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9194 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9197 if (IS_LOOP_SOUND(sound))
9198 PlaySoundLoop(sound);
9201 void PlayMenuSoundIfLoop(void)
9203 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9206 void PlayMenuMusicExt(int music)
9208 if (music == MUS_UNDEFINED)
9211 if (!setup.sound_music)
9214 if (IS_LOOP_MUSIC(music))
9215 PlayMusicLoop(music);
9220 void PlayMenuMusic(void)
9222 char *curr_music = getCurrentlyPlayingMusicFilename();
9223 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9225 if (!strEqual(curr_music, next_music))
9226 PlayMenuMusicExt(menu.music[game_status]);
9229 void PlayMenuSoundsAndMusic(void)
9235 static void FadeMenuSounds(void)
9240 static void FadeMenuMusic(void)
9242 char *curr_music = getCurrentlyPlayingMusicFilename();
9243 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9245 if (!strEqual(curr_music, next_music))
9249 void FadeMenuSoundsAndMusic(void)
9255 void PlaySoundActivating(void)
9258 PlaySound(SND_MENU_ITEM_ACTIVATING);
9262 void PlaySoundSelecting(void)
9265 PlaySound(SND_MENU_ITEM_SELECTING);
9269 void ToggleFullscreenIfNeeded(void)
9271 // if setup and video fullscreen state are already matching, nothing do do
9272 if (setup.fullscreen == video.fullscreen_enabled ||
9273 !video.fullscreen_available)
9276 SDLSetWindowFullscreen(setup.fullscreen);
9278 // set setup value according to successfully changed fullscreen mode
9279 setup.fullscreen = video.fullscreen_enabled;
9282 void ChangeWindowScalingIfNeeded(void)
9284 // if setup and video window scaling are already matching, nothing do do
9285 if (setup.window_scaling_percent == video.window_scaling_percent ||
9286 video.fullscreen_enabled)
9289 SDLSetWindowScaling(setup.window_scaling_percent);
9291 // set setup value according to successfully changed window scaling
9292 setup.window_scaling_percent = video.window_scaling_percent;
9295 void ChangeVsyncModeIfNeeded(void)
9297 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9298 int video_vsync_mode = video.vsync_mode;
9300 // if setup and video vsync mode are already matching, nothing do do
9301 if (setup_vsync_mode == video_vsync_mode)
9304 // if renderer is using OpenGL, vsync mode can directly be changed
9305 SDLSetScreenVsyncMode(setup.vsync_mode);
9307 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9308 if (video.vsync_mode == video_vsync_mode)
9310 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9312 // save backbuffer content which gets lost when re-creating screen
9313 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9315 // force re-creating screen and renderer to set new vsync mode
9316 video.fullscreen_enabled = !setup.fullscreen;
9318 // when creating new renderer, destroy textures linked to old renderer
9319 FreeAllImageTextures(); // needs old renderer to free the textures
9321 // re-create screen and renderer (including change of vsync mode)
9322 ChangeVideoModeIfNeeded(setup.fullscreen);
9324 // set setup value according to successfully changed fullscreen mode
9325 setup.fullscreen = video.fullscreen_enabled;
9327 // restore backbuffer content from temporary backbuffer backup bitmap
9328 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9329 FreeBitmap(tmp_backbuffer);
9331 // update visible window/screen
9332 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9334 // when changing vsync mode, re-create textures for new renderer
9335 InitImageTextures();
9338 // set setup value according to successfully changed vsync mode
9339 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9342 static void JoinRectangles(int *x, int *y, int *width, int *height,
9343 int x2, int y2, int width2, int height2)
9345 // do not join with "off-screen" rectangle
9346 if (x2 == -1 || y2 == -1)
9351 *width = MAX(*width, width2);
9352 *height = MAX(*height, height2);
9355 void SetAnimStatus(int anim_status_new)
9357 if (anim_status_new == GAME_MODE_MAIN)
9358 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9359 else if (anim_status_new == GAME_MODE_SCORES)
9360 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9362 global.anim_status_next = anim_status_new;
9364 // directly set screen modes that are entered without fading
9365 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9366 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9367 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9368 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9369 global.anim_status = global.anim_status_next;
9372 void SetGameStatus(int game_status_new)
9374 if (game_status_new != game_status)
9375 game_status_last_screen = game_status;
9377 game_status = game_status_new;
9379 SetAnimStatus(game_status_new);
9382 void SetFontStatus(int game_status_new)
9384 static int last_game_status = -1;
9386 if (game_status_new != -1)
9388 // set game status for font use after storing last game status
9389 last_game_status = game_status;
9390 game_status = game_status_new;
9394 // reset game status after font use from last stored game status
9395 game_status = last_game_status;
9399 void ResetFontStatus(void)
9404 void SetLevelSetInfo(char *identifier, int level_nr)
9406 setString(&levelset.identifier, identifier);
9408 levelset.level_nr = level_nr;
9411 boolean CheckIfAllViewportsHaveChanged(void)
9413 // if game status has not changed, viewports have not changed either
9414 if (game_status == game_status_last)
9417 // check if all viewports have changed with current game status
9419 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9420 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9421 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9422 int new_real_sx = vp_playfield->x;
9423 int new_real_sy = vp_playfield->y;
9424 int new_full_sxsize = vp_playfield->width;
9425 int new_full_sysize = vp_playfield->height;
9426 int new_dx = vp_door_1->x;
9427 int new_dy = vp_door_1->y;
9428 int new_dxsize = vp_door_1->width;
9429 int new_dysize = vp_door_1->height;
9430 int new_vx = vp_door_2->x;
9431 int new_vy = vp_door_2->y;
9432 int new_vxsize = vp_door_2->width;
9433 int new_vysize = vp_door_2->height;
9435 boolean playfield_viewport_has_changed =
9436 (new_real_sx != REAL_SX ||
9437 new_real_sy != REAL_SY ||
9438 new_full_sxsize != FULL_SXSIZE ||
9439 new_full_sysize != FULL_SYSIZE);
9441 boolean door_1_viewport_has_changed =
9444 new_dxsize != DXSIZE ||
9445 new_dysize != DYSIZE);
9447 boolean door_2_viewport_has_changed =
9450 new_vxsize != VXSIZE ||
9451 new_vysize != VYSIZE ||
9452 game_status_last == GAME_MODE_EDITOR);
9454 return (playfield_viewport_has_changed &&
9455 door_1_viewport_has_changed &&
9456 door_2_viewport_has_changed);
9459 boolean CheckFadeAll(void)
9461 return (CheckIfGlobalBorderHasChanged() ||
9462 CheckIfAllViewportsHaveChanged());
9465 void ChangeViewportPropertiesIfNeeded(void)
9467 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9468 FALSE : setup.small_game_graphics);
9469 int gfx_game_mode = game_status;
9470 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9472 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9473 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9474 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9475 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9476 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9477 int new_win_xsize = vp_window->width;
9478 int new_win_ysize = vp_window->height;
9479 int border_left = vp_playfield->border_left;
9480 int border_right = vp_playfield->border_right;
9481 int border_top = vp_playfield->border_top;
9482 int border_bottom = vp_playfield->border_bottom;
9483 int new_sx = vp_playfield->x + border_left;
9484 int new_sy = vp_playfield->y + border_top;
9485 int new_sxsize = vp_playfield->width - border_left - border_right;
9486 int new_sysize = vp_playfield->height - border_top - border_bottom;
9487 int new_real_sx = vp_playfield->x;
9488 int new_real_sy = vp_playfield->y;
9489 int new_full_sxsize = vp_playfield->width;
9490 int new_full_sysize = vp_playfield->height;
9491 int new_dx = vp_door_1->x;
9492 int new_dy = vp_door_1->y;
9493 int new_dxsize = vp_door_1->width;
9494 int new_dysize = vp_door_1->height;
9495 int new_vx = vp_door_2->x;
9496 int new_vy = vp_door_2->y;
9497 int new_vxsize = vp_door_2->width;
9498 int new_vysize = vp_door_2->height;
9499 int new_ex = vp_door_3->x;
9500 int new_ey = vp_door_3->y;
9501 int new_exsize = vp_door_3->width;
9502 int new_eysize = vp_door_3->height;
9503 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9504 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9505 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9506 int new_scr_fieldx = new_sxsize / tilesize;
9507 int new_scr_fieldy = new_sysize / tilesize;
9508 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9509 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9510 boolean init_gfx_buffers = FALSE;
9511 boolean init_video_buffer = FALSE;
9512 boolean init_gadgets_and_anims = FALSE;
9513 boolean init_em_graphics = FALSE;
9515 if (new_win_xsize != WIN_XSIZE ||
9516 new_win_ysize != WIN_YSIZE)
9518 WIN_XSIZE = new_win_xsize;
9519 WIN_YSIZE = new_win_ysize;
9521 init_video_buffer = TRUE;
9522 init_gfx_buffers = TRUE;
9523 init_gadgets_and_anims = TRUE;
9525 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9528 if (new_scr_fieldx != SCR_FIELDX ||
9529 new_scr_fieldy != SCR_FIELDY)
9531 // this always toggles between MAIN and GAME when using small tile size
9533 SCR_FIELDX = new_scr_fieldx;
9534 SCR_FIELDY = new_scr_fieldy;
9536 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9547 new_sxsize != SXSIZE ||
9548 new_sysize != SYSIZE ||
9549 new_dxsize != DXSIZE ||
9550 new_dysize != DYSIZE ||
9551 new_vxsize != VXSIZE ||
9552 new_vysize != VYSIZE ||
9553 new_exsize != EXSIZE ||
9554 new_eysize != EYSIZE ||
9555 new_real_sx != REAL_SX ||
9556 new_real_sy != REAL_SY ||
9557 new_full_sxsize != FULL_SXSIZE ||
9558 new_full_sysize != FULL_SYSIZE ||
9559 new_tilesize_var != TILESIZE_VAR
9562 // ------------------------------------------------------------------------
9563 // determine next fading area for changed viewport definitions
9564 // ------------------------------------------------------------------------
9566 // start with current playfield area (default fading area)
9569 FADE_SXSIZE = FULL_SXSIZE;
9570 FADE_SYSIZE = FULL_SYSIZE;
9572 // add new playfield area if position or size has changed
9573 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9574 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9576 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9577 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9580 // add current and new door 1 area if position or size has changed
9581 if (new_dx != DX || new_dy != DY ||
9582 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9584 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9585 DX, DY, DXSIZE, DYSIZE);
9586 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9587 new_dx, new_dy, new_dxsize, new_dysize);
9590 // add current and new door 2 area if position or size has changed
9591 if (new_vx != VX || new_vy != VY ||
9592 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9594 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9595 VX, VY, VXSIZE, VYSIZE);
9596 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9597 new_vx, new_vy, new_vxsize, new_vysize);
9600 // ------------------------------------------------------------------------
9601 // handle changed tile size
9602 // ------------------------------------------------------------------------
9604 if (new_tilesize_var != TILESIZE_VAR)
9606 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9608 // changing tile size invalidates scroll values of engine snapshots
9609 FreeEngineSnapshotSingle();
9611 // changing tile size requires update of graphic mapping for EM engine
9612 init_em_graphics = TRUE;
9623 SXSIZE = new_sxsize;
9624 SYSIZE = new_sysize;
9625 DXSIZE = new_dxsize;
9626 DYSIZE = new_dysize;
9627 VXSIZE = new_vxsize;
9628 VYSIZE = new_vysize;
9629 EXSIZE = new_exsize;
9630 EYSIZE = new_eysize;
9631 REAL_SX = new_real_sx;
9632 REAL_SY = new_real_sy;
9633 FULL_SXSIZE = new_full_sxsize;
9634 FULL_SYSIZE = new_full_sysize;
9635 TILESIZE_VAR = new_tilesize_var;
9637 init_gfx_buffers = TRUE;
9638 init_gadgets_and_anims = TRUE;
9640 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9641 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9644 if (init_gfx_buffers)
9646 // Debug("tools:viewport", "init_gfx_buffers");
9648 SCR_FIELDX = new_scr_fieldx_buffers;
9649 SCR_FIELDY = new_scr_fieldy_buffers;
9653 SCR_FIELDX = new_scr_fieldx;
9654 SCR_FIELDY = new_scr_fieldy;
9656 SetDrawDeactivationMask(REDRAW_NONE);
9657 SetDrawBackgroundMask(REDRAW_FIELD);
9660 if (init_video_buffer)
9662 // Debug("tools:viewport", "init_video_buffer");
9664 FreeAllImageTextures(); // needs old renderer to free the textures
9666 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9667 InitImageTextures();
9670 if (init_gadgets_and_anims)
9672 // Debug("tools:viewport", "init_gadgets_and_anims");
9675 InitGlobalAnimations();
9678 if (init_em_graphics)
9680 InitGraphicInfo_EM();