1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int correctLevelPosX_EM(int lx)
203 int correctLevelPosY_EM(int ly)
210 int getFieldbufferOffsetX_RND(int dir, int pos)
212 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
213 int dx = (dir & MV_HORIZONTAL ? pos : 0);
214 int dx_var = dx * TILESIZE_VAR / TILESIZE;
217 if (EVEN(SCR_FIELDX))
219 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 int getFieldbufferOffsetY_RND(int dir, int pos)
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (dir & MV_VERTICAL ? pos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
253 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
255 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
256 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
258 fy += (dy_var > 0 ? TILEY_VAR : 0);
265 if (full_lev_fieldy <= SCR_FIELDY)
267 if (EVEN(SCR_FIELDY))
268 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
270 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
276 static int getLevelFromScreenX_RND(int sx)
278 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
281 int lx = LEVELX((px + dx) / TILESIZE_VAR);
286 static int getLevelFromScreenY_RND(int sy)
288 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
291 int ly = LEVELY((py + dy) / TILESIZE_VAR);
296 static int getLevelFromScreenX_EM(int sx)
298 int level_xsize = level.native_em_level->cav->width;
299 int full_xsize = level_xsize * TILESIZE_VAR;
301 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
303 int fx = getFieldbufferOffsetX_EM();
306 int lx = LEVELX((px + dx) / TILESIZE_VAR);
308 lx = correctLevelPosX_EM(lx);
313 static int getLevelFromScreenY_EM(int sy)
315 int level_ysize = level.native_em_level->cav->height;
316 int full_ysize = level_ysize * TILESIZE_VAR;
318 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
320 int fy = getFieldbufferOffsetY_EM();
323 int ly = LEVELY((py + dy) / TILESIZE_VAR);
325 ly = correctLevelPosY_EM(ly);
330 static int getLevelFromScreenX_SP(int sx)
332 int menBorder = setup.sp_show_border_elements;
333 int level_xsize = level.native_sp_level->width;
334 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
336 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
338 int fx = getFieldbufferOffsetX_SP();
341 int lx = LEVELX((px + dx) / TILESIZE_VAR);
346 static int getLevelFromScreenY_SP(int sy)
348 int menBorder = setup.sp_show_border_elements;
349 int level_ysize = level.native_sp_level->height;
350 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
352 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
354 int fy = getFieldbufferOffsetY_SP();
357 int ly = LEVELY((py + dy) / TILESIZE_VAR);
362 static int getLevelFromScreenX_MM(int sx)
364 int level_xsize = level.native_mm_level->fieldx;
365 int full_xsize = level_xsize * TILESIZE_VAR;
367 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
370 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
375 static int getLevelFromScreenY_MM(int sy)
377 int level_ysize = level.native_mm_level->fieldy;
378 int full_ysize = level_ysize * TILESIZE_VAR;
380 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
383 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
388 int getLevelFromScreenX(int x)
390 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
391 return getLevelFromScreenX_EM(x);
392 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
393 return getLevelFromScreenX_SP(x);
394 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
395 return getLevelFromScreenX_MM(x);
397 return getLevelFromScreenX_RND(x);
400 int getLevelFromScreenY(int y)
402 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
403 return getLevelFromScreenY_EM(y);
404 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
405 return getLevelFromScreenY_SP(y);
406 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
407 return getLevelFromScreenY_MM(y);
409 return getLevelFromScreenY_RND(y);
412 void DumpTile(int x, int y)
418 printf_line("-", 79);
419 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
420 printf_line("-", 79);
422 if (!IN_LEV_FIELD(x, y))
424 printf("(not in level field)\n");
430 token_name = element_info[Feld[x][y]].token_name;
432 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
433 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
434 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
435 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
436 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
437 printf(" MovPos: %d\n", MovPos[x][y]);
438 printf(" MovDir: %d\n", MovDir[x][y]);
439 printf(" MovDelay: %d\n", MovDelay[x][y]);
440 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
441 printf(" CustomValue: %d\n", CustomValue[x][y]);
442 printf(" GfxElement: %d\n", GfxElement[x][y]);
443 printf(" GfxAction: %d\n", GfxAction[x][y]);
444 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
445 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
449 void DumpTileFromScreen(int sx, int sy)
451 int lx = getLevelFromScreenX(sx);
452 int ly = getLevelFromScreenY(sy);
457 void SetDrawtoField(int mode)
459 if (mode == DRAW_TO_FIELDBUFFER)
465 BX2 = SCR_FIELDX + 1;
466 BY2 = SCR_FIELDY + 1;
468 drawto_field = fieldbuffer;
470 else // DRAW_TO_BACKBUFFER
476 BX2 = SCR_FIELDX - 1;
477 BY2 = SCR_FIELDY - 1;
479 drawto_field = backbuffer;
483 static void RedrawPlayfield_RND(void)
485 if (game.envelope_active)
488 DrawLevel(REDRAW_ALL);
492 void RedrawPlayfield(void)
494 if (game_status != GAME_MODE_PLAYING)
497 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
498 RedrawPlayfield_EM(TRUE);
499 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
500 RedrawPlayfield_SP(TRUE);
501 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
502 RedrawPlayfield_MM();
503 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
504 RedrawPlayfield_RND();
506 BlitScreenToBitmap(backbuffer);
508 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
512 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
515 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
516 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
518 if (x == -1 && y == -1)
521 if (draw_target == DRAW_TO_SCREEN)
522 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
524 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
527 static void DrawMaskedBorderExt_FIELD(int draw_target)
529 if (global.border_status >= GAME_MODE_MAIN &&
530 global.border_status <= GAME_MODE_PLAYING &&
531 border.draw_masked[global.border_status])
532 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
536 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
538 // when drawing to backbuffer, never draw border over open doors
539 if (draw_target == DRAW_TO_BACKBUFFER &&
540 (GetDoorState() & DOOR_OPEN_1))
543 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
544 (global.border_status != GAME_MODE_EDITOR ||
545 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
546 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
549 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
551 // when drawing to backbuffer, never draw border over open doors
552 if (draw_target == DRAW_TO_BACKBUFFER &&
553 (GetDoorState() & DOOR_OPEN_2))
556 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
557 global.border_status != GAME_MODE_EDITOR)
558 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
561 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
563 // currently not available
566 static void DrawMaskedBorderExt_ALL(int draw_target)
568 DrawMaskedBorderExt_FIELD(draw_target);
569 DrawMaskedBorderExt_DOOR_1(draw_target);
570 DrawMaskedBorderExt_DOOR_2(draw_target);
571 DrawMaskedBorderExt_DOOR_3(draw_target);
574 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
576 // never draw masked screen borders on borderless screens
577 if (global.border_status == GAME_MODE_LOADING ||
578 global.border_status == GAME_MODE_TITLE)
581 if (redraw_mask & REDRAW_ALL)
582 DrawMaskedBorderExt_ALL(draw_target);
585 if (redraw_mask & REDRAW_FIELD)
586 DrawMaskedBorderExt_FIELD(draw_target);
587 if (redraw_mask & REDRAW_DOOR_1)
588 DrawMaskedBorderExt_DOOR_1(draw_target);
589 if (redraw_mask & REDRAW_DOOR_2)
590 DrawMaskedBorderExt_DOOR_2(draw_target);
591 if (redraw_mask & REDRAW_DOOR_3)
592 DrawMaskedBorderExt_DOOR_3(draw_target);
596 void DrawMaskedBorder_FIELD(void)
598 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
601 void DrawMaskedBorder(int redraw_mask)
603 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
606 void DrawMaskedBorderToTarget(int draw_target)
608 if (draw_target == DRAW_TO_BACKBUFFER ||
609 draw_target == DRAW_TO_SCREEN)
611 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
615 int last_border_status = global.border_status;
617 if (draw_target == DRAW_TO_FADE_SOURCE)
619 global.border_status = gfx.fade_border_source_status;
620 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
622 else if (draw_target == DRAW_TO_FADE_TARGET)
624 global.border_status = gfx.fade_border_target_status;
625 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
628 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
630 global.border_status = last_border_status;
631 gfx.masked_border_bitmap_ptr = backbuffer;
635 void DrawTileCursor(int draw_target)
641 int graphic = IMG_GLOBAL_TILE_CURSOR;
643 int tilesize = TILESIZE_VAR;
644 int width = tilesize;
645 int height = tilesize;
647 if (game_status != GAME_MODE_PLAYING)
650 if (!tile_cursor.enabled ||
654 if (tile_cursor.moving)
656 int step = TILESIZE_VAR / 4;
657 int dx = tile_cursor.target_x - tile_cursor.x;
658 int dy = tile_cursor.target_y - tile_cursor.y;
661 tile_cursor.x = tile_cursor.target_x;
663 tile_cursor.x += SIGN(dx) * step;
666 tile_cursor.y = tile_cursor.target_y;
668 tile_cursor.y += SIGN(dy) * step;
670 if (tile_cursor.x == tile_cursor.target_x &&
671 tile_cursor.y == tile_cursor.target_y)
672 tile_cursor.moving = FALSE;
675 dst_x = tile_cursor.x;
676 dst_y = tile_cursor.y;
678 frame = getGraphicAnimationFrame(graphic, -1);
680 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
683 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
684 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
686 if (draw_target == DRAW_TO_SCREEN)
687 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
689 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
693 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
695 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
698 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
700 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
701 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
703 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
706 void BlitScreenToBitmap(Bitmap *target_bitmap)
708 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
709 BlitScreenToBitmap_EM(target_bitmap);
710 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
711 BlitScreenToBitmap_SP(target_bitmap);
712 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
713 BlitScreenToBitmap_MM(target_bitmap);
714 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
715 BlitScreenToBitmap_RND(target_bitmap);
717 redraw_mask |= REDRAW_FIELD;
720 static void DrawFramesPerSecond(void)
723 int font_nr = FONT_TEXT_2;
724 int font_width = getFontWidth(font_nr);
725 int draw_deactivation_mask = GetDrawDeactivationMask();
726 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
728 // draw FPS with leading space (needed if field buffer deactivated)
729 sprintf(text, " %04.1f fps", global.frames_per_second);
731 // override draw deactivation mask (required for invisible warp mode)
732 SetDrawDeactivationMask(REDRAW_NONE);
734 // draw opaque FPS if field buffer deactivated, else draw masked FPS
735 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
736 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
738 // set draw deactivation mask to previous value
739 SetDrawDeactivationMask(draw_deactivation_mask);
741 // force full-screen redraw in this frame
742 redraw_mask = REDRAW_ALL;
746 static void PrintFrameTimeDebugging(void)
748 static unsigned int last_counter = 0;
749 unsigned int counter = Counter();
750 int diff_1 = counter - last_counter;
751 int diff_2 = diff_1 - GAME_FRAME_DELAY;
753 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
754 char diff_bar[2 * diff_2_max + 5];
758 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
762 i >= diff_2_max - diff_2_cut ? '-' : ' ');
764 diff_bar[pos++] = '|';
766 for (i = 0; i < diff_2_max; i++)
767 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
769 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
771 diff_bar[pos++] = '\0';
773 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
776 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
779 last_counter = counter;
783 static int unifiedRedrawMask(int mask)
785 if (mask & REDRAW_ALL)
788 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
794 static boolean equalRedrawMasks(int mask_1, int mask_2)
796 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
799 void BackToFront(void)
801 static int last_redraw_mask = REDRAW_NONE;
803 // force screen redraw in every frame to continue drawing global animations
804 // (but always use the last redraw mask to prevent unwanted side effects)
805 if (redraw_mask == REDRAW_NONE)
806 redraw_mask = last_redraw_mask;
808 last_redraw_mask = redraw_mask;
811 // masked border now drawn immediately when blitting backbuffer to window
813 // draw masked border to all viewports, if defined
814 DrawMaskedBorder(redraw_mask);
817 // draw frames per second (only if debug mode is enabled)
818 if (redraw_mask & REDRAW_FPS)
819 DrawFramesPerSecond();
821 // remove playfield redraw before potentially merging with doors redraw
822 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
823 redraw_mask &= ~REDRAW_FIELD;
825 // redraw complete window if both playfield and (some) doors need redraw
826 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
827 redraw_mask = REDRAW_ALL;
829 /* although redrawing the whole window would be fine for normal gameplay,
830 being able to only redraw the playfield is required for deactivating
831 certain drawing areas (mainly playfield) to work, which is needed for
832 warp-forward to be fast enough (by skipping redraw of most frames) */
834 if (redraw_mask & REDRAW_ALL)
836 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
838 else if (redraw_mask & REDRAW_FIELD)
840 BlitBitmap(backbuffer, window,
841 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
843 else if (redraw_mask & REDRAW_DOORS)
845 // merge door areas to prevent calling screen redraw more than once
851 if (redraw_mask & REDRAW_DOOR_1)
855 x2 = MAX(x2, DX + DXSIZE);
856 y2 = MAX(y2, DY + DYSIZE);
859 if (redraw_mask & REDRAW_DOOR_2)
863 x2 = MAX(x2, VX + VXSIZE);
864 y2 = MAX(y2, VY + VYSIZE);
867 if (redraw_mask & REDRAW_DOOR_3)
871 x2 = MAX(x2, EX + EXSIZE);
872 y2 = MAX(y2, EY + EYSIZE);
875 // make sure that at least one pixel is blitted, and inside the screen
876 // (else nothing is blitted, causing the animations not to be updated)
877 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
878 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
879 x2 = MIN(MAX(1, x2), WIN_XSIZE);
880 y2 = MIN(MAX(1, y2), WIN_YSIZE);
882 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
885 redraw_mask = REDRAW_NONE;
888 PrintFrameTimeDebugging();
892 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
894 unsigned int frame_delay_value_old = GetVideoFrameDelay();
896 SetVideoFrameDelay(frame_delay_value);
900 SetVideoFrameDelay(frame_delay_value_old);
903 static int fade_type_skip = FADE_TYPE_NONE;
905 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
907 void (*draw_border_function)(void) = NULL;
908 int x, y, width, height;
909 int fade_delay, post_delay;
911 if (fade_type == FADE_TYPE_FADE_OUT)
913 if (fade_type_skip != FADE_TYPE_NONE)
915 // skip all fade operations until specified fade operation
916 if (fade_type & fade_type_skip)
917 fade_type_skip = FADE_TYPE_NONE;
922 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
926 redraw_mask |= fade_mask;
928 if (fade_type == FADE_TYPE_SKIP)
930 fade_type_skip = fade_mode;
935 fade_delay = fading.fade_delay;
936 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
938 if (fade_type_skip != FADE_TYPE_NONE)
940 // skip all fade operations until specified fade operation
941 if (fade_type & fade_type_skip)
942 fade_type_skip = FADE_TYPE_NONE;
947 if (global.autoplay_leveldir)
952 if (fade_mask == REDRAW_FIELD)
957 height = FADE_SYSIZE;
959 if (border.draw_masked_when_fading)
960 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
962 DrawMaskedBorder_FIELD(); // draw once
972 // when switching screens without fading, set fade delay to zero
973 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
976 // do not display black frame when fading out without fade delay
977 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
985 ClearAutoRepeatKeyEvents();
988 static void SetScreenStates_BeforeFadingIn(void)
990 // temporarily set screen mode for animations to screen after fading in
991 global.anim_status = global.anim_status_next;
993 // store backbuffer with all animations that will be started after fading in
994 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
996 // set screen mode for animations back to fading
997 global.anim_status = GAME_MODE_PSEUDO_FADING;
1000 static void SetScreenStates_AfterFadingIn(void)
1002 // store new source screen (to use correct masked border for fading)
1003 gfx.fade_border_source_status = global.border_status;
1005 global.anim_status = global.anim_status_next;
1008 static void SetScreenStates_BeforeFadingOut(void)
1010 // store new target screen (to use correct masked border for fading)
1011 gfx.fade_border_target_status = game_status;
1013 // set screen mode for animations to fading
1014 global.anim_status = GAME_MODE_PSEUDO_FADING;
1016 // store backbuffer with all animations that will be stopped for fading out
1017 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1020 static void SetScreenStates_AfterFadingOut(void)
1022 global.border_status = game_status;
1025 void FadeIn(int fade_mask)
1027 SetScreenStates_BeforeFadingIn();
1030 DrawMaskedBorder(REDRAW_ALL);
1033 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1034 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1036 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1040 FADE_SXSIZE = FULL_SXSIZE;
1041 FADE_SYSIZE = FULL_SYSIZE;
1043 // activate virtual buttons depending on upcoming game status
1044 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1045 game_status == GAME_MODE_PLAYING && !tape.playing)
1046 SetOverlayActive(TRUE);
1048 SetScreenStates_AfterFadingIn();
1050 // force update of global animation status in case of rapid screen changes
1051 redraw_mask = REDRAW_ALL;
1055 void FadeOut(int fade_mask)
1057 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1058 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1059 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1062 SetScreenStates_BeforeFadingOut();
1064 SetTileCursorActive(FALSE);
1065 SetOverlayActive(FALSE);
1068 DrawMaskedBorder(REDRAW_ALL);
1071 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1072 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1074 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1076 SetScreenStates_AfterFadingOut();
1079 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1081 static struct TitleFadingInfo fading_leave_stored;
1084 fading_leave_stored = fading_leave;
1086 fading = fading_leave_stored;
1089 void FadeSetEnterMenu(void)
1091 fading = menu.enter_menu;
1093 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1096 void FadeSetLeaveMenu(void)
1098 fading = menu.leave_menu;
1100 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1103 void FadeSetEnterScreen(void)
1105 fading = menu.enter_screen[game_status];
1107 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1110 void FadeSetNextScreen(void)
1112 fading = menu.next_screen[game_status];
1114 // (do not overwrite fade mode set by FadeSetEnterScreen)
1115 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1118 void FadeSetLeaveScreen(void)
1120 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1123 void FadeSetFromType(int type)
1125 if (type & TYPE_ENTER_SCREEN)
1126 FadeSetEnterScreen();
1127 else if (type & TYPE_ENTER)
1129 else if (type & TYPE_LEAVE)
1133 void FadeSetDisabled(void)
1135 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1137 fading = fading_none;
1140 void FadeSkipNextFadeIn(void)
1142 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1145 void FadeSkipNextFadeOut(void)
1147 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1150 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1152 if (graphic == IMG_UNDEFINED)
1155 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1157 return (graphic_info[graphic].bitmap != NULL || redefined ?
1158 graphic_info[graphic].bitmap :
1159 graphic_info[default_graphic].bitmap);
1162 static Bitmap *getBackgroundBitmap(int graphic)
1164 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1167 static Bitmap *getGlobalBorderBitmap(int graphic)
1169 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1172 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1175 (status == GAME_MODE_MAIN ||
1176 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1177 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1178 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1179 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1182 return getGlobalBorderBitmap(graphic);
1185 void SetWindowBackgroundImageIfDefined(int graphic)
1187 if (graphic_info[graphic].bitmap)
1188 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1191 void SetMainBackgroundImageIfDefined(int graphic)
1193 if (graphic_info[graphic].bitmap)
1194 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1197 void SetDoorBackgroundImageIfDefined(int graphic)
1199 if (graphic_info[graphic].bitmap)
1200 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1203 void SetWindowBackgroundImage(int graphic)
1205 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1208 void SetMainBackgroundImage(int graphic)
1210 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1213 void SetDoorBackgroundImage(int graphic)
1215 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1218 void SetPanelBackground(void)
1220 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1222 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1223 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1225 SetDoorBackgroundBitmap(bitmap_db_panel);
1228 void DrawBackground(int x, int y, int width, int height)
1230 // "drawto" might still point to playfield buffer here (hall of fame)
1231 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1233 if (IN_GFX_FIELD_FULL(x, y))
1234 redraw_mask |= REDRAW_FIELD;
1235 else if (IN_GFX_DOOR_1(x, y))
1236 redraw_mask |= REDRAW_DOOR_1;
1237 else if (IN_GFX_DOOR_2(x, y))
1238 redraw_mask |= REDRAW_DOOR_2;
1239 else if (IN_GFX_DOOR_3(x, y))
1240 redraw_mask |= REDRAW_DOOR_3;
1243 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1245 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1247 if (font->bitmap == NULL)
1250 DrawBackground(x, y, width, height);
1253 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1255 struct GraphicInfo *g = &graphic_info[graphic];
1257 if (g->bitmap == NULL)
1260 DrawBackground(x, y, width, height);
1263 static int game_status_last = -1;
1264 static Bitmap *global_border_bitmap_last = NULL;
1265 static Bitmap *global_border_bitmap = NULL;
1266 static int real_sx_last = -1, real_sy_last = -1;
1267 static int full_sxsize_last = -1, full_sysize_last = -1;
1268 static int dx_last = -1, dy_last = -1;
1269 static int dxsize_last = -1, dysize_last = -1;
1270 static int vx_last = -1, vy_last = -1;
1271 static int vxsize_last = -1, vysize_last = -1;
1272 static int ex_last = -1, ey_last = -1;
1273 static int exsize_last = -1, eysize_last = -1;
1275 boolean CheckIfGlobalBorderHasChanged(void)
1277 // if game status has not changed, global border has not changed either
1278 if (game_status == game_status_last)
1281 // determine and store new global border bitmap for current game status
1282 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 return (global_border_bitmap_last != global_border_bitmap);
1287 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1289 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1290 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1292 // if game status has not changed, nothing has to be redrawn
1293 if (game_status == game_status_last)
1296 // redraw if last screen was title screen
1297 if (game_status_last == GAME_MODE_TITLE)
1300 // redraw if global screen border has changed
1301 if (CheckIfGlobalBorderHasChanged())
1304 // redraw if position or size of playfield area has changed
1305 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1306 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1309 // redraw if position or size of door area has changed
1310 if (dx_last != DX || dy_last != DY ||
1311 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1314 // redraw if position or size of tape area has changed
1315 if (vx_last != VX || vy_last != VY ||
1316 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1319 // redraw if position or size of editor area has changed
1320 if (ex_last != EX || ey_last != EY ||
1321 exsize_last != EXSIZE || eysize_last != EYSIZE)
1328 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1331 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1333 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1336 void RedrawGlobalBorder(void)
1338 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1340 RedrawGlobalBorderFromBitmap(bitmap);
1342 redraw_mask = REDRAW_ALL;
1345 static void RedrawGlobalBorderIfNeeded(void)
1347 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1348 if (game_status == game_status_last)
1352 // copy current draw buffer to later copy back areas that have not changed
1353 if (game_status_last != GAME_MODE_TITLE)
1354 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1356 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1357 if (CheckIfGlobalBorderRedrawIsNeeded())
1360 // redraw global screen border (or clear, if defined to be empty)
1361 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1363 if (game_status == GAME_MODE_EDITOR)
1364 DrawSpecialEditorDoor();
1366 // copy previous playfield and door areas, if they are defined on both
1367 // previous and current screen and if they still have the same size
1369 if (real_sx_last != -1 && real_sy_last != -1 &&
1370 REAL_SX != -1 && REAL_SY != -1 &&
1371 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1376 if (dx_last != -1 && dy_last != -1 &&
1377 DX != -1 && DY != -1 &&
1378 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1379 BlitBitmap(bitmap_db_store_1, backbuffer,
1380 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1382 if (game_status != GAME_MODE_EDITOR)
1384 if (vx_last != -1 && vy_last != -1 &&
1385 VX != -1 && VY != -1 &&
1386 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1387 BlitBitmap(bitmap_db_store_1, backbuffer,
1388 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1392 if (ex_last != -1 && ey_last != -1 &&
1393 EX != -1 && EY != -1 &&
1394 exsize_last == EXSIZE && eysize_last == EYSIZE)
1395 BlitBitmap(bitmap_db_store_1, backbuffer,
1396 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1399 redraw_mask = REDRAW_ALL;
1402 game_status_last = game_status;
1404 global_border_bitmap_last = global_border_bitmap;
1406 real_sx_last = REAL_SX;
1407 real_sy_last = REAL_SY;
1408 full_sxsize_last = FULL_SXSIZE;
1409 full_sysize_last = FULL_SYSIZE;
1412 dxsize_last = DXSIZE;
1413 dysize_last = DYSIZE;
1416 vxsize_last = VXSIZE;
1417 vysize_last = VYSIZE;
1420 exsize_last = EXSIZE;
1421 eysize_last = EYSIZE;
1424 void ClearField(void)
1426 RedrawGlobalBorderIfNeeded();
1428 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1429 // (when entering hall of fame after playing)
1430 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1432 // !!! maybe this should be done before clearing the background !!!
1433 if (game_status == GAME_MODE_PLAYING)
1435 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1436 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1440 SetDrawtoField(DRAW_TO_BACKBUFFER);
1444 void MarkTileDirty(int x, int y)
1446 redraw_mask |= REDRAW_FIELD;
1449 void SetBorderElement(void)
1453 BorderElement = EL_EMPTY;
1455 // only the R'n'D game engine may use an additional steelwall border
1456 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1459 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1461 for (x = 0; x < lev_fieldx; x++)
1463 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1464 BorderElement = EL_STEELWALL;
1466 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1472 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1473 int max_array_fieldx, int max_array_fieldy,
1474 short field[max_array_fieldx][max_array_fieldy],
1475 int max_fieldx, int max_fieldy)
1479 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1480 static int safety = 0;
1482 // check if starting field still has the desired content
1483 if (field[from_x][from_y] == fill_element)
1488 if (safety > max_fieldx * max_fieldy)
1489 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1491 old_element = field[from_x][from_y];
1492 field[from_x][from_y] = fill_element;
1494 for (i = 0; i < 4; i++)
1496 x = from_x + check[i][0];
1497 y = from_y + check[i][1];
1499 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1500 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1501 field, max_fieldx, max_fieldy);
1507 void FloodFillLevel(int from_x, int from_y, int fill_element,
1508 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1509 int max_fieldx, int max_fieldy)
1511 FloodFillLevelExt(from_x, from_y, fill_element,
1512 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1513 max_fieldx, max_fieldy);
1516 void SetRandomAnimationValue(int x, int y)
1518 gfx.anim_random_frame = GfxRandom[x][y];
1521 int getGraphicAnimationFrame(int graphic, int sync_frame)
1523 // animation synchronized with global frame counter, not move position
1524 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1525 sync_frame = FrameCounter;
1527 return getAnimationFrame(graphic_info[graphic].anim_frames,
1528 graphic_info[graphic].anim_delay,
1529 graphic_info[graphic].anim_mode,
1530 graphic_info[graphic].anim_start_frame,
1534 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1536 struct GraphicInfo *g = &graphic_info[graphic];
1537 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1539 if (tilesize == gfx.standard_tile_size)
1540 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1541 else if (tilesize == game.tile_size)
1542 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1544 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1547 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1548 boolean get_backside)
1550 struct GraphicInfo *g = &graphic_info[graphic];
1551 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1552 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1554 if (g->offset_y == 0) // frames are ordered horizontally
1556 int max_width = g->anim_frames_per_line * g->width;
1557 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1559 *x = pos % max_width;
1560 *y = src_y % g->height + pos / max_width * g->height;
1562 else if (g->offset_x == 0) // frames are ordered vertically
1564 int max_height = g->anim_frames_per_line * g->height;
1565 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1567 *x = src_x % g->width + pos / max_height * g->width;
1568 *y = pos % max_height;
1570 else // frames are ordered diagonally
1572 *x = src_x + frame * g->offset_x;
1573 *y = src_y + frame * g->offset_y;
1577 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1578 Bitmap **bitmap, int *x, int *y,
1579 boolean get_backside)
1581 struct GraphicInfo *g = &graphic_info[graphic];
1583 // if no graphics defined at all, use fallback graphics
1584 if (g->bitmaps == NULL)
1585 *g = graphic_info[IMG_CHAR_EXCLAM];
1587 // if no in-game graphics defined, always use standard graphic size
1588 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1589 tilesize = TILESIZE;
1591 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1592 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1594 *x = *x * tilesize / g->tile_size;
1595 *y = *y * tilesize / g->tile_size;
1598 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1599 Bitmap **bitmap, int *x, int *y)
1601 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1604 void getFixedGraphicSource(int graphic, int frame,
1605 Bitmap **bitmap, int *x, int *y)
1607 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1610 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1612 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1615 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1616 int *x, int *y, boolean get_backside)
1618 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1622 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1624 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1627 void DrawGraphic(int x, int y, int graphic, int frame)
1630 if (!IN_SCR_FIELD(x, y))
1632 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1633 printf("DrawGraphic(): This should never happen!\n");
1638 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1641 MarkTileDirty(x, y);
1644 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1647 if (!IN_SCR_FIELD(x, y))
1649 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1650 printf("DrawGraphic(): This should never happen!\n");
1655 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1657 MarkTileDirty(x, y);
1660 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1666 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1668 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1671 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1677 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1678 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1681 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1684 if (!IN_SCR_FIELD(x, y))
1686 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1687 printf("DrawGraphicThruMask(): This should never happen!\n");
1692 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1695 MarkTileDirty(x, y);
1698 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1701 if (!IN_SCR_FIELD(x, y))
1703 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1704 printf("DrawGraphicThruMask(): This should never happen!\n");
1709 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1711 MarkTileDirty(x, y);
1714 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1720 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1722 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1726 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1727 int graphic, int frame)
1732 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1734 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1738 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1740 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1742 MarkTileDirty(x / tilesize, y / tilesize);
1745 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1748 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1749 graphic, frame, tilesize);
1750 MarkTileDirty(x / tilesize, y / tilesize);
1753 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1759 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1760 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1763 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1764 int frame, int tilesize)
1769 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1770 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1773 void DrawMiniGraphic(int x, int y, int graphic)
1775 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1776 MarkTileDirty(x / 2, y / 2);
1779 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1784 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1785 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1788 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1789 int graphic, int frame,
1790 int cut_mode, int mask_mode)
1795 int width = TILEX, height = TILEY;
1798 if (dx || dy) // shifted graphic
1800 if (x < BX1) // object enters playfield from the left
1807 else if (x > BX2) // object enters playfield from the right
1813 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1819 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1821 else if (dx) // general horizontal movement
1822 MarkTileDirty(x + SIGN(dx), y);
1824 if (y < BY1) // object enters playfield from the top
1826 if (cut_mode == CUT_BELOW) // object completely above top border
1834 else if (y > BY2) // object enters playfield from the bottom
1840 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1846 else if (dy > 0 && cut_mode == CUT_ABOVE)
1848 if (y == BY2) // object completely above bottom border
1854 MarkTileDirty(x, y + 1);
1855 } // object leaves playfield to the bottom
1856 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1858 else if (dy) // general vertical movement
1859 MarkTileDirty(x, y + SIGN(dy));
1863 if (!IN_SCR_FIELD(x, y))
1865 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1866 printf("DrawGraphicShifted(): This should never happen!\n");
1871 width = width * TILESIZE_VAR / TILESIZE;
1872 height = height * TILESIZE_VAR / TILESIZE;
1873 cx = cx * TILESIZE_VAR / TILESIZE;
1874 cy = cy * TILESIZE_VAR / TILESIZE;
1875 dx = dx * TILESIZE_VAR / TILESIZE;
1876 dy = dy * TILESIZE_VAR / TILESIZE;
1878 if (width > 0 && height > 0)
1880 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1885 dst_x = FX + x * TILEX_VAR + dx;
1886 dst_y = FY + y * TILEY_VAR + dy;
1888 if (mask_mode == USE_MASKING)
1889 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1892 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1895 MarkTileDirty(x, y);
1899 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1900 int graphic, int frame,
1901 int cut_mode, int mask_mode)
1906 int width = TILEX_VAR, height = TILEY_VAR;
1909 int x2 = x + SIGN(dx);
1910 int y2 = y + SIGN(dy);
1912 // movement with two-tile animations must be sync'ed with movement position,
1913 // not with current GfxFrame (which can be higher when using slow movement)
1914 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1915 int anim_frames = graphic_info[graphic].anim_frames;
1917 // (we also need anim_delay here for movement animations with less frames)
1918 int anim_delay = graphic_info[graphic].anim_delay;
1919 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1921 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1922 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1924 // re-calculate animation frame for two-tile movement animation
1925 frame = getGraphicAnimationFrame(graphic, sync_frame);
1927 // check if movement start graphic inside screen area and should be drawn
1928 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1930 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1932 dst_x = FX + x1 * TILEX_VAR;
1933 dst_y = FY + y1 * TILEY_VAR;
1935 if (mask_mode == USE_MASKING)
1936 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1939 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1942 MarkTileDirty(x1, y1);
1945 // check if movement end graphic inside screen area and should be drawn
1946 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1948 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1950 dst_x = FX + x2 * TILEX_VAR;
1951 dst_y = FY + y2 * TILEY_VAR;
1953 if (mask_mode == USE_MASKING)
1954 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1957 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1960 MarkTileDirty(x2, y2);
1964 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1965 int graphic, int frame,
1966 int cut_mode, int mask_mode)
1970 DrawGraphic(x, y, graphic, frame);
1975 if (graphic_info[graphic].double_movement) // EM style movement images
1976 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1978 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1981 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1982 int graphic, int frame, int cut_mode)
1984 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1987 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1988 int cut_mode, int mask_mode)
1990 int lx = LEVELX(x), ly = LEVELY(y);
1994 if (IN_LEV_FIELD(lx, ly))
1996 SetRandomAnimationValue(lx, ly);
1998 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1999 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2001 // do not use double (EM style) movement graphic when not moving
2002 if (graphic_info[graphic].double_movement && !dx && !dy)
2004 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2005 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2008 else // border element
2010 graphic = el2img(element);
2011 frame = getGraphicAnimationFrame(graphic, -1);
2014 if (element == EL_EXPANDABLE_WALL)
2016 boolean left_stopped = FALSE, right_stopped = FALSE;
2018 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2019 left_stopped = TRUE;
2020 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2021 right_stopped = TRUE;
2023 if (left_stopped && right_stopped)
2025 else if (left_stopped)
2027 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2028 frame = graphic_info[graphic].anim_frames - 1;
2030 else if (right_stopped)
2032 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2033 frame = graphic_info[graphic].anim_frames - 1;
2038 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2039 else if (mask_mode == USE_MASKING)
2040 DrawGraphicThruMask(x, y, graphic, frame);
2042 DrawGraphic(x, y, graphic, frame);
2045 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2046 int cut_mode, int mask_mode)
2048 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2049 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2050 cut_mode, mask_mode);
2053 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2056 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2059 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2062 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2065 void DrawLevelElementThruMask(int x, int y, int element)
2067 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2070 void DrawLevelFieldThruMask(int x, int y)
2072 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2075 // !!! implementation of quicksand is totally broken !!!
2076 #define IS_CRUMBLED_TILE(x, y, e) \
2077 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2078 !IS_MOVING(x, y) || \
2079 (e) == EL_QUICKSAND_EMPTYING || \
2080 (e) == EL_QUICKSAND_FAST_EMPTYING))
2082 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2087 int width, height, cx, cy;
2088 int sx = SCREENX(x), sy = SCREENY(y);
2089 int crumbled_border_size = graphic_info[graphic].border_size;
2090 int crumbled_tile_size = graphic_info[graphic].tile_size;
2091 int crumbled_border_size_var =
2092 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2095 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2097 for (i = 1; i < 4; i++)
2099 int dxx = (i & 1 ? dx : 0);
2100 int dyy = (i & 2 ? dy : 0);
2103 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2106 // check if neighbour field is of same crumble type
2107 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2108 graphic_info[graphic].class ==
2109 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2111 // return if check prevents inner corner
2112 if (same == (dxx == dx && dyy == dy))
2116 // if we reach this point, we have an inner corner
2118 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2120 width = crumbled_border_size_var;
2121 height = crumbled_border_size_var;
2122 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2123 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2125 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2126 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2129 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2134 int width, height, bx, by, cx, cy;
2135 int sx = SCREENX(x), sy = SCREENY(y);
2136 int crumbled_border_size = graphic_info[graphic].border_size;
2137 int crumbled_tile_size = graphic_info[graphic].tile_size;
2138 int crumbled_border_size_var =
2139 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2140 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2143 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2145 // draw simple, sloppy, non-corner-accurate crumbled border
2147 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2148 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2149 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2150 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2152 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2153 FX + sx * TILEX_VAR + cx,
2154 FY + sy * TILEY_VAR + cy);
2156 // (remaining middle border part must be at least as big as corner part)
2157 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2158 crumbled_border_size_var >= TILESIZE_VAR / 3)
2161 // correct corners of crumbled border, if needed
2163 for (i = -1; i <= 1; i += 2)
2165 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2166 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2167 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2170 // check if neighbour field is of same crumble type
2171 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2172 graphic_info[graphic].class ==
2173 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2175 // no crumbled corner, but continued crumbled border
2177 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2178 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2179 int b1 = (i == 1 ? crumbled_border_size_var :
2180 TILESIZE_VAR - 2 * crumbled_border_size_var);
2182 width = crumbled_border_size_var;
2183 height = crumbled_border_size_var;
2185 if (dir == 1 || dir == 2)
2200 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2202 FX + sx * TILEX_VAR + cx,
2203 FY + sy * TILEY_VAR + cy);
2208 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2210 int sx = SCREENX(x), sy = SCREENY(y);
2213 static int xy[4][2] =
2221 if (!IN_LEV_FIELD(x, y))
2224 element = TILE_GFX_ELEMENT(x, y);
2226 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2228 if (!IN_SCR_FIELD(sx, sy))
2231 // crumble field borders towards direct neighbour fields
2232 for (i = 0; i < 4; i++)
2234 int xx = x + xy[i][0];
2235 int yy = y + xy[i][1];
2237 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2240 // check if neighbour field is of same crumble type
2241 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2242 graphic_info[graphic].class ==
2243 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2246 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2249 // crumble inner field corners towards corner neighbour fields
2250 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2251 graphic_info[graphic].anim_frames == 2)
2253 for (i = 0; i < 4; i++)
2255 int dx = (i & 1 ? +1 : -1);
2256 int dy = (i & 2 ? +1 : -1);
2258 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2262 MarkTileDirty(sx, sy);
2264 else // center field is not crumbled -- crumble neighbour fields
2266 // crumble field borders of direct neighbour fields
2267 for (i = 0; i < 4; i++)
2269 int xx = x + xy[i][0];
2270 int yy = y + xy[i][1];
2271 int sxx = sx + xy[i][0];
2272 int syy = sy + xy[i][1];
2274 if (!IN_LEV_FIELD(xx, yy) ||
2275 !IN_SCR_FIELD(sxx, syy))
2278 // do not crumble fields that are being digged or snapped
2279 if (Feld[xx][yy] == EL_EMPTY ||
2280 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2283 element = TILE_GFX_ELEMENT(xx, yy);
2285 if (!IS_CRUMBLED_TILE(xx, yy, element))
2288 graphic = el_act2crm(element, ACTION_DEFAULT);
2290 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2292 MarkTileDirty(sxx, syy);
2295 // crumble inner field corners of corner neighbour fields
2296 for (i = 0; i < 4; i++)
2298 int dx = (i & 1 ? +1 : -1);
2299 int dy = (i & 2 ? +1 : -1);
2305 if (!IN_LEV_FIELD(xx, yy) ||
2306 !IN_SCR_FIELD(sxx, syy))
2309 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2312 element = TILE_GFX_ELEMENT(xx, yy);
2314 if (!IS_CRUMBLED_TILE(xx, yy, element))
2317 graphic = el_act2crm(element, ACTION_DEFAULT);
2319 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2320 graphic_info[graphic].anim_frames == 2)
2321 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2323 MarkTileDirty(sxx, syy);
2328 void DrawLevelFieldCrumbled(int x, int y)
2332 if (!IN_LEV_FIELD(x, y))
2335 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2336 GfxElement[x][y] != EL_UNDEFINED &&
2337 GFX_CRUMBLED(GfxElement[x][y]))
2339 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2344 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2346 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2349 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2352 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2353 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2354 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2355 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2356 int sx = SCREENX(x), sy = SCREENY(y);
2358 DrawGraphic(sx, sy, graphic1, frame1);
2359 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2362 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2364 int sx = SCREENX(x), sy = SCREENY(y);
2365 static int xy[4][2] =
2374 // crumble direct neighbour fields (required for field borders)
2375 for (i = 0; i < 4; i++)
2377 int xx = x + xy[i][0];
2378 int yy = y + xy[i][1];
2379 int sxx = sx + xy[i][0];
2380 int syy = sy + xy[i][1];
2382 if (!IN_LEV_FIELD(xx, yy) ||
2383 !IN_SCR_FIELD(sxx, syy) ||
2384 !GFX_CRUMBLED(Feld[xx][yy]) ||
2388 DrawLevelField(xx, yy);
2391 // crumble corner neighbour fields (required for inner field corners)
2392 for (i = 0; i < 4; i++)
2394 int dx = (i & 1 ? +1 : -1);
2395 int dy = (i & 2 ? +1 : -1);
2401 if (!IN_LEV_FIELD(xx, yy) ||
2402 !IN_SCR_FIELD(sxx, syy) ||
2403 !GFX_CRUMBLED(Feld[xx][yy]) ||
2407 int element = TILE_GFX_ELEMENT(xx, yy);
2408 int graphic = el_act2crm(element, ACTION_DEFAULT);
2410 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2411 graphic_info[graphic].anim_frames == 2)
2412 DrawLevelField(xx, yy);
2416 static int getBorderElement(int x, int y)
2420 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2421 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2422 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2423 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2424 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2425 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2426 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2428 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2429 int steel_position = (x == -1 && y == -1 ? 0 :
2430 x == lev_fieldx && y == -1 ? 1 :
2431 x == -1 && y == lev_fieldy ? 2 :
2432 x == lev_fieldx && y == lev_fieldy ? 3 :
2433 x == -1 || x == lev_fieldx ? 4 :
2434 y == -1 || y == lev_fieldy ? 5 : 6);
2436 return border[steel_position][steel_type];
2439 void DrawScreenElement(int x, int y, int element)
2441 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2442 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2445 void DrawLevelElement(int x, int y, int element)
2447 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2448 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2451 void DrawScreenField(int x, int y)
2453 int lx = LEVELX(x), ly = LEVELY(y);
2454 int element, content;
2456 if (!IN_LEV_FIELD(lx, ly))
2458 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2461 element = getBorderElement(lx, ly);
2463 DrawScreenElement(x, y, element);
2468 element = Feld[lx][ly];
2469 content = Store[lx][ly];
2471 if (IS_MOVING(lx, ly))
2473 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2474 boolean cut_mode = NO_CUTTING;
2476 if (element == EL_QUICKSAND_EMPTYING ||
2477 element == EL_QUICKSAND_FAST_EMPTYING ||
2478 element == EL_MAGIC_WALL_EMPTYING ||
2479 element == EL_BD_MAGIC_WALL_EMPTYING ||
2480 element == EL_DC_MAGIC_WALL_EMPTYING ||
2481 element == EL_AMOEBA_DROPPING)
2482 cut_mode = CUT_ABOVE;
2483 else if (element == EL_QUICKSAND_FILLING ||
2484 element == EL_QUICKSAND_FAST_FILLING ||
2485 element == EL_MAGIC_WALL_FILLING ||
2486 element == EL_BD_MAGIC_WALL_FILLING ||
2487 element == EL_DC_MAGIC_WALL_FILLING)
2488 cut_mode = CUT_BELOW;
2490 if (cut_mode == CUT_ABOVE)
2491 DrawScreenElement(x, y, element);
2493 DrawScreenElement(x, y, EL_EMPTY);
2496 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2497 else if (cut_mode == NO_CUTTING)
2498 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2501 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2503 if (cut_mode == CUT_BELOW &&
2504 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2505 DrawLevelElement(lx, ly + 1, element);
2508 if (content == EL_ACID)
2510 int dir = MovDir[lx][ly];
2511 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2512 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2514 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2516 // prevent target field from being drawn again (but without masking)
2517 // (this would happen if target field is scanned after moving element)
2518 Stop[newlx][newly] = TRUE;
2521 else if (IS_BLOCKED(lx, ly))
2526 boolean cut_mode = NO_CUTTING;
2527 int element_old, content_old;
2529 Blocked2Moving(lx, ly, &oldx, &oldy);
2532 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2533 MovDir[oldx][oldy] == MV_RIGHT);
2535 element_old = Feld[oldx][oldy];
2536 content_old = Store[oldx][oldy];
2538 if (element_old == EL_QUICKSAND_EMPTYING ||
2539 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2540 element_old == EL_MAGIC_WALL_EMPTYING ||
2541 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2542 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2543 element_old == EL_AMOEBA_DROPPING)
2544 cut_mode = CUT_ABOVE;
2546 DrawScreenElement(x, y, EL_EMPTY);
2549 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2551 else if (cut_mode == NO_CUTTING)
2552 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2555 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2558 else if (IS_DRAWABLE(element))
2559 DrawScreenElement(x, y, element);
2561 DrawScreenElement(x, y, EL_EMPTY);
2564 void DrawLevelField(int x, int y)
2566 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2567 DrawScreenField(SCREENX(x), SCREENY(y));
2568 else if (IS_MOVING(x, y))
2572 Moving2Blocked(x, y, &newx, &newy);
2573 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2574 DrawScreenField(SCREENX(newx), SCREENY(newy));
2576 else if (IS_BLOCKED(x, y))
2580 Blocked2Moving(x, y, &oldx, &oldy);
2581 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2582 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2586 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2587 int (*el2img_function)(int), boolean masked,
2588 int element_bits_draw)
2590 int element_base = map_mm_wall_element(element);
2591 int element_bits = (IS_DF_WALL(element) ?
2592 element - EL_DF_WALL_START :
2593 IS_MM_WALL(element) ?
2594 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2595 int graphic = el2img_function(element_base);
2596 int tilesize_draw = tilesize / 2;
2601 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2603 for (i = 0; i < 4; i++)
2605 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2606 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2608 if (!(element_bits_draw & (1 << i)))
2611 if (element_bits & (1 << i))
2614 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2615 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2617 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2618 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2623 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2624 tilesize_draw, tilesize_draw);
2629 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2630 boolean masked, int element_bits_draw)
2632 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2633 element, tilesize, el2edimg, masked, element_bits_draw);
2636 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2637 int (*el2img_function)(int))
2639 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2643 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2646 if (IS_MM_WALL(element))
2648 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2649 element, tilesize, el2edimg, masked, 0x000f);
2653 int graphic = el2edimg(element);
2656 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2658 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2662 void DrawSizedElement(int x, int y, int element, int tilesize)
2664 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2667 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2669 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2672 void DrawMiniElement(int x, int y, int element)
2676 graphic = el2edimg(element);
2677 DrawMiniGraphic(x, y, graphic);
2680 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2683 int x = sx + scroll_x, y = sy + scroll_y;
2685 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2686 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2687 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2688 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2690 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2693 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2695 int x = sx + scroll_x, y = sy + scroll_y;
2697 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2698 DrawMiniElement(sx, sy, EL_EMPTY);
2699 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2700 DrawMiniElement(sx, sy, Feld[x][y]);
2702 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2705 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2706 int x, int y, int xsize, int ysize,
2707 int tile_width, int tile_height)
2711 int dst_x = startx + x * tile_width;
2712 int dst_y = starty + y * tile_height;
2713 int width = graphic_info[graphic].width;
2714 int height = graphic_info[graphic].height;
2715 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2716 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2717 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2718 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2719 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2720 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2721 boolean draw_masked = graphic_info[graphic].draw_masked;
2723 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2725 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2727 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2731 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2732 inner_sx + (x - 1) * tile_width % inner_width);
2733 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2734 inner_sy + (y - 1) * tile_height % inner_height);
2737 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2740 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2744 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2745 int x, int y, int xsize, int ysize,
2748 int font_width = getFontWidth(font_nr);
2749 int font_height = getFontHeight(font_nr);
2751 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2752 font_width, font_height);
2755 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2757 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2758 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2759 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2760 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2761 boolean no_delay = (tape.warp_forward);
2762 unsigned int anim_delay = 0;
2763 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2764 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2765 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2766 int font_width = getFontWidth(font_nr);
2767 int font_height = getFontHeight(font_nr);
2768 int max_xsize = level.envelope[envelope_nr].xsize;
2769 int max_ysize = level.envelope[envelope_nr].ysize;
2770 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2771 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2772 int xend = max_xsize;
2773 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2774 int xstep = (xstart < xend ? 1 : 0);
2775 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2777 int end = MAX(xend - xstart, yend - ystart);
2780 for (i = start; i <= end; i++)
2782 int last_frame = end; // last frame of this "for" loop
2783 int x = xstart + i * xstep;
2784 int y = ystart + i * ystep;
2785 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2786 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2787 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2788 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2791 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2793 BlitScreenToBitmap(backbuffer);
2795 SetDrawtoField(DRAW_TO_BACKBUFFER);
2797 for (yy = 0; yy < ysize; yy++)
2798 for (xx = 0; xx < xsize; xx++)
2799 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2801 DrawTextBuffer(sx + font_width, sy + font_height,
2802 level.envelope[envelope_nr].text, font_nr, max_xsize,
2803 xsize - 2, ysize - 2, 0, mask_mode,
2804 level.envelope[envelope_nr].autowrap,
2805 level.envelope[envelope_nr].centered, FALSE);
2807 redraw_mask |= REDRAW_FIELD;
2810 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2813 ClearAutoRepeatKeyEvents();
2816 void ShowEnvelope(int envelope_nr)
2818 int element = EL_ENVELOPE_1 + envelope_nr;
2819 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2820 int sound_opening = element_info[element].sound[ACTION_OPENING];
2821 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2822 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2823 boolean no_delay = (tape.warp_forward);
2824 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2825 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2826 int anim_mode = graphic_info[graphic].anim_mode;
2827 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2828 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2830 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2832 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2834 if (anim_mode == ANIM_DEFAULT)
2835 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2837 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2840 Delay_WithScreenUpdates(wait_delay_value);
2842 WaitForEventToContinue();
2844 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2846 if (anim_mode != ANIM_NONE)
2847 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2849 if (anim_mode == ANIM_DEFAULT)
2850 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2852 game.envelope_active = FALSE;
2854 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2856 redraw_mask |= REDRAW_FIELD;
2860 static void setRequestBasePosition(int *x, int *y)
2862 int sx_base, sy_base;
2864 if (request.x != -1)
2865 sx_base = request.x;
2866 else if (request.align == ALIGN_LEFT)
2868 else if (request.align == ALIGN_RIGHT)
2869 sx_base = SX + SXSIZE;
2871 sx_base = SX + SXSIZE / 2;
2873 if (request.y != -1)
2874 sy_base = request.y;
2875 else if (request.valign == VALIGN_TOP)
2877 else if (request.valign == VALIGN_BOTTOM)
2878 sy_base = SY + SYSIZE;
2880 sy_base = SY + SYSIZE / 2;
2886 static void setRequestPositionExt(int *x, int *y, int width, int height,
2887 boolean add_border_size)
2889 int border_size = request.border_size;
2890 int sx_base, sy_base;
2893 setRequestBasePosition(&sx_base, &sy_base);
2895 if (request.align == ALIGN_LEFT)
2897 else if (request.align == ALIGN_RIGHT)
2898 sx = sx_base - width;
2900 sx = sx_base - width / 2;
2902 if (request.valign == VALIGN_TOP)
2904 else if (request.valign == VALIGN_BOTTOM)
2905 sy = sy_base - height;
2907 sy = sy_base - height / 2;
2909 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2910 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2912 if (add_border_size)
2922 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2924 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2927 static void DrawEnvelopeRequest(char *text)
2929 char *text_final = text;
2930 char *text_door_style = NULL;
2931 int graphic = IMG_BACKGROUND_REQUEST;
2932 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2933 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2934 int font_nr = FONT_REQUEST;
2935 int font_width = getFontWidth(font_nr);
2936 int font_height = getFontHeight(font_nr);
2937 int border_size = request.border_size;
2938 int line_spacing = request.line_spacing;
2939 int line_height = font_height + line_spacing;
2940 int max_text_width = request.width - 2 * border_size;
2941 int max_text_height = request.height - 2 * border_size;
2942 int line_length = max_text_width / font_width;
2943 int max_lines = max_text_height / line_height;
2944 int text_width = line_length * font_width;
2945 int width = request.width;
2946 int height = request.height;
2947 int tile_size = MAX(request.step_offset, 1);
2948 int x_steps = width / tile_size;
2949 int y_steps = height / tile_size;
2950 int sx_offset = border_size;
2951 int sy_offset = border_size;
2955 if (request.centered)
2956 sx_offset = (request.width - text_width) / 2;
2958 if (request.wrap_single_words && !request.autowrap)
2960 char *src_text_ptr, *dst_text_ptr;
2962 text_door_style = checked_malloc(2 * strlen(text) + 1);
2964 src_text_ptr = text;
2965 dst_text_ptr = text_door_style;
2967 while (*src_text_ptr)
2969 if (*src_text_ptr == ' ' ||
2970 *src_text_ptr == '?' ||
2971 *src_text_ptr == '!')
2972 *dst_text_ptr++ = '\n';
2974 if (*src_text_ptr != ' ')
2975 *dst_text_ptr++ = *src_text_ptr;
2980 *dst_text_ptr = '\0';
2982 text_final = text_door_style;
2985 setRequestPosition(&sx, &sy, FALSE);
2987 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2989 for (y = 0; y < y_steps; y++)
2990 for (x = 0; x < x_steps; x++)
2991 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2992 x, y, x_steps, y_steps,
2993 tile_size, tile_size);
2995 // force DOOR font inside door area
2996 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2998 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2999 line_length, -1, max_lines, line_spacing, mask_mode,
3000 request.autowrap, request.centered, FALSE);
3004 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3005 RedrawGadget(tool_gadget[i]);
3007 // store readily prepared envelope request for later use when animating
3008 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3010 if (text_door_style)
3011 free(text_door_style);
3014 static void AnimateEnvelopeRequest(int anim_mode, int action)
3016 int graphic = IMG_BACKGROUND_REQUEST;
3017 boolean draw_masked = graphic_info[graphic].draw_masked;
3018 int delay_value_normal = request.step_delay;
3019 int delay_value_fast = delay_value_normal / 2;
3020 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3021 boolean no_delay = (tape.warp_forward);
3022 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3023 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3024 unsigned int anim_delay = 0;
3026 int tile_size = MAX(request.step_offset, 1);
3027 int max_xsize = request.width / tile_size;
3028 int max_ysize = request.height / tile_size;
3029 int max_xsize_inner = max_xsize - 2;
3030 int max_ysize_inner = max_ysize - 2;
3032 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3033 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3034 int xend = max_xsize_inner;
3035 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3036 int xstep = (xstart < xend ? 1 : 0);
3037 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3039 int end = MAX(xend - xstart, yend - ystart);
3042 if (setup.quick_doors)
3049 for (i = start; i <= end; i++)
3051 int last_frame = end; // last frame of this "for" loop
3052 int x = xstart + i * xstep;
3053 int y = ystart + i * ystep;
3054 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3055 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3056 int xsize_size_left = (xsize - 1) * tile_size;
3057 int ysize_size_top = (ysize - 1) * tile_size;
3058 int max_xsize_pos = (max_xsize - 1) * tile_size;
3059 int max_ysize_pos = (max_ysize - 1) * tile_size;
3060 int width = xsize * tile_size;
3061 int height = ysize * tile_size;
3066 setRequestPosition(&src_x, &src_y, FALSE);
3067 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3069 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3071 for (yy = 0; yy < 2; yy++)
3073 for (xx = 0; xx < 2; xx++)
3075 int src_xx = src_x + xx * max_xsize_pos;
3076 int src_yy = src_y + yy * max_ysize_pos;
3077 int dst_xx = dst_x + xx * xsize_size_left;
3078 int dst_yy = dst_y + yy * ysize_size_top;
3079 int xx_size = (xx ? tile_size : xsize_size_left);
3080 int yy_size = (yy ? tile_size : ysize_size_top);
3083 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3084 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3086 BlitBitmap(bitmap_db_store_2, backbuffer,
3087 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3091 redraw_mask |= REDRAW_FIELD;
3095 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3098 ClearAutoRepeatKeyEvents();
3101 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3103 int graphic = IMG_BACKGROUND_REQUEST;
3104 int sound_opening = SND_REQUEST_OPENING;
3105 int sound_closing = SND_REQUEST_CLOSING;
3106 int anim_mode_1 = request.anim_mode; // (higher priority)
3107 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3108 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3109 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3110 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3112 if (game_status == GAME_MODE_PLAYING)
3113 BlitScreenToBitmap(backbuffer);
3115 SetDrawtoField(DRAW_TO_BACKBUFFER);
3117 // SetDrawBackgroundMask(REDRAW_NONE);
3119 if (action == ACTION_OPENING)
3121 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3123 if (req_state & REQ_ASK)
3125 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3126 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3130 else if (req_state & REQ_CONFIRM)
3132 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3135 else if (req_state & REQ_PLAYER)
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3140 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3143 DrawEnvelopeRequest(text);
3146 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3148 if (action == ACTION_OPENING)
3150 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3152 if (anim_mode == ANIM_DEFAULT)
3153 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3155 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3159 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3161 if (anim_mode != ANIM_NONE)
3162 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3164 if (anim_mode == ANIM_DEFAULT)
3165 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3168 game.envelope_active = FALSE;
3170 if (action == ACTION_CLOSING)
3171 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3173 // SetDrawBackgroundMask(last_draw_background_mask);
3175 redraw_mask |= REDRAW_FIELD;
3179 if (action == ACTION_CLOSING &&
3180 game_status == GAME_MODE_PLAYING &&
3181 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3182 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3185 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3187 if (IS_MM_WALL(element))
3189 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3195 int graphic = el2preimg(element);
3197 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3198 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3203 void DrawLevel(int draw_background_mask)
3207 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3208 SetDrawBackgroundMask(draw_background_mask);
3212 for (x = BX1; x <= BX2; x++)
3213 for (y = BY1; y <= BY2; y++)
3214 DrawScreenField(x, y);
3216 redraw_mask |= REDRAW_FIELD;
3219 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3224 for (x = 0; x < size_x; x++)
3225 for (y = 0; y < size_y; y++)
3226 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3228 redraw_mask |= REDRAW_FIELD;
3231 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3235 for (x = 0; x < size_x; x++)
3236 for (y = 0; y < size_y; y++)
3237 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3239 redraw_mask |= REDRAW_FIELD;
3242 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3244 boolean show_level_border = (BorderElement != EL_EMPTY);
3245 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3246 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3247 int tile_size = preview.tile_size;
3248 int preview_width = preview.xsize * tile_size;
3249 int preview_height = preview.ysize * tile_size;
3250 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3251 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3252 int real_preview_width = real_preview_xsize * tile_size;
3253 int real_preview_height = real_preview_ysize * tile_size;
3254 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3255 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3258 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3261 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3263 dst_x += (preview_width - real_preview_width) / 2;
3264 dst_y += (preview_height - real_preview_height) / 2;
3266 for (x = 0; x < real_preview_xsize; x++)
3268 for (y = 0; y < real_preview_ysize; y++)
3270 int lx = from_x + x + (show_level_border ? -1 : 0);
3271 int ly = from_y + y + (show_level_border ? -1 : 0);
3272 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3273 getBorderElement(lx, ly));
3275 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3276 element, tile_size);
3280 redraw_mask |= REDRAW_FIELD;
3283 #define MICROLABEL_EMPTY 0
3284 #define MICROLABEL_LEVEL_NAME 1
3285 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3286 #define MICROLABEL_LEVEL_AUTHOR 3
3287 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3288 #define MICROLABEL_IMPORTED_FROM 5
3289 #define MICROLABEL_IMPORTED_BY_HEAD 6
3290 #define MICROLABEL_IMPORTED_BY 7
3292 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3294 int max_text_width = SXSIZE;
3295 int font_width = getFontWidth(font_nr);
3297 if (pos->align == ALIGN_CENTER)
3298 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3299 else if (pos->align == ALIGN_RIGHT)
3300 max_text_width = pos->x;
3302 max_text_width = SXSIZE - pos->x;
3304 return max_text_width / font_width;
3307 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3309 char label_text[MAX_OUTPUT_LINESIZE + 1];
3310 int max_len_label_text;
3311 int font_nr = pos->font;
3314 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3317 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3318 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3319 mode == MICROLABEL_IMPORTED_BY_HEAD)
3320 font_nr = pos->font_alt;
3322 max_len_label_text = getMaxTextLength(pos, font_nr);
3324 if (pos->size != -1)
3325 max_len_label_text = pos->size;
3327 for (i = 0; i < max_len_label_text; i++)
3328 label_text[i] = ' ';
3329 label_text[max_len_label_text] = '\0';
3331 if (strlen(label_text) > 0)
3332 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3335 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3336 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3337 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3338 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3339 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3340 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3341 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3342 max_len_label_text);
3343 label_text[max_len_label_text] = '\0';
3345 if (strlen(label_text) > 0)
3346 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3348 redraw_mask |= REDRAW_FIELD;
3351 static void DrawPreviewLevelLabel(int mode)
3353 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3356 static void DrawPreviewLevelInfo(int mode)
3358 if (mode == MICROLABEL_LEVEL_NAME)
3359 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3360 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3361 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3364 static void DrawPreviewLevelExt(boolean restart)
3366 static unsigned int scroll_delay = 0;
3367 static unsigned int label_delay = 0;
3368 static int from_x, from_y, scroll_direction;
3369 static int label_state, label_counter;
3370 unsigned int scroll_delay_value = preview.step_delay;
3371 boolean show_level_border = (BorderElement != EL_EMPTY);
3372 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3373 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3380 if (preview.anim_mode == ANIM_CENTERED)
3382 if (level_xsize > preview.xsize)
3383 from_x = (level_xsize - preview.xsize) / 2;
3384 if (level_ysize > preview.ysize)
3385 from_y = (level_ysize - preview.ysize) / 2;
3388 from_x += preview.xoffset;
3389 from_y += preview.yoffset;
3391 scroll_direction = MV_RIGHT;
3395 DrawPreviewLevelPlayfield(from_x, from_y);
3396 DrawPreviewLevelLabel(label_state);
3398 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3399 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3401 // initialize delay counters
3402 DelayReached(&scroll_delay, 0);
3403 DelayReached(&label_delay, 0);
3405 if (leveldir_current->name)
3407 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3408 char label_text[MAX_OUTPUT_LINESIZE + 1];
3409 int font_nr = pos->font;
3410 int max_len_label_text = getMaxTextLength(pos, font_nr);
3412 if (pos->size != -1)
3413 max_len_label_text = pos->size;
3415 strncpy(label_text, leveldir_current->name, max_len_label_text);
3416 label_text[max_len_label_text] = '\0';
3418 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3419 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3425 // scroll preview level, if needed
3426 if (preview.anim_mode != ANIM_NONE &&
3427 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3428 DelayReached(&scroll_delay, scroll_delay_value))
3430 switch (scroll_direction)
3435 from_x -= preview.step_offset;
3436 from_x = (from_x < 0 ? 0 : from_x);
3439 scroll_direction = MV_UP;
3443 if (from_x < level_xsize - preview.xsize)
3445 from_x += preview.step_offset;
3446 from_x = (from_x > level_xsize - preview.xsize ?
3447 level_xsize - preview.xsize : from_x);
3450 scroll_direction = MV_DOWN;
3456 from_y -= preview.step_offset;
3457 from_y = (from_y < 0 ? 0 : from_y);
3460 scroll_direction = MV_RIGHT;
3464 if (from_y < level_ysize - preview.ysize)
3466 from_y += preview.step_offset;
3467 from_y = (from_y > level_ysize - preview.ysize ?
3468 level_ysize - preview.ysize : from_y);
3471 scroll_direction = MV_LEFT;
3478 DrawPreviewLevelPlayfield(from_x, from_y);
3481 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3482 // redraw micro level label, if needed
3483 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3484 !strEqual(level.author, ANONYMOUS_NAME) &&
3485 !strEqual(level.author, leveldir_current->name) &&
3486 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3488 int max_label_counter = 23;
3490 if (leveldir_current->imported_from != NULL &&
3491 strlen(leveldir_current->imported_from) > 0)
3492 max_label_counter += 14;
3493 if (leveldir_current->imported_by != NULL &&
3494 strlen(leveldir_current->imported_by) > 0)
3495 max_label_counter += 14;
3497 label_counter = (label_counter + 1) % max_label_counter;
3498 label_state = (label_counter >= 0 && label_counter <= 7 ?
3499 MICROLABEL_LEVEL_NAME :
3500 label_counter >= 9 && label_counter <= 12 ?
3501 MICROLABEL_LEVEL_AUTHOR_HEAD :
3502 label_counter >= 14 && label_counter <= 21 ?
3503 MICROLABEL_LEVEL_AUTHOR :
3504 label_counter >= 23 && label_counter <= 26 ?
3505 MICROLABEL_IMPORTED_FROM_HEAD :
3506 label_counter >= 28 && label_counter <= 35 ?
3507 MICROLABEL_IMPORTED_FROM :
3508 label_counter >= 37 && label_counter <= 40 ?
3509 MICROLABEL_IMPORTED_BY_HEAD :
3510 label_counter >= 42 && label_counter <= 49 ?
3511 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3513 if (leveldir_current->imported_from == NULL &&
3514 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3515 label_state == MICROLABEL_IMPORTED_FROM))
3516 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3517 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3519 DrawPreviewLevelLabel(label_state);
3523 void DrawPreviewPlayers(void)
3525 if (game_status != GAME_MODE_MAIN)
3528 // do not draw preview players if level preview redefined, but players aren't
3529 if (preview.redefined && !menu.main.preview_players.redefined)
3532 boolean player_found[MAX_PLAYERS];
3533 int num_players = 0;
3536 for (i = 0; i < MAX_PLAYERS; i++)
3537 player_found[i] = FALSE;
3539 // check which players can be found in the level (simple approach)
3540 for (x = 0; x < lev_fieldx; x++)
3542 for (y = 0; y < lev_fieldy; y++)
3544 int element = level.field[x][y];
3546 if (ELEM_IS_PLAYER(element))
3548 int player_nr = GET_PLAYER_NR(element);
3550 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3552 if (!player_found[player_nr])
3555 player_found[player_nr] = TRUE;
3560 struct TextPosInfo *pos = &menu.main.preview_players;
3561 int tile_size = pos->tile_size;
3562 int border_size = pos->border_size;
3563 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3564 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3565 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3566 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3567 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3568 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3569 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3570 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3571 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3572 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3573 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3574 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3576 // clear area in which the players will be drawn
3577 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3578 max_players_width, max_players_height);
3580 if (!network.enabled && !setup.team_mode)
3583 // only draw players if level is suited for team mode
3584 if (num_players < 2)
3587 // draw all players that were found in the level
3588 for (i = 0; i < MAX_PLAYERS; i++)
3590 if (player_found[i])
3592 int graphic = el2img(EL_PLAYER_1 + i);
3594 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3596 xpos += player_xoffset;
3597 ypos += player_yoffset;
3602 void DrawPreviewLevelInitial(void)
3604 DrawPreviewLevelExt(TRUE);
3605 DrawPreviewPlayers();
3608 void DrawPreviewLevelAnimation(void)
3610 DrawPreviewLevelExt(FALSE);
3613 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3614 int border_size, int font_nr)
3616 int graphic = el2img(EL_PLAYER_1 + player_nr);
3617 int font_height = getFontHeight(font_nr);
3618 int player_height = MAX(tile_size, font_height);
3619 int xoffset_text = tile_size + border_size;
3620 int yoffset_text = (player_height - font_height) / 2;
3621 int yoffset_graphic = (player_height - tile_size) / 2;
3622 char *player_name = getNetworkPlayerName(player_nr + 1);
3624 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3626 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3629 static void DrawNetworkPlayersExt(boolean force)
3631 if (game_status != GAME_MODE_MAIN)
3634 if (!network.connected && !force)
3637 // do not draw network players if level preview redefined, but players aren't
3638 if (preview.redefined && !menu.main.network_players.redefined)
3641 int num_players = 0;
3644 for (i = 0; i < MAX_PLAYERS; i++)
3645 if (stored_player[i].connected_network)
3648 struct TextPosInfo *pos = &menu.main.network_players;
3649 int tile_size = pos->tile_size;
3650 int border_size = pos->border_size;
3651 int xoffset_text = tile_size + border_size;
3652 int font_nr = pos->font;
3653 int font_width = getFontWidth(font_nr);
3654 int font_height = getFontHeight(font_nr);
3655 int player_height = MAX(tile_size, font_height);
3656 int player_yoffset = player_height + border_size;
3657 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3658 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3659 int all_players_height = num_players * player_yoffset - border_size;
3660 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3661 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3662 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3664 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3665 max_players_width, max_players_height);
3667 // first draw local network player ...
3668 for (i = 0; i < MAX_PLAYERS; i++)
3670 if (stored_player[i].connected_network &&
3671 stored_player[i].connected_locally)
3673 char *player_name = getNetworkPlayerName(i + 1);
3674 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3675 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3677 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3679 ypos += player_yoffset;
3683 // ... then draw all other network players
3684 for (i = 0; i < MAX_PLAYERS; i++)
3686 if (stored_player[i].connected_network &&
3687 !stored_player[i].connected_locally)
3689 char *player_name = getNetworkPlayerName(i + 1);
3690 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3691 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3693 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3695 ypos += player_yoffset;
3700 void DrawNetworkPlayers(void)
3702 DrawNetworkPlayersExt(FALSE);
3705 void ClearNetworkPlayers(void)
3707 DrawNetworkPlayersExt(TRUE);
3710 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3711 int graphic, int sync_frame,
3714 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3716 if (mask_mode == USE_MASKING)
3717 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3719 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3722 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3723 int graphic, int sync_frame, int mask_mode)
3725 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3727 if (mask_mode == USE_MASKING)
3728 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3730 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3733 static void DrawGraphicAnimation(int x, int y, int graphic)
3735 int lx = LEVELX(x), ly = LEVELY(y);
3737 if (!IN_SCR_FIELD(x, y))
3740 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3741 graphic, GfxFrame[lx][ly], NO_MASKING);
3743 MarkTileDirty(x, y);
3746 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3748 int lx = LEVELX(x), ly = LEVELY(y);
3750 if (!IN_SCR_FIELD(x, y))
3753 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3754 graphic, GfxFrame[lx][ly], NO_MASKING);
3755 MarkTileDirty(x, y);
3758 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3760 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3763 void DrawLevelElementAnimation(int x, int y, int element)
3765 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3767 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3770 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3772 int sx = SCREENX(x), sy = SCREENY(y);
3774 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3777 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3780 DrawGraphicAnimation(sx, sy, graphic);
3783 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3784 DrawLevelFieldCrumbled(x, y);
3786 if (GFX_CRUMBLED(Feld[x][y]))
3787 DrawLevelFieldCrumbled(x, y);
3791 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3793 int sx = SCREENX(x), sy = SCREENY(y);
3796 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3799 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3801 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3804 DrawGraphicAnimation(sx, sy, graphic);
3806 if (GFX_CRUMBLED(element))
3807 DrawLevelFieldCrumbled(x, y);
3810 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3812 if (player->use_murphy)
3814 // this works only because currently only one player can be "murphy" ...
3815 static int last_horizontal_dir = MV_LEFT;
3816 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3818 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3819 last_horizontal_dir = move_dir;
3821 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3823 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3825 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3831 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3834 static boolean equalGraphics(int graphic1, int graphic2)
3836 struct GraphicInfo *g1 = &graphic_info[graphic1];
3837 struct GraphicInfo *g2 = &graphic_info[graphic2];
3839 return (g1->bitmap == g2->bitmap &&
3840 g1->src_x == g2->src_x &&
3841 g1->src_y == g2->src_y &&
3842 g1->anim_frames == g2->anim_frames &&
3843 g1->anim_delay == g2->anim_delay &&
3844 g1->anim_mode == g2->anim_mode);
3847 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3851 DRAW_PLAYER_STAGE_INIT = 0,
3852 DRAW_PLAYER_STAGE_LAST_FIELD,
3853 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3854 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3855 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3856 DRAW_PLAYER_STAGE_PLAYER,
3858 DRAW_PLAYER_STAGE_PLAYER,
3859 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3861 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3862 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3864 NUM_DRAW_PLAYER_STAGES
3867 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3869 static int static_last_player_graphic[MAX_PLAYERS];
3870 static int static_last_player_frame[MAX_PLAYERS];
3871 static boolean static_player_is_opaque[MAX_PLAYERS];
3872 static boolean draw_player[MAX_PLAYERS];
3873 int pnr = player->index_nr;
3875 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3877 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3878 static_last_player_frame[pnr] = player->Frame;
3879 static_player_is_opaque[pnr] = FALSE;
3881 draw_player[pnr] = TRUE;
3884 if (!draw_player[pnr])
3888 if (!IN_LEV_FIELD(player->jx, player->jy))
3890 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3891 printf("DrawPlayerField(): This should never happen!\n");
3893 draw_player[pnr] = FALSE;
3899 int last_player_graphic = static_last_player_graphic[pnr];
3900 int last_player_frame = static_last_player_frame[pnr];
3901 boolean player_is_opaque = static_player_is_opaque[pnr];
3903 int jx = player->jx;
3904 int jy = player->jy;
3905 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3906 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3907 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3908 int last_jx = (player->is_moving ? jx - dx : jx);
3909 int last_jy = (player->is_moving ? jy - dy : jy);
3910 int next_jx = jx + dx;
3911 int next_jy = jy + dy;
3912 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3913 int sx = SCREENX(jx);
3914 int sy = SCREENY(jy);
3915 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3916 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3917 int element = Feld[jx][jy];
3918 int last_element = Feld[last_jx][last_jy];
3919 int action = (player->is_pushing ? ACTION_PUSHING :
3920 player->is_digging ? ACTION_DIGGING :
3921 player->is_collecting ? ACTION_COLLECTING :
3922 player->is_moving ? ACTION_MOVING :
3923 player->is_snapping ? ACTION_SNAPPING :
3924 player->is_dropping ? ACTION_DROPPING :
3925 player->is_waiting ? player->action_waiting :
3928 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3930 // ------------------------------------------------------------------------
3931 // initialize drawing the player
3932 // ------------------------------------------------------------------------
3934 draw_player[pnr] = FALSE;
3936 // GfxElement[][] is set to the element the player is digging or collecting;
3937 // remove also for off-screen player if the player is not moving anymore
3938 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3939 GfxElement[jx][jy] = EL_UNDEFINED;
3941 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3944 if (element == EL_EXPLOSION)
3947 InitPlayerGfxAnimation(player, action, move_dir);
3949 draw_player[pnr] = TRUE;
3951 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3953 // ------------------------------------------------------------------------
3954 // draw things in the field the player is leaving, if needed
3955 // ------------------------------------------------------------------------
3957 if (!IN_SCR_FIELD(sx, sy))
3958 draw_player[pnr] = FALSE;
3960 if (!player->is_moving)
3963 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3965 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3967 if (last_element == EL_DYNAMITE_ACTIVE ||
3968 last_element == EL_EM_DYNAMITE_ACTIVE ||
3969 last_element == EL_SP_DISK_RED_ACTIVE)
3970 DrawDynamite(last_jx, last_jy);
3972 DrawLevelFieldThruMask(last_jx, last_jy);
3974 else if (last_element == EL_DYNAMITE_ACTIVE ||
3975 last_element == EL_EM_DYNAMITE_ACTIVE ||
3976 last_element == EL_SP_DISK_RED_ACTIVE)
3977 DrawDynamite(last_jx, last_jy);
3979 DrawLevelField(last_jx, last_jy);
3981 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3982 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3984 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3986 // ------------------------------------------------------------------------
3987 // draw things behind the player, if needed
3988 // ------------------------------------------------------------------------
3992 DrawLevelElement(jx, jy, Back[jx][jy]);
3997 if (IS_ACTIVE_BOMB(element))
3999 DrawLevelElement(jx, jy, EL_EMPTY);
4004 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4006 int old_element = GfxElement[jx][jy];
4007 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4008 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4010 if (GFX_CRUMBLED(old_element))
4011 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4013 DrawGraphic(sx, sy, old_graphic, frame);
4015 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4016 static_player_is_opaque[pnr] = TRUE;
4020 GfxElement[jx][jy] = EL_UNDEFINED;
4022 // make sure that pushed elements are drawn with correct frame rate
4023 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4025 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4026 GfxFrame[jx][jy] = player->StepFrame;
4028 DrawLevelField(jx, jy);
4031 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4033 // ------------------------------------------------------------------------
4034 // draw things the player is pushing, if needed
4035 // ------------------------------------------------------------------------
4037 if (!player->is_pushing || !player->is_moving)
4040 int gfx_frame = GfxFrame[jx][jy];
4042 if (!IS_MOVING(jx, jy)) // push movement already finished
4044 element = Feld[next_jx][next_jy];
4045 gfx_frame = GfxFrame[next_jx][next_jy];
4048 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4049 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4050 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4052 // draw background element under pushed element (like the Sokoban field)
4053 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4055 // this allows transparent pushing animation over non-black background
4058 DrawLevelElement(jx, jy, Back[jx][jy]);
4060 DrawLevelElement(jx, jy, EL_EMPTY);
4062 if (Back[next_jx][next_jy])
4063 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4065 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4067 else if (Back[next_jx][next_jy])
4068 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4070 int px = SCREENX(jx), py = SCREENY(jy);
4071 int pxx = (TILEX - ABS(sxx)) * dx;
4072 int pyy = (TILEY - ABS(syy)) * dy;
4075 // do not draw (EM style) pushing animation when pushing is finished
4076 // (two-tile animations usually do not contain start and end frame)
4077 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4078 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4080 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4082 // masked drawing is needed for EMC style (double) movement graphics
4083 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4084 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4087 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4089 // ------------------------------------------------------------------------
4090 // draw player himself
4091 // ------------------------------------------------------------------------
4093 int graphic = getPlayerGraphic(player, move_dir);
4095 // in the case of changed player action or direction, prevent the current
4096 // animation frame from being restarted for identical animations
4097 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4098 player->Frame = last_player_frame;
4100 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4102 if (player_is_opaque)
4103 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4105 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4107 if (SHIELD_ON(player))
4109 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4110 IMG_SHIELD_NORMAL_ACTIVE);
4111 frame = getGraphicAnimationFrame(graphic, -1);
4113 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4116 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4118 // ------------------------------------------------------------------------
4119 // draw things in front of player (active dynamite or dynabombs)
4120 // ------------------------------------------------------------------------
4122 if (IS_ACTIVE_BOMB(element))
4124 int graphic = el2img(element);
4125 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4127 if (game.emulation == EMU_SUPAPLEX)
4128 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4130 DrawGraphicThruMask(sx, sy, graphic, frame);
4133 if (player_is_moving && last_element == EL_EXPLOSION)
4135 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4136 GfxElement[last_jx][last_jy] : EL_EMPTY);
4137 int graphic = el_act2img(element, ACTION_EXPLODING);
4138 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4139 int phase = ExplodePhase[last_jx][last_jy] - 1;
4140 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4143 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4146 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4148 // ------------------------------------------------------------------------
4149 // draw elements the player is just walking/passing through/under
4150 // ------------------------------------------------------------------------
4152 if (player_is_moving)
4154 // handle the field the player is leaving ...
4155 if (IS_ACCESSIBLE_INSIDE(last_element))
4156 DrawLevelField(last_jx, last_jy);
4157 else if (IS_ACCESSIBLE_UNDER(last_element))
4158 DrawLevelFieldThruMask(last_jx, last_jy);
4161 // do not redraw accessible elements if the player is just pushing them
4162 if (!player_is_moving || !player->is_pushing)
4164 // ... and the field the player is entering
4165 if (IS_ACCESSIBLE_INSIDE(element))
4166 DrawLevelField(jx, jy);
4167 else if (IS_ACCESSIBLE_UNDER(element))
4168 DrawLevelFieldThruMask(jx, jy);
4171 MarkTileDirty(sx, sy);
4175 void DrawPlayer(struct PlayerInfo *player)
4179 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4180 DrawPlayerExt(player, i);
4183 void DrawAllPlayers(void)
4187 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4188 for (j = 0; j < MAX_PLAYERS; j++)
4189 if (stored_player[j].active)
4190 DrawPlayerExt(&stored_player[j], i);
4193 void DrawPlayerField(int x, int y)
4195 if (!IS_PLAYER(x, y))
4198 DrawPlayer(PLAYERINFO(x, y));
4201 // ----------------------------------------------------------------------------
4203 void WaitForEventToContinue(void)
4205 boolean still_wait = TRUE;
4207 if (program.headless)
4210 // simulate releasing mouse button over last gadget, if still pressed
4212 HandleGadgets(-1, -1, 0);
4214 button_status = MB_RELEASED;
4222 if (NextValidEvent(&event))
4226 case EVENT_BUTTONRELEASE:
4227 case EVENT_KEYPRESS:
4228 case SDL_CONTROLLERBUTTONDOWN:
4229 case SDL_JOYBUTTONDOWN:
4233 case EVENT_KEYRELEASE:
4234 ClearPlayerAction();
4238 HandleOtherEvents(&event);
4242 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4251 #define MAX_REQUEST_LINES 13
4252 #define MAX_REQUEST_LINE_FONT1_LEN 7
4253 #define MAX_REQUEST_LINE_FONT2_LEN 10
4255 static int RequestHandleEvents(unsigned int req_state)
4257 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4259 int width = request.width;
4260 int height = request.height;
4264 // when showing request dialog after game ended, deactivate game panel
4265 if (game_just_ended)
4266 game.panel.active = FALSE;
4268 game.request_active = TRUE;
4270 setRequestPosition(&sx, &sy, FALSE);
4272 button_status = MB_RELEASED;
4274 request_gadget_id = -1;
4279 if (game_just_ended)
4281 // the MM game engine does not use a special (scrollable) field buffer
4282 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4283 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4285 HandleGameActions();
4287 SetDrawtoField(DRAW_TO_BACKBUFFER);
4289 if (global.use_envelope_request)
4291 // copy current state of request area to middle of playfield area
4292 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4300 while (NextValidEvent(&event))
4304 case EVENT_BUTTONPRESS:
4305 case EVENT_BUTTONRELEASE:
4306 case EVENT_MOTIONNOTIFY:
4310 if (event.type == EVENT_MOTIONNOTIFY)
4315 motion_status = TRUE;
4316 mx = ((MotionEvent *) &event)->x;
4317 my = ((MotionEvent *) &event)->y;
4321 motion_status = FALSE;
4322 mx = ((ButtonEvent *) &event)->x;
4323 my = ((ButtonEvent *) &event)->y;
4324 if (event.type == EVENT_BUTTONPRESS)
4325 button_status = ((ButtonEvent *) &event)->button;
4327 button_status = MB_RELEASED;
4330 // this sets 'request_gadget_id'
4331 HandleGadgets(mx, my, button_status);
4333 switch (request_gadget_id)
4335 case TOOL_CTRL_ID_YES:
4336 case TOOL_CTRL_ID_TOUCH_YES:
4339 case TOOL_CTRL_ID_NO:
4340 case TOOL_CTRL_ID_TOUCH_NO:
4343 case TOOL_CTRL_ID_CONFIRM:
4344 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4345 result = TRUE | FALSE;
4348 case TOOL_CTRL_ID_PLAYER_1:
4351 case TOOL_CTRL_ID_PLAYER_2:
4354 case TOOL_CTRL_ID_PLAYER_3:
4357 case TOOL_CTRL_ID_PLAYER_4:
4362 // only check clickable animations if no request gadget clicked
4363 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4370 case SDL_WINDOWEVENT:
4371 HandleWindowEvent((WindowEvent *) &event);
4374 case SDL_APP_WILLENTERBACKGROUND:
4375 case SDL_APP_DIDENTERBACKGROUND:
4376 case SDL_APP_WILLENTERFOREGROUND:
4377 case SDL_APP_DIDENTERFOREGROUND:
4378 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4381 case EVENT_KEYPRESS:
4383 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4388 if (req_state & REQ_CONFIRM)
4397 #if defined(KSYM_Rewind)
4398 case KSYM_Rewind: // for Amazon Fire TV remote
4407 #if defined(KSYM_FastForward)
4408 case KSYM_FastForward: // for Amazon Fire TV remote
4414 HandleKeysDebug(key, KEY_PRESSED);
4418 if (req_state & REQ_PLAYER)
4420 int old_player_nr = setup.network_player_nr;
4423 result = old_player_nr + 1;
4428 result = old_player_nr + 1;
4459 case EVENT_KEYRELEASE:
4460 ClearPlayerAction();
4463 case SDL_CONTROLLERBUTTONDOWN:
4464 switch (event.cbutton.button)
4466 case SDL_CONTROLLER_BUTTON_A:
4467 case SDL_CONTROLLER_BUTTON_X:
4468 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4469 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4473 case SDL_CONTROLLER_BUTTON_B:
4474 case SDL_CONTROLLER_BUTTON_Y:
4475 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4476 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4477 case SDL_CONTROLLER_BUTTON_BACK:
4482 if (req_state & REQ_PLAYER)
4484 int old_player_nr = setup.network_player_nr;
4487 result = old_player_nr + 1;
4489 switch (event.cbutton.button)
4491 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4492 case SDL_CONTROLLER_BUTTON_Y:
4496 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4497 case SDL_CONTROLLER_BUTTON_B:
4501 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4502 case SDL_CONTROLLER_BUTTON_A:
4506 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4507 case SDL_CONTROLLER_BUTTON_X:
4518 case SDL_CONTROLLERBUTTONUP:
4519 HandleJoystickEvent(&event);
4520 ClearPlayerAction();
4524 HandleOtherEvents(&event);
4529 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4531 int joy = AnyJoystick();
4533 if (joy & JOY_BUTTON_1)
4535 else if (joy & JOY_BUTTON_2)
4538 else if (AnyJoystick())
4540 int joy = AnyJoystick();
4542 if (req_state & REQ_PLAYER)
4546 else if (joy & JOY_RIGHT)
4548 else if (joy & JOY_DOWN)
4550 else if (joy & JOY_LEFT)
4555 if (game_just_ended)
4557 if (global.use_envelope_request)
4559 // copy back current state of pressed buttons inside request area
4560 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4567 game.request_active = FALSE;
4572 static boolean RequestDoor(char *text, unsigned int req_state)
4574 unsigned int old_door_state;
4575 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4576 int font_nr = FONT_TEXT_2;
4581 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4583 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4584 font_nr = FONT_TEXT_1;
4587 if (game_status == GAME_MODE_PLAYING)
4588 BlitScreenToBitmap(backbuffer);
4590 // disable deactivated drawing when quick-loading level tape recording
4591 if (tape.playing && tape.deactivate_display)
4592 TapeDeactivateDisplayOff(TRUE);
4594 SetMouseCursor(CURSOR_DEFAULT);
4596 // pause network game while waiting for request to answer
4597 if (network.enabled &&
4598 game_status == GAME_MODE_PLAYING &&
4599 !game.all_players_gone &&
4600 req_state & REQUEST_WAIT_FOR_INPUT)
4601 SendToServer_PausePlaying();
4603 old_door_state = GetDoorState();
4605 // simulate releasing mouse button over last gadget, if still pressed
4607 HandleGadgets(-1, -1, 0);
4611 // draw released gadget before proceeding
4614 if (old_door_state & DOOR_OPEN_1)
4616 CloseDoor(DOOR_CLOSE_1);
4618 // save old door content
4619 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4620 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4623 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4624 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4626 // clear door drawing field
4627 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4629 // force DOOR font inside door area
4630 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4632 // write text for request
4633 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4635 char text_line[max_request_line_len + 1];
4641 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4643 tc = *(text_ptr + tx);
4644 // if (!tc || tc == ' ')
4645 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4649 if ((tc == '?' || tc == '!') && tl == 0)
4659 strncpy(text_line, text_ptr, tl);
4662 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4663 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4664 text_line, font_nr);
4666 text_ptr += tl + (tc == ' ' ? 1 : 0);
4667 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4672 if (req_state & REQ_ASK)
4674 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4675 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4676 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4677 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4679 else if (req_state & REQ_CONFIRM)
4681 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4682 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4684 else if (req_state & REQ_PLAYER)
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4687 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4688 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4689 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4692 // copy request gadgets to door backbuffer
4693 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4695 OpenDoor(DOOR_OPEN_1);
4697 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4699 if (game_status == GAME_MODE_PLAYING)
4701 SetPanelBackground();
4702 SetDrawBackgroundMask(REDRAW_DOOR_1);
4706 SetDrawBackgroundMask(REDRAW_FIELD);
4712 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4714 // ---------- handle request buttons ----------
4715 result = RequestHandleEvents(req_state);
4719 if (!(req_state & REQ_STAY_OPEN))
4721 CloseDoor(DOOR_CLOSE_1);
4723 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4724 (req_state & REQ_REOPEN))
4725 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4730 if (game_status == GAME_MODE_PLAYING)
4732 SetPanelBackground();
4733 SetDrawBackgroundMask(REDRAW_DOOR_1);
4737 SetDrawBackgroundMask(REDRAW_FIELD);
4740 // continue network game after request
4741 if (network.enabled &&
4742 game_status == GAME_MODE_PLAYING &&
4743 !game.all_players_gone &&
4744 req_state & REQUEST_WAIT_FOR_INPUT)
4745 SendToServer_ContinuePlaying();
4747 // restore deactivated drawing when quick-loading level tape recording
4748 if (tape.playing && tape.deactivate_display)
4749 TapeDeactivateDisplayOn();
4754 static boolean RequestEnvelope(char *text, unsigned int req_state)
4758 if (game_status == GAME_MODE_PLAYING)
4759 BlitScreenToBitmap(backbuffer);
4761 // disable deactivated drawing when quick-loading level tape recording
4762 if (tape.playing && tape.deactivate_display)
4763 TapeDeactivateDisplayOff(TRUE);
4765 SetMouseCursor(CURSOR_DEFAULT);
4767 // pause network game while waiting for request to answer
4768 if (network.enabled &&
4769 game_status == GAME_MODE_PLAYING &&
4770 !game.all_players_gone &&
4771 req_state & REQUEST_WAIT_FOR_INPUT)
4772 SendToServer_PausePlaying();
4774 // simulate releasing mouse button over last gadget, if still pressed
4776 HandleGadgets(-1, -1, 0);
4780 // (replace with setting corresponding request background)
4781 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4782 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4784 // clear door drawing field
4785 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4787 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4789 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4791 if (game_status == GAME_MODE_PLAYING)
4793 SetPanelBackground();
4794 SetDrawBackgroundMask(REDRAW_DOOR_1);
4798 SetDrawBackgroundMask(REDRAW_FIELD);
4804 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4806 // ---------- handle request buttons ----------
4807 result = RequestHandleEvents(req_state);
4811 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4815 if (game_status == GAME_MODE_PLAYING)
4817 SetPanelBackground();
4818 SetDrawBackgroundMask(REDRAW_DOOR_1);
4822 SetDrawBackgroundMask(REDRAW_FIELD);
4825 // continue network game after request
4826 if (network.enabled &&
4827 game_status == GAME_MODE_PLAYING &&
4828 !game.all_players_gone &&
4829 req_state & REQUEST_WAIT_FOR_INPUT)
4830 SendToServer_ContinuePlaying();
4832 // restore deactivated drawing when quick-loading level tape recording
4833 if (tape.playing && tape.deactivate_display)
4834 TapeDeactivateDisplayOn();
4839 boolean Request(char *text, unsigned int req_state)
4841 boolean overlay_enabled = GetOverlayEnabled();
4844 SetOverlayEnabled(FALSE);
4846 if (global.use_envelope_request)
4847 result = RequestEnvelope(text, req_state);
4849 result = RequestDoor(text, req_state);
4851 SetOverlayEnabled(overlay_enabled);
4856 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4858 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4859 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4862 if (dpo1->sort_priority != dpo2->sort_priority)
4863 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4865 compare_result = dpo1->nr - dpo2->nr;
4867 return compare_result;
4870 void InitGraphicCompatibilityInfo_Doors(void)
4876 struct DoorInfo *door;
4880 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4881 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4883 { -1, -1, -1, NULL }
4885 struct Rect door_rect_list[] =
4887 { DX, DY, DXSIZE, DYSIZE },
4888 { VX, VY, VXSIZE, VYSIZE }
4892 for (i = 0; doors[i].door_token != -1; i++)
4894 int door_token = doors[i].door_token;
4895 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4896 int part_1 = doors[i].part_1;
4897 int part_8 = doors[i].part_8;
4898 int part_2 = part_1 + 1;
4899 int part_3 = part_1 + 2;
4900 struct DoorInfo *door = doors[i].door;
4901 struct Rect *door_rect = &door_rect_list[door_index];
4902 boolean door_gfx_redefined = FALSE;
4904 // check if any door part graphic definitions have been redefined
4906 for (j = 0; door_part_controls[j].door_token != -1; j++)
4908 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4909 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4911 if (dpc->door_token == door_token && fi->redefined)
4912 door_gfx_redefined = TRUE;
4915 // check for old-style door graphic/animation modifications
4917 if (!door_gfx_redefined)
4919 if (door->anim_mode & ANIM_STATIC_PANEL)
4921 door->panel.step_xoffset = 0;
4922 door->panel.step_yoffset = 0;
4925 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4927 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4928 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4929 int num_door_steps, num_panel_steps;
4931 // remove door part graphics other than the two default wings
4933 for (j = 0; door_part_controls[j].door_token != -1; j++)
4935 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4936 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4938 if (dpc->graphic >= part_3 &&
4939 dpc->graphic <= part_8)
4943 // set graphics and screen positions of the default wings
4945 g_part_1->width = door_rect->width;
4946 g_part_1->height = door_rect->height;
4947 g_part_2->width = door_rect->width;
4948 g_part_2->height = door_rect->height;
4949 g_part_2->src_x = door_rect->width;
4950 g_part_2->src_y = g_part_1->src_y;
4952 door->part_2.x = door->part_1.x;
4953 door->part_2.y = door->part_1.y;
4955 if (door->width != -1)
4957 g_part_1->width = door->width;
4958 g_part_2->width = door->width;
4960 // special treatment for graphics and screen position of right wing
4961 g_part_2->src_x += door_rect->width - door->width;
4962 door->part_2.x += door_rect->width - door->width;
4965 if (door->height != -1)
4967 g_part_1->height = door->height;
4968 g_part_2->height = door->height;
4970 // special treatment for graphics and screen position of bottom wing
4971 g_part_2->src_y += door_rect->height - door->height;
4972 door->part_2.y += door_rect->height - door->height;
4975 // set animation delays for the default wings and panels
4977 door->part_1.step_delay = door->step_delay;
4978 door->part_2.step_delay = door->step_delay;
4979 door->panel.step_delay = door->step_delay;
4981 // set animation draw order for the default wings
4983 door->part_1.sort_priority = 2; // draw left wing over ...
4984 door->part_2.sort_priority = 1; // ... right wing
4986 // set animation draw offset for the default wings
4988 if (door->anim_mode & ANIM_HORIZONTAL)
4990 door->part_1.step_xoffset = door->step_offset;
4991 door->part_1.step_yoffset = 0;
4992 door->part_2.step_xoffset = door->step_offset * -1;
4993 door->part_2.step_yoffset = 0;
4995 num_door_steps = g_part_1->width / door->step_offset;
4997 else // ANIM_VERTICAL
4999 door->part_1.step_xoffset = 0;
5000 door->part_1.step_yoffset = door->step_offset;
5001 door->part_2.step_xoffset = 0;
5002 door->part_2.step_yoffset = door->step_offset * -1;
5004 num_door_steps = g_part_1->height / door->step_offset;
5007 // set animation draw offset for the default panels
5009 if (door->step_offset > 1)
5011 num_panel_steps = 2 * door_rect->height / door->step_offset;
5012 door->panel.start_step = num_panel_steps - num_door_steps;
5013 door->panel.start_step_closing = door->panel.start_step;
5017 num_panel_steps = door_rect->height / door->step_offset;
5018 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5019 door->panel.start_step_closing = door->panel.start_step;
5020 door->panel.step_delay *= 2;
5027 void InitDoors(void)
5031 for (i = 0; door_part_controls[i].door_token != -1; i++)
5033 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5034 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5036 // initialize "start_step_opening" and "start_step_closing", if needed
5037 if (dpc->pos->start_step_opening == 0 &&
5038 dpc->pos->start_step_closing == 0)
5040 // dpc->pos->start_step_opening = dpc->pos->start_step;
5041 dpc->pos->start_step_closing = dpc->pos->start_step;
5044 // fill structure for door part draw order (sorted below)
5046 dpo->sort_priority = dpc->pos->sort_priority;
5049 // sort door part controls according to sort_priority and graphic number
5050 qsort(door_part_order, MAX_DOOR_PARTS,
5051 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5054 unsigned int OpenDoor(unsigned int door_state)
5056 if (door_state & DOOR_COPY_BACK)
5058 if (door_state & DOOR_OPEN_1)
5059 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5060 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5062 if (door_state & DOOR_OPEN_2)
5063 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5064 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5066 door_state &= ~DOOR_COPY_BACK;
5069 return MoveDoor(door_state);
5072 unsigned int CloseDoor(unsigned int door_state)
5074 unsigned int old_door_state = GetDoorState();
5076 if (!(door_state & DOOR_NO_COPY_BACK))
5078 if (old_door_state & DOOR_OPEN_1)
5079 BlitBitmap(backbuffer, bitmap_db_door_1,
5080 DX, DY, DXSIZE, DYSIZE, 0, 0);
5082 if (old_door_state & DOOR_OPEN_2)
5083 BlitBitmap(backbuffer, bitmap_db_door_2,
5084 VX, VY, VXSIZE, VYSIZE, 0, 0);
5086 door_state &= ~DOOR_NO_COPY_BACK;
5089 return MoveDoor(door_state);
5092 unsigned int GetDoorState(void)
5094 return MoveDoor(DOOR_GET_STATE);
5097 unsigned int SetDoorState(unsigned int door_state)
5099 return MoveDoor(door_state | DOOR_SET_STATE);
5102 static int euclid(int a, int b)
5104 return (b ? euclid(b, a % b) : a);
5107 unsigned int MoveDoor(unsigned int door_state)
5109 struct Rect door_rect_list[] =
5111 { DX, DY, DXSIZE, DYSIZE },
5112 { VX, VY, VXSIZE, VYSIZE }
5114 static int door1 = DOOR_CLOSE_1;
5115 static int door2 = DOOR_CLOSE_2;
5116 unsigned int door_delay = 0;
5117 unsigned int door_delay_value;
5120 if (door_state == DOOR_GET_STATE)
5121 return (door1 | door2);
5123 if (door_state & DOOR_SET_STATE)
5125 if (door_state & DOOR_ACTION_1)
5126 door1 = door_state & DOOR_ACTION_1;
5127 if (door_state & DOOR_ACTION_2)
5128 door2 = door_state & DOOR_ACTION_2;
5130 return (door1 | door2);
5133 if (!(door_state & DOOR_FORCE_REDRAW))
5135 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5136 door_state &= ~DOOR_OPEN_1;
5137 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5138 door_state &= ~DOOR_CLOSE_1;
5139 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5140 door_state &= ~DOOR_OPEN_2;
5141 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5142 door_state &= ~DOOR_CLOSE_2;
5145 if (global.autoplay_leveldir)
5147 door_state |= DOOR_NO_DELAY;
5148 door_state &= ~DOOR_CLOSE_ALL;
5151 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5152 door_state |= DOOR_NO_DELAY;
5154 if (door_state & DOOR_ACTION)
5156 boolean door_panel_drawn[NUM_DOORS];
5157 boolean panel_has_doors[NUM_DOORS];
5158 boolean door_part_skip[MAX_DOOR_PARTS];
5159 boolean door_part_done[MAX_DOOR_PARTS];
5160 boolean door_part_done_all;
5161 int num_steps[MAX_DOOR_PARTS];
5162 int max_move_delay = 0; // delay for complete animations of all doors
5163 int max_step_delay = 0; // delay (ms) between two animation frames
5164 int num_move_steps = 0; // number of animation steps for all doors
5165 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5166 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5167 int current_move_delay = 0;
5171 for (i = 0; i < NUM_DOORS; i++)
5172 panel_has_doors[i] = FALSE;
5174 for (i = 0; i < MAX_DOOR_PARTS; i++)
5176 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5177 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5178 int door_token = dpc->door_token;
5180 door_part_done[i] = FALSE;
5181 door_part_skip[i] = (!(door_state & door_token) ||
5185 for (i = 0; i < MAX_DOOR_PARTS; i++)
5187 int nr = door_part_order[i].nr;
5188 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5189 struct DoorPartPosInfo *pos = dpc->pos;
5190 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5191 int door_token = dpc->door_token;
5192 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5193 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5194 int step_xoffset = ABS(pos->step_xoffset);
5195 int step_yoffset = ABS(pos->step_yoffset);
5196 int step_delay = pos->step_delay;
5197 int current_door_state = door_state & door_token;
5198 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5199 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5200 boolean part_opening = (is_panel ? door_closing : door_opening);
5201 int start_step = (part_opening ? pos->start_step_opening :
5202 pos->start_step_closing);
5203 float move_xsize = (step_xoffset ? g->width : 0);
5204 float move_ysize = (step_yoffset ? g->height : 0);
5205 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5206 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5207 int move_steps = (move_xsteps && move_ysteps ?
5208 MIN(move_xsteps, move_ysteps) :
5209 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5210 int move_delay = move_steps * step_delay;
5212 if (door_part_skip[nr])
5215 max_move_delay = MAX(max_move_delay, move_delay);
5216 max_step_delay = (max_step_delay == 0 ? step_delay :
5217 euclid(max_step_delay, step_delay));
5218 num_steps[nr] = move_steps;
5222 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5224 panel_has_doors[door_index] = TRUE;
5228 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5230 num_move_steps = max_move_delay / max_step_delay;
5231 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5233 door_delay_value = max_step_delay;
5235 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5237 start = num_move_steps - 1;
5241 // opening door sound has priority over simultaneously closing door
5242 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5244 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5246 if (door_state & DOOR_OPEN_1)
5247 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5248 if (door_state & DOOR_OPEN_2)
5249 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5251 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5253 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5255 if (door_state & DOOR_CLOSE_1)
5256 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5257 if (door_state & DOOR_CLOSE_2)
5258 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5262 for (k = start; k < num_move_steps; k++)
5264 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5266 door_part_done_all = TRUE;
5268 for (i = 0; i < NUM_DOORS; i++)
5269 door_panel_drawn[i] = FALSE;
5271 for (i = 0; i < MAX_DOOR_PARTS; i++)
5273 int nr = door_part_order[i].nr;
5274 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5275 struct DoorPartPosInfo *pos = dpc->pos;
5276 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5277 int door_token = dpc->door_token;
5278 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5279 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5280 boolean is_panel_and_door_has_closed = FALSE;
5281 struct Rect *door_rect = &door_rect_list[door_index];
5282 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5284 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5285 int current_door_state = door_state & door_token;
5286 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5287 boolean door_closing = !door_opening;
5288 boolean part_opening = (is_panel ? door_closing : door_opening);
5289 boolean part_closing = !part_opening;
5290 int start_step = (part_opening ? pos->start_step_opening :
5291 pos->start_step_closing);
5292 int step_delay = pos->step_delay;
5293 int step_factor = step_delay / max_step_delay;
5294 int k1 = (step_factor ? k / step_factor + 1 : k);
5295 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5296 int kk = MAX(0, k2);
5299 int src_x, src_y, src_xx, src_yy;
5300 int dst_x, dst_y, dst_xx, dst_yy;
5303 if (door_part_skip[nr])
5306 if (!(door_state & door_token))
5314 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5315 int kk_door = MAX(0, k2_door);
5316 int sync_frame = kk_door * door_delay_value;
5317 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5319 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5320 &g_src_x, &g_src_y);
5325 if (!door_panel_drawn[door_index])
5327 ClearRectangle(drawto, door_rect->x, door_rect->y,
5328 door_rect->width, door_rect->height);
5330 door_panel_drawn[door_index] = TRUE;
5333 // draw opening or closing door parts
5335 if (pos->step_xoffset < 0) // door part on right side
5338 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5341 if (dst_xx + width > door_rect->width)
5342 width = door_rect->width - dst_xx;
5344 else // door part on left side
5347 dst_xx = pos->x - kk * pos->step_xoffset;
5351 src_xx = ABS(dst_xx);
5355 width = g->width - src_xx;
5357 if (width > door_rect->width)
5358 width = door_rect->width;
5360 // printf("::: k == %d [%d] \n", k, start_step);
5363 if (pos->step_yoffset < 0) // door part on bottom side
5366 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5369 if (dst_yy + height > door_rect->height)
5370 height = door_rect->height - dst_yy;
5372 else // door part on top side
5375 dst_yy = pos->y - kk * pos->step_yoffset;
5379 src_yy = ABS(dst_yy);
5383 height = g->height - src_yy;
5386 src_x = g_src_x + src_xx;
5387 src_y = g_src_y + src_yy;
5389 dst_x = door_rect->x + dst_xx;
5390 dst_y = door_rect->y + dst_yy;
5392 is_panel_and_door_has_closed =
5395 panel_has_doors[door_index] &&
5396 k >= num_move_steps_doors_only - 1);
5398 if (width >= 0 && width <= g->width &&
5399 height >= 0 && height <= g->height &&
5400 !is_panel_and_door_has_closed)
5402 if (is_panel || !pos->draw_masked)
5403 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5406 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5410 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5412 if ((part_opening && (width < 0 || height < 0)) ||
5413 (part_closing && (width >= g->width && height >= g->height)))
5414 door_part_done[nr] = TRUE;
5416 // continue door part animations, but not panel after door has closed
5417 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5418 door_part_done_all = FALSE;
5421 if (!(door_state & DOOR_NO_DELAY))
5425 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5427 current_move_delay += max_step_delay;
5429 // prevent OS (Windows) from complaining about program not responding
5433 if (door_part_done_all)
5437 if (!(door_state & DOOR_NO_DELAY))
5439 // wait for specified door action post delay
5440 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5441 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5442 else if (door_state & DOOR_ACTION_1)
5443 door_delay_value = door_1.post_delay;
5444 else if (door_state & DOOR_ACTION_2)
5445 door_delay_value = door_2.post_delay;
5447 while (!DelayReached(&door_delay, door_delay_value))
5452 if (door_state & DOOR_ACTION_1)
5453 door1 = door_state & DOOR_ACTION_1;
5454 if (door_state & DOOR_ACTION_2)
5455 door2 = door_state & DOOR_ACTION_2;
5457 // draw masked border over door area
5458 DrawMaskedBorder(REDRAW_DOOR_1);
5459 DrawMaskedBorder(REDRAW_DOOR_2);
5461 ClearAutoRepeatKeyEvents();
5463 return (door1 | door2);
5466 static boolean useSpecialEditorDoor(void)
5468 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5469 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5471 // do not draw special editor door if editor border defined or redefined
5472 if (graphic_info[graphic].bitmap != NULL || redefined)
5475 // do not draw special editor door if global border defined to be empty
5476 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5479 // do not draw special editor door if viewport definitions do not match
5483 EY + EYSIZE != VY + VYSIZE)
5489 void DrawSpecialEditorDoor(void)
5491 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5492 int top_border_width = gfx1->width;
5493 int top_border_height = gfx1->height;
5494 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5495 int ex = EX - outer_border;
5496 int ey = EY - outer_border;
5497 int vy = VY - outer_border;
5498 int exsize = EXSIZE + 2 * outer_border;
5500 if (!useSpecialEditorDoor())
5503 // draw bigger level editor toolbox window
5504 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5505 top_border_width, top_border_height, ex, ey - top_border_height);
5506 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5507 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5509 redraw_mask |= REDRAW_ALL;
5512 void UndrawSpecialEditorDoor(void)
5514 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5515 int top_border_width = gfx1->width;
5516 int top_border_height = gfx1->height;
5517 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5518 int ex = EX - outer_border;
5519 int ey = EY - outer_border;
5520 int ey_top = ey - top_border_height;
5521 int exsize = EXSIZE + 2 * outer_border;
5522 int eysize = EYSIZE + 2 * outer_border;
5524 if (!useSpecialEditorDoor())
5527 // draw normal tape recorder window
5528 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5530 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5531 ex, ey_top, top_border_width, top_border_height,
5533 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5534 ex, ey, exsize, eysize, ex, ey);
5538 // if screen background is set to "[NONE]", clear editor toolbox window
5539 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5540 ClearRectangle(drawto, ex, ey, exsize, eysize);
5543 redraw_mask |= REDRAW_ALL;
5547 // ---------- new tool button stuff -------------------------------------------
5552 struct TextPosInfo *pos;
5554 boolean is_touch_button;
5556 } toolbutton_info[NUM_TOOL_BUTTONS] =
5559 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5560 TOOL_CTRL_ID_YES, FALSE, "yes"
5563 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5564 TOOL_CTRL_ID_NO, FALSE, "no"
5567 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5568 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5571 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5572 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5575 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5576 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5579 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5580 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5583 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5584 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5587 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5588 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5591 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5592 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5595 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5596 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5600 void CreateToolButtons(void)
5604 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5606 int graphic = toolbutton_info[i].graphic;
5607 struct GraphicInfo *gfx = &graphic_info[graphic];
5608 struct TextPosInfo *pos = toolbutton_info[i].pos;
5609 struct GadgetInfo *gi;
5610 Bitmap *deco_bitmap = None;
5611 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5612 unsigned int event_mask = GD_EVENT_RELEASED;
5613 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5614 int base_x = (is_touch_button ? 0 : DX);
5615 int base_y = (is_touch_button ? 0 : DY);
5616 int gd_x = gfx->src_x;
5617 int gd_y = gfx->src_y;
5618 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5619 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5624 if (global.use_envelope_request && !is_touch_button)
5626 setRequestPosition(&base_x, &base_y, TRUE);
5628 // check if request buttons are outside of envelope and fix, if needed
5629 if (x < 0 || x + gfx->width > request.width ||
5630 y < 0 || y + gfx->height > request.height)
5632 if (id == TOOL_CTRL_ID_YES)
5635 y = request.height - 2 * request.border_size - gfx->height;
5637 else if (id == TOOL_CTRL_ID_NO)
5639 x = request.width - 2 * request.border_size - gfx->width;
5640 y = request.height - 2 * request.border_size - gfx->height;
5642 else if (id == TOOL_CTRL_ID_CONFIRM)
5644 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5645 y = request.height - 2 * request.border_size - gfx->height;
5647 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5649 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5651 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5652 y = request.height - 2 * request.border_size - gfx->height * 2;
5654 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5655 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5660 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5662 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5664 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5665 pos->size, &deco_bitmap, &deco_x, &deco_y);
5666 deco_xpos = (gfx->width - pos->size) / 2;
5667 deco_ypos = (gfx->height - pos->size) / 2;
5670 gi = CreateGadget(GDI_CUSTOM_ID, id,
5671 GDI_IMAGE_ID, graphic,
5672 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5675 GDI_WIDTH, gfx->width,
5676 GDI_HEIGHT, gfx->height,
5677 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5678 GDI_STATE, GD_BUTTON_UNPRESSED,
5679 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5680 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5681 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5682 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5683 GDI_DECORATION_SIZE, pos->size, pos->size,
5684 GDI_DECORATION_SHIFTING, 1, 1,
5685 GDI_DIRECT_DRAW, FALSE,
5686 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5687 GDI_EVENT_MASK, event_mask,
5688 GDI_CALLBACK_ACTION, HandleToolButtons,
5692 Error(ERR_EXIT, "cannot create gadget");
5694 tool_gadget[id] = gi;
5698 void FreeToolButtons(void)
5702 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5703 FreeGadget(tool_gadget[i]);
5706 static void UnmapToolButtons(void)
5710 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5711 UnmapGadget(tool_gadget[i]);
5714 static void HandleToolButtons(struct GadgetInfo *gi)
5716 request_gadget_id = gi->custom_id;
5719 static struct Mapping_EM_to_RND_object
5722 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5723 boolean is_backside; // backside of moving element
5729 em_object_mapping_list[GAME_TILE_MAX + 1] =
5732 Zborder, FALSE, FALSE,
5736 Zplayer, FALSE, FALSE,
5745 Ztank, FALSE, FALSE,
5749 Zeater, FALSE, FALSE,
5753 Zdynamite, FALSE, FALSE,
5757 Zboom, FALSE, FALSE,
5762 Xchain, FALSE, FALSE,
5763 EL_DEFAULT, ACTION_EXPLODING, -1
5766 Xboom_bug, FALSE, FALSE,
5767 EL_BUG, ACTION_EXPLODING, -1
5770 Xboom_tank, FALSE, FALSE,
5771 EL_SPACESHIP, ACTION_EXPLODING, -1
5774 Xboom_android, FALSE, FALSE,
5775 EL_EMC_ANDROID, ACTION_OTHER, -1
5778 Xboom_1, FALSE, FALSE,
5779 EL_DEFAULT, ACTION_EXPLODING, -1
5782 Xboom_2, FALSE, FALSE,
5783 EL_DEFAULT, ACTION_EXPLODING, -1
5787 Xblank, TRUE, FALSE,
5792 Xsplash_e, FALSE, FALSE,
5793 EL_ACID_SPLASH_RIGHT, -1, -1
5796 Xsplash_w, FALSE, FALSE,
5797 EL_ACID_SPLASH_LEFT, -1, -1
5801 Xplant, TRUE, FALSE,
5802 EL_EMC_PLANT, -1, -1
5805 Yplant, FALSE, FALSE,
5806 EL_EMC_PLANT, -1, -1
5810 Xacid_1, TRUE, FALSE,
5814 Xacid_2, FALSE, FALSE,
5818 Xacid_3, FALSE, FALSE,
5822 Xacid_4, FALSE, FALSE,
5826 Xacid_5, FALSE, FALSE,
5830 Xacid_6, FALSE, FALSE,
5834 Xacid_7, FALSE, FALSE,
5838 Xacid_8, FALSE, FALSE,
5843 Xfake_acid_1, TRUE, FALSE,
5844 EL_EMC_FAKE_ACID, -1, -1
5847 Xfake_acid_2, FALSE, FALSE,
5848 EL_EMC_FAKE_ACID, -1, -1
5851 Xfake_acid_3, FALSE, FALSE,
5852 EL_EMC_FAKE_ACID, -1, -1
5855 Xfake_acid_4, FALSE, FALSE,
5856 EL_EMC_FAKE_ACID, -1, -1
5859 Xfake_acid_5, FALSE, FALSE,
5860 EL_EMC_FAKE_ACID, -1, -1
5863 Xfake_acid_6, FALSE, FALSE,
5864 EL_EMC_FAKE_ACID, -1, -1
5867 Xfake_acid_7, FALSE, FALSE,
5868 EL_EMC_FAKE_ACID, -1, -1
5871 Xfake_acid_8, FALSE, FALSE,
5872 EL_EMC_FAKE_ACID, -1, -1
5876 Xgrass, TRUE, FALSE,
5877 EL_EMC_GRASS, -1, -1
5880 Ygrass_nB, FALSE, FALSE,
5881 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5884 Ygrass_eB, FALSE, FALSE,
5885 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5888 Ygrass_sB, FALSE, FALSE,
5889 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5892 Ygrass_wB, FALSE, FALSE,
5893 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5901 Ydirt_nB, FALSE, FALSE,
5902 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5905 Ydirt_eB, FALSE, FALSE,
5906 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5909 Ydirt_sB, FALSE, FALSE,
5910 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5913 Ydirt_wB, FALSE, FALSE,
5914 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5918 Xandroid, TRUE, FALSE,
5919 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5922 Xandroid_1_n, FALSE, FALSE,
5923 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5926 Xandroid_2_n, FALSE, FALSE,
5927 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5930 Xandroid_1_e, FALSE, FALSE,
5931 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5934 Xandroid_2_e, FALSE, FALSE,
5935 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5938 Xandroid_1_w, FALSE, FALSE,
5939 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5942 Xandroid_2_w, FALSE, FALSE,
5943 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5946 Xandroid_1_s, FALSE, FALSE,
5947 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5950 Xandroid_2_s, FALSE, FALSE,
5951 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5954 Yandroid_n, FALSE, FALSE,
5955 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5958 Yandroid_nB, FALSE, TRUE,
5959 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5962 Yandroid_ne, FALSE, FALSE,
5963 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5966 Yandroid_neB, FALSE, TRUE,
5967 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5970 Yandroid_e, FALSE, FALSE,
5971 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5974 Yandroid_eB, FALSE, TRUE,
5975 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5978 Yandroid_se, FALSE, FALSE,
5979 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5982 Yandroid_seB, FALSE, TRUE,
5983 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5986 Yandroid_s, FALSE, FALSE,
5987 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5990 Yandroid_sB, FALSE, TRUE,
5991 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5994 Yandroid_sw, FALSE, FALSE,
5995 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5998 Yandroid_swB, FALSE, TRUE,
5999 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6002 Yandroid_w, FALSE, FALSE,
6003 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6006 Yandroid_wB, FALSE, TRUE,
6007 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6010 Yandroid_nw, FALSE, FALSE,
6011 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6014 Yandroid_nwB, FALSE, TRUE,
6015 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6019 Xeater_n, TRUE, FALSE,
6020 EL_YAMYAM_UP, -1, -1
6023 Xeater_e, TRUE, FALSE,
6024 EL_YAMYAM_RIGHT, -1, -1
6027 Xeater_w, TRUE, FALSE,
6028 EL_YAMYAM_LEFT, -1, -1
6031 Xeater_s, TRUE, FALSE,
6032 EL_YAMYAM_DOWN, -1, -1
6035 Yeater_n, FALSE, FALSE,
6036 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6039 Yeater_nB, FALSE, TRUE,
6040 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6043 Yeater_e, FALSE, FALSE,
6044 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6047 Yeater_eB, FALSE, TRUE,
6048 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6051 Yeater_s, FALSE, FALSE,
6052 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6055 Yeater_sB, FALSE, TRUE,
6056 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6059 Yeater_w, FALSE, FALSE,
6060 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6063 Yeater_wB, FALSE, TRUE,
6064 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6067 Yeater_stone, FALSE, FALSE,
6068 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6071 Yeater_spring, FALSE, FALSE,
6072 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6076 Xalien, TRUE, FALSE,
6080 Xalien_pause, FALSE, FALSE,
6084 Yalien_n, FALSE, FALSE,
6085 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6088 Yalien_nB, FALSE, TRUE,
6089 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6092 Yalien_e, FALSE, FALSE,
6093 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6096 Yalien_eB, FALSE, TRUE,
6097 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6100 Yalien_s, FALSE, FALSE,
6101 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6104 Yalien_sB, FALSE, TRUE,
6105 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6108 Yalien_w, FALSE, FALSE,
6109 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6112 Yalien_wB, FALSE, TRUE,
6113 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6116 Yalien_stone, FALSE, FALSE,
6117 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6120 Yalien_spring, FALSE, FALSE,
6121 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6125 Xbug_1_n, TRUE, FALSE,
6129 Xbug_1_e, TRUE, FALSE,
6130 EL_BUG_RIGHT, -1, -1
6133 Xbug_1_s, TRUE, FALSE,
6137 Xbug_1_w, TRUE, FALSE,
6141 Xbug_2_n, FALSE, FALSE,
6145 Xbug_2_e, FALSE, FALSE,
6146 EL_BUG_RIGHT, -1, -1
6149 Xbug_2_s, FALSE, FALSE,
6153 Xbug_2_w, FALSE, FALSE,
6157 Ybug_n, FALSE, FALSE,
6158 EL_BUG, ACTION_MOVING, MV_BIT_UP
6161 Ybug_nB, FALSE, TRUE,
6162 EL_BUG, ACTION_MOVING, MV_BIT_UP
6165 Ybug_e, FALSE, FALSE,
6166 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6169 Ybug_eB, FALSE, TRUE,
6170 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6173 Ybug_s, FALSE, FALSE,
6174 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6177 Ybug_sB, FALSE, TRUE,
6178 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6181 Ybug_w, FALSE, FALSE,
6182 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6185 Ybug_wB, FALSE, TRUE,
6186 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6189 Ybug_w_n, FALSE, FALSE,
6190 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6193 Ybug_n_e, FALSE, FALSE,
6194 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6197 Ybug_e_s, FALSE, FALSE,
6198 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6201 Ybug_s_w, FALSE, FALSE,
6202 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6205 Ybug_e_n, FALSE, FALSE,
6206 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6209 Ybug_s_e, FALSE, FALSE,
6210 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6213 Ybug_w_s, FALSE, FALSE,
6214 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6217 Ybug_n_w, FALSE, FALSE,
6218 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6221 Ybug_stone, FALSE, FALSE,
6222 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6225 Ybug_spring, FALSE, FALSE,
6226 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6230 Xtank_1_n, TRUE, FALSE,
6231 EL_SPACESHIP_UP, -1, -1
6234 Xtank_1_e, TRUE, FALSE,
6235 EL_SPACESHIP_RIGHT, -1, -1
6238 Xtank_1_s, TRUE, FALSE,
6239 EL_SPACESHIP_DOWN, -1, -1
6242 Xtank_1_w, TRUE, FALSE,
6243 EL_SPACESHIP_LEFT, -1, -1
6246 Xtank_2_n, FALSE, FALSE,
6247 EL_SPACESHIP_UP, -1, -1
6250 Xtank_2_e, FALSE, FALSE,
6251 EL_SPACESHIP_RIGHT, -1, -1
6254 Xtank_2_s, FALSE, FALSE,
6255 EL_SPACESHIP_DOWN, -1, -1
6258 Xtank_2_w, FALSE, FALSE,
6259 EL_SPACESHIP_LEFT, -1, -1
6262 Ytank_n, FALSE, FALSE,
6263 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6266 Ytank_nB, FALSE, TRUE,
6267 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6270 Ytank_e, FALSE, FALSE,
6271 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6274 Ytank_eB, FALSE, TRUE,
6275 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6278 Ytank_s, FALSE, FALSE,
6279 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6282 Ytank_sB, FALSE, TRUE,
6283 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6286 Ytank_w, FALSE, FALSE,
6287 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6290 Ytank_wB, FALSE, TRUE,
6291 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6294 Ytank_w_n, FALSE, FALSE,
6295 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6298 Ytank_n_e, FALSE, FALSE,
6299 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6302 Ytank_e_s, FALSE, FALSE,
6303 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6306 Ytank_s_w, FALSE, FALSE,
6307 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6310 Ytank_e_n, FALSE, FALSE,
6311 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6314 Ytank_s_e, FALSE, FALSE,
6315 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6318 Ytank_w_s, FALSE, FALSE,
6319 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6322 Ytank_n_w, FALSE, FALSE,
6323 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6326 Ytank_stone, FALSE, FALSE,
6327 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6330 Ytank_spring, FALSE, FALSE,
6331 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6335 Xemerald, TRUE, FALSE,
6339 Xemerald_pause, FALSE, FALSE,
6343 Xemerald_fall, FALSE, FALSE,
6347 Xemerald_shine, FALSE, FALSE,
6348 EL_EMERALD, ACTION_TWINKLING, -1
6351 Yemerald_s, FALSE, FALSE,
6352 EL_EMERALD, ACTION_FALLING, -1
6355 Yemerald_sB, FALSE, TRUE,
6356 EL_EMERALD, ACTION_FALLING, -1
6359 Yemerald_e, FALSE, FALSE,
6360 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6363 Yemerald_eB, FALSE, TRUE,
6364 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6367 Yemerald_w, FALSE, FALSE,
6368 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6371 Yemerald_wB, FALSE, TRUE,
6372 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6375 Yemerald_blank, FALSE, FALSE,
6376 EL_EMERALD, ACTION_COLLECTING, -1
6380 Xdiamond, TRUE, FALSE,
6384 Xdiamond_pause, FALSE, FALSE,
6388 Xdiamond_fall, FALSE, FALSE,
6392 Xdiamond_shine, FALSE, FALSE,
6393 EL_DIAMOND, ACTION_TWINKLING, -1
6396 Ydiamond_s, FALSE, FALSE,
6397 EL_DIAMOND, ACTION_FALLING, -1
6400 Ydiamond_sB, FALSE, TRUE,
6401 EL_DIAMOND, ACTION_FALLING, -1
6404 Ydiamond_e, FALSE, FALSE,
6405 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6408 Ydiamond_eB, FALSE, TRUE,
6409 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6412 Ydiamond_w, FALSE, FALSE,
6413 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6416 Ydiamond_wB, FALSE, TRUE,
6417 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6420 Ydiamond_blank, FALSE, FALSE,
6421 EL_DIAMOND, ACTION_COLLECTING, -1
6424 Ydiamond_stone, FALSE, FALSE,
6425 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6429 Xstone, TRUE, FALSE,
6433 Xstone_pause, FALSE, FALSE,
6437 Xstone_fall, FALSE, FALSE,
6441 Ystone_s, FALSE, FALSE,
6442 EL_ROCK, ACTION_FALLING, -1
6445 Ystone_sB, FALSE, TRUE,
6446 EL_ROCK, ACTION_FALLING, -1
6449 Ystone_e, FALSE, FALSE,
6450 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6453 Ystone_eB, FALSE, TRUE,
6454 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6457 Ystone_w, FALSE, FALSE,
6458 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6461 Ystone_wB, FALSE, TRUE,
6462 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6470 Xbomb_pause, FALSE, FALSE,
6474 Xbomb_fall, FALSE, FALSE,
6478 Ybomb_s, FALSE, FALSE,
6479 EL_BOMB, ACTION_FALLING, -1
6482 Ybomb_sB, FALSE, TRUE,
6483 EL_BOMB, ACTION_FALLING, -1
6486 Ybomb_e, FALSE, FALSE,
6487 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6490 Ybomb_eB, FALSE, TRUE,
6491 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6494 Ybomb_w, FALSE, FALSE,
6495 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6498 Ybomb_wB, FALSE, TRUE,
6499 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6502 Ybomb_blank, FALSE, FALSE,
6503 EL_BOMB, ACTION_ACTIVATING, -1
6511 Xnut_pause, FALSE, FALSE,
6515 Xnut_fall, FALSE, FALSE,
6519 Ynut_s, FALSE, FALSE,
6520 EL_NUT, ACTION_FALLING, -1
6523 Ynut_sB, FALSE, TRUE,
6524 EL_NUT, ACTION_FALLING, -1
6527 Ynut_e, FALSE, FALSE,
6528 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6531 Ynut_eB, FALSE, TRUE,
6532 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6535 Ynut_w, FALSE, FALSE,
6536 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6539 Ynut_wB, FALSE, TRUE,
6540 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6543 Ynut_stone, FALSE, FALSE,
6544 EL_NUT, ACTION_BREAKING, -1
6548 Xspring, TRUE, FALSE,
6552 Xspring_pause, FALSE, FALSE,
6556 Xspring_e, FALSE, FALSE,
6560 Xspring_w, FALSE, FALSE,
6564 Xspring_fall, FALSE, FALSE,
6568 Yspring_s, FALSE, FALSE,
6569 EL_SPRING, ACTION_FALLING, -1
6572 Yspring_sB, FALSE, TRUE,
6573 EL_SPRING, ACTION_FALLING, -1
6576 Yspring_e, FALSE, FALSE,
6577 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6580 Yspring_eB, FALSE, TRUE,
6581 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6584 Yspring_w, FALSE, FALSE,
6585 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6588 Yspring_wB, FALSE, TRUE,
6589 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6592 Yspring_alien_e, FALSE, FALSE,
6593 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6596 Yspring_alien_eB, FALSE, TRUE,
6597 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6600 Yspring_alien_w, FALSE, FALSE,
6601 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6604 Yspring_alien_wB, FALSE, TRUE,
6605 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6609 Xpush_emerald_e, FALSE, FALSE,
6610 EL_EMERALD, -1, MV_BIT_RIGHT
6613 Xpush_emerald_w, FALSE, FALSE,
6614 EL_EMERALD, -1, MV_BIT_LEFT
6617 Xpush_diamond_e, FALSE, FALSE,
6618 EL_DIAMOND, -1, MV_BIT_RIGHT
6621 Xpush_diamond_w, FALSE, FALSE,
6622 EL_DIAMOND, -1, MV_BIT_LEFT
6625 Xpush_stone_e, FALSE, FALSE,
6626 EL_ROCK, -1, MV_BIT_RIGHT
6629 Xpush_stone_w, FALSE, FALSE,
6630 EL_ROCK, -1, MV_BIT_LEFT
6633 Xpush_bomb_e, FALSE, FALSE,
6634 EL_BOMB, -1, MV_BIT_RIGHT
6637 Xpush_bomb_w, FALSE, FALSE,
6638 EL_BOMB, -1, MV_BIT_LEFT
6641 Xpush_nut_e, FALSE, FALSE,
6642 EL_NUT, -1, MV_BIT_RIGHT
6645 Xpush_nut_w, FALSE, FALSE,
6646 EL_NUT, -1, MV_BIT_LEFT
6649 Xpush_spring_e, FALSE, FALSE,
6650 EL_SPRING, -1, MV_BIT_RIGHT
6653 Xpush_spring_w, FALSE, FALSE,
6654 EL_SPRING, -1, MV_BIT_LEFT
6658 Xdynamite, TRUE, FALSE,
6659 EL_EM_DYNAMITE, -1, -1
6662 Ydynamite_blank, FALSE, FALSE,
6663 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6666 Xdynamite_1, TRUE, FALSE,
6667 EL_EM_DYNAMITE_ACTIVE, -1, -1
6670 Xdynamite_2, FALSE, FALSE,
6671 EL_EM_DYNAMITE_ACTIVE, -1, -1
6674 Xdynamite_3, FALSE, FALSE,
6675 EL_EM_DYNAMITE_ACTIVE, -1, -1
6678 Xdynamite_4, FALSE, FALSE,
6679 EL_EM_DYNAMITE_ACTIVE, -1, -1
6683 Xkey_1, TRUE, FALSE,
6687 Xkey_2, TRUE, FALSE,
6691 Xkey_3, TRUE, FALSE,
6695 Xkey_4, TRUE, FALSE,
6699 Xkey_5, TRUE, FALSE,
6700 EL_EMC_KEY_5, -1, -1
6703 Xkey_6, TRUE, FALSE,
6704 EL_EMC_KEY_6, -1, -1
6707 Xkey_7, TRUE, FALSE,
6708 EL_EMC_KEY_7, -1, -1
6711 Xkey_8, TRUE, FALSE,
6712 EL_EMC_KEY_8, -1, -1
6716 Xdoor_1, TRUE, FALSE,
6717 EL_EM_GATE_1, -1, -1
6720 Xdoor_2, TRUE, FALSE,
6721 EL_EM_GATE_2, -1, -1
6724 Xdoor_3, TRUE, FALSE,
6725 EL_EM_GATE_3, -1, -1
6728 Xdoor_4, TRUE, FALSE,
6729 EL_EM_GATE_4, -1, -1
6732 Xdoor_5, TRUE, FALSE,
6733 EL_EMC_GATE_5, -1, -1
6736 Xdoor_6, TRUE, FALSE,
6737 EL_EMC_GATE_6, -1, -1
6740 Xdoor_7, TRUE, FALSE,
6741 EL_EMC_GATE_7, -1, -1
6744 Xdoor_8, TRUE, FALSE,
6745 EL_EMC_GATE_8, -1, -1
6749 Xfake_door_1, TRUE, FALSE,
6750 EL_EM_GATE_1_GRAY, -1, -1
6753 Xfake_door_2, TRUE, FALSE,
6754 EL_EM_GATE_2_GRAY, -1, -1
6757 Xfake_door_3, TRUE, FALSE,
6758 EL_EM_GATE_3_GRAY, -1, -1
6761 Xfake_door_4, TRUE, FALSE,
6762 EL_EM_GATE_4_GRAY, -1, -1
6765 Xfake_door_5, TRUE, FALSE,
6766 EL_EMC_GATE_5_GRAY, -1, -1
6769 Xfake_door_6, TRUE, FALSE,
6770 EL_EMC_GATE_6_GRAY, -1, -1
6773 Xfake_door_7, TRUE, FALSE,
6774 EL_EMC_GATE_7_GRAY, -1, -1
6777 Xfake_door_8, TRUE, FALSE,
6778 EL_EMC_GATE_8_GRAY, -1, -1
6782 Xballoon, TRUE, FALSE,
6786 Yballoon_n, FALSE, FALSE,
6787 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6790 Yballoon_nB, FALSE, TRUE,
6791 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6794 Yballoon_e, FALSE, FALSE,
6795 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6798 Yballoon_eB, FALSE, TRUE,
6799 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6802 Yballoon_s, FALSE, FALSE,
6803 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6806 Yballoon_sB, FALSE, TRUE,
6807 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6810 Yballoon_w, FALSE, FALSE,
6811 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6814 Yballoon_wB, FALSE, TRUE,
6815 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6819 Xball_1, TRUE, FALSE,
6820 EL_EMC_MAGIC_BALL, -1, -1
6823 Yball_1, FALSE, FALSE,
6824 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6827 Xball_2, FALSE, FALSE,
6828 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6831 Yball_2, FALSE, FALSE,
6832 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6835 Yball_blank, FALSE, FALSE,
6836 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6840 Xamoeba_1, TRUE, FALSE,
6841 EL_AMOEBA_DRY, ACTION_OTHER, -1
6844 Xamoeba_2, FALSE, FALSE,
6845 EL_AMOEBA_DRY, ACTION_OTHER, -1
6848 Xamoeba_3, FALSE, FALSE,
6849 EL_AMOEBA_DRY, ACTION_OTHER, -1
6852 Xamoeba_4, FALSE, FALSE,
6853 EL_AMOEBA_DRY, ACTION_OTHER, -1
6856 Xamoeba_5, TRUE, FALSE,
6857 EL_AMOEBA_WET, ACTION_OTHER, -1
6860 Xamoeba_6, FALSE, FALSE,
6861 EL_AMOEBA_WET, ACTION_OTHER, -1
6864 Xamoeba_7, FALSE, FALSE,
6865 EL_AMOEBA_WET, ACTION_OTHER, -1
6868 Xamoeba_8, FALSE, FALSE,
6869 EL_AMOEBA_WET, ACTION_OTHER, -1
6873 Xdrip, FALSE, FALSE,
6874 EL_AMOEBA_DROP, ACTION_GROWING, -1
6877 Xdrip_fall, TRUE, FALSE,
6878 EL_AMOEBA_DROP, -1, -1
6881 Xdrip_stretch, FALSE, FALSE,
6882 EL_AMOEBA_DROP, ACTION_FALLING, -1
6885 Xdrip_stretchB, FALSE, TRUE,
6886 EL_AMOEBA_DROP, ACTION_FALLING, -1
6889 Ydrip_1_s, FALSE, FALSE,
6890 EL_AMOEBA_DROP, ACTION_FALLING, -1
6893 Ydrip_1_sB, FALSE, TRUE,
6894 EL_AMOEBA_DROP, ACTION_FALLING, -1
6897 Ydrip_2_s, FALSE, FALSE,
6898 EL_AMOEBA_DROP, ACTION_FALLING, -1
6901 Ydrip_2_sB, FALSE, TRUE,
6902 EL_AMOEBA_DROP, ACTION_FALLING, -1
6906 Xwonderwall, TRUE, FALSE,
6907 EL_MAGIC_WALL, -1, -1
6910 Ywonderwall, FALSE, FALSE,
6911 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6915 Xwheel, TRUE, FALSE,
6916 EL_ROBOT_WHEEL, -1, -1
6919 Ywheel, FALSE, FALSE,
6920 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6924 Xswitch, TRUE, FALSE,
6925 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6928 Yswitch, FALSE, FALSE,
6929 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6933 Xbumper, TRUE, FALSE,
6934 EL_EMC_SPRING_BUMPER, -1, -1
6937 Ybumper, FALSE, FALSE,
6938 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6942 Xacid_nw, TRUE, FALSE,
6943 EL_ACID_POOL_TOPLEFT, -1, -1
6946 Xacid_ne, TRUE, FALSE,
6947 EL_ACID_POOL_TOPRIGHT, -1, -1
6950 Xacid_sw, TRUE, FALSE,
6951 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6954 Xacid_s, TRUE, FALSE,
6955 EL_ACID_POOL_BOTTOM, -1, -1
6958 Xacid_se, TRUE, FALSE,
6959 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6963 Xfake_blank, TRUE, FALSE,
6964 EL_INVISIBLE_WALL, -1, -1
6967 Yfake_blank, FALSE, FALSE,
6968 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6972 Xfake_grass, TRUE, FALSE,
6973 EL_EMC_FAKE_GRASS, -1, -1
6976 Yfake_grass, FALSE, FALSE,
6977 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6981 Xfake_amoeba, TRUE, FALSE,
6982 EL_EMC_DRIPPER, -1, -1
6985 Yfake_amoeba, FALSE, FALSE,
6986 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6990 Xlenses, TRUE, FALSE,
6991 EL_EMC_LENSES, -1, -1
6995 Xmagnify, TRUE, FALSE,
6996 EL_EMC_MAGNIFIER, -1, -1
7001 EL_QUICKSAND_EMPTY, -1, -1
7004 Xsand_stone, TRUE, FALSE,
7005 EL_QUICKSAND_FULL, -1, -1
7008 Xsand_stonein_1, FALSE, TRUE,
7009 EL_ROCK, ACTION_FILLING, -1
7012 Xsand_stonein_2, FALSE, TRUE,
7013 EL_ROCK, ACTION_FILLING, -1
7016 Xsand_stonein_3, FALSE, TRUE,
7017 EL_ROCK, ACTION_FILLING, -1
7020 Xsand_stonein_4, FALSE, TRUE,
7021 EL_ROCK, ACTION_FILLING, -1
7024 Xsand_sandstone_1, FALSE, FALSE,
7025 EL_QUICKSAND_FILLING, -1, -1
7028 Xsand_sandstone_2, FALSE, FALSE,
7029 EL_QUICKSAND_FILLING, -1, -1
7032 Xsand_sandstone_3, FALSE, FALSE,
7033 EL_QUICKSAND_FILLING, -1, -1
7036 Xsand_sandstone_4, FALSE, FALSE,
7037 EL_QUICKSAND_FILLING, -1, -1
7040 Xsand_stonesand_1, FALSE, FALSE,
7041 EL_QUICKSAND_EMPTYING, -1, -1
7044 Xsand_stonesand_2, FALSE, FALSE,
7045 EL_QUICKSAND_EMPTYING, -1, -1
7048 Xsand_stonesand_3, FALSE, FALSE,
7049 EL_QUICKSAND_EMPTYING, -1, -1
7052 Xsand_stonesand_4, FALSE, FALSE,
7053 EL_QUICKSAND_EMPTYING, -1, -1
7056 Xsand_stoneout_1, FALSE, FALSE,
7057 EL_ROCK, ACTION_EMPTYING, -1
7060 Xsand_stoneout_2, FALSE, FALSE,
7061 EL_ROCK, ACTION_EMPTYING, -1
7064 Xsand_stonesand_quickout_1, FALSE, FALSE,
7065 EL_QUICKSAND_EMPTYING, -1, -1
7068 Xsand_stonesand_quickout_2, FALSE, FALSE,
7069 EL_QUICKSAND_EMPTYING, -1, -1
7073 Xslide_ns, TRUE, FALSE,
7074 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7077 Yslide_ns_blank, FALSE, FALSE,
7078 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7081 Xslide_ew, TRUE, FALSE,
7082 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7085 Yslide_ew_blank, FALSE, FALSE,
7086 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7090 Xwind_n, TRUE, FALSE,
7091 EL_BALLOON_SWITCH_UP, -1, -1
7094 Xwind_e, TRUE, FALSE,
7095 EL_BALLOON_SWITCH_RIGHT, -1, -1
7098 Xwind_s, TRUE, FALSE,
7099 EL_BALLOON_SWITCH_DOWN, -1, -1
7102 Xwind_w, TRUE, FALSE,
7103 EL_BALLOON_SWITCH_LEFT, -1, -1
7106 Xwind_any, TRUE, FALSE,
7107 EL_BALLOON_SWITCH_ANY, -1, -1
7110 Xwind_stop, TRUE, FALSE,
7111 EL_BALLOON_SWITCH_NONE, -1, -1
7116 EL_EM_EXIT_CLOSED, -1, -1
7119 Xexit_1, TRUE, FALSE,
7120 EL_EM_EXIT_OPEN, -1, -1
7123 Xexit_2, FALSE, FALSE,
7124 EL_EM_EXIT_OPEN, -1, -1
7127 Xexit_3, FALSE, FALSE,
7128 EL_EM_EXIT_OPEN, -1, -1
7132 Xpause, FALSE, FALSE,
7137 Xwall_1, TRUE, FALSE,
7141 Xwall_2, TRUE, FALSE,
7142 EL_EMC_WALL_14, -1, -1
7145 Xwall_3, TRUE, FALSE,
7146 EL_EMC_WALL_15, -1, -1
7149 Xwall_4, TRUE, FALSE,
7150 EL_EMC_WALL_16, -1, -1
7154 Xroundwall_1, TRUE, FALSE,
7155 EL_WALL_SLIPPERY, -1, -1
7158 Xroundwall_2, TRUE, FALSE,
7159 EL_EMC_WALL_SLIPPERY_2, -1, -1
7162 Xroundwall_3, TRUE, FALSE,
7163 EL_EMC_WALL_SLIPPERY_3, -1, -1
7166 Xroundwall_4, TRUE, FALSE,
7167 EL_EMC_WALL_SLIPPERY_4, -1, -1
7171 Xsteel_1, TRUE, FALSE,
7172 EL_STEELWALL, -1, -1
7175 Xsteel_2, TRUE, FALSE,
7176 EL_EMC_STEELWALL_2, -1, -1
7179 Xsteel_3, TRUE, FALSE,
7180 EL_EMC_STEELWALL_3, -1, -1
7183 Xsteel_4, TRUE, FALSE,
7184 EL_EMC_STEELWALL_4, -1, -1
7188 Xdecor_1, TRUE, FALSE,
7189 EL_EMC_WALL_8, -1, -1
7192 Xdecor_2, TRUE, FALSE,
7193 EL_EMC_WALL_6, -1, -1
7196 Xdecor_3, TRUE, FALSE,
7197 EL_EMC_WALL_4, -1, -1
7200 Xdecor_4, TRUE, FALSE,
7201 EL_EMC_WALL_7, -1, -1
7204 Xdecor_5, TRUE, FALSE,
7205 EL_EMC_WALL_5, -1, -1
7208 Xdecor_6, TRUE, FALSE,
7209 EL_EMC_WALL_9, -1, -1
7212 Xdecor_7, TRUE, FALSE,
7213 EL_EMC_WALL_10, -1, -1
7216 Xdecor_8, TRUE, FALSE,
7217 EL_EMC_WALL_1, -1, -1
7220 Xdecor_9, TRUE, FALSE,
7221 EL_EMC_WALL_2, -1, -1
7224 Xdecor_10, TRUE, FALSE,
7225 EL_EMC_WALL_3, -1, -1
7228 Xdecor_11, TRUE, FALSE,
7229 EL_EMC_WALL_11, -1, -1
7232 Xdecor_12, TRUE, FALSE,
7233 EL_EMC_WALL_12, -1, -1
7237 Xalpha_0, TRUE, FALSE,
7238 EL_CHAR('0'), -1, -1
7241 Xalpha_1, TRUE, FALSE,
7242 EL_CHAR('1'), -1, -1
7245 Xalpha_2, TRUE, FALSE,
7246 EL_CHAR('2'), -1, -1
7249 Xalpha_3, TRUE, FALSE,
7250 EL_CHAR('3'), -1, -1
7253 Xalpha_4, TRUE, FALSE,
7254 EL_CHAR('4'), -1, -1
7257 Xalpha_5, TRUE, FALSE,
7258 EL_CHAR('5'), -1, -1
7261 Xalpha_6, TRUE, FALSE,
7262 EL_CHAR('6'), -1, -1
7265 Xalpha_7, TRUE, FALSE,
7266 EL_CHAR('7'), -1, -1
7269 Xalpha_8, TRUE, FALSE,
7270 EL_CHAR('8'), -1, -1
7273 Xalpha_9, TRUE, FALSE,
7274 EL_CHAR('9'), -1, -1
7277 Xalpha_excla, TRUE, FALSE,
7278 EL_CHAR('!'), -1, -1
7281 Xalpha_apost, TRUE, FALSE,
7282 EL_CHAR('\''), -1, -1
7285 Xalpha_comma, TRUE, FALSE,
7286 EL_CHAR(','), -1, -1
7289 Xalpha_minus, TRUE, FALSE,
7290 EL_CHAR('-'), -1, -1
7293 Xalpha_perio, TRUE, FALSE,
7294 EL_CHAR('.'), -1, -1
7297 Xalpha_colon, TRUE, FALSE,
7298 EL_CHAR(':'), -1, -1
7301 Xalpha_quest, TRUE, FALSE,
7302 EL_CHAR('?'), -1, -1
7305 Xalpha_a, TRUE, FALSE,
7306 EL_CHAR('A'), -1, -1
7309 Xalpha_b, TRUE, FALSE,
7310 EL_CHAR('B'), -1, -1
7313 Xalpha_c, TRUE, FALSE,
7314 EL_CHAR('C'), -1, -1
7317 Xalpha_d, TRUE, FALSE,
7318 EL_CHAR('D'), -1, -1
7321 Xalpha_e, TRUE, FALSE,
7322 EL_CHAR('E'), -1, -1
7325 Xalpha_f, TRUE, FALSE,
7326 EL_CHAR('F'), -1, -1
7329 Xalpha_g, TRUE, FALSE,
7330 EL_CHAR('G'), -1, -1
7333 Xalpha_h, TRUE, FALSE,
7334 EL_CHAR('H'), -1, -1
7337 Xalpha_i, TRUE, FALSE,
7338 EL_CHAR('I'), -1, -1
7341 Xalpha_j, TRUE, FALSE,
7342 EL_CHAR('J'), -1, -1
7345 Xalpha_k, TRUE, FALSE,
7346 EL_CHAR('K'), -1, -1
7349 Xalpha_l, TRUE, FALSE,
7350 EL_CHAR('L'), -1, -1
7353 Xalpha_m, TRUE, FALSE,
7354 EL_CHAR('M'), -1, -1
7357 Xalpha_n, TRUE, FALSE,
7358 EL_CHAR('N'), -1, -1
7361 Xalpha_o, TRUE, FALSE,
7362 EL_CHAR('O'), -1, -1
7365 Xalpha_p, TRUE, FALSE,
7366 EL_CHAR('P'), -1, -1
7369 Xalpha_q, TRUE, FALSE,
7370 EL_CHAR('Q'), -1, -1
7373 Xalpha_r, TRUE, FALSE,
7374 EL_CHAR('R'), -1, -1
7377 Xalpha_s, TRUE, FALSE,
7378 EL_CHAR('S'), -1, -1
7381 Xalpha_t, TRUE, FALSE,
7382 EL_CHAR('T'), -1, -1
7385 Xalpha_u, TRUE, FALSE,
7386 EL_CHAR('U'), -1, -1
7389 Xalpha_v, TRUE, FALSE,
7390 EL_CHAR('V'), -1, -1
7393 Xalpha_w, TRUE, FALSE,
7394 EL_CHAR('W'), -1, -1
7397 Xalpha_x, TRUE, FALSE,
7398 EL_CHAR('X'), -1, -1
7401 Xalpha_y, TRUE, FALSE,
7402 EL_CHAR('Y'), -1, -1
7405 Xalpha_z, TRUE, FALSE,
7406 EL_CHAR('Z'), -1, -1
7409 Xalpha_arrow_e, TRUE, FALSE,
7410 EL_CHAR('>'), -1, -1
7413 Xalpha_arrow_w, TRUE, FALSE,
7414 EL_CHAR('<'), -1, -1
7417 Xalpha_copyr, TRUE, FALSE,
7418 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7422 Ykey_1_blank, FALSE, FALSE,
7423 EL_EM_KEY_1, ACTION_COLLECTING, -1
7426 Ykey_2_blank, FALSE, FALSE,
7427 EL_EM_KEY_2, ACTION_COLLECTING, -1
7430 Ykey_3_blank, FALSE, FALSE,
7431 EL_EM_KEY_3, ACTION_COLLECTING, -1
7434 Ykey_4_blank, FALSE, FALSE,
7435 EL_EM_KEY_4, ACTION_COLLECTING, -1
7438 Ykey_5_blank, FALSE, FALSE,
7439 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7442 Ykey_6_blank, FALSE, FALSE,
7443 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7446 Ykey_7_blank, FALSE, FALSE,
7447 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7450 Ykey_8_blank, FALSE, FALSE,
7451 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7454 Ylenses_blank, FALSE, FALSE,
7455 EL_EMC_LENSES, ACTION_COLLECTING, -1
7458 Ymagnify_blank, FALSE, FALSE,
7459 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7462 Ygrass_blank, FALSE, FALSE,
7463 EL_EMC_GRASS, ACTION_SNAPPING, -1
7466 Ydirt_blank, FALSE, FALSE,
7467 EL_SAND, ACTION_SNAPPING, -1
7476 static struct Mapping_EM_to_RND_player
7485 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7489 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7493 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7497 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7501 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7505 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7509 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7513 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7517 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7521 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7525 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7529 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7533 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7537 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7541 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7545 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7549 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7553 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7557 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7561 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7565 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7569 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7573 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7577 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7581 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7585 EL_PLAYER_1, ACTION_DEFAULT, -1,
7589 EL_PLAYER_2, ACTION_DEFAULT, -1,
7593 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7597 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7601 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7605 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7609 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7613 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7617 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7621 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7625 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7629 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7633 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7637 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7641 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7645 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7649 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7653 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7657 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7661 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7665 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7669 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7673 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7677 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7681 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7685 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7689 EL_PLAYER_3, ACTION_DEFAULT, -1,
7693 EL_PLAYER_4, ACTION_DEFAULT, -1,
7702 int map_element_RND_to_EM_cave(int element_rnd)
7704 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7705 static boolean mapping_initialized = FALSE;
7707 if (!mapping_initialized)
7711 // return "Xalpha_quest" for all undefined elements in mapping array
7712 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7713 mapping_RND_to_EM[i] = Xalpha_quest;
7715 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7716 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7717 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7718 em_object_mapping_list[i].element_em;
7720 mapping_initialized = TRUE;
7723 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7725 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7730 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7733 int map_element_EM_to_RND_cave(int element_em_cave)
7735 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7736 static boolean mapping_initialized = FALSE;
7738 if (!mapping_initialized)
7742 // return "EL_UNKNOWN" for all undefined elements in mapping array
7743 for (i = 0; i < GAME_TILE_MAX; i++)
7744 mapping_EM_to_RND[i] = EL_UNKNOWN;
7746 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7747 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7748 em_object_mapping_list[i].element_rnd;
7750 mapping_initialized = TRUE;
7753 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7755 Error(ERR_WARN, "invalid EM cave element %d", element_em_cave);
7760 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7763 int map_element_EM_to_RND_game(int element_em_game)
7765 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7766 static boolean mapping_initialized = FALSE;
7768 if (!mapping_initialized)
7772 // return "EL_UNKNOWN" for all undefined elements in mapping array
7773 for (i = 0; i < GAME_TILE_MAX; i++)
7774 mapping_EM_to_RND[i] = EL_UNKNOWN;
7776 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7777 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7778 em_object_mapping_list[i].element_rnd;
7780 mapping_initialized = TRUE;
7783 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7785 Error(ERR_WARN, "invalid EM game element %d", element_em_game);
7790 return mapping_EM_to_RND[element_em_game];
7793 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7795 struct LevelInfo_EM *level_em = level->native_em_level;
7796 struct CAVE *cav = level_em->cav;
7799 for (i = 0; i < GAME_TILE_MAX; i++)
7800 cav->android_array[i] = Cblank;
7802 for (i = 0; i < level->num_android_clone_elements; i++)
7804 int element_rnd = level->android_clone_element[i];
7805 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7807 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7808 if (em_object_mapping_list[j].element_rnd == element_rnd)
7809 cav->android_array[em_object_mapping_list[j].element_em] =
7814 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7816 struct LevelInfo_EM *level_em = level->native_em_level;
7817 struct CAVE *cav = level_em->cav;
7820 level->num_android_clone_elements = 0;
7822 for (i = 0; i < GAME_TILE_MAX; i++)
7824 int element_em_cave = cav->android_array[i];
7826 boolean element_found = FALSE;
7828 if (element_em_cave == Cblank)
7831 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7833 for (j = 0; j < level->num_android_clone_elements; j++)
7834 if (level->android_clone_element[j] == element_rnd)
7835 element_found = TRUE;
7839 level->android_clone_element[level->num_android_clone_elements++] =
7842 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7847 if (level->num_android_clone_elements == 0)
7849 level->num_android_clone_elements = 1;
7850 level->android_clone_element[0] = EL_EMPTY;
7854 int map_direction_RND_to_EM(int direction)
7856 return (direction == MV_UP ? 0 :
7857 direction == MV_RIGHT ? 1 :
7858 direction == MV_DOWN ? 2 :
7859 direction == MV_LEFT ? 3 :
7863 int map_direction_EM_to_RND(int direction)
7865 return (direction == 0 ? MV_UP :
7866 direction == 1 ? MV_RIGHT :
7867 direction == 2 ? MV_DOWN :
7868 direction == 3 ? MV_LEFT :
7872 int map_element_RND_to_SP(int element_rnd)
7874 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7876 if (element_rnd >= EL_SP_START &&
7877 element_rnd <= EL_SP_END)
7878 element_sp = element_rnd - EL_SP_START;
7879 else if (element_rnd == EL_EMPTY_SPACE)
7881 else if (element_rnd == EL_INVISIBLE_WALL)
7887 int map_element_SP_to_RND(int element_sp)
7889 int element_rnd = EL_UNKNOWN;
7891 if (element_sp >= 0x00 &&
7893 element_rnd = EL_SP_START + element_sp;
7894 else if (element_sp == 0x28)
7895 element_rnd = EL_INVISIBLE_WALL;
7900 int map_action_SP_to_RND(int action_sp)
7904 case actActive: return ACTION_ACTIVE;
7905 case actImpact: return ACTION_IMPACT;
7906 case actExploding: return ACTION_EXPLODING;
7907 case actDigging: return ACTION_DIGGING;
7908 case actSnapping: return ACTION_SNAPPING;
7909 case actCollecting: return ACTION_COLLECTING;
7910 case actPassing: return ACTION_PASSING;
7911 case actPushing: return ACTION_PUSHING;
7912 case actDropping: return ACTION_DROPPING;
7914 default: return ACTION_DEFAULT;
7918 int map_element_RND_to_MM(int element_rnd)
7920 return (element_rnd >= EL_MM_START_1 &&
7921 element_rnd <= EL_MM_END_1 ?
7922 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7924 element_rnd >= EL_MM_START_2 &&
7925 element_rnd <= EL_MM_END_2 ?
7926 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7928 element_rnd >= EL_CHAR_START &&
7929 element_rnd <= EL_CHAR_END ?
7930 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7932 element_rnd >= EL_MM_RUNTIME_START &&
7933 element_rnd <= EL_MM_RUNTIME_END ?
7934 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7936 element_rnd >= EL_MM_DUMMY_START &&
7937 element_rnd <= EL_MM_DUMMY_END ?
7938 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7940 EL_MM_EMPTY_NATIVE);
7943 int map_element_MM_to_RND(int element_mm)
7945 return (element_mm == EL_MM_EMPTY_NATIVE ||
7946 element_mm == EL_DF_EMPTY_NATIVE ?
7949 element_mm >= EL_MM_START_1_NATIVE &&
7950 element_mm <= EL_MM_END_1_NATIVE ?
7951 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7953 element_mm >= EL_MM_START_2_NATIVE &&
7954 element_mm <= EL_MM_END_2_NATIVE ?
7955 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7957 element_mm >= EL_MM_CHAR_START_NATIVE &&
7958 element_mm <= EL_MM_CHAR_END_NATIVE ?
7959 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7961 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7962 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7963 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7965 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7966 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7967 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7972 int map_action_MM_to_RND(int action_mm)
7974 // all MM actions are defined to exactly match their RND counterparts
7978 int map_sound_MM_to_RND(int sound_mm)
7982 case SND_MM_GAME_LEVELTIME_CHARGING:
7983 return SND_GAME_LEVELTIME_CHARGING;
7985 case SND_MM_GAME_HEALTH_CHARGING:
7986 return SND_GAME_HEALTH_CHARGING;
7989 return SND_UNDEFINED;
7993 int map_mm_wall_element(int element)
7995 return (element >= EL_MM_STEEL_WALL_START &&
7996 element <= EL_MM_STEEL_WALL_END ?
7999 element >= EL_MM_WOODEN_WALL_START &&
8000 element <= EL_MM_WOODEN_WALL_END ?
8003 element >= EL_MM_ICE_WALL_START &&
8004 element <= EL_MM_ICE_WALL_END ?
8007 element >= EL_MM_AMOEBA_WALL_START &&
8008 element <= EL_MM_AMOEBA_WALL_END ?
8011 element >= EL_DF_STEEL_WALL_START &&
8012 element <= EL_DF_STEEL_WALL_END ?
8015 element >= EL_DF_WOODEN_WALL_START &&
8016 element <= EL_DF_WOODEN_WALL_END ?
8022 int map_mm_wall_element_editor(int element)
8026 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8027 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8028 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8029 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8030 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8031 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8033 default: return element;
8037 int get_next_element(int element)
8041 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8042 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8043 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8044 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8045 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8046 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8047 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8048 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8049 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8050 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8051 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8053 default: return element;
8057 int el2img_mm(int element_mm)
8059 return el2img(map_element_MM_to_RND(element_mm));
8062 int el_act_dir2img(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_graphic[action][direction];
8071 static int el_act_dir2crm(int element, int action, int direction)
8073 element = GFX_ELEMENT(element);
8074 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8076 // direction_graphic[][] == graphic[] for undefined direction graphics
8077 return element_info[element].direction_crumbled[action][direction];
8080 int el_act2img(int element, int action)
8082 element = GFX_ELEMENT(element);
8084 return element_info[element].graphic[action];
8087 int el_act2crm(int element, int action)
8089 element = GFX_ELEMENT(element);
8091 return element_info[element].crumbled[action];
8094 int el_dir2img(int element, int direction)
8096 element = GFX_ELEMENT(element);
8098 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8101 int el2baseimg(int element)
8103 return element_info[element].graphic[ACTION_DEFAULT];
8106 int el2img(int element)
8108 element = GFX_ELEMENT(element);
8110 return element_info[element].graphic[ACTION_DEFAULT];
8113 int el2edimg(int element)
8115 element = GFX_ELEMENT(element);
8117 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8120 int el2preimg(int element)
8122 element = GFX_ELEMENT(element);
8124 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8127 int el2panelimg(int element)
8129 element = GFX_ELEMENT(element);
8131 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8134 int font2baseimg(int font_nr)
8136 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8139 int getBeltNrFromBeltElement(int element)
8141 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8142 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8143 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8146 int getBeltNrFromBeltActiveElement(int element)
8148 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8149 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8150 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8153 int getBeltNrFromBeltSwitchElement(int element)
8155 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8156 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8157 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8160 int getBeltDirNrFromBeltElement(int element)
8162 static int belt_base_element[4] =
8164 EL_CONVEYOR_BELT_1_LEFT,
8165 EL_CONVEYOR_BELT_2_LEFT,
8166 EL_CONVEYOR_BELT_3_LEFT,
8167 EL_CONVEYOR_BELT_4_LEFT
8170 int belt_nr = getBeltNrFromBeltElement(element);
8171 int belt_dir_nr = element - belt_base_element[belt_nr];
8173 return (belt_dir_nr % 3);
8176 int getBeltDirNrFromBeltSwitchElement(int element)
8178 static int belt_base_element[4] =
8180 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8181 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8182 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8183 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8186 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8187 int belt_dir_nr = element - belt_base_element[belt_nr];
8189 return (belt_dir_nr % 3);
8192 int getBeltDirFromBeltElement(int element)
8194 static int belt_move_dir[3] =
8201 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8203 return belt_move_dir[belt_dir_nr];
8206 int getBeltDirFromBeltSwitchElement(int element)
8208 static int belt_move_dir[3] =
8215 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8217 return belt_move_dir[belt_dir_nr];
8220 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8222 static int belt_base_element[4] =
8224 EL_CONVEYOR_BELT_1_LEFT,
8225 EL_CONVEYOR_BELT_2_LEFT,
8226 EL_CONVEYOR_BELT_3_LEFT,
8227 EL_CONVEYOR_BELT_4_LEFT
8230 return belt_base_element[belt_nr] + belt_dir_nr;
8233 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8235 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8237 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8240 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8242 static int belt_base_element[4] =
8244 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8245 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8246 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8247 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8250 return belt_base_element[belt_nr] + belt_dir_nr;
8253 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8255 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8257 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8260 boolean swapTiles_EM(boolean is_pre_emc_cave)
8262 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8265 boolean getTeamMode_EM(void)
8267 return game.team_mode || network_playing;
8270 boolean isActivePlayer_EM(int player_nr)
8272 return stored_player[player_nr].active;
8275 unsigned int InitRND(int seed)
8277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8278 return InitEngineRandom_EM(seed);
8279 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8280 return InitEngineRandom_SP(seed);
8281 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8282 return InitEngineRandom_MM(seed);
8284 return InitEngineRandom_RND(seed);
8287 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8288 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8290 static int get_effective_element_EM(int tile, int frame_em)
8292 int element = object_mapping[tile].element_rnd;
8293 int action = object_mapping[tile].action;
8294 boolean is_backside = object_mapping[tile].is_backside;
8295 boolean action_removing = (action == ACTION_DIGGING ||
8296 action == ACTION_SNAPPING ||
8297 action == ACTION_COLLECTING);
8305 return (frame_em > 5 ? EL_EMPTY : element);
8311 else // frame_em == 7
8322 case Ydiamond_stone:
8326 case Xdrip_stretchB:
8342 case Ymagnify_blank:
8345 case Xsand_stonein_1:
8346 case Xsand_stonein_2:
8347 case Xsand_stonein_3:
8348 case Xsand_stonein_4:
8352 return (is_backside || action_removing ? EL_EMPTY : element);
8357 static boolean check_linear_animation_EM(int tile)
8361 case Xsand_stonesand_1:
8362 case Xsand_stonesand_quickout_1:
8363 case Xsand_sandstone_1:
8364 case Xsand_stonein_1:
8365 case Xsand_stoneout_1:
8393 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8394 boolean has_crumbled_graphics,
8395 int crumbled, int sync_frame)
8397 // if element can be crumbled, but certain action graphics are just empty
8398 // space (like instantly snapping sand to empty space in 1 frame), do not
8399 // treat these empty space graphics as crumbled graphics in EMC engine
8400 if (crumbled == IMG_EMPTY_SPACE)
8401 has_crumbled_graphics = FALSE;
8403 if (has_crumbled_graphics)
8405 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8406 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8407 g_crumbled->anim_delay,
8408 g_crumbled->anim_mode,
8409 g_crumbled->anim_start_frame,
8412 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8413 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8415 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8416 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8418 g_em->has_crumbled_graphics = TRUE;
8422 g_em->crumbled_bitmap = NULL;
8423 g_em->crumbled_src_x = 0;
8424 g_em->crumbled_src_y = 0;
8425 g_em->crumbled_border_size = 0;
8426 g_em->crumbled_tile_size = 0;
8428 g_em->has_crumbled_graphics = FALSE;
8433 void ResetGfxAnimation_EM(int x, int y, int tile)
8439 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8440 int tile, int frame_em, int x, int y)
8442 int action = object_mapping[tile].action;
8443 int direction = object_mapping[tile].direction;
8444 int effective_element = get_effective_element_EM(tile, frame_em);
8445 int graphic = (direction == MV_NONE ?
8446 el_act2img(effective_element, action) :
8447 el_act_dir2img(effective_element, action, direction));
8448 struct GraphicInfo *g = &graphic_info[graphic];
8450 boolean action_removing = (action == ACTION_DIGGING ||
8451 action == ACTION_SNAPPING ||
8452 action == ACTION_COLLECTING);
8453 boolean action_moving = (action == ACTION_FALLING ||
8454 action == ACTION_MOVING ||
8455 action == ACTION_PUSHING ||
8456 action == ACTION_EATING ||
8457 action == ACTION_FILLING ||
8458 action == ACTION_EMPTYING);
8459 boolean action_falling = (action == ACTION_FALLING ||
8460 action == ACTION_FILLING ||
8461 action == ACTION_EMPTYING);
8463 // special case: graphic uses "2nd movement tile" and has defined
8464 // 7 frames for movement animation (or less) => use default graphic
8465 // for last (8th) frame which ends the movement animation
8466 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8468 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8469 graphic = (direction == MV_NONE ?
8470 el_act2img(effective_element, action) :
8471 el_act_dir2img(effective_element, action, direction));
8473 g = &graphic_info[graphic];
8476 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8480 else if (action_moving)
8482 boolean is_backside = object_mapping[tile].is_backside;
8486 int direction = object_mapping[tile].direction;
8487 int move_dir = (action_falling ? MV_DOWN : direction);
8492 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8493 if (g->double_movement && frame_em == 0)
8497 if (move_dir == MV_LEFT)
8498 GfxFrame[x - 1][y] = GfxFrame[x][y];
8499 else if (move_dir == MV_RIGHT)
8500 GfxFrame[x + 1][y] = GfxFrame[x][y];
8501 else if (move_dir == MV_UP)
8502 GfxFrame[x][y - 1] = GfxFrame[x][y];
8503 else if (move_dir == MV_DOWN)
8504 GfxFrame[x][y + 1] = GfxFrame[x][y];
8511 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8512 if (tile == Xsand_stonesand_quickout_1 ||
8513 tile == Xsand_stonesand_quickout_2)
8517 if (graphic_info[graphic].anim_global_sync)
8518 sync_frame = FrameCounter;
8519 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8520 sync_frame = GfxFrame[x][y];
8522 sync_frame = 0; // playfield border (pseudo steel)
8524 SetRandomAnimationValue(x, y);
8526 int frame = getAnimationFrame(g->anim_frames,
8529 g->anim_start_frame,
8532 g_em->unique_identifier =
8533 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8536 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8537 int tile, int frame_em, int x, int y)
8539 int action = object_mapping[tile].action;
8540 int direction = object_mapping[tile].direction;
8541 boolean is_backside = object_mapping[tile].is_backside;
8542 int effective_element = get_effective_element_EM(tile, frame_em);
8543 int effective_action = action;
8544 int graphic = (direction == MV_NONE ?
8545 el_act2img(effective_element, effective_action) :
8546 el_act_dir2img(effective_element, effective_action,
8548 int crumbled = (direction == MV_NONE ?
8549 el_act2crm(effective_element, effective_action) :
8550 el_act_dir2crm(effective_element, effective_action,
8552 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8553 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8554 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8555 struct GraphicInfo *g = &graphic_info[graphic];
8558 // special case: graphic uses "2nd movement tile" and has defined
8559 // 7 frames for movement animation (or less) => use default graphic
8560 // for last (8th) frame which ends the movement animation
8561 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8563 effective_action = ACTION_DEFAULT;
8564 graphic = (direction == MV_NONE ?
8565 el_act2img(effective_element, effective_action) :
8566 el_act_dir2img(effective_element, effective_action,
8568 crumbled = (direction == MV_NONE ?
8569 el_act2crm(effective_element, effective_action) :
8570 el_act_dir2crm(effective_element, effective_action,
8573 g = &graphic_info[graphic];
8576 if (graphic_info[graphic].anim_global_sync)
8577 sync_frame = FrameCounter;
8578 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8579 sync_frame = GfxFrame[x][y];
8581 sync_frame = 0; // playfield border (pseudo steel)
8583 SetRandomAnimationValue(x, y);
8585 int frame = getAnimationFrame(g->anim_frames,
8588 g->anim_start_frame,
8591 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8592 g->double_movement && is_backside);
8594 // (updating the "crumbled" graphic definitions is probably not really needed,
8595 // as animations for crumbled graphics can't be longer than one EMC cycle)
8596 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8600 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8601 int player_nr, int anim, int frame_em)
8603 int element = player_mapping[player_nr][anim].element_rnd;
8604 int action = player_mapping[player_nr][anim].action;
8605 int direction = player_mapping[player_nr][anim].direction;
8606 int graphic = (direction == MV_NONE ?
8607 el_act2img(element, action) :
8608 el_act_dir2img(element, action, direction));
8609 struct GraphicInfo *g = &graphic_info[graphic];
8612 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8614 stored_player[player_nr].StepFrame = frame_em;
8616 sync_frame = stored_player[player_nr].Frame;
8618 int frame = getAnimationFrame(g->anim_frames,
8621 g->anim_start_frame,
8624 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8625 &g_em->src_x, &g_em->src_y, FALSE);
8628 void InitGraphicInfo_EM(void)
8632 // always start with reliable default values
8633 for (i = 0; i < GAME_TILE_MAX; i++)
8635 object_mapping[i].element_rnd = EL_UNKNOWN;
8636 object_mapping[i].is_backside = FALSE;
8637 object_mapping[i].action = ACTION_DEFAULT;
8638 object_mapping[i].direction = MV_NONE;
8641 // always start with reliable default values
8642 for (p = 0; p < MAX_PLAYERS; p++)
8644 for (i = 0; i < PLY_MAX; i++)
8646 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8647 player_mapping[p][i].action = ACTION_DEFAULT;
8648 player_mapping[p][i].direction = MV_NONE;
8652 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8654 int e = em_object_mapping_list[i].element_em;
8656 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8657 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8659 if (em_object_mapping_list[i].action != -1)
8660 object_mapping[e].action = em_object_mapping_list[i].action;
8662 if (em_object_mapping_list[i].direction != -1)
8663 object_mapping[e].direction =
8664 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8667 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8669 int a = em_player_mapping_list[i].action_em;
8670 int p = em_player_mapping_list[i].player_nr;
8672 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8674 if (em_player_mapping_list[i].action != -1)
8675 player_mapping[p][a].action = em_player_mapping_list[i].action;
8677 if (em_player_mapping_list[i].direction != -1)
8678 player_mapping[p][a].direction =
8679 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8682 for (i = 0; i < GAME_TILE_MAX; i++)
8684 int element = object_mapping[i].element_rnd;
8685 int action = object_mapping[i].action;
8686 int direction = object_mapping[i].direction;
8687 boolean is_backside = object_mapping[i].is_backside;
8688 boolean action_exploding = ((action == ACTION_EXPLODING ||
8689 action == ACTION_SMASHED_BY_ROCK ||
8690 action == ACTION_SMASHED_BY_SPRING) &&
8691 element != EL_DIAMOND);
8692 boolean action_active = (action == ACTION_ACTIVE);
8693 boolean action_other = (action == ACTION_OTHER);
8695 for (j = 0; j < 8; j++)
8697 int effective_element = get_effective_element_EM(i, j);
8698 int effective_action = (j < 7 ? action :
8699 i == Xdrip_stretch ? action :
8700 i == Xdrip_stretchB ? action :
8701 i == Ydrip_1_s ? action :
8702 i == Ydrip_1_sB ? action :
8703 i == Yball_1 ? action :
8704 i == Xball_2 ? action :
8705 i == Yball_2 ? action :
8706 i == Yball_blank ? action :
8707 i == Ykey_1_blank ? action :
8708 i == Ykey_2_blank ? action :
8709 i == Ykey_3_blank ? action :
8710 i == Ykey_4_blank ? action :
8711 i == Ykey_5_blank ? action :
8712 i == Ykey_6_blank ? action :
8713 i == Ykey_7_blank ? action :
8714 i == Ykey_8_blank ? action :
8715 i == Ylenses_blank ? action :
8716 i == Ymagnify_blank ? action :
8717 i == Ygrass_blank ? action :
8718 i == Ydirt_blank ? action :
8719 i == Xsand_stonein_1 ? action :
8720 i == Xsand_stonein_2 ? action :
8721 i == Xsand_stonein_3 ? action :
8722 i == Xsand_stonein_4 ? action :
8723 i == Xsand_stoneout_1 ? action :
8724 i == Xsand_stoneout_2 ? action :
8725 i == Xboom_android ? ACTION_EXPLODING :
8726 action_exploding ? ACTION_EXPLODING :
8727 action_active ? action :
8728 action_other ? action :
8730 int graphic = (el_act_dir2img(effective_element, effective_action,
8732 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8734 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8735 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8736 boolean has_action_graphics = (graphic != base_graphic);
8737 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8738 struct GraphicInfo *g = &graphic_info[graphic];
8739 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8742 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8743 boolean special_animation = (action != ACTION_DEFAULT &&
8744 g->anim_frames == 3 &&
8745 g->anim_delay == 2 &&
8746 g->anim_mode & ANIM_LINEAR);
8747 int sync_frame = (i == Xdrip_stretch ? 7 :
8748 i == Xdrip_stretchB ? 7 :
8749 i == Ydrip_2_s ? j + 8 :
8750 i == Ydrip_2_sB ? j + 8 :
8759 i == Xfake_acid_1 ? 0 :
8760 i == Xfake_acid_2 ? 10 :
8761 i == Xfake_acid_3 ? 20 :
8762 i == Xfake_acid_4 ? 30 :
8763 i == Xfake_acid_5 ? 40 :
8764 i == Xfake_acid_6 ? 50 :
8765 i == Xfake_acid_7 ? 60 :
8766 i == Xfake_acid_8 ? 70 :
8768 i == Yball_2 ? j + 8 :
8769 i == Yball_blank ? j + 1 :
8770 i == Ykey_1_blank ? j + 1 :
8771 i == Ykey_2_blank ? j + 1 :
8772 i == Ykey_3_blank ? j + 1 :
8773 i == Ykey_4_blank ? j + 1 :
8774 i == Ykey_5_blank ? j + 1 :
8775 i == Ykey_6_blank ? j + 1 :
8776 i == Ykey_7_blank ? j + 1 :
8777 i == Ykey_8_blank ? j + 1 :
8778 i == Ylenses_blank ? j + 1 :
8779 i == Ymagnify_blank ? j + 1 :
8780 i == Ygrass_blank ? j + 1 :
8781 i == Ydirt_blank ? j + 1 :
8782 i == Xamoeba_1 ? 0 :
8783 i == Xamoeba_2 ? 1 :
8784 i == Xamoeba_3 ? 2 :
8785 i == Xamoeba_4 ? 3 :
8786 i == Xamoeba_5 ? 0 :
8787 i == Xamoeba_6 ? 1 :
8788 i == Xamoeba_7 ? 2 :
8789 i == Xamoeba_8 ? 3 :
8790 i == Xexit_2 ? j + 8 :
8791 i == Xexit_3 ? j + 16 :
8792 i == Xdynamite_1 ? 0 :
8793 i == Xdynamite_2 ? 8 :
8794 i == Xdynamite_3 ? 16 :
8795 i == Xdynamite_4 ? 24 :
8796 i == Xsand_stonein_1 ? j + 1 :
8797 i == Xsand_stonein_2 ? j + 9 :
8798 i == Xsand_stonein_3 ? j + 17 :
8799 i == Xsand_stonein_4 ? j + 25 :
8800 i == Xsand_stoneout_1 && j == 0 ? 0 :
8801 i == Xsand_stoneout_1 && j == 1 ? 0 :
8802 i == Xsand_stoneout_1 && j == 2 ? 1 :
8803 i == Xsand_stoneout_1 && j == 3 ? 2 :
8804 i == Xsand_stoneout_1 && j == 4 ? 2 :
8805 i == Xsand_stoneout_1 && j == 5 ? 3 :
8806 i == Xsand_stoneout_1 && j == 6 ? 4 :
8807 i == Xsand_stoneout_1 && j == 7 ? 4 :
8808 i == Xsand_stoneout_2 && j == 0 ? 5 :
8809 i == Xsand_stoneout_2 && j == 1 ? 6 :
8810 i == Xsand_stoneout_2 && j == 2 ? 7 :
8811 i == Xsand_stoneout_2 && j == 3 ? 8 :
8812 i == Xsand_stoneout_2 && j == 4 ? 9 :
8813 i == Xsand_stoneout_2 && j == 5 ? 11 :
8814 i == Xsand_stoneout_2 && j == 6 ? 13 :
8815 i == Xsand_stoneout_2 && j == 7 ? 15 :
8816 i == Xboom_bug && j == 1 ? 2 :
8817 i == Xboom_bug && j == 2 ? 2 :
8818 i == Xboom_bug && j == 3 ? 4 :
8819 i == Xboom_bug && j == 4 ? 4 :
8820 i == Xboom_bug && j == 5 ? 2 :
8821 i == Xboom_bug && j == 6 ? 2 :
8822 i == Xboom_bug && j == 7 ? 0 :
8823 i == Xboom_tank && j == 1 ? 2 :
8824 i == Xboom_tank && j == 2 ? 2 :
8825 i == Xboom_tank && j == 3 ? 4 :
8826 i == Xboom_tank && j == 4 ? 4 :
8827 i == Xboom_tank && j == 5 ? 2 :
8828 i == Xboom_tank && j == 6 ? 2 :
8829 i == Xboom_tank && j == 7 ? 0 :
8830 i == Xboom_android && j == 7 ? 6 :
8831 i == Xboom_1 && j == 1 ? 2 :
8832 i == Xboom_1 && j == 2 ? 2 :
8833 i == Xboom_1 && j == 3 ? 4 :
8834 i == Xboom_1 && j == 4 ? 4 :
8835 i == Xboom_1 && j == 5 ? 6 :
8836 i == Xboom_1 && j == 6 ? 6 :
8837 i == Xboom_1 && j == 7 ? 8 :
8838 i == Xboom_2 && j == 0 ? 8 :
8839 i == Xboom_2 && j == 1 ? 8 :
8840 i == Xboom_2 && j == 2 ? 10 :
8841 i == Xboom_2 && j == 3 ? 10 :
8842 i == Xboom_2 && j == 4 ? 10 :
8843 i == Xboom_2 && j == 5 ? 12 :
8844 i == Xboom_2 && j == 6 ? 12 :
8845 i == Xboom_2 && j == 7 ? 12 :
8846 special_animation && j == 4 ? 3 :
8847 effective_action != action ? 0 :
8849 int frame = getAnimationFrame(g->anim_frames,
8852 g->anim_start_frame,
8855 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8856 g->double_movement && is_backside);
8858 g_em->bitmap = src_bitmap;
8859 g_em->src_x = src_x;
8860 g_em->src_y = src_y;
8861 g_em->src_offset_x = 0;
8862 g_em->src_offset_y = 0;
8863 g_em->dst_offset_x = 0;
8864 g_em->dst_offset_y = 0;
8865 g_em->width = TILEX;
8866 g_em->height = TILEY;
8868 g_em->preserve_background = FALSE;
8870 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8873 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8874 effective_action == ACTION_MOVING ||
8875 effective_action == ACTION_PUSHING ||
8876 effective_action == ACTION_EATING)) ||
8877 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8878 effective_action == ACTION_EMPTYING)))
8881 (effective_action == ACTION_FALLING ||
8882 effective_action == ACTION_FILLING ||
8883 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8884 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8885 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8886 int num_steps = (i == Ydrip_1_s ? 16 :
8887 i == Ydrip_1_sB ? 16 :
8888 i == Ydrip_2_s ? 16 :
8889 i == Ydrip_2_sB ? 16 :
8890 i == Xsand_stonein_1 ? 32 :
8891 i == Xsand_stonein_2 ? 32 :
8892 i == Xsand_stonein_3 ? 32 :
8893 i == Xsand_stonein_4 ? 32 :
8894 i == Xsand_stoneout_1 ? 16 :
8895 i == Xsand_stoneout_2 ? 16 : 8);
8896 int cx = ABS(dx) * (TILEX / num_steps);
8897 int cy = ABS(dy) * (TILEY / num_steps);
8898 int step_frame = (i == Ydrip_2_s ? j + 8 :
8899 i == Ydrip_2_sB ? j + 8 :
8900 i == Xsand_stonein_2 ? j + 8 :
8901 i == Xsand_stonein_3 ? j + 16 :
8902 i == Xsand_stonein_4 ? j + 24 :
8903 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8904 int step = (is_backside ? step_frame : num_steps - step_frame);
8906 if (is_backside) // tile where movement starts
8908 if (dx < 0 || dy < 0)
8910 g_em->src_offset_x = cx * step;
8911 g_em->src_offset_y = cy * step;
8915 g_em->dst_offset_x = cx * step;
8916 g_em->dst_offset_y = cy * step;
8919 else // tile where movement ends
8921 if (dx < 0 || dy < 0)
8923 g_em->dst_offset_x = cx * step;
8924 g_em->dst_offset_y = cy * step;
8928 g_em->src_offset_x = cx * step;
8929 g_em->src_offset_y = cy * step;
8933 g_em->width = TILEX - cx * step;
8934 g_em->height = TILEY - cy * step;
8937 // create unique graphic identifier to decide if tile must be redrawn
8938 /* bit 31 - 16 (16 bit): EM style graphic
8939 bit 15 - 12 ( 4 bit): EM style frame
8940 bit 11 - 6 ( 6 bit): graphic width
8941 bit 5 - 0 ( 6 bit): graphic height */
8942 g_em->unique_identifier =
8943 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8947 for (i = 0; i < GAME_TILE_MAX; i++)
8949 for (j = 0; j < 8; j++)
8951 int element = object_mapping[i].element_rnd;
8952 int action = object_mapping[i].action;
8953 int direction = object_mapping[i].direction;
8954 boolean is_backside = object_mapping[i].is_backside;
8955 int graphic_action = el_act_dir2img(element, action, direction);
8956 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8958 if ((action == ACTION_SMASHED_BY_ROCK ||
8959 action == ACTION_SMASHED_BY_SPRING ||
8960 action == ACTION_EATING) &&
8961 graphic_action == graphic_default)
8963 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8964 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8965 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8966 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8969 // no separate animation for "smashed by rock" -- use rock instead
8970 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8971 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8973 g_em->bitmap = g_xx->bitmap;
8974 g_em->src_x = g_xx->src_x;
8975 g_em->src_y = g_xx->src_y;
8976 g_em->src_offset_x = g_xx->src_offset_x;
8977 g_em->src_offset_y = g_xx->src_offset_y;
8978 g_em->dst_offset_x = g_xx->dst_offset_x;
8979 g_em->dst_offset_y = g_xx->dst_offset_y;
8980 g_em->width = g_xx->width;
8981 g_em->height = g_xx->height;
8982 g_em->unique_identifier = g_xx->unique_identifier;
8985 g_em->preserve_background = TRUE;
8990 for (p = 0; p < MAX_PLAYERS; p++)
8992 for (i = 0; i < PLY_MAX; i++)
8994 int element = player_mapping[p][i].element_rnd;
8995 int action = player_mapping[p][i].action;
8996 int direction = player_mapping[p][i].direction;
8998 for (j = 0; j < 8; j++)
9000 int effective_element = element;
9001 int effective_action = action;
9002 int graphic = (direction == MV_NONE ?
9003 el_act2img(effective_element, effective_action) :
9004 el_act_dir2img(effective_element, effective_action,
9006 struct GraphicInfo *g = &graphic_info[graphic];
9007 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9011 int frame = getAnimationFrame(g->anim_frames,
9014 g->anim_start_frame,
9017 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9019 g_em->bitmap = src_bitmap;
9020 g_em->src_x = src_x;
9021 g_em->src_y = src_y;
9022 g_em->src_offset_x = 0;
9023 g_em->src_offset_y = 0;
9024 g_em->dst_offset_x = 0;
9025 g_em->dst_offset_y = 0;
9026 g_em->width = TILEX;
9027 g_em->height = TILEY;
9033 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9034 boolean any_player_moving,
9035 boolean any_player_snapping,
9036 boolean any_player_dropping)
9038 if (frame == 7 && !any_player_dropping)
9040 if (!local_player->was_waiting)
9042 if (!CheckSaveEngineSnapshotToList())
9045 local_player->was_waiting = TRUE;
9048 else if (any_player_moving || any_player_snapping || any_player_dropping)
9050 local_player->was_waiting = FALSE;
9054 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9055 boolean murphy_is_dropping)
9057 if (murphy_is_waiting)
9059 if (!local_player->was_waiting)
9061 if (!CheckSaveEngineSnapshotToList())
9064 local_player->was_waiting = TRUE;
9069 local_player->was_waiting = FALSE;
9073 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9074 boolean button_released)
9076 if (button_released)
9078 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9079 CheckSaveEngineSnapshotToList();
9081 else if (element_clicked)
9083 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9084 CheckSaveEngineSnapshotToList();
9086 game.snapshot.changed_action = TRUE;
9090 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9091 boolean any_player_moving,
9092 boolean any_player_snapping,
9093 boolean any_player_dropping)
9095 if (tape.single_step && tape.recording && !tape.pausing)
9096 if (frame == 7 && !any_player_dropping)
9097 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9099 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9100 any_player_snapping, any_player_dropping);
9103 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9104 boolean murphy_is_dropping)
9106 boolean murphy_starts_dropping = FALSE;
9109 for (i = 0; i < MAX_PLAYERS; i++)
9110 if (stored_player[i].force_dropping)
9111 murphy_starts_dropping = TRUE;
9113 if (tape.single_step && tape.recording && !tape.pausing)
9114 if (murphy_is_waiting && !murphy_starts_dropping)
9115 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9117 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9120 void CheckSingleStepMode_MM(boolean element_clicked,
9121 boolean button_released)
9123 if (tape.single_step && tape.recording && !tape.pausing)
9124 if (button_released)
9125 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9127 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9130 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9131 int graphic, int sync_frame, int x, int y)
9133 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9135 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9138 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9140 return (IS_NEXT_FRAME(sync_frame, graphic));
9143 int getGraphicInfo_Delay(int graphic)
9145 return graphic_info[graphic].anim_delay;
9148 void PlayMenuSoundExt(int sound)
9150 if (sound == SND_UNDEFINED)
9153 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9154 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9157 if (IS_LOOP_SOUND(sound))
9158 PlaySoundLoop(sound);
9163 void PlayMenuSound(void)
9165 PlayMenuSoundExt(menu.sound[game_status]);
9168 void PlayMenuSoundStereo(int sound, int stereo_position)
9170 if (sound == SND_UNDEFINED)
9173 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9174 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9177 if (IS_LOOP_SOUND(sound))
9178 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9180 PlaySoundStereo(sound, stereo_position);
9183 void PlayMenuSoundIfLoopExt(int sound)
9185 if (sound == SND_UNDEFINED)
9188 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9189 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9192 if (IS_LOOP_SOUND(sound))
9193 PlaySoundLoop(sound);
9196 void PlayMenuSoundIfLoop(void)
9198 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9201 void PlayMenuMusicExt(int music)
9203 if (music == MUS_UNDEFINED)
9206 if (!setup.sound_music)
9209 if (IS_LOOP_MUSIC(music))
9210 PlayMusicLoop(music);
9215 void PlayMenuMusic(void)
9217 char *curr_music = getCurrentlyPlayingMusicFilename();
9218 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9220 if (!strEqual(curr_music, next_music))
9221 PlayMenuMusicExt(menu.music[game_status]);
9224 void PlayMenuSoundsAndMusic(void)
9230 static void FadeMenuSounds(void)
9235 static void FadeMenuMusic(void)
9237 char *curr_music = getCurrentlyPlayingMusicFilename();
9238 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9240 if (!strEqual(curr_music, next_music))
9244 void FadeMenuSoundsAndMusic(void)
9250 void PlaySoundActivating(void)
9253 PlaySound(SND_MENU_ITEM_ACTIVATING);
9257 void PlaySoundSelecting(void)
9260 PlaySound(SND_MENU_ITEM_SELECTING);
9264 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9266 boolean change_fullscreen = (setup.fullscreen !=
9267 video.fullscreen_enabled);
9268 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9269 setup.window_scaling_percent !=
9270 video.window_scaling_percent);
9272 if (change_window_scaling_percent && video.fullscreen_enabled)
9275 if (!change_window_scaling_percent && !video.fullscreen_available)
9278 if (change_window_scaling_percent)
9280 SDLSetWindowScaling(setup.window_scaling_percent);
9284 else if (change_fullscreen)
9286 SDLSetWindowFullscreen(setup.fullscreen);
9288 // set setup value according to successfully changed fullscreen mode
9289 setup.fullscreen = video.fullscreen_enabled;
9294 if (change_fullscreen ||
9295 change_window_scaling_percent)
9297 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9299 // save backbuffer content which gets lost when toggling fullscreen mode
9300 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9302 if (change_window_scaling_percent)
9304 // keep window mode, but change window scaling
9305 video.fullscreen_enabled = TRUE; // force new window scaling
9308 // toggle fullscreen
9309 ChangeVideoModeIfNeeded(setup.fullscreen);
9311 // set setup value according to successfully changed fullscreen mode
9312 setup.fullscreen = video.fullscreen_enabled;
9314 // restore backbuffer content from temporary backbuffer backup bitmap
9315 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9317 FreeBitmap(tmp_backbuffer);
9319 // update visible window/screen
9320 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9324 static void JoinRectangles(int *x, int *y, int *width, int *height,
9325 int x2, int y2, int width2, int height2)
9327 // do not join with "off-screen" rectangle
9328 if (x2 == -1 || y2 == -1)
9333 *width = MAX(*width, width2);
9334 *height = MAX(*height, height2);
9337 void SetAnimStatus(int anim_status_new)
9339 if (anim_status_new == GAME_MODE_MAIN)
9340 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9341 else if (anim_status_new == GAME_MODE_SCORES)
9342 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9344 global.anim_status_next = anim_status_new;
9346 // directly set screen modes that are entered without fading
9347 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9348 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9349 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9350 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9351 global.anim_status = global.anim_status_next;
9354 void SetGameStatus(int game_status_new)
9356 if (game_status_new != game_status)
9357 game_status_last_screen = game_status;
9359 game_status = game_status_new;
9361 SetAnimStatus(game_status_new);
9364 void SetFontStatus(int game_status_new)
9366 static int last_game_status = -1;
9368 if (game_status_new != -1)
9370 // set game status for font use after storing last game status
9371 last_game_status = game_status;
9372 game_status = game_status_new;
9376 // reset game status after font use from last stored game status
9377 game_status = last_game_status;
9381 void ResetFontStatus(void)
9386 void SetLevelSetInfo(char *identifier, int level_nr)
9388 setString(&levelset.identifier, identifier);
9390 levelset.level_nr = level_nr;
9393 boolean CheckIfAllViewportsHaveChanged(void)
9395 // if game status has not changed, viewports have not changed either
9396 if (game_status == game_status_last)
9399 // check if all viewports have changed with current game status
9401 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9402 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9403 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9404 int new_real_sx = vp_playfield->x;
9405 int new_real_sy = vp_playfield->y;
9406 int new_full_sxsize = vp_playfield->width;
9407 int new_full_sysize = vp_playfield->height;
9408 int new_dx = vp_door_1->x;
9409 int new_dy = vp_door_1->y;
9410 int new_dxsize = vp_door_1->width;
9411 int new_dysize = vp_door_1->height;
9412 int new_vx = vp_door_2->x;
9413 int new_vy = vp_door_2->y;
9414 int new_vxsize = vp_door_2->width;
9415 int new_vysize = vp_door_2->height;
9417 boolean playfield_viewport_has_changed =
9418 (new_real_sx != REAL_SX ||
9419 new_real_sy != REAL_SY ||
9420 new_full_sxsize != FULL_SXSIZE ||
9421 new_full_sysize != FULL_SYSIZE);
9423 boolean door_1_viewport_has_changed =
9426 new_dxsize != DXSIZE ||
9427 new_dysize != DYSIZE);
9429 boolean door_2_viewport_has_changed =
9432 new_vxsize != VXSIZE ||
9433 new_vysize != VYSIZE ||
9434 game_status_last == GAME_MODE_EDITOR);
9436 return (playfield_viewport_has_changed &&
9437 door_1_viewport_has_changed &&
9438 door_2_viewport_has_changed);
9441 boolean CheckFadeAll(void)
9443 return (CheckIfGlobalBorderHasChanged() ||
9444 CheckIfAllViewportsHaveChanged());
9447 void ChangeViewportPropertiesIfNeeded(void)
9449 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9450 FALSE : setup.small_game_graphics);
9451 int gfx_game_mode = game_status;
9452 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9454 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9455 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9456 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9457 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9458 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9459 int new_win_xsize = vp_window->width;
9460 int new_win_ysize = vp_window->height;
9461 int border_left = vp_playfield->border_left;
9462 int border_right = vp_playfield->border_right;
9463 int border_top = vp_playfield->border_top;
9464 int border_bottom = vp_playfield->border_bottom;
9465 int new_sx = vp_playfield->x + border_left;
9466 int new_sy = vp_playfield->y + border_top;
9467 int new_sxsize = vp_playfield->width - border_left - border_right;
9468 int new_sysize = vp_playfield->height - border_top - border_bottom;
9469 int new_real_sx = vp_playfield->x;
9470 int new_real_sy = vp_playfield->y;
9471 int new_full_sxsize = vp_playfield->width;
9472 int new_full_sysize = vp_playfield->height;
9473 int new_dx = vp_door_1->x;
9474 int new_dy = vp_door_1->y;
9475 int new_dxsize = vp_door_1->width;
9476 int new_dysize = vp_door_1->height;
9477 int new_vx = vp_door_2->x;
9478 int new_vy = vp_door_2->y;
9479 int new_vxsize = vp_door_2->width;
9480 int new_vysize = vp_door_2->height;
9481 int new_ex = vp_door_3->x;
9482 int new_ey = vp_door_3->y;
9483 int new_exsize = vp_door_3->width;
9484 int new_eysize = vp_door_3->height;
9485 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9486 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9487 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9488 int new_scr_fieldx = new_sxsize / tilesize;
9489 int new_scr_fieldy = new_sysize / tilesize;
9490 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9491 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9492 boolean init_gfx_buffers = FALSE;
9493 boolean init_video_buffer = FALSE;
9494 boolean init_gadgets_and_anims = FALSE;
9495 boolean init_em_graphics = FALSE;
9497 if (new_win_xsize != WIN_XSIZE ||
9498 new_win_ysize != WIN_YSIZE)
9500 WIN_XSIZE = new_win_xsize;
9501 WIN_YSIZE = new_win_ysize;
9503 init_video_buffer = TRUE;
9504 init_gfx_buffers = TRUE;
9505 init_gadgets_and_anims = TRUE;
9507 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9510 if (new_scr_fieldx != SCR_FIELDX ||
9511 new_scr_fieldy != SCR_FIELDY)
9513 // this always toggles between MAIN and GAME when using small tile size
9515 SCR_FIELDX = new_scr_fieldx;
9516 SCR_FIELDY = new_scr_fieldy;
9518 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9529 new_sxsize != SXSIZE ||
9530 new_sysize != SYSIZE ||
9531 new_dxsize != DXSIZE ||
9532 new_dysize != DYSIZE ||
9533 new_vxsize != VXSIZE ||
9534 new_vysize != VYSIZE ||
9535 new_exsize != EXSIZE ||
9536 new_eysize != EYSIZE ||
9537 new_real_sx != REAL_SX ||
9538 new_real_sy != REAL_SY ||
9539 new_full_sxsize != FULL_SXSIZE ||
9540 new_full_sysize != FULL_SYSIZE ||
9541 new_tilesize_var != TILESIZE_VAR
9544 // ------------------------------------------------------------------------
9545 // determine next fading area for changed viewport definitions
9546 // ------------------------------------------------------------------------
9548 // start with current playfield area (default fading area)
9551 FADE_SXSIZE = FULL_SXSIZE;
9552 FADE_SYSIZE = FULL_SYSIZE;
9554 // add new playfield area if position or size has changed
9555 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9556 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9558 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9559 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9562 // add current and new door 1 area if position or size has changed
9563 if (new_dx != DX || new_dy != DY ||
9564 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9566 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9567 DX, DY, DXSIZE, DYSIZE);
9568 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9569 new_dx, new_dy, new_dxsize, new_dysize);
9572 // add current and new door 2 area if position or size has changed
9573 if (new_vx != VX || new_vy != VY ||
9574 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9576 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9577 VX, VY, VXSIZE, VYSIZE);
9578 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9579 new_vx, new_vy, new_vxsize, new_vysize);
9582 // ------------------------------------------------------------------------
9583 // handle changed tile size
9584 // ------------------------------------------------------------------------
9586 if (new_tilesize_var != TILESIZE_VAR)
9588 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9590 // changing tile size invalidates scroll values of engine snapshots
9591 FreeEngineSnapshotSingle();
9593 // changing tile size requires update of graphic mapping for EM engine
9594 init_em_graphics = TRUE;
9605 SXSIZE = new_sxsize;
9606 SYSIZE = new_sysize;
9607 DXSIZE = new_dxsize;
9608 DYSIZE = new_dysize;
9609 VXSIZE = new_vxsize;
9610 VYSIZE = new_vysize;
9611 EXSIZE = new_exsize;
9612 EYSIZE = new_eysize;
9613 REAL_SX = new_real_sx;
9614 REAL_SY = new_real_sy;
9615 FULL_SXSIZE = new_full_sxsize;
9616 FULL_SYSIZE = new_full_sysize;
9617 TILESIZE_VAR = new_tilesize_var;
9619 init_gfx_buffers = TRUE;
9620 init_gadgets_and_anims = TRUE;
9622 // printf("::: viewports: init_gfx_buffers\n");
9623 // printf("::: viewports: init_gadgets_and_anims\n");
9626 if (init_gfx_buffers)
9628 // printf("::: init_gfx_buffers\n");
9630 SCR_FIELDX = new_scr_fieldx_buffers;
9631 SCR_FIELDY = new_scr_fieldy_buffers;
9635 SCR_FIELDX = new_scr_fieldx;
9636 SCR_FIELDY = new_scr_fieldy;
9638 SetDrawDeactivationMask(REDRAW_NONE);
9639 SetDrawBackgroundMask(REDRAW_FIELD);
9642 if (init_video_buffer)
9644 // printf("::: init_video_buffer\n");
9646 FreeAllImageTextures(); // needs old renderer to free the textures
9648 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9649 InitImageTextures();
9652 if (init_gadgets_and_anims)
9654 // printf("::: init_gadgets_and_anims\n");
9657 InitGlobalAnimations();
9660 if (init_em_graphics)
9662 InitGraphicInfo_EM();