1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 void DumpTile(int x, int y)
401 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
404 if (!IN_LEV_FIELD(x, y))
406 Info("(not in level field)");
412 token_name = element_info[Tile[x][y]].token_name;
414 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
415 Info("Back: %s", print_if_not_empty(Back[x][y]));
416 Info("Store: %s", print_if_not_empty(Store[x][y]));
417 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
418 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
419 Info("MovPos: %d", MovPos[x][y]);
420 Info("MovDir: %d", MovDir[x][y]);
421 Info("MovDelay: %d", MovDelay[x][y]);
422 Info("ChangeDelay: %d", ChangeDelay[x][y]);
423 Info("CustomValue: %d", CustomValue[x][y]);
424 Info("GfxElement: %d", GfxElement[x][y]);
425 Info("GfxAction: %d", GfxAction[x][y]);
426 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
427 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
431 void DumpTileFromScreen(int sx, int sy)
433 int lx = getLevelFromScreenX(sx);
434 int ly = getLevelFromScreenY(sy);
439 void SetDrawtoField(int mode)
441 if (mode == DRAW_TO_FIELDBUFFER)
447 BX2 = SCR_FIELDX + 1;
448 BY2 = SCR_FIELDY + 1;
450 drawto_field = fieldbuffer;
452 else // DRAW_TO_BACKBUFFER
458 BX2 = SCR_FIELDX - 1;
459 BY2 = SCR_FIELDY - 1;
461 drawto_field = backbuffer;
465 static void RedrawPlayfield_RND(void)
467 if (game.envelope_active)
470 DrawLevel(REDRAW_ALL);
474 void RedrawPlayfield(void)
476 if (game_status != GAME_MODE_PLAYING)
479 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
480 RedrawPlayfield_EM(TRUE);
481 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
482 RedrawPlayfield_SP(TRUE);
483 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
484 RedrawPlayfield_MM();
485 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
486 RedrawPlayfield_RND();
488 BlitScreenToBitmap(backbuffer);
490 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
494 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
497 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
498 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
500 if (x == -1 && y == -1)
503 if (draw_target == DRAW_TO_SCREEN)
504 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
506 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
509 static void DrawMaskedBorderExt_FIELD(int draw_target)
511 if (global.border_status >= GAME_MODE_MAIN &&
512 global.border_status <= GAME_MODE_PLAYING &&
513 border.draw_masked[global.border_status])
514 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
518 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
520 // when drawing to backbuffer, never draw border over open doors
521 if (draw_target == DRAW_TO_BACKBUFFER &&
522 (GetDoorState() & DOOR_OPEN_1))
525 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
526 (global.border_status != GAME_MODE_EDITOR ||
527 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
528 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
531 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
533 // when drawing to backbuffer, never draw border over open doors
534 if (draw_target == DRAW_TO_BACKBUFFER &&
535 (GetDoorState() & DOOR_OPEN_2))
538 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
539 global.border_status != GAME_MODE_EDITOR)
540 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
543 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
545 // currently not available
548 static void DrawMaskedBorderExt_ALL(int draw_target)
550 DrawMaskedBorderExt_FIELD(draw_target);
551 DrawMaskedBorderExt_DOOR_1(draw_target);
552 DrawMaskedBorderExt_DOOR_2(draw_target);
553 DrawMaskedBorderExt_DOOR_3(draw_target);
556 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
558 // never draw masked screen borders on borderless screens
559 if (global.border_status == GAME_MODE_LOADING ||
560 global.border_status == GAME_MODE_TITLE)
563 if (redraw_mask & REDRAW_ALL)
564 DrawMaskedBorderExt_ALL(draw_target);
567 if (redraw_mask & REDRAW_FIELD)
568 DrawMaskedBorderExt_FIELD(draw_target);
569 if (redraw_mask & REDRAW_DOOR_1)
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 if (redraw_mask & REDRAW_DOOR_2)
572 DrawMaskedBorderExt_DOOR_2(draw_target);
573 if (redraw_mask & REDRAW_DOOR_3)
574 DrawMaskedBorderExt_DOOR_3(draw_target);
578 void DrawMaskedBorder_FIELD(void)
580 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
583 void DrawMaskedBorder(int redraw_mask)
585 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
588 void DrawMaskedBorderToTarget(int draw_target)
590 if (draw_target == DRAW_TO_BACKBUFFER ||
591 draw_target == DRAW_TO_SCREEN)
593 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
597 int last_border_status = global.border_status;
599 if (draw_target == DRAW_TO_FADE_SOURCE)
601 global.border_status = gfx.fade_border_source_status;
602 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
604 else if (draw_target == DRAW_TO_FADE_TARGET)
606 global.border_status = gfx.fade_border_target_status;
607 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 global.border_status = last_border_status;
613 gfx.masked_border_bitmap_ptr = backbuffer;
617 void DrawTileCursor(int draw_target)
623 int graphic = IMG_GLOBAL_TILE_CURSOR;
625 int tilesize = TILESIZE_VAR;
626 int width = tilesize;
627 int height = tilesize;
629 if (game_status != GAME_MODE_PLAYING)
632 if (!tile_cursor.enabled ||
636 if (tile_cursor.moving)
638 int step = TILESIZE_VAR / 4;
639 int dx = tile_cursor.target_x - tile_cursor.x;
640 int dy = tile_cursor.target_y - tile_cursor.y;
643 tile_cursor.x = tile_cursor.target_x;
645 tile_cursor.x += SIGN(dx) * step;
648 tile_cursor.y = tile_cursor.target_y;
650 tile_cursor.y += SIGN(dy) * step;
652 if (tile_cursor.x == tile_cursor.target_x &&
653 tile_cursor.y == tile_cursor.target_y)
654 tile_cursor.moving = FALSE;
657 dst_x = tile_cursor.x;
658 dst_y = tile_cursor.y;
660 frame = getGraphicAnimationFrame(graphic, -1);
662 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
665 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
666 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
668 if (draw_target == DRAW_TO_SCREEN)
669 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
671 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
675 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
677 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
680 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
682 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
683 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
685 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
688 void BlitScreenToBitmap(Bitmap *target_bitmap)
690 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
691 BlitScreenToBitmap_EM(target_bitmap);
692 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
693 BlitScreenToBitmap_SP(target_bitmap);
694 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
695 BlitScreenToBitmap_MM(target_bitmap);
696 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
697 BlitScreenToBitmap_RND(target_bitmap);
699 redraw_mask |= REDRAW_FIELD;
702 static void DrawFramesPerSecond(void)
705 int font_nr = FONT_TEXT_2;
706 int font_width = getFontWidth(font_nr);
707 int draw_deactivation_mask = GetDrawDeactivationMask();
708 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
710 // draw FPS with leading space (needed if field buffer deactivated)
711 sprintf(text, " %04.1f fps", global.frames_per_second);
713 // override draw deactivation mask (required for invisible warp mode)
714 SetDrawDeactivationMask(REDRAW_NONE);
716 // draw opaque FPS if field buffer deactivated, else draw masked FPS
717 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
718 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
720 // set draw deactivation mask to previous value
721 SetDrawDeactivationMask(draw_deactivation_mask);
723 // force full-screen redraw in this frame
724 redraw_mask = REDRAW_ALL;
728 static void PrintFrameTimeDebugging(void)
730 static unsigned int last_counter = 0;
731 unsigned int counter = Counter();
732 int diff_1 = counter - last_counter;
733 int diff_2 = diff_1 - GAME_FRAME_DELAY;
735 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
736 char diff_bar[2 * diff_2_max + 5];
740 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
742 for (i = 0; i < diff_2_max; i++)
743 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
744 i >= diff_2_max - diff_2_cut ? '-' : ' ');
746 diff_bar[pos++] = '|';
748 for (i = 0; i < diff_2_max; i++)
749 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
751 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
753 diff_bar[pos++] = '\0';
755 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
758 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
761 last_counter = counter;
765 static int unifiedRedrawMask(int mask)
767 if (mask & REDRAW_ALL)
770 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
776 static boolean equalRedrawMasks(int mask_1, int mask_2)
778 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
781 void BackToFront(void)
783 static int last_redraw_mask = REDRAW_NONE;
785 // force screen redraw in every frame to continue drawing global animations
786 // (but always use the last redraw mask to prevent unwanted side effects)
787 if (redraw_mask == REDRAW_NONE)
788 redraw_mask = last_redraw_mask;
790 last_redraw_mask = redraw_mask;
793 // masked border now drawn immediately when blitting backbuffer to window
795 // draw masked border to all viewports, if defined
796 DrawMaskedBorder(redraw_mask);
799 // draw frames per second (only if debug mode is enabled)
800 if (redraw_mask & REDRAW_FPS)
801 DrawFramesPerSecond();
803 // remove playfield redraw before potentially merging with doors redraw
804 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
805 redraw_mask &= ~REDRAW_FIELD;
807 // redraw complete window if both playfield and (some) doors need redraw
808 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
809 redraw_mask = REDRAW_ALL;
811 /* although redrawing the whole window would be fine for normal gameplay,
812 being able to only redraw the playfield is required for deactivating
813 certain drawing areas (mainly playfield) to work, which is needed for
814 warp-forward to be fast enough (by skipping redraw of most frames) */
816 if (redraw_mask & REDRAW_ALL)
818 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
820 else if (redraw_mask & REDRAW_FIELD)
822 BlitBitmap(backbuffer, window,
823 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
825 else if (redraw_mask & REDRAW_DOORS)
827 // merge door areas to prevent calling screen redraw more than once
833 if (redraw_mask & REDRAW_DOOR_1)
837 x2 = MAX(x2, DX + DXSIZE);
838 y2 = MAX(y2, DY + DYSIZE);
841 if (redraw_mask & REDRAW_DOOR_2)
845 x2 = MAX(x2, VX + VXSIZE);
846 y2 = MAX(y2, VY + VYSIZE);
849 if (redraw_mask & REDRAW_DOOR_3)
853 x2 = MAX(x2, EX + EXSIZE);
854 y2 = MAX(y2, EY + EYSIZE);
857 // make sure that at least one pixel is blitted, and inside the screen
858 // (else nothing is blitted, causing the animations not to be updated)
859 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
860 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
861 x2 = MIN(MAX(1, x2), WIN_XSIZE);
862 y2 = MIN(MAX(1, y2), WIN_YSIZE);
864 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
867 redraw_mask = REDRAW_NONE;
870 PrintFrameTimeDebugging();
874 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
876 unsigned int frame_delay_value_old = GetVideoFrameDelay();
878 SetVideoFrameDelay(frame_delay_value);
882 SetVideoFrameDelay(frame_delay_value_old);
885 static int fade_type_skip = FADE_TYPE_NONE;
887 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
889 void (*draw_border_function)(void) = NULL;
890 int x, y, width, height;
891 int fade_delay, post_delay;
893 if (fade_type == FADE_TYPE_FADE_OUT)
895 if (fade_type_skip != FADE_TYPE_NONE)
897 // skip all fade operations until specified fade operation
898 if (fade_type & fade_type_skip)
899 fade_type_skip = FADE_TYPE_NONE;
904 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
908 redraw_mask |= fade_mask;
910 if (fade_type == FADE_TYPE_SKIP)
912 fade_type_skip = fade_mode;
917 fade_delay = fading.fade_delay;
918 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
920 if (fade_type_skip != FADE_TYPE_NONE)
922 // skip all fade operations until specified fade operation
923 if (fade_type & fade_type_skip)
924 fade_type_skip = FADE_TYPE_NONE;
929 if (global.autoplay_leveldir)
934 if (fade_mask == REDRAW_FIELD)
939 height = FADE_SYSIZE;
941 if (border.draw_masked_when_fading)
942 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
944 DrawMaskedBorder_FIELD(); // draw once
954 // when switching screens without fading, set fade delay to zero
955 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
958 // do not display black frame when fading out without fade delay
959 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
962 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
963 draw_border_function);
965 redraw_mask &= ~fade_mask;
967 ClearAutoRepeatKeyEvents();
970 static void SetScreenStates_BeforeFadingIn(void)
972 // temporarily set screen mode for animations to screen after fading in
973 global.anim_status = global.anim_status_next;
975 // store backbuffer with all animations that will be started after fading in
976 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
978 // set screen mode for animations back to fading
979 global.anim_status = GAME_MODE_PSEUDO_FADING;
982 static void SetScreenStates_AfterFadingIn(void)
984 // store new source screen (to use correct masked border for fading)
985 gfx.fade_border_source_status = global.border_status;
987 global.anim_status = global.anim_status_next;
990 static void SetScreenStates_BeforeFadingOut(void)
992 // store new target screen (to use correct masked border for fading)
993 gfx.fade_border_target_status = game_status;
995 // set screen mode for animations to fading
996 global.anim_status = GAME_MODE_PSEUDO_FADING;
998 // store backbuffer with all animations that will be stopped for fading out
999 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1002 static void SetScreenStates_AfterFadingOut(void)
1004 global.border_status = game_status;
1007 void FadeIn(int fade_mask)
1009 SetScreenStates_BeforeFadingIn();
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1018 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1022 FADE_SXSIZE = FULL_SXSIZE;
1023 FADE_SYSIZE = FULL_SYSIZE;
1025 // activate virtual buttons depending on upcoming game status
1026 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1027 game_status == GAME_MODE_PLAYING && !tape.playing)
1028 SetOverlayActive(TRUE);
1030 SetScreenStates_AfterFadingIn();
1032 // force update of global animation status in case of rapid screen changes
1033 redraw_mask = REDRAW_ALL;
1037 void FadeOut(int fade_mask)
1039 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1040 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1041 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1044 SetScreenStates_BeforeFadingOut();
1046 SetTileCursorActive(FALSE);
1047 SetOverlayActive(FALSE);
1050 DrawMaskedBorder(REDRAW_ALL);
1053 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1054 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1056 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1058 SetScreenStates_AfterFadingOut();
1061 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1063 static struct TitleFadingInfo fading_leave_stored;
1066 fading_leave_stored = fading_leave;
1068 fading = fading_leave_stored;
1071 void FadeSetEnterMenu(void)
1073 fading = menu.enter_menu;
1075 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1078 void FadeSetLeaveMenu(void)
1080 fading = menu.leave_menu;
1082 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1085 void FadeSetEnterScreen(void)
1087 fading = menu.enter_screen[game_status];
1089 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1092 void FadeSetNextScreen(void)
1094 fading = menu.next_screen[game_status];
1096 // (do not overwrite fade mode set by FadeSetEnterScreen)
1097 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1100 void FadeSetLeaveScreen(void)
1102 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1105 void FadeSetFromType(int type)
1107 if (type & TYPE_ENTER_SCREEN)
1108 FadeSetEnterScreen();
1109 else if (type & TYPE_ENTER)
1111 else if (type & TYPE_LEAVE)
1115 void FadeSetDisabled(void)
1117 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1119 fading = fading_none;
1122 void FadeSkipNextFadeIn(void)
1124 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1127 void FadeSkipNextFadeOut(void)
1129 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1132 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1134 if (graphic == IMG_UNDEFINED)
1137 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1139 return (graphic_info[graphic].bitmap != NULL || redefined ?
1140 graphic_info[graphic].bitmap :
1141 graphic_info[default_graphic].bitmap);
1144 static Bitmap *getBackgroundBitmap(int graphic)
1146 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1149 static Bitmap *getGlobalBorderBitmap(int graphic)
1151 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1154 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1157 (status == GAME_MODE_MAIN ||
1158 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1159 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1160 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1161 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1164 return getGlobalBorderBitmap(graphic);
1167 void SetWindowBackgroundImageIfDefined(int graphic)
1169 if (graphic_info[graphic].bitmap)
1170 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1173 void SetMainBackgroundImageIfDefined(int graphic)
1175 if (graphic_info[graphic].bitmap)
1176 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1179 void SetDoorBackgroundImageIfDefined(int graphic)
1181 if (graphic_info[graphic].bitmap)
1182 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1185 void SetWindowBackgroundImage(int graphic)
1187 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1190 void SetMainBackgroundImage(int graphic)
1192 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1195 void SetDoorBackgroundImage(int graphic)
1197 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1200 void SetPanelBackground(void)
1202 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1204 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1205 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1207 SetDoorBackgroundBitmap(bitmap_db_panel);
1210 void DrawBackground(int x, int y, int width, int height)
1212 // "drawto" might still point to playfield buffer here (hall of fame)
1213 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1215 if (IN_GFX_FIELD_FULL(x, y))
1216 redraw_mask |= REDRAW_FIELD;
1217 else if (IN_GFX_DOOR_1(x, y))
1218 redraw_mask |= REDRAW_DOOR_1;
1219 else if (IN_GFX_DOOR_2(x, y))
1220 redraw_mask |= REDRAW_DOOR_2;
1221 else if (IN_GFX_DOOR_3(x, y))
1222 redraw_mask |= REDRAW_DOOR_3;
1225 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1227 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1229 if (font->bitmap == NULL)
1232 DrawBackground(x, y, width, height);
1235 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1237 struct GraphicInfo *g = &graphic_info[graphic];
1239 if (g->bitmap == NULL)
1242 DrawBackground(x, y, width, height);
1245 static int game_status_last = -1;
1246 static Bitmap *global_border_bitmap_last = NULL;
1247 static Bitmap *global_border_bitmap = NULL;
1248 static int real_sx_last = -1, real_sy_last = -1;
1249 static int full_sxsize_last = -1, full_sysize_last = -1;
1250 static int dx_last = -1, dy_last = -1;
1251 static int dxsize_last = -1, dysize_last = -1;
1252 static int vx_last = -1, vy_last = -1;
1253 static int vxsize_last = -1, vysize_last = -1;
1254 static int ex_last = -1, ey_last = -1;
1255 static int exsize_last = -1, eysize_last = -1;
1257 boolean CheckIfGlobalBorderHasChanged(void)
1259 // if game status has not changed, global border has not changed either
1260 if (game_status == game_status_last)
1263 // determine and store new global border bitmap for current game status
1264 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1266 return (global_border_bitmap_last != global_border_bitmap);
1269 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1271 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1272 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1274 // if game status has not changed, nothing has to be redrawn
1275 if (game_status == game_status_last)
1278 // redraw if last screen was title screen
1279 if (game_status_last == GAME_MODE_TITLE)
1282 // redraw if global screen border has changed
1283 if (CheckIfGlobalBorderHasChanged())
1286 // redraw if position or size of playfield area has changed
1287 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1288 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1291 // redraw if position or size of door area has changed
1292 if (dx_last != DX || dy_last != DY ||
1293 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1296 // redraw if position or size of tape area has changed
1297 if (vx_last != VX || vy_last != VY ||
1298 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1301 // redraw if position or size of editor area has changed
1302 if (ex_last != EX || ey_last != EY ||
1303 exsize_last != EXSIZE || eysize_last != EYSIZE)
1310 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1313 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1315 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1318 void RedrawGlobalBorder(void)
1320 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1322 RedrawGlobalBorderFromBitmap(bitmap);
1324 redraw_mask = REDRAW_ALL;
1327 static void RedrawGlobalBorderIfNeeded(void)
1329 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1330 if (game_status == game_status_last)
1334 // copy current draw buffer to later copy back areas that have not changed
1335 if (game_status_last != GAME_MODE_TITLE)
1336 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1338 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1339 if (CheckIfGlobalBorderRedrawIsNeeded())
1341 // determine and store new global border bitmap for current game status
1342 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1345 // redraw global screen border (or clear, if defined to be empty)
1346 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1348 if (game_status == GAME_MODE_EDITOR)
1349 DrawSpecialEditorDoor();
1351 // copy previous playfield and door areas, if they are defined on both
1352 // previous and current screen and if they still have the same size
1354 if (real_sx_last != -1 && real_sy_last != -1 &&
1355 REAL_SX != -1 && REAL_SY != -1 &&
1356 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1357 BlitBitmap(bitmap_db_store_1, backbuffer,
1358 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1361 if (dx_last != -1 && dy_last != -1 &&
1362 DX != -1 && DY != -1 &&
1363 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1364 BlitBitmap(bitmap_db_store_1, backbuffer,
1365 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1367 if (game_status != GAME_MODE_EDITOR)
1369 if (vx_last != -1 && vy_last != -1 &&
1370 VX != -1 && VY != -1 &&
1371 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1377 if (ex_last != -1 && ey_last != -1 &&
1378 EX != -1 && EY != -1 &&
1379 exsize_last == EXSIZE && eysize_last == EYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1384 redraw_mask = REDRAW_ALL;
1387 game_status_last = game_status;
1389 global_border_bitmap_last = global_border_bitmap;
1391 real_sx_last = REAL_SX;
1392 real_sy_last = REAL_SY;
1393 full_sxsize_last = FULL_SXSIZE;
1394 full_sysize_last = FULL_SYSIZE;
1397 dxsize_last = DXSIZE;
1398 dysize_last = DYSIZE;
1401 vxsize_last = VXSIZE;
1402 vysize_last = VYSIZE;
1405 exsize_last = EXSIZE;
1406 eysize_last = EYSIZE;
1409 void ClearField(void)
1411 RedrawGlobalBorderIfNeeded();
1413 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1414 // (when entering hall of fame after playing)
1415 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1417 // !!! maybe this should be done before clearing the background !!!
1418 if (game_status == GAME_MODE_PLAYING)
1420 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1421 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1425 SetDrawtoField(DRAW_TO_BACKBUFFER);
1429 void MarkTileDirty(int x, int y)
1431 redraw_mask |= REDRAW_FIELD;
1434 void SetBorderElement(void)
1438 BorderElement = EL_EMPTY;
1440 // only the R'n'D game engine may use an additional steelwall border
1441 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1444 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1446 for (x = 0; x < lev_fieldx; x++)
1448 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1449 BorderElement = EL_STEELWALL;
1451 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1457 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1458 int max_array_fieldx, int max_array_fieldy,
1459 short field[max_array_fieldx][max_array_fieldy],
1460 int max_fieldx, int max_fieldy)
1464 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1465 static int safety = 0;
1467 // check if starting field still has the desired content
1468 if (field[from_x][from_y] == fill_element)
1473 if (safety > max_fieldx * max_fieldy)
1474 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1476 old_element = field[from_x][from_y];
1477 field[from_x][from_y] = fill_element;
1479 for (i = 0; i < 4; i++)
1481 x = from_x + check[i][0];
1482 y = from_y + check[i][1];
1484 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1485 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1486 field, max_fieldx, max_fieldy);
1492 void FloodFillLevel(int from_x, int from_y, int fill_element,
1493 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1494 int max_fieldx, int max_fieldy)
1496 FloodFillLevelExt(from_x, from_y, fill_element,
1497 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1498 max_fieldx, max_fieldy);
1501 void SetRandomAnimationValue(int x, int y)
1503 gfx.anim_random_frame = GfxRandom[x][y];
1506 int getGraphicAnimationFrame(int graphic, int sync_frame)
1508 // animation synchronized with global frame counter, not move position
1509 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1510 sync_frame = FrameCounter;
1512 return getAnimationFrame(graphic_info[graphic].anim_frames,
1513 graphic_info[graphic].anim_delay,
1514 graphic_info[graphic].anim_mode,
1515 graphic_info[graphic].anim_start_frame,
1519 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1521 struct GraphicInfo *g = &graphic_info[graphic];
1522 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1524 if (tilesize == gfx.standard_tile_size)
1525 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1526 else if (tilesize == game.tile_size)
1527 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1529 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1532 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1533 boolean get_backside)
1535 struct GraphicInfo *g = &graphic_info[graphic];
1536 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1537 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1539 if (g->offset_y == 0) // frames are ordered horizontally
1541 int max_width = g->anim_frames_per_line * g->width;
1542 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1544 *x = pos % max_width;
1545 *y = src_y % g->height + pos / max_width * g->height;
1547 else if (g->offset_x == 0) // frames are ordered vertically
1549 int max_height = g->anim_frames_per_line * g->height;
1550 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1552 *x = src_x % g->width + pos / max_height * g->width;
1553 *y = pos % max_height;
1555 else // frames are ordered diagonally
1557 *x = src_x + frame * g->offset_x;
1558 *y = src_y + frame * g->offset_y;
1562 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1563 Bitmap **bitmap, int *x, int *y,
1564 boolean get_backside)
1566 struct GraphicInfo *g = &graphic_info[graphic];
1568 // if no graphics defined at all, use fallback graphics
1569 if (g->bitmaps == NULL)
1570 *g = graphic_info[IMG_CHAR_EXCLAM];
1572 // if no in-game graphics defined, always use standard graphic size
1573 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1574 tilesize = TILESIZE;
1576 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1577 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1579 *x = *x * tilesize / g->tile_size;
1580 *y = *y * tilesize / g->tile_size;
1583 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1584 Bitmap **bitmap, int *x, int *y)
1586 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1589 void getFixedGraphicSource(int graphic, int frame,
1590 Bitmap **bitmap, int *x, int *y)
1592 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1595 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1597 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1600 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1601 int *x, int *y, boolean get_backside)
1603 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1607 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1609 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1612 void DrawGraphic(int x, int y, int graphic, int frame)
1615 if (!IN_SCR_FIELD(x, y))
1617 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1618 Debug("draw:DrawGraphic", "This should never happen!");
1624 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1627 MarkTileDirty(x, y);
1630 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1633 if (!IN_SCR_FIELD(x, y))
1635 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1637 Debug("draw:DrawFixedGraphic", "This should never happen!");
1643 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1645 MarkTileDirty(x, y);
1648 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1654 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1656 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1659 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1665 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1666 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1669 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1672 if (!IN_SCR_FIELD(x, y))
1674 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1676 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1682 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1685 MarkTileDirty(x, y);
1688 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1691 if (!IN_SCR_FIELD(x, y))
1693 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1695 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1701 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1703 MarkTileDirty(x, y);
1706 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1712 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1714 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1718 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1719 int graphic, int frame)
1724 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1726 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1730 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1732 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1734 MarkTileDirty(x / tilesize, y / tilesize);
1737 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1740 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1741 graphic, frame, tilesize);
1742 MarkTileDirty(x / tilesize, y / tilesize);
1745 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1751 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1752 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1755 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1756 int frame, int tilesize)
1761 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1762 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1765 void DrawMiniGraphic(int x, int y, int graphic)
1767 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1768 MarkTileDirty(x / 2, y / 2);
1771 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1776 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1777 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1780 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1781 int graphic, int frame,
1782 int cut_mode, int mask_mode)
1787 int width = TILEX, height = TILEY;
1790 if (dx || dy) // shifted graphic
1792 if (x < BX1) // object enters playfield from the left
1799 else if (x > BX2) // object enters playfield from the right
1805 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1811 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1813 else if (dx) // general horizontal movement
1814 MarkTileDirty(x + SIGN(dx), y);
1816 if (y < BY1) // object enters playfield from the top
1818 if (cut_mode == CUT_BELOW) // object completely above top border
1826 else if (y > BY2) // object enters playfield from the bottom
1832 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1838 else if (dy > 0 && cut_mode == CUT_ABOVE)
1840 if (y == BY2) // object completely above bottom border
1846 MarkTileDirty(x, y + 1);
1847 } // object leaves playfield to the bottom
1848 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1850 else if (dy) // general vertical movement
1851 MarkTileDirty(x, y + SIGN(dy));
1855 if (!IN_SCR_FIELD(x, y))
1857 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1859 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1865 width = width * TILESIZE_VAR / TILESIZE;
1866 height = height * TILESIZE_VAR / TILESIZE;
1867 cx = cx * TILESIZE_VAR / TILESIZE;
1868 cy = cy * TILESIZE_VAR / TILESIZE;
1869 dx = dx * TILESIZE_VAR / TILESIZE;
1870 dy = dy * TILESIZE_VAR / TILESIZE;
1872 if (width > 0 && height > 0)
1874 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1879 dst_x = FX + x * TILEX_VAR + dx;
1880 dst_y = FY + y * TILEY_VAR + dy;
1882 if (mask_mode == USE_MASKING)
1883 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1886 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1889 MarkTileDirty(x, y);
1893 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1894 int graphic, int frame,
1895 int cut_mode, int mask_mode)
1900 int width = TILEX_VAR, height = TILEY_VAR;
1903 int x2 = x + SIGN(dx);
1904 int y2 = y + SIGN(dy);
1906 // movement with two-tile animations must be sync'ed with movement position,
1907 // not with current GfxFrame (which can be higher when using slow movement)
1908 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1909 int anim_frames = graphic_info[graphic].anim_frames;
1911 // (we also need anim_delay here for movement animations with less frames)
1912 int anim_delay = graphic_info[graphic].anim_delay;
1913 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1915 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1916 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1918 // re-calculate animation frame for two-tile movement animation
1919 frame = getGraphicAnimationFrame(graphic, sync_frame);
1921 // check if movement start graphic inside screen area and should be drawn
1922 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1924 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1926 dst_x = FX + x1 * TILEX_VAR;
1927 dst_y = FY + y1 * TILEY_VAR;
1929 if (mask_mode == USE_MASKING)
1930 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1933 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1936 MarkTileDirty(x1, y1);
1939 // check if movement end graphic inside screen area and should be drawn
1940 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1942 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1944 dst_x = FX + x2 * TILEX_VAR;
1945 dst_y = FY + y2 * TILEY_VAR;
1947 if (mask_mode == USE_MASKING)
1948 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 MarkTileDirty(x2, y2);
1958 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1959 int graphic, int frame,
1960 int cut_mode, int mask_mode)
1964 DrawGraphic(x, y, graphic, frame);
1969 if (graphic_info[graphic].double_movement) // EM style movement images
1970 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1972 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1975 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1976 int graphic, int frame, int cut_mode)
1978 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1981 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1982 int cut_mode, int mask_mode)
1984 int lx = LEVELX(x), ly = LEVELY(y);
1988 if (IN_LEV_FIELD(lx, ly))
1990 SetRandomAnimationValue(lx, ly);
1992 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1993 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1995 // do not use double (EM style) movement graphic when not moving
1996 if (graphic_info[graphic].double_movement && !dx && !dy)
1998 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1999 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2002 else // border element
2004 graphic = el2img(element);
2005 frame = getGraphicAnimationFrame(graphic, -1);
2008 if (element == EL_EXPANDABLE_WALL)
2010 boolean left_stopped = FALSE, right_stopped = FALSE;
2012 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2013 left_stopped = TRUE;
2014 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2015 right_stopped = TRUE;
2017 if (left_stopped && right_stopped)
2019 else if (left_stopped)
2021 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2022 frame = graphic_info[graphic].anim_frames - 1;
2024 else if (right_stopped)
2026 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2027 frame = graphic_info[graphic].anim_frames - 1;
2032 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2033 else if (mask_mode == USE_MASKING)
2034 DrawGraphicThruMask(x, y, graphic, frame);
2036 DrawGraphic(x, y, graphic, frame);
2039 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2040 int cut_mode, int mask_mode)
2042 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2043 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2044 cut_mode, mask_mode);
2047 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2050 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2053 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2056 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2059 void DrawLevelElementThruMask(int x, int y, int element)
2061 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2064 void DrawLevelFieldThruMask(int x, int y)
2066 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2069 // !!! implementation of quicksand is totally broken !!!
2070 #define IS_CRUMBLED_TILE(x, y, e) \
2071 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2072 !IS_MOVING(x, y) || \
2073 (e) == EL_QUICKSAND_EMPTYING || \
2074 (e) == EL_QUICKSAND_FAST_EMPTYING))
2076 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2081 int width, height, cx, cy;
2082 int sx = SCREENX(x), sy = SCREENY(y);
2083 int crumbled_border_size = graphic_info[graphic].border_size;
2084 int crumbled_tile_size = graphic_info[graphic].tile_size;
2085 int crumbled_border_size_var =
2086 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2089 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2091 for (i = 1; i < 4; i++)
2093 int dxx = (i & 1 ? dx : 0);
2094 int dyy = (i & 2 ? dy : 0);
2097 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2100 // check if neighbour field is of same crumble type
2101 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2102 graphic_info[graphic].class ==
2103 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2105 // return if check prevents inner corner
2106 if (same == (dxx == dx && dyy == dy))
2110 // if we reach this point, we have an inner corner
2112 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2114 width = crumbled_border_size_var;
2115 height = crumbled_border_size_var;
2116 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2117 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2119 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2120 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2123 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2128 int width, height, bx, by, cx, cy;
2129 int sx = SCREENX(x), sy = SCREENY(y);
2130 int crumbled_border_size = graphic_info[graphic].border_size;
2131 int crumbled_tile_size = graphic_info[graphic].tile_size;
2132 int crumbled_border_size_var =
2133 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2134 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2137 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2139 // draw simple, sloppy, non-corner-accurate crumbled border
2141 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2142 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2143 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2144 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2146 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2147 FX + sx * TILEX_VAR + cx,
2148 FY + sy * TILEY_VAR + cy);
2150 // (remaining middle border part must be at least as big as corner part)
2151 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2152 crumbled_border_size_var >= TILESIZE_VAR / 3)
2155 // correct corners of crumbled border, if needed
2157 for (i = -1; i <= 1; i += 2)
2159 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2160 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2161 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2164 // check if neighbour field is of same crumble type
2165 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2166 graphic_info[graphic].class ==
2167 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2169 // no crumbled corner, but continued crumbled border
2171 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2172 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2173 int b1 = (i == 1 ? crumbled_border_size_var :
2174 TILESIZE_VAR - 2 * crumbled_border_size_var);
2176 width = crumbled_border_size_var;
2177 height = crumbled_border_size_var;
2179 if (dir == 1 || dir == 2)
2194 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2196 FX + sx * TILEX_VAR + cx,
2197 FY + sy * TILEY_VAR + cy);
2202 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2204 int sx = SCREENX(x), sy = SCREENY(y);
2207 static int xy[4][2] =
2215 if (!IN_LEV_FIELD(x, y))
2218 element = TILE_GFX_ELEMENT(x, y);
2220 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2222 if (!IN_SCR_FIELD(sx, sy))
2225 // crumble field borders towards direct neighbour fields
2226 for (i = 0; i < 4; i++)
2228 int xx = x + xy[i][0];
2229 int yy = y + xy[i][1];
2231 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2234 // check if neighbour field is of same crumble type
2235 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2236 graphic_info[graphic].class ==
2237 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2240 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2243 // crumble inner field corners towards corner neighbour fields
2244 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2245 graphic_info[graphic].anim_frames == 2)
2247 for (i = 0; i < 4; i++)
2249 int dx = (i & 1 ? +1 : -1);
2250 int dy = (i & 2 ? +1 : -1);
2252 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2256 MarkTileDirty(sx, sy);
2258 else // center field is not crumbled -- crumble neighbour fields
2260 // crumble field borders of direct neighbour fields
2261 for (i = 0; i < 4; i++)
2263 int xx = x + xy[i][0];
2264 int yy = y + xy[i][1];
2265 int sxx = sx + xy[i][0];
2266 int syy = sy + xy[i][1];
2268 if (!IN_LEV_FIELD(xx, yy) ||
2269 !IN_SCR_FIELD(sxx, syy))
2272 // do not crumble fields that are being digged or snapped
2273 if (Tile[xx][yy] == EL_EMPTY ||
2274 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2277 element = TILE_GFX_ELEMENT(xx, yy);
2279 if (!IS_CRUMBLED_TILE(xx, yy, element))
2282 graphic = el_act2crm(element, ACTION_DEFAULT);
2284 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2286 MarkTileDirty(sxx, syy);
2289 // crumble inner field corners of corner neighbour fields
2290 for (i = 0; i < 4; i++)
2292 int dx = (i & 1 ? +1 : -1);
2293 int dy = (i & 2 ? +1 : -1);
2299 if (!IN_LEV_FIELD(xx, yy) ||
2300 !IN_SCR_FIELD(sxx, syy))
2303 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2306 element = TILE_GFX_ELEMENT(xx, yy);
2308 if (!IS_CRUMBLED_TILE(xx, yy, element))
2311 graphic = el_act2crm(element, ACTION_DEFAULT);
2313 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2314 graphic_info[graphic].anim_frames == 2)
2315 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2317 MarkTileDirty(sxx, syy);
2322 void DrawLevelFieldCrumbled(int x, int y)
2326 if (!IN_LEV_FIELD(x, y))
2329 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2330 GfxElement[x][y] != EL_UNDEFINED &&
2331 GFX_CRUMBLED(GfxElement[x][y]))
2333 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2338 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2340 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2343 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2346 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2347 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2348 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2349 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2350 int sx = SCREENX(x), sy = SCREENY(y);
2352 DrawGraphic(sx, sy, graphic1, frame1);
2353 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2356 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2358 int sx = SCREENX(x), sy = SCREENY(y);
2359 static int xy[4][2] =
2368 // crumble direct neighbour fields (required for field borders)
2369 for (i = 0; i < 4; i++)
2371 int xx = x + xy[i][0];
2372 int yy = y + xy[i][1];
2373 int sxx = sx + xy[i][0];
2374 int syy = sy + xy[i][1];
2376 if (!IN_LEV_FIELD(xx, yy) ||
2377 !IN_SCR_FIELD(sxx, syy) ||
2378 !GFX_CRUMBLED(Tile[xx][yy]) ||
2382 DrawLevelField(xx, yy);
2385 // crumble corner neighbour fields (required for inner field corners)
2386 for (i = 0; i < 4; i++)
2388 int dx = (i & 1 ? +1 : -1);
2389 int dy = (i & 2 ? +1 : -1);
2395 if (!IN_LEV_FIELD(xx, yy) ||
2396 !IN_SCR_FIELD(sxx, syy) ||
2397 !GFX_CRUMBLED(Tile[xx][yy]) ||
2401 int element = TILE_GFX_ELEMENT(xx, yy);
2402 int graphic = el_act2crm(element, ACTION_DEFAULT);
2404 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2405 graphic_info[graphic].anim_frames == 2)
2406 DrawLevelField(xx, yy);
2410 static int getBorderElement(int x, int y)
2414 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2415 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2416 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2417 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2418 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2419 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2420 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2422 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2423 int steel_position = (x == -1 && y == -1 ? 0 :
2424 x == lev_fieldx && y == -1 ? 1 :
2425 x == -1 && y == lev_fieldy ? 2 :
2426 x == lev_fieldx && y == lev_fieldy ? 3 :
2427 x == -1 || x == lev_fieldx ? 4 :
2428 y == -1 || y == lev_fieldy ? 5 : 6);
2430 return border[steel_position][steel_type];
2433 void DrawScreenElement(int x, int y, int element)
2435 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2436 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2439 void DrawLevelElement(int x, int y, int element)
2441 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2442 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2445 void DrawScreenField(int x, int y)
2447 int lx = LEVELX(x), ly = LEVELY(y);
2448 int element, content;
2450 if (!IN_LEV_FIELD(lx, ly))
2452 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2455 element = getBorderElement(lx, ly);
2457 DrawScreenElement(x, y, element);
2462 element = Tile[lx][ly];
2463 content = Store[lx][ly];
2465 if (IS_MOVING(lx, ly))
2467 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2468 boolean cut_mode = NO_CUTTING;
2470 if (element == EL_QUICKSAND_EMPTYING ||
2471 element == EL_QUICKSAND_FAST_EMPTYING ||
2472 element == EL_MAGIC_WALL_EMPTYING ||
2473 element == EL_BD_MAGIC_WALL_EMPTYING ||
2474 element == EL_DC_MAGIC_WALL_EMPTYING ||
2475 element == EL_AMOEBA_DROPPING)
2476 cut_mode = CUT_ABOVE;
2477 else if (element == EL_QUICKSAND_FILLING ||
2478 element == EL_QUICKSAND_FAST_FILLING ||
2479 element == EL_MAGIC_WALL_FILLING ||
2480 element == EL_BD_MAGIC_WALL_FILLING ||
2481 element == EL_DC_MAGIC_WALL_FILLING)
2482 cut_mode = CUT_BELOW;
2484 if (cut_mode == CUT_ABOVE)
2485 DrawScreenElement(x, y, element);
2487 DrawScreenElement(x, y, EL_EMPTY);
2490 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2491 else if (cut_mode == NO_CUTTING)
2492 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2495 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2497 if (cut_mode == CUT_BELOW &&
2498 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2499 DrawLevelElement(lx, ly + 1, element);
2502 if (content == EL_ACID)
2504 int dir = MovDir[lx][ly];
2505 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2506 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2508 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2510 // prevent target field from being drawn again (but without masking)
2511 // (this would happen if target field is scanned after moving element)
2512 Stop[newlx][newly] = TRUE;
2515 else if (IS_BLOCKED(lx, ly))
2520 boolean cut_mode = NO_CUTTING;
2521 int element_old, content_old;
2523 Blocked2Moving(lx, ly, &oldx, &oldy);
2526 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2527 MovDir[oldx][oldy] == MV_RIGHT);
2529 element_old = Tile[oldx][oldy];
2530 content_old = Store[oldx][oldy];
2532 if (element_old == EL_QUICKSAND_EMPTYING ||
2533 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2534 element_old == EL_MAGIC_WALL_EMPTYING ||
2535 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2536 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2537 element_old == EL_AMOEBA_DROPPING)
2538 cut_mode = CUT_ABOVE;
2540 DrawScreenElement(x, y, EL_EMPTY);
2543 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2545 else if (cut_mode == NO_CUTTING)
2546 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2549 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2552 else if (IS_DRAWABLE(element))
2553 DrawScreenElement(x, y, element);
2555 DrawScreenElement(x, y, EL_EMPTY);
2558 void DrawLevelField(int x, int y)
2560 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2561 DrawScreenField(SCREENX(x), SCREENY(y));
2562 else if (IS_MOVING(x, y))
2566 Moving2Blocked(x, y, &newx, &newy);
2567 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2568 DrawScreenField(SCREENX(newx), SCREENY(newy));
2570 else if (IS_BLOCKED(x, y))
2574 Blocked2Moving(x, y, &oldx, &oldy);
2575 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2576 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2580 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2581 int (*el2img_function)(int), boolean masked,
2582 int element_bits_draw)
2584 int element_base = map_mm_wall_element(element);
2585 int element_bits = (IS_DF_WALL(element) ?
2586 element - EL_DF_WALL_START :
2587 IS_MM_WALL(element) ?
2588 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2589 int graphic = el2img_function(element_base);
2590 int tilesize_draw = tilesize / 2;
2595 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2597 for (i = 0; i < 4; i++)
2599 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2600 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2602 if (!(element_bits_draw & (1 << i)))
2605 if (element_bits & (1 << i))
2608 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2609 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2611 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2612 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2617 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2618 tilesize_draw, tilesize_draw);
2623 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2624 boolean masked, int element_bits_draw)
2626 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2627 element, tilesize, el2edimg, masked, element_bits_draw);
2630 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2631 int (*el2img_function)(int))
2633 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2637 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2640 if (IS_MM_WALL(element))
2642 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2643 element, tilesize, el2edimg, masked, 0x000f);
2647 int graphic = el2edimg(element);
2650 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2652 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2656 void DrawSizedElement(int x, int y, int element, int tilesize)
2658 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2661 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2663 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2666 void DrawMiniElement(int x, int y, int element)
2670 graphic = el2edimg(element);
2671 DrawMiniGraphic(x, y, graphic);
2674 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2677 int x = sx + scroll_x, y = sy + scroll_y;
2679 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2680 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2681 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2682 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2684 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2687 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2689 int x = sx + scroll_x, y = sy + scroll_y;
2691 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2692 DrawMiniElement(sx, sy, EL_EMPTY);
2693 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2694 DrawMiniElement(sx, sy, Tile[x][y]);
2696 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2699 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2700 int x, int y, int xsize, int ysize,
2701 int tile_width, int tile_height)
2705 int dst_x = startx + x * tile_width;
2706 int dst_y = starty + y * tile_height;
2707 int width = graphic_info[graphic].width;
2708 int height = graphic_info[graphic].height;
2709 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2710 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2711 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2712 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2713 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2714 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2715 boolean draw_masked = graphic_info[graphic].draw_masked;
2717 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2719 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2721 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2725 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2726 inner_sx + (x - 1) * tile_width % inner_width);
2727 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2728 inner_sy + (y - 1) * tile_height % inner_height);
2731 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2734 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2738 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2739 int x, int y, int xsize, int ysize,
2742 int font_width = getFontWidth(font_nr);
2743 int font_height = getFontHeight(font_nr);
2745 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2746 font_width, font_height);
2749 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2751 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2752 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2753 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2754 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2755 boolean no_delay = (tape.warp_forward);
2756 unsigned int anim_delay = 0;
2757 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2758 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2759 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2760 int font_width = getFontWidth(font_nr);
2761 int font_height = getFontHeight(font_nr);
2762 int max_xsize = level.envelope[envelope_nr].xsize;
2763 int max_ysize = level.envelope[envelope_nr].ysize;
2764 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2765 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2766 int xend = max_xsize;
2767 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2768 int xstep = (xstart < xend ? 1 : 0);
2769 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2771 int end = MAX(xend - xstart, yend - ystart);
2774 for (i = start; i <= end; i++)
2776 int last_frame = end; // last frame of this "for" loop
2777 int x = xstart + i * xstep;
2778 int y = ystart + i * ystep;
2779 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2780 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2781 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2782 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2785 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2787 BlitScreenToBitmap(backbuffer);
2789 SetDrawtoField(DRAW_TO_BACKBUFFER);
2791 for (yy = 0; yy < ysize; yy++)
2792 for (xx = 0; xx < xsize; xx++)
2793 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2795 DrawTextBuffer(sx + font_width, sy + font_height,
2796 level.envelope[envelope_nr].text, font_nr, max_xsize,
2797 xsize - 2, ysize - 2, 0, mask_mode,
2798 level.envelope[envelope_nr].autowrap,
2799 level.envelope[envelope_nr].centered, FALSE);
2801 redraw_mask |= REDRAW_FIELD;
2804 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2807 ClearAutoRepeatKeyEvents();
2810 void ShowEnvelope(int envelope_nr)
2812 int element = EL_ENVELOPE_1 + envelope_nr;
2813 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2814 int sound_opening = element_info[element].sound[ACTION_OPENING];
2815 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2816 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2817 boolean no_delay = (tape.warp_forward);
2818 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2819 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2820 int anim_mode = graphic_info[graphic].anim_mode;
2821 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2822 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2823 boolean overlay_enabled = GetOverlayEnabled();
2825 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2827 SetOverlayEnabled(FALSE);
2830 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2832 if (anim_mode == ANIM_DEFAULT)
2833 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2835 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2838 Delay_WithScreenUpdates(wait_delay_value);
2840 WaitForEventToContinue();
2843 SetOverlayEnabled(overlay_enabled);
2845 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2847 if (anim_mode != ANIM_NONE)
2848 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2850 if (anim_mode == ANIM_DEFAULT)
2851 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2853 game.envelope_active = FALSE;
2855 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2857 redraw_mask |= REDRAW_FIELD;
2861 static void setRequestBasePosition(int *x, int *y)
2863 int sx_base, sy_base;
2865 if (request.x != -1)
2866 sx_base = request.x;
2867 else if (request.align == ALIGN_LEFT)
2869 else if (request.align == ALIGN_RIGHT)
2870 sx_base = SX + SXSIZE;
2872 sx_base = SX + SXSIZE / 2;
2874 if (request.y != -1)
2875 sy_base = request.y;
2876 else if (request.valign == VALIGN_TOP)
2878 else if (request.valign == VALIGN_BOTTOM)
2879 sy_base = SY + SYSIZE;
2881 sy_base = SY + SYSIZE / 2;
2887 static void setRequestPositionExt(int *x, int *y, int width, int height,
2888 boolean add_border_size)
2890 int border_size = request.border_size;
2891 int sx_base, sy_base;
2894 setRequestBasePosition(&sx_base, &sy_base);
2896 if (request.align == ALIGN_LEFT)
2898 else if (request.align == ALIGN_RIGHT)
2899 sx = sx_base - width;
2901 sx = sx_base - width / 2;
2903 if (request.valign == VALIGN_TOP)
2905 else if (request.valign == VALIGN_BOTTOM)
2906 sy = sy_base - height;
2908 sy = sy_base - height / 2;
2910 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2911 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2913 if (add_border_size)
2923 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2925 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2928 static void DrawEnvelopeRequest(char *text)
2930 char *text_final = text;
2931 char *text_door_style = NULL;
2932 int graphic = IMG_BACKGROUND_REQUEST;
2933 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2934 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2935 int font_nr = FONT_REQUEST;
2936 int font_width = getFontWidth(font_nr);
2937 int font_height = getFontHeight(font_nr);
2938 int border_size = request.border_size;
2939 int line_spacing = request.line_spacing;
2940 int line_height = font_height + line_spacing;
2941 int max_text_width = request.width - 2 * border_size;
2942 int max_text_height = request.height - 2 * border_size;
2943 int line_length = max_text_width / font_width;
2944 int max_lines = max_text_height / line_height;
2945 int text_width = line_length * font_width;
2946 int width = request.width;
2947 int height = request.height;
2948 int tile_size = MAX(request.step_offset, 1);
2949 int x_steps = width / tile_size;
2950 int y_steps = height / tile_size;
2951 int sx_offset = border_size;
2952 int sy_offset = border_size;
2956 if (request.centered)
2957 sx_offset = (request.width - text_width) / 2;
2959 if (request.wrap_single_words && !request.autowrap)
2961 char *src_text_ptr, *dst_text_ptr;
2963 text_door_style = checked_malloc(2 * strlen(text) + 1);
2965 src_text_ptr = text;
2966 dst_text_ptr = text_door_style;
2968 while (*src_text_ptr)
2970 if (*src_text_ptr == ' ' ||
2971 *src_text_ptr == '?' ||
2972 *src_text_ptr == '!')
2973 *dst_text_ptr++ = '\n';
2975 if (*src_text_ptr != ' ')
2976 *dst_text_ptr++ = *src_text_ptr;
2981 *dst_text_ptr = '\0';
2983 text_final = text_door_style;
2986 setRequestPosition(&sx, &sy, FALSE);
2988 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2990 for (y = 0; y < y_steps; y++)
2991 for (x = 0; x < x_steps; x++)
2992 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2993 x, y, x_steps, y_steps,
2994 tile_size, tile_size);
2996 // force DOOR font inside door area
2997 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2999 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3000 line_length, -1, max_lines, line_spacing, mask_mode,
3001 request.autowrap, request.centered, FALSE);
3005 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3006 RedrawGadget(tool_gadget[i]);
3008 // store readily prepared envelope request for later use when animating
3009 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3011 if (text_door_style)
3012 free(text_door_style);
3015 static void AnimateEnvelopeRequest(int anim_mode, int action)
3017 int graphic = IMG_BACKGROUND_REQUEST;
3018 boolean draw_masked = graphic_info[graphic].draw_masked;
3019 int delay_value_normal = request.step_delay;
3020 int delay_value_fast = delay_value_normal / 2;
3021 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3022 boolean no_delay = (tape.warp_forward);
3023 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3024 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3025 unsigned int anim_delay = 0;
3027 int tile_size = MAX(request.step_offset, 1);
3028 int max_xsize = request.width / tile_size;
3029 int max_ysize = request.height / tile_size;
3030 int max_xsize_inner = max_xsize - 2;
3031 int max_ysize_inner = max_ysize - 2;
3033 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3034 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3035 int xend = max_xsize_inner;
3036 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3037 int xstep = (xstart < xend ? 1 : 0);
3038 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3040 int end = MAX(xend - xstart, yend - ystart);
3043 if (setup.quick_doors)
3050 for (i = start; i <= end; i++)
3052 int last_frame = end; // last frame of this "for" loop
3053 int x = xstart + i * xstep;
3054 int y = ystart + i * ystep;
3055 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3056 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3057 int xsize_size_left = (xsize - 1) * tile_size;
3058 int ysize_size_top = (ysize - 1) * tile_size;
3059 int max_xsize_pos = (max_xsize - 1) * tile_size;
3060 int max_ysize_pos = (max_ysize - 1) * tile_size;
3061 int width = xsize * tile_size;
3062 int height = ysize * tile_size;
3067 setRequestPosition(&src_x, &src_y, FALSE);
3068 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3070 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3072 for (yy = 0; yy < 2; yy++)
3074 for (xx = 0; xx < 2; xx++)
3076 int src_xx = src_x + xx * max_xsize_pos;
3077 int src_yy = src_y + yy * max_ysize_pos;
3078 int dst_xx = dst_x + xx * xsize_size_left;
3079 int dst_yy = dst_y + yy * ysize_size_top;
3080 int xx_size = (xx ? tile_size : xsize_size_left);
3081 int yy_size = (yy ? tile_size : ysize_size_top);
3084 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3085 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3087 BlitBitmap(bitmap_db_store_2, backbuffer,
3088 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3092 redraw_mask |= REDRAW_FIELD;
3096 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3099 ClearAutoRepeatKeyEvents();
3102 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3104 int graphic = IMG_BACKGROUND_REQUEST;
3105 int sound_opening = SND_REQUEST_OPENING;
3106 int sound_closing = SND_REQUEST_CLOSING;
3107 int anim_mode_1 = request.anim_mode; // (higher priority)
3108 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3109 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3110 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3111 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3113 if (game_status == GAME_MODE_PLAYING)
3114 BlitScreenToBitmap(backbuffer);
3116 SetDrawtoField(DRAW_TO_BACKBUFFER);
3118 // SetDrawBackgroundMask(REDRAW_NONE);
3120 if (action == ACTION_OPENING)
3122 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3124 if (req_state & REQ_ASK)
3126 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3131 else if (req_state & REQ_CONFIRM)
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3136 else if (req_state & REQ_PLAYER)
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3140 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3141 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3144 DrawEnvelopeRequest(text);
3147 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3149 if (action == ACTION_OPENING)
3151 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3153 if (anim_mode == ANIM_DEFAULT)
3154 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3156 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3160 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3162 if (anim_mode != ANIM_NONE)
3163 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3165 if (anim_mode == ANIM_DEFAULT)
3166 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3169 game.envelope_active = FALSE;
3171 if (action == ACTION_CLOSING)
3172 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3174 // SetDrawBackgroundMask(last_draw_background_mask);
3176 redraw_mask |= REDRAW_FIELD;
3180 if (action == ACTION_CLOSING &&
3181 game_status == GAME_MODE_PLAYING &&
3182 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3183 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3186 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3188 if (IS_MM_WALL(element))
3190 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3196 int graphic = el2preimg(element);
3198 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3199 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3204 void DrawLevel(int draw_background_mask)
3208 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3209 SetDrawBackgroundMask(draw_background_mask);
3213 for (x = BX1; x <= BX2; x++)
3214 for (y = BY1; y <= BY2; y++)
3215 DrawScreenField(x, y);
3217 redraw_mask |= REDRAW_FIELD;
3220 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3225 for (x = 0; x < size_x; x++)
3226 for (y = 0; y < size_y; y++)
3227 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3229 redraw_mask |= REDRAW_FIELD;
3232 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3236 for (x = 0; x < size_x; x++)
3237 for (y = 0; y < size_y; y++)
3238 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3240 redraw_mask |= REDRAW_FIELD;
3243 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3245 boolean show_level_border = (BorderElement != EL_EMPTY);
3246 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3247 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3248 int tile_size = preview.tile_size;
3249 int preview_width = preview.xsize * tile_size;
3250 int preview_height = preview.ysize * tile_size;
3251 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3252 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3253 int real_preview_width = real_preview_xsize * tile_size;
3254 int real_preview_height = real_preview_ysize * tile_size;
3255 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3256 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3259 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3262 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3264 dst_x += (preview_width - real_preview_width) / 2;
3265 dst_y += (preview_height - real_preview_height) / 2;
3267 for (x = 0; x < real_preview_xsize; x++)
3269 for (y = 0; y < real_preview_ysize; y++)
3271 int lx = from_x + x + (show_level_border ? -1 : 0);
3272 int ly = from_y + y + (show_level_border ? -1 : 0);
3273 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3274 getBorderElement(lx, ly));
3276 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3277 element, tile_size);
3281 redraw_mask |= REDRAW_FIELD;
3284 #define MICROLABEL_EMPTY 0
3285 #define MICROLABEL_LEVEL_NAME 1
3286 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3287 #define MICROLABEL_LEVEL_AUTHOR 3
3288 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3289 #define MICROLABEL_IMPORTED_FROM 5
3290 #define MICROLABEL_IMPORTED_BY_HEAD 6
3291 #define MICROLABEL_IMPORTED_BY 7
3293 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3295 int max_text_width = SXSIZE;
3296 int font_width = getFontWidth(font_nr);
3298 if (pos->align == ALIGN_CENTER)
3299 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3300 else if (pos->align == ALIGN_RIGHT)
3301 max_text_width = pos->x;
3303 max_text_width = SXSIZE - pos->x;
3305 return max_text_width / font_width;
3308 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3310 char label_text[MAX_OUTPUT_LINESIZE + 1];
3311 int max_len_label_text;
3312 int font_nr = pos->font;
3315 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3318 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3319 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3320 mode == MICROLABEL_IMPORTED_BY_HEAD)
3321 font_nr = pos->font_alt;
3323 max_len_label_text = getMaxTextLength(pos, font_nr);
3325 if (pos->size != -1)
3326 max_len_label_text = pos->size;
3328 for (i = 0; i < max_len_label_text; i++)
3329 label_text[i] = ' ';
3330 label_text[max_len_label_text] = '\0';
3332 if (strlen(label_text) > 0)
3333 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3336 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3337 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3338 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3339 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3340 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3341 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3342 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3343 max_len_label_text);
3344 label_text[max_len_label_text] = '\0';
3346 if (strlen(label_text) > 0)
3347 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3349 redraw_mask |= REDRAW_FIELD;
3352 static void DrawPreviewLevelLabel(int mode)
3354 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3357 static void DrawPreviewLevelInfo(int mode)
3359 if (mode == MICROLABEL_LEVEL_NAME)
3360 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3361 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3362 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3365 static void DrawPreviewLevelExt(boolean restart)
3367 static unsigned int scroll_delay = 0;
3368 static unsigned int label_delay = 0;
3369 static int from_x, from_y, scroll_direction;
3370 static int label_state, label_counter;
3371 unsigned int scroll_delay_value = preview.step_delay;
3372 boolean show_level_border = (BorderElement != EL_EMPTY);
3373 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3374 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3381 if (preview.anim_mode == ANIM_CENTERED)
3383 if (level_xsize > preview.xsize)
3384 from_x = (level_xsize - preview.xsize) / 2;
3385 if (level_ysize > preview.ysize)
3386 from_y = (level_ysize - preview.ysize) / 2;
3389 from_x += preview.xoffset;
3390 from_y += preview.yoffset;
3392 scroll_direction = MV_RIGHT;
3396 DrawPreviewLevelPlayfield(from_x, from_y);
3397 DrawPreviewLevelLabel(label_state);
3399 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3400 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3402 // initialize delay counters
3403 DelayReached(&scroll_delay, 0);
3404 DelayReached(&label_delay, 0);
3406 if (leveldir_current->name)
3408 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3409 char label_text[MAX_OUTPUT_LINESIZE + 1];
3410 int font_nr = pos->font;
3411 int max_len_label_text = getMaxTextLength(pos, font_nr);
3413 if (pos->size != -1)
3414 max_len_label_text = pos->size;
3416 strncpy(label_text, leveldir_current->name, max_len_label_text);
3417 label_text[max_len_label_text] = '\0';
3419 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3420 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3426 // scroll preview level, if needed
3427 if (preview.anim_mode != ANIM_NONE &&
3428 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3429 DelayReached(&scroll_delay, scroll_delay_value))
3431 switch (scroll_direction)
3436 from_x -= preview.step_offset;
3437 from_x = (from_x < 0 ? 0 : from_x);
3440 scroll_direction = MV_UP;
3444 if (from_x < level_xsize - preview.xsize)
3446 from_x += preview.step_offset;
3447 from_x = (from_x > level_xsize - preview.xsize ?
3448 level_xsize - preview.xsize : from_x);
3451 scroll_direction = MV_DOWN;
3457 from_y -= preview.step_offset;
3458 from_y = (from_y < 0 ? 0 : from_y);
3461 scroll_direction = MV_RIGHT;
3465 if (from_y < level_ysize - preview.ysize)
3467 from_y += preview.step_offset;
3468 from_y = (from_y > level_ysize - preview.ysize ?
3469 level_ysize - preview.ysize : from_y);
3472 scroll_direction = MV_LEFT;
3479 DrawPreviewLevelPlayfield(from_x, from_y);
3482 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3483 // redraw micro level label, if needed
3484 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3485 !strEqual(level.author, ANONYMOUS_NAME) &&
3486 !strEqual(level.author, leveldir_current->name) &&
3487 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3489 int max_label_counter = 23;
3491 if (leveldir_current->imported_from != NULL &&
3492 strlen(leveldir_current->imported_from) > 0)
3493 max_label_counter += 14;
3494 if (leveldir_current->imported_by != NULL &&
3495 strlen(leveldir_current->imported_by) > 0)
3496 max_label_counter += 14;
3498 label_counter = (label_counter + 1) % max_label_counter;
3499 label_state = (label_counter >= 0 && label_counter <= 7 ?
3500 MICROLABEL_LEVEL_NAME :
3501 label_counter >= 9 && label_counter <= 12 ?
3502 MICROLABEL_LEVEL_AUTHOR_HEAD :
3503 label_counter >= 14 && label_counter <= 21 ?
3504 MICROLABEL_LEVEL_AUTHOR :
3505 label_counter >= 23 && label_counter <= 26 ?
3506 MICROLABEL_IMPORTED_FROM_HEAD :
3507 label_counter >= 28 && label_counter <= 35 ?
3508 MICROLABEL_IMPORTED_FROM :
3509 label_counter >= 37 && label_counter <= 40 ?
3510 MICROLABEL_IMPORTED_BY_HEAD :
3511 label_counter >= 42 && label_counter <= 49 ?
3512 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3514 if (leveldir_current->imported_from == NULL &&
3515 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3516 label_state == MICROLABEL_IMPORTED_FROM))
3517 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3518 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3520 DrawPreviewLevelLabel(label_state);
3524 void DrawPreviewPlayers(void)
3526 if (game_status != GAME_MODE_MAIN)
3529 // do not draw preview players if level preview redefined, but players aren't
3530 if (preview.redefined && !menu.main.preview_players.redefined)
3533 boolean player_found[MAX_PLAYERS];
3534 int num_players = 0;
3537 for (i = 0; i < MAX_PLAYERS; i++)
3538 player_found[i] = FALSE;
3540 // check which players can be found in the level (simple approach)
3541 for (x = 0; x < lev_fieldx; x++)
3543 for (y = 0; y < lev_fieldy; y++)
3545 int element = level.field[x][y];
3547 if (ELEM_IS_PLAYER(element))
3549 int player_nr = GET_PLAYER_NR(element);
3551 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3553 if (!player_found[player_nr])
3556 player_found[player_nr] = TRUE;
3561 struct TextPosInfo *pos = &menu.main.preview_players;
3562 int tile_size = pos->tile_size;
3563 int border_size = pos->border_size;
3564 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3565 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3566 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3567 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3568 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3569 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3570 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3571 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3572 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3573 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3574 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3575 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3577 // clear area in which the players will be drawn
3578 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3579 max_players_width, max_players_height);
3581 if (!network.enabled && !setup.team_mode)
3584 // only draw players if level is suited for team mode
3585 if (num_players < 2)
3588 // draw all players that were found in the level
3589 for (i = 0; i < MAX_PLAYERS; i++)
3591 if (player_found[i])
3593 int graphic = el2img(EL_PLAYER_1 + i);
3595 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3597 xpos += player_xoffset;
3598 ypos += player_yoffset;
3603 void DrawPreviewLevelInitial(void)
3605 DrawPreviewLevelExt(TRUE);
3606 DrawPreviewPlayers();
3609 void DrawPreviewLevelAnimation(void)
3611 DrawPreviewLevelExt(FALSE);
3614 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3615 int border_size, int font_nr)
3617 int graphic = el2img(EL_PLAYER_1 + player_nr);
3618 int font_height = getFontHeight(font_nr);
3619 int player_height = MAX(tile_size, font_height);
3620 int xoffset_text = tile_size + border_size;
3621 int yoffset_text = (player_height - font_height) / 2;
3622 int yoffset_graphic = (player_height - tile_size) / 2;
3623 char *player_name = getNetworkPlayerName(player_nr + 1);
3625 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3627 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3630 static void DrawNetworkPlayersExt(boolean force)
3632 if (game_status != GAME_MODE_MAIN)
3635 if (!network.connected && !force)
3638 // do not draw network players if level preview redefined, but players aren't
3639 if (preview.redefined && !menu.main.network_players.redefined)
3642 int num_players = 0;
3645 for (i = 0; i < MAX_PLAYERS; i++)
3646 if (stored_player[i].connected_network)
3649 struct TextPosInfo *pos = &menu.main.network_players;
3650 int tile_size = pos->tile_size;
3651 int border_size = pos->border_size;
3652 int xoffset_text = tile_size + border_size;
3653 int font_nr = pos->font;
3654 int font_width = getFontWidth(font_nr);
3655 int font_height = getFontHeight(font_nr);
3656 int player_height = MAX(tile_size, font_height);
3657 int player_yoffset = player_height + border_size;
3658 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3659 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3660 int all_players_height = num_players * player_yoffset - border_size;
3661 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3662 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3663 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3665 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3666 max_players_width, max_players_height);
3668 // first draw local network player ...
3669 for (i = 0; i < MAX_PLAYERS; i++)
3671 if (stored_player[i].connected_network &&
3672 stored_player[i].connected_locally)
3674 char *player_name = getNetworkPlayerName(i + 1);
3675 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3676 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3678 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3680 ypos += player_yoffset;
3684 // ... then draw all other network players
3685 for (i = 0; i < MAX_PLAYERS; i++)
3687 if (stored_player[i].connected_network &&
3688 !stored_player[i].connected_locally)
3690 char *player_name = getNetworkPlayerName(i + 1);
3691 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3692 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3694 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3696 ypos += player_yoffset;
3701 void DrawNetworkPlayers(void)
3703 DrawNetworkPlayersExt(FALSE);
3706 void ClearNetworkPlayers(void)
3708 DrawNetworkPlayersExt(TRUE);
3711 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3712 int graphic, int sync_frame,
3715 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3717 if (mask_mode == USE_MASKING)
3718 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3720 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3723 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3724 int graphic, int sync_frame, int mask_mode)
3726 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3728 if (mask_mode == USE_MASKING)
3729 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3731 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3734 static void DrawGraphicAnimation(int x, int y, int graphic)
3736 int lx = LEVELX(x), ly = LEVELY(y);
3738 if (!IN_SCR_FIELD(x, y))
3741 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3742 graphic, GfxFrame[lx][ly], NO_MASKING);
3744 MarkTileDirty(x, y);
3747 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3749 int lx = LEVELX(x), ly = LEVELY(y);
3751 if (!IN_SCR_FIELD(x, y))
3754 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3755 graphic, GfxFrame[lx][ly], NO_MASKING);
3756 MarkTileDirty(x, y);
3759 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3761 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3764 void DrawLevelElementAnimation(int x, int y, int element)
3766 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3768 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3771 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3773 int sx = SCREENX(x), sy = SCREENY(y);
3775 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3778 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3781 DrawGraphicAnimation(sx, sy, graphic);
3784 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3785 DrawLevelFieldCrumbled(x, y);
3787 if (GFX_CRUMBLED(Tile[x][y]))
3788 DrawLevelFieldCrumbled(x, y);
3792 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3794 int sx = SCREENX(x), sy = SCREENY(y);
3797 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3800 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3802 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3805 DrawGraphicAnimation(sx, sy, graphic);
3807 if (GFX_CRUMBLED(element))
3808 DrawLevelFieldCrumbled(x, y);
3811 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3813 if (player->use_murphy)
3815 // this works only because currently only one player can be "murphy" ...
3816 static int last_horizontal_dir = MV_LEFT;
3817 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3819 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3820 last_horizontal_dir = move_dir;
3822 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3824 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3826 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3832 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3835 static boolean equalGraphics(int graphic1, int graphic2)
3837 struct GraphicInfo *g1 = &graphic_info[graphic1];
3838 struct GraphicInfo *g2 = &graphic_info[graphic2];
3840 return (g1->bitmap == g2->bitmap &&
3841 g1->src_x == g2->src_x &&
3842 g1->src_y == g2->src_y &&
3843 g1->anim_frames == g2->anim_frames &&
3844 g1->anim_delay == g2->anim_delay &&
3845 g1->anim_mode == g2->anim_mode);
3848 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3852 DRAW_PLAYER_STAGE_INIT = 0,
3853 DRAW_PLAYER_STAGE_LAST_FIELD,
3854 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3855 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3856 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3857 DRAW_PLAYER_STAGE_PLAYER,
3859 DRAW_PLAYER_STAGE_PLAYER,
3860 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3862 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3863 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3865 NUM_DRAW_PLAYER_STAGES
3868 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3870 static int static_last_player_graphic[MAX_PLAYERS];
3871 static int static_last_player_frame[MAX_PLAYERS];
3872 static boolean static_player_is_opaque[MAX_PLAYERS];
3873 static boolean draw_player[MAX_PLAYERS];
3874 int pnr = player->index_nr;
3876 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3878 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3879 static_last_player_frame[pnr] = player->Frame;
3880 static_player_is_opaque[pnr] = FALSE;
3882 draw_player[pnr] = TRUE;
3885 if (!draw_player[pnr])
3889 if (!IN_LEV_FIELD(player->jx, player->jy))
3891 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3892 Debug("draw:DrawPlayerExt", "This should never happen!");
3894 draw_player[pnr] = FALSE;
3900 int last_player_graphic = static_last_player_graphic[pnr];
3901 int last_player_frame = static_last_player_frame[pnr];
3902 boolean player_is_opaque = static_player_is_opaque[pnr];
3904 int jx = player->jx;
3905 int jy = player->jy;
3906 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3907 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3908 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3909 int last_jx = (player->is_moving ? jx - dx : jx);
3910 int last_jy = (player->is_moving ? jy - dy : jy);
3911 int next_jx = jx + dx;
3912 int next_jy = jy + dy;
3913 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3914 int sx = SCREENX(jx);
3915 int sy = SCREENY(jy);
3916 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3917 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3918 int element = Tile[jx][jy];
3919 int last_element = Tile[last_jx][last_jy];
3920 int action = (player->is_pushing ? ACTION_PUSHING :
3921 player->is_digging ? ACTION_DIGGING :
3922 player->is_collecting ? ACTION_COLLECTING :
3923 player->is_moving ? ACTION_MOVING :
3924 player->is_snapping ? ACTION_SNAPPING :
3925 player->is_dropping ? ACTION_DROPPING :
3926 player->is_waiting ? player->action_waiting :
3929 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3931 // ------------------------------------------------------------------------
3932 // initialize drawing the player
3933 // ------------------------------------------------------------------------
3935 draw_player[pnr] = FALSE;
3937 // GfxElement[][] is set to the element the player is digging or collecting;
3938 // remove also for off-screen player if the player is not moving anymore
3939 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3940 GfxElement[jx][jy] = EL_UNDEFINED;
3942 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3945 if (element == EL_EXPLOSION)
3948 InitPlayerGfxAnimation(player, action, move_dir);
3950 draw_player[pnr] = TRUE;
3952 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3954 // ------------------------------------------------------------------------
3955 // draw things in the field the player is leaving, if needed
3956 // ------------------------------------------------------------------------
3958 if (!IN_SCR_FIELD(sx, sy))
3959 draw_player[pnr] = FALSE;
3961 if (!player->is_moving)
3964 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3966 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3968 if (last_element == EL_DYNAMITE_ACTIVE ||
3969 last_element == EL_EM_DYNAMITE_ACTIVE ||
3970 last_element == EL_SP_DISK_RED_ACTIVE)
3971 DrawDynamite(last_jx, last_jy);
3973 DrawLevelFieldThruMask(last_jx, last_jy);
3975 else if (last_element == EL_DYNAMITE_ACTIVE ||
3976 last_element == EL_EM_DYNAMITE_ACTIVE ||
3977 last_element == EL_SP_DISK_RED_ACTIVE)
3978 DrawDynamite(last_jx, last_jy);
3980 DrawLevelField(last_jx, last_jy);
3982 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3983 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3985 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3987 // ------------------------------------------------------------------------
3988 // draw things behind the player, if needed
3989 // ------------------------------------------------------------------------
3993 DrawLevelElement(jx, jy, Back[jx][jy]);
3998 if (IS_ACTIVE_BOMB(element))
4000 DrawLevelElement(jx, jy, EL_EMPTY);
4005 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4007 int old_element = GfxElement[jx][jy];
4008 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4009 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4011 if (GFX_CRUMBLED(old_element))
4012 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4014 DrawGraphic(sx, sy, old_graphic, frame);
4016 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4017 static_player_is_opaque[pnr] = TRUE;
4021 GfxElement[jx][jy] = EL_UNDEFINED;
4023 // make sure that pushed elements are drawn with correct frame rate
4024 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4026 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4027 GfxFrame[jx][jy] = player->StepFrame;
4029 DrawLevelField(jx, jy);
4032 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4034 // ------------------------------------------------------------------------
4035 // draw things the player is pushing, if needed
4036 // ------------------------------------------------------------------------
4038 if (!player->is_pushing || !player->is_moving)
4041 int gfx_frame = GfxFrame[jx][jy];
4043 if (!IS_MOVING(jx, jy)) // push movement already finished
4045 element = Tile[next_jx][next_jy];
4046 gfx_frame = GfxFrame[next_jx][next_jy];
4049 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4050 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4051 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4053 // draw background element under pushed element (like the Sokoban field)
4054 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4056 // this allows transparent pushing animation over non-black background
4059 DrawLevelElement(jx, jy, Back[jx][jy]);
4061 DrawLevelElement(jx, jy, EL_EMPTY);
4063 if (Back[next_jx][next_jy])
4064 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4066 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4068 else if (Back[next_jx][next_jy])
4069 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4071 int px = SCREENX(jx), py = SCREENY(jy);
4072 int pxx = (TILEX - ABS(sxx)) * dx;
4073 int pyy = (TILEY - ABS(syy)) * dy;
4076 // do not draw (EM style) pushing animation when pushing is finished
4077 // (two-tile animations usually do not contain start and end frame)
4078 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4079 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4081 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4083 // masked drawing is needed for EMC style (double) movement graphics
4084 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4085 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4088 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4090 // ------------------------------------------------------------------------
4091 // draw player himself
4092 // ------------------------------------------------------------------------
4094 int graphic = getPlayerGraphic(player, move_dir);
4096 // in the case of changed player action or direction, prevent the current
4097 // animation frame from being restarted for identical animations
4098 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4099 player->Frame = last_player_frame;
4101 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4103 if (player_is_opaque)
4104 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4106 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4108 if (SHIELD_ON(player))
4110 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4111 IMG_SHIELD_NORMAL_ACTIVE);
4112 frame = getGraphicAnimationFrame(graphic, -1);
4114 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4117 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4119 // ------------------------------------------------------------------------
4120 // draw things in front of player (active dynamite or dynabombs)
4121 // ------------------------------------------------------------------------
4123 if (IS_ACTIVE_BOMB(element))
4125 int graphic = el2img(element);
4126 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4128 if (game.emulation == EMU_SUPAPLEX)
4129 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4131 DrawGraphicThruMask(sx, sy, graphic, frame);
4134 if (player_is_moving && last_element == EL_EXPLOSION)
4136 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4137 GfxElement[last_jx][last_jy] : EL_EMPTY);
4138 int graphic = el_act2img(element, ACTION_EXPLODING);
4139 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4140 int phase = ExplodePhase[last_jx][last_jy] - 1;
4141 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4144 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4147 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4149 // ------------------------------------------------------------------------
4150 // draw elements the player is just walking/passing through/under
4151 // ------------------------------------------------------------------------
4153 if (player_is_moving)
4155 // handle the field the player is leaving ...
4156 if (IS_ACCESSIBLE_INSIDE(last_element))
4157 DrawLevelField(last_jx, last_jy);
4158 else if (IS_ACCESSIBLE_UNDER(last_element))
4159 DrawLevelFieldThruMask(last_jx, last_jy);
4162 // do not redraw accessible elements if the player is just pushing them
4163 if (!player_is_moving || !player->is_pushing)
4165 // ... and the field the player is entering
4166 if (IS_ACCESSIBLE_INSIDE(element))
4167 DrawLevelField(jx, jy);
4168 else if (IS_ACCESSIBLE_UNDER(element))
4169 DrawLevelFieldThruMask(jx, jy);
4172 MarkTileDirty(sx, sy);
4176 void DrawPlayer(struct PlayerInfo *player)
4180 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4181 DrawPlayerExt(player, i);
4184 void DrawAllPlayers(void)
4188 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4189 for (j = 0; j < MAX_PLAYERS; j++)
4190 if (stored_player[j].active)
4191 DrawPlayerExt(&stored_player[j], i);
4194 void DrawPlayerField(int x, int y)
4196 if (!IS_PLAYER(x, y))
4199 DrawPlayer(PLAYERINFO(x, y));
4202 // ----------------------------------------------------------------------------
4204 void WaitForEventToContinue(void)
4206 boolean first_wait = TRUE;
4207 boolean still_wait = TRUE;
4209 if (program.headless)
4212 // simulate releasing mouse button over last gadget, if still pressed
4214 HandleGadgets(-1, -1, 0);
4216 button_status = MB_RELEASED;
4219 ClearPlayerAction();
4225 if (NextValidEvent(&event))
4229 case EVENT_BUTTONPRESS:
4230 case EVENT_FINGERPRESS:
4234 case EVENT_BUTTONRELEASE:
4235 case EVENT_FINGERRELEASE:
4236 still_wait = first_wait;
4239 case EVENT_KEYPRESS:
4240 case SDL_CONTROLLERBUTTONDOWN:
4241 case SDL_JOYBUTTONDOWN:
4246 HandleOtherEvents(&event);
4250 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4255 if (!PendingEvent())
4260 #define MAX_REQUEST_LINES 13
4261 #define MAX_REQUEST_LINE_FONT1_LEN 7
4262 #define MAX_REQUEST_LINE_FONT2_LEN 10
4264 static int RequestHandleEvents(unsigned int req_state)
4266 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4268 int width = request.width;
4269 int height = request.height;
4273 // when showing request dialog after game ended, deactivate game panel
4274 if (game_just_ended)
4275 game.panel.active = FALSE;
4277 game.request_active = TRUE;
4279 setRequestPosition(&sx, &sy, FALSE);
4281 button_status = MB_RELEASED;
4283 request_gadget_id = -1;
4288 if (game_just_ended)
4290 // the MM game engine does not use a special (scrollable) field buffer
4291 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4292 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4294 HandleGameActions();
4296 SetDrawtoField(DRAW_TO_BACKBUFFER);
4298 if (global.use_envelope_request)
4300 // copy current state of request area to middle of playfield area
4301 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4309 while (NextValidEvent(&event))
4313 case EVENT_BUTTONPRESS:
4314 case EVENT_BUTTONRELEASE:
4315 case EVENT_MOTIONNOTIFY:
4319 if (event.type == EVENT_MOTIONNOTIFY)
4324 motion_status = TRUE;
4325 mx = ((MotionEvent *) &event)->x;
4326 my = ((MotionEvent *) &event)->y;
4330 motion_status = FALSE;
4331 mx = ((ButtonEvent *) &event)->x;
4332 my = ((ButtonEvent *) &event)->y;
4333 if (event.type == EVENT_BUTTONPRESS)
4334 button_status = ((ButtonEvent *) &event)->button;
4336 button_status = MB_RELEASED;
4339 // this sets 'request_gadget_id'
4340 HandleGadgets(mx, my, button_status);
4342 switch (request_gadget_id)
4344 case TOOL_CTRL_ID_YES:
4345 case TOOL_CTRL_ID_TOUCH_YES:
4348 case TOOL_CTRL_ID_NO:
4349 case TOOL_CTRL_ID_TOUCH_NO:
4352 case TOOL_CTRL_ID_CONFIRM:
4353 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4354 result = TRUE | FALSE;
4357 case TOOL_CTRL_ID_PLAYER_1:
4360 case TOOL_CTRL_ID_PLAYER_2:
4363 case TOOL_CTRL_ID_PLAYER_3:
4366 case TOOL_CTRL_ID_PLAYER_4:
4371 // only check clickable animations if no request gadget clicked
4372 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4379 case SDL_WINDOWEVENT:
4380 HandleWindowEvent((WindowEvent *) &event);
4383 case SDL_APP_WILLENTERBACKGROUND:
4384 case SDL_APP_DIDENTERBACKGROUND:
4385 case SDL_APP_WILLENTERFOREGROUND:
4386 case SDL_APP_DIDENTERFOREGROUND:
4387 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4390 case EVENT_KEYPRESS:
4392 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4397 if (req_state & REQ_CONFIRM)
4406 #if defined(KSYM_Rewind)
4407 case KSYM_Rewind: // for Amazon Fire TV remote
4416 #if defined(KSYM_FastForward)
4417 case KSYM_FastForward: // for Amazon Fire TV remote
4423 HandleKeysDebug(key, KEY_PRESSED);
4427 if (req_state & REQ_PLAYER)
4429 int old_player_nr = setup.network_player_nr;
4432 result = old_player_nr + 1;
4437 result = old_player_nr + 1;
4468 case EVENT_FINGERRELEASE:
4469 case EVENT_KEYRELEASE:
4470 ClearPlayerAction();
4473 case SDL_CONTROLLERBUTTONDOWN:
4474 switch (event.cbutton.button)
4476 case SDL_CONTROLLER_BUTTON_A:
4477 case SDL_CONTROLLER_BUTTON_X:
4478 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4479 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4483 case SDL_CONTROLLER_BUTTON_B:
4484 case SDL_CONTROLLER_BUTTON_Y:
4485 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4486 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4487 case SDL_CONTROLLER_BUTTON_BACK:
4492 if (req_state & REQ_PLAYER)
4494 int old_player_nr = setup.network_player_nr;
4497 result = old_player_nr + 1;
4499 switch (event.cbutton.button)
4501 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4502 case SDL_CONTROLLER_BUTTON_Y:
4506 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4507 case SDL_CONTROLLER_BUTTON_B:
4511 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4512 case SDL_CONTROLLER_BUTTON_A:
4516 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4517 case SDL_CONTROLLER_BUTTON_X:
4528 case SDL_CONTROLLERBUTTONUP:
4529 HandleJoystickEvent(&event);
4530 ClearPlayerAction();
4534 HandleOtherEvents(&event);
4539 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4541 int joy = AnyJoystick();
4543 if (joy & JOY_BUTTON_1)
4545 else if (joy & JOY_BUTTON_2)
4548 else if (AnyJoystick())
4550 int joy = AnyJoystick();
4552 if (req_state & REQ_PLAYER)
4556 else if (joy & JOY_RIGHT)
4558 else if (joy & JOY_DOWN)
4560 else if (joy & JOY_LEFT)
4565 if (game_just_ended)
4567 if (global.use_envelope_request)
4569 // copy back current state of pressed buttons inside request area
4570 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4577 game.request_active = FALSE;
4582 static boolean RequestDoor(char *text, unsigned int req_state)
4584 unsigned int old_door_state;
4585 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4586 int font_nr = FONT_TEXT_2;
4591 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4593 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4594 font_nr = FONT_TEXT_1;
4597 if (game_status == GAME_MODE_PLAYING)
4598 BlitScreenToBitmap(backbuffer);
4600 // disable deactivated drawing when quick-loading level tape recording
4601 if (tape.playing && tape.deactivate_display)
4602 TapeDeactivateDisplayOff(TRUE);
4604 SetMouseCursor(CURSOR_DEFAULT);
4606 // pause network game while waiting for request to answer
4607 if (network.enabled &&
4608 game_status == GAME_MODE_PLAYING &&
4609 !game.all_players_gone &&
4610 req_state & REQUEST_WAIT_FOR_INPUT)
4611 SendToServer_PausePlaying();
4613 old_door_state = GetDoorState();
4615 // simulate releasing mouse button over last gadget, if still pressed
4617 HandleGadgets(-1, -1, 0);
4621 // draw released gadget before proceeding
4624 if (old_door_state & DOOR_OPEN_1)
4626 CloseDoor(DOOR_CLOSE_1);
4628 // save old door content
4629 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4630 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4633 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4634 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4636 // clear door drawing field
4637 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4639 // force DOOR font inside door area
4640 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4642 // write text for request
4643 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4645 char text_line[max_request_line_len + 1];
4651 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4653 tc = *(text_ptr + tx);
4654 // if (!tc || tc == ' ')
4655 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4659 if ((tc == '?' || tc == '!') && tl == 0)
4669 strncpy(text_line, text_ptr, tl);
4672 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4673 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4674 text_line, font_nr);
4676 text_ptr += tl + (tc == ' ' ? 1 : 0);
4677 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4682 if (req_state & REQ_ASK)
4684 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4685 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4687 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4689 else if (req_state & REQ_CONFIRM)
4691 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4692 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4694 else if (req_state & REQ_PLAYER)
4696 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4697 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4698 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4699 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4702 // copy request gadgets to door backbuffer
4703 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4705 OpenDoor(DOOR_OPEN_1);
4707 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4709 if (game_status == GAME_MODE_PLAYING)
4711 SetPanelBackground();
4712 SetDrawBackgroundMask(REDRAW_DOOR_1);
4716 SetDrawBackgroundMask(REDRAW_FIELD);
4722 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4724 // ---------- handle request buttons ----------
4725 result = RequestHandleEvents(req_state);
4729 if (!(req_state & REQ_STAY_OPEN))
4731 CloseDoor(DOOR_CLOSE_1);
4733 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4734 (req_state & REQ_REOPEN))
4735 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4740 if (game_status == GAME_MODE_PLAYING)
4742 SetPanelBackground();
4743 SetDrawBackgroundMask(REDRAW_DOOR_1);
4747 SetDrawBackgroundMask(REDRAW_FIELD);
4750 // continue network game after request
4751 if (network.enabled &&
4752 game_status == GAME_MODE_PLAYING &&
4753 !game.all_players_gone &&
4754 req_state & REQUEST_WAIT_FOR_INPUT)
4755 SendToServer_ContinuePlaying();
4757 // restore deactivated drawing when quick-loading level tape recording
4758 if (tape.playing && tape.deactivate_display)
4759 TapeDeactivateDisplayOn();
4764 static boolean RequestEnvelope(char *text, unsigned int req_state)
4768 if (game_status == GAME_MODE_PLAYING)
4769 BlitScreenToBitmap(backbuffer);
4771 // disable deactivated drawing when quick-loading level tape recording
4772 if (tape.playing && tape.deactivate_display)
4773 TapeDeactivateDisplayOff(TRUE);
4775 SetMouseCursor(CURSOR_DEFAULT);
4777 // pause network game while waiting for request to answer
4778 if (network.enabled &&
4779 game_status == GAME_MODE_PLAYING &&
4780 !game.all_players_gone &&
4781 req_state & REQUEST_WAIT_FOR_INPUT)
4782 SendToServer_PausePlaying();
4784 // simulate releasing mouse button over last gadget, if still pressed
4786 HandleGadgets(-1, -1, 0);
4790 // (replace with setting corresponding request background)
4791 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4792 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4794 // clear door drawing field
4795 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4797 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4799 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4801 if (game_status == GAME_MODE_PLAYING)
4803 SetPanelBackground();
4804 SetDrawBackgroundMask(REDRAW_DOOR_1);
4808 SetDrawBackgroundMask(REDRAW_FIELD);
4814 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4816 // ---------- handle request buttons ----------
4817 result = RequestHandleEvents(req_state);
4821 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4825 if (game_status == GAME_MODE_PLAYING)
4827 SetPanelBackground();
4828 SetDrawBackgroundMask(REDRAW_DOOR_1);
4832 SetDrawBackgroundMask(REDRAW_FIELD);
4835 // continue network game after request
4836 if (network.enabled &&
4837 game_status == GAME_MODE_PLAYING &&
4838 !game.all_players_gone &&
4839 req_state & REQUEST_WAIT_FOR_INPUT)
4840 SendToServer_ContinuePlaying();
4842 // restore deactivated drawing when quick-loading level tape recording
4843 if (tape.playing && tape.deactivate_display)
4844 TapeDeactivateDisplayOn();
4849 boolean Request(char *text, unsigned int req_state)
4851 boolean overlay_enabled = GetOverlayEnabled();
4854 SetOverlayEnabled(FALSE);
4856 if (global.use_envelope_request)
4857 result = RequestEnvelope(text, req_state);
4859 result = RequestDoor(text, req_state);
4861 SetOverlayEnabled(overlay_enabled);
4866 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4868 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4869 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4872 if (dpo1->sort_priority != dpo2->sort_priority)
4873 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4875 compare_result = dpo1->nr - dpo2->nr;
4877 return compare_result;
4880 void InitGraphicCompatibilityInfo_Doors(void)
4886 struct DoorInfo *door;
4890 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4891 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4893 { -1, -1, -1, NULL }
4895 struct Rect door_rect_list[] =
4897 { DX, DY, DXSIZE, DYSIZE },
4898 { VX, VY, VXSIZE, VYSIZE }
4902 for (i = 0; doors[i].door_token != -1; i++)
4904 int door_token = doors[i].door_token;
4905 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4906 int part_1 = doors[i].part_1;
4907 int part_8 = doors[i].part_8;
4908 int part_2 = part_1 + 1;
4909 int part_3 = part_1 + 2;
4910 struct DoorInfo *door = doors[i].door;
4911 struct Rect *door_rect = &door_rect_list[door_index];
4912 boolean door_gfx_redefined = FALSE;
4914 // check if any door part graphic definitions have been redefined
4916 for (j = 0; door_part_controls[j].door_token != -1; j++)
4918 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4919 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4921 if (dpc->door_token == door_token && fi->redefined)
4922 door_gfx_redefined = TRUE;
4925 // check for old-style door graphic/animation modifications
4927 if (!door_gfx_redefined)
4929 if (door->anim_mode & ANIM_STATIC_PANEL)
4931 door->panel.step_xoffset = 0;
4932 door->panel.step_yoffset = 0;
4935 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4937 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4938 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4939 int num_door_steps, num_panel_steps;
4941 // remove door part graphics other than the two default wings
4943 for (j = 0; door_part_controls[j].door_token != -1; j++)
4945 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4946 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4948 if (dpc->graphic >= part_3 &&
4949 dpc->graphic <= part_8)
4953 // set graphics and screen positions of the default wings
4955 g_part_1->width = door_rect->width;
4956 g_part_1->height = door_rect->height;
4957 g_part_2->width = door_rect->width;
4958 g_part_2->height = door_rect->height;
4959 g_part_2->src_x = door_rect->width;
4960 g_part_2->src_y = g_part_1->src_y;
4962 door->part_2.x = door->part_1.x;
4963 door->part_2.y = door->part_1.y;
4965 if (door->width != -1)
4967 g_part_1->width = door->width;
4968 g_part_2->width = door->width;
4970 // special treatment for graphics and screen position of right wing
4971 g_part_2->src_x += door_rect->width - door->width;
4972 door->part_2.x += door_rect->width - door->width;
4975 if (door->height != -1)
4977 g_part_1->height = door->height;
4978 g_part_2->height = door->height;
4980 // special treatment for graphics and screen position of bottom wing
4981 g_part_2->src_y += door_rect->height - door->height;
4982 door->part_2.y += door_rect->height - door->height;
4985 // set animation delays for the default wings and panels
4987 door->part_1.step_delay = door->step_delay;
4988 door->part_2.step_delay = door->step_delay;
4989 door->panel.step_delay = door->step_delay;
4991 // set animation draw order for the default wings
4993 door->part_1.sort_priority = 2; // draw left wing over ...
4994 door->part_2.sort_priority = 1; // ... right wing
4996 // set animation draw offset for the default wings
4998 if (door->anim_mode & ANIM_HORIZONTAL)
5000 door->part_1.step_xoffset = door->step_offset;
5001 door->part_1.step_yoffset = 0;
5002 door->part_2.step_xoffset = door->step_offset * -1;
5003 door->part_2.step_yoffset = 0;
5005 num_door_steps = g_part_1->width / door->step_offset;
5007 else // ANIM_VERTICAL
5009 door->part_1.step_xoffset = 0;
5010 door->part_1.step_yoffset = door->step_offset;
5011 door->part_2.step_xoffset = 0;
5012 door->part_2.step_yoffset = door->step_offset * -1;
5014 num_door_steps = g_part_1->height / door->step_offset;
5017 // set animation draw offset for the default panels
5019 if (door->step_offset > 1)
5021 num_panel_steps = 2 * door_rect->height / door->step_offset;
5022 door->panel.start_step = num_panel_steps - num_door_steps;
5023 door->panel.start_step_closing = door->panel.start_step;
5027 num_panel_steps = door_rect->height / door->step_offset;
5028 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5029 door->panel.start_step_closing = door->panel.start_step;
5030 door->panel.step_delay *= 2;
5037 void InitDoors(void)
5041 for (i = 0; door_part_controls[i].door_token != -1; i++)
5043 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5044 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5046 // initialize "start_step_opening" and "start_step_closing", if needed
5047 if (dpc->pos->start_step_opening == 0 &&
5048 dpc->pos->start_step_closing == 0)
5050 // dpc->pos->start_step_opening = dpc->pos->start_step;
5051 dpc->pos->start_step_closing = dpc->pos->start_step;
5054 // fill structure for door part draw order (sorted below)
5056 dpo->sort_priority = dpc->pos->sort_priority;
5059 // sort door part controls according to sort_priority and graphic number
5060 qsort(door_part_order, MAX_DOOR_PARTS,
5061 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5064 unsigned int OpenDoor(unsigned int door_state)
5066 if (door_state & DOOR_COPY_BACK)
5068 if (door_state & DOOR_OPEN_1)
5069 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5070 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5072 if (door_state & DOOR_OPEN_2)
5073 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5074 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5076 door_state &= ~DOOR_COPY_BACK;
5079 return MoveDoor(door_state);
5082 unsigned int CloseDoor(unsigned int door_state)
5084 unsigned int old_door_state = GetDoorState();
5086 if (!(door_state & DOOR_NO_COPY_BACK))
5088 if (old_door_state & DOOR_OPEN_1)
5089 BlitBitmap(backbuffer, bitmap_db_door_1,
5090 DX, DY, DXSIZE, DYSIZE, 0, 0);
5092 if (old_door_state & DOOR_OPEN_2)
5093 BlitBitmap(backbuffer, bitmap_db_door_2,
5094 VX, VY, VXSIZE, VYSIZE, 0, 0);
5096 door_state &= ~DOOR_NO_COPY_BACK;
5099 return MoveDoor(door_state);
5102 unsigned int GetDoorState(void)
5104 return MoveDoor(DOOR_GET_STATE);
5107 unsigned int SetDoorState(unsigned int door_state)
5109 return MoveDoor(door_state | DOOR_SET_STATE);
5112 static int euclid(int a, int b)
5114 return (b ? euclid(b, a % b) : a);
5117 unsigned int MoveDoor(unsigned int door_state)
5119 struct Rect door_rect_list[] =
5121 { DX, DY, DXSIZE, DYSIZE },
5122 { VX, VY, VXSIZE, VYSIZE }
5124 static int door1 = DOOR_CLOSE_1;
5125 static int door2 = DOOR_CLOSE_2;
5126 unsigned int door_delay = 0;
5127 unsigned int door_delay_value;
5130 if (door_state == DOOR_GET_STATE)
5131 return (door1 | door2);
5133 if (door_state & DOOR_SET_STATE)
5135 if (door_state & DOOR_ACTION_1)
5136 door1 = door_state & DOOR_ACTION_1;
5137 if (door_state & DOOR_ACTION_2)
5138 door2 = door_state & DOOR_ACTION_2;
5140 return (door1 | door2);
5143 if (!(door_state & DOOR_FORCE_REDRAW))
5145 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5146 door_state &= ~DOOR_OPEN_1;
5147 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5148 door_state &= ~DOOR_CLOSE_1;
5149 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5150 door_state &= ~DOOR_OPEN_2;
5151 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5152 door_state &= ~DOOR_CLOSE_2;
5155 if (global.autoplay_leveldir)
5157 door_state |= DOOR_NO_DELAY;
5158 door_state &= ~DOOR_CLOSE_ALL;
5161 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5162 door_state |= DOOR_NO_DELAY;
5164 if (door_state & DOOR_ACTION)
5166 boolean door_panel_drawn[NUM_DOORS];
5167 boolean panel_has_doors[NUM_DOORS];
5168 boolean door_part_skip[MAX_DOOR_PARTS];
5169 boolean door_part_done[MAX_DOOR_PARTS];
5170 boolean door_part_done_all;
5171 int num_steps[MAX_DOOR_PARTS];
5172 int max_move_delay = 0; // delay for complete animations of all doors
5173 int max_step_delay = 0; // delay (ms) between two animation frames
5174 int num_move_steps = 0; // number of animation steps for all doors
5175 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5176 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5177 int current_move_delay = 0;
5181 for (i = 0; i < NUM_DOORS; i++)
5182 panel_has_doors[i] = FALSE;
5184 for (i = 0; i < MAX_DOOR_PARTS; i++)
5186 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5187 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5188 int door_token = dpc->door_token;
5190 door_part_done[i] = FALSE;
5191 door_part_skip[i] = (!(door_state & door_token) ||
5195 for (i = 0; i < MAX_DOOR_PARTS; i++)
5197 int nr = door_part_order[i].nr;
5198 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5199 struct DoorPartPosInfo *pos = dpc->pos;
5200 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5201 int door_token = dpc->door_token;
5202 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5203 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5204 int step_xoffset = ABS(pos->step_xoffset);
5205 int step_yoffset = ABS(pos->step_yoffset);
5206 int step_delay = pos->step_delay;
5207 int current_door_state = door_state & door_token;
5208 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5209 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5210 boolean part_opening = (is_panel ? door_closing : door_opening);
5211 int start_step = (part_opening ? pos->start_step_opening :
5212 pos->start_step_closing);
5213 float move_xsize = (step_xoffset ? g->width : 0);
5214 float move_ysize = (step_yoffset ? g->height : 0);
5215 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5216 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5217 int move_steps = (move_xsteps && move_ysteps ?
5218 MIN(move_xsteps, move_ysteps) :
5219 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5220 int move_delay = move_steps * step_delay;
5222 if (door_part_skip[nr])
5225 max_move_delay = MAX(max_move_delay, move_delay);
5226 max_step_delay = (max_step_delay == 0 ? step_delay :
5227 euclid(max_step_delay, step_delay));
5228 num_steps[nr] = move_steps;
5232 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5234 panel_has_doors[door_index] = TRUE;
5238 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5240 num_move_steps = max_move_delay / max_step_delay;
5241 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5243 door_delay_value = max_step_delay;
5245 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5247 start = num_move_steps - 1;
5251 // opening door sound has priority over simultaneously closing door
5252 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5254 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5256 if (door_state & DOOR_OPEN_1)
5257 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5258 if (door_state & DOOR_OPEN_2)
5259 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5261 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5263 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5265 if (door_state & DOOR_CLOSE_1)
5266 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5267 if (door_state & DOOR_CLOSE_2)
5268 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5272 for (k = start; k < num_move_steps; k++)
5274 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5276 door_part_done_all = TRUE;
5278 for (i = 0; i < NUM_DOORS; i++)
5279 door_panel_drawn[i] = FALSE;
5281 for (i = 0; i < MAX_DOOR_PARTS; i++)
5283 int nr = door_part_order[i].nr;
5284 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5285 struct DoorPartPosInfo *pos = dpc->pos;
5286 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5287 int door_token = dpc->door_token;
5288 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5289 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5290 boolean is_panel_and_door_has_closed = FALSE;
5291 struct Rect *door_rect = &door_rect_list[door_index];
5292 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5294 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5295 int current_door_state = door_state & door_token;
5296 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5297 boolean door_closing = !door_opening;
5298 boolean part_opening = (is_panel ? door_closing : door_opening);
5299 boolean part_closing = !part_opening;
5300 int start_step = (part_opening ? pos->start_step_opening :
5301 pos->start_step_closing);
5302 int step_delay = pos->step_delay;
5303 int step_factor = step_delay / max_step_delay;
5304 int k1 = (step_factor ? k / step_factor + 1 : k);
5305 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5306 int kk = MAX(0, k2);
5309 int src_x, src_y, src_xx, src_yy;
5310 int dst_x, dst_y, dst_xx, dst_yy;
5313 if (door_part_skip[nr])
5316 if (!(door_state & door_token))
5324 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5325 int kk_door = MAX(0, k2_door);
5326 int sync_frame = kk_door * door_delay_value;
5327 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5329 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5330 &g_src_x, &g_src_y);
5335 if (!door_panel_drawn[door_index])
5337 ClearRectangle(drawto, door_rect->x, door_rect->y,
5338 door_rect->width, door_rect->height);
5340 door_panel_drawn[door_index] = TRUE;
5343 // draw opening or closing door parts
5345 if (pos->step_xoffset < 0) // door part on right side
5348 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5351 if (dst_xx + width > door_rect->width)
5352 width = door_rect->width - dst_xx;
5354 else // door part on left side
5357 dst_xx = pos->x - kk * pos->step_xoffset;
5361 src_xx = ABS(dst_xx);
5365 width = g->width - src_xx;
5367 if (width > door_rect->width)
5368 width = door_rect->width;
5370 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5373 if (pos->step_yoffset < 0) // door part on bottom side
5376 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5379 if (dst_yy + height > door_rect->height)
5380 height = door_rect->height - dst_yy;
5382 else // door part on top side
5385 dst_yy = pos->y - kk * pos->step_yoffset;
5389 src_yy = ABS(dst_yy);
5393 height = g->height - src_yy;
5396 src_x = g_src_x + src_xx;
5397 src_y = g_src_y + src_yy;
5399 dst_x = door_rect->x + dst_xx;
5400 dst_y = door_rect->y + dst_yy;
5402 is_panel_and_door_has_closed =
5405 panel_has_doors[door_index] &&
5406 k >= num_move_steps_doors_only - 1);
5408 if (width >= 0 && width <= g->width &&
5409 height >= 0 && height <= g->height &&
5410 !is_panel_and_door_has_closed)
5412 if (is_panel || !pos->draw_masked)
5413 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5416 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5420 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5422 if ((part_opening && (width < 0 || height < 0)) ||
5423 (part_closing && (width >= g->width && height >= g->height)))
5424 door_part_done[nr] = TRUE;
5426 // continue door part animations, but not panel after door has closed
5427 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5428 door_part_done_all = FALSE;
5431 if (!(door_state & DOOR_NO_DELAY))
5435 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5437 current_move_delay += max_step_delay;
5439 // prevent OS (Windows) from complaining about program not responding
5443 if (door_part_done_all)
5447 if (!(door_state & DOOR_NO_DELAY))
5449 // wait for specified door action post delay
5450 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5451 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5452 else if (door_state & DOOR_ACTION_1)
5453 door_delay_value = door_1.post_delay;
5454 else if (door_state & DOOR_ACTION_2)
5455 door_delay_value = door_2.post_delay;
5457 while (!DelayReached(&door_delay, door_delay_value))
5462 if (door_state & DOOR_ACTION_1)
5463 door1 = door_state & DOOR_ACTION_1;
5464 if (door_state & DOOR_ACTION_2)
5465 door2 = door_state & DOOR_ACTION_2;
5467 // draw masked border over door area
5468 DrawMaskedBorder(REDRAW_DOOR_1);
5469 DrawMaskedBorder(REDRAW_DOOR_2);
5471 ClearAutoRepeatKeyEvents();
5473 return (door1 | door2);
5476 static boolean useSpecialEditorDoor(void)
5478 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5479 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5481 // do not draw special editor door if editor border defined or redefined
5482 if (graphic_info[graphic].bitmap != NULL || redefined)
5485 // do not draw special editor door if global border defined to be empty
5486 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5489 // do not draw special editor door if viewport definitions do not match
5493 EY + EYSIZE != VY + VYSIZE)
5499 void DrawSpecialEditorDoor(void)
5501 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5502 int top_border_width = gfx1->width;
5503 int top_border_height = gfx1->height;
5504 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5505 int ex = EX - outer_border;
5506 int ey = EY - outer_border;
5507 int vy = VY - outer_border;
5508 int exsize = EXSIZE + 2 * outer_border;
5510 if (!useSpecialEditorDoor())
5513 // draw bigger level editor toolbox window
5514 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5515 top_border_width, top_border_height, ex, ey - top_border_height);
5516 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5517 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5519 redraw_mask |= REDRAW_ALL;
5522 void UndrawSpecialEditorDoor(void)
5524 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5525 int top_border_width = gfx1->width;
5526 int top_border_height = gfx1->height;
5527 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5528 int ex = EX - outer_border;
5529 int ey = EY - outer_border;
5530 int ey_top = ey - top_border_height;
5531 int exsize = EXSIZE + 2 * outer_border;
5532 int eysize = EYSIZE + 2 * outer_border;
5534 if (!useSpecialEditorDoor())
5537 // draw normal tape recorder window
5538 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5540 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5541 ex, ey_top, top_border_width, top_border_height,
5543 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5544 ex, ey, exsize, eysize, ex, ey);
5548 // if screen background is set to "[NONE]", clear editor toolbox window
5549 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5550 ClearRectangle(drawto, ex, ey, exsize, eysize);
5553 redraw_mask |= REDRAW_ALL;
5557 // ---------- new tool button stuff -------------------------------------------
5562 struct TextPosInfo *pos;
5564 boolean is_touch_button;
5566 } toolbutton_info[NUM_TOOL_BUTTONS] =
5569 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5570 TOOL_CTRL_ID_YES, FALSE, "yes"
5573 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5574 TOOL_CTRL_ID_NO, FALSE, "no"
5577 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5578 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5581 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5582 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5585 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5586 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5589 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5590 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5593 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5594 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5597 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5598 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5601 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5602 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5605 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5606 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5610 void CreateToolButtons(void)
5614 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5616 int graphic = toolbutton_info[i].graphic;
5617 struct GraphicInfo *gfx = &graphic_info[graphic];
5618 struct TextPosInfo *pos = toolbutton_info[i].pos;
5619 struct GadgetInfo *gi;
5620 Bitmap *deco_bitmap = None;
5621 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5622 unsigned int event_mask = GD_EVENT_RELEASED;
5623 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5624 int base_x = (is_touch_button ? 0 : DX);
5625 int base_y = (is_touch_button ? 0 : DY);
5626 int gd_x = gfx->src_x;
5627 int gd_y = gfx->src_y;
5628 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5629 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5634 if (global.use_envelope_request && !is_touch_button)
5636 setRequestPosition(&base_x, &base_y, TRUE);
5638 // check if request buttons are outside of envelope and fix, if needed
5639 if (x < 0 || x + gfx->width > request.width ||
5640 y < 0 || y + gfx->height > request.height)
5642 if (id == TOOL_CTRL_ID_YES)
5645 y = request.height - 2 * request.border_size - gfx->height;
5647 else if (id == TOOL_CTRL_ID_NO)
5649 x = request.width - 2 * request.border_size - gfx->width;
5650 y = request.height - 2 * request.border_size - gfx->height;
5652 else if (id == TOOL_CTRL_ID_CONFIRM)
5654 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5655 y = request.height - 2 * request.border_size - gfx->height;
5657 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5659 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5661 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5662 y = request.height - 2 * request.border_size - gfx->height * 2;
5664 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5665 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5670 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5672 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5674 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5675 pos->size, &deco_bitmap, &deco_x, &deco_y);
5676 deco_xpos = (gfx->width - pos->size) / 2;
5677 deco_ypos = (gfx->height - pos->size) / 2;
5680 gi = CreateGadget(GDI_CUSTOM_ID, id,
5681 GDI_IMAGE_ID, graphic,
5682 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5685 GDI_WIDTH, gfx->width,
5686 GDI_HEIGHT, gfx->height,
5687 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5688 GDI_STATE, GD_BUTTON_UNPRESSED,
5689 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5690 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5691 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5692 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5693 GDI_DECORATION_SIZE, pos->size, pos->size,
5694 GDI_DECORATION_SHIFTING, 1, 1,
5695 GDI_DIRECT_DRAW, FALSE,
5696 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5697 GDI_EVENT_MASK, event_mask,
5698 GDI_CALLBACK_ACTION, HandleToolButtons,
5702 Fail("cannot create gadget");
5704 tool_gadget[id] = gi;
5708 void FreeToolButtons(void)
5712 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5713 FreeGadget(tool_gadget[i]);
5716 static void UnmapToolButtons(void)
5720 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5721 UnmapGadget(tool_gadget[i]);
5724 static void HandleToolButtons(struct GadgetInfo *gi)
5726 request_gadget_id = gi->custom_id;
5729 static struct Mapping_EM_to_RND_object
5732 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5733 boolean is_backside; // backside of moving element
5739 em_object_mapping_list[GAME_TILE_MAX + 1] =
5742 Zborder, FALSE, FALSE,
5746 Zplayer, FALSE, FALSE,
5755 Ztank, FALSE, FALSE,
5759 Zeater, FALSE, FALSE,
5763 Zdynamite, FALSE, FALSE,
5767 Zboom, FALSE, FALSE,
5772 Xchain, FALSE, FALSE,
5773 EL_DEFAULT, ACTION_EXPLODING, -1
5776 Xboom_bug, FALSE, FALSE,
5777 EL_BUG, ACTION_EXPLODING, -1
5780 Xboom_tank, FALSE, FALSE,
5781 EL_SPACESHIP, ACTION_EXPLODING, -1
5784 Xboom_android, FALSE, FALSE,
5785 EL_EMC_ANDROID, ACTION_OTHER, -1
5788 Xboom_1, FALSE, FALSE,
5789 EL_DEFAULT, ACTION_EXPLODING, -1
5792 Xboom_2, FALSE, FALSE,
5793 EL_DEFAULT, ACTION_EXPLODING, -1
5797 Xblank, TRUE, FALSE,
5802 Xsplash_e, FALSE, FALSE,
5803 EL_ACID_SPLASH_RIGHT, -1, -1
5806 Xsplash_w, FALSE, FALSE,
5807 EL_ACID_SPLASH_LEFT, -1, -1
5811 Xplant, TRUE, FALSE,
5812 EL_EMC_PLANT, -1, -1
5815 Yplant, FALSE, FALSE,
5816 EL_EMC_PLANT, -1, -1
5820 Xacid_1, TRUE, FALSE,
5824 Xacid_2, FALSE, FALSE,
5828 Xacid_3, FALSE, FALSE,
5832 Xacid_4, FALSE, FALSE,
5836 Xacid_5, FALSE, FALSE,
5840 Xacid_6, FALSE, FALSE,
5844 Xacid_7, FALSE, FALSE,
5848 Xacid_8, FALSE, FALSE,
5853 Xfake_acid_1, TRUE, FALSE,
5854 EL_EMC_FAKE_ACID, -1, -1
5857 Xfake_acid_2, FALSE, FALSE,
5858 EL_EMC_FAKE_ACID, -1, -1
5861 Xfake_acid_3, FALSE, FALSE,
5862 EL_EMC_FAKE_ACID, -1, -1
5865 Xfake_acid_4, FALSE, FALSE,
5866 EL_EMC_FAKE_ACID, -1, -1
5869 Xfake_acid_5, FALSE, FALSE,
5870 EL_EMC_FAKE_ACID, -1, -1
5873 Xfake_acid_6, FALSE, FALSE,
5874 EL_EMC_FAKE_ACID, -1, -1
5877 Xfake_acid_7, FALSE, FALSE,
5878 EL_EMC_FAKE_ACID, -1, -1
5881 Xfake_acid_8, FALSE, FALSE,
5882 EL_EMC_FAKE_ACID, -1, -1
5886 Xfake_acid_1_player, FALSE, FALSE,
5887 EL_EMC_FAKE_ACID, -1, -1
5890 Xfake_acid_2_player, FALSE, FALSE,
5891 EL_EMC_FAKE_ACID, -1, -1
5894 Xfake_acid_3_player, FALSE, FALSE,
5895 EL_EMC_FAKE_ACID, -1, -1
5898 Xfake_acid_4_player, FALSE, FALSE,
5899 EL_EMC_FAKE_ACID, -1, -1
5902 Xfake_acid_5_player, FALSE, FALSE,
5903 EL_EMC_FAKE_ACID, -1, -1
5906 Xfake_acid_6_player, FALSE, FALSE,
5907 EL_EMC_FAKE_ACID, -1, -1
5910 Xfake_acid_7_player, FALSE, FALSE,
5911 EL_EMC_FAKE_ACID, -1, -1
5914 Xfake_acid_8_player, FALSE, FALSE,
5915 EL_EMC_FAKE_ACID, -1, -1
5919 Xgrass, TRUE, FALSE,
5920 EL_EMC_GRASS, -1, -1
5923 Ygrass_nB, FALSE, FALSE,
5924 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5927 Ygrass_eB, FALSE, FALSE,
5928 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5931 Ygrass_sB, FALSE, FALSE,
5932 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5935 Ygrass_wB, FALSE, FALSE,
5936 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5944 Ydirt_nB, FALSE, FALSE,
5945 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5948 Ydirt_eB, FALSE, FALSE,
5949 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5952 Ydirt_sB, FALSE, FALSE,
5953 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5956 Ydirt_wB, FALSE, FALSE,
5957 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5961 Xandroid, TRUE, FALSE,
5962 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5965 Xandroid_1_n, FALSE, FALSE,
5966 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5969 Xandroid_2_n, FALSE, FALSE,
5970 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5973 Xandroid_1_e, FALSE, FALSE,
5974 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5977 Xandroid_2_e, FALSE, FALSE,
5978 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5981 Xandroid_1_w, FALSE, FALSE,
5982 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5985 Xandroid_2_w, FALSE, FALSE,
5986 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5989 Xandroid_1_s, FALSE, FALSE,
5990 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5993 Xandroid_2_s, FALSE, FALSE,
5994 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5997 Yandroid_n, FALSE, FALSE,
5998 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6001 Yandroid_nB, FALSE, TRUE,
6002 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6005 Yandroid_ne, FALSE, FALSE,
6006 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6009 Yandroid_neB, FALSE, TRUE,
6010 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6013 Yandroid_e, FALSE, FALSE,
6014 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6017 Yandroid_eB, FALSE, TRUE,
6018 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6021 Yandroid_se, FALSE, FALSE,
6022 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6025 Yandroid_seB, FALSE, TRUE,
6026 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6029 Yandroid_s, FALSE, FALSE,
6030 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6033 Yandroid_sB, FALSE, TRUE,
6034 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6037 Yandroid_sw, FALSE, FALSE,
6038 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6041 Yandroid_swB, FALSE, TRUE,
6042 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6045 Yandroid_w, FALSE, FALSE,
6046 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6049 Yandroid_wB, FALSE, TRUE,
6050 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6053 Yandroid_nw, FALSE, FALSE,
6054 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6057 Yandroid_nwB, FALSE, TRUE,
6058 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6062 Xeater_n, TRUE, FALSE,
6063 EL_YAMYAM_UP, -1, -1
6066 Xeater_e, TRUE, FALSE,
6067 EL_YAMYAM_RIGHT, -1, -1
6070 Xeater_w, TRUE, FALSE,
6071 EL_YAMYAM_LEFT, -1, -1
6074 Xeater_s, TRUE, FALSE,
6075 EL_YAMYAM_DOWN, -1, -1
6078 Yeater_n, FALSE, FALSE,
6079 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6082 Yeater_nB, FALSE, TRUE,
6083 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6086 Yeater_e, FALSE, FALSE,
6087 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6090 Yeater_eB, FALSE, TRUE,
6091 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6094 Yeater_s, FALSE, FALSE,
6095 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6098 Yeater_sB, FALSE, TRUE,
6099 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6102 Yeater_w, FALSE, FALSE,
6103 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6106 Yeater_wB, FALSE, TRUE,
6107 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6110 Yeater_stone, FALSE, FALSE,
6111 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6114 Yeater_spring, FALSE, FALSE,
6115 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6119 Xalien, TRUE, FALSE,
6123 Xalien_pause, FALSE, FALSE,
6127 Yalien_n, FALSE, FALSE,
6128 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6131 Yalien_nB, FALSE, TRUE,
6132 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6135 Yalien_e, FALSE, FALSE,
6136 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6139 Yalien_eB, FALSE, TRUE,
6140 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6143 Yalien_s, FALSE, FALSE,
6144 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6147 Yalien_sB, FALSE, TRUE,
6148 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6151 Yalien_w, FALSE, FALSE,
6152 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6155 Yalien_wB, FALSE, TRUE,
6156 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6159 Yalien_stone, FALSE, FALSE,
6160 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6163 Yalien_spring, FALSE, FALSE,
6164 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6168 Xbug_1_n, TRUE, FALSE,
6172 Xbug_1_e, TRUE, FALSE,
6173 EL_BUG_RIGHT, -1, -1
6176 Xbug_1_s, TRUE, FALSE,
6180 Xbug_1_w, TRUE, FALSE,
6184 Xbug_2_n, FALSE, FALSE,
6188 Xbug_2_e, FALSE, FALSE,
6189 EL_BUG_RIGHT, -1, -1
6192 Xbug_2_s, FALSE, FALSE,
6196 Xbug_2_w, FALSE, FALSE,
6200 Ybug_n, FALSE, FALSE,
6201 EL_BUG, ACTION_MOVING, MV_BIT_UP
6204 Ybug_nB, FALSE, TRUE,
6205 EL_BUG, ACTION_MOVING, MV_BIT_UP
6208 Ybug_e, FALSE, FALSE,
6209 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6212 Ybug_eB, FALSE, TRUE,
6213 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6216 Ybug_s, FALSE, FALSE,
6217 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6220 Ybug_sB, FALSE, TRUE,
6221 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6224 Ybug_w, FALSE, FALSE,
6225 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6228 Ybug_wB, FALSE, TRUE,
6229 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6232 Ybug_w_n, FALSE, FALSE,
6233 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6236 Ybug_n_e, FALSE, FALSE,
6237 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6240 Ybug_e_s, FALSE, FALSE,
6241 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6244 Ybug_s_w, FALSE, FALSE,
6245 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6248 Ybug_e_n, FALSE, FALSE,
6249 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6252 Ybug_s_e, FALSE, FALSE,
6253 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6256 Ybug_w_s, FALSE, FALSE,
6257 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6260 Ybug_n_w, FALSE, FALSE,
6261 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6264 Ybug_stone, FALSE, FALSE,
6265 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6268 Ybug_spring, FALSE, FALSE,
6269 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6273 Xtank_1_n, TRUE, FALSE,
6274 EL_SPACESHIP_UP, -1, -1
6277 Xtank_1_e, TRUE, FALSE,
6278 EL_SPACESHIP_RIGHT, -1, -1
6281 Xtank_1_s, TRUE, FALSE,
6282 EL_SPACESHIP_DOWN, -1, -1
6285 Xtank_1_w, TRUE, FALSE,
6286 EL_SPACESHIP_LEFT, -1, -1
6289 Xtank_2_n, FALSE, FALSE,
6290 EL_SPACESHIP_UP, -1, -1
6293 Xtank_2_e, FALSE, FALSE,
6294 EL_SPACESHIP_RIGHT, -1, -1
6297 Xtank_2_s, FALSE, FALSE,
6298 EL_SPACESHIP_DOWN, -1, -1
6301 Xtank_2_w, FALSE, FALSE,
6302 EL_SPACESHIP_LEFT, -1, -1
6305 Ytank_n, FALSE, FALSE,
6306 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6309 Ytank_nB, FALSE, TRUE,
6310 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6313 Ytank_e, FALSE, FALSE,
6314 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6317 Ytank_eB, FALSE, TRUE,
6318 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6321 Ytank_s, FALSE, FALSE,
6322 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6325 Ytank_sB, FALSE, TRUE,
6326 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6329 Ytank_w, FALSE, FALSE,
6330 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6333 Ytank_wB, FALSE, TRUE,
6334 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6337 Ytank_w_n, FALSE, FALSE,
6338 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6341 Ytank_n_e, FALSE, FALSE,
6342 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6345 Ytank_e_s, FALSE, FALSE,
6346 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6349 Ytank_s_w, FALSE, FALSE,
6350 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6353 Ytank_e_n, FALSE, FALSE,
6354 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6357 Ytank_s_e, FALSE, FALSE,
6358 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6361 Ytank_w_s, FALSE, FALSE,
6362 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6365 Ytank_n_w, FALSE, FALSE,
6366 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6369 Ytank_stone, FALSE, FALSE,
6370 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6373 Ytank_spring, FALSE, FALSE,
6374 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6378 Xemerald, TRUE, FALSE,
6382 Xemerald_pause, FALSE, FALSE,
6386 Xemerald_fall, FALSE, FALSE,
6390 Xemerald_shine, FALSE, FALSE,
6391 EL_EMERALD, ACTION_TWINKLING, -1
6394 Yemerald_s, FALSE, FALSE,
6395 EL_EMERALD, ACTION_FALLING, -1
6398 Yemerald_sB, FALSE, TRUE,
6399 EL_EMERALD, ACTION_FALLING, -1
6402 Yemerald_e, FALSE, FALSE,
6403 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6406 Yemerald_eB, FALSE, TRUE,
6407 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6410 Yemerald_w, FALSE, FALSE,
6411 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6414 Yemerald_wB, FALSE, TRUE,
6415 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6418 Yemerald_blank, FALSE, FALSE,
6419 EL_EMERALD, ACTION_COLLECTING, -1
6423 Xdiamond, TRUE, FALSE,
6427 Xdiamond_pause, FALSE, FALSE,
6431 Xdiamond_fall, FALSE, FALSE,
6435 Xdiamond_shine, FALSE, FALSE,
6436 EL_DIAMOND, ACTION_TWINKLING, -1
6439 Ydiamond_s, FALSE, FALSE,
6440 EL_DIAMOND, ACTION_FALLING, -1
6443 Ydiamond_sB, FALSE, TRUE,
6444 EL_DIAMOND, ACTION_FALLING, -1
6447 Ydiamond_e, FALSE, FALSE,
6448 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6451 Ydiamond_eB, FALSE, TRUE,
6452 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6455 Ydiamond_w, FALSE, FALSE,
6456 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6459 Ydiamond_wB, FALSE, TRUE,
6460 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6463 Ydiamond_blank, FALSE, FALSE,
6464 EL_DIAMOND, ACTION_COLLECTING, -1
6467 Ydiamond_stone, FALSE, FALSE,
6468 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6472 Xstone, TRUE, FALSE,
6476 Xstone_pause, FALSE, FALSE,
6480 Xstone_fall, FALSE, FALSE,
6484 Ystone_s, FALSE, FALSE,
6485 EL_ROCK, ACTION_FALLING, -1
6488 Ystone_sB, FALSE, TRUE,
6489 EL_ROCK, ACTION_FALLING, -1
6492 Ystone_e, FALSE, FALSE,
6493 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6496 Ystone_eB, FALSE, TRUE,
6497 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6500 Ystone_w, FALSE, FALSE,
6501 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6504 Ystone_wB, FALSE, TRUE,
6505 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6513 Xbomb_pause, FALSE, FALSE,
6517 Xbomb_fall, FALSE, FALSE,
6521 Ybomb_s, FALSE, FALSE,
6522 EL_BOMB, ACTION_FALLING, -1
6525 Ybomb_sB, FALSE, TRUE,
6526 EL_BOMB, ACTION_FALLING, -1
6529 Ybomb_e, FALSE, FALSE,
6530 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6533 Ybomb_eB, FALSE, TRUE,
6534 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6537 Ybomb_w, FALSE, FALSE,
6538 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6541 Ybomb_wB, FALSE, TRUE,
6542 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6545 Ybomb_blank, FALSE, FALSE,
6546 EL_BOMB, ACTION_ACTIVATING, -1
6554 Xnut_pause, FALSE, FALSE,
6558 Xnut_fall, FALSE, FALSE,
6562 Ynut_s, FALSE, FALSE,
6563 EL_NUT, ACTION_FALLING, -1
6566 Ynut_sB, FALSE, TRUE,
6567 EL_NUT, ACTION_FALLING, -1
6570 Ynut_e, FALSE, FALSE,
6571 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6574 Ynut_eB, FALSE, TRUE,
6575 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6578 Ynut_w, FALSE, FALSE,
6579 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6582 Ynut_wB, FALSE, TRUE,
6583 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6586 Ynut_stone, FALSE, FALSE,
6587 EL_NUT, ACTION_BREAKING, -1
6591 Xspring, TRUE, FALSE,
6595 Xspring_pause, FALSE, FALSE,
6599 Xspring_e, TRUE, FALSE,
6600 EL_SPRING_RIGHT, -1, -1
6603 Xspring_w, TRUE, FALSE,
6604 EL_SPRING_LEFT, -1, -1
6607 Xspring_fall, FALSE, FALSE,
6611 Yspring_s, FALSE, FALSE,
6612 EL_SPRING, ACTION_FALLING, -1
6615 Yspring_sB, FALSE, TRUE,
6616 EL_SPRING, ACTION_FALLING, -1
6619 Yspring_e, FALSE, FALSE,
6620 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6623 Yspring_eB, FALSE, TRUE,
6624 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6627 Yspring_w, FALSE, FALSE,
6628 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6631 Yspring_wB, FALSE, TRUE,
6632 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6635 Yspring_alien_e, FALSE, FALSE,
6636 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6639 Yspring_alien_eB, FALSE, TRUE,
6640 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6643 Yspring_alien_w, FALSE, FALSE,
6644 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6647 Yspring_alien_wB, FALSE, TRUE,
6648 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6652 Xpush_emerald_e, FALSE, FALSE,
6653 EL_EMERALD, -1, MV_BIT_RIGHT
6656 Xpush_emerald_w, FALSE, FALSE,
6657 EL_EMERALD, -1, MV_BIT_LEFT
6660 Xpush_diamond_e, FALSE, FALSE,
6661 EL_DIAMOND, -1, MV_BIT_RIGHT
6664 Xpush_diamond_w, FALSE, FALSE,
6665 EL_DIAMOND, -1, MV_BIT_LEFT
6668 Xpush_stone_e, FALSE, FALSE,
6669 EL_ROCK, -1, MV_BIT_RIGHT
6672 Xpush_stone_w, FALSE, FALSE,
6673 EL_ROCK, -1, MV_BIT_LEFT
6676 Xpush_bomb_e, FALSE, FALSE,
6677 EL_BOMB, -1, MV_BIT_RIGHT
6680 Xpush_bomb_w, FALSE, FALSE,
6681 EL_BOMB, -1, MV_BIT_LEFT
6684 Xpush_nut_e, FALSE, FALSE,
6685 EL_NUT, -1, MV_BIT_RIGHT
6688 Xpush_nut_w, FALSE, FALSE,
6689 EL_NUT, -1, MV_BIT_LEFT
6692 Xpush_spring_e, FALSE, FALSE,
6693 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6696 Xpush_spring_w, FALSE, FALSE,
6697 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6701 Xdynamite, TRUE, FALSE,
6702 EL_EM_DYNAMITE, -1, -1
6705 Ydynamite_blank, FALSE, FALSE,
6706 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6709 Xdynamite_1, TRUE, FALSE,
6710 EL_EM_DYNAMITE_ACTIVE, -1, -1
6713 Xdynamite_2, FALSE, FALSE,
6714 EL_EM_DYNAMITE_ACTIVE, -1, -1
6717 Xdynamite_3, FALSE, FALSE,
6718 EL_EM_DYNAMITE_ACTIVE, -1, -1
6721 Xdynamite_4, FALSE, FALSE,
6722 EL_EM_DYNAMITE_ACTIVE, -1, -1
6726 Xkey_1, TRUE, FALSE,
6730 Xkey_2, TRUE, FALSE,
6734 Xkey_3, TRUE, FALSE,
6738 Xkey_4, TRUE, FALSE,
6742 Xkey_5, TRUE, FALSE,
6743 EL_EMC_KEY_5, -1, -1
6746 Xkey_6, TRUE, FALSE,
6747 EL_EMC_KEY_6, -1, -1
6750 Xkey_7, TRUE, FALSE,
6751 EL_EMC_KEY_7, -1, -1
6754 Xkey_8, TRUE, FALSE,
6755 EL_EMC_KEY_8, -1, -1
6759 Xdoor_1, TRUE, FALSE,
6760 EL_EM_GATE_1, -1, -1
6763 Xdoor_2, TRUE, FALSE,
6764 EL_EM_GATE_2, -1, -1
6767 Xdoor_3, TRUE, FALSE,
6768 EL_EM_GATE_3, -1, -1
6771 Xdoor_4, TRUE, FALSE,
6772 EL_EM_GATE_4, -1, -1
6775 Xdoor_5, TRUE, FALSE,
6776 EL_EMC_GATE_5, -1, -1
6779 Xdoor_6, TRUE, FALSE,
6780 EL_EMC_GATE_6, -1, -1
6783 Xdoor_7, TRUE, FALSE,
6784 EL_EMC_GATE_7, -1, -1
6787 Xdoor_8, TRUE, FALSE,
6788 EL_EMC_GATE_8, -1, -1
6792 Xfake_door_1, TRUE, FALSE,
6793 EL_EM_GATE_1_GRAY, -1, -1
6796 Xfake_door_2, TRUE, FALSE,
6797 EL_EM_GATE_2_GRAY, -1, -1
6800 Xfake_door_3, TRUE, FALSE,
6801 EL_EM_GATE_3_GRAY, -1, -1
6804 Xfake_door_4, TRUE, FALSE,
6805 EL_EM_GATE_4_GRAY, -1, -1
6808 Xfake_door_5, TRUE, FALSE,
6809 EL_EMC_GATE_5_GRAY, -1, -1
6812 Xfake_door_6, TRUE, FALSE,
6813 EL_EMC_GATE_6_GRAY, -1, -1
6816 Xfake_door_7, TRUE, FALSE,
6817 EL_EMC_GATE_7_GRAY, -1, -1
6820 Xfake_door_8, TRUE, FALSE,
6821 EL_EMC_GATE_8_GRAY, -1, -1
6825 Xballoon, TRUE, FALSE,
6829 Yballoon_n, FALSE, FALSE,
6830 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6833 Yballoon_nB, FALSE, TRUE,
6834 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6837 Yballoon_e, FALSE, FALSE,
6838 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6841 Yballoon_eB, FALSE, TRUE,
6842 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6845 Yballoon_s, FALSE, FALSE,
6846 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6849 Yballoon_sB, FALSE, TRUE,
6850 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6853 Yballoon_w, FALSE, FALSE,
6854 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6857 Yballoon_wB, FALSE, TRUE,
6858 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6862 Xball_1, TRUE, FALSE,
6863 EL_EMC_MAGIC_BALL, -1, -1
6866 Yball_1, FALSE, FALSE,
6867 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6870 Xball_2, FALSE, FALSE,
6871 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6874 Yball_2, FALSE, FALSE,
6875 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6878 Yball_blank, FALSE, FALSE,
6879 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6883 Xamoeba_1, TRUE, FALSE,
6884 EL_AMOEBA_DRY, ACTION_OTHER, -1
6887 Xamoeba_2, FALSE, FALSE,
6888 EL_AMOEBA_DRY, ACTION_OTHER, -1
6891 Xamoeba_3, FALSE, FALSE,
6892 EL_AMOEBA_DRY, ACTION_OTHER, -1
6895 Xamoeba_4, FALSE, FALSE,
6896 EL_AMOEBA_DRY, ACTION_OTHER, -1
6899 Xamoeba_5, TRUE, FALSE,
6900 EL_AMOEBA_WET, ACTION_OTHER, -1
6903 Xamoeba_6, FALSE, FALSE,
6904 EL_AMOEBA_WET, ACTION_OTHER, -1
6907 Xamoeba_7, FALSE, FALSE,
6908 EL_AMOEBA_WET, ACTION_OTHER, -1
6911 Xamoeba_8, FALSE, FALSE,
6912 EL_AMOEBA_WET, ACTION_OTHER, -1
6917 EL_AMOEBA_DROP, ACTION_GROWING, -1
6920 Xdrip_fall, FALSE, FALSE,
6921 EL_AMOEBA_DROP, -1, -1
6924 Xdrip_stretch, FALSE, FALSE,
6925 EL_AMOEBA_DROP, ACTION_FALLING, -1
6928 Xdrip_stretchB, FALSE, TRUE,
6929 EL_AMOEBA_DROP, ACTION_FALLING, -1
6932 Ydrip_1_s, FALSE, FALSE,
6933 EL_AMOEBA_DROP, ACTION_FALLING, -1
6936 Ydrip_1_sB, FALSE, TRUE,
6937 EL_AMOEBA_DROP, ACTION_FALLING, -1
6940 Ydrip_2_s, FALSE, FALSE,
6941 EL_AMOEBA_DROP, ACTION_FALLING, -1
6944 Ydrip_2_sB, FALSE, TRUE,
6945 EL_AMOEBA_DROP, ACTION_FALLING, -1
6949 Xwonderwall, TRUE, FALSE,
6950 EL_MAGIC_WALL, -1, -1
6953 Ywonderwall, FALSE, FALSE,
6954 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6958 Xwheel, TRUE, FALSE,
6959 EL_ROBOT_WHEEL, -1, -1
6962 Ywheel, FALSE, FALSE,
6963 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6967 Xswitch, TRUE, FALSE,
6968 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6971 Yswitch, FALSE, FALSE,
6972 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6976 Xbumper, TRUE, FALSE,
6977 EL_EMC_SPRING_BUMPER, -1, -1
6980 Ybumper, FALSE, FALSE,
6981 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6985 Xacid_nw, TRUE, FALSE,
6986 EL_ACID_POOL_TOPLEFT, -1, -1
6989 Xacid_ne, TRUE, FALSE,
6990 EL_ACID_POOL_TOPRIGHT, -1, -1
6993 Xacid_sw, TRUE, FALSE,
6994 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6997 Xacid_s, TRUE, FALSE,
6998 EL_ACID_POOL_BOTTOM, -1, -1
7001 Xacid_se, TRUE, FALSE,
7002 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7006 Xfake_blank, TRUE, FALSE,
7007 EL_INVISIBLE_WALL, -1, -1
7010 Yfake_blank, FALSE, FALSE,
7011 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7015 Xfake_grass, TRUE, FALSE,
7016 EL_EMC_FAKE_GRASS, -1, -1
7019 Yfake_grass, FALSE, FALSE,
7020 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7024 Xfake_amoeba, TRUE, FALSE,
7025 EL_EMC_DRIPPER, -1, -1
7028 Yfake_amoeba, FALSE, FALSE,
7029 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7033 Xlenses, TRUE, FALSE,
7034 EL_EMC_LENSES, -1, -1
7038 Xmagnify, TRUE, FALSE,
7039 EL_EMC_MAGNIFIER, -1, -1
7044 EL_QUICKSAND_EMPTY, -1, -1
7047 Xsand_stone, TRUE, FALSE,
7048 EL_QUICKSAND_FULL, -1, -1
7051 Xsand_stonein_1, FALSE, TRUE,
7052 EL_ROCK, ACTION_FILLING, -1
7055 Xsand_stonein_2, FALSE, TRUE,
7056 EL_ROCK, ACTION_FILLING, -1
7059 Xsand_stonein_3, FALSE, TRUE,
7060 EL_ROCK, ACTION_FILLING, -1
7063 Xsand_stonein_4, FALSE, TRUE,
7064 EL_ROCK, ACTION_FILLING, -1
7067 Xsand_sandstone_1, FALSE, FALSE,
7068 EL_QUICKSAND_FILLING, -1, -1
7071 Xsand_sandstone_2, FALSE, FALSE,
7072 EL_QUICKSAND_FILLING, -1, -1
7075 Xsand_sandstone_3, FALSE, FALSE,
7076 EL_QUICKSAND_FILLING, -1, -1
7079 Xsand_sandstone_4, FALSE, FALSE,
7080 EL_QUICKSAND_FILLING, -1, -1
7083 Xsand_stonesand_1, FALSE, FALSE,
7084 EL_QUICKSAND_EMPTYING, -1, -1
7087 Xsand_stonesand_2, FALSE, FALSE,
7088 EL_QUICKSAND_EMPTYING, -1, -1
7091 Xsand_stonesand_3, FALSE, FALSE,
7092 EL_QUICKSAND_EMPTYING, -1, -1
7095 Xsand_stonesand_4, FALSE, FALSE,
7096 EL_QUICKSAND_EMPTYING, -1, -1
7099 Xsand_stoneout_1, FALSE, FALSE,
7100 EL_ROCK, ACTION_EMPTYING, -1
7103 Xsand_stoneout_2, FALSE, FALSE,
7104 EL_ROCK, ACTION_EMPTYING, -1
7107 Xsand_stonesand_quickout_1, FALSE, FALSE,
7108 EL_QUICKSAND_EMPTYING, -1, -1
7111 Xsand_stonesand_quickout_2, FALSE, FALSE,
7112 EL_QUICKSAND_EMPTYING, -1, -1
7116 Xslide_ns, TRUE, FALSE,
7117 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7120 Yslide_ns_blank, FALSE, FALSE,
7121 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7124 Xslide_ew, TRUE, FALSE,
7125 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7128 Yslide_ew_blank, FALSE, FALSE,
7129 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7133 Xwind_n, TRUE, FALSE,
7134 EL_BALLOON_SWITCH_UP, -1, -1
7137 Xwind_e, TRUE, FALSE,
7138 EL_BALLOON_SWITCH_RIGHT, -1, -1
7141 Xwind_s, TRUE, FALSE,
7142 EL_BALLOON_SWITCH_DOWN, -1, -1
7145 Xwind_w, TRUE, FALSE,
7146 EL_BALLOON_SWITCH_LEFT, -1, -1
7149 Xwind_any, TRUE, FALSE,
7150 EL_BALLOON_SWITCH_ANY, -1, -1
7153 Xwind_stop, TRUE, FALSE,
7154 EL_BALLOON_SWITCH_NONE, -1, -1
7159 EL_EM_EXIT_CLOSED, -1, -1
7162 Xexit_1, TRUE, FALSE,
7163 EL_EM_EXIT_OPEN, -1, -1
7166 Xexit_2, FALSE, FALSE,
7167 EL_EM_EXIT_OPEN, -1, -1
7170 Xexit_3, FALSE, FALSE,
7171 EL_EM_EXIT_OPEN, -1, -1
7175 Xpause, FALSE, FALSE,
7180 Xwall_1, TRUE, FALSE,
7184 Xwall_2, TRUE, FALSE,
7185 EL_EMC_WALL_14, -1, -1
7188 Xwall_3, TRUE, FALSE,
7189 EL_EMC_WALL_15, -1, -1
7192 Xwall_4, TRUE, FALSE,
7193 EL_EMC_WALL_16, -1, -1
7197 Xroundwall_1, TRUE, FALSE,
7198 EL_WALL_SLIPPERY, -1, -1
7201 Xroundwall_2, TRUE, FALSE,
7202 EL_EMC_WALL_SLIPPERY_2, -1, -1
7205 Xroundwall_3, TRUE, FALSE,
7206 EL_EMC_WALL_SLIPPERY_3, -1, -1
7209 Xroundwall_4, TRUE, FALSE,
7210 EL_EMC_WALL_SLIPPERY_4, -1, -1
7214 Xsteel_1, TRUE, FALSE,
7215 EL_STEELWALL, -1, -1
7218 Xsteel_2, TRUE, FALSE,
7219 EL_EMC_STEELWALL_2, -1, -1
7222 Xsteel_3, TRUE, FALSE,
7223 EL_EMC_STEELWALL_3, -1, -1
7226 Xsteel_4, TRUE, FALSE,
7227 EL_EMC_STEELWALL_4, -1, -1
7231 Xdecor_1, TRUE, FALSE,
7232 EL_EMC_WALL_8, -1, -1
7235 Xdecor_2, TRUE, FALSE,
7236 EL_EMC_WALL_6, -1, -1
7239 Xdecor_3, TRUE, FALSE,
7240 EL_EMC_WALL_4, -1, -1
7243 Xdecor_4, TRUE, FALSE,
7244 EL_EMC_WALL_7, -1, -1
7247 Xdecor_5, TRUE, FALSE,
7248 EL_EMC_WALL_5, -1, -1
7251 Xdecor_6, TRUE, FALSE,
7252 EL_EMC_WALL_9, -1, -1
7255 Xdecor_7, TRUE, FALSE,
7256 EL_EMC_WALL_10, -1, -1
7259 Xdecor_8, TRUE, FALSE,
7260 EL_EMC_WALL_1, -1, -1
7263 Xdecor_9, TRUE, FALSE,
7264 EL_EMC_WALL_2, -1, -1
7267 Xdecor_10, TRUE, FALSE,
7268 EL_EMC_WALL_3, -1, -1
7271 Xdecor_11, TRUE, FALSE,
7272 EL_EMC_WALL_11, -1, -1
7275 Xdecor_12, TRUE, FALSE,
7276 EL_EMC_WALL_12, -1, -1
7280 Xalpha_0, TRUE, FALSE,
7281 EL_CHAR('0'), -1, -1
7284 Xalpha_1, TRUE, FALSE,
7285 EL_CHAR('1'), -1, -1
7288 Xalpha_2, TRUE, FALSE,
7289 EL_CHAR('2'), -1, -1
7292 Xalpha_3, TRUE, FALSE,
7293 EL_CHAR('3'), -1, -1
7296 Xalpha_4, TRUE, FALSE,
7297 EL_CHAR('4'), -1, -1
7300 Xalpha_5, TRUE, FALSE,
7301 EL_CHAR('5'), -1, -1
7304 Xalpha_6, TRUE, FALSE,
7305 EL_CHAR('6'), -1, -1
7308 Xalpha_7, TRUE, FALSE,
7309 EL_CHAR('7'), -1, -1
7312 Xalpha_8, TRUE, FALSE,
7313 EL_CHAR('8'), -1, -1
7316 Xalpha_9, TRUE, FALSE,
7317 EL_CHAR('9'), -1, -1
7320 Xalpha_excla, TRUE, FALSE,
7321 EL_CHAR('!'), -1, -1
7324 Xalpha_apost, TRUE, FALSE,
7325 EL_CHAR('\''), -1, -1
7328 Xalpha_comma, TRUE, FALSE,
7329 EL_CHAR(','), -1, -1
7332 Xalpha_minus, TRUE, FALSE,
7333 EL_CHAR('-'), -1, -1
7336 Xalpha_perio, TRUE, FALSE,
7337 EL_CHAR('.'), -1, -1
7340 Xalpha_colon, TRUE, FALSE,
7341 EL_CHAR(':'), -1, -1
7344 Xalpha_quest, TRUE, FALSE,
7345 EL_CHAR('?'), -1, -1
7348 Xalpha_a, TRUE, FALSE,
7349 EL_CHAR('A'), -1, -1
7352 Xalpha_b, TRUE, FALSE,
7353 EL_CHAR('B'), -1, -1
7356 Xalpha_c, TRUE, FALSE,
7357 EL_CHAR('C'), -1, -1
7360 Xalpha_d, TRUE, FALSE,
7361 EL_CHAR('D'), -1, -1
7364 Xalpha_e, TRUE, FALSE,
7365 EL_CHAR('E'), -1, -1
7368 Xalpha_f, TRUE, FALSE,
7369 EL_CHAR('F'), -1, -1
7372 Xalpha_g, TRUE, FALSE,
7373 EL_CHAR('G'), -1, -1
7376 Xalpha_h, TRUE, FALSE,
7377 EL_CHAR('H'), -1, -1
7380 Xalpha_i, TRUE, FALSE,
7381 EL_CHAR('I'), -1, -1
7384 Xalpha_j, TRUE, FALSE,
7385 EL_CHAR('J'), -1, -1
7388 Xalpha_k, TRUE, FALSE,
7389 EL_CHAR('K'), -1, -1
7392 Xalpha_l, TRUE, FALSE,
7393 EL_CHAR('L'), -1, -1
7396 Xalpha_m, TRUE, FALSE,
7397 EL_CHAR('M'), -1, -1
7400 Xalpha_n, TRUE, FALSE,
7401 EL_CHAR('N'), -1, -1
7404 Xalpha_o, TRUE, FALSE,
7405 EL_CHAR('O'), -1, -1
7408 Xalpha_p, TRUE, FALSE,
7409 EL_CHAR('P'), -1, -1
7412 Xalpha_q, TRUE, FALSE,
7413 EL_CHAR('Q'), -1, -1
7416 Xalpha_r, TRUE, FALSE,
7417 EL_CHAR('R'), -1, -1
7420 Xalpha_s, TRUE, FALSE,
7421 EL_CHAR('S'), -1, -1
7424 Xalpha_t, TRUE, FALSE,
7425 EL_CHAR('T'), -1, -1
7428 Xalpha_u, TRUE, FALSE,
7429 EL_CHAR('U'), -1, -1
7432 Xalpha_v, TRUE, FALSE,
7433 EL_CHAR('V'), -1, -1
7436 Xalpha_w, TRUE, FALSE,
7437 EL_CHAR('W'), -1, -1
7440 Xalpha_x, TRUE, FALSE,
7441 EL_CHAR('X'), -1, -1
7444 Xalpha_y, TRUE, FALSE,
7445 EL_CHAR('Y'), -1, -1
7448 Xalpha_z, TRUE, FALSE,
7449 EL_CHAR('Z'), -1, -1
7452 Xalpha_arrow_e, TRUE, FALSE,
7453 EL_CHAR('>'), -1, -1
7456 Xalpha_arrow_w, TRUE, FALSE,
7457 EL_CHAR('<'), -1, -1
7460 Xalpha_copyr, TRUE, FALSE,
7461 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7465 Ykey_1_blank, FALSE, FALSE,
7466 EL_EM_KEY_1, ACTION_COLLECTING, -1
7469 Ykey_2_blank, FALSE, FALSE,
7470 EL_EM_KEY_2, ACTION_COLLECTING, -1
7473 Ykey_3_blank, FALSE, FALSE,
7474 EL_EM_KEY_3, ACTION_COLLECTING, -1
7477 Ykey_4_blank, FALSE, FALSE,
7478 EL_EM_KEY_4, ACTION_COLLECTING, -1
7481 Ykey_5_blank, FALSE, FALSE,
7482 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7485 Ykey_6_blank, FALSE, FALSE,
7486 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7489 Ykey_7_blank, FALSE, FALSE,
7490 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7493 Ykey_8_blank, FALSE, FALSE,
7494 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7497 Ylenses_blank, FALSE, FALSE,
7498 EL_EMC_LENSES, ACTION_COLLECTING, -1
7501 Ymagnify_blank, FALSE, FALSE,
7502 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7505 Ygrass_blank, FALSE, FALSE,
7506 EL_EMC_GRASS, ACTION_SNAPPING, -1
7509 Ydirt_blank, FALSE, FALSE,
7510 EL_SAND, ACTION_SNAPPING, -1
7519 static struct Mapping_EM_to_RND_player
7528 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7532 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7536 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7540 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7544 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7548 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7552 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7556 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7560 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7564 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7568 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7572 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7576 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7580 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7584 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7588 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7592 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7596 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7600 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7604 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7608 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7612 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7616 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7620 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7624 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7628 EL_PLAYER_1, ACTION_DEFAULT, -1,
7632 EL_PLAYER_2, ACTION_DEFAULT, -1,
7636 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7640 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7644 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7648 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7652 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7656 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7660 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7664 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7668 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7672 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7676 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7680 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7684 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7688 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7692 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7696 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7700 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7704 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7708 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7712 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7716 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7720 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7724 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7728 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7732 EL_PLAYER_3, ACTION_DEFAULT, -1,
7736 EL_PLAYER_4, ACTION_DEFAULT, -1,
7745 int map_element_RND_to_EM_cave(int element_rnd)
7747 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7748 static boolean mapping_initialized = FALSE;
7750 if (!mapping_initialized)
7754 // return "Xalpha_quest" for all undefined elements in mapping array
7755 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7756 mapping_RND_to_EM[i] = Xalpha_quest;
7758 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7759 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7760 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7761 em_object_mapping_list[i].element_em;
7763 mapping_initialized = TRUE;
7766 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7768 Warn("invalid RND level element %d", element_rnd);
7773 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7776 int map_element_EM_to_RND_cave(int element_em_cave)
7778 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7779 static boolean mapping_initialized = FALSE;
7781 if (!mapping_initialized)
7785 // return "EL_UNKNOWN" for all undefined elements in mapping array
7786 for (i = 0; i < GAME_TILE_MAX; i++)
7787 mapping_EM_to_RND[i] = EL_UNKNOWN;
7789 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7790 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7791 em_object_mapping_list[i].element_rnd;
7793 mapping_initialized = TRUE;
7796 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7798 Warn("invalid EM cave element %d", element_em_cave);
7803 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7806 int map_element_EM_to_RND_game(int element_em_game)
7808 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7809 static boolean mapping_initialized = FALSE;
7811 if (!mapping_initialized)
7815 // return "EL_UNKNOWN" for all undefined elements in mapping array
7816 for (i = 0; i < GAME_TILE_MAX; i++)
7817 mapping_EM_to_RND[i] = EL_UNKNOWN;
7819 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7820 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7821 em_object_mapping_list[i].element_rnd;
7823 mapping_initialized = TRUE;
7826 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7828 Warn("invalid EM game element %d", element_em_game);
7833 return mapping_EM_to_RND[element_em_game];
7836 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7838 struct LevelInfo_EM *level_em = level->native_em_level;
7839 struct CAVE *cav = level_em->cav;
7842 for (i = 0; i < GAME_TILE_MAX; i++)
7843 cav->android_array[i] = Cblank;
7845 for (i = 0; i < level->num_android_clone_elements; i++)
7847 int element_rnd = level->android_clone_element[i];
7848 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7850 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7851 if (em_object_mapping_list[j].element_rnd == element_rnd)
7852 cav->android_array[em_object_mapping_list[j].element_em] =
7857 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7859 struct LevelInfo_EM *level_em = level->native_em_level;
7860 struct CAVE *cav = level_em->cav;
7863 level->num_android_clone_elements = 0;
7865 for (i = 0; i < GAME_TILE_MAX; i++)
7867 int element_em_cave = cav->android_array[i];
7869 boolean element_found = FALSE;
7871 if (element_em_cave == Cblank)
7874 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7876 for (j = 0; j < level->num_android_clone_elements; j++)
7877 if (level->android_clone_element[j] == element_rnd)
7878 element_found = TRUE;
7882 level->android_clone_element[level->num_android_clone_elements++] =
7885 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7890 if (level->num_android_clone_elements == 0)
7892 level->num_android_clone_elements = 1;
7893 level->android_clone_element[0] = EL_EMPTY;
7897 int map_direction_RND_to_EM(int direction)
7899 return (direction == MV_UP ? 0 :
7900 direction == MV_RIGHT ? 1 :
7901 direction == MV_DOWN ? 2 :
7902 direction == MV_LEFT ? 3 :
7906 int map_direction_EM_to_RND(int direction)
7908 return (direction == 0 ? MV_UP :
7909 direction == 1 ? MV_RIGHT :
7910 direction == 2 ? MV_DOWN :
7911 direction == 3 ? MV_LEFT :
7915 int map_element_RND_to_SP(int element_rnd)
7917 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7919 if (element_rnd >= EL_SP_START &&
7920 element_rnd <= EL_SP_END)
7921 element_sp = element_rnd - EL_SP_START;
7922 else if (element_rnd == EL_EMPTY_SPACE)
7924 else if (element_rnd == EL_INVISIBLE_WALL)
7930 int map_element_SP_to_RND(int element_sp)
7932 int element_rnd = EL_UNKNOWN;
7934 if (element_sp >= 0x00 &&
7936 element_rnd = EL_SP_START + element_sp;
7937 else if (element_sp == 0x28)
7938 element_rnd = EL_INVISIBLE_WALL;
7943 int map_action_SP_to_RND(int action_sp)
7947 case actActive: return ACTION_ACTIVE;
7948 case actImpact: return ACTION_IMPACT;
7949 case actExploding: return ACTION_EXPLODING;
7950 case actDigging: return ACTION_DIGGING;
7951 case actSnapping: return ACTION_SNAPPING;
7952 case actCollecting: return ACTION_COLLECTING;
7953 case actPassing: return ACTION_PASSING;
7954 case actPushing: return ACTION_PUSHING;
7955 case actDropping: return ACTION_DROPPING;
7957 default: return ACTION_DEFAULT;
7961 int map_element_RND_to_MM(int element_rnd)
7963 return (element_rnd >= EL_MM_START_1 &&
7964 element_rnd <= EL_MM_END_1 ?
7965 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7967 element_rnd >= EL_MM_START_2 &&
7968 element_rnd <= EL_MM_END_2 ?
7969 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7971 element_rnd >= EL_CHAR_START &&
7972 element_rnd <= EL_CHAR_END ?
7973 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7975 element_rnd >= EL_MM_RUNTIME_START &&
7976 element_rnd <= EL_MM_RUNTIME_END ?
7977 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7979 element_rnd >= EL_MM_DUMMY_START &&
7980 element_rnd <= EL_MM_DUMMY_END ?
7981 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7983 EL_MM_EMPTY_NATIVE);
7986 int map_element_MM_to_RND(int element_mm)
7988 return (element_mm == EL_MM_EMPTY_NATIVE ||
7989 element_mm == EL_DF_EMPTY_NATIVE ?
7992 element_mm >= EL_MM_START_1_NATIVE &&
7993 element_mm <= EL_MM_END_1_NATIVE ?
7994 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7996 element_mm >= EL_MM_START_2_NATIVE &&
7997 element_mm <= EL_MM_END_2_NATIVE ?
7998 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8000 element_mm >= EL_MM_CHAR_START_NATIVE &&
8001 element_mm <= EL_MM_CHAR_END_NATIVE ?
8002 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8004 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8005 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8006 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8008 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8009 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8010 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8015 int map_action_MM_to_RND(int action_mm)
8017 // all MM actions are defined to exactly match their RND counterparts
8021 int map_sound_MM_to_RND(int sound_mm)
8025 case SND_MM_GAME_LEVELTIME_CHARGING:
8026 return SND_GAME_LEVELTIME_CHARGING;
8028 case SND_MM_GAME_HEALTH_CHARGING:
8029 return SND_GAME_HEALTH_CHARGING;
8032 return SND_UNDEFINED;
8036 int map_mm_wall_element(int element)
8038 return (element >= EL_MM_STEEL_WALL_START &&
8039 element <= EL_MM_STEEL_WALL_END ?
8042 element >= EL_MM_WOODEN_WALL_START &&
8043 element <= EL_MM_WOODEN_WALL_END ?
8046 element >= EL_MM_ICE_WALL_START &&
8047 element <= EL_MM_ICE_WALL_END ?
8050 element >= EL_MM_AMOEBA_WALL_START &&
8051 element <= EL_MM_AMOEBA_WALL_END ?
8054 element >= EL_DF_STEEL_WALL_START &&
8055 element <= EL_DF_STEEL_WALL_END ?
8058 element >= EL_DF_WOODEN_WALL_START &&
8059 element <= EL_DF_WOODEN_WALL_END ?
8065 int map_mm_wall_element_editor(int element)
8069 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8070 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8071 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8072 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8073 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8074 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8076 default: return element;
8080 int get_next_element(int element)
8084 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8085 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8086 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8087 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8088 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8089 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8090 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8091 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8092 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8093 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8094 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8096 default: return element;
8100 int el2img_mm(int element_mm)
8102 return el2img(map_element_MM_to_RND(element_mm));
8105 int el_act_dir2img(int element, int action, int direction)
8107 element = GFX_ELEMENT(element);
8108 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8110 // direction_graphic[][] == graphic[] for undefined direction graphics
8111 return element_info[element].direction_graphic[action][direction];
8114 static int el_act_dir2crm(int element, int action, int direction)
8116 element = GFX_ELEMENT(element);
8117 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8119 // direction_graphic[][] == graphic[] for undefined direction graphics
8120 return element_info[element].direction_crumbled[action][direction];
8123 int el_act2img(int element, int action)
8125 element = GFX_ELEMENT(element);
8127 return element_info[element].graphic[action];
8130 int el_act2crm(int element, int action)
8132 element = GFX_ELEMENT(element);
8134 return element_info[element].crumbled[action];
8137 int el_dir2img(int element, int direction)
8139 element = GFX_ELEMENT(element);
8141 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8144 int el2baseimg(int element)
8146 return element_info[element].graphic[ACTION_DEFAULT];
8149 int el2img(int element)
8151 element = GFX_ELEMENT(element);
8153 return element_info[element].graphic[ACTION_DEFAULT];
8156 int el2edimg(int element)
8158 element = GFX_ELEMENT(element);
8160 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8163 int el2preimg(int element)
8165 element = GFX_ELEMENT(element);
8167 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8170 int el2panelimg(int element)
8172 element = GFX_ELEMENT(element);
8174 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8177 int font2baseimg(int font_nr)
8179 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8182 int getBeltNrFromBeltElement(int element)
8184 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8185 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8186 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8189 int getBeltNrFromBeltActiveElement(int element)
8191 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8192 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8193 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8196 int getBeltNrFromBeltSwitchElement(int element)
8198 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8199 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8200 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8203 int getBeltDirNrFromBeltElement(int element)
8205 static int belt_base_element[4] =
8207 EL_CONVEYOR_BELT_1_LEFT,
8208 EL_CONVEYOR_BELT_2_LEFT,
8209 EL_CONVEYOR_BELT_3_LEFT,
8210 EL_CONVEYOR_BELT_4_LEFT
8213 int belt_nr = getBeltNrFromBeltElement(element);
8214 int belt_dir_nr = element - belt_base_element[belt_nr];
8216 return (belt_dir_nr % 3);
8219 int getBeltDirNrFromBeltSwitchElement(int element)
8221 static int belt_base_element[4] =
8223 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8224 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8225 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8226 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8229 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8230 int belt_dir_nr = element - belt_base_element[belt_nr];
8232 return (belt_dir_nr % 3);
8235 int getBeltDirFromBeltElement(int element)
8237 static int belt_move_dir[3] =
8244 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8246 return belt_move_dir[belt_dir_nr];
8249 int getBeltDirFromBeltSwitchElement(int element)
8251 static int belt_move_dir[3] =
8258 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8260 return belt_move_dir[belt_dir_nr];
8263 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8265 static int belt_base_element[4] =
8267 EL_CONVEYOR_BELT_1_LEFT,
8268 EL_CONVEYOR_BELT_2_LEFT,
8269 EL_CONVEYOR_BELT_3_LEFT,
8270 EL_CONVEYOR_BELT_4_LEFT
8273 return belt_base_element[belt_nr] + belt_dir_nr;
8276 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8278 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8280 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8283 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8285 static int belt_base_element[4] =
8287 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8288 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8289 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8290 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8293 return belt_base_element[belt_nr] + belt_dir_nr;
8296 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8298 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8300 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8303 boolean swapTiles_EM(boolean is_pre_emc_cave)
8305 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8308 boolean getTeamMode_EM(void)
8310 return game.team_mode || network_playing;
8313 boolean isActivePlayer_EM(int player_nr)
8315 return stored_player[player_nr].active;
8318 unsigned int InitRND(int seed)
8320 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8321 return InitEngineRandom_EM(seed);
8322 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8323 return InitEngineRandom_SP(seed);
8324 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8325 return InitEngineRandom_MM(seed);
8327 return InitEngineRandom_RND(seed);
8330 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8331 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8333 static int get_effective_element_EM(int tile, int frame_em)
8335 int element = object_mapping[tile].element_rnd;
8336 int action = object_mapping[tile].action;
8337 boolean is_backside = object_mapping[tile].is_backside;
8338 boolean action_removing = (action == ACTION_DIGGING ||
8339 action == ACTION_SNAPPING ||
8340 action == ACTION_COLLECTING);
8348 return (frame_em > 5 ? EL_EMPTY : element);
8354 else // frame_em == 7
8365 case Ydiamond_stone:
8369 case Xdrip_stretchB:
8385 case Ymagnify_blank:
8388 case Xsand_stonein_1:
8389 case Xsand_stonein_2:
8390 case Xsand_stonein_3:
8391 case Xsand_stonein_4:
8395 return (is_backside || action_removing ? EL_EMPTY : element);
8400 static boolean check_linear_animation_EM(int tile)
8404 case Xsand_stonesand_1:
8405 case Xsand_stonesand_quickout_1:
8406 case Xsand_sandstone_1:
8407 case Xsand_stonein_1:
8408 case Xsand_stoneout_1:
8436 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8437 boolean has_crumbled_graphics,
8438 int crumbled, int sync_frame)
8440 // if element can be crumbled, but certain action graphics are just empty
8441 // space (like instantly snapping sand to empty space in 1 frame), do not
8442 // treat these empty space graphics as crumbled graphics in EMC engine
8443 if (crumbled == IMG_EMPTY_SPACE)
8444 has_crumbled_graphics = FALSE;
8446 if (has_crumbled_graphics)
8448 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8449 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8450 g_crumbled->anim_delay,
8451 g_crumbled->anim_mode,
8452 g_crumbled->anim_start_frame,
8455 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8456 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8458 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8459 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8461 g_em->has_crumbled_graphics = TRUE;
8465 g_em->crumbled_bitmap = NULL;
8466 g_em->crumbled_src_x = 0;
8467 g_em->crumbled_src_y = 0;
8468 g_em->crumbled_border_size = 0;
8469 g_em->crumbled_tile_size = 0;
8471 g_em->has_crumbled_graphics = FALSE;
8476 void ResetGfxAnimation_EM(int x, int y, int tile)
8482 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8483 int tile, int frame_em, int x, int y)
8485 int action = object_mapping[tile].action;
8486 int direction = object_mapping[tile].direction;
8487 int effective_element = get_effective_element_EM(tile, frame_em);
8488 int graphic = (direction == MV_NONE ?
8489 el_act2img(effective_element, action) :
8490 el_act_dir2img(effective_element, action, direction));
8491 struct GraphicInfo *g = &graphic_info[graphic];
8493 boolean action_removing = (action == ACTION_DIGGING ||
8494 action == ACTION_SNAPPING ||
8495 action == ACTION_COLLECTING);
8496 boolean action_moving = (action == ACTION_FALLING ||
8497 action == ACTION_MOVING ||
8498 action == ACTION_PUSHING ||
8499 action == ACTION_EATING ||
8500 action == ACTION_FILLING ||
8501 action == ACTION_EMPTYING);
8502 boolean action_falling = (action == ACTION_FALLING ||
8503 action == ACTION_FILLING ||
8504 action == ACTION_EMPTYING);
8506 // special case: graphic uses "2nd movement tile" and has defined
8507 // 7 frames for movement animation (or less) => use default graphic
8508 // for last (8th) frame which ends the movement animation
8509 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8511 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8512 graphic = (direction == MV_NONE ?
8513 el_act2img(effective_element, action) :
8514 el_act_dir2img(effective_element, action, direction));
8516 g = &graphic_info[graphic];
8519 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8523 else if (action_moving)
8525 boolean is_backside = object_mapping[tile].is_backside;
8529 int direction = object_mapping[tile].direction;
8530 int move_dir = (action_falling ? MV_DOWN : direction);
8535 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8536 if (g->double_movement && frame_em == 0)
8540 if (move_dir == MV_LEFT)
8541 GfxFrame[x - 1][y] = GfxFrame[x][y];
8542 else if (move_dir == MV_RIGHT)
8543 GfxFrame[x + 1][y] = GfxFrame[x][y];
8544 else if (move_dir == MV_UP)
8545 GfxFrame[x][y - 1] = GfxFrame[x][y];
8546 else if (move_dir == MV_DOWN)
8547 GfxFrame[x][y + 1] = GfxFrame[x][y];
8554 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8555 if (tile == Xsand_stonesand_quickout_1 ||
8556 tile == Xsand_stonesand_quickout_2)
8560 if (graphic_info[graphic].anim_global_sync)
8561 sync_frame = FrameCounter;
8562 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8563 sync_frame = GfxFrame[x][y];
8565 sync_frame = 0; // playfield border (pseudo steel)
8567 SetRandomAnimationValue(x, y);
8569 int frame = getAnimationFrame(g->anim_frames,
8572 g->anim_start_frame,
8575 g_em->unique_identifier =
8576 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8579 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8580 int tile, int frame_em, int x, int y)
8582 int action = object_mapping[tile].action;
8583 int direction = object_mapping[tile].direction;
8584 boolean is_backside = object_mapping[tile].is_backside;
8585 int effective_element = get_effective_element_EM(tile, frame_em);
8586 int effective_action = action;
8587 int graphic = (direction == MV_NONE ?
8588 el_act2img(effective_element, effective_action) :
8589 el_act_dir2img(effective_element, effective_action,
8591 int crumbled = (direction == MV_NONE ?
8592 el_act2crm(effective_element, effective_action) :
8593 el_act_dir2crm(effective_element, effective_action,
8595 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8596 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8597 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8598 struct GraphicInfo *g = &graphic_info[graphic];
8601 // special case: graphic uses "2nd movement tile" and has defined
8602 // 7 frames for movement animation (or less) => use default graphic
8603 // for last (8th) frame which ends the movement animation
8604 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8606 effective_action = ACTION_DEFAULT;
8607 graphic = (direction == MV_NONE ?
8608 el_act2img(effective_element, effective_action) :
8609 el_act_dir2img(effective_element, effective_action,
8611 crumbled = (direction == MV_NONE ?
8612 el_act2crm(effective_element, effective_action) :
8613 el_act_dir2crm(effective_element, effective_action,
8616 g = &graphic_info[graphic];
8619 if (graphic_info[graphic].anim_global_sync)
8620 sync_frame = FrameCounter;
8621 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8622 sync_frame = GfxFrame[x][y];
8624 sync_frame = 0; // playfield border (pseudo steel)
8626 SetRandomAnimationValue(x, y);
8628 int frame = getAnimationFrame(g->anim_frames,
8631 g->anim_start_frame,
8634 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8635 g->double_movement && is_backside);
8637 // (updating the "crumbled" graphic definitions is probably not really needed,
8638 // as animations for crumbled graphics can't be longer than one EMC cycle)
8639 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8643 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8644 int player_nr, int anim, int frame_em)
8646 int element = player_mapping[player_nr][anim].element_rnd;
8647 int action = player_mapping[player_nr][anim].action;
8648 int direction = player_mapping[player_nr][anim].direction;
8649 int graphic = (direction == MV_NONE ?
8650 el_act2img(element, action) :
8651 el_act_dir2img(element, action, direction));
8652 struct GraphicInfo *g = &graphic_info[graphic];
8655 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8657 stored_player[player_nr].StepFrame = frame_em;
8659 sync_frame = stored_player[player_nr].Frame;
8661 int frame = getAnimationFrame(g->anim_frames,
8664 g->anim_start_frame,
8667 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8668 &g_em->src_x, &g_em->src_y, FALSE);
8671 void InitGraphicInfo_EM(void)
8675 // always start with reliable default values
8676 for (i = 0; i < GAME_TILE_MAX; i++)
8678 object_mapping[i].element_rnd = EL_UNKNOWN;
8679 object_mapping[i].is_backside = FALSE;
8680 object_mapping[i].action = ACTION_DEFAULT;
8681 object_mapping[i].direction = MV_NONE;
8684 // always start with reliable default values
8685 for (p = 0; p < MAX_PLAYERS; p++)
8687 for (i = 0; i < PLY_MAX; i++)
8689 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8690 player_mapping[p][i].action = ACTION_DEFAULT;
8691 player_mapping[p][i].direction = MV_NONE;
8695 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8697 int e = em_object_mapping_list[i].element_em;
8699 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8700 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8702 if (em_object_mapping_list[i].action != -1)
8703 object_mapping[e].action = em_object_mapping_list[i].action;
8705 if (em_object_mapping_list[i].direction != -1)
8706 object_mapping[e].direction =
8707 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8710 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8712 int a = em_player_mapping_list[i].action_em;
8713 int p = em_player_mapping_list[i].player_nr;
8715 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8717 if (em_player_mapping_list[i].action != -1)
8718 player_mapping[p][a].action = em_player_mapping_list[i].action;
8720 if (em_player_mapping_list[i].direction != -1)
8721 player_mapping[p][a].direction =
8722 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8725 for (i = 0; i < GAME_TILE_MAX; i++)
8727 int element = object_mapping[i].element_rnd;
8728 int action = object_mapping[i].action;
8729 int direction = object_mapping[i].direction;
8730 boolean is_backside = object_mapping[i].is_backside;
8731 boolean action_exploding = ((action == ACTION_EXPLODING ||
8732 action == ACTION_SMASHED_BY_ROCK ||
8733 action == ACTION_SMASHED_BY_SPRING) &&
8734 element != EL_DIAMOND);
8735 boolean action_active = (action == ACTION_ACTIVE);
8736 boolean action_other = (action == ACTION_OTHER);
8738 for (j = 0; j < 8; j++)
8740 int effective_element = get_effective_element_EM(i, j);
8741 int effective_action = (j < 7 ? action :
8742 i == Xdrip_stretch ? action :
8743 i == Xdrip_stretchB ? action :
8744 i == Ydrip_1_s ? action :
8745 i == Ydrip_1_sB ? action :
8746 i == Yball_1 ? action :
8747 i == Xball_2 ? action :
8748 i == Yball_2 ? action :
8749 i == Yball_blank ? action :
8750 i == Ykey_1_blank ? action :
8751 i == Ykey_2_blank ? action :
8752 i == Ykey_3_blank ? action :
8753 i == Ykey_4_blank ? action :
8754 i == Ykey_5_blank ? action :
8755 i == Ykey_6_blank ? action :
8756 i == Ykey_7_blank ? action :
8757 i == Ykey_8_blank ? action :
8758 i == Ylenses_blank ? action :
8759 i == Ymagnify_blank ? action :
8760 i == Ygrass_blank ? action :
8761 i == Ydirt_blank ? action :
8762 i == Xsand_stonein_1 ? action :
8763 i == Xsand_stonein_2 ? action :
8764 i == Xsand_stonein_3 ? action :
8765 i == Xsand_stonein_4 ? action :
8766 i == Xsand_stoneout_1 ? action :
8767 i == Xsand_stoneout_2 ? action :
8768 i == Xboom_android ? ACTION_EXPLODING :
8769 action_exploding ? ACTION_EXPLODING :
8770 action_active ? action :
8771 action_other ? action :
8773 int graphic = (el_act_dir2img(effective_element, effective_action,
8775 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8777 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8778 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8779 boolean has_action_graphics = (graphic != base_graphic);
8780 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8781 struct GraphicInfo *g = &graphic_info[graphic];
8782 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8785 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8786 boolean special_animation = (action != ACTION_DEFAULT &&
8787 g->anim_frames == 3 &&
8788 g->anim_delay == 2 &&
8789 g->anim_mode & ANIM_LINEAR);
8790 int sync_frame = (i == Xdrip_stretch ? 7 :
8791 i == Xdrip_stretchB ? 7 :
8792 i == Ydrip_2_s ? j + 8 :
8793 i == Ydrip_2_sB ? j + 8 :
8802 i == Xfake_acid_1 ? 0 :
8803 i == Xfake_acid_2 ? 10 :
8804 i == Xfake_acid_3 ? 20 :
8805 i == Xfake_acid_4 ? 30 :
8806 i == Xfake_acid_5 ? 40 :
8807 i == Xfake_acid_6 ? 50 :
8808 i == Xfake_acid_7 ? 60 :
8809 i == Xfake_acid_8 ? 70 :
8810 i == Xfake_acid_1_player ? 0 :
8811 i == Xfake_acid_2_player ? 10 :
8812 i == Xfake_acid_3_player ? 20 :
8813 i == Xfake_acid_4_player ? 30 :
8814 i == Xfake_acid_5_player ? 40 :
8815 i == Xfake_acid_6_player ? 50 :
8816 i == Xfake_acid_7_player ? 60 :
8817 i == Xfake_acid_8_player ? 70 :
8819 i == Yball_2 ? j + 8 :
8820 i == Yball_blank ? j + 1 :
8821 i == Ykey_1_blank ? j + 1 :
8822 i == Ykey_2_blank ? j + 1 :
8823 i == Ykey_3_blank ? j + 1 :
8824 i == Ykey_4_blank ? j + 1 :
8825 i == Ykey_5_blank ? j + 1 :
8826 i == Ykey_6_blank ? j + 1 :
8827 i == Ykey_7_blank ? j + 1 :
8828 i == Ykey_8_blank ? j + 1 :
8829 i == Ylenses_blank ? j + 1 :
8830 i == Ymagnify_blank ? j + 1 :
8831 i == Ygrass_blank ? j + 1 :
8832 i == Ydirt_blank ? j + 1 :
8833 i == Xamoeba_1 ? 0 :
8834 i == Xamoeba_2 ? 1 :
8835 i == Xamoeba_3 ? 2 :
8836 i == Xamoeba_4 ? 3 :
8837 i == Xamoeba_5 ? 0 :
8838 i == Xamoeba_6 ? 1 :
8839 i == Xamoeba_7 ? 2 :
8840 i == Xamoeba_8 ? 3 :
8841 i == Xexit_2 ? j + 8 :
8842 i == Xexit_3 ? j + 16 :
8843 i == Xdynamite_1 ? 0 :
8844 i == Xdynamite_2 ? 8 :
8845 i == Xdynamite_3 ? 16 :
8846 i == Xdynamite_4 ? 24 :
8847 i == Xsand_stonein_1 ? j + 1 :
8848 i == Xsand_stonein_2 ? j + 9 :
8849 i == Xsand_stonein_3 ? j + 17 :
8850 i == Xsand_stonein_4 ? j + 25 :
8851 i == Xsand_stoneout_1 && j == 0 ? 0 :
8852 i == Xsand_stoneout_1 && j == 1 ? 0 :
8853 i == Xsand_stoneout_1 && j == 2 ? 1 :
8854 i == Xsand_stoneout_1 && j == 3 ? 2 :
8855 i == Xsand_stoneout_1 && j == 4 ? 2 :
8856 i == Xsand_stoneout_1 && j == 5 ? 3 :
8857 i == Xsand_stoneout_1 && j == 6 ? 4 :
8858 i == Xsand_stoneout_1 && j == 7 ? 4 :
8859 i == Xsand_stoneout_2 && j == 0 ? 5 :
8860 i == Xsand_stoneout_2 && j == 1 ? 6 :
8861 i == Xsand_stoneout_2 && j == 2 ? 7 :
8862 i == Xsand_stoneout_2 && j == 3 ? 8 :
8863 i == Xsand_stoneout_2 && j == 4 ? 9 :
8864 i == Xsand_stoneout_2 && j == 5 ? 11 :
8865 i == Xsand_stoneout_2 && j == 6 ? 13 :
8866 i == Xsand_stoneout_2 && j == 7 ? 15 :
8867 i == Xboom_bug && j == 1 ? 2 :
8868 i == Xboom_bug && j == 2 ? 2 :
8869 i == Xboom_bug && j == 3 ? 4 :
8870 i == Xboom_bug && j == 4 ? 4 :
8871 i == Xboom_bug && j == 5 ? 2 :
8872 i == Xboom_bug && j == 6 ? 2 :
8873 i == Xboom_bug && j == 7 ? 0 :
8874 i == Xboom_tank && j == 1 ? 2 :
8875 i == Xboom_tank && j == 2 ? 2 :
8876 i == Xboom_tank && j == 3 ? 4 :
8877 i == Xboom_tank && j == 4 ? 4 :
8878 i == Xboom_tank && j == 5 ? 2 :
8879 i == Xboom_tank && j == 6 ? 2 :
8880 i == Xboom_tank && j == 7 ? 0 :
8881 i == Xboom_android && j == 7 ? 6 :
8882 i == Xboom_1 && j == 1 ? 2 :
8883 i == Xboom_1 && j == 2 ? 2 :
8884 i == Xboom_1 && j == 3 ? 4 :
8885 i == Xboom_1 && j == 4 ? 4 :
8886 i == Xboom_1 && j == 5 ? 6 :
8887 i == Xboom_1 && j == 6 ? 6 :
8888 i == Xboom_1 && j == 7 ? 8 :
8889 i == Xboom_2 && j == 0 ? 8 :
8890 i == Xboom_2 && j == 1 ? 8 :
8891 i == Xboom_2 && j == 2 ? 10 :
8892 i == Xboom_2 && j == 3 ? 10 :
8893 i == Xboom_2 && j == 4 ? 10 :
8894 i == Xboom_2 && j == 5 ? 12 :
8895 i == Xboom_2 && j == 6 ? 12 :
8896 i == Xboom_2 && j == 7 ? 12 :
8897 special_animation && j == 4 ? 3 :
8898 effective_action != action ? 0 :
8900 int frame = getAnimationFrame(g->anim_frames,
8903 g->anim_start_frame,
8906 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8907 g->double_movement && is_backside);
8909 g_em->bitmap = src_bitmap;
8910 g_em->src_x = src_x;
8911 g_em->src_y = src_y;
8912 g_em->src_offset_x = 0;
8913 g_em->src_offset_y = 0;
8914 g_em->dst_offset_x = 0;
8915 g_em->dst_offset_y = 0;
8916 g_em->width = TILEX;
8917 g_em->height = TILEY;
8919 g_em->preserve_background = FALSE;
8921 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8924 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8925 effective_action == ACTION_MOVING ||
8926 effective_action == ACTION_PUSHING ||
8927 effective_action == ACTION_EATING)) ||
8928 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8929 effective_action == ACTION_EMPTYING)))
8932 (effective_action == ACTION_FALLING ||
8933 effective_action == ACTION_FILLING ||
8934 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8935 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8936 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8937 int num_steps = (i == Ydrip_1_s ? 16 :
8938 i == Ydrip_1_sB ? 16 :
8939 i == Ydrip_2_s ? 16 :
8940 i == Ydrip_2_sB ? 16 :
8941 i == Xsand_stonein_1 ? 32 :
8942 i == Xsand_stonein_2 ? 32 :
8943 i == Xsand_stonein_3 ? 32 :
8944 i == Xsand_stonein_4 ? 32 :
8945 i == Xsand_stoneout_1 ? 16 :
8946 i == Xsand_stoneout_2 ? 16 : 8);
8947 int cx = ABS(dx) * (TILEX / num_steps);
8948 int cy = ABS(dy) * (TILEY / num_steps);
8949 int step_frame = (i == Ydrip_2_s ? j + 8 :
8950 i == Ydrip_2_sB ? j + 8 :
8951 i == Xsand_stonein_2 ? j + 8 :
8952 i == Xsand_stonein_3 ? j + 16 :
8953 i == Xsand_stonein_4 ? j + 24 :
8954 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8955 int step = (is_backside ? step_frame : num_steps - step_frame);
8957 if (is_backside) // tile where movement starts
8959 if (dx < 0 || dy < 0)
8961 g_em->src_offset_x = cx * step;
8962 g_em->src_offset_y = cy * step;
8966 g_em->dst_offset_x = cx * step;
8967 g_em->dst_offset_y = cy * step;
8970 else // tile where movement ends
8972 if (dx < 0 || dy < 0)
8974 g_em->dst_offset_x = cx * step;
8975 g_em->dst_offset_y = cy * step;
8979 g_em->src_offset_x = cx * step;
8980 g_em->src_offset_y = cy * step;
8984 g_em->width = TILEX - cx * step;
8985 g_em->height = TILEY - cy * step;
8988 // create unique graphic identifier to decide if tile must be redrawn
8989 /* bit 31 - 16 (16 bit): EM style graphic
8990 bit 15 - 12 ( 4 bit): EM style frame
8991 bit 11 - 6 ( 6 bit): graphic width
8992 bit 5 - 0 ( 6 bit): graphic height */
8993 g_em->unique_identifier =
8994 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8998 for (i = 0; i < GAME_TILE_MAX; i++)
9000 for (j = 0; j < 8; j++)
9002 int element = object_mapping[i].element_rnd;
9003 int action = object_mapping[i].action;
9004 int direction = object_mapping[i].direction;
9005 boolean is_backside = object_mapping[i].is_backside;
9006 int graphic_action = el_act_dir2img(element, action, direction);
9007 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9009 if ((action == ACTION_SMASHED_BY_ROCK ||
9010 action == ACTION_SMASHED_BY_SPRING ||
9011 action == ACTION_EATING) &&
9012 graphic_action == graphic_default)
9014 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9015 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9016 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9017 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9020 // no separate animation for "smashed by rock" -- use rock instead
9021 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9022 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9024 g_em->bitmap = g_xx->bitmap;
9025 g_em->src_x = g_xx->src_x;
9026 g_em->src_y = g_xx->src_y;
9027 g_em->src_offset_x = g_xx->src_offset_x;
9028 g_em->src_offset_y = g_xx->src_offset_y;
9029 g_em->dst_offset_x = g_xx->dst_offset_x;
9030 g_em->dst_offset_y = g_xx->dst_offset_y;
9031 g_em->width = g_xx->width;
9032 g_em->height = g_xx->height;
9033 g_em->unique_identifier = g_xx->unique_identifier;
9036 g_em->preserve_background = TRUE;
9041 for (p = 0; p < MAX_PLAYERS; p++)
9043 for (i = 0; i < PLY_MAX; i++)
9045 int element = player_mapping[p][i].element_rnd;
9046 int action = player_mapping[p][i].action;
9047 int direction = player_mapping[p][i].direction;
9049 for (j = 0; j < 8; j++)
9051 int effective_element = element;
9052 int effective_action = action;
9053 int graphic = (direction == MV_NONE ?
9054 el_act2img(effective_element, effective_action) :
9055 el_act_dir2img(effective_element, effective_action,
9057 struct GraphicInfo *g = &graphic_info[graphic];
9058 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9062 int frame = getAnimationFrame(g->anim_frames,
9065 g->anim_start_frame,
9068 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9070 g_em->bitmap = src_bitmap;
9071 g_em->src_x = src_x;
9072 g_em->src_y = src_y;
9073 g_em->src_offset_x = 0;
9074 g_em->src_offset_y = 0;
9075 g_em->dst_offset_x = 0;
9076 g_em->dst_offset_y = 0;
9077 g_em->width = TILEX;
9078 g_em->height = TILEY;
9084 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9085 boolean any_player_moving,
9086 boolean any_player_snapping,
9087 boolean any_player_dropping)
9089 if (frame == 7 && !any_player_dropping)
9091 if (!local_player->was_waiting)
9093 if (!CheckSaveEngineSnapshotToList())
9096 local_player->was_waiting = TRUE;
9099 else if (any_player_moving || any_player_snapping || any_player_dropping)
9101 local_player->was_waiting = FALSE;
9105 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9106 boolean murphy_is_dropping)
9108 if (murphy_is_waiting)
9110 if (!local_player->was_waiting)
9112 if (!CheckSaveEngineSnapshotToList())
9115 local_player->was_waiting = TRUE;
9120 local_player->was_waiting = FALSE;
9124 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9125 boolean button_released)
9127 if (button_released)
9129 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9130 CheckSaveEngineSnapshotToList();
9132 else if (element_clicked)
9134 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9135 CheckSaveEngineSnapshotToList();
9137 game.snapshot.changed_action = TRUE;
9141 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9142 boolean any_player_moving,
9143 boolean any_player_snapping,
9144 boolean any_player_dropping)
9146 if (tape.single_step && tape.recording && !tape.pausing)
9147 if (frame == 7 && !any_player_dropping)
9148 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9150 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9151 any_player_snapping, any_player_dropping);
9153 return tape.pausing;
9156 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9157 boolean murphy_is_dropping)
9159 boolean murphy_starts_dropping = FALSE;
9162 for (i = 0; i < MAX_PLAYERS; i++)
9163 if (stored_player[i].force_dropping)
9164 murphy_starts_dropping = TRUE;
9166 if (tape.single_step && tape.recording && !tape.pausing)
9167 if (murphy_is_waiting && !murphy_starts_dropping)
9168 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9170 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9173 void CheckSingleStepMode_MM(boolean element_clicked,
9174 boolean button_released)
9176 if (tape.single_step && tape.recording && !tape.pausing)
9177 if (button_released)
9178 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9180 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9183 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9184 int graphic, int sync_frame, int x, int y)
9186 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9188 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9191 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9193 return (IS_NEXT_FRAME(sync_frame, graphic));
9196 int getGraphicInfo_Delay(int graphic)
9198 return graphic_info[graphic].anim_delay;
9201 void PlayMenuSoundExt(int sound)
9203 if (sound == SND_UNDEFINED)
9206 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9207 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9210 if (IS_LOOP_SOUND(sound))
9211 PlaySoundLoop(sound);
9216 void PlayMenuSound(void)
9218 PlayMenuSoundExt(menu.sound[game_status]);
9221 void PlayMenuSoundStereo(int sound, int stereo_position)
9223 if (sound == SND_UNDEFINED)
9226 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9227 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9230 if (IS_LOOP_SOUND(sound))
9231 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9233 PlaySoundStereo(sound, stereo_position);
9236 void PlayMenuSoundIfLoopExt(int sound)
9238 if (sound == SND_UNDEFINED)
9241 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9242 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9245 if (IS_LOOP_SOUND(sound))
9246 PlaySoundLoop(sound);
9249 void PlayMenuSoundIfLoop(void)
9251 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9254 void PlayMenuMusicExt(int music)
9256 if (music == MUS_UNDEFINED)
9259 if (!setup.sound_music)
9262 if (IS_LOOP_MUSIC(music))
9263 PlayMusicLoop(music);
9268 void PlayMenuMusic(void)
9270 char *curr_music = getCurrentlyPlayingMusicFilename();
9271 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9273 if (!strEqual(curr_music, next_music))
9274 PlayMenuMusicExt(menu.music[game_status]);
9277 void PlayMenuSoundsAndMusic(void)
9283 static void FadeMenuSounds(void)
9288 static void FadeMenuMusic(void)
9290 char *curr_music = getCurrentlyPlayingMusicFilename();
9291 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9293 if (!strEqual(curr_music, next_music))
9297 void FadeMenuSoundsAndMusic(void)
9303 void PlaySoundActivating(void)
9306 PlaySound(SND_MENU_ITEM_ACTIVATING);
9310 void PlaySoundSelecting(void)
9313 PlaySound(SND_MENU_ITEM_SELECTING);
9317 void ToggleFullscreenIfNeeded(void)
9319 // if setup and video fullscreen state are already matching, nothing do do
9320 if (setup.fullscreen == video.fullscreen_enabled ||
9321 !video.fullscreen_available)
9324 SDLSetWindowFullscreen(setup.fullscreen);
9326 // set setup value according to successfully changed fullscreen mode
9327 setup.fullscreen = video.fullscreen_enabled;
9330 void ChangeWindowScalingIfNeeded(void)
9332 // if setup and video window scaling are already matching, nothing do do
9333 if (setup.window_scaling_percent == video.window_scaling_percent ||
9334 video.fullscreen_enabled)
9337 SDLSetWindowScaling(setup.window_scaling_percent);
9339 // set setup value according to successfully changed window scaling
9340 setup.window_scaling_percent = video.window_scaling_percent;
9343 void ChangeVsyncModeIfNeeded(void)
9345 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9346 int video_vsync_mode = video.vsync_mode;
9348 // if setup and video vsync mode are already matching, nothing do do
9349 if (setup_vsync_mode == video_vsync_mode)
9352 // if renderer is using OpenGL, vsync mode can directly be changed
9353 SDLSetScreenVsyncMode(setup.vsync_mode);
9355 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9356 if (video.vsync_mode == video_vsync_mode)
9358 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9360 // save backbuffer content which gets lost when re-creating screen
9361 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9363 // force re-creating screen and renderer to set new vsync mode
9364 video.fullscreen_enabled = !setup.fullscreen;
9366 // when creating new renderer, destroy textures linked to old renderer
9367 FreeAllImageTextures(); // needs old renderer to free the textures
9369 // re-create screen and renderer (including change of vsync mode)
9370 ChangeVideoModeIfNeeded(setup.fullscreen);
9372 // set setup value according to successfully changed fullscreen mode
9373 setup.fullscreen = video.fullscreen_enabled;
9375 // restore backbuffer content from temporary backbuffer backup bitmap
9376 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9377 FreeBitmap(tmp_backbuffer);
9379 // update visible window/screen
9380 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9382 // when changing vsync mode, re-create textures for new renderer
9383 InitImageTextures();
9386 // set setup value according to successfully changed vsync mode
9387 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9390 static void JoinRectangles(int *x, int *y, int *width, int *height,
9391 int x2, int y2, int width2, int height2)
9393 // do not join with "off-screen" rectangle
9394 if (x2 == -1 || y2 == -1)
9399 *width = MAX(*width, width2);
9400 *height = MAX(*height, height2);
9403 void SetAnimStatus(int anim_status_new)
9405 if (anim_status_new == GAME_MODE_MAIN)
9406 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9407 else if (anim_status_new == GAME_MODE_SCORES)
9408 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9410 global.anim_status_next = anim_status_new;
9412 // directly set screen modes that are entered without fading
9413 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9414 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9415 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9416 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9417 global.anim_status = global.anim_status_next;
9420 void SetGameStatus(int game_status_new)
9422 if (game_status_new != game_status)
9423 game_status_last_screen = game_status;
9425 game_status = game_status_new;
9427 SetAnimStatus(game_status_new);
9430 void SetFontStatus(int game_status_new)
9432 static int last_game_status = -1;
9434 if (game_status_new != -1)
9436 // set game status for font use after storing last game status
9437 last_game_status = game_status;
9438 game_status = game_status_new;
9442 // reset game status after font use from last stored game status
9443 game_status = last_game_status;
9447 void ResetFontStatus(void)
9452 void SetLevelSetInfo(char *identifier, int level_nr)
9454 setString(&levelset.identifier, identifier);
9456 levelset.level_nr = level_nr;
9459 boolean CheckIfAllViewportsHaveChanged(void)
9461 // if game status has not changed, viewports have not changed either
9462 if (game_status == game_status_last)
9465 // check if all viewports have changed with current game status
9467 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9468 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9469 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9470 int new_real_sx = vp_playfield->x;
9471 int new_real_sy = vp_playfield->y;
9472 int new_full_sxsize = vp_playfield->width;
9473 int new_full_sysize = vp_playfield->height;
9474 int new_dx = vp_door_1->x;
9475 int new_dy = vp_door_1->y;
9476 int new_dxsize = vp_door_1->width;
9477 int new_dysize = vp_door_1->height;
9478 int new_vx = vp_door_2->x;
9479 int new_vy = vp_door_2->y;
9480 int new_vxsize = vp_door_2->width;
9481 int new_vysize = vp_door_2->height;
9483 boolean playfield_viewport_has_changed =
9484 (new_real_sx != REAL_SX ||
9485 new_real_sy != REAL_SY ||
9486 new_full_sxsize != FULL_SXSIZE ||
9487 new_full_sysize != FULL_SYSIZE);
9489 boolean door_1_viewport_has_changed =
9492 new_dxsize != DXSIZE ||
9493 new_dysize != DYSIZE);
9495 boolean door_2_viewport_has_changed =
9498 new_vxsize != VXSIZE ||
9499 new_vysize != VYSIZE ||
9500 game_status_last == GAME_MODE_EDITOR);
9502 return (playfield_viewport_has_changed &&
9503 door_1_viewport_has_changed &&
9504 door_2_viewport_has_changed);
9507 boolean CheckFadeAll(void)
9509 return (CheckIfGlobalBorderHasChanged() ||
9510 CheckIfAllViewportsHaveChanged());
9513 void ChangeViewportPropertiesIfNeeded(void)
9515 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9516 FALSE : setup.small_game_graphics);
9517 int gfx_game_mode = game_status;
9518 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9520 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9521 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9522 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9523 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9524 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9525 int new_win_xsize = vp_window->width;
9526 int new_win_ysize = vp_window->height;
9527 int border_left = vp_playfield->border_left;
9528 int border_right = vp_playfield->border_right;
9529 int border_top = vp_playfield->border_top;
9530 int border_bottom = vp_playfield->border_bottom;
9531 int new_sx = vp_playfield->x + border_left;
9532 int new_sy = vp_playfield->y + border_top;
9533 int new_sxsize = vp_playfield->width - border_left - border_right;
9534 int new_sysize = vp_playfield->height - border_top - border_bottom;
9535 int new_real_sx = vp_playfield->x;
9536 int new_real_sy = vp_playfield->y;
9537 int new_full_sxsize = vp_playfield->width;
9538 int new_full_sysize = vp_playfield->height;
9539 int new_dx = vp_door_1->x;
9540 int new_dy = vp_door_1->y;
9541 int new_dxsize = vp_door_1->width;
9542 int new_dysize = vp_door_1->height;
9543 int new_vx = vp_door_2->x;
9544 int new_vy = vp_door_2->y;
9545 int new_vxsize = vp_door_2->width;
9546 int new_vysize = vp_door_2->height;
9547 int new_ex = vp_door_3->x;
9548 int new_ey = vp_door_3->y;
9549 int new_exsize = vp_door_3->width;
9550 int new_eysize = vp_door_3->height;
9551 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9552 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9553 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9554 int new_scr_fieldx = new_sxsize / tilesize;
9555 int new_scr_fieldy = new_sysize / tilesize;
9556 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9557 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9558 boolean init_gfx_buffers = FALSE;
9559 boolean init_video_buffer = FALSE;
9560 boolean init_gadgets_and_anims = FALSE;
9561 boolean init_em_graphics = FALSE;
9563 if (new_win_xsize != WIN_XSIZE ||
9564 new_win_ysize != WIN_YSIZE)
9566 WIN_XSIZE = new_win_xsize;
9567 WIN_YSIZE = new_win_ysize;
9569 init_video_buffer = TRUE;
9570 init_gfx_buffers = TRUE;
9571 init_gadgets_and_anims = TRUE;
9573 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9576 if (new_scr_fieldx != SCR_FIELDX ||
9577 new_scr_fieldy != SCR_FIELDY)
9579 // this always toggles between MAIN and GAME when using small tile size
9581 SCR_FIELDX = new_scr_fieldx;
9582 SCR_FIELDY = new_scr_fieldy;
9584 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9595 new_sxsize != SXSIZE ||
9596 new_sysize != SYSIZE ||
9597 new_dxsize != DXSIZE ||
9598 new_dysize != DYSIZE ||
9599 new_vxsize != VXSIZE ||
9600 new_vysize != VYSIZE ||
9601 new_exsize != EXSIZE ||
9602 new_eysize != EYSIZE ||
9603 new_real_sx != REAL_SX ||
9604 new_real_sy != REAL_SY ||
9605 new_full_sxsize != FULL_SXSIZE ||
9606 new_full_sysize != FULL_SYSIZE ||
9607 new_tilesize_var != TILESIZE_VAR
9610 // ------------------------------------------------------------------------
9611 // determine next fading area for changed viewport definitions
9612 // ------------------------------------------------------------------------
9614 // start with current playfield area (default fading area)
9617 FADE_SXSIZE = FULL_SXSIZE;
9618 FADE_SYSIZE = FULL_SYSIZE;
9620 // add new playfield area if position or size has changed
9621 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9622 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9624 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9625 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9628 // add current and new door 1 area if position or size has changed
9629 if (new_dx != DX || new_dy != DY ||
9630 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9632 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9633 DX, DY, DXSIZE, DYSIZE);
9634 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9635 new_dx, new_dy, new_dxsize, new_dysize);
9638 // add current and new door 2 area if position or size has changed
9639 if (new_vx != VX || new_vy != VY ||
9640 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9642 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9643 VX, VY, VXSIZE, VYSIZE);
9644 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9645 new_vx, new_vy, new_vxsize, new_vysize);
9648 // ------------------------------------------------------------------------
9649 // handle changed tile size
9650 // ------------------------------------------------------------------------
9652 if (new_tilesize_var != TILESIZE_VAR)
9654 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9656 // changing tile size invalidates scroll values of engine snapshots
9657 FreeEngineSnapshotSingle();
9659 // changing tile size requires update of graphic mapping for EM engine
9660 init_em_graphics = TRUE;
9671 SXSIZE = new_sxsize;
9672 SYSIZE = new_sysize;
9673 DXSIZE = new_dxsize;
9674 DYSIZE = new_dysize;
9675 VXSIZE = new_vxsize;
9676 VYSIZE = new_vysize;
9677 EXSIZE = new_exsize;
9678 EYSIZE = new_eysize;
9679 REAL_SX = new_real_sx;
9680 REAL_SY = new_real_sy;
9681 FULL_SXSIZE = new_full_sxsize;
9682 FULL_SYSIZE = new_full_sysize;
9683 TILESIZE_VAR = new_tilesize_var;
9685 init_gfx_buffers = TRUE;
9686 init_gadgets_and_anims = TRUE;
9688 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9689 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9692 if (init_gfx_buffers)
9694 // Debug("tools:viewport", "init_gfx_buffers");
9696 SCR_FIELDX = new_scr_fieldx_buffers;
9697 SCR_FIELDY = new_scr_fieldy_buffers;
9701 SCR_FIELDX = new_scr_fieldx;
9702 SCR_FIELDY = new_scr_fieldy;
9704 SetDrawDeactivationMask(REDRAW_NONE);
9705 SetDrawBackgroundMask(REDRAW_FIELD);
9708 if (init_video_buffer)
9710 // Debug("tools:viewport", "init_video_buffer");
9712 FreeAllImageTextures(); // needs old renderer to free the textures
9714 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9715 InitImageTextures();
9718 if (init_gadgets_and_anims)
9720 // Debug("tools:viewport", "init_gadgets_and_anims");
9723 InitGlobalAnimations();
9726 if (init_em_graphics)
9728 InitGraphicInfo_EM();