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)
400 printf_line("-", 79);
401 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
402 printf_line("-", 79);
404 if (!IN_LEV_FIELD(x, y))
406 printf("(not in level field)\n");
412 token_name = element_info[Feld[x][y]].token_name;
414 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
415 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
416 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
417 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
418 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
419 printf(" MovPos: %d\n", MovPos[x][y]);
420 printf(" MovDir: %d\n", MovDir[x][y]);
421 printf(" MovDelay: %d\n", MovDelay[x][y]);
422 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
423 printf(" CustomValue: %d\n", CustomValue[x][y]);
424 printf(" GfxElement: %d\n", GfxElement[x][y]);
425 printf(" GfxAction: %d\n", GfxAction[x][y]);
426 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
427 printf(" Player x/y: %d, %d\n", 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 Error(ERR_INFO, "%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())
1342 // redraw global screen border (or clear, if defined to be empty)
1343 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1345 if (game_status == GAME_MODE_EDITOR)
1346 DrawSpecialEditorDoor();
1348 // copy previous playfield and door areas, if they are defined on both
1349 // previous and current screen and if they still have the same size
1351 if (real_sx_last != -1 && real_sy_last != -1 &&
1352 REAL_SX != -1 && REAL_SY != -1 &&
1353 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1354 BlitBitmap(bitmap_db_store_1, backbuffer,
1355 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1358 if (dx_last != -1 && dy_last != -1 &&
1359 DX != -1 && DY != -1 &&
1360 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1361 BlitBitmap(bitmap_db_store_1, backbuffer,
1362 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1364 if (game_status != GAME_MODE_EDITOR)
1366 if (vx_last != -1 && vy_last != -1 &&
1367 VX != -1 && VY != -1 &&
1368 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1369 BlitBitmap(bitmap_db_store_1, backbuffer,
1370 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1374 if (ex_last != -1 && ey_last != -1 &&
1375 EX != -1 && EY != -1 &&
1376 exsize_last == EXSIZE && eysize_last == EYSIZE)
1377 BlitBitmap(bitmap_db_store_1, backbuffer,
1378 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1381 redraw_mask = REDRAW_ALL;
1384 game_status_last = game_status;
1386 global_border_bitmap_last = global_border_bitmap;
1388 real_sx_last = REAL_SX;
1389 real_sy_last = REAL_SY;
1390 full_sxsize_last = FULL_SXSIZE;
1391 full_sysize_last = FULL_SYSIZE;
1394 dxsize_last = DXSIZE;
1395 dysize_last = DYSIZE;
1398 vxsize_last = VXSIZE;
1399 vysize_last = VYSIZE;
1402 exsize_last = EXSIZE;
1403 eysize_last = EYSIZE;
1406 void ClearField(void)
1408 RedrawGlobalBorderIfNeeded();
1410 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1411 // (when entering hall of fame after playing)
1412 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1414 // !!! maybe this should be done before clearing the background !!!
1415 if (game_status == GAME_MODE_PLAYING)
1417 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1418 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1422 SetDrawtoField(DRAW_TO_BACKBUFFER);
1426 void MarkTileDirty(int x, int y)
1428 redraw_mask |= REDRAW_FIELD;
1431 void SetBorderElement(void)
1435 BorderElement = EL_EMPTY;
1437 // only the R'n'D game engine may use an additional steelwall border
1438 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1441 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1443 for (x = 0; x < lev_fieldx; x++)
1445 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1446 BorderElement = EL_STEELWALL;
1448 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1454 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1455 int max_array_fieldx, int max_array_fieldy,
1456 short field[max_array_fieldx][max_array_fieldy],
1457 int max_fieldx, int max_fieldy)
1461 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1462 static int safety = 0;
1464 // check if starting field still has the desired content
1465 if (field[from_x][from_y] == fill_element)
1470 if (safety > max_fieldx * max_fieldy)
1471 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1473 old_element = field[from_x][from_y];
1474 field[from_x][from_y] = fill_element;
1476 for (i = 0; i < 4; i++)
1478 x = from_x + check[i][0];
1479 y = from_y + check[i][1];
1481 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1482 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1483 field, max_fieldx, max_fieldy);
1489 void FloodFillLevel(int from_x, int from_y, int fill_element,
1490 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1491 int max_fieldx, int max_fieldy)
1493 FloodFillLevelExt(from_x, from_y, fill_element,
1494 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1495 max_fieldx, max_fieldy);
1498 void SetRandomAnimationValue(int x, int y)
1500 gfx.anim_random_frame = GfxRandom[x][y];
1503 int getGraphicAnimationFrame(int graphic, int sync_frame)
1505 // animation synchronized with global frame counter, not move position
1506 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1507 sync_frame = FrameCounter;
1509 return getAnimationFrame(graphic_info[graphic].anim_frames,
1510 graphic_info[graphic].anim_delay,
1511 graphic_info[graphic].anim_mode,
1512 graphic_info[graphic].anim_start_frame,
1516 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1518 struct GraphicInfo *g = &graphic_info[graphic];
1519 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1521 if (tilesize == gfx.standard_tile_size)
1522 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1523 else if (tilesize == game.tile_size)
1524 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1526 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1529 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1530 boolean get_backside)
1532 struct GraphicInfo *g = &graphic_info[graphic];
1533 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1534 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1536 if (g->offset_y == 0) // frames are ordered horizontally
1538 int max_width = g->anim_frames_per_line * g->width;
1539 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1541 *x = pos % max_width;
1542 *y = src_y % g->height + pos / max_width * g->height;
1544 else if (g->offset_x == 0) // frames are ordered vertically
1546 int max_height = g->anim_frames_per_line * g->height;
1547 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1549 *x = src_x % g->width + pos / max_height * g->width;
1550 *y = pos % max_height;
1552 else // frames are ordered diagonally
1554 *x = src_x + frame * g->offset_x;
1555 *y = src_y + frame * g->offset_y;
1559 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1560 Bitmap **bitmap, int *x, int *y,
1561 boolean get_backside)
1563 struct GraphicInfo *g = &graphic_info[graphic];
1565 // if no graphics defined at all, use fallback graphics
1566 if (g->bitmaps == NULL)
1567 *g = graphic_info[IMG_CHAR_EXCLAM];
1569 // if no in-game graphics defined, always use standard graphic size
1570 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1571 tilesize = TILESIZE;
1573 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1574 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1576 *x = *x * tilesize / g->tile_size;
1577 *y = *y * tilesize / g->tile_size;
1580 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1581 Bitmap **bitmap, int *x, int *y)
1583 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1586 void getFixedGraphicSource(int graphic, int frame,
1587 Bitmap **bitmap, int *x, int *y)
1589 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1592 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1594 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1597 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1598 int *x, int *y, boolean get_backside)
1600 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1604 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1606 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1609 void DrawGraphic(int x, int y, int graphic, int frame)
1612 if (!IN_SCR_FIELD(x, y))
1614 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1615 printf("DrawGraphic(): This should never happen!\n");
1620 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1623 MarkTileDirty(x, y);
1626 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1629 if (!IN_SCR_FIELD(x, y))
1631 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1632 printf("DrawGraphic(): This should never happen!\n");
1637 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1639 MarkTileDirty(x, y);
1642 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1648 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1650 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1653 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1659 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1660 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1663 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1666 if (!IN_SCR_FIELD(x, y))
1668 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1669 printf("DrawGraphicThruMask(): This should never happen!\n");
1674 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1677 MarkTileDirty(x, y);
1680 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1686 printf("DrawGraphicThruMask(): This should never happen!\n");
1691 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1693 MarkTileDirty(x, y);
1696 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1702 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1704 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1708 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1709 int graphic, int frame)
1714 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1716 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1720 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1722 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1724 MarkTileDirty(x / tilesize, y / tilesize);
1727 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1730 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1731 graphic, frame, tilesize);
1732 MarkTileDirty(x / tilesize, y / tilesize);
1735 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1741 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1742 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1745 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1746 int frame, int tilesize)
1751 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1752 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1755 void DrawMiniGraphic(int x, int y, int graphic)
1757 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1758 MarkTileDirty(x / 2, y / 2);
1761 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1766 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1767 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1770 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1771 int graphic, int frame,
1772 int cut_mode, int mask_mode)
1777 int width = TILEX, height = TILEY;
1780 if (dx || dy) // shifted graphic
1782 if (x < BX1) // object enters playfield from the left
1789 else if (x > BX2) // object enters playfield from the right
1795 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1801 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1803 else if (dx) // general horizontal movement
1804 MarkTileDirty(x + SIGN(dx), y);
1806 if (y < BY1) // object enters playfield from the top
1808 if (cut_mode == CUT_BELOW) // object completely above top border
1816 else if (y > BY2) // object enters playfield from the bottom
1822 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1828 else if (dy > 0 && cut_mode == CUT_ABOVE)
1830 if (y == BY2) // object completely above bottom border
1836 MarkTileDirty(x, y + 1);
1837 } // object leaves playfield to the bottom
1838 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1840 else if (dy) // general vertical movement
1841 MarkTileDirty(x, y + SIGN(dy));
1845 if (!IN_SCR_FIELD(x, y))
1847 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1848 printf("DrawGraphicShifted(): This should never happen!\n");
1853 width = width * TILESIZE_VAR / TILESIZE;
1854 height = height * TILESIZE_VAR / TILESIZE;
1855 cx = cx * TILESIZE_VAR / TILESIZE;
1856 cy = cy * TILESIZE_VAR / TILESIZE;
1857 dx = dx * TILESIZE_VAR / TILESIZE;
1858 dy = dy * TILESIZE_VAR / TILESIZE;
1860 if (width > 0 && height > 0)
1862 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1867 dst_x = FX + x * TILEX_VAR + dx;
1868 dst_y = FY + y * TILEY_VAR + dy;
1870 if (mask_mode == USE_MASKING)
1871 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1874 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1877 MarkTileDirty(x, y);
1881 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1882 int graphic, int frame,
1883 int cut_mode, int mask_mode)
1888 int width = TILEX_VAR, height = TILEY_VAR;
1891 int x2 = x + SIGN(dx);
1892 int y2 = y + SIGN(dy);
1894 // movement with two-tile animations must be sync'ed with movement position,
1895 // not with current GfxFrame (which can be higher when using slow movement)
1896 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1897 int anim_frames = graphic_info[graphic].anim_frames;
1899 // (we also need anim_delay here for movement animations with less frames)
1900 int anim_delay = graphic_info[graphic].anim_delay;
1901 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1903 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1904 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1906 // re-calculate animation frame for two-tile movement animation
1907 frame = getGraphicAnimationFrame(graphic, sync_frame);
1909 // check if movement start graphic inside screen area and should be drawn
1910 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1912 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1914 dst_x = FX + x1 * TILEX_VAR;
1915 dst_y = FY + y1 * TILEY_VAR;
1917 if (mask_mode == USE_MASKING)
1918 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1921 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1924 MarkTileDirty(x1, y1);
1927 // check if movement end graphic inside screen area and should be drawn
1928 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1930 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1932 dst_x = FX + x2 * TILEX_VAR;
1933 dst_y = FY + y2 * TILEY_VAR;
1935 if (mask_mode == USE_MASKING)
1936 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1939 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1942 MarkTileDirty(x2, y2);
1946 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1947 int graphic, int frame,
1948 int cut_mode, int mask_mode)
1952 DrawGraphic(x, y, graphic, frame);
1957 if (graphic_info[graphic].double_movement) // EM style movement images
1958 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1960 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1963 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1964 int graphic, int frame, int cut_mode)
1966 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1969 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1970 int cut_mode, int mask_mode)
1972 int lx = LEVELX(x), ly = LEVELY(y);
1976 if (IN_LEV_FIELD(lx, ly))
1978 SetRandomAnimationValue(lx, ly);
1980 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1981 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1983 // do not use double (EM style) movement graphic when not moving
1984 if (graphic_info[graphic].double_movement && !dx && !dy)
1986 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1987 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1990 else // border element
1992 graphic = el2img(element);
1993 frame = getGraphicAnimationFrame(graphic, -1);
1996 if (element == EL_EXPANDABLE_WALL)
1998 boolean left_stopped = FALSE, right_stopped = FALSE;
2000 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2001 left_stopped = TRUE;
2002 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2003 right_stopped = TRUE;
2005 if (left_stopped && right_stopped)
2007 else if (left_stopped)
2009 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2010 frame = graphic_info[graphic].anim_frames - 1;
2012 else if (right_stopped)
2014 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2015 frame = graphic_info[graphic].anim_frames - 1;
2020 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2021 else if (mask_mode == USE_MASKING)
2022 DrawGraphicThruMask(x, y, graphic, frame);
2024 DrawGraphic(x, y, graphic, frame);
2027 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2028 int cut_mode, int mask_mode)
2030 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2031 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2032 cut_mode, mask_mode);
2035 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2038 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2041 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2044 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2047 void DrawLevelElementThruMask(int x, int y, int element)
2049 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2052 void DrawLevelFieldThruMask(int x, int y)
2054 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2057 // !!! implementation of quicksand is totally broken !!!
2058 #define IS_CRUMBLED_TILE(x, y, e) \
2059 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2060 !IS_MOVING(x, y) || \
2061 (e) == EL_QUICKSAND_EMPTYING || \
2062 (e) == EL_QUICKSAND_FAST_EMPTYING))
2064 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2069 int width, height, cx, cy;
2070 int sx = SCREENX(x), sy = SCREENY(y);
2071 int crumbled_border_size = graphic_info[graphic].border_size;
2072 int crumbled_tile_size = graphic_info[graphic].tile_size;
2073 int crumbled_border_size_var =
2074 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2077 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2079 for (i = 1; i < 4; i++)
2081 int dxx = (i & 1 ? dx : 0);
2082 int dyy = (i & 2 ? dy : 0);
2085 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2088 // check if neighbour field is of same crumble type
2089 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2090 graphic_info[graphic].class ==
2091 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2093 // return if check prevents inner corner
2094 if (same == (dxx == dx && dyy == dy))
2098 // if we reach this point, we have an inner corner
2100 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2102 width = crumbled_border_size_var;
2103 height = crumbled_border_size_var;
2104 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2105 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2107 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2108 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2111 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2116 int width, height, bx, by, cx, cy;
2117 int sx = SCREENX(x), sy = SCREENY(y);
2118 int crumbled_border_size = graphic_info[graphic].border_size;
2119 int crumbled_tile_size = graphic_info[graphic].tile_size;
2120 int crumbled_border_size_var =
2121 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2122 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2125 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2127 // draw simple, sloppy, non-corner-accurate crumbled border
2129 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2130 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2131 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2132 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2134 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2135 FX + sx * TILEX_VAR + cx,
2136 FY + sy * TILEY_VAR + cy);
2138 // (remaining middle border part must be at least as big as corner part)
2139 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2140 crumbled_border_size_var >= TILESIZE_VAR / 3)
2143 // correct corners of crumbled border, if needed
2145 for (i = -1; i <= 1; i += 2)
2147 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2148 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2149 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2152 // check if neighbour field is of same crumble type
2153 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2154 graphic_info[graphic].class ==
2155 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2157 // no crumbled corner, but continued crumbled border
2159 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2160 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2161 int b1 = (i == 1 ? crumbled_border_size_var :
2162 TILESIZE_VAR - 2 * crumbled_border_size_var);
2164 width = crumbled_border_size_var;
2165 height = crumbled_border_size_var;
2167 if (dir == 1 || dir == 2)
2182 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2184 FX + sx * TILEX_VAR + cx,
2185 FY + sy * TILEY_VAR + cy);
2190 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2192 int sx = SCREENX(x), sy = SCREENY(y);
2195 static int xy[4][2] =
2203 if (!IN_LEV_FIELD(x, y))
2206 element = TILE_GFX_ELEMENT(x, y);
2208 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2210 if (!IN_SCR_FIELD(sx, sy))
2213 // crumble field borders towards direct neighbour fields
2214 for (i = 0; i < 4; i++)
2216 int xx = x + xy[i][0];
2217 int yy = y + xy[i][1];
2219 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2222 // check if neighbour field is of same crumble type
2223 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2224 graphic_info[graphic].class ==
2225 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2228 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2231 // crumble inner field corners towards corner neighbour fields
2232 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2233 graphic_info[graphic].anim_frames == 2)
2235 for (i = 0; i < 4; i++)
2237 int dx = (i & 1 ? +1 : -1);
2238 int dy = (i & 2 ? +1 : -1);
2240 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2244 MarkTileDirty(sx, sy);
2246 else // center field is not crumbled -- crumble neighbour fields
2248 // crumble field borders of direct neighbour fields
2249 for (i = 0; i < 4; i++)
2251 int xx = x + xy[i][0];
2252 int yy = y + xy[i][1];
2253 int sxx = sx + xy[i][0];
2254 int syy = sy + xy[i][1];
2256 if (!IN_LEV_FIELD(xx, yy) ||
2257 !IN_SCR_FIELD(sxx, syy))
2260 // do not crumble fields that are being digged or snapped
2261 if (Feld[xx][yy] == EL_EMPTY ||
2262 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2265 element = TILE_GFX_ELEMENT(xx, yy);
2267 if (!IS_CRUMBLED_TILE(xx, yy, element))
2270 graphic = el_act2crm(element, ACTION_DEFAULT);
2272 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2274 MarkTileDirty(sxx, syy);
2277 // crumble inner field corners of corner neighbour fields
2278 for (i = 0; i < 4; i++)
2280 int dx = (i & 1 ? +1 : -1);
2281 int dy = (i & 2 ? +1 : -1);
2287 if (!IN_LEV_FIELD(xx, yy) ||
2288 !IN_SCR_FIELD(sxx, syy))
2291 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2294 element = TILE_GFX_ELEMENT(xx, yy);
2296 if (!IS_CRUMBLED_TILE(xx, yy, element))
2299 graphic = el_act2crm(element, ACTION_DEFAULT);
2301 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2302 graphic_info[graphic].anim_frames == 2)
2303 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2305 MarkTileDirty(sxx, syy);
2310 void DrawLevelFieldCrumbled(int x, int y)
2314 if (!IN_LEV_FIELD(x, y))
2317 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2318 GfxElement[x][y] != EL_UNDEFINED &&
2319 GFX_CRUMBLED(GfxElement[x][y]))
2321 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2326 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2328 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2331 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2334 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2335 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2336 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2337 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2338 int sx = SCREENX(x), sy = SCREENY(y);
2340 DrawGraphic(sx, sy, graphic1, frame1);
2341 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2344 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2346 int sx = SCREENX(x), sy = SCREENY(y);
2347 static int xy[4][2] =
2356 // crumble direct neighbour fields (required for field borders)
2357 for (i = 0; i < 4; i++)
2359 int xx = x + xy[i][0];
2360 int yy = y + xy[i][1];
2361 int sxx = sx + xy[i][0];
2362 int syy = sy + xy[i][1];
2364 if (!IN_LEV_FIELD(xx, yy) ||
2365 !IN_SCR_FIELD(sxx, syy) ||
2366 !GFX_CRUMBLED(Feld[xx][yy]) ||
2370 DrawLevelField(xx, yy);
2373 // crumble corner neighbour fields (required for inner field corners)
2374 for (i = 0; i < 4; i++)
2376 int dx = (i & 1 ? +1 : -1);
2377 int dy = (i & 2 ? +1 : -1);
2383 if (!IN_LEV_FIELD(xx, yy) ||
2384 !IN_SCR_FIELD(sxx, syy) ||
2385 !GFX_CRUMBLED(Feld[xx][yy]) ||
2389 int element = TILE_GFX_ELEMENT(xx, yy);
2390 int graphic = el_act2crm(element, ACTION_DEFAULT);
2392 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2393 graphic_info[graphic].anim_frames == 2)
2394 DrawLevelField(xx, yy);
2398 static int getBorderElement(int x, int y)
2402 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2403 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2404 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2405 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2406 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2407 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2408 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2410 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2411 int steel_position = (x == -1 && y == -1 ? 0 :
2412 x == lev_fieldx && y == -1 ? 1 :
2413 x == -1 && y == lev_fieldy ? 2 :
2414 x == lev_fieldx && y == lev_fieldy ? 3 :
2415 x == -1 || x == lev_fieldx ? 4 :
2416 y == -1 || y == lev_fieldy ? 5 : 6);
2418 return border[steel_position][steel_type];
2421 void DrawScreenElement(int x, int y, int element)
2423 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2424 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2427 void DrawLevelElement(int x, int y, int element)
2429 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2430 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2433 void DrawScreenField(int x, int y)
2435 int lx = LEVELX(x), ly = LEVELY(y);
2436 int element, content;
2438 if (!IN_LEV_FIELD(lx, ly))
2440 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2443 element = getBorderElement(lx, ly);
2445 DrawScreenElement(x, y, element);
2450 element = Feld[lx][ly];
2451 content = Store[lx][ly];
2453 if (IS_MOVING(lx, ly))
2455 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2456 boolean cut_mode = NO_CUTTING;
2458 if (element == EL_QUICKSAND_EMPTYING ||
2459 element == EL_QUICKSAND_FAST_EMPTYING ||
2460 element == EL_MAGIC_WALL_EMPTYING ||
2461 element == EL_BD_MAGIC_WALL_EMPTYING ||
2462 element == EL_DC_MAGIC_WALL_EMPTYING ||
2463 element == EL_AMOEBA_DROPPING)
2464 cut_mode = CUT_ABOVE;
2465 else if (element == EL_QUICKSAND_FILLING ||
2466 element == EL_QUICKSAND_FAST_FILLING ||
2467 element == EL_MAGIC_WALL_FILLING ||
2468 element == EL_BD_MAGIC_WALL_FILLING ||
2469 element == EL_DC_MAGIC_WALL_FILLING)
2470 cut_mode = CUT_BELOW;
2472 if (cut_mode == CUT_ABOVE)
2473 DrawScreenElement(x, y, element);
2475 DrawScreenElement(x, y, EL_EMPTY);
2478 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2479 else if (cut_mode == NO_CUTTING)
2480 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2483 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2485 if (cut_mode == CUT_BELOW &&
2486 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2487 DrawLevelElement(lx, ly + 1, element);
2490 if (content == EL_ACID)
2492 int dir = MovDir[lx][ly];
2493 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2494 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2496 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2498 // prevent target field from being drawn again (but without masking)
2499 // (this would happen if target field is scanned after moving element)
2500 Stop[newlx][newly] = TRUE;
2503 else if (IS_BLOCKED(lx, ly))
2508 boolean cut_mode = NO_CUTTING;
2509 int element_old, content_old;
2511 Blocked2Moving(lx, ly, &oldx, &oldy);
2514 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2515 MovDir[oldx][oldy] == MV_RIGHT);
2517 element_old = Feld[oldx][oldy];
2518 content_old = Store[oldx][oldy];
2520 if (element_old == EL_QUICKSAND_EMPTYING ||
2521 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2522 element_old == EL_MAGIC_WALL_EMPTYING ||
2523 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2524 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2525 element_old == EL_AMOEBA_DROPPING)
2526 cut_mode = CUT_ABOVE;
2528 DrawScreenElement(x, y, EL_EMPTY);
2531 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2533 else if (cut_mode == NO_CUTTING)
2534 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2537 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2540 else if (IS_DRAWABLE(element))
2541 DrawScreenElement(x, y, element);
2543 DrawScreenElement(x, y, EL_EMPTY);
2546 void DrawLevelField(int x, int y)
2548 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2549 DrawScreenField(SCREENX(x), SCREENY(y));
2550 else if (IS_MOVING(x, y))
2554 Moving2Blocked(x, y, &newx, &newy);
2555 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2556 DrawScreenField(SCREENX(newx), SCREENY(newy));
2558 else if (IS_BLOCKED(x, y))
2562 Blocked2Moving(x, y, &oldx, &oldy);
2563 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2564 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2568 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2569 int (*el2img_function)(int), boolean masked,
2570 int element_bits_draw)
2572 int element_base = map_mm_wall_element(element);
2573 int element_bits = (IS_DF_WALL(element) ?
2574 element - EL_DF_WALL_START :
2575 IS_MM_WALL(element) ?
2576 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2577 int graphic = el2img_function(element_base);
2578 int tilesize_draw = tilesize / 2;
2583 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2585 for (i = 0; i < 4; i++)
2587 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2588 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2590 if (!(element_bits_draw & (1 << i)))
2593 if (element_bits & (1 << i))
2596 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2597 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2599 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2600 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2605 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2606 tilesize_draw, tilesize_draw);
2611 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2612 boolean masked, int element_bits_draw)
2614 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2615 element, tilesize, el2edimg, masked, element_bits_draw);
2618 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2619 int (*el2img_function)(int))
2621 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2625 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2628 if (IS_MM_WALL(element))
2630 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2631 element, tilesize, el2edimg, masked, 0x000f);
2635 int graphic = el2edimg(element);
2638 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2640 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2644 void DrawSizedElement(int x, int y, int element, int tilesize)
2646 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2649 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2651 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2654 void DrawMiniElement(int x, int y, int element)
2658 graphic = el2edimg(element);
2659 DrawMiniGraphic(x, y, graphic);
2662 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2665 int x = sx + scroll_x, y = sy + scroll_y;
2667 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2668 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2669 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2670 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2672 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2675 void DrawMiniElementOrWall(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 DrawMiniElement(sx, sy, EL_EMPTY);
2681 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2682 DrawMiniElement(sx, sy, Feld[x][y]);
2684 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2687 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2688 int x, int y, int xsize, int ysize,
2689 int tile_width, int tile_height)
2693 int dst_x = startx + x * tile_width;
2694 int dst_y = starty + y * tile_height;
2695 int width = graphic_info[graphic].width;
2696 int height = graphic_info[graphic].height;
2697 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2698 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2699 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2700 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2701 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2702 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2703 boolean draw_masked = graphic_info[graphic].draw_masked;
2705 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2707 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2709 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2713 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2714 inner_sx + (x - 1) * tile_width % inner_width);
2715 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2716 inner_sy + (y - 1) * tile_height % inner_height);
2719 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2722 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2726 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2727 int x, int y, int xsize, int ysize,
2730 int font_width = getFontWidth(font_nr);
2731 int font_height = getFontHeight(font_nr);
2733 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2734 font_width, font_height);
2737 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2739 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2740 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2741 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2742 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2743 boolean no_delay = (tape.warp_forward);
2744 unsigned int anim_delay = 0;
2745 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2746 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2747 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2748 int font_width = getFontWidth(font_nr);
2749 int font_height = getFontHeight(font_nr);
2750 int max_xsize = level.envelope[envelope_nr].xsize;
2751 int max_ysize = level.envelope[envelope_nr].ysize;
2752 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2753 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2754 int xend = max_xsize;
2755 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2756 int xstep = (xstart < xend ? 1 : 0);
2757 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2759 int end = MAX(xend - xstart, yend - ystart);
2762 for (i = start; i <= end; i++)
2764 int last_frame = end; // last frame of this "for" loop
2765 int x = xstart + i * xstep;
2766 int y = ystart + i * ystep;
2767 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2768 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2769 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2770 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2773 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2775 BlitScreenToBitmap(backbuffer);
2777 SetDrawtoField(DRAW_TO_BACKBUFFER);
2779 for (yy = 0; yy < ysize; yy++)
2780 for (xx = 0; xx < xsize; xx++)
2781 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2783 DrawTextBuffer(sx + font_width, sy + font_height,
2784 level.envelope[envelope_nr].text, font_nr, max_xsize,
2785 xsize - 2, ysize - 2, 0, mask_mode,
2786 level.envelope[envelope_nr].autowrap,
2787 level.envelope[envelope_nr].centered, FALSE);
2789 redraw_mask |= REDRAW_FIELD;
2792 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2795 ClearAutoRepeatKeyEvents();
2798 void ShowEnvelope(int envelope_nr)
2800 int element = EL_ENVELOPE_1 + envelope_nr;
2801 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2802 int sound_opening = element_info[element].sound[ACTION_OPENING];
2803 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2804 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2805 boolean no_delay = (tape.warp_forward);
2806 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2807 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2808 int anim_mode = graphic_info[graphic].anim_mode;
2809 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2810 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2812 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2814 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2816 if (anim_mode == ANIM_DEFAULT)
2817 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2819 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2822 Delay_WithScreenUpdates(wait_delay_value);
2824 WaitForEventToContinue();
2826 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2828 if (anim_mode != ANIM_NONE)
2829 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2831 if (anim_mode == ANIM_DEFAULT)
2832 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2834 game.envelope_active = FALSE;
2836 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2838 redraw_mask |= REDRAW_FIELD;
2842 static void setRequestBasePosition(int *x, int *y)
2844 int sx_base, sy_base;
2846 if (request.x != -1)
2847 sx_base = request.x;
2848 else if (request.align == ALIGN_LEFT)
2850 else if (request.align == ALIGN_RIGHT)
2851 sx_base = SX + SXSIZE;
2853 sx_base = SX + SXSIZE / 2;
2855 if (request.y != -1)
2856 sy_base = request.y;
2857 else if (request.valign == VALIGN_TOP)
2859 else if (request.valign == VALIGN_BOTTOM)
2860 sy_base = SY + SYSIZE;
2862 sy_base = SY + SYSIZE / 2;
2868 static void setRequestPositionExt(int *x, int *y, int width, int height,
2869 boolean add_border_size)
2871 int border_size = request.border_size;
2872 int sx_base, sy_base;
2875 setRequestBasePosition(&sx_base, &sy_base);
2877 if (request.align == ALIGN_LEFT)
2879 else if (request.align == ALIGN_RIGHT)
2880 sx = sx_base - width;
2882 sx = sx_base - width / 2;
2884 if (request.valign == VALIGN_TOP)
2886 else if (request.valign == VALIGN_BOTTOM)
2887 sy = sy_base - height;
2889 sy = sy_base - height / 2;
2891 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2892 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2894 if (add_border_size)
2904 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2906 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2909 static void DrawEnvelopeRequest(char *text)
2911 char *text_final = text;
2912 char *text_door_style = NULL;
2913 int graphic = IMG_BACKGROUND_REQUEST;
2914 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2915 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2916 int font_nr = FONT_REQUEST;
2917 int font_width = getFontWidth(font_nr);
2918 int font_height = getFontHeight(font_nr);
2919 int border_size = request.border_size;
2920 int line_spacing = request.line_spacing;
2921 int line_height = font_height + line_spacing;
2922 int max_text_width = request.width - 2 * border_size;
2923 int max_text_height = request.height - 2 * border_size;
2924 int line_length = max_text_width / font_width;
2925 int max_lines = max_text_height / line_height;
2926 int text_width = line_length * font_width;
2927 int width = request.width;
2928 int height = request.height;
2929 int tile_size = MAX(request.step_offset, 1);
2930 int x_steps = width / tile_size;
2931 int y_steps = height / tile_size;
2932 int sx_offset = border_size;
2933 int sy_offset = border_size;
2937 if (request.centered)
2938 sx_offset = (request.width - text_width) / 2;
2940 if (request.wrap_single_words && !request.autowrap)
2942 char *src_text_ptr, *dst_text_ptr;
2944 text_door_style = checked_malloc(2 * strlen(text) + 1);
2946 src_text_ptr = text;
2947 dst_text_ptr = text_door_style;
2949 while (*src_text_ptr)
2951 if (*src_text_ptr == ' ' ||
2952 *src_text_ptr == '?' ||
2953 *src_text_ptr == '!')
2954 *dst_text_ptr++ = '\n';
2956 if (*src_text_ptr != ' ')
2957 *dst_text_ptr++ = *src_text_ptr;
2962 *dst_text_ptr = '\0';
2964 text_final = text_door_style;
2967 setRequestPosition(&sx, &sy, FALSE);
2969 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2971 for (y = 0; y < y_steps; y++)
2972 for (x = 0; x < x_steps; x++)
2973 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2974 x, y, x_steps, y_steps,
2975 tile_size, tile_size);
2977 // force DOOR font inside door area
2978 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2980 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2981 line_length, -1, max_lines, line_spacing, mask_mode,
2982 request.autowrap, request.centered, FALSE);
2986 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2987 RedrawGadget(tool_gadget[i]);
2989 // store readily prepared envelope request for later use when animating
2990 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2992 if (text_door_style)
2993 free(text_door_style);
2996 static void AnimateEnvelopeRequest(int anim_mode, int action)
2998 int graphic = IMG_BACKGROUND_REQUEST;
2999 boolean draw_masked = graphic_info[graphic].draw_masked;
3000 int delay_value_normal = request.step_delay;
3001 int delay_value_fast = delay_value_normal / 2;
3002 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3003 boolean no_delay = (tape.warp_forward);
3004 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3005 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3006 unsigned int anim_delay = 0;
3008 int tile_size = MAX(request.step_offset, 1);
3009 int max_xsize = request.width / tile_size;
3010 int max_ysize = request.height / tile_size;
3011 int max_xsize_inner = max_xsize - 2;
3012 int max_ysize_inner = max_ysize - 2;
3014 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3015 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3016 int xend = max_xsize_inner;
3017 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3018 int xstep = (xstart < xend ? 1 : 0);
3019 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3021 int end = MAX(xend - xstart, yend - ystart);
3024 if (setup.quick_doors)
3031 for (i = start; i <= end; i++)
3033 int last_frame = end; // last frame of this "for" loop
3034 int x = xstart + i * xstep;
3035 int y = ystart + i * ystep;
3036 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3037 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3038 int xsize_size_left = (xsize - 1) * tile_size;
3039 int ysize_size_top = (ysize - 1) * tile_size;
3040 int max_xsize_pos = (max_xsize - 1) * tile_size;
3041 int max_ysize_pos = (max_ysize - 1) * tile_size;
3042 int width = xsize * tile_size;
3043 int height = ysize * tile_size;
3048 setRequestPosition(&src_x, &src_y, FALSE);
3049 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3051 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3053 for (yy = 0; yy < 2; yy++)
3055 for (xx = 0; xx < 2; xx++)
3057 int src_xx = src_x + xx * max_xsize_pos;
3058 int src_yy = src_y + yy * max_ysize_pos;
3059 int dst_xx = dst_x + xx * xsize_size_left;
3060 int dst_yy = dst_y + yy * ysize_size_top;
3061 int xx_size = (xx ? tile_size : xsize_size_left);
3062 int yy_size = (yy ? tile_size : ysize_size_top);
3065 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3066 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3068 BlitBitmap(bitmap_db_store_2, backbuffer,
3069 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3073 redraw_mask |= REDRAW_FIELD;
3077 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3080 ClearAutoRepeatKeyEvents();
3083 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3085 int graphic = IMG_BACKGROUND_REQUEST;
3086 int sound_opening = SND_REQUEST_OPENING;
3087 int sound_closing = SND_REQUEST_CLOSING;
3088 int anim_mode_1 = request.anim_mode; // (higher priority)
3089 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3090 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3091 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3092 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3094 if (game_status == GAME_MODE_PLAYING)
3095 BlitScreenToBitmap(backbuffer);
3097 SetDrawtoField(DRAW_TO_BACKBUFFER);
3099 // SetDrawBackgroundMask(REDRAW_NONE);
3101 if (action == ACTION_OPENING)
3103 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3105 if (req_state & REQ_ASK)
3107 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3108 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3109 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3110 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3112 else if (req_state & REQ_CONFIRM)
3114 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3115 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3117 else if (req_state & REQ_PLAYER)
3119 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3120 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3121 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3122 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3125 DrawEnvelopeRequest(text);
3128 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3130 if (action == ACTION_OPENING)
3132 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3134 if (anim_mode == ANIM_DEFAULT)
3135 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3137 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3141 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3143 if (anim_mode != ANIM_NONE)
3144 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3146 if (anim_mode == ANIM_DEFAULT)
3147 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3150 game.envelope_active = FALSE;
3152 if (action == ACTION_CLOSING)
3153 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3155 // SetDrawBackgroundMask(last_draw_background_mask);
3157 redraw_mask |= REDRAW_FIELD;
3161 if (action == ACTION_CLOSING &&
3162 game_status == GAME_MODE_PLAYING &&
3163 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3164 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3167 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3169 if (IS_MM_WALL(element))
3171 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3177 int graphic = el2preimg(element);
3179 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3180 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3185 void DrawLevel(int draw_background_mask)
3189 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3190 SetDrawBackgroundMask(draw_background_mask);
3194 for (x = BX1; x <= BX2; x++)
3195 for (y = BY1; y <= BY2; y++)
3196 DrawScreenField(x, y);
3198 redraw_mask |= REDRAW_FIELD;
3201 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3206 for (x = 0; x < size_x; x++)
3207 for (y = 0; y < size_y; y++)
3208 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3210 redraw_mask |= REDRAW_FIELD;
3213 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3217 for (x = 0; x < size_x; x++)
3218 for (y = 0; y < size_y; y++)
3219 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3221 redraw_mask |= REDRAW_FIELD;
3224 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3226 boolean show_level_border = (BorderElement != EL_EMPTY);
3227 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3228 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3229 int tile_size = preview.tile_size;
3230 int preview_width = preview.xsize * tile_size;
3231 int preview_height = preview.ysize * tile_size;
3232 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3233 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3234 int real_preview_width = real_preview_xsize * tile_size;
3235 int real_preview_height = real_preview_ysize * tile_size;
3236 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3237 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3240 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3243 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3245 dst_x += (preview_width - real_preview_width) / 2;
3246 dst_y += (preview_height - real_preview_height) / 2;
3248 for (x = 0; x < real_preview_xsize; x++)
3250 for (y = 0; y < real_preview_ysize; y++)
3252 int lx = from_x + x + (show_level_border ? -1 : 0);
3253 int ly = from_y + y + (show_level_border ? -1 : 0);
3254 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3255 getBorderElement(lx, ly));
3257 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3258 element, tile_size);
3262 redraw_mask |= REDRAW_FIELD;
3265 #define MICROLABEL_EMPTY 0
3266 #define MICROLABEL_LEVEL_NAME 1
3267 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3268 #define MICROLABEL_LEVEL_AUTHOR 3
3269 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3270 #define MICROLABEL_IMPORTED_FROM 5
3271 #define MICROLABEL_IMPORTED_BY_HEAD 6
3272 #define MICROLABEL_IMPORTED_BY 7
3274 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3276 int max_text_width = SXSIZE;
3277 int font_width = getFontWidth(font_nr);
3279 if (pos->align == ALIGN_CENTER)
3280 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3281 else if (pos->align == ALIGN_RIGHT)
3282 max_text_width = pos->x;
3284 max_text_width = SXSIZE - pos->x;
3286 return max_text_width / font_width;
3289 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3291 char label_text[MAX_OUTPUT_LINESIZE + 1];
3292 int max_len_label_text;
3293 int font_nr = pos->font;
3296 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3299 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3300 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3301 mode == MICROLABEL_IMPORTED_BY_HEAD)
3302 font_nr = pos->font_alt;
3304 max_len_label_text = getMaxTextLength(pos, font_nr);
3306 if (pos->size != -1)
3307 max_len_label_text = pos->size;
3309 for (i = 0; i < max_len_label_text; i++)
3310 label_text[i] = ' ';
3311 label_text[max_len_label_text] = '\0';
3313 if (strlen(label_text) > 0)
3314 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3317 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3318 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3319 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3320 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3321 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3322 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3323 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3324 max_len_label_text);
3325 label_text[max_len_label_text] = '\0';
3327 if (strlen(label_text) > 0)
3328 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3330 redraw_mask |= REDRAW_FIELD;
3333 static void DrawPreviewLevelLabel(int mode)
3335 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3338 static void DrawPreviewLevelInfo(int mode)
3340 if (mode == MICROLABEL_LEVEL_NAME)
3341 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3342 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3343 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3346 static void DrawPreviewLevelExt(boolean restart)
3348 static unsigned int scroll_delay = 0;
3349 static unsigned int label_delay = 0;
3350 static int from_x, from_y, scroll_direction;
3351 static int label_state, label_counter;
3352 unsigned int scroll_delay_value = preview.step_delay;
3353 boolean show_level_border = (BorderElement != EL_EMPTY);
3354 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3355 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3362 if (preview.anim_mode == ANIM_CENTERED)
3364 if (level_xsize > preview.xsize)
3365 from_x = (level_xsize - preview.xsize) / 2;
3366 if (level_ysize > preview.ysize)
3367 from_y = (level_ysize - preview.ysize) / 2;
3370 from_x += preview.xoffset;
3371 from_y += preview.yoffset;
3373 scroll_direction = MV_RIGHT;
3377 DrawPreviewLevelPlayfield(from_x, from_y);
3378 DrawPreviewLevelLabel(label_state);
3380 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3381 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3383 // initialize delay counters
3384 DelayReached(&scroll_delay, 0);
3385 DelayReached(&label_delay, 0);
3387 if (leveldir_current->name)
3389 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3390 char label_text[MAX_OUTPUT_LINESIZE + 1];
3391 int font_nr = pos->font;
3392 int max_len_label_text = getMaxTextLength(pos, font_nr);
3394 if (pos->size != -1)
3395 max_len_label_text = pos->size;
3397 strncpy(label_text, leveldir_current->name, max_len_label_text);
3398 label_text[max_len_label_text] = '\0';
3400 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3401 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3407 // scroll preview level, if needed
3408 if (preview.anim_mode != ANIM_NONE &&
3409 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3410 DelayReached(&scroll_delay, scroll_delay_value))
3412 switch (scroll_direction)
3417 from_x -= preview.step_offset;
3418 from_x = (from_x < 0 ? 0 : from_x);
3421 scroll_direction = MV_UP;
3425 if (from_x < level_xsize - preview.xsize)
3427 from_x += preview.step_offset;
3428 from_x = (from_x > level_xsize - preview.xsize ?
3429 level_xsize - preview.xsize : from_x);
3432 scroll_direction = MV_DOWN;
3438 from_y -= preview.step_offset;
3439 from_y = (from_y < 0 ? 0 : from_y);
3442 scroll_direction = MV_RIGHT;
3446 if (from_y < level_ysize - preview.ysize)
3448 from_y += preview.step_offset;
3449 from_y = (from_y > level_ysize - preview.ysize ?
3450 level_ysize - preview.ysize : from_y);
3453 scroll_direction = MV_LEFT;
3460 DrawPreviewLevelPlayfield(from_x, from_y);
3463 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3464 // redraw micro level label, if needed
3465 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3466 !strEqual(level.author, ANONYMOUS_NAME) &&
3467 !strEqual(level.author, leveldir_current->name) &&
3468 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3470 int max_label_counter = 23;
3472 if (leveldir_current->imported_from != NULL &&
3473 strlen(leveldir_current->imported_from) > 0)
3474 max_label_counter += 14;
3475 if (leveldir_current->imported_by != NULL &&
3476 strlen(leveldir_current->imported_by) > 0)
3477 max_label_counter += 14;
3479 label_counter = (label_counter + 1) % max_label_counter;
3480 label_state = (label_counter >= 0 && label_counter <= 7 ?
3481 MICROLABEL_LEVEL_NAME :
3482 label_counter >= 9 && label_counter <= 12 ?
3483 MICROLABEL_LEVEL_AUTHOR_HEAD :
3484 label_counter >= 14 && label_counter <= 21 ?
3485 MICROLABEL_LEVEL_AUTHOR :
3486 label_counter >= 23 && label_counter <= 26 ?
3487 MICROLABEL_IMPORTED_FROM_HEAD :
3488 label_counter >= 28 && label_counter <= 35 ?
3489 MICROLABEL_IMPORTED_FROM :
3490 label_counter >= 37 && label_counter <= 40 ?
3491 MICROLABEL_IMPORTED_BY_HEAD :
3492 label_counter >= 42 && label_counter <= 49 ?
3493 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3495 if (leveldir_current->imported_from == NULL &&
3496 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3497 label_state == MICROLABEL_IMPORTED_FROM))
3498 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3499 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3501 DrawPreviewLevelLabel(label_state);
3505 void DrawPreviewPlayers(void)
3507 if (game_status != GAME_MODE_MAIN)
3510 // do not draw preview players if level preview redefined, but players aren't
3511 if (preview.redefined && !menu.main.preview_players.redefined)
3514 boolean player_found[MAX_PLAYERS];
3515 int num_players = 0;
3518 for (i = 0; i < MAX_PLAYERS; i++)
3519 player_found[i] = FALSE;
3521 // check which players can be found in the level (simple approach)
3522 for (x = 0; x < lev_fieldx; x++)
3524 for (y = 0; y < lev_fieldy; y++)
3526 int element = level.field[x][y];
3528 if (ELEM_IS_PLAYER(element))
3530 int player_nr = GET_PLAYER_NR(element);
3532 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3534 if (!player_found[player_nr])
3537 player_found[player_nr] = TRUE;
3542 struct TextPosInfo *pos = &menu.main.preview_players;
3543 int tile_size = pos->tile_size;
3544 int border_size = pos->border_size;
3545 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3546 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3547 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3548 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3549 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3550 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3551 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3552 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3553 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3554 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3555 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3556 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3558 // clear area in which the players will be drawn
3559 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3560 max_players_width, max_players_height);
3562 if (!network.enabled && !setup.team_mode)
3565 // only draw players if level is suited for team mode
3566 if (num_players < 2)
3569 // draw all players that were found in the level
3570 for (i = 0; i < MAX_PLAYERS; i++)
3572 if (player_found[i])
3574 int graphic = el2img(EL_PLAYER_1 + i);
3576 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3578 xpos += player_xoffset;
3579 ypos += player_yoffset;
3584 void DrawPreviewLevelInitial(void)
3586 DrawPreviewLevelExt(TRUE);
3587 DrawPreviewPlayers();
3590 void DrawPreviewLevelAnimation(void)
3592 DrawPreviewLevelExt(FALSE);
3595 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3596 int border_size, int font_nr)
3598 int graphic = el2img(EL_PLAYER_1 + player_nr);
3599 int font_height = getFontHeight(font_nr);
3600 int player_height = MAX(tile_size, font_height);
3601 int xoffset_text = tile_size + border_size;
3602 int yoffset_text = (player_height - font_height) / 2;
3603 int yoffset_graphic = (player_height - tile_size) / 2;
3604 char *player_name = getNetworkPlayerName(player_nr + 1);
3606 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3608 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3611 static void DrawNetworkPlayersExt(boolean force)
3613 if (game_status != GAME_MODE_MAIN)
3616 if (!network.connected && !force)
3619 // do not draw network players if level preview redefined, but players aren't
3620 if (preview.redefined && !menu.main.network_players.redefined)
3623 int num_players = 0;
3626 for (i = 0; i < MAX_PLAYERS; i++)
3627 if (stored_player[i].connected_network)
3630 struct TextPosInfo *pos = &menu.main.network_players;
3631 int tile_size = pos->tile_size;
3632 int border_size = pos->border_size;
3633 int xoffset_text = tile_size + border_size;
3634 int font_nr = pos->font;
3635 int font_width = getFontWidth(font_nr);
3636 int font_height = getFontHeight(font_nr);
3637 int player_height = MAX(tile_size, font_height);
3638 int player_yoffset = player_height + border_size;
3639 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3640 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3641 int all_players_height = num_players * player_yoffset - border_size;
3642 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3643 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3644 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3646 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3647 max_players_width, max_players_height);
3649 // first draw local network player ...
3650 for (i = 0; i < MAX_PLAYERS; i++)
3652 if (stored_player[i].connected_network &&
3653 stored_player[i].connected_locally)
3655 char *player_name = getNetworkPlayerName(i + 1);
3656 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3657 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3659 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3661 ypos += player_yoffset;
3665 // ... then draw all other network players
3666 for (i = 0; i < MAX_PLAYERS; i++)
3668 if (stored_player[i].connected_network &&
3669 !stored_player[i].connected_locally)
3671 char *player_name = getNetworkPlayerName(i + 1);
3672 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3673 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3675 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3677 ypos += player_yoffset;
3682 void DrawNetworkPlayers(void)
3684 DrawNetworkPlayersExt(FALSE);
3687 void ClearNetworkPlayers(void)
3689 DrawNetworkPlayersExt(TRUE);
3692 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3693 int graphic, int sync_frame,
3696 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3698 if (mask_mode == USE_MASKING)
3699 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3701 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3704 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3705 int graphic, int sync_frame, int mask_mode)
3707 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3709 if (mask_mode == USE_MASKING)
3710 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3712 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3715 static void DrawGraphicAnimation(int x, int y, int graphic)
3717 int lx = LEVELX(x), ly = LEVELY(y);
3719 if (!IN_SCR_FIELD(x, y))
3722 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3723 graphic, GfxFrame[lx][ly], NO_MASKING);
3725 MarkTileDirty(x, y);
3728 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3730 int lx = LEVELX(x), ly = LEVELY(y);
3732 if (!IN_SCR_FIELD(x, y))
3735 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3736 graphic, GfxFrame[lx][ly], NO_MASKING);
3737 MarkTileDirty(x, y);
3740 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3742 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3745 void DrawLevelElementAnimation(int x, int y, int element)
3747 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3749 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3752 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3754 int sx = SCREENX(x), sy = SCREENY(y);
3756 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3759 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3762 DrawGraphicAnimation(sx, sy, graphic);
3765 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3766 DrawLevelFieldCrumbled(x, y);
3768 if (GFX_CRUMBLED(Feld[x][y]))
3769 DrawLevelFieldCrumbled(x, y);
3773 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3775 int sx = SCREENX(x), sy = SCREENY(y);
3778 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3781 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3783 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3786 DrawGraphicAnimation(sx, sy, graphic);
3788 if (GFX_CRUMBLED(element))
3789 DrawLevelFieldCrumbled(x, y);
3792 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3794 if (player->use_murphy)
3796 // this works only because currently only one player can be "murphy" ...
3797 static int last_horizontal_dir = MV_LEFT;
3798 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3800 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3801 last_horizontal_dir = move_dir;
3803 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3805 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3807 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3813 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3816 static boolean equalGraphics(int graphic1, int graphic2)
3818 struct GraphicInfo *g1 = &graphic_info[graphic1];
3819 struct GraphicInfo *g2 = &graphic_info[graphic2];
3821 return (g1->bitmap == g2->bitmap &&
3822 g1->src_x == g2->src_x &&
3823 g1->src_y == g2->src_y &&
3824 g1->anim_frames == g2->anim_frames &&
3825 g1->anim_delay == g2->anim_delay &&
3826 g1->anim_mode == g2->anim_mode);
3829 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3833 DRAW_PLAYER_STAGE_INIT = 0,
3834 DRAW_PLAYER_STAGE_LAST_FIELD,
3835 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3836 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3837 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3838 DRAW_PLAYER_STAGE_PLAYER,
3840 DRAW_PLAYER_STAGE_PLAYER,
3841 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3843 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3844 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3846 NUM_DRAW_PLAYER_STAGES
3849 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3851 static int static_last_player_graphic[MAX_PLAYERS];
3852 static int static_last_player_frame[MAX_PLAYERS];
3853 static boolean static_player_is_opaque[MAX_PLAYERS];
3854 static boolean draw_player[MAX_PLAYERS];
3855 int pnr = player->index_nr;
3857 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3859 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3860 static_last_player_frame[pnr] = player->Frame;
3861 static_player_is_opaque[pnr] = FALSE;
3863 draw_player[pnr] = TRUE;
3866 if (!draw_player[pnr])
3870 if (!IN_LEV_FIELD(player->jx, player->jy))
3872 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3873 printf("DrawPlayerField(): This should never happen!\n");
3875 draw_player[pnr] = FALSE;
3881 int last_player_graphic = static_last_player_graphic[pnr];
3882 int last_player_frame = static_last_player_frame[pnr];
3883 boolean player_is_opaque = static_player_is_opaque[pnr];
3885 int jx = player->jx;
3886 int jy = player->jy;
3887 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3888 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3889 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3890 int last_jx = (player->is_moving ? jx - dx : jx);
3891 int last_jy = (player->is_moving ? jy - dy : jy);
3892 int next_jx = jx + dx;
3893 int next_jy = jy + dy;
3894 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3895 int sx = SCREENX(jx);
3896 int sy = SCREENY(jy);
3897 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3898 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3899 int element = Feld[jx][jy];
3900 int last_element = Feld[last_jx][last_jy];
3901 int action = (player->is_pushing ? ACTION_PUSHING :
3902 player->is_digging ? ACTION_DIGGING :
3903 player->is_collecting ? ACTION_COLLECTING :
3904 player->is_moving ? ACTION_MOVING :
3905 player->is_snapping ? ACTION_SNAPPING :
3906 player->is_dropping ? ACTION_DROPPING :
3907 player->is_waiting ? player->action_waiting :
3910 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3912 // ------------------------------------------------------------------------
3913 // initialize drawing the player
3914 // ------------------------------------------------------------------------
3916 draw_player[pnr] = FALSE;
3918 // GfxElement[][] is set to the element the player is digging or collecting;
3919 // remove also for off-screen player if the player is not moving anymore
3920 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3921 GfxElement[jx][jy] = EL_UNDEFINED;
3923 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3926 if (element == EL_EXPLOSION)
3929 InitPlayerGfxAnimation(player, action, move_dir);
3931 draw_player[pnr] = TRUE;
3933 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3935 // ------------------------------------------------------------------------
3936 // draw things in the field the player is leaving, if needed
3937 // ------------------------------------------------------------------------
3939 if (!IN_SCR_FIELD(sx, sy))
3940 draw_player[pnr] = FALSE;
3942 if (!player->is_moving)
3945 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3947 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3949 if (last_element == EL_DYNAMITE_ACTIVE ||
3950 last_element == EL_EM_DYNAMITE_ACTIVE ||
3951 last_element == EL_SP_DISK_RED_ACTIVE)
3952 DrawDynamite(last_jx, last_jy);
3954 DrawLevelFieldThruMask(last_jx, last_jy);
3956 else if (last_element == EL_DYNAMITE_ACTIVE ||
3957 last_element == EL_EM_DYNAMITE_ACTIVE ||
3958 last_element == EL_SP_DISK_RED_ACTIVE)
3959 DrawDynamite(last_jx, last_jy);
3961 DrawLevelField(last_jx, last_jy);
3963 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3964 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3966 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3968 // ------------------------------------------------------------------------
3969 // draw things behind the player, if needed
3970 // ------------------------------------------------------------------------
3974 DrawLevelElement(jx, jy, Back[jx][jy]);
3979 if (IS_ACTIVE_BOMB(element))
3981 DrawLevelElement(jx, jy, EL_EMPTY);
3986 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3988 int old_element = GfxElement[jx][jy];
3989 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3990 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3992 if (GFX_CRUMBLED(old_element))
3993 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3995 DrawGraphic(sx, sy, old_graphic, frame);
3997 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3998 static_player_is_opaque[pnr] = TRUE;
4002 GfxElement[jx][jy] = EL_UNDEFINED;
4004 // make sure that pushed elements are drawn with correct frame rate
4005 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4007 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4008 GfxFrame[jx][jy] = player->StepFrame;
4010 DrawLevelField(jx, jy);
4013 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4015 // ------------------------------------------------------------------------
4016 // draw things the player is pushing, if needed
4017 // ------------------------------------------------------------------------
4019 if (!player->is_pushing || !player->is_moving)
4022 int gfx_frame = GfxFrame[jx][jy];
4024 if (!IS_MOVING(jx, jy)) // push movement already finished
4026 element = Feld[next_jx][next_jy];
4027 gfx_frame = GfxFrame[next_jx][next_jy];
4030 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4031 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4032 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4034 // draw background element under pushed element (like the Sokoban field)
4035 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4037 // this allows transparent pushing animation over non-black background
4040 DrawLevelElement(jx, jy, Back[jx][jy]);
4042 DrawLevelElement(jx, jy, EL_EMPTY);
4044 if (Back[next_jx][next_jy])
4045 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4047 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4049 else if (Back[next_jx][next_jy])
4050 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4052 int px = SCREENX(jx), py = SCREENY(jy);
4053 int pxx = (TILEX - ABS(sxx)) * dx;
4054 int pyy = (TILEY - ABS(syy)) * dy;
4057 // do not draw (EM style) pushing animation when pushing is finished
4058 // (two-tile animations usually do not contain start and end frame)
4059 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4060 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4062 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4064 // masked drawing is needed for EMC style (double) movement graphics
4065 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4066 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4069 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4071 // ------------------------------------------------------------------------
4072 // draw player himself
4073 // ------------------------------------------------------------------------
4075 int graphic = getPlayerGraphic(player, move_dir);
4077 // in the case of changed player action or direction, prevent the current
4078 // animation frame from being restarted for identical animations
4079 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4080 player->Frame = last_player_frame;
4082 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4084 if (player_is_opaque)
4085 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4087 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4089 if (SHIELD_ON(player))
4091 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4092 IMG_SHIELD_NORMAL_ACTIVE);
4093 frame = getGraphicAnimationFrame(graphic, -1);
4095 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4098 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4100 // ------------------------------------------------------------------------
4101 // draw things in front of player (active dynamite or dynabombs)
4102 // ------------------------------------------------------------------------
4104 if (IS_ACTIVE_BOMB(element))
4106 int graphic = el2img(element);
4107 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4109 if (game.emulation == EMU_SUPAPLEX)
4110 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4112 DrawGraphicThruMask(sx, sy, graphic, frame);
4115 if (player_is_moving && last_element == EL_EXPLOSION)
4117 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4118 GfxElement[last_jx][last_jy] : EL_EMPTY);
4119 int graphic = el_act2img(element, ACTION_EXPLODING);
4120 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4121 int phase = ExplodePhase[last_jx][last_jy] - 1;
4122 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4125 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4128 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4130 // ------------------------------------------------------------------------
4131 // draw elements the player is just walking/passing through/under
4132 // ------------------------------------------------------------------------
4134 if (player_is_moving)
4136 // handle the field the player is leaving ...
4137 if (IS_ACCESSIBLE_INSIDE(last_element))
4138 DrawLevelField(last_jx, last_jy);
4139 else if (IS_ACCESSIBLE_UNDER(last_element))
4140 DrawLevelFieldThruMask(last_jx, last_jy);
4143 // do not redraw accessible elements if the player is just pushing them
4144 if (!player_is_moving || !player->is_pushing)
4146 // ... and the field the player is entering
4147 if (IS_ACCESSIBLE_INSIDE(element))
4148 DrawLevelField(jx, jy);
4149 else if (IS_ACCESSIBLE_UNDER(element))
4150 DrawLevelFieldThruMask(jx, jy);
4153 MarkTileDirty(sx, sy);
4157 void DrawPlayer(struct PlayerInfo *player)
4161 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4162 DrawPlayerExt(player, i);
4165 void DrawAllPlayers(void)
4169 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4170 for (j = 0; j < MAX_PLAYERS; j++)
4171 if (stored_player[j].active)
4172 DrawPlayerExt(&stored_player[j], i);
4175 void DrawPlayerField(int x, int y)
4177 if (!IS_PLAYER(x, y))
4180 DrawPlayer(PLAYERINFO(x, y));
4183 // ----------------------------------------------------------------------------
4185 void WaitForEventToContinue(void)
4187 boolean still_wait = TRUE;
4189 if (program.headless)
4192 // simulate releasing mouse button over last gadget, if still pressed
4194 HandleGadgets(-1, -1, 0);
4196 button_status = MB_RELEASED;
4204 if (NextValidEvent(&event))
4208 case EVENT_BUTTONRELEASE:
4209 case EVENT_KEYPRESS:
4210 case SDL_CONTROLLERBUTTONDOWN:
4211 case SDL_JOYBUTTONDOWN:
4215 case EVENT_KEYRELEASE:
4216 ClearPlayerAction();
4220 HandleOtherEvents(&event);
4224 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4233 #define MAX_REQUEST_LINES 13
4234 #define MAX_REQUEST_LINE_FONT1_LEN 7
4235 #define MAX_REQUEST_LINE_FONT2_LEN 10
4237 static int RequestHandleEvents(unsigned int req_state)
4239 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4241 int width = request.width;
4242 int height = request.height;
4246 // when showing request dialog after game ended, deactivate game panel
4247 if (game_just_ended)
4248 game.panel.active = FALSE;
4250 game.request_active = TRUE;
4252 setRequestPosition(&sx, &sy, FALSE);
4254 button_status = MB_RELEASED;
4256 request_gadget_id = -1;
4261 if (game_just_ended)
4263 // the MM game engine does not use a special (scrollable) field buffer
4264 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4265 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4267 HandleGameActions();
4269 SetDrawtoField(DRAW_TO_BACKBUFFER);
4271 if (global.use_envelope_request)
4273 // copy current state of request area to middle of playfield area
4274 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4282 while (NextValidEvent(&event))
4286 case EVENT_BUTTONPRESS:
4287 case EVENT_BUTTONRELEASE:
4288 case EVENT_MOTIONNOTIFY:
4292 if (event.type == EVENT_MOTIONNOTIFY)
4297 motion_status = TRUE;
4298 mx = ((MotionEvent *) &event)->x;
4299 my = ((MotionEvent *) &event)->y;
4303 motion_status = FALSE;
4304 mx = ((ButtonEvent *) &event)->x;
4305 my = ((ButtonEvent *) &event)->y;
4306 if (event.type == EVENT_BUTTONPRESS)
4307 button_status = ((ButtonEvent *) &event)->button;
4309 button_status = MB_RELEASED;
4312 // this sets 'request_gadget_id'
4313 HandleGadgets(mx, my, button_status);
4315 switch (request_gadget_id)
4317 case TOOL_CTRL_ID_YES:
4318 case TOOL_CTRL_ID_TOUCH_YES:
4321 case TOOL_CTRL_ID_NO:
4322 case TOOL_CTRL_ID_TOUCH_NO:
4325 case TOOL_CTRL_ID_CONFIRM:
4326 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4327 result = TRUE | FALSE;
4330 case TOOL_CTRL_ID_PLAYER_1:
4333 case TOOL_CTRL_ID_PLAYER_2:
4336 case TOOL_CTRL_ID_PLAYER_3:
4339 case TOOL_CTRL_ID_PLAYER_4:
4344 // only check clickable animations if no request gadget clicked
4345 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4352 case SDL_WINDOWEVENT:
4353 HandleWindowEvent((WindowEvent *) &event);
4356 case SDL_APP_WILLENTERBACKGROUND:
4357 case SDL_APP_DIDENTERBACKGROUND:
4358 case SDL_APP_WILLENTERFOREGROUND:
4359 case SDL_APP_DIDENTERFOREGROUND:
4360 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4363 case EVENT_KEYPRESS:
4365 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4370 if (req_state & REQ_CONFIRM)
4379 #if defined(KSYM_Rewind)
4380 case KSYM_Rewind: // for Amazon Fire TV remote
4389 #if defined(KSYM_FastForward)
4390 case KSYM_FastForward: // for Amazon Fire TV remote
4396 HandleKeysDebug(key, KEY_PRESSED);
4400 if (req_state & REQ_PLAYER)
4402 int old_player_nr = setup.network_player_nr;
4405 result = old_player_nr + 1;
4410 result = old_player_nr + 1;
4441 case EVENT_KEYRELEASE:
4442 ClearPlayerAction();
4445 case SDL_CONTROLLERBUTTONDOWN:
4446 switch (event.cbutton.button)
4448 case SDL_CONTROLLER_BUTTON_A:
4449 case SDL_CONTROLLER_BUTTON_X:
4450 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4451 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4455 case SDL_CONTROLLER_BUTTON_B:
4456 case SDL_CONTROLLER_BUTTON_Y:
4457 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4458 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4459 case SDL_CONTROLLER_BUTTON_BACK:
4464 if (req_state & REQ_PLAYER)
4466 int old_player_nr = setup.network_player_nr;
4469 result = old_player_nr + 1;
4471 switch (event.cbutton.button)
4473 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4474 case SDL_CONTROLLER_BUTTON_Y:
4478 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4479 case SDL_CONTROLLER_BUTTON_B:
4483 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4484 case SDL_CONTROLLER_BUTTON_A:
4488 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4489 case SDL_CONTROLLER_BUTTON_X:
4500 case SDL_CONTROLLERBUTTONUP:
4501 HandleJoystickEvent(&event);
4502 ClearPlayerAction();
4506 HandleOtherEvents(&event);
4511 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4513 int joy = AnyJoystick();
4515 if (joy & JOY_BUTTON_1)
4517 else if (joy & JOY_BUTTON_2)
4520 else if (AnyJoystick())
4522 int joy = AnyJoystick();
4524 if (req_state & REQ_PLAYER)
4528 else if (joy & JOY_RIGHT)
4530 else if (joy & JOY_DOWN)
4532 else if (joy & JOY_LEFT)
4537 if (game_just_ended)
4539 if (global.use_envelope_request)
4541 // copy back current state of pressed buttons inside request area
4542 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4549 game.request_active = FALSE;
4554 static boolean RequestDoor(char *text, unsigned int req_state)
4556 unsigned int old_door_state;
4557 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4558 int font_nr = FONT_TEXT_2;
4563 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4565 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4566 font_nr = FONT_TEXT_1;
4569 if (game_status == GAME_MODE_PLAYING)
4570 BlitScreenToBitmap(backbuffer);
4572 // disable deactivated drawing when quick-loading level tape recording
4573 if (tape.playing && tape.deactivate_display)
4574 TapeDeactivateDisplayOff(TRUE);
4576 SetMouseCursor(CURSOR_DEFAULT);
4578 // pause network game while waiting for request to answer
4579 if (network.enabled &&
4580 game_status == GAME_MODE_PLAYING &&
4581 !game.all_players_gone &&
4582 req_state & REQUEST_WAIT_FOR_INPUT)
4583 SendToServer_PausePlaying();
4585 old_door_state = GetDoorState();
4587 // simulate releasing mouse button over last gadget, if still pressed
4589 HandleGadgets(-1, -1, 0);
4593 // draw released gadget before proceeding
4596 if (old_door_state & DOOR_OPEN_1)
4598 CloseDoor(DOOR_CLOSE_1);
4600 // save old door content
4601 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4602 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4605 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4606 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4608 // clear door drawing field
4609 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4611 // force DOOR font inside door area
4612 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4614 // write text for request
4615 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4617 char text_line[max_request_line_len + 1];
4623 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4625 tc = *(text_ptr + tx);
4626 // if (!tc || tc == ' ')
4627 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4631 if ((tc == '?' || tc == '!') && tl == 0)
4641 strncpy(text_line, text_ptr, tl);
4644 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4645 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4646 text_line, font_nr);
4648 text_ptr += tl + (tc == ' ' ? 1 : 0);
4649 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4654 if (req_state & REQ_ASK)
4656 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4657 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4658 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4659 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4661 else if (req_state & REQ_CONFIRM)
4663 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4664 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4666 else if (req_state & REQ_PLAYER)
4668 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4669 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4670 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4671 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4674 // copy request gadgets to door backbuffer
4675 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4677 OpenDoor(DOOR_OPEN_1);
4679 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4681 if (game_status == GAME_MODE_PLAYING)
4683 SetPanelBackground();
4684 SetDrawBackgroundMask(REDRAW_DOOR_1);
4688 SetDrawBackgroundMask(REDRAW_FIELD);
4694 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4696 // ---------- handle request buttons ----------
4697 result = RequestHandleEvents(req_state);
4701 if (!(req_state & REQ_STAY_OPEN))
4703 CloseDoor(DOOR_CLOSE_1);
4705 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4706 (req_state & REQ_REOPEN))
4707 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4712 if (game_status == GAME_MODE_PLAYING)
4714 SetPanelBackground();
4715 SetDrawBackgroundMask(REDRAW_DOOR_1);
4719 SetDrawBackgroundMask(REDRAW_FIELD);
4722 // continue network game after request
4723 if (network.enabled &&
4724 game_status == GAME_MODE_PLAYING &&
4725 !game.all_players_gone &&
4726 req_state & REQUEST_WAIT_FOR_INPUT)
4727 SendToServer_ContinuePlaying();
4729 // restore deactivated drawing when quick-loading level tape recording
4730 if (tape.playing && tape.deactivate_display)
4731 TapeDeactivateDisplayOn();
4736 static boolean RequestEnvelope(char *text, unsigned int req_state)
4740 if (game_status == GAME_MODE_PLAYING)
4741 BlitScreenToBitmap(backbuffer);
4743 // disable deactivated drawing when quick-loading level tape recording
4744 if (tape.playing && tape.deactivate_display)
4745 TapeDeactivateDisplayOff(TRUE);
4747 SetMouseCursor(CURSOR_DEFAULT);
4749 // pause network game while waiting for request to answer
4750 if (network.enabled &&
4751 game_status == GAME_MODE_PLAYING &&
4752 !game.all_players_gone &&
4753 req_state & REQUEST_WAIT_FOR_INPUT)
4754 SendToServer_PausePlaying();
4756 // simulate releasing mouse button over last gadget, if still pressed
4758 HandleGadgets(-1, -1, 0);
4762 // (replace with setting corresponding request background)
4763 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4764 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4766 // clear door drawing field
4767 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4769 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4771 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4773 if (game_status == GAME_MODE_PLAYING)
4775 SetPanelBackground();
4776 SetDrawBackgroundMask(REDRAW_DOOR_1);
4780 SetDrawBackgroundMask(REDRAW_FIELD);
4786 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4788 // ---------- handle request buttons ----------
4789 result = RequestHandleEvents(req_state);
4793 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4797 if (game_status == GAME_MODE_PLAYING)
4799 SetPanelBackground();
4800 SetDrawBackgroundMask(REDRAW_DOOR_1);
4804 SetDrawBackgroundMask(REDRAW_FIELD);
4807 // continue network game after request
4808 if (network.enabled &&
4809 game_status == GAME_MODE_PLAYING &&
4810 !game.all_players_gone &&
4811 req_state & REQUEST_WAIT_FOR_INPUT)
4812 SendToServer_ContinuePlaying();
4814 // restore deactivated drawing when quick-loading level tape recording
4815 if (tape.playing && tape.deactivate_display)
4816 TapeDeactivateDisplayOn();
4821 boolean Request(char *text, unsigned int req_state)
4823 boolean overlay_enabled = GetOverlayEnabled();
4826 SetOverlayEnabled(FALSE);
4828 if (global.use_envelope_request)
4829 result = RequestEnvelope(text, req_state);
4831 result = RequestDoor(text, req_state);
4833 SetOverlayEnabled(overlay_enabled);
4838 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4840 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4841 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4844 if (dpo1->sort_priority != dpo2->sort_priority)
4845 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4847 compare_result = dpo1->nr - dpo2->nr;
4849 return compare_result;
4852 void InitGraphicCompatibilityInfo_Doors(void)
4858 struct DoorInfo *door;
4862 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4863 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4865 { -1, -1, -1, NULL }
4867 struct Rect door_rect_list[] =
4869 { DX, DY, DXSIZE, DYSIZE },
4870 { VX, VY, VXSIZE, VYSIZE }
4874 for (i = 0; doors[i].door_token != -1; i++)
4876 int door_token = doors[i].door_token;
4877 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4878 int part_1 = doors[i].part_1;
4879 int part_8 = doors[i].part_8;
4880 int part_2 = part_1 + 1;
4881 int part_3 = part_1 + 2;
4882 struct DoorInfo *door = doors[i].door;
4883 struct Rect *door_rect = &door_rect_list[door_index];
4884 boolean door_gfx_redefined = FALSE;
4886 // check if any door part graphic definitions have been redefined
4888 for (j = 0; door_part_controls[j].door_token != -1; j++)
4890 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4891 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4893 if (dpc->door_token == door_token && fi->redefined)
4894 door_gfx_redefined = TRUE;
4897 // check for old-style door graphic/animation modifications
4899 if (!door_gfx_redefined)
4901 if (door->anim_mode & ANIM_STATIC_PANEL)
4903 door->panel.step_xoffset = 0;
4904 door->panel.step_yoffset = 0;
4907 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4909 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4910 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4911 int num_door_steps, num_panel_steps;
4913 // remove door part graphics other than the two default wings
4915 for (j = 0; door_part_controls[j].door_token != -1; j++)
4917 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4918 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4920 if (dpc->graphic >= part_3 &&
4921 dpc->graphic <= part_8)
4925 // set graphics and screen positions of the default wings
4927 g_part_1->width = door_rect->width;
4928 g_part_1->height = door_rect->height;
4929 g_part_2->width = door_rect->width;
4930 g_part_2->height = door_rect->height;
4931 g_part_2->src_x = door_rect->width;
4932 g_part_2->src_y = g_part_1->src_y;
4934 door->part_2.x = door->part_1.x;
4935 door->part_2.y = door->part_1.y;
4937 if (door->width != -1)
4939 g_part_1->width = door->width;
4940 g_part_2->width = door->width;
4942 // special treatment for graphics and screen position of right wing
4943 g_part_2->src_x += door_rect->width - door->width;
4944 door->part_2.x += door_rect->width - door->width;
4947 if (door->height != -1)
4949 g_part_1->height = door->height;
4950 g_part_2->height = door->height;
4952 // special treatment for graphics and screen position of bottom wing
4953 g_part_2->src_y += door_rect->height - door->height;
4954 door->part_2.y += door_rect->height - door->height;
4957 // set animation delays for the default wings and panels
4959 door->part_1.step_delay = door->step_delay;
4960 door->part_2.step_delay = door->step_delay;
4961 door->panel.step_delay = door->step_delay;
4963 // set animation draw order for the default wings
4965 door->part_1.sort_priority = 2; // draw left wing over ...
4966 door->part_2.sort_priority = 1; // ... right wing
4968 // set animation draw offset for the default wings
4970 if (door->anim_mode & ANIM_HORIZONTAL)
4972 door->part_1.step_xoffset = door->step_offset;
4973 door->part_1.step_yoffset = 0;
4974 door->part_2.step_xoffset = door->step_offset * -1;
4975 door->part_2.step_yoffset = 0;
4977 num_door_steps = g_part_1->width / door->step_offset;
4979 else // ANIM_VERTICAL
4981 door->part_1.step_xoffset = 0;
4982 door->part_1.step_yoffset = door->step_offset;
4983 door->part_2.step_xoffset = 0;
4984 door->part_2.step_yoffset = door->step_offset * -1;
4986 num_door_steps = g_part_1->height / door->step_offset;
4989 // set animation draw offset for the default panels
4991 if (door->step_offset > 1)
4993 num_panel_steps = 2 * door_rect->height / door->step_offset;
4994 door->panel.start_step = num_panel_steps - num_door_steps;
4995 door->panel.start_step_closing = door->panel.start_step;
4999 num_panel_steps = door_rect->height / door->step_offset;
5000 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5001 door->panel.start_step_closing = door->panel.start_step;
5002 door->panel.step_delay *= 2;
5009 void InitDoors(void)
5013 for (i = 0; door_part_controls[i].door_token != -1; i++)
5015 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5016 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5018 // initialize "start_step_opening" and "start_step_closing", if needed
5019 if (dpc->pos->start_step_opening == 0 &&
5020 dpc->pos->start_step_closing == 0)
5022 // dpc->pos->start_step_opening = dpc->pos->start_step;
5023 dpc->pos->start_step_closing = dpc->pos->start_step;
5026 // fill structure for door part draw order (sorted below)
5028 dpo->sort_priority = dpc->pos->sort_priority;
5031 // sort door part controls according to sort_priority and graphic number
5032 qsort(door_part_order, MAX_DOOR_PARTS,
5033 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5036 unsigned int OpenDoor(unsigned int door_state)
5038 if (door_state & DOOR_COPY_BACK)
5040 if (door_state & DOOR_OPEN_1)
5041 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5042 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5044 if (door_state & DOOR_OPEN_2)
5045 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5046 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5048 door_state &= ~DOOR_COPY_BACK;
5051 return MoveDoor(door_state);
5054 unsigned int CloseDoor(unsigned int door_state)
5056 unsigned int old_door_state = GetDoorState();
5058 if (!(door_state & DOOR_NO_COPY_BACK))
5060 if (old_door_state & DOOR_OPEN_1)
5061 BlitBitmap(backbuffer, bitmap_db_door_1,
5062 DX, DY, DXSIZE, DYSIZE, 0, 0);
5064 if (old_door_state & DOOR_OPEN_2)
5065 BlitBitmap(backbuffer, bitmap_db_door_2,
5066 VX, VY, VXSIZE, VYSIZE, 0, 0);
5068 door_state &= ~DOOR_NO_COPY_BACK;
5071 return MoveDoor(door_state);
5074 unsigned int GetDoorState(void)
5076 return MoveDoor(DOOR_GET_STATE);
5079 unsigned int SetDoorState(unsigned int door_state)
5081 return MoveDoor(door_state | DOOR_SET_STATE);
5084 static int euclid(int a, int b)
5086 return (b ? euclid(b, a % b) : a);
5089 unsigned int MoveDoor(unsigned int door_state)
5091 struct Rect door_rect_list[] =
5093 { DX, DY, DXSIZE, DYSIZE },
5094 { VX, VY, VXSIZE, VYSIZE }
5096 static int door1 = DOOR_CLOSE_1;
5097 static int door2 = DOOR_CLOSE_2;
5098 unsigned int door_delay = 0;
5099 unsigned int door_delay_value;
5102 if (door_state == DOOR_GET_STATE)
5103 return (door1 | door2);
5105 if (door_state & DOOR_SET_STATE)
5107 if (door_state & DOOR_ACTION_1)
5108 door1 = door_state & DOOR_ACTION_1;
5109 if (door_state & DOOR_ACTION_2)
5110 door2 = door_state & DOOR_ACTION_2;
5112 return (door1 | door2);
5115 if (!(door_state & DOOR_FORCE_REDRAW))
5117 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5118 door_state &= ~DOOR_OPEN_1;
5119 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5120 door_state &= ~DOOR_CLOSE_1;
5121 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5122 door_state &= ~DOOR_OPEN_2;
5123 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5124 door_state &= ~DOOR_CLOSE_2;
5127 if (global.autoplay_leveldir)
5129 door_state |= DOOR_NO_DELAY;
5130 door_state &= ~DOOR_CLOSE_ALL;
5133 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5134 door_state |= DOOR_NO_DELAY;
5136 if (door_state & DOOR_ACTION)
5138 boolean door_panel_drawn[NUM_DOORS];
5139 boolean panel_has_doors[NUM_DOORS];
5140 boolean door_part_skip[MAX_DOOR_PARTS];
5141 boolean door_part_done[MAX_DOOR_PARTS];
5142 boolean door_part_done_all;
5143 int num_steps[MAX_DOOR_PARTS];
5144 int max_move_delay = 0; // delay for complete animations of all doors
5145 int max_step_delay = 0; // delay (ms) between two animation frames
5146 int num_move_steps = 0; // number of animation steps for all doors
5147 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5148 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5149 int current_move_delay = 0;
5153 for (i = 0; i < NUM_DOORS; i++)
5154 panel_has_doors[i] = FALSE;
5156 for (i = 0; i < MAX_DOOR_PARTS; i++)
5158 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5159 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5160 int door_token = dpc->door_token;
5162 door_part_done[i] = FALSE;
5163 door_part_skip[i] = (!(door_state & door_token) ||
5167 for (i = 0; i < MAX_DOOR_PARTS; i++)
5169 int nr = door_part_order[i].nr;
5170 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5171 struct DoorPartPosInfo *pos = dpc->pos;
5172 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5173 int door_token = dpc->door_token;
5174 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5175 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5176 int step_xoffset = ABS(pos->step_xoffset);
5177 int step_yoffset = ABS(pos->step_yoffset);
5178 int step_delay = pos->step_delay;
5179 int current_door_state = door_state & door_token;
5180 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5181 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5182 boolean part_opening = (is_panel ? door_closing : door_opening);
5183 int start_step = (part_opening ? pos->start_step_opening :
5184 pos->start_step_closing);
5185 float move_xsize = (step_xoffset ? g->width : 0);
5186 float move_ysize = (step_yoffset ? g->height : 0);
5187 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5188 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5189 int move_steps = (move_xsteps && move_ysteps ?
5190 MIN(move_xsteps, move_ysteps) :
5191 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5192 int move_delay = move_steps * step_delay;
5194 if (door_part_skip[nr])
5197 max_move_delay = MAX(max_move_delay, move_delay);
5198 max_step_delay = (max_step_delay == 0 ? step_delay :
5199 euclid(max_step_delay, step_delay));
5200 num_steps[nr] = move_steps;
5204 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5206 panel_has_doors[door_index] = TRUE;
5210 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5212 num_move_steps = max_move_delay / max_step_delay;
5213 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5215 door_delay_value = max_step_delay;
5217 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5219 start = num_move_steps - 1;
5223 // opening door sound has priority over simultaneously closing door
5224 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5226 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5228 if (door_state & DOOR_OPEN_1)
5229 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5230 if (door_state & DOOR_OPEN_2)
5231 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5233 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5235 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5237 if (door_state & DOOR_CLOSE_1)
5238 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5239 if (door_state & DOOR_CLOSE_2)
5240 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5244 for (k = start; k < num_move_steps; k++)
5246 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5248 door_part_done_all = TRUE;
5250 for (i = 0; i < NUM_DOORS; i++)
5251 door_panel_drawn[i] = FALSE;
5253 for (i = 0; i < MAX_DOOR_PARTS; i++)
5255 int nr = door_part_order[i].nr;
5256 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5257 struct DoorPartPosInfo *pos = dpc->pos;
5258 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5259 int door_token = dpc->door_token;
5260 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5261 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5262 boolean is_panel_and_door_has_closed = FALSE;
5263 struct Rect *door_rect = &door_rect_list[door_index];
5264 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5266 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5267 int current_door_state = door_state & door_token;
5268 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5269 boolean door_closing = !door_opening;
5270 boolean part_opening = (is_panel ? door_closing : door_opening);
5271 boolean part_closing = !part_opening;
5272 int start_step = (part_opening ? pos->start_step_opening :
5273 pos->start_step_closing);
5274 int step_delay = pos->step_delay;
5275 int step_factor = step_delay / max_step_delay;
5276 int k1 = (step_factor ? k / step_factor + 1 : k);
5277 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5278 int kk = MAX(0, k2);
5281 int src_x, src_y, src_xx, src_yy;
5282 int dst_x, dst_y, dst_xx, dst_yy;
5285 if (door_part_skip[nr])
5288 if (!(door_state & door_token))
5296 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5297 int kk_door = MAX(0, k2_door);
5298 int sync_frame = kk_door * door_delay_value;
5299 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5301 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5302 &g_src_x, &g_src_y);
5307 if (!door_panel_drawn[door_index])
5309 ClearRectangle(drawto, door_rect->x, door_rect->y,
5310 door_rect->width, door_rect->height);
5312 door_panel_drawn[door_index] = TRUE;
5315 // draw opening or closing door parts
5317 if (pos->step_xoffset < 0) // door part on right side
5320 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5323 if (dst_xx + width > door_rect->width)
5324 width = door_rect->width - dst_xx;
5326 else // door part on left side
5329 dst_xx = pos->x - kk * pos->step_xoffset;
5333 src_xx = ABS(dst_xx);
5337 width = g->width - src_xx;
5339 if (width > door_rect->width)
5340 width = door_rect->width;
5342 // printf("::: k == %d [%d] \n", k, start_step);
5345 if (pos->step_yoffset < 0) // door part on bottom side
5348 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5351 if (dst_yy + height > door_rect->height)
5352 height = door_rect->height - dst_yy;
5354 else // door part on top side
5357 dst_yy = pos->y - kk * pos->step_yoffset;
5361 src_yy = ABS(dst_yy);
5365 height = g->height - src_yy;
5368 src_x = g_src_x + src_xx;
5369 src_y = g_src_y + src_yy;
5371 dst_x = door_rect->x + dst_xx;
5372 dst_y = door_rect->y + dst_yy;
5374 is_panel_and_door_has_closed =
5377 panel_has_doors[door_index] &&
5378 k >= num_move_steps_doors_only - 1);
5380 if (width >= 0 && width <= g->width &&
5381 height >= 0 && height <= g->height &&
5382 !is_panel_and_door_has_closed)
5384 if (is_panel || !pos->draw_masked)
5385 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5388 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5392 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5394 if ((part_opening && (width < 0 || height < 0)) ||
5395 (part_closing && (width >= g->width && height >= g->height)))
5396 door_part_done[nr] = TRUE;
5398 // continue door part animations, but not panel after door has closed
5399 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5400 door_part_done_all = FALSE;
5403 if (!(door_state & DOOR_NO_DELAY))
5407 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5409 current_move_delay += max_step_delay;
5411 // prevent OS (Windows) from complaining about program not responding
5415 if (door_part_done_all)
5419 if (!(door_state & DOOR_NO_DELAY))
5421 // wait for specified door action post delay
5422 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5423 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5424 else if (door_state & DOOR_ACTION_1)
5425 door_delay_value = door_1.post_delay;
5426 else if (door_state & DOOR_ACTION_2)
5427 door_delay_value = door_2.post_delay;
5429 while (!DelayReached(&door_delay, door_delay_value))
5434 if (door_state & DOOR_ACTION_1)
5435 door1 = door_state & DOOR_ACTION_1;
5436 if (door_state & DOOR_ACTION_2)
5437 door2 = door_state & DOOR_ACTION_2;
5439 // draw masked border over door area
5440 DrawMaskedBorder(REDRAW_DOOR_1);
5441 DrawMaskedBorder(REDRAW_DOOR_2);
5443 ClearAutoRepeatKeyEvents();
5445 return (door1 | door2);
5448 static boolean useSpecialEditorDoor(void)
5450 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5451 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5453 // do not draw special editor door if editor border defined or redefined
5454 if (graphic_info[graphic].bitmap != NULL || redefined)
5457 // do not draw special editor door if global border defined to be empty
5458 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5461 // do not draw special editor door if viewport definitions do not match
5465 EY + EYSIZE != VY + VYSIZE)
5471 void DrawSpecialEditorDoor(void)
5473 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5474 int top_border_width = gfx1->width;
5475 int top_border_height = gfx1->height;
5476 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5477 int ex = EX - outer_border;
5478 int ey = EY - outer_border;
5479 int vy = VY - outer_border;
5480 int exsize = EXSIZE + 2 * outer_border;
5482 if (!useSpecialEditorDoor())
5485 // draw bigger level editor toolbox window
5486 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5487 top_border_width, top_border_height, ex, ey - top_border_height);
5488 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5489 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5491 redraw_mask |= REDRAW_ALL;
5494 void UndrawSpecialEditorDoor(void)
5496 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5497 int top_border_width = gfx1->width;
5498 int top_border_height = gfx1->height;
5499 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5500 int ex = EX - outer_border;
5501 int ey = EY - outer_border;
5502 int ey_top = ey - top_border_height;
5503 int exsize = EXSIZE + 2 * outer_border;
5504 int eysize = EYSIZE + 2 * outer_border;
5506 if (!useSpecialEditorDoor())
5509 // draw normal tape recorder window
5510 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5512 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5513 ex, ey_top, top_border_width, top_border_height,
5515 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5516 ex, ey, exsize, eysize, ex, ey);
5520 // if screen background is set to "[NONE]", clear editor toolbox window
5521 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5522 ClearRectangle(drawto, ex, ey, exsize, eysize);
5525 redraw_mask |= REDRAW_ALL;
5529 // ---------- new tool button stuff -------------------------------------------
5534 struct TextPosInfo *pos;
5536 boolean is_touch_button;
5538 } toolbutton_info[NUM_TOOL_BUTTONS] =
5541 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5542 TOOL_CTRL_ID_YES, FALSE, "yes"
5545 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5546 TOOL_CTRL_ID_NO, FALSE, "no"
5549 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5550 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5553 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5554 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5557 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5558 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5561 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5562 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5565 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5566 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5569 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5570 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5573 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5574 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5577 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5578 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5582 void CreateToolButtons(void)
5586 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5588 int graphic = toolbutton_info[i].graphic;
5589 struct GraphicInfo *gfx = &graphic_info[graphic];
5590 struct TextPosInfo *pos = toolbutton_info[i].pos;
5591 struct GadgetInfo *gi;
5592 Bitmap *deco_bitmap = None;
5593 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5594 unsigned int event_mask = GD_EVENT_RELEASED;
5595 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5596 int base_x = (is_touch_button ? 0 : DX);
5597 int base_y = (is_touch_button ? 0 : DY);
5598 int gd_x = gfx->src_x;
5599 int gd_y = gfx->src_y;
5600 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5601 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5606 if (global.use_envelope_request && !is_touch_button)
5608 setRequestPosition(&base_x, &base_y, TRUE);
5610 // check if request buttons are outside of envelope and fix, if needed
5611 if (x < 0 || x + gfx->width > request.width ||
5612 y < 0 || y + gfx->height > request.height)
5614 if (id == TOOL_CTRL_ID_YES)
5617 y = request.height - 2 * request.border_size - gfx->height;
5619 else if (id == TOOL_CTRL_ID_NO)
5621 x = request.width - 2 * request.border_size - gfx->width;
5622 y = request.height - 2 * request.border_size - gfx->height;
5624 else if (id == TOOL_CTRL_ID_CONFIRM)
5626 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5627 y = request.height - 2 * request.border_size - gfx->height;
5629 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5631 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5633 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5634 y = request.height - 2 * request.border_size - gfx->height * 2;
5636 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5637 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5642 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5644 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5646 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5647 pos->size, &deco_bitmap, &deco_x, &deco_y);
5648 deco_xpos = (gfx->width - pos->size) / 2;
5649 deco_ypos = (gfx->height - pos->size) / 2;
5652 gi = CreateGadget(GDI_CUSTOM_ID, id,
5653 GDI_IMAGE_ID, graphic,
5654 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5657 GDI_WIDTH, gfx->width,
5658 GDI_HEIGHT, gfx->height,
5659 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5660 GDI_STATE, GD_BUTTON_UNPRESSED,
5661 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5662 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5663 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5664 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5665 GDI_DECORATION_SIZE, pos->size, pos->size,
5666 GDI_DECORATION_SHIFTING, 1, 1,
5667 GDI_DIRECT_DRAW, FALSE,
5668 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5669 GDI_EVENT_MASK, event_mask,
5670 GDI_CALLBACK_ACTION, HandleToolButtons,
5674 Error(ERR_EXIT, "cannot create gadget");
5676 tool_gadget[id] = gi;
5680 void FreeToolButtons(void)
5684 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5685 FreeGadget(tool_gadget[i]);
5688 static void UnmapToolButtons(void)
5692 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5693 UnmapGadget(tool_gadget[i]);
5696 static void HandleToolButtons(struct GadgetInfo *gi)
5698 request_gadget_id = gi->custom_id;
5701 static struct Mapping_EM_to_RND_object
5704 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5705 boolean is_backside; // backside of moving element
5711 em_object_mapping_list[GAME_TILE_MAX + 1] =
5714 Zborder, FALSE, FALSE,
5718 Zplayer, FALSE, FALSE,
5727 Ztank, FALSE, FALSE,
5731 Zeater, FALSE, FALSE,
5735 Zdynamite, FALSE, FALSE,
5739 Zboom, FALSE, FALSE,
5744 Xchain, FALSE, FALSE,
5745 EL_DEFAULT, ACTION_EXPLODING, -1
5748 Xboom_bug, FALSE, FALSE,
5749 EL_BUG, ACTION_EXPLODING, -1
5752 Xboom_tank, FALSE, FALSE,
5753 EL_SPACESHIP, ACTION_EXPLODING, -1
5756 Xboom_android, FALSE, FALSE,
5757 EL_EMC_ANDROID, ACTION_OTHER, -1
5760 Xboom_1, FALSE, FALSE,
5761 EL_DEFAULT, ACTION_EXPLODING, -1
5764 Xboom_2, FALSE, FALSE,
5765 EL_DEFAULT, ACTION_EXPLODING, -1
5769 Xblank, TRUE, FALSE,
5774 Xsplash_e, FALSE, FALSE,
5775 EL_ACID_SPLASH_RIGHT, -1, -1
5778 Xsplash_w, FALSE, FALSE,
5779 EL_ACID_SPLASH_LEFT, -1, -1
5783 Xplant, TRUE, FALSE,
5784 EL_EMC_PLANT, -1, -1
5787 Yplant, FALSE, FALSE,
5788 EL_EMC_PLANT, -1, -1
5792 Xacid_1, TRUE, FALSE,
5796 Xacid_2, FALSE, FALSE,
5800 Xacid_3, FALSE, FALSE,
5804 Xacid_4, FALSE, FALSE,
5808 Xacid_5, FALSE, FALSE,
5812 Xacid_6, FALSE, FALSE,
5816 Xacid_7, FALSE, FALSE,
5820 Xacid_8, FALSE, FALSE,
5825 Xfake_acid_1, TRUE, FALSE,
5826 EL_EMC_FAKE_ACID, -1, -1
5829 Xfake_acid_2, FALSE, FALSE,
5830 EL_EMC_FAKE_ACID, -1, -1
5833 Xfake_acid_3, FALSE, FALSE,
5834 EL_EMC_FAKE_ACID, -1, -1
5837 Xfake_acid_4, FALSE, FALSE,
5838 EL_EMC_FAKE_ACID, -1, -1
5841 Xfake_acid_5, FALSE, FALSE,
5842 EL_EMC_FAKE_ACID, -1, -1
5845 Xfake_acid_6, FALSE, FALSE,
5846 EL_EMC_FAKE_ACID, -1, -1
5849 Xfake_acid_7, FALSE, FALSE,
5850 EL_EMC_FAKE_ACID, -1, -1
5853 Xfake_acid_8, FALSE, FALSE,
5854 EL_EMC_FAKE_ACID, -1, -1
5858 Xgrass, TRUE, FALSE,
5859 EL_EMC_GRASS, -1, -1
5862 Ygrass_nB, FALSE, FALSE,
5863 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5866 Ygrass_eB, FALSE, FALSE,
5867 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5870 Ygrass_sB, FALSE, FALSE,
5871 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5874 Ygrass_wB, FALSE, FALSE,
5875 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5883 Ydirt_nB, FALSE, FALSE,
5884 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5887 Ydirt_eB, FALSE, FALSE,
5888 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5891 Ydirt_sB, FALSE, FALSE,
5892 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5895 Ydirt_wB, FALSE, FALSE,
5896 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5900 Xandroid, TRUE, FALSE,
5901 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5904 Xandroid_1_n, FALSE, FALSE,
5905 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5908 Xandroid_2_n, FALSE, FALSE,
5909 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5912 Xandroid_1_e, FALSE, FALSE,
5913 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5916 Xandroid_2_e, FALSE, FALSE,
5917 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5920 Xandroid_1_w, FALSE, FALSE,
5921 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5924 Xandroid_2_w, FALSE, FALSE,
5925 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5928 Xandroid_1_s, FALSE, FALSE,
5929 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5932 Xandroid_2_s, FALSE, FALSE,
5933 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5936 Yandroid_n, FALSE, FALSE,
5937 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5940 Yandroid_nB, FALSE, TRUE,
5941 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5944 Yandroid_ne, FALSE, FALSE,
5945 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5948 Yandroid_neB, FALSE, TRUE,
5949 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5952 Yandroid_e, FALSE, FALSE,
5953 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5956 Yandroid_eB, FALSE, TRUE,
5957 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5960 Yandroid_se, FALSE, FALSE,
5961 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5964 Yandroid_seB, FALSE, TRUE,
5965 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5968 Yandroid_s, FALSE, FALSE,
5969 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5972 Yandroid_sB, FALSE, TRUE,
5973 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5976 Yandroid_sw, FALSE, FALSE,
5977 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5980 Yandroid_swB, FALSE, TRUE,
5981 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5984 Yandroid_w, FALSE, FALSE,
5985 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5988 Yandroid_wB, FALSE, TRUE,
5989 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5992 Yandroid_nw, FALSE, FALSE,
5993 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5996 Yandroid_nwB, FALSE, TRUE,
5997 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6001 Xeater_n, TRUE, FALSE,
6002 EL_YAMYAM_UP, -1, -1
6005 Xeater_e, TRUE, FALSE,
6006 EL_YAMYAM_RIGHT, -1, -1
6009 Xeater_w, TRUE, FALSE,
6010 EL_YAMYAM_LEFT, -1, -1
6013 Xeater_s, TRUE, FALSE,
6014 EL_YAMYAM_DOWN, -1, -1
6017 Yeater_n, FALSE, FALSE,
6018 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6021 Yeater_nB, FALSE, TRUE,
6022 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6025 Yeater_e, FALSE, FALSE,
6026 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6029 Yeater_eB, FALSE, TRUE,
6030 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6033 Yeater_s, FALSE, FALSE,
6034 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6037 Yeater_sB, FALSE, TRUE,
6038 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6041 Yeater_w, FALSE, FALSE,
6042 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6045 Yeater_wB, FALSE, TRUE,
6046 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6049 Yeater_stone, FALSE, FALSE,
6050 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6053 Yeater_spring, FALSE, FALSE,
6054 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6058 Xalien, TRUE, FALSE,
6062 Xalien_pause, FALSE, FALSE,
6066 Yalien_n, FALSE, FALSE,
6067 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6070 Yalien_nB, FALSE, TRUE,
6071 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6074 Yalien_e, FALSE, FALSE,
6075 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6078 Yalien_eB, FALSE, TRUE,
6079 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6082 Yalien_s, FALSE, FALSE,
6083 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6086 Yalien_sB, FALSE, TRUE,
6087 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6090 Yalien_w, FALSE, FALSE,
6091 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6094 Yalien_wB, FALSE, TRUE,
6095 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6098 Yalien_stone, FALSE, FALSE,
6099 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6102 Yalien_spring, FALSE, FALSE,
6103 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6107 Xbug_1_n, TRUE, FALSE,
6111 Xbug_1_e, TRUE, FALSE,
6112 EL_BUG_RIGHT, -1, -1
6115 Xbug_1_s, TRUE, FALSE,
6119 Xbug_1_w, TRUE, FALSE,
6123 Xbug_2_n, FALSE, FALSE,
6127 Xbug_2_e, FALSE, FALSE,
6128 EL_BUG_RIGHT, -1, -1
6131 Xbug_2_s, FALSE, FALSE,
6135 Xbug_2_w, FALSE, FALSE,
6139 Ybug_n, FALSE, FALSE,
6140 EL_BUG, ACTION_MOVING, MV_BIT_UP
6143 Ybug_nB, FALSE, TRUE,
6144 EL_BUG, ACTION_MOVING, MV_BIT_UP
6147 Ybug_e, FALSE, FALSE,
6148 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6151 Ybug_eB, FALSE, TRUE,
6152 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6155 Ybug_s, FALSE, FALSE,
6156 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6159 Ybug_sB, FALSE, TRUE,
6160 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6163 Ybug_w, FALSE, FALSE,
6164 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6167 Ybug_wB, FALSE, TRUE,
6168 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6171 Ybug_w_n, FALSE, FALSE,
6172 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6175 Ybug_n_e, FALSE, FALSE,
6176 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6179 Ybug_e_s, FALSE, FALSE,
6180 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6183 Ybug_s_w, FALSE, FALSE,
6184 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6187 Ybug_e_n, FALSE, FALSE,
6188 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6191 Ybug_s_e, FALSE, FALSE,
6192 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6195 Ybug_w_s, FALSE, FALSE,
6196 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6199 Ybug_n_w, FALSE, FALSE,
6200 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6203 Ybug_stone, FALSE, FALSE,
6204 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6207 Ybug_spring, FALSE, FALSE,
6208 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6212 Xtank_1_n, TRUE, FALSE,
6213 EL_SPACESHIP_UP, -1, -1
6216 Xtank_1_e, TRUE, FALSE,
6217 EL_SPACESHIP_RIGHT, -1, -1
6220 Xtank_1_s, TRUE, FALSE,
6221 EL_SPACESHIP_DOWN, -1, -1
6224 Xtank_1_w, TRUE, FALSE,
6225 EL_SPACESHIP_LEFT, -1, -1
6228 Xtank_2_n, FALSE, FALSE,
6229 EL_SPACESHIP_UP, -1, -1
6232 Xtank_2_e, FALSE, FALSE,
6233 EL_SPACESHIP_RIGHT, -1, -1
6236 Xtank_2_s, FALSE, FALSE,
6237 EL_SPACESHIP_DOWN, -1, -1
6240 Xtank_2_w, FALSE, FALSE,
6241 EL_SPACESHIP_LEFT, -1, -1
6244 Ytank_n, FALSE, FALSE,
6245 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6248 Ytank_nB, FALSE, TRUE,
6249 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6252 Ytank_e, FALSE, FALSE,
6253 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6256 Ytank_eB, FALSE, TRUE,
6257 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6260 Ytank_s, FALSE, FALSE,
6261 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6264 Ytank_sB, FALSE, TRUE,
6265 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6268 Ytank_w, FALSE, FALSE,
6269 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6272 Ytank_wB, FALSE, TRUE,
6273 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6276 Ytank_w_n, FALSE, FALSE,
6277 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6280 Ytank_n_e, FALSE, FALSE,
6281 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6284 Ytank_e_s, FALSE, FALSE,
6285 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6288 Ytank_s_w, FALSE, FALSE,
6289 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6292 Ytank_e_n, FALSE, FALSE,
6293 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6296 Ytank_s_e, FALSE, FALSE,
6297 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6300 Ytank_w_s, FALSE, FALSE,
6301 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6304 Ytank_n_w, FALSE, FALSE,
6305 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6308 Ytank_stone, FALSE, FALSE,
6309 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6312 Ytank_spring, FALSE, FALSE,
6313 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6317 Xemerald, TRUE, FALSE,
6321 Xemerald_pause, FALSE, FALSE,
6325 Xemerald_fall, FALSE, FALSE,
6329 Xemerald_shine, FALSE, FALSE,
6330 EL_EMERALD, ACTION_TWINKLING, -1
6333 Yemerald_s, FALSE, FALSE,
6334 EL_EMERALD, ACTION_FALLING, -1
6337 Yemerald_sB, FALSE, TRUE,
6338 EL_EMERALD, ACTION_FALLING, -1
6341 Yemerald_e, FALSE, FALSE,
6342 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6345 Yemerald_eB, FALSE, TRUE,
6346 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6349 Yemerald_w, FALSE, FALSE,
6350 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6353 Yemerald_wB, FALSE, TRUE,
6354 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6357 Yemerald_blank, FALSE, FALSE,
6358 EL_EMERALD, ACTION_COLLECTING, -1
6362 Xdiamond, TRUE, FALSE,
6366 Xdiamond_pause, FALSE, FALSE,
6370 Xdiamond_fall, FALSE, FALSE,
6374 Xdiamond_shine, FALSE, FALSE,
6375 EL_DIAMOND, ACTION_TWINKLING, -1
6378 Ydiamond_s, FALSE, FALSE,
6379 EL_DIAMOND, ACTION_FALLING, -1
6382 Ydiamond_sB, FALSE, TRUE,
6383 EL_DIAMOND, ACTION_FALLING, -1
6386 Ydiamond_e, FALSE, FALSE,
6387 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6390 Ydiamond_eB, FALSE, TRUE,
6391 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6394 Ydiamond_w, FALSE, FALSE,
6395 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6398 Ydiamond_wB, FALSE, TRUE,
6399 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6402 Ydiamond_blank, FALSE, FALSE,
6403 EL_DIAMOND, ACTION_COLLECTING, -1
6406 Ydiamond_stone, FALSE, FALSE,
6407 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6411 Xstone, TRUE, FALSE,
6415 Xstone_pause, FALSE, FALSE,
6419 Xstone_fall, FALSE, FALSE,
6423 Ystone_s, FALSE, FALSE,
6424 EL_ROCK, ACTION_FALLING, -1
6427 Ystone_sB, FALSE, TRUE,
6428 EL_ROCK, ACTION_FALLING, -1
6431 Ystone_e, FALSE, FALSE,
6432 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6435 Ystone_eB, FALSE, TRUE,
6436 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6439 Ystone_w, FALSE, FALSE,
6440 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6443 Ystone_wB, FALSE, TRUE,
6444 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6452 Xbomb_pause, FALSE, FALSE,
6456 Xbomb_fall, FALSE, FALSE,
6460 Ybomb_s, FALSE, FALSE,
6461 EL_BOMB, ACTION_FALLING, -1
6464 Ybomb_sB, FALSE, TRUE,
6465 EL_BOMB, ACTION_FALLING, -1
6468 Ybomb_e, FALSE, FALSE,
6469 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6472 Ybomb_eB, FALSE, TRUE,
6473 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6476 Ybomb_w, FALSE, FALSE,
6477 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6480 Ybomb_wB, FALSE, TRUE,
6481 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6484 Ybomb_blank, FALSE, FALSE,
6485 EL_BOMB, ACTION_ACTIVATING, -1
6493 Xnut_pause, FALSE, FALSE,
6497 Xnut_fall, FALSE, FALSE,
6501 Ynut_s, FALSE, FALSE,
6502 EL_NUT, ACTION_FALLING, -1
6505 Ynut_sB, FALSE, TRUE,
6506 EL_NUT, ACTION_FALLING, -1
6509 Ynut_e, FALSE, FALSE,
6510 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6513 Ynut_eB, FALSE, TRUE,
6514 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6517 Ynut_w, FALSE, FALSE,
6518 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6521 Ynut_wB, FALSE, TRUE,
6522 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6525 Ynut_stone, FALSE, FALSE,
6526 EL_NUT, ACTION_BREAKING, -1
6530 Xspring, TRUE, FALSE,
6534 Xspring_pause, FALSE, FALSE,
6538 Xspring_e, TRUE, FALSE,
6539 EL_SPRING_RIGHT, -1, -1
6542 Xspring_w, TRUE, FALSE,
6543 EL_SPRING_LEFT, -1, -1
6546 Xspring_fall, FALSE, FALSE,
6550 Yspring_s, FALSE, FALSE,
6551 EL_SPRING, ACTION_FALLING, -1
6554 Yspring_sB, FALSE, TRUE,
6555 EL_SPRING, ACTION_FALLING, -1
6558 Yspring_e, FALSE, FALSE,
6559 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6562 Yspring_eB, FALSE, TRUE,
6563 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6566 Yspring_w, FALSE, FALSE,
6567 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6570 Yspring_wB, FALSE, TRUE,
6571 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6574 Yspring_alien_e, FALSE, FALSE,
6575 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6578 Yspring_alien_eB, FALSE, TRUE,
6579 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6582 Yspring_alien_w, FALSE, FALSE,
6583 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6586 Yspring_alien_wB, FALSE, TRUE,
6587 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6591 Xpush_emerald_e, FALSE, FALSE,
6592 EL_EMERALD, -1, MV_BIT_RIGHT
6595 Xpush_emerald_w, FALSE, FALSE,
6596 EL_EMERALD, -1, MV_BIT_LEFT
6599 Xpush_diamond_e, FALSE, FALSE,
6600 EL_DIAMOND, -1, MV_BIT_RIGHT
6603 Xpush_diamond_w, FALSE, FALSE,
6604 EL_DIAMOND, -1, MV_BIT_LEFT
6607 Xpush_stone_e, FALSE, FALSE,
6608 EL_ROCK, -1, MV_BIT_RIGHT
6611 Xpush_stone_w, FALSE, FALSE,
6612 EL_ROCK, -1, MV_BIT_LEFT
6615 Xpush_bomb_e, FALSE, FALSE,
6616 EL_BOMB, -1, MV_BIT_RIGHT
6619 Xpush_bomb_w, FALSE, FALSE,
6620 EL_BOMB, -1, MV_BIT_LEFT
6623 Xpush_nut_e, FALSE, FALSE,
6624 EL_NUT, -1, MV_BIT_RIGHT
6627 Xpush_nut_w, FALSE, FALSE,
6628 EL_NUT, -1, MV_BIT_LEFT
6631 Xpush_spring_e, FALSE, FALSE,
6632 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6635 Xpush_spring_w, FALSE, FALSE,
6636 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6640 Xdynamite, TRUE, FALSE,
6641 EL_EM_DYNAMITE, -1, -1
6644 Ydynamite_blank, FALSE, FALSE,
6645 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6648 Xdynamite_1, TRUE, FALSE,
6649 EL_EM_DYNAMITE_ACTIVE, -1, -1
6652 Xdynamite_2, FALSE, FALSE,
6653 EL_EM_DYNAMITE_ACTIVE, -1, -1
6656 Xdynamite_3, FALSE, FALSE,
6657 EL_EM_DYNAMITE_ACTIVE, -1, -1
6660 Xdynamite_4, FALSE, FALSE,
6661 EL_EM_DYNAMITE_ACTIVE, -1, -1
6665 Xkey_1, TRUE, FALSE,
6669 Xkey_2, TRUE, FALSE,
6673 Xkey_3, TRUE, FALSE,
6677 Xkey_4, TRUE, FALSE,
6681 Xkey_5, TRUE, FALSE,
6682 EL_EMC_KEY_5, -1, -1
6685 Xkey_6, TRUE, FALSE,
6686 EL_EMC_KEY_6, -1, -1
6689 Xkey_7, TRUE, FALSE,
6690 EL_EMC_KEY_7, -1, -1
6693 Xkey_8, TRUE, FALSE,
6694 EL_EMC_KEY_8, -1, -1
6698 Xdoor_1, TRUE, FALSE,
6699 EL_EM_GATE_1, -1, -1
6702 Xdoor_2, TRUE, FALSE,
6703 EL_EM_GATE_2, -1, -1
6706 Xdoor_3, TRUE, FALSE,
6707 EL_EM_GATE_3, -1, -1
6710 Xdoor_4, TRUE, FALSE,
6711 EL_EM_GATE_4, -1, -1
6714 Xdoor_5, TRUE, FALSE,
6715 EL_EMC_GATE_5, -1, -1
6718 Xdoor_6, TRUE, FALSE,
6719 EL_EMC_GATE_6, -1, -1
6722 Xdoor_7, TRUE, FALSE,
6723 EL_EMC_GATE_7, -1, -1
6726 Xdoor_8, TRUE, FALSE,
6727 EL_EMC_GATE_8, -1, -1
6731 Xfake_door_1, TRUE, FALSE,
6732 EL_EM_GATE_1_GRAY, -1, -1
6735 Xfake_door_2, TRUE, FALSE,
6736 EL_EM_GATE_2_GRAY, -1, -1
6739 Xfake_door_3, TRUE, FALSE,
6740 EL_EM_GATE_3_GRAY, -1, -1
6743 Xfake_door_4, TRUE, FALSE,
6744 EL_EM_GATE_4_GRAY, -1, -1
6747 Xfake_door_5, TRUE, FALSE,
6748 EL_EMC_GATE_5_GRAY, -1, -1
6751 Xfake_door_6, TRUE, FALSE,
6752 EL_EMC_GATE_6_GRAY, -1, -1
6755 Xfake_door_7, TRUE, FALSE,
6756 EL_EMC_GATE_7_GRAY, -1, -1
6759 Xfake_door_8, TRUE, FALSE,
6760 EL_EMC_GATE_8_GRAY, -1, -1
6764 Xballoon, TRUE, FALSE,
6768 Yballoon_n, FALSE, FALSE,
6769 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6772 Yballoon_nB, FALSE, TRUE,
6773 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6776 Yballoon_e, FALSE, FALSE,
6777 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6780 Yballoon_eB, FALSE, TRUE,
6781 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6784 Yballoon_s, FALSE, FALSE,
6785 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6788 Yballoon_sB, FALSE, TRUE,
6789 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6792 Yballoon_w, FALSE, FALSE,
6793 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6796 Yballoon_wB, FALSE, TRUE,
6797 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6801 Xball_1, TRUE, FALSE,
6802 EL_EMC_MAGIC_BALL, -1, -1
6805 Yball_1, FALSE, FALSE,
6806 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6809 Xball_2, FALSE, FALSE,
6810 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6813 Yball_2, FALSE, FALSE,
6814 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6817 Yball_blank, FALSE, FALSE,
6818 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6822 Xamoeba_1, TRUE, FALSE,
6823 EL_AMOEBA_DRY, ACTION_OTHER, -1
6826 Xamoeba_2, FALSE, FALSE,
6827 EL_AMOEBA_DRY, ACTION_OTHER, -1
6830 Xamoeba_3, FALSE, FALSE,
6831 EL_AMOEBA_DRY, ACTION_OTHER, -1
6834 Xamoeba_4, FALSE, FALSE,
6835 EL_AMOEBA_DRY, ACTION_OTHER, -1
6838 Xamoeba_5, TRUE, FALSE,
6839 EL_AMOEBA_WET, ACTION_OTHER, -1
6842 Xamoeba_6, FALSE, FALSE,
6843 EL_AMOEBA_WET, ACTION_OTHER, -1
6846 Xamoeba_7, FALSE, FALSE,
6847 EL_AMOEBA_WET, ACTION_OTHER, -1
6850 Xamoeba_8, FALSE, FALSE,
6851 EL_AMOEBA_WET, ACTION_OTHER, -1
6856 EL_AMOEBA_DROP, ACTION_GROWING, -1
6859 Xdrip_fall, FALSE, FALSE,
6860 EL_AMOEBA_DROP, -1, -1
6863 Xdrip_stretch, FALSE, FALSE,
6864 EL_AMOEBA_DROP, ACTION_FALLING, -1
6867 Xdrip_stretchB, FALSE, TRUE,
6868 EL_AMOEBA_DROP, ACTION_FALLING, -1
6871 Ydrip_1_s, FALSE, FALSE,
6872 EL_AMOEBA_DROP, ACTION_FALLING, -1
6875 Ydrip_1_sB, FALSE, TRUE,
6876 EL_AMOEBA_DROP, ACTION_FALLING, -1
6879 Ydrip_2_s, FALSE, FALSE,
6880 EL_AMOEBA_DROP, ACTION_FALLING, -1
6883 Ydrip_2_sB, FALSE, TRUE,
6884 EL_AMOEBA_DROP, ACTION_FALLING, -1
6888 Xwonderwall, TRUE, FALSE,
6889 EL_MAGIC_WALL, -1, -1
6892 Ywonderwall, FALSE, FALSE,
6893 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6897 Xwheel, TRUE, FALSE,
6898 EL_ROBOT_WHEEL, -1, -1
6901 Ywheel, FALSE, FALSE,
6902 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6906 Xswitch, TRUE, FALSE,
6907 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6910 Yswitch, FALSE, FALSE,
6911 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6915 Xbumper, TRUE, FALSE,
6916 EL_EMC_SPRING_BUMPER, -1, -1
6919 Ybumper, FALSE, FALSE,
6920 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6924 Xacid_nw, TRUE, FALSE,
6925 EL_ACID_POOL_TOPLEFT, -1, -1
6928 Xacid_ne, TRUE, FALSE,
6929 EL_ACID_POOL_TOPRIGHT, -1, -1
6932 Xacid_sw, TRUE, FALSE,
6933 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6936 Xacid_s, TRUE, FALSE,
6937 EL_ACID_POOL_BOTTOM, -1, -1
6940 Xacid_se, TRUE, FALSE,
6941 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6945 Xfake_blank, TRUE, FALSE,
6946 EL_INVISIBLE_WALL, -1, -1
6949 Yfake_blank, FALSE, FALSE,
6950 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6954 Xfake_grass, TRUE, FALSE,
6955 EL_EMC_FAKE_GRASS, -1, -1
6958 Yfake_grass, FALSE, FALSE,
6959 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6963 Xfake_amoeba, TRUE, FALSE,
6964 EL_EMC_DRIPPER, -1, -1
6967 Yfake_amoeba, FALSE, FALSE,
6968 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6972 Xlenses, TRUE, FALSE,
6973 EL_EMC_LENSES, -1, -1
6977 Xmagnify, TRUE, FALSE,
6978 EL_EMC_MAGNIFIER, -1, -1
6983 EL_QUICKSAND_EMPTY, -1, -1
6986 Xsand_stone, TRUE, FALSE,
6987 EL_QUICKSAND_FULL, -1, -1
6990 Xsand_stonein_1, FALSE, TRUE,
6991 EL_ROCK, ACTION_FILLING, -1
6994 Xsand_stonein_2, FALSE, TRUE,
6995 EL_ROCK, ACTION_FILLING, -1
6998 Xsand_stonein_3, FALSE, TRUE,
6999 EL_ROCK, ACTION_FILLING, -1
7002 Xsand_stonein_4, FALSE, TRUE,
7003 EL_ROCK, ACTION_FILLING, -1
7006 Xsand_sandstone_1, FALSE, FALSE,
7007 EL_QUICKSAND_FILLING, -1, -1
7010 Xsand_sandstone_2, FALSE, FALSE,
7011 EL_QUICKSAND_FILLING, -1, -1
7014 Xsand_sandstone_3, FALSE, FALSE,
7015 EL_QUICKSAND_FILLING, -1, -1
7018 Xsand_sandstone_4, FALSE, FALSE,
7019 EL_QUICKSAND_FILLING, -1, -1
7022 Xsand_stonesand_1, FALSE, FALSE,
7023 EL_QUICKSAND_EMPTYING, -1, -1
7026 Xsand_stonesand_2, FALSE, FALSE,
7027 EL_QUICKSAND_EMPTYING, -1, -1
7030 Xsand_stonesand_3, FALSE, FALSE,
7031 EL_QUICKSAND_EMPTYING, -1, -1
7034 Xsand_stonesand_4, FALSE, FALSE,
7035 EL_QUICKSAND_EMPTYING, -1, -1
7038 Xsand_stoneout_1, FALSE, FALSE,
7039 EL_ROCK, ACTION_EMPTYING, -1
7042 Xsand_stoneout_2, FALSE, FALSE,
7043 EL_ROCK, ACTION_EMPTYING, -1
7046 Xsand_stonesand_quickout_1, FALSE, FALSE,
7047 EL_QUICKSAND_EMPTYING, -1, -1
7050 Xsand_stonesand_quickout_2, FALSE, FALSE,
7051 EL_QUICKSAND_EMPTYING, -1, -1
7055 Xslide_ns, TRUE, FALSE,
7056 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7059 Yslide_ns_blank, FALSE, FALSE,
7060 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7063 Xslide_ew, TRUE, FALSE,
7064 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7067 Yslide_ew_blank, FALSE, FALSE,
7068 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7072 Xwind_n, TRUE, FALSE,
7073 EL_BALLOON_SWITCH_UP, -1, -1
7076 Xwind_e, TRUE, FALSE,
7077 EL_BALLOON_SWITCH_RIGHT, -1, -1
7080 Xwind_s, TRUE, FALSE,
7081 EL_BALLOON_SWITCH_DOWN, -1, -1
7084 Xwind_w, TRUE, FALSE,
7085 EL_BALLOON_SWITCH_LEFT, -1, -1
7088 Xwind_any, TRUE, FALSE,
7089 EL_BALLOON_SWITCH_ANY, -1, -1
7092 Xwind_stop, TRUE, FALSE,
7093 EL_BALLOON_SWITCH_NONE, -1, -1
7098 EL_EM_EXIT_CLOSED, -1, -1
7101 Xexit_1, TRUE, FALSE,
7102 EL_EM_EXIT_OPEN, -1, -1
7105 Xexit_2, FALSE, FALSE,
7106 EL_EM_EXIT_OPEN, -1, -1
7109 Xexit_3, FALSE, FALSE,
7110 EL_EM_EXIT_OPEN, -1, -1
7114 Xpause, FALSE, FALSE,
7119 Xwall_1, TRUE, FALSE,
7123 Xwall_2, TRUE, FALSE,
7124 EL_EMC_WALL_14, -1, -1
7127 Xwall_3, TRUE, FALSE,
7128 EL_EMC_WALL_15, -1, -1
7131 Xwall_4, TRUE, FALSE,
7132 EL_EMC_WALL_16, -1, -1
7136 Xroundwall_1, TRUE, FALSE,
7137 EL_WALL_SLIPPERY, -1, -1
7140 Xroundwall_2, TRUE, FALSE,
7141 EL_EMC_WALL_SLIPPERY_2, -1, -1
7144 Xroundwall_3, TRUE, FALSE,
7145 EL_EMC_WALL_SLIPPERY_3, -1, -1
7148 Xroundwall_4, TRUE, FALSE,
7149 EL_EMC_WALL_SLIPPERY_4, -1, -1
7153 Xsteel_1, TRUE, FALSE,
7154 EL_STEELWALL, -1, -1
7157 Xsteel_2, TRUE, FALSE,
7158 EL_EMC_STEELWALL_2, -1, -1
7161 Xsteel_3, TRUE, FALSE,
7162 EL_EMC_STEELWALL_3, -1, -1
7165 Xsteel_4, TRUE, FALSE,
7166 EL_EMC_STEELWALL_4, -1, -1
7170 Xdecor_1, TRUE, FALSE,
7171 EL_EMC_WALL_8, -1, -1
7174 Xdecor_2, TRUE, FALSE,
7175 EL_EMC_WALL_6, -1, -1
7178 Xdecor_3, TRUE, FALSE,
7179 EL_EMC_WALL_4, -1, -1
7182 Xdecor_4, TRUE, FALSE,
7183 EL_EMC_WALL_7, -1, -1
7186 Xdecor_5, TRUE, FALSE,
7187 EL_EMC_WALL_5, -1, -1
7190 Xdecor_6, TRUE, FALSE,
7191 EL_EMC_WALL_9, -1, -1
7194 Xdecor_7, TRUE, FALSE,
7195 EL_EMC_WALL_10, -1, -1
7198 Xdecor_8, TRUE, FALSE,
7199 EL_EMC_WALL_1, -1, -1
7202 Xdecor_9, TRUE, FALSE,
7203 EL_EMC_WALL_2, -1, -1
7206 Xdecor_10, TRUE, FALSE,
7207 EL_EMC_WALL_3, -1, -1
7210 Xdecor_11, TRUE, FALSE,
7211 EL_EMC_WALL_11, -1, -1
7214 Xdecor_12, TRUE, FALSE,
7215 EL_EMC_WALL_12, -1, -1
7219 Xalpha_0, TRUE, FALSE,
7220 EL_CHAR('0'), -1, -1
7223 Xalpha_1, TRUE, FALSE,
7224 EL_CHAR('1'), -1, -1
7227 Xalpha_2, TRUE, FALSE,
7228 EL_CHAR('2'), -1, -1
7231 Xalpha_3, TRUE, FALSE,
7232 EL_CHAR('3'), -1, -1
7235 Xalpha_4, TRUE, FALSE,
7236 EL_CHAR('4'), -1, -1
7239 Xalpha_5, TRUE, FALSE,
7240 EL_CHAR('5'), -1, -1
7243 Xalpha_6, TRUE, FALSE,
7244 EL_CHAR('6'), -1, -1
7247 Xalpha_7, TRUE, FALSE,
7248 EL_CHAR('7'), -1, -1
7251 Xalpha_8, TRUE, FALSE,
7252 EL_CHAR('8'), -1, -1
7255 Xalpha_9, TRUE, FALSE,
7256 EL_CHAR('9'), -1, -1
7259 Xalpha_excla, TRUE, FALSE,
7260 EL_CHAR('!'), -1, -1
7263 Xalpha_apost, TRUE, FALSE,
7264 EL_CHAR('\''), -1, -1
7267 Xalpha_comma, TRUE, FALSE,
7268 EL_CHAR(','), -1, -1
7271 Xalpha_minus, TRUE, FALSE,
7272 EL_CHAR('-'), -1, -1
7275 Xalpha_perio, TRUE, FALSE,
7276 EL_CHAR('.'), -1, -1
7279 Xalpha_colon, TRUE, FALSE,
7280 EL_CHAR(':'), -1, -1
7283 Xalpha_quest, TRUE, FALSE,
7284 EL_CHAR('?'), -1, -1
7287 Xalpha_a, TRUE, FALSE,
7288 EL_CHAR('A'), -1, -1
7291 Xalpha_b, TRUE, FALSE,
7292 EL_CHAR('B'), -1, -1
7295 Xalpha_c, TRUE, FALSE,
7296 EL_CHAR('C'), -1, -1
7299 Xalpha_d, TRUE, FALSE,
7300 EL_CHAR('D'), -1, -1
7303 Xalpha_e, TRUE, FALSE,
7304 EL_CHAR('E'), -1, -1
7307 Xalpha_f, TRUE, FALSE,
7308 EL_CHAR('F'), -1, -1
7311 Xalpha_g, TRUE, FALSE,
7312 EL_CHAR('G'), -1, -1
7315 Xalpha_h, TRUE, FALSE,
7316 EL_CHAR('H'), -1, -1
7319 Xalpha_i, TRUE, FALSE,
7320 EL_CHAR('I'), -1, -1
7323 Xalpha_j, TRUE, FALSE,
7324 EL_CHAR('J'), -1, -1
7327 Xalpha_k, TRUE, FALSE,
7328 EL_CHAR('K'), -1, -1
7331 Xalpha_l, TRUE, FALSE,
7332 EL_CHAR('L'), -1, -1
7335 Xalpha_m, TRUE, FALSE,
7336 EL_CHAR('M'), -1, -1
7339 Xalpha_n, TRUE, FALSE,
7340 EL_CHAR('N'), -1, -1
7343 Xalpha_o, TRUE, FALSE,
7344 EL_CHAR('O'), -1, -1
7347 Xalpha_p, TRUE, FALSE,
7348 EL_CHAR('P'), -1, -1
7351 Xalpha_q, TRUE, FALSE,
7352 EL_CHAR('Q'), -1, -1
7355 Xalpha_r, TRUE, FALSE,
7356 EL_CHAR('R'), -1, -1
7359 Xalpha_s, TRUE, FALSE,
7360 EL_CHAR('S'), -1, -1
7363 Xalpha_t, TRUE, FALSE,
7364 EL_CHAR('T'), -1, -1
7367 Xalpha_u, TRUE, FALSE,
7368 EL_CHAR('U'), -1, -1
7371 Xalpha_v, TRUE, FALSE,
7372 EL_CHAR('V'), -1, -1
7375 Xalpha_w, TRUE, FALSE,
7376 EL_CHAR('W'), -1, -1
7379 Xalpha_x, TRUE, FALSE,
7380 EL_CHAR('X'), -1, -1
7383 Xalpha_y, TRUE, FALSE,
7384 EL_CHAR('Y'), -1, -1
7387 Xalpha_z, TRUE, FALSE,
7388 EL_CHAR('Z'), -1, -1
7391 Xalpha_arrow_e, TRUE, FALSE,
7392 EL_CHAR('>'), -1, -1
7395 Xalpha_arrow_w, TRUE, FALSE,
7396 EL_CHAR('<'), -1, -1
7399 Xalpha_copyr, TRUE, FALSE,
7400 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7404 Ykey_1_blank, FALSE, FALSE,
7405 EL_EM_KEY_1, ACTION_COLLECTING, -1
7408 Ykey_2_blank, FALSE, FALSE,
7409 EL_EM_KEY_2, ACTION_COLLECTING, -1
7412 Ykey_3_blank, FALSE, FALSE,
7413 EL_EM_KEY_3, ACTION_COLLECTING, -1
7416 Ykey_4_blank, FALSE, FALSE,
7417 EL_EM_KEY_4, ACTION_COLLECTING, -1
7420 Ykey_5_blank, FALSE, FALSE,
7421 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7424 Ykey_6_blank, FALSE, FALSE,
7425 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7428 Ykey_7_blank, FALSE, FALSE,
7429 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7432 Ykey_8_blank, FALSE, FALSE,
7433 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7436 Ylenses_blank, FALSE, FALSE,
7437 EL_EMC_LENSES, ACTION_COLLECTING, -1
7440 Ymagnify_blank, FALSE, FALSE,
7441 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7444 Ygrass_blank, FALSE, FALSE,
7445 EL_EMC_GRASS, ACTION_SNAPPING, -1
7448 Ydirt_blank, FALSE, FALSE,
7449 EL_SAND, ACTION_SNAPPING, -1
7458 static struct Mapping_EM_to_RND_player
7467 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7471 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7475 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7479 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7483 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7487 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7491 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7495 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7499 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7503 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7507 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7511 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7515 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7519 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7523 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7527 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7531 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7535 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7539 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7543 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7547 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7551 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7555 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7559 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7563 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7567 EL_PLAYER_1, ACTION_DEFAULT, -1,
7571 EL_PLAYER_2, ACTION_DEFAULT, -1,
7575 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7579 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7583 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7587 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7591 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7595 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7599 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7603 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7607 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7611 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7615 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7619 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7623 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7627 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7631 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7635 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7639 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7643 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7647 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7651 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7655 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7659 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7663 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7667 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7671 EL_PLAYER_3, ACTION_DEFAULT, -1,
7675 EL_PLAYER_4, ACTION_DEFAULT, -1,
7684 int map_element_RND_to_EM_cave(int element_rnd)
7686 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7687 static boolean mapping_initialized = FALSE;
7689 if (!mapping_initialized)
7693 // return "Xalpha_quest" for all undefined elements in mapping array
7694 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7695 mapping_RND_to_EM[i] = Xalpha_quest;
7697 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7698 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7699 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7700 em_object_mapping_list[i].element_em;
7702 mapping_initialized = TRUE;
7705 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7707 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7712 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7715 int map_element_EM_to_RND_cave(int element_em_cave)
7717 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7718 static boolean mapping_initialized = FALSE;
7720 if (!mapping_initialized)
7724 // return "EL_UNKNOWN" for all undefined elements in mapping array
7725 for (i = 0; i < GAME_TILE_MAX; i++)
7726 mapping_EM_to_RND[i] = EL_UNKNOWN;
7728 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7729 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7730 em_object_mapping_list[i].element_rnd;
7732 mapping_initialized = TRUE;
7735 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7737 Error(ERR_WARN, "invalid EM cave element %d", element_em_cave);
7742 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7745 int map_element_EM_to_RND_game(int element_em_game)
7747 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7748 static boolean mapping_initialized = FALSE;
7750 if (!mapping_initialized)
7754 // return "EL_UNKNOWN" for all undefined elements in mapping array
7755 for (i = 0; i < GAME_TILE_MAX; i++)
7756 mapping_EM_to_RND[i] = EL_UNKNOWN;
7758 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7759 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7760 em_object_mapping_list[i].element_rnd;
7762 mapping_initialized = TRUE;
7765 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7767 Error(ERR_WARN, "invalid EM game element %d", element_em_game);
7772 return mapping_EM_to_RND[element_em_game];
7775 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7777 struct LevelInfo_EM *level_em = level->native_em_level;
7778 struct CAVE *cav = level_em->cav;
7781 for (i = 0; i < GAME_TILE_MAX; i++)
7782 cav->android_array[i] = Cblank;
7784 for (i = 0; i < level->num_android_clone_elements; i++)
7786 int element_rnd = level->android_clone_element[i];
7787 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7789 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7790 if (em_object_mapping_list[j].element_rnd == element_rnd)
7791 cav->android_array[em_object_mapping_list[j].element_em] =
7796 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7798 struct LevelInfo_EM *level_em = level->native_em_level;
7799 struct CAVE *cav = level_em->cav;
7802 level->num_android_clone_elements = 0;
7804 for (i = 0; i < GAME_TILE_MAX; i++)
7806 int element_em_cave = cav->android_array[i];
7808 boolean element_found = FALSE;
7810 if (element_em_cave == Cblank)
7813 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7815 for (j = 0; j < level->num_android_clone_elements; j++)
7816 if (level->android_clone_element[j] == element_rnd)
7817 element_found = TRUE;
7821 level->android_clone_element[level->num_android_clone_elements++] =
7824 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7829 if (level->num_android_clone_elements == 0)
7831 level->num_android_clone_elements = 1;
7832 level->android_clone_element[0] = EL_EMPTY;
7836 int map_direction_RND_to_EM(int direction)
7838 return (direction == MV_UP ? 0 :
7839 direction == MV_RIGHT ? 1 :
7840 direction == MV_DOWN ? 2 :
7841 direction == MV_LEFT ? 3 :
7845 int map_direction_EM_to_RND(int direction)
7847 return (direction == 0 ? MV_UP :
7848 direction == 1 ? MV_RIGHT :
7849 direction == 2 ? MV_DOWN :
7850 direction == 3 ? MV_LEFT :
7854 int map_element_RND_to_SP(int element_rnd)
7856 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7858 if (element_rnd >= EL_SP_START &&
7859 element_rnd <= EL_SP_END)
7860 element_sp = element_rnd - EL_SP_START;
7861 else if (element_rnd == EL_EMPTY_SPACE)
7863 else if (element_rnd == EL_INVISIBLE_WALL)
7869 int map_element_SP_to_RND(int element_sp)
7871 int element_rnd = EL_UNKNOWN;
7873 if (element_sp >= 0x00 &&
7875 element_rnd = EL_SP_START + element_sp;
7876 else if (element_sp == 0x28)
7877 element_rnd = EL_INVISIBLE_WALL;
7882 int map_action_SP_to_RND(int action_sp)
7886 case actActive: return ACTION_ACTIVE;
7887 case actImpact: return ACTION_IMPACT;
7888 case actExploding: return ACTION_EXPLODING;
7889 case actDigging: return ACTION_DIGGING;
7890 case actSnapping: return ACTION_SNAPPING;
7891 case actCollecting: return ACTION_COLLECTING;
7892 case actPassing: return ACTION_PASSING;
7893 case actPushing: return ACTION_PUSHING;
7894 case actDropping: return ACTION_DROPPING;
7896 default: return ACTION_DEFAULT;
7900 int map_element_RND_to_MM(int element_rnd)
7902 return (element_rnd >= EL_MM_START_1 &&
7903 element_rnd <= EL_MM_END_1 ?
7904 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7906 element_rnd >= EL_MM_START_2 &&
7907 element_rnd <= EL_MM_END_2 ?
7908 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7910 element_rnd >= EL_CHAR_START &&
7911 element_rnd <= EL_CHAR_END ?
7912 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7914 element_rnd >= EL_MM_RUNTIME_START &&
7915 element_rnd <= EL_MM_RUNTIME_END ?
7916 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7918 element_rnd >= EL_MM_DUMMY_START &&
7919 element_rnd <= EL_MM_DUMMY_END ?
7920 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7922 EL_MM_EMPTY_NATIVE);
7925 int map_element_MM_to_RND(int element_mm)
7927 return (element_mm == EL_MM_EMPTY_NATIVE ||
7928 element_mm == EL_DF_EMPTY_NATIVE ?
7931 element_mm >= EL_MM_START_1_NATIVE &&
7932 element_mm <= EL_MM_END_1_NATIVE ?
7933 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7935 element_mm >= EL_MM_START_2_NATIVE &&
7936 element_mm <= EL_MM_END_2_NATIVE ?
7937 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7939 element_mm >= EL_MM_CHAR_START_NATIVE &&
7940 element_mm <= EL_MM_CHAR_END_NATIVE ?
7941 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7943 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7944 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7945 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7947 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7948 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7949 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7954 int map_action_MM_to_RND(int action_mm)
7956 // all MM actions are defined to exactly match their RND counterparts
7960 int map_sound_MM_to_RND(int sound_mm)
7964 case SND_MM_GAME_LEVELTIME_CHARGING:
7965 return SND_GAME_LEVELTIME_CHARGING;
7967 case SND_MM_GAME_HEALTH_CHARGING:
7968 return SND_GAME_HEALTH_CHARGING;
7971 return SND_UNDEFINED;
7975 int map_mm_wall_element(int element)
7977 return (element >= EL_MM_STEEL_WALL_START &&
7978 element <= EL_MM_STEEL_WALL_END ?
7981 element >= EL_MM_WOODEN_WALL_START &&
7982 element <= EL_MM_WOODEN_WALL_END ?
7985 element >= EL_MM_ICE_WALL_START &&
7986 element <= EL_MM_ICE_WALL_END ?
7989 element >= EL_MM_AMOEBA_WALL_START &&
7990 element <= EL_MM_AMOEBA_WALL_END ?
7993 element >= EL_DF_STEEL_WALL_START &&
7994 element <= EL_DF_STEEL_WALL_END ?
7997 element >= EL_DF_WOODEN_WALL_START &&
7998 element <= EL_DF_WOODEN_WALL_END ?
8004 int map_mm_wall_element_editor(int element)
8008 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8009 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8010 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8011 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8012 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8013 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8015 default: return element;
8019 int get_next_element(int element)
8023 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8024 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8025 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8026 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8027 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8028 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8029 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8030 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8031 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8032 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8033 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8035 default: return element;
8039 int el2img_mm(int element_mm)
8041 return el2img(map_element_MM_to_RND(element_mm));
8044 int el_act_dir2img(int element, int action, int direction)
8046 element = GFX_ELEMENT(element);
8047 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8049 // direction_graphic[][] == graphic[] for undefined direction graphics
8050 return element_info[element].direction_graphic[action][direction];
8053 static int el_act_dir2crm(int element, int action, int direction)
8055 element = GFX_ELEMENT(element);
8056 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8058 // direction_graphic[][] == graphic[] for undefined direction graphics
8059 return element_info[element].direction_crumbled[action][direction];
8062 int el_act2img(int element, int action)
8064 element = GFX_ELEMENT(element);
8066 return element_info[element].graphic[action];
8069 int el_act2crm(int element, int action)
8071 element = GFX_ELEMENT(element);
8073 return element_info[element].crumbled[action];
8076 int el_dir2img(int element, int direction)
8078 element = GFX_ELEMENT(element);
8080 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8083 int el2baseimg(int element)
8085 return element_info[element].graphic[ACTION_DEFAULT];
8088 int el2img(int element)
8090 element = GFX_ELEMENT(element);
8092 return element_info[element].graphic[ACTION_DEFAULT];
8095 int el2edimg(int element)
8097 element = GFX_ELEMENT(element);
8099 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8102 int el2preimg(int element)
8104 element = GFX_ELEMENT(element);
8106 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8109 int el2panelimg(int element)
8111 element = GFX_ELEMENT(element);
8113 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8116 int font2baseimg(int font_nr)
8118 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8121 int getBeltNrFromBeltElement(int element)
8123 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8124 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8125 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8128 int getBeltNrFromBeltActiveElement(int element)
8130 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8131 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8132 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8135 int getBeltNrFromBeltSwitchElement(int element)
8137 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8138 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8139 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8142 int getBeltDirNrFromBeltElement(int element)
8144 static int belt_base_element[4] =
8146 EL_CONVEYOR_BELT_1_LEFT,
8147 EL_CONVEYOR_BELT_2_LEFT,
8148 EL_CONVEYOR_BELT_3_LEFT,
8149 EL_CONVEYOR_BELT_4_LEFT
8152 int belt_nr = getBeltNrFromBeltElement(element);
8153 int belt_dir_nr = element - belt_base_element[belt_nr];
8155 return (belt_dir_nr % 3);
8158 int getBeltDirNrFromBeltSwitchElement(int element)
8160 static int belt_base_element[4] =
8162 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8163 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8164 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8165 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8168 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8169 int belt_dir_nr = element - belt_base_element[belt_nr];
8171 return (belt_dir_nr % 3);
8174 int getBeltDirFromBeltElement(int element)
8176 static int belt_move_dir[3] =
8183 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8185 return belt_move_dir[belt_dir_nr];
8188 int getBeltDirFromBeltSwitchElement(int element)
8190 static int belt_move_dir[3] =
8197 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8199 return belt_move_dir[belt_dir_nr];
8202 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8204 static int belt_base_element[4] =
8206 EL_CONVEYOR_BELT_1_LEFT,
8207 EL_CONVEYOR_BELT_2_LEFT,
8208 EL_CONVEYOR_BELT_3_LEFT,
8209 EL_CONVEYOR_BELT_4_LEFT
8212 return belt_base_element[belt_nr] + belt_dir_nr;
8215 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8217 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8219 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8222 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8224 static int belt_base_element[4] =
8226 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8227 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8228 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8229 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8232 return belt_base_element[belt_nr] + belt_dir_nr;
8235 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8237 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8239 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8242 boolean swapTiles_EM(boolean is_pre_emc_cave)
8244 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8247 boolean getTeamMode_EM(void)
8249 return game.team_mode || network_playing;
8252 boolean isActivePlayer_EM(int player_nr)
8254 return stored_player[player_nr].active;
8257 unsigned int InitRND(int seed)
8259 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8260 return InitEngineRandom_EM(seed);
8261 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8262 return InitEngineRandom_SP(seed);
8263 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8264 return InitEngineRandom_MM(seed);
8266 return InitEngineRandom_RND(seed);
8269 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8270 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8272 static int get_effective_element_EM(int tile, int frame_em)
8274 int element = object_mapping[tile].element_rnd;
8275 int action = object_mapping[tile].action;
8276 boolean is_backside = object_mapping[tile].is_backside;
8277 boolean action_removing = (action == ACTION_DIGGING ||
8278 action == ACTION_SNAPPING ||
8279 action == ACTION_COLLECTING);
8287 return (frame_em > 5 ? EL_EMPTY : element);
8293 else // frame_em == 7
8304 case Ydiamond_stone:
8308 case Xdrip_stretchB:
8324 case Ymagnify_blank:
8327 case Xsand_stonein_1:
8328 case Xsand_stonein_2:
8329 case Xsand_stonein_3:
8330 case Xsand_stonein_4:
8334 return (is_backside || action_removing ? EL_EMPTY : element);
8339 static boolean check_linear_animation_EM(int tile)
8343 case Xsand_stonesand_1:
8344 case Xsand_stonesand_quickout_1:
8345 case Xsand_sandstone_1:
8346 case Xsand_stonein_1:
8347 case Xsand_stoneout_1:
8375 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8376 boolean has_crumbled_graphics,
8377 int crumbled, int sync_frame)
8379 // if element can be crumbled, but certain action graphics are just empty
8380 // space (like instantly snapping sand to empty space in 1 frame), do not
8381 // treat these empty space graphics as crumbled graphics in EMC engine
8382 if (crumbled == IMG_EMPTY_SPACE)
8383 has_crumbled_graphics = FALSE;
8385 if (has_crumbled_graphics)
8387 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8388 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8389 g_crumbled->anim_delay,
8390 g_crumbled->anim_mode,
8391 g_crumbled->anim_start_frame,
8394 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8395 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8397 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8398 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8400 g_em->has_crumbled_graphics = TRUE;
8404 g_em->crumbled_bitmap = NULL;
8405 g_em->crumbled_src_x = 0;
8406 g_em->crumbled_src_y = 0;
8407 g_em->crumbled_border_size = 0;
8408 g_em->crumbled_tile_size = 0;
8410 g_em->has_crumbled_graphics = FALSE;
8415 void ResetGfxAnimation_EM(int x, int y, int tile)
8421 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8422 int tile, int frame_em, int x, int y)
8424 int action = object_mapping[tile].action;
8425 int direction = object_mapping[tile].direction;
8426 int effective_element = get_effective_element_EM(tile, frame_em);
8427 int graphic = (direction == MV_NONE ?
8428 el_act2img(effective_element, action) :
8429 el_act_dir2img(effective_element, action, direction));
8430 struct GraphicInfo *g = &graphic_info[graphic];
8432 boolean action_removing = (action == ACTION_DIGGING ||
8433 action == ACTION_SNAPPING ||
8434 action == ACTION_COLLECTING);
8435 boolean action_moving = (action == ACTION_FALLING ||
8436 action == ACTION_MOVING ||
8437 action == ACTION_PUSHING ||
8438 action == ACTION_EATING ||
8439 action == ACTION_FILLING ||
8440 action == ACTION_EMPTYING);
8441 boolean action_falling = (action == ACTION_FALLING ||
8442 action == ACTION_FILLING ||
8443 action == ACTION_EMPTYING);
8445 // special case: graphic uses "2nd movement tile" and has defined
8446 // 7 frames for movement animation (or less) => use default graphic
8447 // for last (8th) frame which ends the movement animation
8448 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8450 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8451 graphic = (direction == MV_NONE ?
8452 el_act2img(effective_element, action) :
8453 el_act_dir2img(effective_element, action, direction));
8455 g = &graphic_info[graphic];
8458 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8462 else if (action_moving)
8464 boolean is_backside = object_mapping[tile].is_backside;
8468 int direction = object_mapping[tile].direction;
8469 int move_dir = (action_falling ? MV_DOWN : direction);
8474 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8475 if (g->double_movement && frame_em == 0)
8479 if (move_dir == MV_LEFT)
8480 GfxFrame[x - 1][y] = GfxFrame[x][y];
8481 else if (move_dir == MV_RIGHT)
8482 GfxFrame[x + 1][y] = GfxFrame[x][y];
8483 else if (move_dir == MV_UP)
8484 GfxFrame[x][y - 1] = GfxFrame[x][y];
8485 else if (move_dir == MV_DOWN)
8486 GfxFrame[x][y + 1] = GfxFrame[x][y];
8493 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8494 if (tile == Xsand_stonesand_quickout_1 ||
8495 tile == Xsand_stonesand_quickout_2)
8499 if (graphic_info[graphic].anim_global_sync)
8500 sync_frame = FrameCounter;
8501 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8502 sync_frame = GfxFrame[x][y];
8504 sync_frame = 0; // playfield border (pseudo steel)
8506 SetRandomAnimationValue(x, y);
8508 int frame = getAnimationFrame(g->anim_frames,
8511 g->anim_start_frame,
8514 g_em->unique_identifier =
8515 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8518 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8519 int tile, int frame_em, int x, int y)
8521 int action = object_mapping[tile].action;
8522 int direction = object_mapping[tile].direction;
8523 boolean is_backside = object_mapping[tile].is_backside;
8524 int effective_element = get_effective_element_EM(tile, frame_em);
8525 int effective_action = action;
8526 int graphic = (direction == MV_NONE ?
8527 el_act2img(effective_element, effective_action) :
8528 el_act_dir2img(effective_element, effective_action,
8530 int crumbled = (direction == MV_NONE ?
8531 el_act2crm(effective_element, effective_action) :
8532 el_act_dir2crm(effective_element, effective_action,
8534 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8535 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8536 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8537 struct GraphicInfo *g = &graphic_info[graphic];
8540 // special case: graphic uses "2nd movement tile" and has defined
8541 // 7 frames for movement animation (or less) => use default graphic
8542 // for last (8th) frame which ends the movement animation
8543 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8545 effective_action = ACTION_DEFAULT;
8546 graphic = (direction == MV_NONE ?
8547 el_act2img(effective_element, effective_action) :
8548 el_act_dir2img(effective_element, effective_action,
8550 crumbled = (direction == MV_NONE ?
8551 el_act2crm(effective_element, effective_action) :
8552 el_act_dir2crm(effective_element, effective_action,
8555 g = &graphic_info[graphic];
8558 if (graphic_info[graphic].anim_global_sync)
8559 sync_frame = FrameCounter;
8560 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8561 sync_frame = GfxFrame[x][y];
8563 sync_frame = 0; // playfield border (pseudo steel)
8565 SetRandomAnimationValue(x, y);
8567 int frame = getAnimationFrame(g->anim_frames,
8570 g->anim_start_frame,
8573 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8574 g->double_movement && is_backside);
8576 // (updating the "crumbled" graphic definitions is probably not really needed,
8577 // as animations for crumbled graphics can't be longer than one EMC cycle)
8578 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8582 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8583 int player_nr, int anim, int frame_em)
8585 int element = player_mapping[player_nr][anim].element_rnd;
8586 int action = player_mapping[player_nr][anim].action;
8587 int direction = player_mapping[player_nr][anim].direction;
8588 int graphic = (direction == MV_NONE ?
8589 el_act2img(element, action) :
8590 el_act_dir2img(element, action, direction));
8591 struct GraphicInfo *g = &graphic_info[graphic];
8594 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8596 stored_player[player_nr].StepFrame = frame_em;
8598 sync_frame = stored_player[player_nr].Frame;
8600 int frame = getAnimationFrame(g->anim_frames,
8603 g->anim_start_frame,
8606 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8607 &g_em->src_x, &g_em->src_y, FALSE);
8610 void InitGraphicInfo_EM(void)
8614 // always start with reliable default values
8615 for (i = 0; i < GAME_TILE_MAX; i++)
8617 object_mapping[i].element_rnd = EL_UNKNOWN;
8618 object_mapping[i].is_backside = FALSE;
8619 object_mapping[i].action = ACTION_DEFAULT;
8620 object_mapping[i].direction = MV_NONE;
8623 // always start with reliable default values
8624 for (p = 0; p < MAX_PLAYERS; p++)
8626 for (i = 0; i < PLY_MAX; i++)
8628 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8629 player_mapping[p][i].action = ACTION_DEFAULT;
8630 player_mapping[p][i].direction = MV_NONE;
8634 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8636 int e = em_object_mapping_list[i].element_em;
8638 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8639 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8641 if (em_object_mapping_list[i].action != -1)
8642 object_mapping[e].action = em_object_mapping_list[i].action;
8644 if (em_object_mapping_list[i].direction != -1)
8645 object_mapping[e].direction =
8646 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8649 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8651 int a = em_player_mapping_list[i].action_em;
8652 int p = em_player_mapping_list[i].player_nr;
8654 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8656 if (em_player_mapping_list[i].action != -1)
8657 player_mapping[p][a].action = em_player_mapping_list[i].action;
8659 if (em_player_mapping_list[i].direction != -1)
8660 player_mapping[p][a].direction =
8661 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8664 for (i = 0; i < GAME_TILE_MAX; i++)
8666 int element = object_mapping[i].element_rnd;
8667 int action = object_mapping[i].action;
8668 int direction = object_mapping[i].direction;
8669 boolean is_backside = object_mapping[i].is_backside;
8670 boolean action_exploding = ((action == ACTION_EXPLODING ||
8671 action == ACTION_SMASHED_BY_ROCK ||
8672 action == ACTION_SMASHED_BY_SPRING) &&
8673 element != EL_DIAMOND);
8674 boolean action_active = (action == ACTION_ACTIVE);
8675 boolean action_other = (action == ACTION_OTHER);
8677 for (j = 0; j < 8; j++)
8679 int effective_element = get_effective_element_EM(i, j);
8680 int effective_action = (j < 7 ? action :
8681 i == Xdrip_stretch ? action :
8682 i == Xdrip_stretchB ? action :
8683 i == Ydrip_1_s ? action :
8684 i == Ydrip_1_sB ? action :
8685 i == Yball_1 ? action :
8686 i == Xball_2 ? action :
8687 i == Yball_2 ? action :
8688 i == Yball_blank ? action :
8689 i == Ykey_1_blank ? action :
8690 i == Ykey_2_blank ? action :
8691 i == Ykey_3_blank ? action :
8692 i == Ykey_4_blank ? action :
8693 i == Ykey_5_blank ? action :
8694 i == Ykey_6_blank ? action :
8695 i == Ykey_7_blank ? action :
8696 i == Ykey_8_blank ? action :
8697 i == Ylenses_blank ? action :
8698 i == Ymagnify_blank ? action :
8699 i == Ygrass_blank ? action :
8700 i == Ydirt_blank ? action :
8701 i == Xsand_stonein_1 ? action :
8702 i == Xsand_stonein_2 ? action :
8703 i == Xsand_stonein_3 ? action :
8704 i == Xsand_stonein_4 ? action :
8705 i == Xsand_stoneout_1 ? action :
8706 i == Xsand_stoneout_2 ? action :
8707 i == Xboom_android ? ACTION_EXPLODING :
8708 action_exploding ? ACTION_EXPLODING :
8709 action_active ? action :
8710 action_other ? action :
8712 int graphic = (el_act_dir2img(effective_element, effective_action,
8714 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8716 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8717 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8718 boolean has_action_graphics = (graphic != base_graphic);
8719 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8720 struct GraphicInfo *g = &graphic_info[graphic];
8721 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8724 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8725 boolean special_animation = (action != ACTION_DEFAULT &&
8726 g->anim_frames == 3 &&
8727 g->anim_delay == 2 &&
8728 g->anim_mode & ANIM_LINEAR);
8729 int sync_frame = (i == Xdrip_stretch ? 7 :
8730 i == Xdrip_stretchB ? 7 :
8731 i == Ydrip_2_s ? j + 8 :
8732 i == Ydrip_2_sB ? j + 8 :
8741 i == Xfake_acid_1 ? 0 :
8742 i == Xfake_acid_2 ? 10 :
8743 i == Xfake_acid_3 ? 20 :
8744 i == Xfake_acid_4 ? 30 :
8745 i == Xfake_acid_5 ? 40 :
8746 i == Xfake_acid_6 ? 50 :
8747 i == Xfake_acid_7 ? 60 :
8748 i == Xfake_acid_8 ? 70 :
8750 i == Yball_2 ? j + 8 :
8751 i == Yball_blank ? j + 1 :
8752 i == Ykey_1_blank ? j + 1 :
8753 i == Ykey_2_blank ? j + 1 :
8754 i == Ykey_3_blank ? j + 1 :
8755 i == Ykey_4_blank ? j + 1 :
8756 i == Ykey_5_blank ? j + 1 :
8757 i == Ykey_6_blank ? j + 1 :
8758 i == Ykey_7_blank ? j + 1 :
8759 i == Ykey_8_blank ? j + 1 :
8760 i == Ylenses_blank ? j + 1 :
8761 i == Ymagnify_blank ? j + 1 :
8762 i == Ygrass_blank ? j + 1 :
8763 i == Ydirt_blank ? j + 1 :
8764 i == Xamoeba_1 ? 0 :
8765 i == Xamoeba_2 ? 1 :
8766 i == Xamoeba_3 ? 2 :
8767 i == Xamoeba_4 ? 3 :
8768 i == Xamoeba_5 ? 0 :
8769 i == Xamoeba_6 ? 1 :
8770 i == Xamoeba_7 ? 2 :
8771 i == Xamoeba_8 ? 3 :
8772 i == Xexit_2 ? j + 8 :
8773 i == Xexit_3 ? j + 16 :
8774 i == Xdynamite_1 ? 0 :
8775 i == Xdynamite_2 ? 8 :
8776 i == Xdynamite_3 ? 16 :
8777 i == Xdynamite_4 ? 24 :
8778 i == Xsand_stonein_1 ? j + 1 :
8779 i == Xsand_stonein_2 ? j + 9 :
8780 i == Xsand_stonein_3 ? j + 17 :
8781 i == Xsand_stonein_4 ? j + 25 :
8782 i == Xsand_stoneout_1 && j == 0 ? 0 :
8783 i == Xsand_stoneout_1 && j == 1 ? 0 :
8784 i == Xsand_stoneout_1 && j == 2 ? 1 :
8785 i == Xsand_stoneout_1 && j == 3 ? 2 :
8786 i == Xsand_stoneout_1 && j == 4 ? 2 :
8787 i == Xsand_stoneout_1 && j == 5 ? 3 :
8788 i == Xsand_stoneout_1 && j == 6 ? 4 :
8789 i == Xsand_stoneout_1 && j == 7 ? 4 :
8790 i == Xsand_stoneout_2 && j == 0 ? 5 :
8791 i == Xsand_stoneout_2 && j == 1 ? 6 :
8792 i == Xsand_stoneout_2 && j == 2 ? 7 :
8793 i == Xsand_stoneout_2 && j == 3 ? 8 :
8794 i == Xsand_stoneout_2 && j == 4 ? 9 :
8795 i == Xsand_stoneout_2 && j == 5 ? 11 :
8796 i == Xsand_stoneout_2 && j == 6 ? 13 :
8797 i == Xsand_stoneout_2 && j == 7 ? 15 :
8798 i == Xboom_bug && j == 1 ? 2 :
8799 i == Xboom_bug && j == 2 ? 2 :
8800 i == Xboom_bug && j == 3 ? 4 :
8801 i == Xboom_bug && j == 4 ? 4 :
8802 i == Xboom_bug && j == 5 ? 2 :
8803 i == Xboom_bug && j == 6 ? 2 :
8804 i == Xboom_bug && j == 7 ? 0 :
8805 i == Xboom_tank && j == 1 ? 2 :
8806 i == Xboom_tank && j == 2 ? 2 :
8807 i == Xboom_tank && j == 3 ? 4 :
8808 i == Xboom_tank && j == 4 ? 4 :
8809 i == Xboom_tank && j == 5 ? 2 :
8810 i == Xboom_tank && j == 6 ? 2 :
8811 i == Xboom_tank && j == 7 ? 0 :
8812 i == Xboom_android && j == 7 ? 6 :
8813 i == Xboom_1 && j == 1 ? 2 :
8814 i == Xboom_1 && j == 2 ? 2 :
8815 i == Xboom_1 && j == 3 ? 4 :
8816 i == Xboom_1 && j == 4 ? 4 :
8817 i == Xboom_1 && j == 5 ? 6 :
8818 i == Xboom_1 && j == 6 ? 6 :
8819 i == Xboom_1 && j == 7 ? 8 :
8820 i == Xboom_2 && j == 0 ? 8 :
8821 i == Xboom_2 && j == 1 ? 8 :
8822 i == Xboom_2 && j == 2 ? 10 :
8823 i == Xboom_2 && j == 3 ? 10 :
8824 i == Xboom_2 && j == 4 ? 10 :
8825 i == Xboom_2 && j == 5 ? 12 :
8826 i == Xboom_2 && j == 6 ? 12 :
8827 i == Xboom_2 && j == 7 ? 12 :
8828 special_animation && j == 4 ? 3 :
8829 effective_action != action ? 0 :
8831 int frame = getAnimationFrame(g->anim_frames,
8834 g->anim_start_frame,
8837 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8838 g->double_movement && is_backside);
8840 g_em->bitmap = src_bitmap;
8841 g_em->src_x = src_x;
8842 g_em->src_y = src_y;
8843 g_em->src_offset_x = 0;
8844 g_em->src_offset_y = 0;
8845 g_em->dst_offset_x = 0;
8846 g_em->dst_offset_y = 0;
8847 g_em->width = TILEX;
8848 g_em->height = TILEY;
8850 g_em->preserve_background = FALSE;
8852 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8855 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8856 effective_action == ACTION_MOVING ||
8857 effective_action == ACTION_PUSHING ||
8858 effective_action == ACTION_EATING)) ||
8859 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8860 effective_action == ACTION_EMPTYING)))
8863 (effective_action == ACTION_FALLING ||
8864 effective_action == ACTION_FILLING ||
8865 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8866 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8867 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8868 int num_steps = (i == Ydrip_1_s ? 16 :
8869 i == Ydrip_1_sB ? 16 :
8870 i == Ydrip_2_s ? 16 :
8871 i == Ydrip_2_sB ? 16 :
8872 i == Xsand_stonein_1 ? 32 :
8873 i == Xsand_stonein_2 ? 32 :
8874 i == Xsand_stonein_3 ? 32 :
8875 i == Xsand_stonein_4 ? 32 :
8876 i == Xsand_stoneout_1 ? 16 :
8877 i == Xsand_stoneout_2 ? 16 : 8);
8878 int cx = ABS(dx) * (TILEX / num_steps);
8879 int cy = ABS(dy) * (TILEY / num_steps);
8880 int step_frame = (i == Ydrip_2_s ? j + 8 :
8881 i == Ydrip_2_sB ? j + 8 :
8882 i == Xsand_stonein_2 ? j + 8 :
8883 i == Xsand_stonein_3 ? j + 16 :
8884 i == Xsand_stonein_4 ? j + 24 :
8885 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8886 int step = (is_backside ? step_frame : num_steps - step_frame);
8888 if (is_backside) // tile where movement starts
8890 if (dx < 0 || dy < 0)
8892 g_em->src_offset_x = cx * step;
8893 g_em->src_offset_y = cy * step;
8897 g_em->dst_offset_x = cx * step;
8898 g_em->dst_offset_y = cy * step;
8901 else // tile where movement ends
8903 if (dx < 0 || dy < 0)
8905 g_em->dst_offset_x = cx * step;
8906 g_em->dst_offset_y = cy * step;
8910 g_em->src_offset_x = cx * step;
8911 g_em->src_offset_y = cy * step;
8915 g_em->width = TILEX - cx * step;
8916 g_em->height = TILEY - cy * step;
8919 // create unique graphic identifier to decide if tile must be redrawn
8920 /* bit 31 - 16 (16 bit): EM style graphic
8921 bit 15 - 12 ( 4 bit): EM style frame
8922 bit 11 - 6 ( 6 bit): graphic width
8923 bit 5 - 0 ( 6 bit): graphic height */
8924 g_em->unique_identifier =
8925 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8929 for (i = 0; i < GAME_TILE_MAX; i++)
8931 for (j = 0; j < 8; j++)
8933 int element = object_mapping[i].element_rnd;
8934 int action = object_mapping[i].action;
8935 int direction = object_mapping[i].direction;
8936 boolean is_backside = object_mapping[i].is_backside;
8937 int graphic_action = el_act_dir2img(element, action, direction);
8938 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8940 if ((action == ACTION_SMASHED_BY_ROCK ||
8941 action == ACTION_SMASHED_BY_SPRING ||
8942 action == ACTION_EATING) &&
8943 graphic_action == graphic_default)
8945 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8946 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8947 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8948 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8951 // no separate animation for "smashed by rock" -- use rock instead
8952 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8953 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8955 g_em->bitmap = g_xx->bitmap;
8956 g_em->src_x = g_xx->src_x;
8957 g_em->src_y = g_xx->src_y;
8958 g_em->src_offset_x = g_xx->src_offset_x;
8959 g_em->src_offset_y = g_xx->src_offset_y;
8960 g_em->dst_offset_x = g_xx->dst_offset_x;
8961 g_em->dst_offset_y = g_xx->dst_offset_y;
8962 g_em->width = g_xx->width;
8963 g_em->height = g_xx->height;
8964 g_em->unique_identifier = g_xx->unique_identifier;
8967 g_em->preserve_background = TRUE;
8972 for (p = 0; p < MAX_PLAYERS; p++)
8974 for (i = 0; i < PLY_MAX; i++)
8976 int element = player_mapping[p][i].element_rnd;
8977 int action = player_mapping[p][i].action;
8978 int direction = player_mapping[p][i].direction;
8980 for (j = 0; j < 8; j++)
8982 int effective_element = element;
8983 int effective_action = action;
8984 int graphic = (direction == MV_NONE ?
8985 el_act2img(effective_element, effective_action) :
8986 el_act_dir2img(effective_element, effective_action,
8988 struct GraphicInfo *g = &graphic_info[graphic];
8989 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
8993 int frame = getAnimationFrame(g->anim_frames,
8996 g->anim_start_frame,
8999 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9001 g_em->bitmap = src_bitmap;
9002 g_em->src_x = src_x;
9003 g_em->src_y = src_y;
9004 g_em->src_offset_x = 0;
9005 g_em->src_offset_y = 0;
9006 g_em->dst_offset_x = 0;
9007 g_em->dst_offset_y = 0;
9008 g_em->width = TILEX;
9009 g_em->height = TILEY;
9015 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9016 boolean any_player_moving,
9017 boolean any_player_snapping,
9018 boolean any_player_dropping)
9020 if (frame == 7 && !any_player_dropping)
9022 if (!local_player->was_waiting)
9024 if (!CheckSaveEngineSnapshotToList())
9027 local_player->was_waiting = TRUE;
9030 else if (any_player_moving || any_player_snapping || any_player_dropping)
9032 local_player->was_waiting = FALSE;
9036 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9037 boolean murphy_is_dropping)
9039 if (murphy_is_waiting)
9041 if (!local_player->was_waiting)
9043 if (!CheckSaveEngineSnapshotToList())
9046 local_player->was_waiting = TRUE;
9051 local_player->was_waiting = FALSE;
9055 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9056 boolean button_released)
9058 if (button_released)
9060 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9061 CheckSaveEngineSnapshotToList();
9063 else if (element_clicked)
9065 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9066 CheckSaveEngineSnapshotToList();
9068 game.snapshot.changed_action = TRUE;
9072 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9073 boolean any_player_moving,
9074 boolean any_player_snapping,
9075 boolean any_player_dropping)
9077 if (tape.single_step && tape.recording && !tape.pausing)
9078 if (frame == 7 && !any_player_dropping)
9079 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9081 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9082 any_player_snapping, any_player_dropping);
9085 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9086 boolean murphy_is_dropping)
9088 boolean murphy_starts_dropping = FALSE;
9091 for (i = 0; i < MAX_PLAYERS; i++)
9092 if (stored_player[i].force_dropping)
9093 murphy_starts_dropping = TRUE;
9095 if (tape.single_step && tape.recording && !tape.pausing)
9096 if (murphy_is_waiting && !murphy_starts_dropping)
9097 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9099 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9102 void CheckSingleStepMode_MM(boolean element_clicked,
9103 boolean button_released)
9105 if (tape.single_step && tape.recording && !tape.pausing)
9106 if (button_released)
9107 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9109 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9112 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9113 int graphic, int sync_frame, int x, int y)
9115 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9117 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9120 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9122 return (IS_NEXT_FRAME(sync_frame, graphic));
9125 int getGraphicInfo_Delay(int graphic)
9127 return graphic_info[graphic].anim_delay;
9130 void PlayMenuSoundExt(int sound)
9132 if (sound == SND_UNDEFINED)
9135 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9136 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9139 if (IS_LOOP_SOUND(sound))
9140 PlaySoundLoop(sound);
9145 void PlayMenuSound(void)
9147 PlayMenuSoundExt(menu.sound[game_status]);
9150 void PlayMenuSoundStereo(int sound, int stereo_position)
9152 if (sound == SND_UNDEFINED)
9155 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9156 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9159 if (IS_LOOP_SOUND(sound))
9160 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9162 PlaySoundStereo(sound, stereo_position);
9165 void PlayMenuSoundIfLoopExt(int sound)
9167 if (sound == SND_UNDEFINED)
9170 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9171 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9174 if (IS_LOOP_SOUND(sound))
9175 PlaySoundLoop(sound);
9178 void PlayMenuSoundIfLoop(void)
9180 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9183 void PlayMenuMusicExt(int music)
9185 if (music == MUS_UNDEFINED)
9188 if (!setup.sound_music)
9191 if (IS_LOOP_MUSIC(music))
9192 PlayMusicLoop(music);
9197 void PlayMenuMusic(void)
9199 char *curr_music = getCurrentlyPlayingMusicFilename();
9200 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9202 if (!strEqual(curr_music, next_music))
9203 PlayMenuMusicExt(menu.music[game_status]);
9206 void PlayMenuSoundsAndMusic(void)
9212 static void FadeMenuSounds(void)
9217 static void FadeMenuMusic(void)
9219 char *curr_music = getCurrentlyPlayingMusicFilename();
9220 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9222 if (!strEqual(curr_music, next_music))
9226 void FadeMenuSoundsAndMusic(void)
9232 void PlaySoundActivating(void)
9235 PlaySound(SND_MENU_ITEM_ACTIVATING);
9239 void PlaySoundSelecting(void)
9242 PlaySound(SND_MENU_ITEM_SELECTING);
9246 void ToggleFullscreenIfNeeded(void)
9248 // if setup and video fullscreen state are already matching, nothing do do
9249 if (setup.fullscreen == video.fullscreen_enabled ||
9250 !video.fullscreen_available)
9253 SDLSetWindowFullscreen(setup.fullscreen);
9255 // set setup value according to successfully changed fullscreen mode
9256 setup.fullscreen = video.fullscreen_enabled;
9259 void ChangeWindowScalingIfNeeded(void)
9261 // if setup and video window scaling are already matching, nothing do do
9262 if (setup.window_scaling_percent == video.window_scaling_percent ||
9263 video.fullscreen_enabled)
9266 SDLSetWindowScaling(setup.window_scaling_percent);
9268 // set setup value according to successfully changed window scaling
9269 setup.window_scaling_percent = video.window_scaling_percent;
9272 void ChangeVsyncModeIfNeeded(void)
9274 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9275 int video_vsync_mode = video.vsync_mode;
9277 // if setup and video vsync mode are already matching, nothing do do
9278 if (setup_vsync_mode == video_vsync_mode)
9281 // if renderer is using OpenGL, vsync mode can directly be changed
9282 SDLSetScreenVsyncMode(setup.vsync_mode);
9284 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9285 if (video.vsync_mode == video_vsync_mode)
9287 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9289 // save backbuffer content which gets lost when re-creating screen
9290 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9292 // force re-creating screen and renderer to set new vsync mode
9293 video.fullscreen_enabled = !setup.fullscreen;
9295 // when creating new renderer, destroy textures linked to old renderer
9296 FreeAllImageTextures(); // needs old renderer to free the textures
9298 // re-create screen and renderer (including change of vsync mode)
9299 ChangeVideoModeIfNeeded(setup.fullscreen);
9301 // set setup value according to successfully changed fullscreen mode
9302 setup.fullscreen = video.fullscreen_enabled;
9304 // restore backbuffer content from temporary backbuffer backup bitmap
9305 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9306 FreeBitmap(tmp_backbuffer);
9308 // update visible window/screen
9309 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9311 // when changing vsync mode, re-create textures for new renderer
9312 InitImageTextures();
9315 // set setup value according to successfully changed vsync mode
9316 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9319 static void JoinRectangles(int *x, int *y, int *width, int *height,
9320 int x2, int y2, int width2, int height2)
9322 // do not join with "off-screen" rectangle
9323 if (x2 == -1 || y2 == -1)
9328 *width = MAX(*width, width2);
9329 *height = MAX(*height, height2);
9332 void SetAnimStatus(int anim_status_new)
9334 if (anim_status_new == GAME_MODE_MAIN)
9335 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9336 else if (anim_status_new == GAME_MODE_SCORES)
9337 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9339 global.anim_status_next = anim_status_new;
9341 // directly set screen modes that are entered without fading
9342 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9343 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9344 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9345 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9346 global.anim_status = global.anim_status_next;
9349 void SetGameStatus(int game_status_new)
9351 if (game_status_new != game_status)
9352 game_status_last_screen = game_status;
9354 game_status = game_status_new;
9356 SetAnimStatus(game_status_new);
9359 void SetFontStatus(int game_status_new)
9361 static int last_game_status = -1;
9363 if (game_status_new != -1)
9365 // set game status for font use after storing last game status
9366 last_game_status = game_status;
9367 game_status = game_status_new;
9371 // reset game status after font use from last stored game status
9372 game_status = last_game_status;
9376 void ResetFontStatus(void)
9381 void SetLevelSetInfo(char *identifier, int level_nr)
9383 setString(&levelset.identifier, identifier);
9385 levelset.level_nr = level_nr;
9388 boolean CheckIfAllViewportsHaveChanged(void)
9390 // if game status has not changed, viewports have not changed either
9391 if (game_status == game_status_last)
9394 // check if all viewports have changed with current game status
9396 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9397 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9398 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9399 int new_real_sx = vp_playfield->x;
9400 int new_real_sy = vp_playfield->y;
9401 int new_full_sxsize = vp_playfield->width;
9402 int new_full_sysize = vp_playfield->height;
9403 int new_dx = vp_door_1->x;
9404 int new_dy = vp_door_1->y;
9405 int new_dxsize = vp_door_1->width;
9406 int new_dysize = vp_door_1->height;
9407 int new_vx = vp_door_2->x;
9408 int new_vy = vp_door_2->y;
9409 int new_vxsize = vp_door_2->width;
9410 int new_vysize = vp_door_2->height;
9412 boolean playfield_viewport_has_changed =
9413 (new_real_sx != REAL_SX ||
9414 new_real_sy != REAL_SY ||
9415 new_full_sxsize != FULL_SXSIZE ||
9416 new_full_sysize != FULL_SYSIZE);
9418 boolean door_1_viewport_has_changed =
9421 new_dxsize != DXSIZE ||
9422 new_dysize != DYSIZE);
9424 boolean door_2_viewport_has_changed =
9427 new_vxsize != VXSIZE ||
9428 new_vysize != VYSIZE ||
9429 game_status_last == GAME_MODE_EDITOR);
9431 return (playfield_viewport_has_changed &&
9432 door_1_viewport_has_changed &&
9433 door_2_viewport_has_changed);
9436 boolean CheckFadeAll(void)
9438 return (CheckIfGlobalBorderHasChanged() ||
9439 CheckIfAllViewportsHaveChanged());
9442 void ChangeViewportPropertiesIfNeeded(void)
9444 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9445 FALSE : setup.small_game_graphics);
9446 int gfx_game_mode = game_status;
9447 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9449 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9450 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9451 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9452 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9453 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9454 int new_win_xsize = vp_window->width;
9455 int new_win_ysize = vp_window->height;
9456 int border_left = vp_playfield->border_left;
9457 int border_right = vp_playfield->border_right;
9458 int border_top = vp_playfield->border_top;
9459 int border_bottom = vp_playfield->border_bottom;
9460 int new_sx = vp_playfield->x + border_left;
9461 int new_sy = vp_playfield->y + border_top;
9462 int new_sxsize = vp_playfield->width - border_left - border_right;
9463 int new_sysize = vp_playfield->height - border_top - border_bottom;
9464 int new_real_sx = vp_playfield->x;
9465 int new_real_sy = vp_playfield->y;
9466 int new_full_sxsize = vp_playfield->width;
9467 int new_full_sysize = vp_playfield->height;
9468 int new_dx = vp_door_1->x;
9469 int new_dy = vp_door_1->y;
9470 int new_dxsize = vp_door_1->width;
9471 int new_dysize = vp_door_1->height;
9472 int new_vx = vp_door_2->x;
9473 int new_vy = vp_door_2->y;
9474 int new_vxsize = vp_door_2->width;
9475 int new_vysize = vp_door_2->height;
9476 int new_ex = vp_door_3->x;
9477 int new_ey = vp_door_3->y;
9478 int new_exsize = vp_door_3->width;
9479 int new_eysize = vp_door_3->height;
9480 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9481 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9482 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9483 int new_scr_fieldx = new_sxsize / tilesize;
9484 int new_scr_fieldy = new_sysize / tilesize;
9485 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9486 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9487 boolean init_gfx_buffers = FALSE;
9488 boolean init_video_buffer = FALSE;
9489 boolean init_gadgets_and_anims = FALSE;
9490 boolean init_em_graphics = FALSE;
9492 if (new_win_xsize != WIN_XSIZE ||
9493 new_win_ysize != WIN_YSIZE)
9495 WIN_XSIZE = new_win_xsize;
9496 WIN_YSIZE = new_win_ysize;
9498 init_video_buffer = TRUE;
9499 init_gfx_buffers = TRUE;
9500 init_gadgets_and_anims = TRUE;
9502 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9505 if (new_scr_fieldx != SCR_FIELDX ||
9506 new_scr_fieldy != SCR_FIELDY)
9508 // this always toggles between MAIN and GAME when using small tile size
9510 SCR_FIELDX = new_scr_fieldx;
9511 SCR_FIELDY = new_scr_fieldy;
9513 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9524 new_sxsize != SXSIZE ||
9525 new_sysize != SYSIZE ||
9526 new_dxsize != DXSIZE ||
9527 new_dysize != DYSIZE ||
9528 new_vxsize != VXSIZE ||
9529 new_vysize != VYSIZE ||
9530 new_exsize != EXSIZE ||
9531 new_eysize != EYSIZE ||
9532 new_real_sx != REAL_SX ||
9533 new_real_sy != REAL_SY ||
9534 new_full_sxsize != FULL_SXSIZE ||
9535 new_full_sysize != FULL_SYSIZE ||
9536 new_tilesize_var != TILESIZE_VAR
9539 // ------------------------------------------------------------------------
9540 // determine next fading area for changed viewport definitions
9541 // ------------------------------------------------------------------------
9543 // start with current playfield area (default fading area)
9546 FADE_SXSIZE = FULL_SXSIZE;
9547 FADE_SYSIZE = FULL_SYSIZE;
9549 // add new playfield area if position or size has changed
9550 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9551 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9553 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9554 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9557 // add current and new door 1 area if position or size has changed
9558 if (new_dx != DX || new_dy != DY ||
9559 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9561 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9562 DX, DY, DXSIZE, DYSIZE);
9563 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9564 new_dx, new_dy, new_dxsize, new_dysize);
9567 // add current and new door 2 area if position or size has changed
9568 if (new_vx != VX || new_vy != VY ||
9569 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9571 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9572 VX, VY, VXSIZE, VYSIZE);
9573 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9574 new_vx, new_vy, new_vxsize, new_vysize);
9577 // ------------------------------------------------------------------------
9578 // handle changed tile size
9579 // ------------------------------------------------------------------------
9581 if (new_tilesize_var != TILESIZE_VAR)
9583 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9585 // changing tile size invalidates scroll values of engine snapshots
9586 FreeEngineSnapshotSingle();
9588 // changing tile size requires update of graphic mapping for EM engine
9589 init_em_graphics = TRUE;
9600 SXSIZE = new_sxsize;
9601 SYSIZE = new_sysize;
9602 DXSIZE = new_dxsize;
9603 DYSIZE = new_dysize;
9604 VXSIZE = new_vxsize;
9605 VYSIZE = new_vysize;
9606 EXSIZE = new_exsize;
9607 EYSIZE = new_eysize;
9608 REAL_SX = new_real_sx;
9609 REAL_SY = new_real_sy;
9610 FULL_SXSIZE = new_full_sxsize;
9611 FULL_SYSIZE = new_full_sysize;
9612 TILESIZE_VAR = new_tilesize_var;
9614 init_gfx_buffers = TRUE;
9615 init_gadgets_and_anims = TRUE;
9617 // printf("::: viewports: init_gfx_buffers\n");
9618 // printf("::: viewports: init_gadgets_and_anims\n");
9621 if (init_gfx_buffers)
9623 // printf("::: init_gfx_buffers\n");
9625 SCR_FIELDX = new_scr_fieldx_buffers;
9626 SCR_FIELDY = new_scr_fieldy_buffers;
9630 SCR_FIELDX = new_scr_fieldx;
9631 SCR_FIELDY = new_scr_fieldy;
9633 SetDrawDeactivationMask(REDRAW_NONE);
9634 SetDrawBackgroundMask(REDRAW_FIELD);
9637 if (init_video_buffer)
9639 // printf("::: init_video_buffer\n");
9641 FreeAllImageTextures(); // needs old renderer to free the textures
9643 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9644 InitImageTextures();
9647 if (init_gadgets_and_anims)
9649 // printf("::: init_gadgets_and_anims\n");
9652 InitGlobalAnimations();
9655 if (init_em_graphics)
9657 InitGraphicInfo_EM();