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())
1342 // redraw global screen border (or clear, if defined to be empty)
1343 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1345 if (game_status == GAME_MODE_EDITOR)
1346 DrawSpecialEditorDoor();
1348 // copy previous playfield and door areas, if they are defined on both
1349 // previous and current screen and if they still have the same size
1351 if (real_sx_last != -1 && real_sy_last != -1 &&
1352 REAL_SX != -1 && REAL_SY != -1 &&
1353 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1354 BlitBitmap(bitmap_db_store_1, backbuffer,
1355 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1358 if (dx_last != -1 && dy_last != -1 &&
1359 DX != -1 && DY != -1 &&
1360 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1361 BlitBitmap(bitmap_db_store_1, backbuffer,
1362 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1364 if (game_status != GAME_MODE_EDITOR)
1366 if (vx_last != -1 && vy_last != -1 &&
1367 VX != -1 && VY != -1 &&
1368 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1369 BlitBitmap(bitmap_db_store_1, backbuffer,
1370 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1374 if (ex_last != -1 && ey_last != -1 &&
1375 EX != -1 && EY != -1 &&
1376 exsize_last == EXSIZE && eysize_last == EYSIZE)
1377 BlitBitmap(bitmap_db_store_1, backbuffer,
1378 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1381 redraw_mask = REDRAW_ALL;
1384 game_status_last = game_status;
1386 global_border_bitmap_last = global_border_bitmap;
1388 real_sx_last = REAL_SX;
1389 real_sy_last = REAL_SY;
1390 full_sxsize_last = FULL_SXSIZE;
1391 full_sysize_last = FULL_SYSIZE;
1394 dxsize_last = DXSIZE;
1395 dysize_last = DYSIZE;
1398 vxsize_last = VXSIZE;
1399 vysize_last = VYSIZE;
1402 exsize_last = EXSIZE;
1403 eysize_last = EYSIZE;
1406 void ClearField(void)
1408 RedrawGlobalBorderIfNeeded();
1410 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1411 // (when entering hall of fame after playing)
1412 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1414 // !!! maybe this should be done before clearing the background !!!
1415 if (game_status == GAME_MODE_PLAYING)
1417 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1418 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1422 SetDrawtoField(DRAW_TO_BACKBUFFER);
1426 void MarkTileDirty(int x, int y)
1428 redraw_mask |= REDRAW_FIELD;
1431 void SetBorderElement(void)
1435 BorderElement = EL_EMPTY;
1437 // only the R'n'D game engine may use an additional steelwall border
1438 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1441 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1443 for (x = 0; x < lev_fieldx; x++)
1445 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1446 BorderElement = EL_STEELWALL;
1448 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1454 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1455 int max_array_fieldx, int max_array_fieldy,
1456 short field[max_array_fieldx][max_array_fieldy],
1457 int max_fieldx, int max_fieldy)
1461 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1462 static int safety = 0;
1464 // check if starting field still has the desired content
1465 if (field[from_x][from_y] == fill_element)
1470 if (safety > max_fieldx * max_fieldy)
1471 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1473 old_element = field[from_x][from_y];
1474 field[from_x][from_y] = fill_element;
1476 for (i = 0; i < 4; i++)
1478 x = from_x + check[i][0];
1479 y = from_y + check[i][1];
1481 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1482 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1483 field, max_fieldx, max_fieldy);
1489 void FloodFillLevel(int from_x, int from_y, int fill_element,
1490 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1491 int max_fieldx, int max_fieldy)
1493 FloodFillLevelExt(from_x, from_y, fill_element,
1494 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1495 max_fieldx, max_fieldy);
1498 void SetRandomAnimationValue(int x, int y)
1500 gfx.anim_random_frame = GfxRandom[x][y];
1503 int getGraphicAnimationFrame(int graphic, int sync_frame)
1505 // animation synchronized with global frame counter, not move position
1506 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1507 sync_frame = FrameCounter;
1509 return getAnimationFrame(graphic_info[graphic].anim_frames,
1510 graphic_info[graphic].anim_delay,
1511 graphic_info[graphic].anim_mode,
1512 graphic_info[graphic].anim_start_frame,
1516 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1518 struct GraphicInfo *g = &graphic_info[graphic];
1519 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1521 if (tilesize == gfx.standard_tile_size)
1522 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1523 else if (tilesize == game.tile_size)
1524 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1526 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1529 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1530 boolean get_backside)
1532 struct GraphicInfo *g = &graphic_info[graphic];
1533 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1534 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1536 if (g->offset_y == 0) // frames are ordered horizontally
1538 int max_width = g->anim_frames_per_line * g->width;
1539 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1541 *x = pos % max_width;
1542 *y = src_y % g->height + pos / max_width * g->height;
1544 else if (g->offset_x == 0) // frames are ordered vertically
1546 int max_height = g->anim_frames_per_line * g->height;
1547 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1549 *x = src_x % g->width + pos / max_height * g->width;
1550 *y = pos % max_height;
1552 else // frames are ordered diagonally
1554 *x = src_x + frame * g->offset_x;
1555 *y = src_y + frame * g->offset_y;
1559 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1560 Bitmap **bitmap, int *x, int *y,
1561 boolean get_backside)
1563 struct GraphicInfo *g = &graphic_info[graphic];
1565 // if no graphics defined at all, use fallback graphics
1566 if (g->bitmaps == NULL)
1567 *g = graphic_info[IMG_CHAR_EXCLAM];
1569 // if no in-game graphics defined, always use standard graphic size
1570 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1571 tilesize = TILESIZE;
1573 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1574 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1576 *x = *x * tilesize / g->tile_size;
1577 *y = *y * tilesize / g->tile_size;
1580 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1581 Bitmap **bitmap, int *x, int *y)
1583 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1586 void getFixedGraphicSource(int graphic, int frame,
1587 Bitmap **bitmap, int *x, int *y)
1589 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1592 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1594 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1597 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1598 int *x, int *y, boolean get_backside)
1600 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1604 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1606 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1609 void DrawGraphic(int x, int y, int graphic, int frame)
1612 if (!IN_SCR_FIELD(x, y))
1614 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1615 Debug("draw:DrawGraphic", "This should never happen!");
1621 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1624 MarkTileDirty(x, y);
1627 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1630 if (!IN_SCR_FIELD(x, y))
1632 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1634 Debug("draw:DrawFixedGraphic", "This should never happen!");
1640 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1642 MarkTileDirty(x, y);
1645 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1651 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1653 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1656 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1662 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1663 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1666 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1669 if (!IN_SCR_FIELD(x, y))
1671 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1673 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1679 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1682 MarkTileDirty(x, y);
1685 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1688 if (!IN_SCR_FIELD(x, y))
1690 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1692 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1698 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1700 MarkTileDirty(x, y);
1703 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1709 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1711 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1715 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1716 int graphic, int frame)
1721 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1723 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1727 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1729 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1731 MarkTileDirty(x / tilesize, y / tilesize);
1734 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1737 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1738 graphic, frame, tilesize);
1739 MarkTileDirty(x / tilesize, y / tilesize);
1742 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1748 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1749 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1752 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1753 int frame, int tilesize)
1758 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1759 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1762 void DrawMiniGraphic(int x, int y, int graphic)
1764 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1765 MarkTileDirty(x / 2, y / 2);
1768 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1773 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1774 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1777 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1778 int graphic, int frame,
1779 int cut_mode, int mask_mode)
1784 int width = TILEX, height = TILEY;
1787 if (dx || dy) // shifted graphic
1789 if (x < BX1) // object enters playfield from the left
1796 else if (x > BX2) // object enters playfield from the right
1802 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1808 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1810 else if (dx) // general horizontal movement
1811 MarkTileDirty(x + SIGN(dx), y);
1813 if (y < BY1) // object enters playfield from the top
1815 if (cut_mode == CUT_BELOW) // object completely above top border
1823 else if (y > BY2) // object enters playfield from the bottom
1829 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1835 else if (dy > 0 && cut_mode == CUT_ABOVE)
1837 if (y == BY2) // object completely above bottom border
1843 MarkTileDirty(x, y + 1);
1844 } // object leaves playfield to the bottom
1845 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1847 else if (dy) // general vertical movement
1848 MarkTileDirty(x, y + SIGN(dy));
1852 if (!IN_SCR_FIELD(x, y))
1854 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1856 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1862 width = width * TILESIZE_VAR / TILESIZE;
1863 height = height * TILESIZE_VAR / TILESIZE;
1864 cx = cx * TILESIZE_VAR / TILESIZE;
1865 cy = cy * TILESIZE_VAR / TILESIZE;
1866 dx = dx * TILESIZE_VAR / TILESIZE;
1867 dy = dy * TILESIZE_VAR / TILESIZE;
1869 if (width > 0 && height > 0)
1871 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1876 dst_x = FX + x * TILEX_VAR + dx;
1877 dst_y = FY + y * TILEY_VAR + dy;
1879 if (mask_mode == USE_MASKING)
1880 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1883 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1886 MarkTileDirty(x, y);
1890 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1891 int graphic, int frame,
1892 int cut_mode, int mask_mode)
1897 int width = TILEX_VAR, height = TILEY_VAR;
1900 int x2 = x + SIGN(dx);
1901 int y2 = y + SIGN(dy);
1903 // movement with two-tile animations must be sync'ed with movement position,
1904 // not with current GfxFrame (which can be higher when using slow movement)
1905 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1906 int anim_frames = graphic_info[graphic].anim_frames;
1908 // (we also need anim_delay here for movement animations with less frames)
1909 int anim_delay = graphic_info[graphic].anim_delay;
1910 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1912 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1913 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1915 // re-calculate animation frame for two-tile movement animation
1916 frame = getGraphicAnimationFrame(graphic, sync_frame);
1918 // check if movement start graphic inside screen area and should be drawn
1919 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1921 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1923 dst_x = FX + x1 * TILEX_VAR;
1924 dst_y = FY + y1 * TILEY_VAR;
1926 if (mask_mode == USE_MASKING)
1927 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1930 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1933 MarkTileDirty(x1, y1);
1936 // check if movement end graphic inside screen area and should be drawn
1937 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1939 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1941 dst_x = FX + x2 * TILEX_VAR;
1942 dst_y = FY + y2 * TILEY_VAR;
1944 if (mask_mode == USE_MASKING)
1945 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1948 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 MarkTileDirty(x2, y2);
1955 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1956 int graphic, int frame,
1957 int cut_mode, int mask_mode)
1961 DrawGraphic(x, y, graphic, frame);
1966 if (graphic_info[graphic].double_movement) // EM style movement images
1967 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1969 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1972 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1973 int graphic, int frame, int cut_mode)
1975 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1978 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1979 int cut_mode, int mask_mode)
1981 int lx = LEVELX(x), ly = LEVELY(y);
1985 if (IN_LEV_FIELD(lx, ly))
1987 SetRandomAnimationValue(lx, ly);
1989 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1990 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1992 // do not use double (EM style) movement graphic when not moving
1993 if (graphic_info[graphic].double_movement && !dx && !dy)
1995 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1996 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1999 else // border element
2001 graphic = el2img(element);
2002 frame = getGraphicAnimationFrame(graphic, -1);
2005 if (element == EL_EXPANDABLE_WALL)
2007 boolean left_stopped = FALSE, right_stopped = FALSE;
2009 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2010 left_stopped = TRUE;
2011 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2012 right_stopped = TRUE;
2014 if (left_stopped && right_stopped)
2016 else if (left_stopped)
2018 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2019 frame = graphic_info[graphic].anim_frames - 1;
2021 else if (right_stopped)
2023 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2024 frame = graphic_info[graphic].anim_frames - 1;
2029 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2030 else if (mask_mode == USE_MASKING)
2031 DrawGraphicThruMask(x, y, graphic, frame);
2033 DrawGraphic(x, y, graphic, frame);
2036 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2037 int cut_mode, int mask_mode)
2039 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2040 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2041 cut_mode, mask_mode);
2044 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2047 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2050 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2053 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2056 void DrawLevelElementThruMask(int x, int y, int element)
2058 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2061 void DrawLevelFieldThruMask(int x, int y)
2063 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2066 // !!! implementation of quicksand is totally broken !!!
2067 #define IS_CRUMBLED_TILE(x, y, e) \
2068 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2069 !IS_MOVING(x, y) || \
2070 (e) == EL_QUICKSAND_EMPTYING || \
2071 (e) == EL_QUICKSAND_FAST_EMPTYING))
2073 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2078 int width, height, cx, cy;
2079 int sx = SCREENX(x), sy = SCREENY(y);
2080 int crumbled_border_size = graphic_info[graphic].border_size;
2081 int crumbled_tile_size = graphic_info[graphic].tile_size;
2082 int crumbled_border_size_var =
2083 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2086 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2088 for (i = 1; i < 4; i++)
2090 int dxx = (i & 1 ? dx : 0);
2091 int dyy = (i & 2 ? dy : 0);
2094 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2097 // check if neighbour field is of same crumble type
2098 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2099 graphic_info[graphic].class ==
2100 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2102 // return if check prevents inner corner
2103 if (same == (dxx == dx && dyy == dy))
2107 // if we reach this point, we have an inner corner
2109 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2111 width = crumbled_border_size_var;
2112 height = crumbled_border_size_var;
2113 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2114 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2116 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2117 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2120 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2125 int width, height, bx, by, cx, cy;
2126 int sx = SCREENX(x), sy = SCREENY(y);
2127 int crumbled_border_size = graphic_info[graphic].border_size;
2128 int crumbled_tile_size = graphic_info[graphic].tile_size;
2129 int crumbled_border_size_var =
2130 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2131 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2134 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2136 // draw simple, sloppy, non-corner-accurate crumbled border
2138 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2139 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2140 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2141 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2143 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2144 FX + sx * TILEX_VAR + cx,
2145 FY + sy * TILEY_VAR + cy);
2147 // (remaining middle border part must be at least as big as corner part)
2148 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2149 crumbled_border_size_var >= TILESIZE_VAR / 3)
2152 // correct corners of crumbled border, if needed
2154 for (i = -1; i <= 1; i += 2)
2156 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2157 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2158 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2161 // check if neighbour field is of same crumble type
2162 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2163 graphic_info[graphic].class ==
2164 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2166 // no crumbled corner, but continued crumbled border
2168 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2169 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2170 int b1 = (i == 1 ? crumbled_border_size_var :
2171 TILESIZE_VAR - 2 * crumbled_border_size_var);
2173 width = crumbled_border_size_var;
2174 height = crumbled_border_size_var;
2176 if (dir == 1 || dir == 2)
2191 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2193 FX + sx * TILEX_VAR + cx,
2194 FY + sy * TILEY_VAR + cy);
2199 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2201 int sx = SCREENX(x), sy = SCREENY(y);
2204 static int xy[4][2] =
2212 if (!IN_LEV_FIELD(x, y))
2215 element = TILE_GFX_ELEMENT(x, y);
2217 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2219 if (!IN_SCR_FIELD(sx, sy))
2222 // crumble field borders towards direct neighbour fields
2223 for (i = 0; i < 4; i++)
2225 int xx = x + xy[i][0];
2226 int yy = y + xy[i][1];
2228 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2231 // check if neighbour field is of same crumble type
2232 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2233 graphic_info[graphic].class ==
2234 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2237 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2240 // crumble inner field corners towards corner neighbour fields
2241 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2242 graphic_info[graphic].anim_frames == 2)
2244 for (i = 0; i < 4; i++)
2246 int dx = (i & 1 ? +1 : -1);
2247 int dy = (i & 2 ? +1 : -1);
2249 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2253 MarkTileDirty(sx, sy);
2255 else // center field is not crumbled -- crumble neighbour fields
2257 // crumble field borders of direct neighbour fields
2258 for (i = 0; i < 4; i++)
2260 int xx = x + xy[i][0];
2261 int yy = y + xy[i][1];
2262 int sxx = sx + xy[i][0];
2263 int syy = sy + xy[i][1];
2265 if (!IN_LEV_FIELD(xx, yy) ||
2266 !IN_SCR_FIELD(sxx, syy))
2269 // do not crumble fields that are being digged or snapped
2270 if (Tile[xx][yy] == EL_EMPTY ||
2271 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2274 element = TILE_GFX_ELEMENT(xx, yy);
2276 if (!IS_CRUMBLED_TILE(xx, yy, element))
2279 graphic = el_act2crm(element, ACTION_DEFAULT);
2281 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2283 MarkTileDirty(sxx, syy);
2286 // crumble inner field corners of corner neighbour fields
2287 for (i = 0; i < 4; i++)
2289 int dx = (i & 1 ? +1 : -1);
2290 int dy = (i & 2 ? +1 : -1);
2296 if (!IN_LEV_FIELD(xx, yy) ||
2297 !IN_SCR_FIELD(sxx, syy))
2300 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2303 element = TILE_GFX_ELEMENT(xx, yy);
2305 if (!IS_CRUMBLED_TILE(xx, yy, element))
2308 graphic = el_act2crm(element, ACTION_DEFAULT);
2310 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2311 graphic_info[graphic].anim_frames == 2)
2312 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2314 MarkTileDirty(sxx, syy);
2319 void DrawLevelFieldCrumbled(int x, int y)
2323 if (!IN_LEV_FIELD(x, y))
2326 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2327 GfxElement[x][y] != EL_UNDEFINED &&
2328 GFX_CRUMBLED(GfxElement[x][y]))
2330 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2335 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2337 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2340 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2343 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2344 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2345 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2346 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2347 int sx = SCREENX(x), sy = SCREENY(y);
2349 DrawGraphic(sx, sy, graphic1, frame1);
2350 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2353 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2355 int sx = SCREENX(x), sy = SCREENY(y);
2356 static int xy[4][2] =
2365 // crumble direct neighbour fields (required for field borders)
2366 for (i = 0; i < 4; i++)
2368 int xx = x + xy[i][0];
2369 int yy = y + xy[i][1];
2370 int sxx = sx + xy[i][0];
2371 int syy = sy + xy[i][1];
2373 if (!IN_LEV_FIELD(xx, yy) ||
2374 !IN_SCR_FIELD(sxx, syy) ||
2375 !GFX_CRUMBLED(Tile[xx][yy]) ||
2379 DrawLevelField(xx, yy);
2382 // crumble corner neighbour fields (required for inner field corners)
2383 for (i = 0; i < 4; i++)
2385 int dx = (i & 1 ? +1 : -1);
2386 int dy = (i & 2 ? +1 : -1);
2392 if (!IN_LEV_FIELD(xx, yy) ||
2393 !IN_SCR_FIELD(sxx, syy) ||
2394 !GFX_CRUMBLED(Tile[xx][yy]) ||
2398 int element = TILE_GFX_ELEMENT(xx, yy);
2399 int graphic = el_act2crm(element, ACTION_DEFAULT);
2401 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2402 graphic_info[graphic].anim_frames == 2)
2403 DrawLevelField(xx, yy);
2407 static int getBorderElement(int x, int y)
2411 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2412 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2413 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2414 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2415 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2416 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2417 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2419 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2420 int steel_position = (x == -1 && y == -1 ? 0 :
2421 x == lev_fieldx && y == -1 ? 1 :
2422 x == -1 && y == lev_fieldy ? 2 :
2423 x == lev_fieldx && y == lev_fieldy ? 3 :
2424 x == -1 || x == lev_fieldx ? 4 :
2425 y == -1 || y == lev_fieldy ? 5 : 6);
2427 return border[steel_position][steel_type];
2430 void DrawScreenElement(int x, int y, int element)
2432 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2433 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2436 void DrawLevelElement(int x, int y, int element)
2438 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2439 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2442 void DrawScreenField(int x, int y)
2444 int lx = LEVELX(x), ly = LEVELY(y);
2445 int element, content;
2447 if (!IN_LEV_FIELD(lx, ly))
2449 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2452 element = getBorderElement(lx, ly);
2454 DrawScreenElement(x, y, element);
2459 element = Tile[lx][ly];
2460 content = Store[lx][ly];
2462 if (IS_MOVING(lx, ly))
2464 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2465 boolean cut_mode = NO_CUTTING;
2467 if (element == EL_QUICKSAND_EMPTYING ||
2468 element == EL_QUICKSAND_FAST_EMPTYING ||
2469 element == EL_MAGIC_WALL_EMPTYING ||
2470 element == EL_BD_MAGIC_WALL_EMPTYING ||
2471 element == EL_DC_MAGIC_WALL_EMPTYING ||
2472 element == EL_AMOEBA_DROPPING)
2473 cut_mode = CUT_ABOVE;
2474 else if (element == EL_QUICKSAND_FILLING ||
2475 element == EL_QUICKSAND_FAST_FILLING ||
2476 element == EL_MAGIC_WALL_FILLING ||
2477 element == EL_BD_MAGIC_WALL_FILLING ||
2478 element == EL_DC_MAGIC_WALL_FILLING)
2479 cut_mode = CUT_BELOW;
2481 if (cut_mode == CUT_ABOVE)
2482 DrawScreenElement(x, y, element);
2484 DrawScreenElement(x, y, EL_EMPTY);
2487 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2488 else if (cut_mode == NO_CUTTING)
2489 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2492 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2494 if (cut_mode == CUT_BELOW &&
2495 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2496 DrawLevelElement(lx, ly + 1, element);
2499 if (content == EL_ACID)
2501 int dir = MovDir[lx][ly];
2502 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2503 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2505 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2507 // prevent target field from being drawn again (but without masking)
2508 // (this would happen if target field is scanned after moving element)
2509 Stop[newlx][newly] = TRUE;
2512 else if (IS_BLOCKED(lx, ly))
2517 boolean cut_mode = NO_CUTTING;
2518 int element_old, content_old;
2520 Blocked2Moving(lx, ly, &oldx, &oldy);
2523 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2524 MovDir[oldx][oldy] == MV_RIGHT);
2526 element_old = Tile[oldx][oldy];
2527 content_old = Store[oldx][oldy];
2529 if (element_old == EL_QUICKSAND_EMPTYING ||
2530 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2531 element_old == EL_MAGIC_WALL_EMPTYING ||
2532 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2533 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2534 element_old == EL_AMOEBA_DROPPING)
2535 cut_mode = CUT_ABOVE;
2537 DrawScreenElement(x, y, EL_EMPTY);
2540 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2542 else if (cut_mode == NO_CUTTING)
2543 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2546 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2549 else if (IS_DRAWABLE(element))
2550 DrawScreenElement(x, y, element);
2552 DrawScreenElement(x, y, EL_EMPTY);
2555 void DrawLevelField(int x, int y)
2557 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2558 DrawScreenField(SCREENX(x), SCREENY(y));
2559 else if (IS_MOVING(x, y))
2563 Moving2Blocked(x, y, &newx, &newy);
2564 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2565 DrawScreenField(SCREENX(newx), SCREENY(newy));
2567 else if (IS_BLOCKED(x, y))
2571 Blocked2Moving(x, y, &oldx, &oldy);
2572 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2573 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2577 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2578 int (*el2img_function)(int), boolean masked,
2579 int element_bits_draw)
2581 int element_base = map_mm_wall_element(element);
2582 int element_bits = (IS_DF_WALL(element) ?
2583 element - EL_DF_WALL_START :
2584 IS_MM_WALL(element) ?
2585 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2586 int graphic = el2img_function(element_base);
2587 int tilesize_draw = tilesize / 2;
2592 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2594 for (i = 0; i < 4; i++)
2596 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2597 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2599 if (!(element_bits_draw & (1 << i)))
2602 if (element_bits & (1 << i))
2605 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2606 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2608 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2609 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2614 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2615 tilesize_draw, tilesize_draw);
2620 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2621 boolean masked, int element_bits_draw)
2623 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2624 element, tilesize, el2edimg, masked, element_bits_draw);
2627 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2628 int (*el2img_function)(int))
2630 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2634 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2637 if (IS_MM_WALL(element))
2639 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2640 element, tilesize, el2edimg, masked, 0x000f);
2644 int graphic = el2edimg(element);
2647 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2649 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2653 void DrawSizedElement(int x, int y, int element, int tilesize)
2655 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2658 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2660 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2663 void DrawMiniElement(int x, int y, int element)
2667 graphic = el2edimg(element);
2668 DrawMiniGraphic(x, y, graphic);
2671 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2674 int x = sx + scroll_x, y = sy + scroll_y;
2676 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2677 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2678 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2679 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2681 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2684 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2686 int x = sx + scroll_x, y = sy + scroll_y;
2688 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2689 DrawMiniElement(sx, sy, EL_EMPTY);
2690 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2691 DrawMiniElement(sx, sy, Tile[x][y]);
2693 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2696 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2697 int x, int y, int xsize, int ysize,
2698 int tile_width, int tile_height)
2702 int dst_x = startx + x * tile_width;
2703 int dst_y = starty + y * tile_height;
2704 int width = graphic_info[graphic].width;
2705 int height = graphic_info[graphic].height;
2706 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2707 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2708 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2709 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2710 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2711 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2712 boolean draw_masked = graphic_info[graphic].draw_masked;
2714 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2716 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2718 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2722 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2723 inner_sx + (x - 1) * tile_width % inner_width);
2724 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2725 inner_sy + (y - 1) * tile_height % inner_height);
2728 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2731 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2735 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2736 int x, int y, int xsize, int ysize,
2739 int font_width = getFontWidth(font_nr);
2740 int font_height = getFontHeight(font_nr);
2742 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2743 font_width, font_height);
2746 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2748 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2749 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2750 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2751 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2752 boolean no_delay = (tape.warp_forward);
2753 unsigned int anim_delay = 0;
2754 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2755 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2756 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2757 int font_width = getFontWidth(font_nr);
2758 int font_height = getFontHeight(font_nr);
2759 int max_xsize = level.envelope[envelope_nr].xsize;
2760 int max_ysize = level.envelope[envelope_nr].ysize;
2761 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2762 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2763 int xend = max_xsize;
2764 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2765 int xstep = (xstart < xend ? 1 : 0);
2766 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2768 int end = MAX(xend - xstart, yend - ystart);
2771 for (i = start; i <= end; i++)
2773 int last_frame = end; // last frame of this "for" loop
2774 int x = xstart + i * xstep;
2775 int y = ystart + i * ystep;
2776 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2777 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2778 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2779 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2782 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2784 BlitScreenToBitmap(backbuffer);
2786 SetDrawtoField(DRAW_TO_BACKBUFFER);
2788 for (yy = 0; yy < ysize; yy++)
2789 for (xx = 0; xx < xsize; xx++)
2790 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2792 DrawTextBuffer(sx + font_width, sy + font_height,
2793 level.envelope[envelope_nr].text, font_nr, max_xsize,
2794 xsize - 2, ysize - 2, 0, mask_mode,
2795 level.envelope[envelope_nr].autowrap,
2796 level.envelope[envelope_nr].centered, FALSE);
2798 redraw_mask |= REDRAW_FIELD;
2801 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2804 ClearAutoRepeatKeyEvents();
2807 void ShowEnvelope(int envelope_nr)
2809 int element = EL_ENVELOPE_1 + envelope_nr;
2810 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2811 int sound_opening = element_info[element].sound[ACTION_OPENING];
2812 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2813 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2814 boolean no_delay = (tape.warp_forward);
2815 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2816 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2817 int anim_mode = graphic_info[graphic].anim_mode;
2818 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2819 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2821 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2823 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2825 if (anim_mode == ANIM_DEFAULT)
2826 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2828 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2831 Delay_WithScreenUpdates(wait_delay_value);
2833 WaitForEventToContinue();
2835 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2837 if (anim_mode != ANIM_NONE)
2838 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2840 if (anim_mode == ANIM_DEFAULT)
2841 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2843 game.envelope_active = FALSE;
2845 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2847 redraw_mask |= REDRAW_FIELD;
2851 static void setRequestBasePosition(int *x, int *y)
2853 int sx_base, sy_base;
2855 if (request.x != -1)
2856 sx_base = request.x;
2857 else if (request.align == ALIGN_LEFT)
2859 else if (request.align == ALIGN_RIGHT)
2860 sx_base = SX + SXSIZE;
2862 sx_base = SX + SXSIZE / 2;
2864 if (request.y != -1)
2865 sy_base = request.y;
2866 else if (request.valign == VALIGN_TOP)
2868 else if (request.valign == VALIGN_BOTTOM)
2869 sy_base = SY + SYSIZE;
2871 sy_base = SY + SYSIZE / 2;
2877 static void setRequestPositionExt(int *x, int *y, int width, int height,
2878 boolean add_border_size)
2880 int border_size = request.border_size;
2881 int sx_base, sy_base;
2884 setRequestBasePosition(&sx_base, &sy_base);
2886 if (request.align == ALIGN_LEFT)
2888 else if (request.align == ALIGN_RIGHT)
2889 sx = sx_base - width;
2891 sx = sx_base - width / 2;
2893 if (request.valign == VALIGN_TOP)
2895 else if (request.valign == VALIGN_BOTTOM)
2896 sy = sy_base - height;
2898 sy = sy_base - height / 2;
2900 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2901 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2903 if (add_border_size)
2913 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2915 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2918 static void DrawEnvelopeRequest(char *text)
2920 char *text_final = text;
2921 char *text_door_style = NULL;
2922 int graphic = IMG_BACKGROUND_REQUEST;
2923 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2924 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2925 int font_nr = FONT_REQUEST;
2926 int font_width = getFontWidth(font_nr);
2927 int font_height = getFontHeight(font_nr);
2928 int border_size = request.border_size;
2929 int line_spacing = request.line_spacing;
2930 int line_height = font_height + line_spacing;
2931 int max_text_width = request.width - 2 * border_size;
2932 int max_text_height = request.height - 2 * border_size;
2933 int line_length = max_text_width / font_width;
2934 int max_lines = max_text_height / line_height;
2935 int text_width = line_length * font_width;
2936 int width = request.width;
2937 int height = request.height;
2938 int tile_size = MAX(request.step_offset, 1);
2939 int x_steps = width / tile_size;
2940 int y_steps = height / tile_size;
2941 int sx_offset = border_size;
2942 int sy_offset = border_size;
2946 if (request.centered)
2947 sx_offset = (request.width - text_width) / 2;
2949 if (request.wrap_single_words && !request.autowrap)
2951 char *src_text_ptr, *dst_text_ptr;
2953 text_door_style = checked_malloc(2 * strlen(text) + 1);
2955 src_text_ptr = text;
2956 dst_text_ptr = text_door_style;
2958 while (*src_text_ptr)
2960 if (*src_text_ptr == ' ' ||
2961 *src_text_ptr == '?' ||
2962 *src_text_ptr == '!')
2963 *dst_text_ptr++ = '\n';
2965 if (*src_text_ptr != ' ')
2966 *dst_text_ptr++ = *src_text_ptr;
2971 *dst_text_ptr = '\0';
2973 text_final = text_door_style;
2976 setRequestPosition(&sx, &sy, FALSE);
2978 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2980 for (y = 0; y < y_steps; y++)
2981 for (x = 0; x < x_steps; x++)
2982 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2983 x, y, x_steps, y_steps,
2984 tile_size, tile_size);
2986 // force DOOR font inside door area
2987 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2989 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2990 line_length, -1, max_lines, line_spacing, mask_mode,
2991 request.autowrap, request.centered, FALSE);
2995 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2996 RedrawGadget(tool_gadget[i]);
2998 // store readily prepared envelope request for later use when animating
2999 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3001 if (text_door_style)
3002 free(text_door_style);
3005 static void AnimateEnvelopeRequest(int anim_mode, int action)
3007 int graphic = IMG_BACKGROUND_REQUEST;
3008 boolean draw_masked = graphic_info[graphic].draw_masked;
3009 int delay_value_normal = request.step_delay;
3010 int delay_value_fast = delay_value_normal / 2;
3011 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3012 boolean no_delay = (tape.warp_forward);
3013 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3014 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3015 unsigned int anim_delay = 0;
3017 int tile_size = MAX(request.step_offset, 1);
3018 int max_xsize = request.width / tile_size;
3019 int max_ysize = request.height / tile_size;
3020 int max_xsize_inner = max_xsize - 2;
3021 int max_ysize_inner = max_ysize - 2;
3023 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3024 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3025 int xend = max_xsize_inner;
3026 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3027 int xstep = (xstart < xend ? 1 : 0);
3028 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3030 int end = MAX(xend - xstart, yend - ystart);
3033 if (setup.quick_doors)
3040 for (i = start; i <= end; i++)
3042 int last_frame = end; // last frame of this "for" loop
3043 int x = xstart + i * xstep;
3044 int y = ystart + i * ystep;
3045 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3046 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3047 int xsize_size_left = (xsize - 1) * tile_size;
3048 int ysize_size_top = (ysize - 1) * tile_size;
3049 int max_xsize_pos = (max_xsize - 1) * tile_size;
3050 int max_ysize_pos = (max_ysize - 1) * tile_size;
3051 int width = xsize * tile_size;
3052 int height = ysize * tile_size;
3057 setRequestPosition(&src_x, &src_y, FALSE);
3058 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3060 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3062 for (yy = 0; yy < 2; yy++)
3064 for (xx = 0; xx < 2; xx++)
3066 int src_xx = src_x + xx * max_xsize_pos;
3067 int src_yy = src_y + yy * max_ysize_pos;
3068 int dst_xx = dst_x + xx * xsize_size_left;
3069 int dst_yy = dst_y + yy * ysize_size_top;
3070 int xx_size = (xx ? tile_size : xsize_size_left);
3071 int yy_size = (yy ? tile_size : ysize_size_top);
3074 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3075 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3077 BlitBitmap(bitmap_db_store_2, backbuffer,
3078 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3082 redraw_mask |= REDRAW_FIELD;
3086 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3089 ClearAutoRepeatKeyEvents();
3092 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3094 int graphic = IMG_BACKGROUND_REQUEST;
3095 int sound_opening = SND_REQUEST_OPENING;
3096 int sound_closing = SND_REQUEST_CLOSING;
3097 int anim_mode_1 = request.anim_mode; // (higher priority)
3098 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3099 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3100 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3101 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3103 if (game_status == GAME_MODE_PLAYING)
3104 BlitScreenToBitmap(backbuffer);
3106 SetDrawtoField(DRAW_TO_BACKBUFFER);
3108 // SetDrawBackgroundMask(REDRAW_NONE);
3110 if (action == ACTION_OPENING)
3112 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3114 if (req_state & REQ_ASK)
3116 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3117 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3118 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3119 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3121 else if (req_state & REQ_CONFIRM)
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3124 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3126 else if (req_state & REQ_PLAYER)
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3134 DrawEnvelopeRequest(text);
3137 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3139 if (action == ACTION_OPENING)
3141 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3143 if (anim_mode == ANIM_DEFAULT)
3144 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3146 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3150 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3152 if (anim_mode != ANIM_NONE)
3153 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3155 if (anim_mode == ANIM_DEFAULT)
3156 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3159 game.envelope_active = FALSE;
3161 if (action == ACTION_CLOSING)
3162 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3164 // SetDrawBackgroundMask(last_draw_background_mask);
3166 redraw_mask |= REDRAW_FIELD;
3170 if (action == ACTION_CLOSING &&
3171 game_status == GAME_MODE_PLAYING &&
3172 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3173 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3176 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3178 if (IS_MM_WALL(element))
3180 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3186 int graphic = el2preimg(element);
3188 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3189 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3194 void DrawLevel(int draw_background_mask)
3198 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3199 SetDrawBackgroundMask(draw_background_mask);
3203 for (x = BX1; x <= BX2; x++)
3204 for (y = BY1; y <= BY2; y++)
3205 DrawScreenField(x, y);
3207 redraw_mask |= REDRAW_FIELD;
3210 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3215 for (x = 0; x < size_x; x++)
3216 for (y = 0; y < size_y; y++)
3217 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3219 redraw_mask |= REDRAW_FIELD;
3222 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3226 for (x = 0; x < size_x; x++)
3227 for (y = 0; y < size_y; y++)
3228 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3230 redraw_mask |= REDRAW_FIELD;
3233 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3235 boolean show_level_border = (BorderElement != EL_EMPTY);
3236 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3237 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3238 int tile_size = preview.tile_size;
3239 int preview_width = preview.xsize * tile_size;
3240 int preview_height = preview.ysize * tile_size;
3241 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3242 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3243 int real_preview_width = real_preview_xsize * tile_size;
3244 int real_preview_height = real_preview_ysize * tile_size;
3245 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3246 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3249 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3252 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3254 dst_x += (preview_width - real_preview_width) / 2;
3255 dst_y += (preview_height - real_preview_height) / 2;
3257 for (x = 0; x < real_preview_xsize; x++)
3259 for (y = 0; y < real_preview_ysize; y++)
3261 int lx = from_x + x + (show_level_border ? -1 : 0);
3262 int ly = from_y + y + (show_level_border ? -1 : 0);
3263 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3264 getBorderElement(lx, ly));
3266 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3267 element, tile_size);
3271 redraw_mask |= REDRAW_FIELD;
3274 #define MICROLABEL_EMPTY 0
3275 #define MICROLABEL_LEVEL_NAME 1
3276 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3277 #define MICROLABEL_LEVEL_AUTHOR 3
3278 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3279 #define MICROLABEL_IMPORTED_FROM 5
3280 #define MICROLABEL_IMPORTED_BY_HEAD 6
3281 #define MICROLABEL_IMPORTED_BY 7
3283 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3285 int max_text_width = SXSIZE;
3286 int font_width = getFontWidth(font_nr);
3288 if (pos->align == ALIGN_CENTER)
3289 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3290 else if (pos->align == ALIGN_RIGHT)
3291 max_text_width = pos->x;
3293 max_text_width = SXSIZE - pos->x;
3295 return max_text_width / font_width;
3298 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3300 char label_text[MAX_OUTPUT_LINESIZE + 1];
3301 int max_len_label_text;
3302 int font_nr = pos->font;
3305 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3308 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3309 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3310 mode == MICROLABEL_IMPORTED_BY_HEAD)
3311 font_nr = pos->font_alt;
3313 max_len_label_text = getMaxTextLength(pos, font_nr);
3315 if (pos->size != -1)
3316 max_len_label_text = pos->size;
3318 for (i = 0; i < max_len_label_text; i++)
3319 label_text[i] = ' ';
3320 label_text[max_len_label_text] = '\0';
3322 if (strlen(label_text) > 0)
3323 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3326 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3327 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3328 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3329 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3330 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3331 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3332 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3333 max_len_label_text);
3334 label_text[max_len_label_text] = '\0';
3336 if (strlen(label_text) > 0)
3337 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3339 redraw_mask |= REDRAW_FIELD;
3342 static void DrawPreviewLevelLabel(int mode)
3344 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3347 static void DrawPreviewLevelInfo(int mode)
3349 if (mode == MICROLABEL_LEVEL_NAME)
3350 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3351 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3352 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3355 static void DrawPreviewLevelExt(boolean restart)
3357 static unsigned int scroll_delay = 0;
3358 static unsigned int label_delay = 0;
3359 static int from_x, from_y, scroll_direction;
3360 static int label_state, label_counter;
3361 unsigned int scroll_delay_value = preview.step_delay;
3362 boolean show_level_border = (BorderElement != EL_EMPTY);
3363 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3364 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3371 if (preview.anim_mode == ANIM_CENTERED)
3373 if (level_xsize > preview.xsize)
3374 from_x = (level_xsize - preview.xsize) / 2;
3375 if (level_ysize > preview.ysize)
3376 from_y = (level_ysize - preview.ysize) / 2;
3379 from_x += preview.xoffset;
3380 from_y += preview.yoffset;
3382 scroll_direction = MV_RIGHT;
3386 DrawPreviewLevelPlayfield(from_x, from_y);
3387 DrawPreviewLevelLabel(label_state);
3389 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3390 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3392 // initialize delay counters
3393 DelayReached(&scroll_delay, 0);
3394 DelayReached(&label_delay, 0);
3396 if (leveldir_current->name)
3398 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3399 char label_text[MAX_OUTPUT_LINESIZE + 1];
3400 int font_nr = pos->font;
3401 int max_len_label_text = getMaxTextLength(pos, font_nr);
3403 if (pos->size != -1)
3404 max_len_label_text = pos->size;
3406 strncpy(label_text, leveldir_current->name, max_len_label_text);
3407 label_text[max_len_label_text] = '\0';
3409 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3410 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3416 // scroll preview level, if needed
3417 if (preview.anim_mode != ANIM_NONE &&
3418 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3419 DelayReached(&scroll_delay, scroll_delay_value))
3421 switch (scroll_direction)
3426 from_x -= preview.step_offset;
3427 from_x = (from_x < 0 ? 0 : from_x);
3430 scroll_direction = MV_UP;
3434 if (from_x < level_xsize - preview.xsize)
3436 from_x += preview.step_offset;
3437 from_x = (from_x > level_xsize - preview.xsize ?
3438 level_xsize - preview.xsize : from_x);
3441 scroll_direction = MV_DOWN;
3447 from_y -= preview.step_offset;
3448 from_y = (from_y < 0 ? 0 : from_y);
3451 scroll_direction = MV_RIGHT;
3455 if (from_y < level_ysize - preview.ysize)
3457 from_y += preview.step_offset;
3458 from_y = (from_y > level_ysize - preview.ysize ?
3459 level_ysize - preview.ysize : from_y);
3462 scroll_direction = MV_LEFT;
3469 DrawPreviewLevelPlayfield(from_x, from_y);
3472 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3473 // redraw micro level label, if needed
3474 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3475 !strEqual(level.author, ANONYMOUS_NAME) &&
3476 !strEqual(level.author, leveldir_current->name) &&
3477 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3479 int max_label_counter = 23;
3481 if (leveldir_current->imported_from != NULL &&
3482 strlen(leveldir_current->imported_from) > 0)
3483 max_label_counter += 14;
3484 if (leveldir_current->imported_by != NULL &&
3485 strlen(leveldir_current->imported_by) > 0)
3486 max_label_counter += 14;
3488 label_counter = (label_counter + 1) % max_label_counter;
3489 label_state = (label_counter >= 0 && label_counter <= 7 ?
3490 MICROLABEL_LEVEL_NAME :
3491 label_counter >= 9 && label_counter <= 12 ?
3492 MICROLABEL_LEVEL_AUTHOR_HEAD :
3493 label_counter >= 14 && label_counter <= 21 ?
3494 MICROLABEL_LEVEL_AUTHOR :
3495 label_counter >= 23 && label_counter <= 26 ?
3496 MICROLABEL_IMPORTED_FROM_HEAD :
3497 label_counter >= 28 && label_counter <= 35 ?
3498 MICROLABEL_IMPORTED_FROM :
3499 label_counter >= 37 && label_counter <= 40 ?
3500 MICROLABEL_IMPORTED_BY_HEAD :
3501 label_counter >= 42 && label_counter <= 49 ?
3502 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3504 if (leveldir_current->imported_from == NULL &&
3505 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3506 label_state == MICROLABEL_IMPORTED_FROM))
3507 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3508 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3510 DrawPreviewLevelLabel(label_state);
3514 void DrawPreviewPlayers(void)
3516 if (game_status != GAME_MODE_MAIN)
3519 // do not draw preview players if level preview redefined, but players aren't
3520 if (preview.redefined && !menu.main.preview_players.redefined)
3523 boolean player_found[MAX_PLAYERS];
3524 int num_players = 0;
3527 for (i = 0; i < MAX_PLAYERS; i++)
3528 player_found[i] = FALSE;
3530 // check which players can be found in the level (simple approach)
3531 for (x = 0; x < lev_fieldx; x++)
3533 for (y = 0; y < lev_fieldy; y++)
3535 int element = level.field[x][y];
3537 if (ELEM_IS_PLAYER(element))
3539 int player_nr = GET_PLAYER_NR(element);
3541 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3543 if (!player_found[player_nr])
3546 player_found[player_nr] = TRUE;
3551 struct TextPosInfo *pos = &menu.main.preview_players;
3552 int tile_size = pos->tile_size;
3553 int border_size = pos->border_size;
3554 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3555 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3556 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3557 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3558 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3559 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3560 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3561 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3562 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3563 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3564 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3565 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3567 // clear area in which the players will be drawn
3568 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3569 max_players_width, max_players_height);
3571 if (!network.enabled && !setup.team_mode)
3574 // only draw players if level is suited for team mode
3575 if (num_players < 2)
3578 // draw all players that were found in the level
3579 for (i = 0; i < MAX_PLAYERS; i++)
3581 if (player_found[i])
3583 int graphic = el2img(EL_PLAYER_1 + i);
3585 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3587 xpos += player_xoffset;
3588 ypos += player_yoffset;
3593 void DrawPreviewLevelInitial(void)
3595 DrawPreviewLevelExt(TRUE);
3596 DrawPreviewPlayers();
3599 void DrawPreviewLevelAnimation(void)
3601 DrawPreviewLevelExt(FALSE);
3604 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3605 int border_size, int font_nr)
3607 int graphic = el2img(EL_PLAYER_1 + player_nr);
3608 int font_height = getFontHeight(font_nr);
3609 int player_height = MAX(tile_size, font_height);
3610 int xoffset_text = tile_size + border_size;
3611 int yoffset_text = (player_height - font_height) / 2;
3612 int yoffset_graphic = (player_height - tile_size) / 2;
3613 char *player_name = getNetworkPlayerName(player_nr + 1);
3615 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3617 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3620 static void DrawNetworkPlayersExt(boolean force)
3622 if (game_status != GAME_MODE_MAIN)
3625 if (!network.connected && !force)
3628 // do not draw network players if level preview redefined, but players aren't
3629 if (preview.redefined && !menu.main.network_players.redefined)
3632 int num_players = 0;
3635 for (i = 0; i < MAX_PLAYERS; i++)
3636 if (stored_player[i].connected_network)
3639 struct TextPosInfo *pos = &menu.main.network_players;
3640 int tile_size = pos->tile_size;
3641 int border_size = pos->border_size;
3642 int xoffset_text = tile_size + border_size;
3643 int font_nr = pos->font;
3644 int font_width = getFontWidth(font_nr);
3645 int font_height = getFontHeight(font_nr);
3646 int player_height = MAX(tile_size, font_height);
3647 int player_yoffset = player_height + border_size;
3648 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3649 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3650 int all_players_height = num_players * player_yoffset - border_size;
3651 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3652 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3653 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3655 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3656 max_players_width, max_players_height);
3658 // first draw local network player ...
3659 for (i = 0; i < MAX_PLAYERS; i++)
3661 if (stored_player[i].connected_network &&
3662 stored_player[i].connected_locally)
3664 char *player_name = getNetworkPlayerName(i + 1);
3665 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3666 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3668 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3670 ypos += player_yoffset;
3674 // ... then draw all other network players
3675 for (i = 0; i < MAX_PLAYERS; i++)
3677 if (stored_player[i].connected_network &&
3678 !stored_player[i].connected_locally)
3680 char *player_name = getNetworkPlayerName(i + 1);
3681 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3682 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3684 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3686 ypos += player_yoffset;
3691 void DrawNetworkPlayers(void)
3693 DrawNetworkPlayersExt(FALSE);
3696 void ClearNetworkPlayers(void)
3698 DrawNetworkPlayersExt(TRUE);
3701 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3702 int graphic, int sync_frame,
3705 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3707 if (mask_mode == USE_MASKING)
3708 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3710 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3713 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3714 int graphic, int sync_frame, int mask_mode)
3716 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3718 if (mask_mode == USE_MASKING)
3719 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3721 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3724 static void DrawGraphicAnimation(int x, int y, int graphic)
3726 int lx = LEVELX(x), ly = LEVELY(y);
3728 if (!IN_SCR_FIELD(x, y))
3731 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3732 graphic, GfxFrame[lx][ly], NO_MASKING);
3734 MarkTileDirty(x, y);
3737 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3739 int lx = LEVELX(x), ly = LEVELY(y);
3741 if (!IN_SCR_FIELD(x, y))
3744 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3745 graphic, GfxFrame[lx][ly], NO_MASKING);
3746 MarkTileDirty(x, y);
3749 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3751 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3754 void DrawLevelElementAnimation(int x, int y, int element)
3756 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3758 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3761 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3763 int sx = SCREENX(x), sy = SCREENY(y);
3765 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3768 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3771 DrawGraphicAnimation(sx, sy, graphic);
3774 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3775 DrawLevelFieldCrumbled(x, y);
3777 if (GFX_CRUMBLED(Tile[x][y]))
3778 DrawLevelFieldCrumbled(x, y);
3782 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3784 int sx = SCREENX(x), sy = SCREENY(y);
3787 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3790 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3792 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3795 DrawGraphicAnimation(sx, sy, graphic);
3797 if (GFX_CRUMBLED(element))
3798 DrawLevelFieldCrumbled(x, y);
3801 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3803 if (player->use_murphy)
3805 // this works only because currently only one player can be "murphy" ...
3806 static int last_horizontal_dir = MV_LEFT;
3807 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3809 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3810 last_horizontal_dir = move_dir;
3812 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3814 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3816 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3822 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3825 static boolean equalGraphics(int graphic1, int graphic2)
3827 struct GraphicInfo *g1 = &graphic_info[graphic1];
3828 struct GraphicInfo *g2 = &graphic_info[graphic2];
3830 return (g1->bitmap == g2->bitmap &&
3831 g1->src_x == g2->src_x &&
3832 g1->src_y == g2->src_y &&
3833 g1->anim_frames == g2->anim_frames &&
3834 g1->anim_delay == g2->anim_delay &&
3835 g1->anim_mode == g2->anim_mode);
3838 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3842 DRAW_PLAYER_STAGE_INIT = 0,
3843 DRAW_PLAYER_STAGE_LAST_FIELD,
3844 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3845 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3846 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3847 DRAW_PLAYER_STAGE_PLAYER,
3849 DRAW_PLAYER_STAGE_PLAYER,
3850 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3852 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3853 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3855 NUM_DRAW_PLAYER_STAGES
3858 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3860 static int static_last_player_graphic[MAX_PLAYERS];
3861 static int static_last_player_frame[MAX_PLAYERS];
3862 static boolean static_player_is_opaque[MAX_PLAYERS];
3863 static boolean draw_player[MAX_PLAYERS];
3864 int pnr = player->index_nr;
3866 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3868 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3869 static_last_player_frame[pnr] = player->Frame;
3870 static_player_is_opaque[pnr] = FALSE;
3872 draw_player[pnr] = TRUE;
3875 if (!draw_player[pnr])
3879 if (!IN_LEV_FIELD(player->jx, player->jy))
3881 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3882 Debug("draw:DrawPlayerExt", "This should never happen!");
3884 draw_player[pnr] = FALSE;
3890 int last_player_graphic = static_last_player_graphic[pnr];
3891 int last_player_frame = static_last_player_frame[pnr];
3892 boolean player_is_opaque = static_player_is_opaque[pnr];
3894 int jx = player->jx;
3895 int jy = player->jy;
3896 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3897 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3898 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3899 int last_jx = (player->is_moving ? jx - dx : jx);
3900 int last_jy = (player->is_moving ? jy - dy : jy);
3901 int next_jx = jx + dx;
3902 int next_jy = jy + dy;
3903 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3904 int sx = SCREENX(jx);
3905 int sy = SCREENY(jy);
3906 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3907 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3908 int element = Tile[jx][jy];
3909 int last_element = Tile[last_jx][last_jy];
3910 int action = (player->is_pushing ? ACTION_PUSHING :
3911 player->is_digging ? ACTION_DIGGING :
3912 player->is_collecting ? ACTION_COLLECTING :
3913 player->is_moving ? ACTION_MOVING :
3914 player->is_snapping ? ACTION_SNAPPING :
3915 player->is_dropping ? ACTION_DROPPING :
3916 player->is_waiting ? player->action_waiting :
3919 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3921 // ------------------------------------------------------------------------
3922 // initialize drawing the player
3923 // ------------------------------------------------------------------------
3925 draw_player[pnr] = FALSE;
3927 // GfxElement[][] is set to the element the player is digging or collecting;
3928 // remove also for off-screen player if the player is not moving anymore
3929 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3930 GfxElement[jx][jy] = EL_UNDEFINED;
3932 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3935 if (element == EL_EXPLOSION)
3938 InitPlayerGfxAnimation(player, action, move_dir);
3940 draw_player[pnr] = TRUE;
3942 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3944 // ------------------------------------------------------------------------
3945 // draw things in the field the player is leaving, if needed
3946 // ------------------------------------------------------------------------
3948 if (!IN_SCR_FIELD(sx, sy))
3949 draw_player[pnr] = FALSE;
3951 if (!player->is_moving)
3954 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3956 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3958 if (last_element == EL_DYNAMITE_ACTIVE ||
3959 last_element == EL_EM_DYNAMITE_ACTIVE ||
3960 last_element == EL_SP_DISK_RED_ACTIVE)
3961 DrawDynamite(last_jx, last_jy);
3963 DrawLevelFieldThruMask(last_jx, last_jy);
3965 else if (last_element == EL_DYNAMITE_ACTIVE ||
3966 last_element == EL_EM_DYNAMITE_ACTIVE ||
3967 last_element == EL_SP_DISK_RED_ACTIVE)
3968 DrawDynamite(last_jx, last_jy);
3970 DrawLevelField(last_jx, last_jy);
3972 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3973 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3975 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3977 // ------------------------------------------------------------------------
3978 // draw things behind the player, if needed
3979 // ------------------------------------------------------------------------
3983 DrawLevelElement(jx, jy, Back[jx][jy]);
3988 if (IS_ACTIVE_BOMB(element))
3990 DrawLevelElement(jx, jy, EL_EMPTY);
3995 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3997 int old_element = GfxElement[jx][jy];
3998 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3999 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4001 if (GFX_CRUMBLED(old_element))
4002 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4004 DrawGraphic(sx, sy, old_graphic, frame);
4006 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4007 static_player_is_opaque[pnr] = TRUE;
4011 GfxElement[jx][jy] = EL_UNDEFINED;
4013 // make sure that pushed elements are drawn with correct frame rate
4014 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4016 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4017 GfxFrame[jx][jy] = player->StepFrame;
4019 DrawLevelField(jx, jy);
4022 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4024 // ------------------------------------------------------------------------
4025 // draw things the player is pushing, if needed
4026 // ------------------------------------------------------------------------
4028 if (!player->is_pushing || !player->is_moving)
4031 int gfx_frame = GfxFrame[jx][jy];
4033 if (!IS_MOVING(jx, jy)) // push movement already finished
4035 element = Tile[next_jx][next_jy];
4036 gfx_frame = GfxFrame[next_jx][next_jy];
4039 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4040 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4041 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4043 // draw background element under pushed element (like the Sokoban field)
4044 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4046 // this allows transparent pushing animation over non-black background
4049 DrawLevelElement(jx, jy, Back[jx][jy]);
4051 DrawLevelElement(jx, jy, EL_EMPTY);
4053 if (Back[next_jx][next_jy])
4054 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4056 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4058 else if (Back[next_jx][next_jy])
4059 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4061 int px = SCREENX(jx), py = SCREENY(jy);
4062 int pxx = (TILEX - ABS(sxx)) * dx;
4063 int pyy = (TILEY - ABS(syy)) * dy;
4066 // do not draw (EM style) pushing animation when pushing is finished
4067 // (two-tile animations usually do not contain start and end frame)
4068 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4069 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4071 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4073 // masked drawing is needed for EMC style (double) movement graphics
4074 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4075 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4078 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4080 // ------------------------------------------------------------------------
4081 // draw player himself
4082 // ------------------------------------------------------------------------
4084 int graphic = getPlayerGraphic(player, move_dir);
4086 // in the case of changed player action or direction, prevent the current
4087 // animation frame from being restarted for identical animations
4088 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4089 player->Frame = last_player_frame;
4091 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4093 if (player_is_opaque)
4094 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4096 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4098 if (SHIELD_ON(player))
4100 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4101 IMG_SHIELD_NORMAL_ACTIVE);
4102 frame = getGraphicAnimationFrame(graphic, -1);
4104 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4107 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4109 // ------------------------------------------------------------------------
4110 // draw things in front of player (active dynamite or dynabombs)
4111 // ------------------------------------------------------------------------
4113 if (IS_ACTIVE_BOMB(element))
4115 int graphic = el2img(element);
4116 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4118 if (game.emulation == EMU_SUPAPLEX)
4119 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4121 DrawGraphicThruMask(sx, sy, graphic, frame);
4124 if (player_is_moving && last_element == EL_EXPLOSION)
4126 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4127 GfxElement[last_jx][last_jy] : EL_EMPTY);
4128 int graphic = el_act2img(element, ACTION_EXPLODING);
4129 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4130 int phase = ExplodePhase[last_jx][last_jy] - 1;
4131 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4134 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4137 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4139 // ------------------------------------------------------------------------
4140 // draw elements the player is just walking/passing through/under
4141 // ------------------------------------------------------------------------
4143 if (player_is_moving)
4145 // handle the field the player is leaving ...
4146 if (IS_ACCESSIBLE_INSIDE(last_element))
4147 DrawLevelField(last_jx, last_jy);
4148 else if (IS_ACCESSIBLE_UNDER(last_element))
4149 DrawLevelFieldThruMask(last_jx, last_jy);
4152 // do not redraw accessible elements if the player is just pushing them
4153 if (!player_is_moving || !player->is_pushing)
4155 // ... and the field the player is entering
4156 if (IS_ACCESSIBLE_INSIDE(element))
4157 DrawLevelField(jx, jy);
4158 else if (IS_ACCESSIBLE_UNDER(element))
4159 DrawLevelFieldThruMask(jx, jy);
4162 MarkTileDirty(sx, sy);
4166 void DrawPlayer(struct PlayerInfo *player)
4170 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4171 DrawPlayerExt(player, i);
4174 void DrawAllPlayers(void)
4178 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4179 for (j = 0; j < MAX_PLAYERS; j++)
4180 if (stored_player[j].active)
4181 DrawPlayerExt(&stored_player[j], i);
4184 void DrawPlayerField(int x, int y)
4186 if (!IS_PLAYER(x, y))
4189 DrawPlayer(PLAYERINFO(x, y));
4192 // ----------------------------------------------------------------------------
4194 void WaitForEventToContinue(void)
4196 boolean still_wait = TRUE;
4198 if (program.headless)
4201 // simulate releasing mouse button over last gadget, if still pressed
4203 HandleGadgets(-1, -1, 0);
4205 button_status = MB_RELEASED;
4213 if (NextValidEvent(&event))
4217 case EVENT_BUTTONRELEASE:
4218 case EVENT_KEYPRESS:
4219 case SDL_CONTROLLERBUTTONDOWN:
4220 case SDL_JOYBUTTONDOWN:
4224 case EVENT_KEYRELEASE:
4225 ClearPlayerAction();
4229 HandleOtherEvents(&event);
4233 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4242 #define MAX_REQUEST_LINES 13
4243 #define MAX_REQUEST_LINE_FONT1_LEN 7
4244 #define MAX_REQUEST_LINE_FONT2_LEN 10
4246 static int RequestHandleEvents(unsigned int req_state)
4248 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4250 int width = request.width;
4251 int height = request.height;
4255 // when showing request dialog after game ended, deactivate game panel
4256 if (game_just_ended)
4257 game.panel.active = FALSE;
4259 game.request_active = TRUE;
4261 setRequestPosition(&sx, &sy, FALSE);
4263 button_status = MB_RELEASED;
4265 request_gadget_id = -1;
4270 if (game_just_ended)
4272 // the MM game engine does not use a special (scrollable) field buffer
4273 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4274 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4276 HandleGameActions();
4278 SetDrawtoField(DRAW_TO_BACKBUFFER);
4280 if (global.use_envelope_request)
4282 // copy current state of request area to middle of playfield area
4283 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4291 while (NextValidEvent(&event))
4295 case EVENT_BUTTONPRESS:
4296 case EVENT_BUTTONRELEASE:
4297 case EVENT_MOTIONNOTIFY:
4301 if (event.type == EVENT_MOTIONNOTIFY)
4306 motion_status = TRUE;
4307 mx = ((MotionEvent *) &event)->x;
4308 my = ((MotionEvent *) &event)->y;
4312 motion_status = FALSE;
4313 mx = ((ButtonEvent *) &event)->x;
4314 my = ((ButtonEvent *) &event)->y;
4315 if (event.type == EVENT_BUTTONPRESS)
4316 button_status = ((ButtonEvent *) &event)->button;
4318 button_status = MB_RELEASED;
4321 // this sets 'request_gadget_id'
4322 HandleGadgets(mx, my, button_status);
4324 switch (request_gadget_id)
4326 case TOOL_CTRL_ID_YES:
4327 case TOOL_CTRL_ID_TOUCH_YES:
4330 case TOOL_CTRL_ID_NO:
4331 case TOOL_CTRL_ID_TOUCH_NO:
4334 case TOOL_CTRL_ID_CONFIRM:
4335 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4336 result = TRUE | FALSE;
4339 case TOOL_CTRL_ID_PLAYER_1:
4342 case TOOL_CTRL_ID_PLAYER_2:
4345 case TOOL_CTRL_ID_PLAYER_3:
4348 case TOOL_CTRL_ID_PLAYER_4:
4353 // only check clickable animations if no request gadget clicked
4354 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4361 case SDL_WINDOWEVENT:
4362 HandleWindowEvent((WindowEvent *) &event);
4365 case SDL_APP_WILLENTERBACKGROUND:
4366 case SDL_APP_DIDENTERBACKGROUND:
4367 case SDL_APP_WILLENTERFOREGROUND:
4368 case SDL_APP_DIDENTERFOREGROUND:
4369 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4372 case EVENT_KEYPRESS:
4374 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4379 if (req_state & REQ_CONFIRM)
4388 #if defined(KSYM_Rewind)
4389 case KSYM_Rewind: // for Amazon Fire TV remote
4398 #if defined(KSYM_FastForward)
4399 case KSYM_FastForward: // for Amazon Fire TV remote
4405 HandleKeysDebug(key, KEY_PRESSED);
4409 if (req_state & REQ_PLAYER)
4411 int old_player_nr = setup.network_player_nr;
4414 result = old_player_nr + 1;
4419 result = old_player_nr + 1;
4450 case EVENT_KEYRELEASE:
4451 ClearPlayerAction();
4454 case SDL_CONTROLLERBUTTONDOWN:
4455 switch (event.cbutton.button)
4457 case SDL_CONTROLLER_BUTTON_A:
4458 case SDL_CONTROLLER_BUTTON_X:
4459 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4460 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4464 case SDL_CONTROLLER_BUTTON_B:
4465 case SDL_CONTROLLER_BUTTON_Y:
4466 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4467 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4468 case SDL_CONTROLLER_BUTTON_BACK:
4473 if (req_state & REQ_PLAYER)
4475 int old_player_nr = setup.network_player_nr;
4478 result = old_player_nr + 1;
4480 switch (event.cbutton.button)
4482 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4483 case SDL_CONTROLLER_BUTTON_Y:
4487 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4488 case SDL_CONTROLLER_BUTTON_B:
4492 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4493 case SDL_CONTROLLER_BUTTON_A:
4497 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4498 case SDL_CONTROLLER_BUTTON_X:
4509 case SDL_CONTROLLERBUTTONUP:
4510 HandleJoystickEvent(&event);
4511 ClearPlayerAction();
4515 HandleOtherEvents(&event);
4520 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4522 int joy = AnyJoystick();
4524 if (joy & JOY_BUTTON_1)
4526 else if (joy & JOY_BUTTON_2)
4529 else if (AnyJoystick())
4531 int joy = AnyJoystick();
4533 if (req_state & REQ_PLAYER)
4537 else if (joy & JOY_RIGHT)
4539 else if (joy & JOY_DOWN)
4541 else if (joy & JOY_LEFT)
4546 if (game_just_ended)
4548 if (global.use_envelope_request)
4550 // copy back current state of pressed buttons inside request area
4551 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4558 game.request_active = FALSE;
4563 static boolean RequestDoor(char *text, unsigned int req_state)
4565 unsigned int old_door_state;
4566 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4567 int font_nr = FONT_TEXT_2;
4572 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4574 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4575 font_nr = FONT_TEXT_1;
4578 if (game_status == GAME_MODE_PLAYING)
4579 BlitScreenToBitmap(backbuffer);
4581 // disable deactivated drawing when quick-loading level tape recording
4582 if (tape.playing && tape.deactivate_display)
4583 TapeDeactivateDisplayOff(TRUE);
4585 SetMouseCursor(CURSOR_DEFAULT);
4587 // pause network game while waiting for request to answer
4588 if (network.enabled &&
4589 game_status == GAME_MODE_PLAYING &&
4590 !game.all_players_gone &&
4591 req_state & REQUEST_WAIT_FOR_INPUT)
4592 SendToServer_PausePlaying();
4594 old_door_state = GetDoorState();
4596 // simulate releasing mouse button over last gadget, if still pressed
4598 HandleGadgets(-1, -1, 0);
4602 // draw released gadget before proceeding
4605 if (old_door_state & DOOR_OPEN_1)
4607 CloseDoor(DOOR_CLOSE_1);
4609 // save old door content
4610 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4611 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4614 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4615 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4617 // clear door drawing field
4618 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4620 // force DOOR font inside door area
4621 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4623 // write text for request
4624 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4626 char text_line[max_request_line_len + 1];
4632 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4634 tc = *(text_ptr + tx);
4635 // if (!tc || tc == ' ')
4636 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4640 if ((tc == '?' || tc == '!') && tl == 0)
4650 strncpy(text_line, text_ptr, tl);
4653 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4654 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4655 text_line, font_nr);
4657 text_ptr += tl + (tc == ' ' ? 1 : 0);
4658 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4663 if (req_state & REQ_ASK)
4665 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4666 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4667 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4668 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4670 else if (req_state & REQ_CONFIRM)
4672 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4673 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4675 else if (req_state & REQ_PLAYER)
4677 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4678 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4679 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4680 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4683 // copy request gadgets to door backbuffer
4684 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4686 OpenDoor(DOOR_OPEN_1);
4688 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4690 if (game_status == GAME_MODE_PLAYING)
4692 SetPanelBackground();
4693 SetDrawBackgroundMask(REDRAW_DOOR_1);
4697 SetDrawBackgroundMask(REDRAW_FIELD);
4703 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4705 // ---------- handle request buttons ----------
4706 result = RequestHandleEvents(req_state);
4710 if (!(req_state & REQ_STAY_OPEN))
4712 CloseDoor(DOOR_CLOSE_1);
4714 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4715 (req_state & REQ_REOPEN))
4716 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4721 if (game_status == GAME_MODE_PLAYING)
4723 SetPanelBackground();
4724 SetDrawBackgroundMask(REDRAW_DOOR_1);
4728 SetDrawBackgroundMask(REDRAW_FIELD);
4731 // continue network game after request
4732 if (network.enabled &&
4733 game_status == GAME_MODE_PLAYING &&
4734 !game.all_players_gone &&
4735 req_state & REQUEST_WAIT_FOR_INPUT)
4736 SendToServer_ContinuePlaying();
4738 // restore deactivated drawing when quick-loading level tape recording
4739 if (tape.playing && tape.deactivate_display)
4740 TapeDeactivateDisplayOn();
4745 static boolean RequestEnvelope(char *text, unsigned int req_state)
4749 if (game_status == GAME_MODE_PLAYING)
4750 BlitScreenToBitmap(backbuffer);
4752 // disable deactivated drawing when quick-loading level tape recording
4753 if (tape.playing && tape.deactivate_display)
4754 TapeDeactivateDisplayOff(TRUE);
4756 SetMouseCursor(CURSOR_DEFAULT);
4758 // pause network game while waiting for request to answer
4759 if (network.enabled &&
4760 game_status == GAME_MODE_PLAYING &&
4761 !game.all_players_gone &&
4762 req_state & REQUEST_WAIT_FOR_INPUT)
4763 SendToServer_PausePlaying();
4765 // simulate releasing mouse button over last gadget, if still pressed
4767 HandleGadgets(-1, -1, 0);
4771 // (replace with setting corresponding request background)
4772 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4773 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4775 // clear door drawing field
4776 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4778 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4780 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4782 if (game_status == GAME_MODE_PLAYING)
4784 SetPanelBackground();
4785 SetDrawBackgroundMask(REDRAW_DOOR_1);
4789 SetDrawBackgroundMask(REDRAW_FIELD);
4795 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4797 // ---------- handle request buttons ----------
4798 result = RequestHandleEvents(req_state);
4802 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4806 if (game_status == GAME_MODE_PLAYING)
4808 SetPanelBackground();
4809 SetDrawBackgroundMask(REDRAW_DOOR_1);
4813 SetDrawBackgroundMask(REDRAW_FIELD);
4816 // continue network game after request
4817 if (network.enabled &&
4818 game_status == GAME_MODE_PLAYING &&
4819 !game.all_players_gone &&
4820 req_state & REQUEST_WAIT_FOR_INPUT)
4821 SendToServer_ContinuePlaying();
4823 // restore deactivated drawing when quick-loading level tape recording
4824 if (tape.playing && tape.deactivate_display)
4825 TapeDeactivateDisplayOn();
4830 boolean Request(char *text, unsigned int req_state)
4832 boolean overlay_enabled = GetOverlayEnabled();
4835 SetOverlayEnabled(FALSE);
4837 if (global.use_envelope_request)
4838 result = RequestEnvelope(text, req_state);
4840 result = RequestDoor(text, req_state);
4842 SetOverlayEnabled(overlay_enabled);
4847 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4849 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4850 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4853 if (dpo1->sort_priority != dpo2->sort_priority)
4854 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4856 compare_result = dpo1->nr - dpo2->nr;
4858 return compare_result;
4861 void InitGraphicCompatibilityInfo_Doors(void)
4867 struct DoorInfo *door;
4871 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4872 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4874 { -1, -1, -1, NULL }
4876 struct Rect door_rect_list[] =
4878 { DX, DY, DXSIZE, DYSIZE },
4879 { VX, VY, VXSIZE, VYSIZE }
4883 for (i = 0; doors[i].door_token != -1; i++)
4885 int door_token = doors[i].door_token;
4886 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4887 int part_1 = doors[i].part_1;
4888 int part_8 = doors[i].part_8;
4889 int part_2 = part_1 + 1;
4890 int part_3 = part_1 + 2;
4891 struct DoorInfo *door = doors[i].door;
4892 struct Rect *door_rect = &door_rect_list[door_index];
4893 boolean door_gfx_redefined = FALSE;
4895 // check if any door part graphic definitions have been redefined
4897 for (j = 0; door_part_controls[j].door_token != -1; j++)
4899 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4900 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4902 if (dpc->door_token == door_token && fi->redefined)
4903 door_gfx_redefined = TRUE;
4906 // check for old-style door graphic/animation modifications
4908 if (!door_gfx_redefined)
4910 if (door->anim_mode & ANIM_STATIC_PANEL)
4912 door->panel.step_xoffset = 0;
4913 door->panel.step_yoffset = 0;
4916 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4918 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4919 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4920 int num_door_steps, num_panel_steps;
4922 // remove door part graphics other than the two default wings
4924 for (j = 0; door_part_controls[j].door_token != -1; j++)
4926 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4927 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4929 if (dpc->graphic >= part_3 &&
4930 dpc->graphic <= part_8)
4934 // set graphics and screen positions of the default wings
4936 g_part_1->width = door_rect->width;
4937 g_part_1->height = door_rect->height;
4938 g_part_2->width = door_rect->width;
4939 g_part_2->height = door_rect->height;
4940 g_part_2->src_x = door_rect->width;
4941 g_part_2->src_y = g_part_1->src_y;
4943 door->part_2.x = door->part_1.x;
4944 door->part_2.y = door->part_1.y;
4946 if (door->width != -1)
4948 g_part_1->width = door->width;
4949 g_part_2->width = door->width;
4951 // special treatment for graphics and screen position of right wing
4952 g_part_2->src_x += door_rect->width - door->width;
4953 door->part_2.x += door_rect->width - door->width;
4956 if (door->height != -1)
4958 g_part_1->height = door->height;
4959 g_part_2->height = door->height;
4961 // special treatment for graphics and screen position of bottom wing
4962 g_part_2->src_y += door_rect->height - door->height;
4963 door->part_2.y += door_rect->height - door->height;
4966 // set animation delays for the default wings and panels
4968 door->part_1.step_delay = door->step_delay;
4969 door->part_2.step_delay = door->step_delay;
4970 door->panel.step_delay = door->step_delay;
4972 // set animation draw order for the default wings
4974 door->part_1.sort_priority = 2; // draw left wing over ...
4975 door->part_2.sort_priority = 1; // ... right wing
4977 // set animation draw offset for the default wings
4979 if (door->anim_mode & ANIM_HORIZONTAL)
4981 door->part_1.step_xoffset = door->step_offset;
4982 door->part_1.step_yoffset = 0;
4983 door->part_2.step_xoffset = door->step_offset * -1;
4984 door->part_2.step_yoffset = 0;
4986 num_door_steps = g_part_1->width / door->step_offset;
4988 else // ANIM_VERTICAL
4990 door->part_1.step_xoffset = 0;
4991 door->part_1.step_yoffset = door->step_offset;
4992 door->part_2.step_xoffset = 0;
4993 door->part_2.step_yoffset = door->step_offset * -1;
4995 num_door_steps = g_part_1->height / door->step_offset;
4998 // set animation draw offset for the default panels
5000 if (door->step_offset > 1)
5002 num_panel_steps = 2 * door_rect->height / door->step_offset;
5003 door->panel.start_step = num_panel_steps - num_door_steps;
5004 door->panel.start_step_closing = door->panel.start_step;
5008 num_panel_steps = door_rect->height / door->step_offset;
5009 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5010 door->panel.start_step_closing = door->panel.start_step;
5011 door->panel.step_delay *= 2;
5018 void InitDoors(void)
5022 for (i = 0; door_part_controls[i].door_token != -1; i++)
5024 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5025 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5027 // initialize "start_step_opening" and "start_step_closing", if needed
5028 if (dpc->pos->start_step_opening == 0 &&
5029 dpc->pos->start_step_closing == 0)
5031 // dpc->pos->start_step_opening = dpc->pos->start_step;
5032 dpc->pos->start_step_closing = dpc->pos->start_step;
5035 // fill structure for door part draw order (sorted below)
5037 dpo->sort_priority = dpc->pos->sort_priority;
5040 // sort door part controls according to sort_priority and graphic number
5041 qsort(door_part_order, MAX_DOOR_PARTS,
5042 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5045 unsigned int OpenDoor(unsigned int door_state)
5047 if (door_state & DOOR_COPY_BACK)
5049 if (door_state & DOOR_OPEN_1)
5050 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5051 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5053 if (door_state & DOOR_OPEN_2)
5054 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5055 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5057 door_state &= ~DOOR_COPY_BACK;
5060 return MoveDoor(door_state);
5063 unsigned int CloseDoor(unsigned int door_state)
5065 unsigned int old_door_state = GetDoorState();
5067 if (!(door_state & DOOR_NO_COPY_BACK))
5069 if (old_door_state & DOOR_OPEN_1)
5070 BlitBitmap(backbuffer, bitmap_db_door_1,
5071 DX, DY, DXSIZE, DYSIZE, 0, 0);
5073 if (old_door_state & DOOR_OPEN_2)
5074 BlitBitmap(backbuffer, bitmap_db_door_2,
5075 VX, VY, VXSIZE, VYSIZE, 0, 0);
5077 door_state &= ~DOOR_NO_COPY_BACK;
5080 return MoveDoor(door_state);
5083 unsigned int GetDoorState(void)
5085 return MoveDoor(DOOR_GET_STATE);
5088 unsigned int SetDoorState(unsigned int door_state)
5090 return MoveDoor(door_state | DOOR_SET_STATE);
5093 static int euclid(int a, int b)
5095 return (b ? euclid(b, a % b) : a);
5098 unsigned int MoveDoor(unsigned int door_state)
5100 struct Rect door_rect_list[] =
5102 { DX, DY, DXSIZE, DYSIZE },
5103 { VX, VY, VXSIZE, VYSIZE }
5105 static int door1 = DOOR_CLOSE_1;
5106 static int door2 = DOOR_CLOSE_2;
5107 unsigned int door_delay = 0;
5108 unsigned int door_delay_value;
5111 if (door_state == DOOR_GET_STATE)
5112 return (door1 | door2);
5114 if (door_state & DOOR_SET_STATE)
5116 if (door_state & DOOR_ACTION_1)
5117 door1 = door_state & DOOR_ACTION_1;
5118 if (door_state & DOOR_ACTION_2)
5119 door2 = door_state & DOOR_ACTION_2;
5121 return (door1 | door2);
5124 if (!(door_state & DOOR_FORCE_REDRAW))
5126 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5127 door_state &= ~DOOR_OPEN_1;
5128 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5129 door_state &= ~DOOR_CLOSE_1;
5130 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5131 door_state &= ~DOOR_OPEN_2;
5132 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5133 door_state &= ~DOOR_CLOSE_2;
5136 if (global.autoplay_leveldir)
5138 door_state |= DOOR_NO_DELAY;
5139 door_state &= ~DOOR_CLOSE_ALL;
5142 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5143 door_state |= DOOR_NO_DELAY;
5145 if (door_state & DOOR_ACTION)
5147 boolean door_panel_drawn[NUM_DOORS];
5148 boolean panel_has_doors[NUM_DOORS];
5149 boolean door_part_skip[MAX_DOOR_PARTS];
5150 boolean door_part_done[MAX_DOOR_PARTS];
5151 boolean door_part_done_all;
5152 int num_steps[MAX_DOOR_PARTS];
5153 int max_move_delay = 0; // delay for complete animations of all doors
5154 int max_step_delay = 0; // delay (ms) between two animation frames
5155 int num_move_steps = 0; // number of animation steps for all doors
5156 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5157 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5158 int current_move_delay = 0;
5162 for (i = 0; i < NUM_DOORS; i++)
5163 panel_has_doors[i] = FALSE;
5165 for (i = 0; i < MAX_DOOR_PARTS; i++)
5167 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5168 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5169 int door_token = dpc->door_token;
5171 door_part_done[i] = FALSE;
5172 door_part_skip[i] = (!(door_state & door_token) ||
5176 for (i = 0; i < MAX_DOOR_PARTS; i++)
5178 int nr = door_part_order[i].nr;
5179 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5180 struct DoorPartPosInfo *pos = dpc->pos;
5181 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5182 int door_token = dpc->door_token;
5183 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5184 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5185 int step_xoffset = ABS(pos->step_xoffset);
5186 int step_yoffset = ABS(pos->step_yoffset);
5187 int step_delay = pos->step_delay;
5188 int current_door_state = door_state & door_token;
5189 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5190 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5191 boolean part_opening = (is_panel ? door_closing : door_opening);
5192 int start_step = (part_opening ? pos->start_step_opening :
5193 pos->start_step_closing);
5194 float move_xsize = (step_xoffset ? g->width : 0);
5195 float move_ysize = (step_yoffset ? g->height : 0);
5196 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5197 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5198 int move_steps = (move_xsteps && move_ysteps ?
5199 MIN(move_xsteps, move_ysteps) :
5200 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5201 int move_delay = move_steps * step_delay;
5203 if (door_part_skip[nr])
5206 max_move_delay = MAX(max_move_delay, move_delay);
5207 max_step_delay = (max_step_delay == 0 ? step_delay :
5208 euclid(max_step_delay, step_delay));
5209 num_steps[nr] = move_steps;
5213 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5215 panel_has_doors[door_index] = TRUE;
5219 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5221 num_move_steps = max_move_delay / max_step_delay;
5222 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5224 door_delay_value = max_step_delay;
5226 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5228 start = num_move_steps - 1;
5232 // opening door sound has priority over simultaneously closing door
5233 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5235 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5237 if (door_state & DOOR_OPEN_1)
5238 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5239 if (door_state & DOOR_OPEN_2)
5240 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5242 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5244 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5246 if (door_state & DOOR_CLOSE_1)
5247 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5248 if (door_state & DOOR_CLOSE_2)
5249 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5253 for (k = start; k < num_move_steps; k++)
5255 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5257 door_part_done_all = TRUE;
5259 for (i = 0; i < NUM_DOORS; i++)
5260 door_panel_drawn[i] = FALSE;
5262 for (i = 0; i < MAX_DOOR_PARTS; i++)
5264 int nr = door_part_order[i].nr;
5265 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5266 struct DoorPartPosInfo *pos = dpc->pos;
5267 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5268 int door_token = dpc->door_token;
5269 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5270 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5271 boolean is_panel_and_door_has_closed = FALSE;
5272 struct Rect *door_rect = &door_rect_list[door_index];
5273 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5275 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5276 int current_door_state = door_state & door_token;
5277 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5278 boolean door_closing = !door_opening;
5279 boolean part_opening = (is_panel ? door_closing : door_opening);
5280 boolean part_closing = !part_opening;
5281 int start_step = (part_opening ? pos->start_step_opening :
5282 pos->start_step_closing);
5283 int step_delay = pos->step_delay;
5284 int step_factor = step_delay / max_step_delay;
5285 int k1 = (step_factor ? k / step_factor + 1 : k);
5286 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5287 int kk = MAX(0, k2);
5290 int src_x, src_y, src_xx, src_yy;
5291 int dst_x, dst_y, dst_xx, dst_yy;
5294 if (door_part_skip[nr])
5297 if (!(door_state & door_token))
5305 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5306 int kk_door = MAX(0, k2_door);
5307 int sync_frame = kk_door * door_delay_value;
5308 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5310 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5311 &g_src_x, &g_src_y);
5316 if (!door_panel_drawn[door_index])
5318 ClearRectangle(drawto, door_rect->x, door_rect->y,
5319 door_rect->width, door_rect->height);
5321 door_panel_drawn[door_index] = TRUE;
5324 // draw opening or closing door parts
5326 if (pos->step_xoffset < 0) // door part on right side
5329 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5332 if (dst_xx + width > door_rect->width)
5333 width = door_rect->width - dst_xx;
5335 else // door part on left side
5338 dst_xx = pos->x - kk * pos->step_xoffset;
5342 src_xx = ABS(dst_xx);
5346 width = g->width - src_xx;
5348 if (width > door_rect->width)
5349 width = door_rect->width;
5351 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5354 if (pos->step_yoffset < 0) // door part on bottom side
5357 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5360 if (dst_yy + height > door_rect->height)
5361 height = door_rect->height - dst_yy;
5363 else // door part on top side
5366 dst_yy = pos->y - kk * pos->step_yoffset;
5370 src_yy = ABS(dst_yy);
5374 height = g->height - src_yy;
5377 src_x = g_src_x + src_xx;
5378 src_y = g_src_y + src_yy;
5380 dst_x = door_rect->x + dst_xx;
5381 dst_y = door_rect->y + dst_yy;
5383 is_panel_and_door_has_closed =
5386 panel_has_doors[door_index] &&
5387 k >= num_move_steps_doors_only - 1);
5389 if (width >= 0 && width <= g->width &&
5390 height >= 0 && height <= g->height &&
5391 !is_panel_and_door_has_closed)
5393 if (is_panel || !pos->draw_masked)
5394 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5397 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5401 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5403 if ((part_opening && (width < 0 || height < 0)) ||
5404 (part_closing && (width >= g->width && height >= g->height)))
5405 door_part_done[nr] = TRUE;
5407 // continue door part animations, but not panel after door has closed
5408 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5409 door_part_done_all = FALSE;
5412 if (!(door_state & DOOR_NO_DELAY))
5416 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5418 current_move_delay += max_step_delay;
5420 // prevent OS (Windows) from complaining about program not responding
5424 if (door_part_done_all)
5428 if (!(door_state & DOOR_NO_DELAY))
5430 // wait for specified door action post delay
5431 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5432 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5433 else if (door_state & DOOR_ACTION_1)
5434 door_delay_value = door_1.post_delay;
5435 else if (door_state & DOOR_ACTION_2)
5436 door_delay_value = door_2.post_delay;
5438 while (!DelayReached(&door_delay, door_delay_value))
5443 if (door_state & DOOR_ACTION_1)
5444 door1 = door_state & DOOR_ACTION_1;
5445 if (door_state & DOOR_ACTION_2)
5446 door2 = door_state & DOOR_ACTION_2;
5448 // draw masked border over door area
5449 DrawMaskedBorder(REDRAW_DOOR_1);
5450 DrawMaskedBorder(REDRAW_DOOR_2);
5452 ClearAutoRepeatKeyEvents();
5454 return (door1 | door2);
5457 static boolean useSpecialEditorDoor(void)
5459 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5460 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5462 // do not draw special editor door if editor border defined or redefined
5463 if (graphic_info[graphic].bitmap != NULL || redefined)
5466 // do not draw special editor door if global border defined to be empty
5467 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5470 // do not draw special editor door if viewport definitions do not match
5474 EY + EYSIZE != VY + VYSIZE)
5480 void DrawSpecialEditorDoor(void)
5482 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5483 int top_border_width = gfx1->width;
5484 int top_border_height = gfx1->height;
5485 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5486 int ex = EX - outer_border;
5487 int ey = EY - outer_border;
5488 int vy = VY - outer_border;
5489 int exsize = EXSIZE + 2 * outer_border;
5491 if (!useSpecialEditorDoor())
5494 // draw bigger level editor toolbox window
5495 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5496 top_border_width, top_border_height, ex, ey - top_border_height);
5497 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5498 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5500 redraw_mask |= REDRAW_ALL;
5503 void UndrawSpecialEditorDoor(void)
5505 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5506 int top_border_width = gfx1->width;
5507 int top_border_height = gfx1->height;
5508 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5509 int ex = EX - outer_border;
5510 int ey = EY - outer_border;
5511 int ey_top = ey - top_border_height;
5512 int exsize = EXSIZE + 2 * outer_border;
5513 int eysize = EYSIZE + 2 * outer_border;
5515 if (!useSpecialEditorDoor())
5518 // draw normal tape recorder window
5519 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5521 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5522 ex, ey_top, top_border_width, top_border_height,
5524 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5525 ex, ey, exsize, eysize, ex, ey);
5529 // if screen background is set to "[NONE]", clear editor toolbox window
5530 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5531 ClearRectangle(drawto, ex, ey, exsize, eysize);
5534 redraw_mask |= REDRAW_ALL;
5538 // ---------- new tool button stuff -------------------------------------------
5543 struct TextPosInfo *pos;
5545 boolean is_touch_button;
5547 } toolbutton_info[NUM_TOOL_BUTTONS] =
5550 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5551 TOOL_CTRL_ID_YES, FALSE, "yes"
5554 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5555 TOOL_CTRL_ID_NO, FALSE, "no"
5558 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5559 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5562 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5563 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5566 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5567 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5570 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5571 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5574 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5575 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5578 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5579 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5582 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5583 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5586 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5587 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5591 void CreateToolButtons(void)
5595 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5597 int graphic = toolbutton_info[i].graphic;
5598 struct GraphicInfo *gfx = &graphic_info[graphic];
5599 struct TextPosInfo *pos = toolbutton_info[i].pos;
5600 struct GadgetInfo *gi;
5601 Bitmap *deco_bitmap = None;
5602 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5603 unsigned int event_mask = GD_EVENT_RELEASED;
5604 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5605 int base_x = (is_touch_button ? 0 : DX);
5606 int base_y = (is_touch_button ? 0 : DY);
5607 int gd_x = gfx->src_x;
5608 int gd_y = gfx->src_y;
5609 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5610 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5615 if (global.use_envelope_request && !is_touch_button)
5617 setRequestPosition(&base_x, &base_y, TRUE);
5619 // check if request buttons are outside of envelope and fix, if needed
5620 if (x < 0 || x + gfx->width > request.width ||
5621 y < 0 || y + gfx->height > request.height)
5623 if (id == TOOL_CTRL_ID_YES)
5626 y = request.height - 2 * request.border_size - gfx->height;
5628 else if (id == TOOL_CTRL_ID_NO)
5630 x = request.width - 2 * request.border_size - gfx->width;
5631 y = request.height - 2 * request.border_size - gfx->height;
5633 else if (id == TOOL_CTRL_ID_CONFIRM)
5635 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5636 y = request.height - 2 * request.border_size - gfx->height;
5638 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5640 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5642 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5643 y = request.height - 2 * request.border_size - gfx->height * 2;
5645 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5646 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5651 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5653 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5655 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5656 pos->size, &deco_bitmap, &deco_x, &deco_y);
5657 deco_xpos = (gfx->width - pos->size) / 2;
5658 deco_ypos = (gfx->height - pos->size) / 2;
5661 gi = CreateGadget(GDI_CUSTOM_ID, id,
5662 GDI_IMAGE_ID, graphic,
5663 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5666 GDI_WIDTH, gfx->width,
5667 GDI_HEIGHT, gfx->height,
5668 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5669 GDI_STATE, GD_BUTTON_UNPRESSED,
5670 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5671 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5672 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5673 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5674 GDI_DECORATION_SIZE, pos->size, pos->size,
5675 GDI_DECORATION_SHIFTING, 1, 1,
5676 GDI_DIRECT_DRAW, FALSE,
5677 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5678 GDI_EVENT_MASK, event_mask,
5679 GDI_CALLBACK_ACTION, HandleToolButtons,
5683 Fail("cannot create gadget");
5685 tool_gadget[id] = gi;
5689 void FreeToolButtons(void)
5693 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5694 FreeGadget(tool_gadget[i]);
5697 static void UnmapToolButtons(void)
5701 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5702 UnmapGadget(tool_gadget[i]);
5705 static void HandleToolButtons(struct GadgetInfo *gi)
5707 request_gadget_id = gi->custom_id;
5710 static struct Mapping_EM_to_RND_object
5713 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5714 boolean is_backside; // backside of moving element
5720 em_object_mapping_list[GAME_TILE_MAX + 1] =
5723 Zborder, FALSE, FALSE,
5727 Zplayer, FALSE, FALSE,
5736 Ztank, FALSE, FALSE,
5740 Zeater, FALSE, FALSE,
5744 Zdynamite, FALSE, FALSE,
5748 Zboom, FALSE, FALSE,
5753 Xchain, FALSE, FALSE,
5754 EL_DEFAULT, ACTION_EXPLODING, -1
5757 Xboom_bug, FALSE, FALSE,
5758 EL_BUG, ACTION_EXPLODING, -1
5761 Xboom_tank, FALSE, FALSE,
5762 EL_SPACESHIP, ACTION_EXPLODING, -1
5765 Xboom_android, FALSE, FALSE,
5766 EL_EMC_ANDROID, ACTION_OTHER, -1
5769 Xboom_1, FALSE, FALSE,
5770 EL_DEFAULT, ACTION_EXPLODING, -1
5773 Xboom_2, FALSE, FALSE,
5774 EL_DEFAULT, ACTION_EXPLODING, -1
5778 Xblank, TRUE, FALSE,
5783 Xsplash_e, FALSE, FALSE,
5784 EL_ACID_SPLASH_RIGHT, -1, -1
5787 Xsplash_w, FALSE, FALSE,
5788 EL_ACID_SPLASH_LEFT, -1, -1
5792 Xplant, TRUE, FALSE,
5793 EL_EMC_PLANT, -1, -1
5796 Yplant, FALSE, FALSE,
5797 EL_EMC_PLANT, -1, -1
5801 Xacid_1, TRUE, FALSE,
5805 Xacid_2, FALSE, FALSE,
5809 Xacid_3, FALSE, FALSE,
5813 Xacid_4, FALSE, FALSE,
5817 Xacid_5, FALSE, FALSE,
5821 Xacid_6, FALSE, FALSE,
5825 Xacid_7, FALSE, FALSE,
5829 Xacid_8, FALSE, FALSE,
5834 Xfake_acid_1, TRUE, FALSE,
5835 EL_EMC_FAKE_ACID, -1, -1
5838 Xfake_acid_2, FALSE, FALSE,
5839 EL_EMC_FAKE_ACID, -1, -1
5842 Xfake_acid_3, FALSE, FALSE,
5843 EL_EMC_FAKE_ACID, -1, -1
5846 Xfake_acid_4, FALSE, FALSE,
5847 EL_EMC_FAKE_ACID, -1, -1
5850 Xfake_acid_5, FALSE, FALSE,
5851 EL_EMC_FAKE_ACID, -1, -1
5854 Xfake_acid_6, FALSE, FALSE,
5855 EL_EMC_FAKE_ACID, -1, -1
5858 Xfake_acid_7, FALSE, FALSE,
5859 EL_EMC_FAKE_ACID, -1, -1
5862 Xfake_acid_8, FALSE, FALSE,
5863 EL_EMC_FAKE_ACID, -1, -1
5867 Xgrass, TRUE, FALSE,
5868 EL_EMC_GRASS, -1, -1
5871 Ygrass_nB, FALSE, FALSE,
5872 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5875 Ygrass_eB, FALSE, FALSE,
5876 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5879 Ygrass_sB, FALSE, FALSE,
5880 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5883 Ygrass_wB, FALSE, FALSE,
5884 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5892 Ydirt_nB, FALSE, FALSE,
5893 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5896 Ydirt_eB, FALSE, FALSE,
5897 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5900 Ydirt_sB, FALSE, FALSE,
5901 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5904 Ydirt_wB, FALSE, FALSE,
5905 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5909 Xandroid, TRUE, FALSE,
5910 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5913 Xandroid_1_n, FALSE, FALSE,
5914 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5917 Xandroid_2_n, FALSE, FALSE,
5918 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5921 Xandroid_1_e, FALSE, FALSE,
5922 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5925 Xandroid_2_e, FALSE, FALSE,
5926 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5929 Xandroid_1_w, FALSE, FALSE,
5930 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5933 Xandroid_2_w, FALSE, FALSE,
5934 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5937 Xandroid_1_s, FALSE, FALSE,
5938 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5941 Xandroid_2_s, FALSE, FALSE,
5942 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5945 Yandroid_n, FALSE, FALSE,
5946 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5949 Yandroid_nB, FALSE, TRUE,
5950 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5953 Yandroid_ne, FALSE, FALSE,
5954 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5957 Yandroid_neB, FALSE, TRUE,
5958 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5961 Yandroid_e, FALSE, FALSE,
5962 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5965 Yandroid_eB, FALSE, TRUE,
5966 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5969 Yandroid_se, FALSE, FALSE,
5970 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5973 Yandroid_seB, FALSE, TRUE,
5974 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5977 Yandroid_s, FALSE, FALSE,
5978 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5981 Yandroid_sB, FALSE, TRUE,
5982 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5985 Yandroid_sw, FALSE, FALSE,
5986 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5989 Yandroid_swB, FALSE, TRUE,
5990 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5993 Yandroid_w, FALSE, FALSE,
5994 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5997 Yandroid_wB, FALSE, TRUE,
5998 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6001 Yandroid_nw, FALSE, FALSE,
6002 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6005 Yandroid_nwB, FALSE, TRUE,
6006 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6010 Xeater_n, TRUE, FALSE,
6011 EL_YAMYAM_UP, -1, -1
6014 Xeater_e, TRUE, FALSE,
6015 EL_YAMYAM_RIGHT, -1, -1
6018 Xeater_w, TRUE, FALSE,
6019 EL_YAMYAM_LEFT, -1, -1
6022 Xeater_s, TRUE, FALSE,
6023 EL_YAMYAM_DOWN, -1, -1
6026 Yeater_n, FALSE, FALSE,
6027 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6030 Yeater_nB, FALSE, TRUE,
6031 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6034 Yeater_e, FALSE, FALSE,
6035 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6038 Yeater_eB, FALSE, TRUE,
6039 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6042 Yeater_s, FALSE, FALSE,
6043 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6046 Yeater_sB, FALSE, TRUE,
6047 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6050 Yeater_w, FALSE, FALSE,
6051 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6054 Yeater_wB, FALSE, TRUE,
6055 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6058 Yeater_stone, FALSE, FALSE,
6059 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6062 Yeater_spring, FALSE, FALSE,
6063 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6067 Xalien, TRUE, FALSE,
6071 Xalien_pause, FALSE, FALSE,
6075 Yalien_n, FALSE, FALSE,
6076 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6079 Yalien_nB, FALSE, TRUE,
6080 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6083 Yalien_e, FALSE, FALSE,
6084 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6087 Yalien_eB, FALSE, TRUE,
6088 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6091 Yalien_s, FALSE, FALSE,
6092 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6095 Yalien_sB, FALSE, TRUE,
6096 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6099 Yalien_w, FALSE, FALSE,
6100 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6103 Yalien_wB, FALSE, TRUE,
6104 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6107 Yalien_stone, FALSE, FALSE,
6108 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6111 Yalien_spring, FALSE, FALSE,
6112 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6116 Xbug_1_n, TRUE, FALSE,
6120 Xbug_1_e, TRUE, FALSE,
6121 EL_BUG_RIGHT, -1, -1
6124 Xbug_1_s, TRUE, FALSE,
6128 Xbug_1_w, TRUE, FALSE,
6132 Xbug_2_n, FALSE, FALSE,
6136 Xbug_2_e, FALSE, FALSE,
6137 EL_BUG_RIGHT, -1, -1
6140 Xbug_2_s, FALSE, FALSE,
6144 Xbug_2_w, FALSE, FALSE,
6148 Ybug_n, FALSE, FALSE,
6149 EL_BUG, ACTION_MOVING, MV_BIT_UP
6152 Ybug_nB, FALSE, TRUE,
6153 EL_BUG, ACTION_MOVING, MV_BIT_UP
6156 Ybug_e, FALSE, FALSE,
6157 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6160 Ybug_eB, FALSE, TRUE,
6161 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6164 Ybug_s, FALSE, FALSE,
6165 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6168 Ybug_sB, FALSE, TRUE,
6169 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6172 Ybug_w, FALSE, FALSE,
6173 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6176 Ybug_wB, FALSE, TRUE,
6177 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6180 Ybug_w_n, FALSE, FALSE,
6181 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6184 Ybug_n_e, FALSE, FALSE,
6185 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6188 Ybug_e_s, FALSE, FALSE,
6189 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6192 Ybug_s_w, FALSE, FALSE,
6193 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6196 Ybug_e_n, FALSE, FALSE,
6197 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6200 Ybug_s_e, FALSE, FALSE,
6201 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6204 Ybug_w_s, FALSE, FALSE,
6205 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6208 Ybug_n_w, FALSE, FALSE,
6209 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6212 Ybug_stone, FALSE, FALSE,
6213 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6216 Ybug_spring, FALSE, FALSE,
6217 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6221 Xtank_1_n, TRUE, FALSE,
6222 EL_SPACESHIP_UP, -1, -1
6225 Xtank_1_e, TRUE, FALSE,
6226 EL_SPACESHIP_RIGHT, -1, -1
6229 Xtank_1_s, TRUE, FALSE,
6230 EL_SPACESHIP_DOWN, -1, -1
6233 Xtank_1_w, TRUE, FALSE,
6234 EL_SPACESHIP_LEFT, -1, -1
6237 Xtank_2_n, FALSE, FALSE,
6238 EL_SPACESHIP_UP, -1, -1
6241 Xtank_2_e, FALSE, FALSE,
6242 EL_SPACESHIP_RIGHT, -1, -1
6245 Xtank_2_s, FALSE, FALSE,
6246 EL_SPACESHIP_DOWN, -1, -1
6249 Xtank_2_w, FALSE, FALSE,
6250 EL_SPACESHIP_LEFT, -1, -1
6253 Ytank_n, FALSE, FALSE,
6254 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6257 Ytank_nB, FALSE, TRUE,
6258 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6261 Ytank_e, FALSE, FALSE,
6262 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6265 Ytank_eB, FALSE, TRUE,
6266 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6269 Ytank_s, FALSE, FALSE,
6270 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6273 Ytank_sB, FALSE, TRUE,
6274 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6277 Ytank_w, FALSE, FALSE,
6278 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6281 Ytank_wB, FALSE, TRUE,
6282 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6285 Ytank_w_n, FALSE, FALSE,
6286 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6289 Ytank_n_e, FALSE, FALSE,
6290 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6293 Ytank_e_s, FALSE, FALSE,
6294 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6297 Ytank_s_w, FALSE, FALSE,
6298 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6301 Ytank_e_n, FALSE, FALSE,
6302 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6305 Ytank_s_e, FALSE, FALSE,
6306 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6309 Ytank_w_s, FALSE, FALSE,
6310 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6313 Ytank_n_w, FALSE, FALSE,
6314 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6317 Ytank_stone, FALSE, FALSE,
6318 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6321 Ytank_spring, FALSE, FALSE,
6322 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6326 Xemerald, TRUE, FALSE,
6330 Xemerald_pause, FALSE, FALSE,
6334 Xemerald_fall, FALSE, FALSE,
6338 Xemerald_shine, FALSE, FALSE,
6339 EL_EMERALD, ACTION_TWINKLING, -1
6342 Yemerald_s, FALSE, FALSE,
6343 EL_EMERALD, ACTION_FALLING, -1
6346 Yemerald_sB, FALSE, TRUE,
6347 EL_EMERALD, ACTION_FALLING, -1
6350 Yemerald_e, FALSE, FALSE,
6351 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6354 Yemerald_eB, FALSE, TRUE,
6355 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6358 Yemerald_w, FALSE, FALSE,
6359 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6362 Yemerald_wB, FALSE, TRUE,
6363 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6366 Yemerald_blank, FALSE, FALSE,
6367 EL_EMERALD, ACTION_COLLECTING, -1
6371 Xdiamond, TRUE, FALSE,
6375 Xdiamond_pause, FALSE, FALSE,
6379 Xdiamond_fall, FALSE, FALSE,
6383 Xdiamond_shine, FALSE, FALSE,
6384 EL_DIAMOND, ACTION_TWINKLING, -1
6387 Ydiamond_s, FALSE, FALSE,
6388 EL_DIAMOND, ACTION_FALLING, -1
6391 Ydiamond_sB, FALSE, TRUE,
6392 EL_DIAMOND, ACTION_FALLING, -1
6395 Ydiamond_e, FALSE, FALSE,
6396 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6399 Ydiamond_eB, FALSE, TRUE,
6400 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6403 Ydiamond_w, FALSE, FALSE,
6404 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6407 Ydiamond_wB, FALSE, TRUE,
6408 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6411 Ydiamond_blank, FALSE, FALSE,
6412 EL_DIAMOND, ACTION_COLLECTING, -1
6415 Ydiamond_stone, FALSE, FALSE,
6416 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6420 Xstone, TRUE, FALSE,
6424 Xstone_pause, FALSE, FALSE,
6428 Xstone_fall, FALSE, FALSE,
6432 Ystone_s, FALSE, FALSE,
6433 EL_ROCK, ACTION_FALLING, -1
6436 Ystone_sB, FALSE, TRUE,
6437 EL_ROCK, ACTION_FALLING, -1
6440 Ystone_e, FALSE, FALSE,
6441 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6444 Ystone_eB, FALSE, TRUE,
6445 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6448 Ystone_w, FALSE, FALSE,
6449 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6452 Ystone_wB, FALSE, TRUE,
6453 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6461 Xbomb_pause, FALSE, FALSE,
6465 Xbomb_fall, FALSE, FALSE,
6469 Ybomb_s, FALSE, FALSE,
6470 EL_BOMB, ACTION_FALLING, -1
6473 Ybomb_sB, FALSE, TRUE,
6474 EL_BOMB, ACTION_FALLING, -1
6477 Ybomb_e, FALSE, FALSE,
6478 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6481 Ybomb_eB, FALSE, TRUE,
6482 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6485 Ybomb_w, FALSE, FALSE,
6486 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6489 Ybomb_wB, FALSE, TRUE,
6490 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6493 Ybomb_blank, FALSE, FALSE,
6494 EL_BOMB, ACTION_ACTIVATING, -1
6502 Xnut_pause, FALSE, FALSE,
6506 Xnut_fall, FALSE, FALSE,
6510 Ynut_s, FALSE, FALSE,
6511 EL_NUT, ACTION_FALLING, -1
6514 Ynut_sB, FALSE, TRUE,
6515 EL_NUT, ACTION_FALLING, -1
6518 Ynut_e, FALSE, FALSE,
6519 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6522 Ynut_eB, FALSE, TRUE,
6523 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6526 Ynut_w, FALSE, FALSE,
6527 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6530 Ynut_wB, FALSE, TRUE,
6531 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6534 Ynut_stone, FALSE, FALSE,
6535 EL_NUT, ACTION_BREAKING, -1
6539 Xspring, TRUE, FALSE,
6543 Xspring_pause, FALSE, FALSE,
6547 Xspring_e, TRUE, FALSE,
6548 EL_SPRING_RIGHT, -1, -1
6551 Xspring_w, TRUE, FALSE,
6552 EL_SPRING_LEFT, -1, -1
6555 Xspring_fall, FALSE, FALSE,
6559 Yspring_s, FALSE, FALSE,
6560 EL_SPRING, ACTION_FALLING, -1
6563 Yspring_sB, FALSE, TRUE,
6564 EL_SPRING, ACTION_FALLING, -1
6567 Yspring_e, FALSE, FALSE,
6568 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6571 Yspring_eB, FALSE, TRUE,
6572 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6575 Yspring_w, FALSE, FALSE,
6576 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6579 Yspring_wB, FALSE, TRUE,
6580 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6583 Yspring_alien_e, FALSE, FALSE,
6584 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6587 Yspring_alien_eB, FALSE, TRUE,
6588 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6591 Yspring_alien_w, FALSE, FALSE,
6592 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6595 Yspring_alien_wB, FALSE, TRUE,
6596 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6600 Xpush_emerald_e, FALSE, FALSE,
6601 EL_EMERALD, -1, MV_BIT_RIGHT
6604 Xpush_emerald_w, FALSE, FALSE,
6605 EL_EMERALD, -1, MV_BIT_LEFT
6608 Xpush_diamond_e, FALSE, FALSE,
6609 EL_DIAMOND, -1, MV_BIT_RIGHT
6612 Xpush_diamond_w, FALSE, FALSE,
6613 EL_DIAMOND, -1, MV_BIT_LEFT
6616 Xpush_stone_e, FALSE, FALSE,
6617 EL_ROCK, -1, MV_BIT_RIGHT
6620 Xpush_stone_w, FALSE, FALSE,
6621 EL_ROCK, -1, MV_BIT_LEFT
6624 Xpush_bomb_e, FALSE, FALSE,
6625 EL_BOMB, -1, MV_BIT_RIGHT
6628 Xpush_bomb_w, FALSE, FALSE,
6629 EL_BOMB, -1, MV_BIT_LEFT
6632 Xpush_nut_e, FALSE, FALSE,
6633 EL_NUT, -1, MV_BIT_RIGHT
6636 Xpush_nut_w, FALSE, FALSE,
6637 EL_NUT, -1, MV_BIT_LEFT
6640 Xpush_spring_e, FALSE, FALSE,
6641 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6644 Xpush_spring_w, FALSE, FALSE,
6645 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6649 Xdynamite, TRUE, FALSE,
6650 EL_EM_DYNAMITE, -1, -1
6653 Ydynamite_blank, FALSE, FALSE,
6654 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6657 Xdynamite_1, TRUE, FALSE,
6658 EL_EM_DYNAMITE_ACTIVE, -1, -1
6661 Xdynamite_2, FALSE, FALSE,
6662 EL_EM_DYNAMITE_ACTIVE, -1, -1
6665 Xdynamite_3, FALSE, FALSE,
6666 EL_EM_DYNAMITE_ACTIVE, -1, -1
6669 Xdynamite_4, FALSE, FALSE,
6670 EL_EM_DYNAMITE_ACTIVE, -1, -1
6674 Xkey_1, TRUE, FALSE,
6678 Xkey_2, TRUE, FALSE,
6682 Xkey_3, TRUE, FALSE,
6686 Xkey_4, TRUE, FALSE,
6690 Xkey_5, TRUE, FALSE,
6691 EL_EMC_KEY_5, -1, -1
6694 Xkey_6, TRUE, FALSE,
6695 EL_EMC_KEY_6, -1, -1
6698 Xkey_7, TRUE, FALSE,
6699 EL_EMC_KEY_7, -1, -1
6702 Xkey_8, TRUE, FALSE,
6703 EL_EMC_KEY_8, -1, -1
6707 Xdoor_1, TRUE, FALSE,
6708 EL_EM_GATE_1, -1, -1
6711 Xdoor_2, TRUE, FALSE,
6712 EL_EM_GATE_2, -1, -1
6715 Xdoor_3, TRUE, FALSE,
6716 EL_EM_GATE_3, -1, -1
6719 Xdoor_4, TRUE, FALSE,
6720 EL_EM_GATE_4, -1, -1
6723 Xdoor_5, TRUE, FALSE,
6724 EL_EMC_GATE_5, -1, -1
6727 Xdoor_6, TRUE, FALSE,
6728 EL_EMC_GATE_6, -1, -1
6731 Xdoor_7, TRUE, FALSE,
6732 EL_EMC_GATE_7, -1, -1
6735 Xdoor_8, TRUE, FALSE,
6736 EL_EMC_GATE_8, -1, -1
6740 Xfake_door_1, TRUE, FALSE,
6741 EL_EM_GATE_1_GRAY, -1, -1
6744 Xfake_door_2, TRUE, FALSE,
6745 EL_EM_GATE_2_GRAY, -1, -1
6748 Xfake_door_3, TRUE, FALSE,
6749 EL_EM_GATE_3_GRAY, -1, -1
6752 Xfake_door_4, TRUE, FALSE,
6753 EL_EM_GATE_4_GRAY, -1, -1
6756 Xfake_door_5, TRUE, FALSE,
6757 EL_EMC_GATE_5_GRAY, -1, -1
6760 Xfake_door_6, TRUE, FALSE,
6761 EL_EMC_GATE_6_GRAY, -1, -1
6764 Xfake_door_7, TRUE, FALSE,
6765 EL_EMC_GATE_7_GRAY, -1, -1
6768 Xfake_door_8, TRUE, FALSE,
6769 EL_EMC_GATE_8_GRAY, -1, -1
6773 Xballoon, TRUE, FALSE,
6777 Yballoon_n, FALSE, FALSE,
6778 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6781 Yballoon_nB, FALSE, TRUE,
6782 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6785 Yballoon_e, FALSE, FALSE,
6786 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6789 Yballoon_eB, FALSE, TRUE,
6790 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6793 Yballoon_s, FALSE, FALSE,
6794 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6797 Yballoon_sB, FALSE, TRUE,
6798 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6801 Yballoon_w, FALSE, FALSE,
6802 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6805 Yballoon_wB, FALSE, TRUE,
6806 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6810 Xball_1, TRUE, FALSE,
6811 EL_EMC_MAGIC_BALL, -1, -1
6814 Yball_1, FALSE, FALSE,
6815 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6818 Xball_2, FALSE, FALSE,
6819 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6822 Yball_2, FALSE, FALSE,
6823 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6826 Yball_blank, FALSE, FALSE,
6827 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6831 Xamoeba_1, TRUE, FALSE,
6832 EL_AMOEBA_DRY, ACTION_OTHER, -1
6835 Xamoeba_2, FALSE, FALSE,
6836 EL_AMOEBA_DRY, ACTION_OTHER, -1
6839 Xamoeba_3, FALSE, FALSE,
6840 EL_AMOEBA_DRY, ACTION_OTHER, -1
6843 Xamoeba_4, FALSE, FALSE,
6844 EL_AMOEBA_DRY, ACTION_OTHER, -1
6847 Xamoeba_5, TRUE, FALSE,
6848 EL_AMOEBA_WET, ACTION_OTHER, -1
6851 Xamoeba_6, FALSE, FALSE,
6852 EL_AMOEBA_WET, ACTION_OTHER, -1
6855 Xamoeba_7, FALSE, FALSE,
6856 EL_AMOEBA_WET, ACTION_OTHER, -1
6859 Xamoeba_8, FALSE, FALSE,
6860 EL_AMOEBA_WET, ACTION_OTHER, -1
6865 EL_AMOEBA_DROP, ACTION_GROWING, -1
6868 Xdrip_fall, FALSE, FALSE,
6869 EL_AMOEBA_DROP, -1, -1
6872 Xdrip_stretch, FALSE, FALSE,
6873 EL_AMOEBA_DROP, ACTION_FALLING, -1
6876 Xdrip_stretchB, FALSE, TRUE,
6877 EL_AMOEBA_DROP, ACTION_FALLING, -1
6880 Ydrip_1_s, FALSE, FALSE,
6881 EL_AMOEBA_DROP, ACTION_FALLING, -1
6884 Ydrip_1_sB, FALSE, TRUE,
6885 EL_AMOEBA_DROP, ACTION_FALLING, -1
6888 Ydrip_2_s, FALSE, FALSE,
6889 EL_AMOEBA_DROP, ACTION_FALLING, -1
6892 Ydrip_2_sB, FALSE, TRUE,
6893 EL_AMOEBA_DROP, ACTION_FALLING, -1
6897 Xwonderwall, TRUE, FALSE,
6898 EL_MAGIC_WALL, -1, -1
6901 Ywonderwall, FALSE, FALSE,
6902 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6906 Xwheel, TRUE, FALSE,
6907 EL_ROBOT_WHEEL, -1, -1
6910 Ywheel, FALSE, FALSE,
6911 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6915 Xswitch, TRUE, FALSE,
6916 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6919 Yswitch, FALSE, FALSE,
6920 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6924 Xbumper, TRUE, FALSE,
6925 EL_EMC_SPRING_BUMPER, -1, -1
6928 Ybumper, FALSE, FALSE,
6929 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6933 Xacid_nw, TRUE, FALSE,
6934 EL_ACID_POOL_TOPLEFT, -1, -1
6937 Xacid_ne, TRUE, FALSE,
6938 EL_ACID_POOL_TOPRIGHT, -1, -1
6941 Xacid_sw, TRUE, FALSE,
6942 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6945 Xacid_s, TRUE, FALSE,
6946 EL_ACID_POOL_BOTTOM, -1, -1
6949 Xacid_se, TRUE, FALSE,
6950 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6954 Xfake_blank, TRUE, FALSE,
6955 EL_INVISIBLE_WALL, -1, -1
6958 Yfake_blank, FALSE, FALSE,
6959 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6963 Xfake_grass, TRUE, FALSE,
6964 EL_EMC_FAKE_GRASS, -1, -1
6967 Yfake_grass, FALSE, FALSE,
6968 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6972 Xfake_amoeba, TRUE, FALSE,
6973 EL_EMC_DRIPPER, -1, -1
6976 Yfake_amoeba, FALSE, FALSE,
6977 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6981 Xlenses, TRUE, FALSE,
6982 EL_EMC_LENSES, -1, -1
6986 Xmagnify, TRUE, FALSE,
6987 EL_EMC_MAGNIFIER, -1, -1
6992 EL_QUICKSAND_EMPTY, -1, -1
6995 Xsand_stone, TRUE, FALSE,
6996 EL_QUICKSAND_FULL, -1, -1
6999 Xsand_stonein_1, FALSE, TRUE,
7000 EL_ROCK, ACTION_FILLING, -1
7003 Xsand_stonein_2, FALSE, TRUE,
7004 EL_ROCK, ACTION_FILLING, -1
7007 Xsand_stonein_3, FALSE, TRUE,
7008 EL_ROCK, ACTION_FILLING, -1
7011 Xsand_stonein_4, FALSE, TRUE,
7012 EL_ROCK, ACTION_FILLING, -1
7015 Xsand_sandstone_1, FALSE, FALSE,
7016 EL_QUICKSAND_FILLING, -1, -1
7019 Xsand_sandstone_2, FALSE, FALSE,
7020 EL_QUICKSAND_FILLING, -1, -1
7023 Xsand_sandstone_3, FALSE, FALSE,
7024 EL_QUICKSAND_FILLING, -1, -1
7027 Xsand_sandstone_4, FALSE, FALSE,
7028 EL_QUICKSAND_FILLING, -1, -1
7031 Xsand_stonesand_1, FALSE, FALSE,
7032 EL_QUICKSAND_EMPTYING, -1, -1
7035 Xsand_stonesand_2, FALSE, FALSE,
7036 EL_QUICKSAND_EMPTYING, -1, -1
7039 Xsand_stonesand_3, FALSE, FALSE,
7040 EL_QUICKSAND_EMPTYING, -1, -1
7043 Xsand_stonesand_4, FALSE, FALSE,
7044 EL_QUICKSAND_EMPTYING, -1, -1
7047 Xsand_stoneout_1, FALSE, FALSE,
7048 EL_ROCK, ACTION_EMPTYING, -1
7051 Xsand_stoneout_2, FALSE, FALSE,
7052 EL_ROCK, ACTION_EMPTYING, -1
7055 Xsand_stonesand_quickout_1, FALSE, FALSE,
7056 EL_QUICKSAND_EMPTYING, -1, -1
7059 Xsand_stonesand_quickout_2, FALSE, FALSE,
7060 EL_QUICKSAND_EMPTYING, -1, -1
7064 Xslide_ns, TRUE, FALSE,
7065 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7068 Yslide_ns_blank, FALSE, FALSE,
7069 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7072 Xslide_ew, TRUE, FALSE,
7073 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7076 Yslide_ew_blank, FALSE, FALSE,
7077 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7081 Xwind_n, TRUE, FALSE,
7082 EL_BALLOON_SWITCH_UP, -1, -1
7085 Xwind_e, TRUE, FALSE,
7086 EL_BALLOON_SWITCH_RIGHT, -1, -1
7089 Xwind_s, TRUE, FALSE,
7090 EL_BALLOON_SWITCH_DOWN, -1, -1
7093 Xwind_w, TRUE, FALSE,
7094 EL_BALLOON_SWITCH_LEFT, -1, -1
7097 Xwind_any, TRUE, FALSE,
7098 EL_BALLOON_SWITCH_ANY, -1, -1
7101 Xwind_stop, TRUE, FALSE,
7102 EL_BALLOON_SWITCH_NONE, -1, -1
7107 EL_EM_EXIT_CLOSED, -1, -1
7110 Xexit_1, TRUE, FALSE,
7111 EL_EM_EXIT_OPEN, -1, -1
7114 Xexit_2, FALSE, FALSE,
7115 EL_EM_EXIT_OPEN, -1, -1
7118 Xexit_3, FALSE, FALSE,
7119 EL_EM_EXIT_OPEN, -1, -1
7123 Xpause, FALSE, FALSE,
7128 Xwall_1, TRUE, FALSE,
7132 Xwall_2, TRUE, FALSE,
7133 EL_EMC_WALL_14, -1, -1
7136 Xwall_3, TRUE, FALSE,
7137 EL_EMC_WALL_15, -1, -1
7140 Xwall_4, TRUE, FALSE,
7141 EL_EMC_WALL_16, -1, -1
7145 Xroundwall_1, TRUE, FALSE,
7146 EL_WALL_SLIPPERY, -1, -1
7149 Xroundwall_2, TRUE, FALSE,
7150 EL_EMC_WALL_SLIPPERY_2, -1, -1
7153 Xroundwall_3, TRUE, FALSE,
7154 EL_EMC_WALL_SLIPPERY_3, -1, -1
7157 Xroundwall_4, TRUE, FALSE,
7158 EL_EMC_WALL_SLIPPERY_4, -1, -1
7162 Xsteel_1, TRUE, FALSE,
7163 EL_STEELWALL, -1, -1
7166 Xsteel_2, TRUE, FALSE,
7167 EL_EMC_STEELWALL_2, -1, -1
7170 Xsteel_3, TRUE, FALSE,
7171 EL_EMC_STEELWALL_3, -1, -1
7174 Xsteel_4, TRUE, FALSE,
7175 EL_EMC_STEELWALL_4, -1, -1
7179 Xdecor_1, TRUE, FALSE,
7180 EL_EMC_WALL_8, -1, -1
7183 Xdecor_2, TRUE, FALSE,
7184 EL_EMC_WALL_6, -1, -1
7187 Xdecor_3, TRUE, FALSE,
7188 EL_EMC_WALL_4, -1, -1
7191 Xdecor_4, TRUE, FALSE,
7192 EL_EMC_WALL_7, -1, -1
7195 Xdecor_5, TRUE, FALSE,
7196 EL_EMC_WALL_5, -1, -1
7199 Xdecor_6, TRUE, FALSE,
7200 EL_EMC_WALL_9, -1, -1
7203 Xdecor_7, TRUE, FALSE,
7204 EL_EMC_WALL_10, -1, -1
7207 Xdecor_8, TRUE, FALSE,
7208 EL_EMC_WALL_1, -1, -1
7211 Xdecor_9, TRUE, FALSE,
7212 EL_EMC_WALL_2, -1, -1
7215 Xdecor_10, TRUE, FALSE,
7216 EL_EMC_WALL_3, -1, -1
7219 Xdecor_11, TRUE, FALSE,
7220 EL_EMC_WALL_11, -1, -1
7223 Xdecor_12, TRUE, FALSE,
7224 EL_EMC_WALL_12, -1, -1
7228 Xalpha_0, TRUE, FALSE,
7229 EL_CHAR('0'), -1, -1
7232 Xalpha_1, TRUE, FALSE,
7233 EL_CHAR('1'), -1, -1
7236 Xalpha_2, TRUE, FALSE,
7237 EL_CHAR('2'), -1, -1
7240 Xalpha_3, TRUE, FALSE,
7241 EL_CHAR('3'), -1, -1
7244 Xalpha_4, TRUE, FALSE,
7245 EL_CHAR('4'), -1, -1
7248 Xalpha_5, TRUE, FALSE,
7249 EL_CHAR('5'), -1, -1
7252 Xalpha_6, TRUE, FALSE,
7253 EL_CHAR('6'), -1, -1
7256 Xalpha_7, TRUE, FALSE,
7257 EL_CHAR('7'), -1, -1
7260 Xalpha_8, TRUE, FALSE,
7261 EL_CHAR('8'), -1, -1
7264 Xalpha_9, TRUE, FALSE,
7265 EL_CHAR('9'), -1, -1
7268 Xalpha_excla, TRUE, FALSE,
7269 EL_CHAR('!'), -1, -1
7272 Xalpha_apost, TRUE, FALSE,
7273 EL_CHAR('\''), -1, -1
7276 Xalpha_comma, TRUE, FALSE,
7277 EL_CHAR(','), -1, -1
7280 Xalpha_minus, TRUE, FALSE,
7281 EL_CHAR('-'), -1, -1
7284 Xalpha_perio, TRUE, FALSE,
7285 EL_CHAR('.'), -1, -1
7288 Xalpha_colon, TRUE, FALSE,
7289 EL_CHAR(':'), -1, -1
7292 Xalpha_quest, TRUE, FALSE,
7293 EL_CHAR('?'), -1, -1
7296 Xalpha_a, TRUE, FALSE,
7297 EL_CHAR('A'), -1, -1
7300 Xalpha_b, TRUE, FALSE,
7301 EL_CHAR('B'), -1, -1
7304 Xalpha_c, TRUE, FALSE,
7305 EL_CHAR('C'), -1, -1
7308 Xalpha_d, TRUE, FALSE,
7309 EL_CHAR('D'), -1, -1
7312 Xalpha_e, TRUE, FALSE,
7313 EL_CHAR('E'), -1, -1
7316 Xalpha_f, TRUE, FALSE,
7317 EL_CHAR('F'), -1, -1
7320 Xalpha_g, TRUE, FALSE,
7321 EL_CHAR('G'), -1, -1
7324 Xalpha_h, TRUE, FALSE,
7325 EL_CHAR('H'), -1, -1
7328 Xalpha_i, TRUE, FALSE,
7329 EL_CHAR('I'), -1, -1
7332 Xalpha_j, TRUE, FALSE,
7333 EL_CHAR('J'), -1, -1
7336 Xalpha_k, TRUE, FALSE,
7337 EL_CHAR('K'), -1, -1
7340 Xalpha_l, TRUE, FALSE,
7341 EL_CHAR('L'), -1, -1
7344 Xalpha_m, TRUE, FALSE,
7345 EL_CHAR('M'), -1, -1
7348 Xalpha_n, TRUE, FALSE,
7349 EL_CHAR('N'), -1, -1
7352 Xalpha_o, TRUE, FALSE,
7353 EL_CHAR('O'), -1, -1
7356 Xalpha_p, TRUE, FALSE,
7357 EL_CHAR('P'), -1, -1
7360 Xalpha_q, TRUE, FALSE,
7361 EL_CHAR('Q'), -1, -1
7364 Xalpha_r, TRUE, FALSE,
7365 EL_CHAR('R'), -1, -1
7368 Xalpha_s, TRUE, FALSE,
7369 EL_CHAR('S'), -1, -1
7372 Xalpha_t, TRUE, FALSE,
7373 EL_CHAR('T'), -1, -1
7376 Xalpha_u, TRUE, FALSE,
7377 EL_CHAR('U'), -1, -1
7380 Xalpha_v, TRUE, FALSE,
7381 EL_CHAR('V'), -1, -1
7384 Xalpha_w, TRUE, FALSE,
7385 EL_CHAR('W'), -1, -1
7388 Xalpha_x, TRUE, FALSE,
7389 EL_CHAR('X'), -1, -1
7392 Xalpha_y, TRUE, FALSE,
7393 EL_CHAR('Y'), -1, -1
7396 Xalpha_z, TRUE, FALSE,
7397 EL_CHAR('Z'), -1, -1
7400 Xalpha_arrow_e, TRUE, FALSE,
7401 EL_CHAR('>'), -1, -1
7404 Xalpha_arrow_w, TRUE, FALSE,
7405 EL_CHAR('<'), -1, -1
7408 Xalpha_copyr, TRUE, FALSE,
7409 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7413 Ykey_1_blank, FALSE, FALSE,
7414 EL_EM_KEY_1, ACTION_COLLECTING, -1
7417 Ykey_2_blank, FALSE, FALSE,
7418 EL_EM_KEY_2, ACTION_COLLECTING, -1
7421 Ykey_3_blank, FALSE, FALSE,
7422 EL_EM_KEY_3, ACTION_COLLECTING, -1
7425 Ykey_4_blank, FALSE, FALSE,
7426 EL_EM_KEY_4, ACTION_COLLECTING, -1
7429 Ykey_5_blank, FALSE, FALSE,
7430 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7433 Ykey_6_blank, FALSE, FALSE,
7434 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7437 Ykey_7_blank, FALSE, FALSE,
7438 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7441 Ykey_8_blank, FALSE, FALSE,
7442 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7445 Ylenses_blank, FALSE, FALSE,
7446 EL_EMC_LENSES, ACTION_COLLECTING, -1
7449 Ymagnify_blank, FALSE, FALSE,
7450 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7453 Ygrass_blank, FALSE, FALSE,
7454 EL_EMC_GRASS, ACTION_SNAPPING, -1
7457 Ydirt_blank, FALSE, FALSE,
7458 EL_SAND, ACTION_SNAPPING, -1
7467 static struct Mapping_EM_to_RND_player
7476 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7480 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7484 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7488 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7492 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7496 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7500 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7504 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7508 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7512 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7516 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7520 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7524 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7528 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7532 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7536 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7540 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7544 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7548 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7552 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7556 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7560 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7564 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7568 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7572 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7576 EL_PLAYER_1, ACTION_DEFAULT, -1,
7580 EL_PLAYER_2, ACTION_DEFAULT, -1,
7584 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7588 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7592 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7596 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7600 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7604 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7608 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7612 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7616 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7620 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7624 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7628 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7632 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7636 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7640 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7644 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7648 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7652 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7656 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7660 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7664 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7668 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7672 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7676 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7680 EL_PLAYER_3, ACTION_DEFAULT, -1,
7684 EL_PLAYER_4, ACTION_DEFAULT, -1,
7693 int map_element_RND_to_EM_cave(int element_rnd)
7695 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7696 static boolean mapping_initialized = FALSE;
7698 if (!mapping_initialized)
7702 // return "Xalpha_quest" for all undefined elements in mapping array
7703 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7704 mapping_RND_to_EM[i] = Xalpha_quest;
7706 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7707 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7708 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7709 em_object_mapping_list[i].element_em;
7711 mapping_initialized = TRUE;
7714 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7716 Warn("invalid RND level element %d", element_rnd);
7721 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7724 int map_element_EM_to_RND_cave(int element_em_cave)
7726 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7727 static boolean mapping_initialized = FALSE;
7729 if (!mapping_initialized)
7733 // return "EL_UNKNOWN" for all undefined elements in mapping array
7734 for (i = 0; i < GAME_TILE_MAX; i++)
7735 mapping_EM_to_RND[i] = EL_UNKNOWN;
7737 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7738 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7739 em_object_mapping_list[i].element_rnd;
7741 mapping_initialized = TRUE;
7744 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7746 Warn("invalid EM cave element %d", element_em_cave);
7751 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7754 int map_element_EM_to_RND_game(int element_em_game)
7756 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7757 static boolean mapping_initialized = FALSE;
7759 if (!mapping_initialized)
7763 // return "EL_UNKNOWN" for all undefined elements in mapping array
7764 for (i = 0; i < GAME_TILE_MAX; i++)
7765 mapping_EM_to_RND[i] = EL_UNKNOWN;
7767 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7768 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7769 em_object_mapping_list[i].element_rnd;
7771 mapping_initialized = TRUE;
7774 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7776 Warn("invalid EM game element %d", element_em_game);
7781 return mapping_EM_to_RND[element_em_game];
7784 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7786 struct LevelInfo_EM *level_em = level->native_em_level;
7787 struct CAVE *cav = level_em->cav;
7790 for (i = 0; i < GAME_TILE_MAX; i++)
7791 cav->android_array[i] = Cblank;
7793 for (i = 0; i < level->num_android_clone_elements; i++)
7795 int element_rnd = level->android_clone_element[i];
7796 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7798 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7799 if (em_object_mapping_list[j].element_rnd == element_rnd)
7800 cav->android_array[em_object_mapping_list[j].element_em] =
7805 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7807 struct LevelInfo_EM *level_em = level->native_em_level;
7808 struct CAVE *cav = level_em->cav;
7811 level->num_android_clone_elements = 0;
7813 for (i = 0; i < GAME_TILE_MAX; i++)
7815 int element_em_cave = cav->android_array[i];
7817 boolean element_found = FALSE;
7819 if (element_em_cave == Cblank)
7822 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7824 for (j = 0; j < level->num_android_clone_elements; j++)
7825 if (level->android_clone_element[j] == element_rnd)
7826 element_found = TRUE;
7830 level->android_clone_element[level->num_android_clone_elements++] =
7833 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7838 if (level->num_android_clone_elements == 0)
7840 level->num_android_clone_elements = 1;
7841 level->android_clone_element[0] = EL_EMPTY;
7845 int map_direction_RND_to_EM(int direction)
7847 return (direction == MV_UP ? 0 :
7848 direction == MV_RIGHT ? 1 :
7849 direction == MV_DOWN ? 2 :
7850 direction == MV_LEFT ? 3 :
7854 int map_direction_EM_to_RND(int direction)
7856 return (direction == 0 ? MV_UP :
7857 direction == 1 ? MV_RIGHT :
7858 direction == 2 ? MV_DOWN :
7859 direction == 3 ? MV_LEFT :
7863 int map_element_RND_to_SP(int element_rnd)
7865 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7867 if (element_rnd >= EL_SP_START &&
7868 element_rnd <= EL_SP_END)
7869 element_sp = element_rnd - EL_SP_START;
7870 else if (element_rnd == EL_EMPTY_SPACE)
7872 else if (element_rnd == EL_INVISIBLE_WALL)
7878 int map_element_SP_to_RND(int element_sp)
7880 int element_rnd = EL_UNKNOWN;
7882 if (element_sp >= 0x00 &&
7884 element_rnd = EL_SP_START + element_sp;
7885 else if (element_sp == 0x28)
7886 element_rnd = EL_INVISIBLE_WALL;
7891 int map_action_SP_to_RND(int action_sp)
7895 case actActive: return ACTION_ACTIVE;
7896 case actImpact: return ACTION_IMPACT;
7897 case actExploding: return ACTION_EXPLODING;
7898 case actDigging: return ACTION_DIGGING;
7899 case actSnapping: return ACTION_SNAPPING;
7900 case actCollecting: return ACTION_COLLECTING;
7901 case actPassing: return ACTION_PASSING;
7902 case actPushing: return ACTION_PUSHING;
7903 case actDropping: return ACTION_DROPPING;
7905 default: return ACTION_DEFAULT;
7909 int map_element_RND_to_MM(int element_rnd)
7911 return (element_rnd >= EL_MM_START_1 &&
7912 element_rnd <= EL_MM_END_1 ?
7913 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7915 element_rnd >= EL_MM_START_2 &&
7916 element_rnd <= EL_MM_END_2 ?
7917 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7919 element_rnd >= EL_CHAR_START &&
7920 element_rnd <= EL_CHAR_END ?
7921 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7923 element_rnd >= EL_MM_RUNTIME_START &&
7924 element_rnd <= EL_MM_RUNTIME_END ?
7925 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7927 element_rnd >= EL_MM_DUMMY_START &&
7928 element_rnd <= EL_MM_DUMMY_END ?
7929 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7931 EL_MM_EMPTY_NATIVE);
7934 int map_element_MM_to_RND(int element_mm)
7936 return (element_mm == EL_MM_EMPTY_NATIVE ||
7937 element_mm == EL_DF_EMPTY_NATIVE ?
7940 element_mm >= EL_MM_START_1_NATIVE &&
7941 element_mm <= EL_MM_END_1_NATIVE ?
7942 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7944 element_mm >= EL_MM_START_2_NATIVE &&
7945 element_mm <= EL_MM_END_2_NATIVE ?
7946 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7948 element_mm >= EL_MM_CHAR_START_NATIVE &&
7949 element_mm <= EL_MM_CHAR_END_NATIVE ?
7950 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7952 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7953 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7954 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7956 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7957 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7958 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7963 int map_action_MM_to_RND(int action_mm)
7965 // all MM actions are defined to exactly match their RND counterparts
7969 int map_sound_MM_to_RND(int sound_mm)
7973 case SND_MM_GAME_LEVELTIME_CHARGING:
7974 return SND_GAME_LEVELTIME_CHARGING;
7976 case SND_MM_GAME_HEALTH_CHARGING:
7977 return SND_GAME_HEALTH_CHARGING;
7980 return SND_UNDEFINED;
7984 int map_mm_wall_element(int element)
7986 return (element >= EL_MM_STEEL_WALL_START &&
7987 element <= EL_MM_STEEL_WALL_END ?
7990 element >= EL_MM_WOODEN_WALL_START &&
7991 element <= EL_MM_WOODEN_WALL_END ?
7994 element >= EL_MM_ICE_WALL_START &&
7995 element <= EL_MM_ICE_WALL_END ?
7998 element >= EL_MM_AMOEBA_WALL_START &&
7999 element <= EL_MM_AMOEBA_WALL_END ?
8002 element >= EL_DF_STEEL_WALL_START &&
8003 element <= EL_DF_STEEL_WALL_END ?
8006 element >= EL_DF_WOODEN_WALL_START &&
8007 element <= EL_DF_WOODEN_WALL_END ?
8013 int map_mm_wall_element_editor(int element)
8017 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8018 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8019 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8020 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8021 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8022 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8024 default: return element;
8028 int get_next_element(int element)
8032 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8033 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8034 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8035 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8036 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8037 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8038 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8039 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8040 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8041 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8042 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8044 default: return element;
8048 int el2img_mm(int element_mm)
8050 return el2img(map_element_MM_to_RND(element_mm));
8053 int el_act_dir2img(int element, int action, int direction)
8055 element = GFX_ELEMENT(element);
8056 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8058 // direction_graphic[][] == graphic[] for undefined direction graphics
8059 return element_info[element].direction_graphic[action][direction];
8062 static int el_act_dir2crm(int element, int action, int direction)
8064 element = GFX_ELEMENT(element);
8065 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8067 // direction_graphic[][] == graphic[] for undefined direction graphics
8068 return element_info[element].direction_crumbled[action][direction];
8071 int el_act2img(int element, int action)
8073 element = GFX_ELEMENT(element);
8075 return element_info[element].graphic[action];
8078 int el_act2crm(int element, int action)
8080 element = GFX_ELEMENT(element);
8082 return element_info[element].crumbled[action];
8085 int el_dir2img(int element, int direction)
8087 element = GFX_ELEMENT(element);
8089 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8092 int el2baseimg(int element)
8094 return element_info[element].graphic[ACTION_DEFAULT];
8097 int el2img(int element)
8099 element = GFX_ELEMENT(element);
8101 return element_info[element].graphic[ACTION_DEFAULT];
8104 int el2edimg(int element)
8106 element = GFX_ELEMENT(element);
8108 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8111 int el2preimg(int element)
8113 element = GFX_ELEMENT(element);
8115 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8118 int el2panelimg(int element)
8120 element = GFX_ELEMENT(element);
8122 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8125 int font2baseimg(int font_nr)
8127 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8130 int getBeltNrFromBeltElement(int element)
8132 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8133 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8134 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8137 int getBeltNrFromBeltActiveElement(int element)
8139 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8140 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8141 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8144 int getBeltNrFromBeltSwitchElement(int element)
8146 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8147 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8148 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8151 int getBeltDirNrFromBeltElement(int element)
8153 static int belt_base_element[4] =
8155 EL_CONVEYOR_BELT_1_LEFT,
8156 EL_CONVEYOR_BELT_2_LEFT,
8157 EL_CONVEYOR_BELT_3_LEFT,
8158 EL_CONVEYOR_BELT_4_LEFT
8161 int belt_nr = getBeltNrFromBeltElement(element);
8162 int belt_dir_nr = element - belt_base_element[belt_nr];
8164 return (belt_dir_nr % 3);
8167 int getBeltDirNrFromBeltSwitchElement(int element)
8169 static int belt_base_element[4] =
8171 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8172 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8173 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8174 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8177 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8178 int belt_dir_nr = element - belt_base_element[belt_nr];
8180 return (belt_dir_nr % 3);
8183 int getBeltDirFromBeltElement(int element)
8185 static int belt_move_dir[3] =
8192 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8194 return belt_move_dir[belt_dir_nr];
8197 int getBeltDirFromBeltSwitchElement(int element)
8199 static int belt_move_dir[3] =
8206 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8208 return belt_move_dir[belt_dir_nr];
8211 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8213 static int belt_base_element[4] =
8215 EL_CONVEYOR_BELT_1_LEFT,
8216 EL_CONVEYOR_BELT_2_LEFT,
8217 EL_CONVEYOR_BELT_3_LEFT,
8218 EL_CONVEYOR_BELT_4_LEFT
8221 return belt_base_element[belt_nr] + belt_dir_nr;
8224 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8226 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8228 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8231 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8233 static int belt_base_element[4] =
8235 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8236 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8237 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8238 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8241 return belt_base_element[belt_nr] + belt_dir_nr;
8244 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8246 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8248 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8251 boolean swapTiles_EM(boolean is_pre_emc_cave)
8253 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8256 boolean getTeamMode_EM(void)
8258 return game.team_mode || network_playing;
8261 boolean isActivePlayer_EM(int player_nr)
8263 return stored_player[player_nr].active;
8266 unsigned int InitRND(int seed)
8268 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8269 return InitEngineRandom_EM(seed);
8270 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8271 return InitEngineRandom_SP(seed);
8272 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8273 return InitEngineRandom_MM(seed);
8275 return InitEngineRandom_RND(seed);
8278 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8279 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8281 static int get_effective_element_EM(int tile, int frame_em)
8283 int element = object_mapping[tile].element_rnd;
8284 int action = object_mapping[tile].action;
8285 boolean is_backside = object_mapping[tile].is_backside;
8286 boolean action_removing = (action == ACTION_DIGGING ||
8287 action == ACTION_SNAPPING ||
8288 action == ACTION_COLLECTING);
8296 return (frame_em > 5 ? EL_EMPTY : element);
8302 else // frame_em == 7
8313 case Ydiamond_stone:
8317 case Xdrip_stretchB:
8333 case Ymagnify_blank:
8336 case Xsand_stonein_1:
8337 case Xsand_stonein_2:
8338 case Xsand_stonein_3:
8339 case Xsand_stonein_4:
8343 return (is_backside || action_removing ? EL_EMPTY : element);
8348 static boolean check_linear_animation_EM(int tile)
8352 case Xsand_stonesand_1:
8353 case Xsand_stonesand_quickout_1:
8354 case Xsand_sandstone_1:
8355 case Xsand_stonein_1:
8356 case Xsand_stoneout_1:
8384 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8385 boolean has_crumbled_graphics,
8386 int crumbled, int sync_frame)
8388 // if element can be crumbled, but certain action graphics are just empty
8389 // space (like instantly snapping sand to empty space in 1 frame), do not
8390 // treat these empty space graphics as crumbled graphics in EMC engine
8391 if (crumbled == IMG_EMPTY_SPACE)
8392 has_crumbled_graphics = FALSE;
8394 if (has_crumbled_graphics)
8396 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8397 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8398 g_crumbled->anim_delay,
8399 g_crumbled->anim_mode,
8400 g_crumbled->anim_start_frame,
8403 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8404 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8406 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8407 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8409 g_em->has_crumbled_graphics = TRUE;
8413 g_em->crumbled_bitmap = NULL;
8414 g_em->crumbled_src_x = 0;
8415 g_em->crumbled_src_y = 0;
8416 g_em->crumbled_border_size = 0;
8417 g_em->crumbled_tile_size = 0;
8419 g_em->has_crumbled_graphics = FALSE;
8424 void ResetGfxAnimation_EM(int x, int y, int tile)
8430 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8431 int tile, int frame_em, int x, int y)
8433 int action = object_mapping[tile].action;
8434 int direction = object_mapping[tile].direction;
8435 int effective_element = get_effective_element_EM(tile, frame_em);
8436 int graphic = (direction == MV_NONE ?
8437 el_act2img(effective_element, action) :
8438 el_act_dir2img(effective_element, action, direction));
8439 struct GraphicInfo *g = &graphic_info[graphic];
8441 boolean action_removing = (action == ACTION_DIGGING ||
8442 action == ACTION_SNAPPING ||
8443 action == ACTION_COLLECTING);
8444 boolean action_moving = (action == ACTION_FALLING ||
8445 action == ACTION_MOVING ||
8446 action == ACTION_PUSHING ||
8447 action == ACTION_EATING ||
8448 action == ACTION_FILLING ||
8449 action == ACTION_EMPTYING);
8450 boolean action_falling = (action == ACTION_FALLING ||
8451 action == ACTION_FILLING ||
8452 action == ACTION_EMPTYING);
8454 // special case: graphic uses "2nd movement tile" and has defined
8455 // 7 frames for movement animation (or less) => use default graphic
8456 // for last (8th) frame which ends the movement animation
8457 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8459 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8460 graphic = (direction == MV_NONE ?
8461 el_act2img(effective_element, action) :
8462 el_act_dir2img(effective_element, action, direction));
8464 g = &graphic_info[graphic];
8467 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8471 else if (action_moving)
8473 boolean is_backside = object_mapping[tile].is_backside;
8477 int direction = object_mapping[tile].direction;
8478 int move_dir = (action_falling ? MV_DOWN : direction);
8483 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8484 if (g->double_movement && frame_em == 0)
8488 if (move_dir == MV_LEFT)
8489 GfxFrame[x - 1][y] = GfxFrame[x][y];
8490 else if (move_dir == MV_RIGHT)
8491 GfxFrame[x + 1][y] = GfxFrame[x][y];
8492 else if (move_dir == MV_UP)
8493 GfxFrame[x][y - 1] = GfxFrame[x][y];
8494 else if (move_dir == MV_DOWN)
8495 GfxFrame[x][y + 1] = GfxFrame[x][y];
8502 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8503 if (tile == Xsand_stonesand_quickout_1 ||
8504 tile == Xsand_stonesand_quickout_2)
8508 if (graphic_info[graphic].anim_global_sync)
8509 sync_frame = FrameCounter;
8510 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8511 sync_frame = GfxFrame[x][y];
8513 sync_frame = 0; // playfield border (pseudo steel)
8515 SetRandomAnimationValue(x, y);
8517 int frame = getAnimationFrame(g->anim_frames,
8520 g->anim_start_frame,
8523 g_em->unique_identifier =
8524 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8527 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8528 int tile, int frame_em, int x, int y)
8530 int action = object_mapping[tile].action;
8531 int direction = object_mapping[tile].direction;
8532 boolean is_backside = object_mapping[tile].is_backside;
8533 int effective_element = get_effective_element_EM(tile, frame_em);
8534 int effective_action = action;
8535 int graphic = (direction == MV_NONE ?
8536 el_act2img(effective_element, effective_action) :
8537 el_act_dir2img(effective_element, effective_action,
8539 int crumbled = (direction == MV_NONE ?
8540 el_act2crm(effective_element, effective_action) :
8541 el_act_dir2crm(effective_element, effective_action,
8543 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8544 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8545 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8546 struct GraphicInfo *g = &graphic_info[graphic];
8549 // special case: graphic uses "2nd movement tile" and has defined
8550 // 7 frames for movement animation (or less) => use default graphic
8551 // for last (8th) frame which ends the movement animation
8552 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8554 effective_action = ACTION_DEFAULT;
8555 graphic = (direction == MV_NONE ?
8556 el_act2img(effective_element, effective_action) :
8557 el_act_dir2img(effective_element, effective_action,
8559 crumbled = (direction == MV_NONE ?
8560 el_act2crm(effective_element, effective_action) :
8561 el_act_dir2crm(effective_element, effective_action,
8564 g = &graphic_info[graphic];
8567 if (graphic_info[graphic].anim_global_sync)
8568 sync_frame = FrameCounter;
8569 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8570 sync_frame = GfxFrame[x][y];
8572 sync_frame = 0; // playfield border (pseudo steel)
8574 SetRandomAnimationValue(x, y);
8576 int frame = getAnimationFrame(g->anim_frames,
8579 g->anim_start_frame,
8582 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8583 g->double_movement && is_backside);
8585 // (updating the "crumbled" graphic definitions is probably not really needed,
8586 // as animations for crumbled graphics can't be longer than one EMC cycle)
8587 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8591 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8592 int player_nr, int anim, int frame_em)
8594 int element = player_mapping[player_nr][anim].element_rnd;
8595 int action = player_mapping[player_nr][anim].action;
8596 int direction = player_mapping[player_nr][anim].direction;
8597 int graphic = (direction == MV_NONE ?
8598 el_act2img(element, action) :
8599 el_act_dir2img(element, action, direction));
8600 struct GraphicInfo *g = &graphic_info[graphic];
8603 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8605 stored_player[player_nr].StepFrame = frame_em;
8607 sync_frame = stored_player[player_nr].Frame;
8609 int frame = getAnimationFrame(g->anim_frames,
8612 g->anim_start_frame,
8615 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8616 &g_em->src_x, &g_em->src_y, FALSE);
8619 void InitGraphicInfo_EM(void)
8623 // always start with reliable default values
8624 for (i = 0; i < GAME_TILE_MAX; i++)
8626 object_mapping[i].element_rnd = EL_UNKNOWN;
8627 object_mapping[i].is_backside = FALSE;
8628 object_mapping[i].action = ACTION_DEFAULT;
8629 object_mapping[i].direction = MV_NONE;
8632 // always start with reliable default values
8633 for (p = 0; p < MAX_PLAYERS; p++)
8635 for (i = 0; i < PLY_MAX; i++)
8637 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8638 player_mapping[p][i].action = ACTION_DEFAULT;
8639 player_mapping[p][i].direction = MV_NONE;
8643 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8645 int e = em_object_mapping_list[i].element_em;
8647 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8648 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8650 if (em_object_mapping_list[i].action != -1)
8651 object_mapping[e].action = em_object_mapping_list[i].action;
8653 if (em_object_mapping_list[i].direction != -1)
8654 object_mapping[e].direction =
8655 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8658 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8660 int a = em_player_mapping_list[i].action_em;
8661 int p = em_player_mapping_list[i].player_nr;
8663 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8665 if (em_player_mapping_list[i].action != -1)
8666 player_mapping[p][a].action = em_player_mapping_list[i].action;
8668 if (em_player_mapping_list[i].direction != -1)
8669 player_mapping[p][a].direction =
8670 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8673 for (i = 0; i < GAME_TILE_MAX; i++)
8675 int element = object_mapping[i].element_rnd;
8676 int action = object_mapping[i].action;
8677 int direction = object_mapping[i].direction;
8678 boolean is_backside = object_mapping[i].is_backside;
8679 boolean action_exploding = ((action == ACTION_EXPLODING ||
8680 action == ACTION_SMASHED_BY_ROCK ||
8681 action == ACTION_SMASHED_BY_SPRING) &&
8682 element != EL_DIAMOND);
8683 boolean action_active = (action == ACTION_ACTIVE);
8684 boolean action_other = (action == ACTION_OTHER);
8686 for (j = 0; j < 8; j++)
8688 int effective_element = get_effective_element_EM(i, j);
8689 int effective_action = (j < 7 ? action :
8690 i == Xdrip_stretch ? action :
8691 i == Xdrip_stretchB ? action :
8692 i == Ydrip_1_s ? action :
8693 i == Ydrip_1_sB ? action :
8694 i == Yball_1 ? action :
8695 i == Xball_2 ? action :
8696 i == Yball_2 ? action :
8697 i == Yball_blank ? action :
8698 i == Ykey_1_blank ? action :
8699 i == Ykey_2_blank ? action :
8700 i == Ykey_3_blank ? action :
8701 i == Ykey_4_blank ? action :
8702 i == Ykey_5_blank ? action :
8703 i == Ykey_6_blank ? action :
8704 i == Ykey_7_blank ? action :
8705 i == Ykey_8_blank ? action :
8706 i == Ylenses_blank ? action :
8707 i == Ymagnify_blank ? action :
8708 i == Ygrass_blank ? action :
8709 i == Ydirt_blank ? action :
8710 i == Xsand_stonein_1 ? action :
8711 i == Xsand_stonein_2 ? action :
8712 i == Xsand_stonein_3 ? action :
8713 i == Xsand_stonein_4 ? action :
8714 i == Xsand_stoneout_1 ? action :
8715 i == Xsand_stoneout_2 ? action :
8716 i == Xboom_android ? ACTION_EXPLODING :
8717 action_exploding ? ACTION_EXPLODING :
8718 action_active ? action :
8719 action_other ? action :
8721 int graphic = (el_act_dir2img(effective_element, effective_action,
8723 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8725 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8726 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8727 boolean has_action_graphics = (graphic != base_graphic);
8728 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8729 struct GraphicInfo *g = &graphic_info[graphic];
8730 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8733 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8734 boolean special_animation = (action != ACTION_DEFAULT &&
8735 g->anim_frames == 3 &&
8736 g->anim_delay == 2 &&
8737 g->anim_mode & ANIM_LINEAR);
8738 int sync_frame = (i == Xdrip_stretch ? 7 :
8739 i == Xdrip_stretchB ? 7 :
8740 i == Ydrip_2_s ? j + 8 :
8741 i == Ydrip_2_sB ? j + 8 :
8750 i == Xfake_acid_1 ? 0 :
8751 i == Xfake_acid_2 ? 10 :
8752 i == Xfake_acid_3 ? 20 :
8753 i == Xfake_acid_4 ? 30 :
8754 i == Xfake_acid_5 ? 40 :
8755 i == Xfake_acid_6 ? 50 :
8756 i == Xfake_acid_7 ? 60 :
8757 i == Xfake_acid_8 ? 70 :
8759 i == Yball_2 ? j + 8 :
8760 i == Yball_blank ? j + 1 :
8761 i == Ykey_1_blank ? j + 1 :
8762 i == Ykey_2_blank ? j + 1 :
8763 i == Ykey_3_blank ? j + 1 :
8764 i == Ykey_4_blank ? j + 1 :
8765 i == Ykey_5_blank ? j + 1 :
8766 i == Ykey_6_blank ? j + 1 :
8767 i == Ykey_7_blank ? j + 1 :
8768 i == Ykey_8_blank ? j + 1 :
8769 i == Ylenses_blank ? j + 1 :
8770 i == Ymagnify_blank ? j + 1 :
8771 i == Ygrass_blank ? j + 1 :
8772 i == Ydirt_blank ? j + 1 :
8773 i == Xamoeba_1 ? 0 :
8774 i == Xamoeba_2 ? 1 :
8775 i == Xamoeba_3 ? 2 :
8776 i == Xamoeba_4 ? 3 :
8777 i == Xamoeba_5 ? 0 :
8778 i == Xamoeba_6 ? 1 :
8779 i == Xamoeba_7 ? 2 :
8780 i == Xamoeba_8 ? 3 :
8781 i == Xexit_2 ? j + 8 :
8782 i == Xexit_3 ? j + 16 :
8783 i == Xdynamite_1 ? 0 :
8784 i == Xdynamite_2 ? 8 :
8785 i == Xdynamite_3 ? 16 :
8786 i == Xdynamite_4 ? 24 :
8787 i == Xsand_stonein_1 ? j + 1 :
8788 i == Xsand_stonein_2 ? j + 9 :
8789 i == Xsand_stonein_3 ? j + 17 :
8790 i == Xsand_stonein_4 ? j + 25 :
8791 i == Xsand_stoneout_1 && j == 0 ? 0 :
8792 i == Xsand_stoneout_1 && j == 1 ? 0 :
8793 i == Xsand_stoneout_1 && j == 2 ? 1 :
8794 i == Xsand_stoneout_1 && j == 3 ? 2 :
8795 i == Xsand_stoneout_1 && j == 4 ? 2 :
8796 i == Xsand_stoneout_1 && j == 5 ? 3 :
8797 i == Xsand_stoneout_1 && j == 6 ? 4 :
8798 i == Xsand_stoneout_1 && j == 7 ? 4 :
8799 i == Xsand_stoneout_2 && j == 0 ? 5 :
8800 i == Xsand_stoneout_2 && j == 1 ? 6 :
8801 i == Xsand_stoneout_2 && j == 2 ? 7 :
8802 i == Xsand_stoneout_2 && j == 3 ? 8 :
8803 i == Xsand_stoneout_2 && j == 4 ? 9 :
8804 i == Xsand_stoneout_2 && j == 5 ? 11 :
8805 i == Xsand_stoneout_2 && j == 6 ? 13 :
8806 i == Xsand_stoneout_2 && j == 7 ? 15 :
8807 i == Xboom_bug && j == 1 ? 2 :
8808 i == Xboom_bug && j == 2 ? 2 :
8809 i == Xboom_bug && j == 3 ? 4 :
8810 i == Xboom_bug && j == 4 ? 4 :
8811 i == Xboom_bug && j == 5 ? 2 :
8812 i == Xboom_bug && j == 6 ? 2 :
8813 i == Xboom_bug && j == 7 ? 0 :
8814 i == Xboom_tank && j == 1 ? 2 :
8815 i == Xboom_tank && j == 2 ? 2 :
8816 i == Xboom_tank && j == 3 ? 4 :
8817 i == Xboom_tank && j == 4 ? 4 :
8818 i == Xboom_tank && j == 5 ? 2 :
8819 i == Xboom_tank && j == 6 ? 2 :
8820 i == Xboom_tank && j == 7 ? 0 :
8821 i == Xboom_android && j == 7 ? 6 :
8822 i == Xboom_1 && j == 1 ? 2 :
8823 i == Xboom_1 && j == 2 ? 2 :
8824 i == Xboom_1 && j == 3 ? 4 :
8825 i == Xboom_1 && j == 4 ? 4 :
8826 i == Xboom_1 && j == 5 ? 6 :
8827 i == Xboom_1 && j == 6 ? 6 :
8828 i == Xboom_1 && j == 7 ? 8 :
8829 i == Xboom_2 && j == 0 ? 8 :
8830 i == Xboom_2 && j == 1 ? 8 :
8831 i == Xboom_2 && j == 2 ? 10 :
8832 i == Xboom_2 && j == 3 ? 10 :
8833 i == Xboom_2 && j == 4 ? 10 :
8834 i == Xboom_2 && j == 5 ? 12 :
8835 i == Xboom_2 && j == 6 ? 12 :
8836 i == Xboom_2 && j == 7 ? 12 :
8837 special_animation && j == 4 ? 3 :
8838 effective_action != action ? 0 :
8840 int frame = getAnimationFrame(g->anim_frames,
8843 g->anim_start_frame,
8846 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8847 g->double_movement && is_backside);
8849 g_em->bitmap = src_bitmap;
8850 g_em->src_x = src_x;
8851 g_em->src_y = src_y;
8852 g_em->src_offset_x = 0;
8853 g_em->src_offset_y = 0;
8854 g_em->dst_offset_x = 0;
8855 g_em->dst_offset_y = 0;
8856 g_em->width = TILEX;
8857 g_em->height = TILEY;
8859 g_em->preserve_background = FALSE;
8861 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8864 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8865 effective_action == ACTION_MOVING ||
8866 effective_action == ACTION_PUSHING ||
8867 effective_action == ACTION_EATING)) ||
8868 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8869 effective_action == ACTION_EMPTYING)))
8872 (effective_action == ACTION_FALLING ||
8873 effective_action == ACTION_FILLING ||
8874 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8875 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8876 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8877 int num_steps = (i == Ydrip_1_s ? 16 :
8878 i == Ydrip_1_sB ? 16 :
8879 i == Ydrip_2_s ? 16 :
8880 i == Ydrip_2_sB ? 16 :
8881 i == Xsand_stonein_1 ? 32 :
8882 i == Xsand_stonein_2 ? 32 :
8883 i == Xsand_stonein_3 ? 32 :
8884 i == Xsand_stonein_4 ? 32 :
8885 i == Xsand_stoneout_1 ? 16 :
8886 i == Xsand_stoneout_2 ? 16 : 8);
8887 int cx = ABS(dx) * (TILEX / num_steps);
8888 int cy = ABS(dy) * (TILEY / num_steps);
8889 int step_frame = (i == Ydrip_2_s ? j + 8 :
8890 i == Ydrip_2_sB ? j + 8 :
8891 i == Xsand_stonein_2 ? j + 8 :
8892 i == Xsand_stonein_3 ? j + 16 :
8893 i == Xsand_stonein_4 ? j + 24 :
8894 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8895 int step = (is_backside ? step_frame : num_steps - step_frame);
8897 if (is_backside) // tile where movement starts
8899 if (dx < 0 || dy < 0)
8901 g_em->src_offset_x = cx * step;
8902 g_em->src_offset_y = cy * step;
8906 g_em->dst_offset_x = cx * step;
8907 g_em->dst_offset_y = cy * step;
8910 else // tile where movement ends
8912 if (dx < 0 || dy < 0)
8914 g_em->dst_offset_x = cx * step;
8915 g_em->dst_offset_y = cy * step;
8919 g_em->src_offset_x = cx * step;
8920 g_em->src_offset_y = cy * step;
8924 g_em->width = TILEX - cx * step;
8925 g_em->height = TILEY - cy * step;
8928 // create unique graphic identifier to decide if tile must be redrawn
8929 /* bit 31 - 16 (16 bit): EM style graphic
8930 bit 15 - 12 ( 4 bit): EM style frame
8931 bit 11 - 6 ( 6 bit): graphic width
8932 bit 5 - 0 ( 6 bit): graphic height */
8933 g_em->unique_identifier =
8934 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8938 for (i = 0; i < GAME_TILE_MAX; i++)
8940 for (j = 0; j < 8; j++)
8942 int element = object_mapping[i].element_rnd;
8943 int action = object_mapping[i].action;
8944 int direction = object_mapping[i].direction;
8945 boolean is_backside = object_mapping[i].is_backside;
8946 int graphic_action = el_act_dir2img(element, action, direction);
8947 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8949 if ((action == ACTION_SMASHED_BY_ROCK ||
8950 action == ACTION_SMASHED_BY_SPRING ||
8951 action == ACTION_EATING) &&
8952 graphic_action == graphic_default)
8954 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8955 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8956 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8957 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8960 // no separate animation for "smashed by rock" -- use rock instead
8961 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8962 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8964 g_em->bitmap = g_xx->bitmap;
8965 g_em->src_x = g_xx->src_x;
8966 g_em->src_y = g_xx->src_y;
8967 g_em->src_offset_x = g_xx->src_offset_x;
8968 g_em->src_offset_y = g_xx->src_offset_y;
8969 g_em->dst_offset_x = g_xx->dst_offset_x;
8970 g_em->dst_offset_y = g_xx->dst_offset_y;
8971 g_em->width = g_xx->width;
8972 g_em->height = g_xx->height;
8973 g_em->unique_identifier = g_xx->unique_identifier;
8976 g_em->preserve_background = TRUE;
8981 for (p = 0; p < MAX_PLAYERS; p++)
8983 for (i = 0; i < PLY_MAX; i++)
8985 int element = player_mapping[p][i].element_rnd;
8986 int action = player_mapping[p][i].action;
8987 int direction = player_mapping[p][i].direction;
8989 for (j = 0; j < 8; j++)
8991 int effective_element = element;
8992 int effective_action = action;
8993 int graphic = (direction == MV_NONE ?
8994 el_act2img(effective_element, effective_action) :
8995 el_act_dir2img(effective_element, effective_action,
8997 struct GraphicInfo *g = &graphic_info[graphic];
8998 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9002 int frame = getAnimationFrame(g->anim_frames,
9005 g->anim_start_frame,
9008 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9010 g_em->bitmap = src_bitmap;
9011 g_em->src_x = src_x;
9012 g_em->src_y = src_y;
9013 g_em->src_offset_x = 0;
9014 g_em->src_offset_y = 0;
9015 g_em->dst_offset_x = 0;
9016 g_em->dst_offset_y = 0;
9017 g_em->width = TILEX;
9018 g_em->height = TILEY;
9024 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9025 boolean any_player_moving,
9026 boolean any_player_snapping,
9027 boolean any_player_dropping)
9029 if (frame == 7 && !any_player_dropping)
9031 if (!local_player->was_waiting)
9033 if (!CheckSaveEngineSnapshotToList())
9036 local_player->was_waiting = TRUE;
9039 else if (any_player_moving || any_player_snapping || any_player_dropping)
9041 local_player->was_waiting = FALSE;
9045 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9046 boolean murphy_is_dropping)
9048 if (murphy_is_waiting)
9050 if (!local_player->was_waiting)
9052 if (!CheckSaveEngineSnapshotToList())
9055 local_player->was_waiting = TRUE;
9060 local_player->was_waiting = FALSE;
9064 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9065 boolean button_released)
9067 if (button_released)
9069 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9070 CheckSaveEngineSnapshotToList();
9072 else if (element_clicked)
9074 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9075 CheckSaveEngineSnapshotToList();
9077 game.snapshot.changed_action = TRUE;
9081 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9082 boolean any_player_moving,
9083 boolean any_player_snapping,
9084 boolean any_player_dropping)
9086 if (tape.single_step && tape.recording && !tape.pausing)
9087 if (frame == 7 && !any_player_dropping)
9088 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9090 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9091 any_player_snapping, any_player_dropping);
9094 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9095 boolean murphy_is_dropping)
9097 boolean murphy_starts_dropping = FALSE;
9100 for (i = 0; i < MAX_PLAYERS; i++)
9101 if (stored_player[i].force_dropping)
9102 murphy_starts_dropping = TRUE;
9104 if (tape.single_step && tape.recording && !tape.pausing)
9105 if (murphy_is_waiting && !murphy_starts_dropping)
9106 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9108 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9111 void CheckSingleStepMode_MM(boolean element_clicked,
9112 boolean button_released)
9114 if (tape.single_step && tape.recording && !tape.pausing)
9115 if (button_released)
9116 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9118 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9121 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9122 int graphic, int sync_frame, int x, int y)
9124 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9126 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9129 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9131 return (IS_NEXT_FRAME(sync_frame, graphic));
9134 int getGraphicInfo_Delay(int graphic)
9136 return graphic_info[graphic].anim_delay;
9139 void PlayMenuSoundExt(int sound)
9141 if (sound == SND_UNDEFINED)
9144 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9145 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9148 if (IS_LOOP_SOUND(sound))
9149 PlaySoundLoop(sound);
9154 void PlayMenuSound(void)
9156 PlayMenuSoundExt(menu.sound[game_status]);
9159 void PlayMenuSoundStereo(int sound, int stereo_position)
9161 if (sound == SND_UNDEFINED)
9164 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9165 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9168 if (IS_LOOP_SOUND(sound))
9169 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9171 PlaySoundStereo(sound, stereo_position);
9174 void PlayMenuSoundIfLoopExt(int sound)
9176 if (sound == SND_UNDEFINED)
9179 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9180 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9183 if (IS_LOOP_SOUND(sound))
9184 PlaySoundLoop(sound);
9187 void PlayMenuSoundIfLoop(void)
9189 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9192 void PlayMenuMusicExt(int music)
9194 if (music == MUS_UNDEFINED)
9197 if (!setup.sound_music)
9200 if (IS_LOOP_MUSIC(music))
9201 PlayMusicLoop(music);
9206 void PlayMenuMusic(void)
9208 char *curr_music = getCurrentlyPlayingMusicFilename();
9209 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9211 if (!strEqual(curr_music, next_music))
9212 PlayMenuMusicExt(menu.music[game_status]);
9215 void PlayMenuSoundsAndMusic(void)
9221 static void FadeMenuSounds(void)
9226 static void FadeMenuMusic(void)
9228 char *curr_music = getCurrentlyPlayingMusicFilename();
9229 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9231 if (!strEqual(curr_music, next_music))
9235 void FadeMenuSoundsAndMusic(void)
9241 void PlaySoundActivating(void)
9244 PlaySound(SND_MENU_ITEM_ACTIVATING);
9248 void PlaySoundSelecting(void)
9251 PlaySound(SND_MENU_ITEM_SELECTING);
9255 void ToggleFullscreenIfNeeded(void)
9257 // if setup and video fullscreen state are already matching, nothing do do
9258 if (setup.fullscreen == video.fullscreen_enabled ||
9259 !video.fullscreen_available)
9262 SDLSetWindowFullscreen(setup.fullscreen);
9264 // set setup value according to successfully changed fullscreen mode
9265 setup.fullscreen = video.fullscreen_enabled;
9268 void ChangeWindowScalingIfNeeded(void)
9270 // if setup and video window scaling are already matching, nothing do do
9271 if (setup.window_scaling_percent == video.window_scaling_percent ||
9272 video.fullscreen_enabled)
9275 SDLSetWindowScaling(setup.window_scaling_percent);
9277 // set setup value according to successfully changed window scaling
9278 setup.window_scaling_percent = video.window_scaling_percent;
9281 void ChangeVsyncModeIfNeeded(void)
9283 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9284 int video_vsync_mode = video.vsync_mode;
9286 // if setup and video vsync mode are already matching, nothing do do
9287 if (setup_vsync_mode == video_vsync_mode)
9290 // if renderer is using OpenGL, vsync mode can directly be changed
9291 SDLSetScreenVsyncMode(setup.vsync_mode);
9293 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9294 if (video.vsync_mode == video_vsync_mode)
9296 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9298 // save backbuffer content which gets lost when re-creating screen
9299 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9301 // force re-creating screen and renderer to set new vsync mode
9302 video.fullscreen_enabled = !setup.fullscreen;
9304 // when creating new renderer, destroy textures linked to old renderer
9305 FreeAllImageTextures(); // needs old renderer to free the textures
9307 // re-create screen and renderer (including change of vsync mode)
9308 ChangeVideoModeIfNeeded(setup.fullscreen);
9310 // set setup value according to successfully changed fullscreen mode
9311 setup.fullscreen = video.fullscreen_enabled;
9313 // restore backbuffer content from temporary backbuffer backup bitmap
9314 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9315 FreeBitmap(tmp_backbuffer);
9317 // update visible window/screen
9318 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9320 // when changing vsync mode, re-create textures for new renderer
9321 InitImageTextures();
9324 // set setup value according to successfully changed vsync mode
9325 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9328 static void JoinRectangles(int *x, int *y, int *width, int *height,
9329 int x2, int y2, int width2, int height2)
9331 // do not join with "off-screen" rectangle
9332 if (x2 == -1 || y2 == -1)
9337 *width = MAX(*width, width2);
9338 *height = MAX(*height, height2);
9341 void SetAnimStatus(int anim_status_new)
9343 if (anim_status_new == GAME_MODE_MAIN)
9344 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9345 else if (anim_status_new == GAME_MODE_SCORES)
9346 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9348 global.anim_status_next = anim_status_new;
9350 // directly set screen modes that are entered without fading
9351 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9352 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9353 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9354 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9355 global.anim_status = global.anim_status_next;
9358 void SetGameStatus(int game_status_new)
9360 if (game_status_new != game_status)
9361 game_status_last_screen = game_status;
9363 game_status = game_status_new;
9365 SetAnimStatus(game_status_new);
9368 void SetFontStatus(int game_status_new)
9370 static int last_game_status = -1;
9372 if (game_status_new != -1)
9374 // set game status for font use after storing last game status
9375 last_game_status = game_status;
9376 game_status = game_status_new;
9380 // reset game status after font use from last stored game status
9381 game_status = last_game_status;
9385 void ResetFontStatus(void)
9390 void SetLevelSetInfo(char *identifier, int level_nr)
9392 setString(&levelset.identifier, identifier);
9394 levelset.level_nr = level_nr;
9397 boolean CheckIfAllViewportsHaveChanged(void)
9399 // if game status has not changed, viewports have not changed either
9400 if (game_status == game_status_last)
9403 // check if all viewports have changed with current game status
9405 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9406 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9407 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9408 int new_real_sx = vp_playfield->x;
9409 int new_real_sy = vp_playfield->y;
9410 int new_full_sxsize = vp_playfield->width;
9411 int new_full_sysize = vp_playfield->height;
9412 int new_dx = vp_door_1->x;
9413 int new_dy = vp_door_1->y;
9414 int new_dxsize = vp_door_1->width;
9415 int new_dysize = vp_door_1->height;
9416 int new_vx = vp_door_2->x;
9417 int new_vy = vp_door_2->y;
9418 int new_vxsize = vp_door_2->width;
9419 int new_vysize = vp_door_2->height;
9421 boolean playfield_viewport_has_changed =
9422 (new_real_sx != REAL_SX ||
9423 new_real_sy != REAL_SY ||
9424 new_full_sxsize != FULL_SXSIZE ||
9425 new_full_sysize != FULL_SYSIZE);
9427 boolean door_1_viewport_has_changed =
9430 new_dxsize != DXSIZE ||
9431 new_dysize != DYSIZE);
9433 boolean door_2_viewport_has_changed =
9436 new_vxsize != VXSIZE ||
9437 new_vysize != VYSIZE ||
9438 game_status_last == GAME_MODE_EDITOR);
9440 return (playfield_viewport_has_changed &&
9441 door_1_viewport_has_changed &&
9442 door_2_viewport_has_changed);
9445 boolean CheckFadeAll(void)
9447 return (CheckIfGlobalBorderHasChanged() ||
9448 CheckIfAllViewportsHaveChanged());
9451 void ChangeViewportPropertiesIfNeeded(void)
9453 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9454 FALSE : setup.small_game_graphics);
9455 int gfx_game_mode = game_status;
9456 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9458 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9459 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9460 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9461 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9462 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9463 int new_win_xsize = vp_window->width;
9464 int new_win_ysize = vp_window->height;
9465 int border_left = vp_playfield->border_left;
9466 int border_right = vp_playfield->border_right;
9467 int border_top = vp_playfield->border_top;
9468 int border_bottom = vp_playfield->border_bottom;
9469 int new_sx = vp_playfield->x + border_left;
9470 int new_sy = vp_playfield->y + border_top;
9471 int new_sxsize = vp_playfield->width - border_left - border_right;
9472 int new_sysize = vp_playfield->height - border_top - border_bottom;
9473 int new_real_sx = vp_playfield->x;
9474 int new_real_sy = vp_playfield->y;
9475 int new_full_sxsize = vp_playfield->width;
9476 int new_full_sysize = vp_playfield->height;
9477 int new_dx = vp_door_1->x;
9478 int new_dy = vp_door_1->y;
9479 int new_dxsize = vp_door_1->width;
9480 int new_dysize = vp_door_1->height;
9481 int new_vx = vp_door_2->x;
9482 int new_vy = vp_door_2->y;
9483 int new_vxsize = vp_door_2->width;
9484 int new_vysize = vp_door_2->height;
9485 int new_ex = vp_door_3->x;
9486 int new_ey = vp_door_3->y;
9487 int new_exsize = vp_door_3->width;
9488 int new_eysize = vp_door_3->height;
9489 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9490 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9491 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9492 int new_scr_fieldx = new_sxsize / tilesize;
9493 int new_scr_fieldy = new_sysize / tilesize;
9494 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9495 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9496 boolean init_gfx_buffers = FALSE;
9497 boolean init_video_buffer = FALSE;
9498 boolean init_gadgets_and_anims = FALSE;
9499 boolean init_em_graphics = FALSE;
9501 if (new_win_xsize != WIN_XSIZE ||
9502 new_win_ysize != WIN_YSIZE)
9504 WIN_XSIZE = new_win_xsize;
9505 WIN_YSIZE = new_win_ysize;
9507 init_video_buffer = TRUE;
9508 init_gfx_buffers = TRUE;
9509 init_gadgets_and_anims = TRUE;
9511 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9514 if (new_scr_fieldx != SCR_FIELDX ||
9515 new_scr_fieldy != SCR_FIELDY)
9517 // this always toggles between MAIN and GAME when using small tile size
9519 SCR_FIELDX = new_scr_fieldx;
9520 SCR_FIELDY = new_scr_fieldy;
9522 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9533 new_sxsize != SXSIZE ||
9534 new_sysize != SYSIZE ||
9535 new_dxsize != DXSIZE ||
9536 new_dysize != DYSIZE ||
9537 new_vxsize != VXSIZE ||
9538 new_vysize != VYSIZE ||
9539 new_exsize != EXSIZE ||
9540 new_eysize != EYSIZE ||
9541 new_real_sx != REAL_SX ||
9542 new_real_sy != REAL_SY ||
9543 new_full_sxsize != FULL_SXSIZE ||
9544 new_full_sysize != FULL_SYSIZE ||
9545 new_tilesize_var != TILESIZE_VAR
9548 // ------------------------------------------------------------------------
9549 // determine next fading area for changed viewport definitions
9550 // ------------------------------------------------------------------------
9552 // start with current playfield area (default fading area)
9555 FADE_SXSIZE = FULL_SXSIZE;
9556 FADE_SYSIZE = FULL_SYSIZE;
9558 // add new playfield area if position or size has changed
9559 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9560 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9562 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9563 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9566 // add current and new door 1 area if position or size has changed
9567 if (new_dx != DX || new_dy != DY ||
9568 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9570 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9571 DX, DY, DXSIZE, DYSIZE);
9572 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9573 new_dx, new_dy, new_dxsize, new_dysize);
9576 // add current and new door 2 area if position or size has changed
9577 if (new_vx != VX || new_vy != VY ||
9578 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9580 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9581 VX, VY, VXSIZE, VYSIZE);
9582 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9583 new_vx, new_vy, new_vxsize, new_vysize);
9586 // ------------------------------------------------------------------------
9587 // handle changed tile size
9588 // ------------------------------------------------------------------------
9590 if (new_tilesize_var != TILESIZE_VAR)
9592 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9594 // changing tile size invalidates scroll values of engine snapshots
9595 FreeEngineSnapshotSingle();
9597 // changing tile size requires update of graphic mapping for EM engine
9598 init_em_graphics = TRUE;
9609 SXSIZE = new_sxsize;
9610 SYSIZE = new_sysize;
9611 DXSIZE = new_dxsize;
9612 DYSIZE = new_dysize;
9613 VXSIZE = new_vxsize;
9614 VYSIZE = new_vysize;
9615 EXSIZE = new_exsize;
9616 EYSIZE = new_eysize;
9617 REAL_SX = new_real_sx;
9618 REAL_SY = new_real_sy;
9619 FULL_SXSIZE = new_full_sxsize;
9620 FULL_SYSIZE = new_full_sysize;
9621 TILESIZE_VAR = new_tilesize_var;
9623 init_gfx_buffers = TRUE;
9624 init_gadgets_and_anims = TRUE;
9626 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9627 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9630 if (init_gfx_buffers)
9632 // Debug("tools:viewport", "init_gfx_buffers");
9634 SCR_FIELDX = new_scr_fieldx_buffers;
9635 SCR_FIELDY = new_scr_fieldy_buffers;
9639 SCR_FIELDX = new_scr_fieldx;
9640 SCR_FIELDY = new_scr_fieldy;
9642 SetDrawDeactivationMask(REDRAW_NONE);
9643 SetDrawBackgroundMask(REDRAW_FIELD);
9646 if (init_video_buffer)
9648 // Debug("tools:viewport", "init_video_buffer");
9650 FreeAllImageTextures(); // needs old renderer to free the textures
9652 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9653 InitImageTextures();
9656 if (init_gadgets_and_anims)
9658 // Debug("tools:viewport", "init_gadgets_and_anims");
9661 InitGlobalAnimations();
9664 if (init_em_graphics)
9666 InitGraphicInfo_EM();