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 getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
294 lx = correctLevelPosX_EM(lx);
299 static int getLevelFromScreenY_EM(int sy)
301 int level_ysize = level.native_em_level->cav->height;
302 int full_ysize = level_ysize * TILESIZE_VAR;
304 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
306 int fy = getFieldbufferOffsetY_EM();
309 int ly = LEVELY((py + dy) / TILESIZE_VAR);
311 ly = correctLevelPosY_EM(ly);
316 static int getLevelFromScreenX_SP(int sx)
318 int menBorder = setup.sp_show_border_elements;
319 int level_xsize = level.native_sp_level->width;
320 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
322 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
324 int fx = getFieldbufferOffsetX_SP();
327 int lx = LEVELX((px + dx) / TILESIZE_VAR);
332 static int getLevelFromScreenY_SP(int sy)
334 int menBorder = setup.sp_show_border_elements;
335 int level_ysize = level.native_sp_level->height;
336 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
338 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
340 int fy = getFieldbufferOffsetY_SP();
343 int ly = LEVELY((py + dy) / TILESIZE_VAR);
348 static int getLevelFromScreenX_MM(int sx)
350 int level_xsize = level.native_mm_level->fieldx;
351 int full_xsize = level_xsize * TILESIZE_VAR;
353 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
356 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
361 static int getLevelFromScreenY_MM(int sy)
363 int level_ysize = level.native_mm_level->fieldy;
364 int full_ysize = level_ysize * TILESIZE_VAR;
366 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
369 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 int getLevelFromScreenX(int x)
376 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
377 return getLevelFromScreenX_EM(x);
378 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
379 return getLevelFromScreenX_SP(x);
380 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
381 return getLevelFromScreenX_MM(x);
383 return getLevelFromScreenX_RND(x);
386 int getLevelFromScreenY(int y)
388 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
389 return getLevelFromScreenY_EM(y);
390 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
391 return getLevelFromScreenY_SP(y);
392 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
393 return getLevelFromScreenY_MM(y);
395 return getLevelFromScreenY_RND(y);
398 void DumpTile(int x, int y)
404 printf_line("-", 79);
405 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
406 printf_line("-", 79);
408 if (!IN_LEV_FIELD(x, y))
410 printf("(not in level field)\n");
416 token_name = element_info[Feld[x][y]].token_name;
418 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
419 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
420 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
421 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
422 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
423 printf(" MovPos: %d\n", MovPos[x][y]);
424 printf(" MovDir: %d\n", MovDir[x][y]);
425 printf(" MovDelay: %d\n", MovDelay[x][y]);
426 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
427 printf(" CustomValue: %d\n", CustomValue[x][y]);
428 printf(" GfxElement: %d\n", GfxElement[x][y]);
429 printf(" GfxAction: %d\n", GfxAction[x][y]);
430 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
431 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
435 void DumpTileFromScreen(int sx, int sy)
437 int lx = getLevelFromScreenX(sx);
438 int ly = getLevelFromScreenY(sy);
443 void SetDrawtoField(int mode)
445 if (mode == DRAW_TO_FIELDBUFFER)
451 BX2 = SCR_FIELDX + 1;
452 BY2 = SCR_FIELDY + 1;
454 drawto_field = fieldbuffer;
456 else // DRAW_TO_BACKBUFFER
462 BX2 = SCR_FIELDX - 1;
463 BY2 = SCR_FIELDY - 1;
465 drawto_field = backbuffer;
469 static void RedrawPlayfield_RND(void)
471 if (game.envelope_active)
474 DrawLevel(REDRAW_ALL);
478 void RedrawPlayfield(void)
480 if (game_status != GAME_MODE_PLAYING)
483 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
484 RedrawPlayfield_EM(TRUE);
485 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
486 RedrawPlayfield_SP(TRUE);
487 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
488 RedrawPlayfield_MM();
489 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
490 RedrawPlayfield_RND();
492 BlitScreenToBitmap(backbuffer);
494 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
498 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
501 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
502 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
504 if (x == -1 && y == -1)
507 if (draw_target == DRAW_TO_SCREEN)
508 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
510 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
513 static void DrawMaskedBorderExt_FIELD(int draw_target)
515 if (global.border_status >= GAME_MODE_MAIN &&
516 global.border_status <= GAME_MODE_PLAYING &&
517 border.draw_masked[global.border_status])
518 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
522 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
524 // when drawing to backbuffer, never draw border over open doors
525 if (draw_target == DRAW_TO_BACKBUFFER &&
526 (GetDoorState() & DOOR_OPEN_1))
529 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
530 (global.border_status != GAME_MODE_EDITOR ||
531 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
532 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
535 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_2))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 global.border_status != GAME_MODE_EDITOR)
544 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
547 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
549 // currently not available
552 static void DrawMaskedBorderExt_ALL(int draw_target)
554 DrawMaskedBorderExt_FIELD(draw_target);
555 DrawMaskedBorderExt_DOOR_1(draw_target);
556 DrawMaskedBorderExt_DOOR_2(draw_target);
557 DrawMaskedBorderExt_DOOR_3(draw_target);
560 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
562 // never draw masked screen borders on borderless screens
563 if (global.border_status == GAME_MODE_LOADING ||
564 global.border_status == GAME_MODE_TITLE)
567 if (redraw_mask & REDRAW_ALL)
568 DrawMaskedBorderExt_ALL(draw_target);
571 if (redraw_mask & REDRAW_FIELD)
572 DrawMaskedBorderExt_FIELD(draw_target);
573 if (redraw_mask & REDRAW_DOOR_1)
574 DrawMaskedBorderExt_DOOR_1(draw_target);
575 if (redraw_mask & REDRAW_DOOR_2)
576 DrawMaskedBorderExt_DOOR_2(draw_target);
577 if (redraw_mask & REDRAW_DOOR_3)
578 DrawMaskedBorderExt_DOOR_3(draw_target);
582 void DrawMaskedBorder_FIELD(void)
584 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
587 void DrawMaskedBorder(int redraw_mask)
589 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
592 void DrawMaskedBorderToTarget(int draw_target)
594 if (draw_target == DRAW_TO_BACKBUFFER ||
595 draw_target == DRAW_TO_SCREEN)
597 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
601 int last_border_status = global.border_status;
603 if (draw_target == DRAW_TO_FADE_SOURCE)
605 global.border_status = gfx.fade_border_source_status;
606 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
608 else if (draw_target == DRAW_TO_FADE_TARGET)
610 global.border_status = gfx.fade_border_target_status;
611 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
614 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
616 global.border_status = last_border_status;
617 gfx.masked_border_bitmap_ptr = backbuffer;
621 void DrawTileCursor(int draw_target)
627 int graphic = IMG_GLOBAL_TILE_CURSOR;
629 int tilesize = TILESIZE_VAR;
630 int width = tilesize;
631 int height = tilesize;
633 if (game_status != GAME_MODE_PLAYING)
636 if (!tile_cursor.enabled ||
640 if (tile_cursor.moving)
642 int step = TILESIZE_VAR / 4;
643 int dx = tile_cursor.target_x - tile_cursor.x;
644 int dy = tile_cursor.target_y - tile_cursor.y;
647 tile_cursor.x = tile_cursor.target_x;
649 tile_cursor.x += SIGN(dx) * step;
652 tile_cursor.y = tile_cursor.target_y;
654 tile_cursor.y += SIGN(dy) * step;
656 if (tile_cursor.x == tile_cursor.target_x &&
657 tile_cursor.y == tile_cursor.target_y)
658 tile_cursor.moving = FALSE;
661 dst_x = tile_cursor.x;
662 dst_y = tile_cursor.y;
664 frame = getGraphicAnimationFrame(graphic, -1);
666 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
669 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
670 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
672 if (draw_target == DRAW_TO_SCREEN)
673 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
675 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
679 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
681 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
684 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
686 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
687 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
689 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
692 void BlitScreenToBitmap(Bitmap *target_bitmap)
694 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
695 BlitScreenToBitmap_EM(target_bitmap);
696 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
697 BlitScreenToBitmap_SP(target_bitmap);
698 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
699 BlitScreenToBitmap_MM(target_bitmap);
700 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
701 BlitScreenToBitmap_RND(target_bitmap);
703 redraw_mask |= REDRAW_FIELD;
706 static void DrawFramesPerSecond(void)
709 int font_nr = FONT_TEXT_2;
710 int font_width = getFontWidth(font_nr);
711 int draw_deactivation_mask = GetDrawDeactivationMask();
712 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
714 // draw FPS with leading space (needed if field buffer deactivated)
715 sprintf(text, " %04.1f fps", global.frames_per_second);
717 // override draw deactivation mask (required for invisible warp mode)
718 SetDrawDeactivationMask(REDRAW_NONE);
720 // draw opaque FPS if field buffer deactivated, else draw masked FPS
721 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
722 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
724 // set draw deactivation mask to previous value
725 SetDrawDeactivationMask(draw_deactivation_mask);
727 // force full-screen redraw in this frame
728 redraw_mask = REDRAW_ALL;
732 static void PrintFrameTimeDebugging(void)
734 static unsigned int last_counter = 0;
735 unsigned int counter = Counter();
736 int diff_1 = counter - last_counter;
737 int diff_2 = diff_1 - GAME_FRAME_DELAY;
739 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
740 char diff_bar[2 * diff_2_max + 5];
744 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
746 for (i = 0; i < diff_2_max; i++)
747 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
748 i >= diff_2_max - diff_2_cut ? '-' : ' ');
750 diff_bar[pos++] = '|';
752 for (i = 0; i < diff_2_max; i++)
753 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
755 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
757 diff_bar[pos++] = '\0';
759 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
762 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
765 last_counter = counter;
769 static int unifiedRedrawMask(int mask)
771 if (mask & REDRAW_ALL)
774 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
780 static boolean equalRedrawMasks(int mask_1, int mask_2)
782 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
785 void BackToFront(void)
787 static int last_redraw_mask = REDRAW_NONE;
789 // force screen redraw in every frame to continue drawing global animations
790 // (but always use the last redraw mask to prevent unwanted side effects)
791 if (redraw_mask == REDRAW_NONE)
792 redraw_mask = last_redraw_mask;
794 last_redraw_mask = redraw_mask;
797 // masked border now drawn immediately when blitting backbuffer to window
799 // draw masked border to all viewports, if defined
800 DrawMaskedBorder(redraw_mask);
803 // draw frames per second (only if debug mode is enabled)
804 if (redraw_mask & REDRAW_FPS)
805 DrawFramesPerSecond();
807 // remove playfield redraw before potentially merging with doors redraw
808 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
809 redraw_mask &= ~REDRAW_FIELD;
811 // redraw complete window if both playfield and (some) doors need redraw
812 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
813 redraw_mask = REDRAW_ALL;
815 /* although redrawing the whole window would be fine for normal gameplay,
816 being able to only redraw the playfield is required for deactivating
817 certain drawing areas (mainly playfield) to work, which is needed for
818 warp-forward to be fast enough (by skipping redraw of most frames) */
820 if (redraw_mask & REDRAW_ALL)
822 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
824 else if (redraw_mask & REDRAW_FIELD)
826 BlitBitmap(backbuffer, window,
827 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
829 else if (redraw_mask & REDRAW_DOORS)
831 // merge door areas to prevent calling screen redraw more than once
837 if (redraw_mask & REDRAW_DOOR_1)
841 x2 = MAX(x2, DX + DXSIZE);
842 y2 = MAX(y2, DY + DYSIZE);
845 if (redraw_mask & REDRAW_DOOR_2)
849 x2 = MAX(x2, VX + VXSIZE);
850 y2 = MAX(y2, VY + VYSIZE);
853 if (redraw_mask & REDRAW_DOOR_3)
857 x2 = MAX(x2, EX + EXSIZE);
858 y2 = MAX(y2, EY + EYSIZE);
861 // make sure that at least one pixel is blitted, and inside the screen
862 // (else nothing is blitted, causing the animations not to be updated)
863 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
864 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
865 x2 = MIN(MAX(1, x2), WIN_XSIZE);
866 y2 = MIN(MAX(1, y2), WIN_YSIZE);
868 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
871 redraw_mask = REDRAW_NONE;
874 PrintFrameTimeDebugging();
878 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
880 unsigned int frame_delay_value_old = GetVideoFrameDelay();
882 SetVideoFrameDelay(frame_delay_value);
886 SetVideoFrameDelay(frame_delay_value_old);
889 static int fade_type_skip = FADE_TYPE_NONE;
891 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
893 void (*draw_border_function)(void) = NULL;
894 int x, y, width, height;
895 int fade_delay, post_delay;
897 if (fade_type == FADE_TYPE_FADE_OUT)
899 if (fade_type_skip != FADE_TYPE_NONE)
901 // skip all fade operations until specified fade operation
902 if (fade_type & fade_type_skip)
903 fade_type_skip = FADE_TYPE_NONE;
908 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
912 redraw_mask |= fade_mask;
914 if (fade_type == FADE_TYPE_SKIP)
916 fade_type_skip = fade_mode;
921 fade_delay = fading.fade_delay;
922 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
924 if (fade_type_skip != FADE_TYPE_NONE)
926 // skip all fade operations until specified fade operation
927 if (fade_type & fade_type_skip)
928 fade_type_skip = FADE_TYPE_NONE;
933 if (global.autoplay_leveldir)
938 if (fade_mask == REDRAW_FIELD)
943 height = FADE_SYSIZE;
945 if (border.draw_masked_when_fading)
946 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
948 DrawMaskedBorder_FIELD(); // draw once
958 // when switching screens without fading, set fade delay to zero
959 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
962 // do not display black frame when fading out without fade delay
963 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
966 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
967 draw_border_function);
969 redraw_mask &= ~fade_mask;
971 ClearAutoRepeatKeyEvents();
974 static void SetScreenStates_BeforeFadingIn(void)
976 // temporarily set screen mode for animations to screen after fading in
977 global.anim_status = global.anim_status_next;
979 // store backbuffer with all animations that will be started after fading in
980 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
982 // set screen mode for animations back to fading
983 global.anim_status = GAME_MODE_PSEUDO_FADING;
986 static void SetScreenStates_AfterFadingIn(void)
988 // store new source screen (to use correct masked border for fading)
989 gfx.fade_border_source_status = global.border_status;
991 global.anim_status = global.anim_status_next;
994 static void SetScreenStates_BeforeFadingOut(void)
996 // store new target screen (to use correct masked border for fading)
997 gfx.fade_border_target_status = game_status;
999 // set screen mode for animations to fading
1000 global.anim_status = GAME_MODE_PSEUDO_FADING;
1002 // store backbuffer with all animations that will be stopped for fading out
1003 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1006 static void SetScreenStates_AfterFadingOut(void)
1008 global.border_status = game_status;
1011 void FadeIn(int fade_mask)
1013 SetScreenStates_BeforeFadingIn();
1016 DrawMaskedBorder(REDRAW_ALL);
1019 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1020 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1022 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1026 FADE_SXSIZE = FULL_SXSIZE;
1027 FADE_SYSIZE = FULL_SYSIZE;
1029 // activate virtual buttons depending on upcoming game status
1030 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1031 game_status == GAME_MODE_PLAYING && !tape.playing)
1032 SetOverlayActive(TRUE);
1034 SetScreenStates_AfterFadingIn();
1036 // force update of global animation status in case of rapid screen changes
1037 redraw_mask = REDRAW_ALL;
1041 void FadeOut(int fade_mask)
1043 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1044 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1045 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1048 SetScreenStates_BeforeFadingOut();
1050 SetTileCursorActive(FALSE);
1051 SetOverlayActive(FALSE);
1054 DrawMaskedBorder(REDRAW_ALL);
1057 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1058 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1060 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1062 SetScreenStates_AfterFadingOut();
1065 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1067 static struct TitleFadingInfo fading_leave_stored;
1070 fading_leave_stored = fading_leave;
1072 fading = fading_leave_stored;
1075 void FadeSetEnterMenu(void)
1077 fading = menu.enter_menu;
1079 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1082 void FadeSetLeaveMenu(void)
1084 fading = menu.leave_menu;
1086 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1089 void FadeSetEnterScreen(void)
1091 fading = menu.enter_screen[game_status];
1093 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1096 void FadeSetNextScreen(void)
1098 fading = menu.next_screen[game_status];
1100 // (do not overwrite fade mode set by FadeSetEnterScreen)
1101 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1104 void FadeSetLeaveScreen(void)
1106 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1109 void FadeSetFromType(int type)
1111 if (type & TYPE_ENTER_SCREEN)
1112 FadeSetEnterScreen();
1113 else if (type & TYPE_ENTER)
1115 else if (type & TYPE_LEAVE)
1119 void FadeSetDisabled(void)
1121 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1123 fading = fading_none;
1126 void FadeSkipNextFadeIn(void)
1128 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1131 void FadeSkipNextFadeOut(void)
1133 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1136 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1138 if (graphic == IMG_UNDEFINED)
1141 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1143 return (graphic_info[graphic].bitmap != NULL || redefined ?
1144 graphic_info[graphic].bitmap :
1145 graphic_info[default_graphic].bitmap);
1148 static Bitmap *getBackgroundBitmap(int graphic)
1150 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1153 static Bitmap *getGlobalBorderBitmap(int graphic)
1155 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1158 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1161 (status == GAME_MODE_MAIN ||
1162 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1163 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1164 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1165 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1168 return getGlobalBorderBitmap(graphic);
1171 void SetWindowBackgroundImageIfDefined(int graphic)
1173 if (graphic_info[graphic].bitmap)
1174 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1177 void SetMainBackgroundImageIfDefined(int graphic)
1179 if (graphic_info[graphic].bitmap)
1180 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1183 void SetDoorBackgroundImageIfDefined(int graphic)
1185 if (graphic_info[graphic].bitmap)
1186 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1189 void SetWindowBackgroundImage(int graphic)
1191 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1194 void SetMainBackgroundImage(int graphic)
1196 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1199 void SetDoorBackgroundImage(int graphic)
1201 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1204 void SetPanelBackground(void)
1206 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1208 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1209 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1211 SetDoorBackgroundBitmap(bitmap_db_panel);
1214 void DrawBackground(int x, int y, int width, int height)
1216 // "drawto" might still point to playfield buffer here (hall of fame)
1217 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1219 if (IN_GFX_FIELD_FULL(x, y))
1220 redraw_mask |= REDRAW_FIELD;
1221 else if (IN_GFX_DOOR_1(x, y))
1222 redraw_mask |= REDRAW_DOOR_1;
1223 else if (IN_GFX_DOOR_2(x, y))
1224 redraw_mask |= REDRAW_DOOR_2;
1225 else if (IN_GFX_DOOR_3(x, y))
1226 redraw_mask |= REDRAW_DOOR_3;
1229 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1231 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1233 if (font->bitmap == NULL)
1236 DrawBackground(x, y, width, height);
1239 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1241 struct GraphicInfo *g = &graphic_info[graphic];
1243 if (g->bitmap == NULL)
1246 DrawBackground(x, y, width, height);
1249 static int game_status_last = -1;
1250 static Bitmap *global_border_bitmap_last = NULL;
1251 static Bitmap *global_border_bitmap = NULL;
1252 static int real_sx_last = -1, real_sy_last = -1;
1253 static int full_sxsize_last = -1, full_sysize_last = -1;
1254 static int dx_last = -1, dy_last = -1;
1255 static int dxsize_last = -1, dysize_last = -1;
1256 static int vx_last = -1, vy_last = -1;
1257 static int vxsize_last = -1, vysize_last = -1;
1258 static int ex_last = -1, ey_last = -1;
1259 static int exsize_last = -1, eysize_last = -1;
1261 boolean CheckIfGlobalBorderHasChanged(void)
1263 // if game status has not changed, global border has not changed either
1264 if (game_status == game_status_last)
1267 // determine and store new global border bitmap for current game status
1268 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1270 return (global_border_bitmap_last != global_border_bitmap);
1273 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1275 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1276 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1278 // if game status has not changed, nothing has to be redrawn
1279 if (game_status == game_status_last)
1282 // redraw if last screen was title screen
1283 if (game_status_last == GAME_MODE_TITLE)
1286 // redraw if global screen border has changed
1287 if (CheckIfGlobalBorderHasChanged())
1290 // redraw if position or size of playfield area has changed
1291 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1292 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1295 // redraw if position or size of door area has changed
1296 if (dx_last != DX || dy_last != DY ||
1297 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1300 // redraw if position or size of tape area has changed
1301 if (vx_last != VX || vy_last != VY ||
1302 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1305 // redraw if position or size of editor area has changed
1306 if (ex_last != EX || ey_last != EY ||
1307 exsize_last != EXSIZE || eysize_last != EYSIZE)
1314 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1317 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1319 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1322 void RedrawGlobalBorder(void)
1324 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1326 RedrawGlobalBorderFromBitmap(bitmap);
1328 redraw_mask = REDRAW_ALL;
1331 static void RedrawGlobalBorderIfNeeded(void)
1333 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1334 if (game_status == game_status_last)
1338 // copy current draw buffer to later copy back areas that have not changed
1339 if (game_status_last != GAME_MODE_TITLE)
1340 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1342 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1343 if (CheckIfGlobalBorderRedrawIsNeeded())
1346 // redraw global screen border (or clear, if defined to be empty)
1347 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1349 if (game_status == GAME_MODE_EDITOR)
1350 DrawSpecialEditorDoor();
1352 // copy previous playfield and door areas, if they are defined on both
1353 // previous and current screen and if they still have the same size
1355 if (real_sx_last != -1 && real_sy_last != -1 &&
1356 REAL_SX != -1 && REAL_SY != -1 &&
1357 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1358 BlitBitmap(bitmap_db_store_1, backbuffer,
1359 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1362 if (dx_last != -1 && dy_last != -1 &&
1363 DX != -1 && DY != -1 &&
1364 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1365 BlitBitmap(bitmap_db_store_1, backbuffer,
1366 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1368 if (game_status != GAME_MODE_EDITOR)
1370 if (vx_last != -1 && vy_last != -1 &&
1371 VX != -1 && VY != -1 &&
1372 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1373 BlitBitmap(bitmap_db_store_1, backbuffer,
1374 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1378 if (ex_last != -1 && ey_last != -1 &&
1379 EX != -1 && EY != -1 &&
1380 exsize_last == EXSIZE && eysize_last == EYSIZE)
1381 BlitBitmap(bitmap_db_store_1, backbuffer,
1382 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1385 redraw_mask = REDRAW_ALL;
1388 game_status_last = game_status;
1390 global_border_bitmap_last = global_border_bitmap;
1392 real_sx_last = REAL_SX;
1393 real_sy_last = REAL_SY;
1394 full_sxsize_last = FULL_SXSIZE;
1395 full_sysize_last = FULL_SYSIZE;
1398 dxsize_last = DXSIZE;
1399 dysize_last = DYSIZE;
1402 vxsize_last = VXSIZE;
1403 vysize_last = VYSIZE;
1406 exsize_last = EXSIZE;
1407 eysize_last = EYSIZE;
1410 void ClearField(void)
1412 RedrawGlobalBorderIfNeeded();
1414 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1415 // (when entering hall of fame after playing)
1416 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1418 // !!! maybe this should be done before clearing the background !!!
1419 if (game_status == GAME_MODE_PLAYING)
1421 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1422 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1426 SetDrawtoField(DRAW_TO_BACKBUFFER);
1430 void MarkTileDirty(int x, int y)
1432 redraw_mask |= REDRAW_FIELD;
1435 void SetBorderElement(void)
1439 BorderElement = EL_EMPTY;
1441 // only the R'n'D game engine may use an additional steelwall border
1442 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1445 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1447 for (x = 0; x < lev_fieldx; x++)
1449 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1450 BorderElement = EL_STEELWALL;
1452 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1458 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1459 int max_array_fieldx, int max_array_fieldy,
1460 short field[max_array_fieldx][max_array_fieldy],
1461 int max_fieldx, int max_fieldy)
1465 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1466 static int safety = 0;
1468 // check if starting field still has the desired content
1469 if (field[from_x][from_y] == fill_element)
1474 if (safety > max_fieldx * max_fieldy)
1475 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1477 old_element = field[from_x][from_y];
1478 field[from_x][from_y] = fill_element;
1480 for (i = 0; i < 4; i++)
1482 x = from_x + check[i][0];
1483 y = from_y + check[i][1];
1485 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1486 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1487 field, max_fieldx, max_fieldy);
1493 void FloodFillLevel(int from_x, int from_y, int fill_element,
1494 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1495 int max_fieldx, int max_fieldy)
1497 FloodFillLevelExt(from_x, from_y, fill_element,
1498 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1499 max_fieldx, max_fieldy);
1502 void SetRandomAnimationValue(int x, int y)
1504 gfx.anim_random_frame = GfxRandom[x][y];
1507 int getGraphicAnimationFrame(int graphic, int sync_frame)
1509 // animation synchronized with global frame counter, not move position
1510 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1511 sync_frame = FrameCounter;
1513 return getAnimationFrame(graphic_info[graphic].anim_frames,
1514 graphic_info[graphic].anim_delay,
1515 graphic_info[graphic].anim_mode,
1516 graphic_info[graphic].anim_start_frame,
1520 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1522 struct GraphicInfo *g = &graphic_info[graphic];
1523 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1525 if (tilesize == gfx.standard_tile_size)
1526 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1527 else if (tilesize == game.tile_size)
1528 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1530 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1533 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1534 boolean get_backside)
1536 struct GraphicInfo *g = &graphic_info[graphic];
1537 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1538 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1540 if (g->offset_y == 0) // frames are ordered horizontally
1542 int max_width = g->anim_frames_per_line * g->width;
1543 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1545 *x = pos % max_width;
1546 *y = src_y % g->height + pos / max_width * g->height;
1548 else if (g->offset_x == 0) // frames are ordered vertically
1550 int max_height = g->anim_frames_per_line * g->height;
1551 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1553 *x = src_x % g->width + pos / max_height * g->width;
1554 *y = pos % max_height;
1556 else // frames are ordered diagonally
1558 *x = src_x + frame * g->offset_x;
1559 *y = src_y + frame * g->offset_y;
1563 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1564 Bitmap **bitmap, int *x, int *y,
1565 boolean get_backside)
1567 struct GraphicInfo *g = &graphic_info[graphic];
1569 // if no graphics defined at all, use fallback graphics
1570 if (g->bitmaps == NULL)
1571 *g = graphic_info[IMG_CHAR_EXCLAM];
1573 // if no in-game graphics defined, always use standard graphic size
1574 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1575 tilesize = TILESIZE;
1577 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1578 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1580 *x = *x * tilesize / g->tile_size;
1581 *y = *y * tilesize / g->tile_size;
1584 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1585 Bitmap **bitmap, int *x, int *y)
1587 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1590 void getFixedGraphicSource(int graphic, int frame,
1591 Bitmap **bitmap, int *x, int *y)
1593 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1596 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1598 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1601 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1602 int *x, int *y, boolean get_backside)
1604 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1608 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1610 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1613 void DrawGraphic(int x, int y, int graphic, int frame)
1616 if (!IN_SCR_FIELD(x, y))
1618 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1619 printf("DrawGraphic(): This should never happen!\n");
1624 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1627 MarkTileDirty(x, y);
1630 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1633 if (!IN_SCR_FIELD(x, y))
1635 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1636 printf("DrawGraphic(): This should never happen!\n");
1641 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1643 MarkTileDirty(x, y);
1646 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1652 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1654 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1657 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1663 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1664 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1667 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1670 if (!IN_SCR_FIELD(x, y))
1672 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1673 printf("DrawGraphicThruMask(): This should never happen!\n");
1678 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1681 MarkTileDirty(x, y);
1684 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1687 if (!IN_SCR_FIELD(x, y))
1689 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1690 printf("DrawGraphicThruMask(): This should never happen!\n");
1695 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1697 MarkTileDirty(x, y);
1700 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1706 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1708 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1712 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1713 int graphic, int frame)
1718 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1720 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1724 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1726 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1728 MarkTileDirty(x / tilesize, y / tilesize);
1731 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1734 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1735 graphic, frame, tilesize);
1736 MarkTileDirty(x / tilesize, y / tilesize);
1739 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1745 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1746 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1749 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1750 int frame, int tilesize)
1755 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1756 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1759 void DrawMiniGraphic(int x, int y, int graphic)
1761 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1762 MarkTileDirty(x / 2, y / 2);
1765 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1770 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1771 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1774 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1775 int graphic, int frame,
1776 int cut_mode, int mask_mode)
1781 int width = TILEX, height = TILEY;
1784 if (dx || dy) // shifted graphic
1786 if (x < BX1) // object enters playfield from the left
1793 else if (x > BX2) // object enters playfield from the right
1799 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1805 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1807 else if (dx) // general horizontal movement
1808 MarkTileDirty(x + SIGN(dx), y);
1810 if (y < BY1) // object enters playfield from the top
1812 if (cut_mode == CUT_BELOW) // object completely above top border
1820 else if (y > BY2) // object enters playfield from the bottom
1826 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1832 else if (dy > 0 && cut_mode == CUT_ABOVE)
1834 if (y == BY2) // object completely above bottom border
1840 MarkTileDirty(x, y + 1);
1841 } // object leaves playfield to the bottom
1842 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1844 else if (dy) // general vertical movement
1845 MarkTileDirty(x, y + SIGN(dy));
1849 if (!IN_SCR_FIELD(x, y))
1851 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1852 printf("DrawGraphicShifted(): This should never happen!\n");
1857 width = width * TILESIZE_VAR / TILESIZE;
1858 height = height * TILESIZE_VAR / TILESIZE;
1859 cx = cx * TILESIZE_VAR / TILESIZE;
1860 cy = cy * TILESIZE_VAR / TILESIZE;
1861 dx = dx * TILESIZE_VAR / TILESIZE;
1862 dy = dy * TILESIZE_VAR / TILESIZE;
1864 if (width > 0 && height > 0)
1866 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1871 dst_x = FX + x * TILEX_VAR + dx;
1872 dst_y = FY + y * TILEY_VAR + dy;
1874 if (mask_mode == USE_MASKING)
1875 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1878 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1881 MarkTileDirty(x, y);
1885 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1886 int graphic, int frame,
1887 int cut_mode, int mask_mode)
1892 int width = TILEX_VAR, height = TILEY_VAR;
1895 int x2 = x + SIGN(dx);
1896 int y2 = y + SIGN(dy);
1898 // movement with two-tile animations must be sync'ed with movement position,
1899 // not with current GfxFrame (which can be higher when using slow movement)
1900 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1901 int anim_frames = graphic_info[graphic].anim_frames;
1903 // (we also need anim_delay here for movement animations with less frames)
1904 int anim_delay = graphic_info[graphic].anim_delay;
1905 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1907 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1908 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1910 // re-calculate animation frame for two-tile movement animation
1911 frame = getGraphicAnimationFrame(graphic, sync_frame);
1913 // check if movement start graphic inside screen area and should be drawn
1914 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1916 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1918 dst_x = FX + x1 * TILEX_VAR;
1919 dst_y = FY + y1 * TILEY_VAR;
1921 if (mask_mode == USE_MASKING)
1922 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1925 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1928 MarkTileDirty(x1, y1);
1931 // check if movement end graphic inside screen area and should be drawn
1932 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1934 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1936 dst_x = FX + x2 * TILEX_VAR;
1937 dst_y = FY + y2 * TILEY_VAR;
1939 if (mask_mode == USE_MASKING)
1940 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1943 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1946 MarkTileDirty(x2, y2);
1950 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1951 int graphic, int frame,
1952 int cut_mode, int mask_mode)
1956 DrawGraphic(x, y, graphic, frame);
1961 if (graphic_info[graphic].double_movement) // EM style movement images
1962 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1964 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1967 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1968 int graphic, int frame, int cut_mode)
1970 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1973 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1974 int cut_mode, int mask_mode)
1976 int lx = LEVELX(x), ly = LEVELY(y);
1980 if (IN_LEV_FIELD(lx, ly))
1982 SetRandomAnimationValue(lx, ly);
1984 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1985 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1987 // do not use double (EM style) movement graphic when not moving
1988 if (graphic_info[graphic].double_movement && !dx && !dy)
1990 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1991 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1994 else // border element
1996 graphic = el2img(element);
1997 frame = getGraphicAnimationFrame(graphic, -1);
2000 if (element == EL_EXPANDABLE_WALL)
2002 boolean left_stopped = FALSE, right_stopped = FALSE;
2004 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2005 left_stopped = TRUE;
2006 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2007 right_stopped = TRUE;
2009 if (left_stopped && right_stopped)
2011 else if (left_stopped)
2013 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2014 frame = graphic_info[graphic].anim_frames - 1;
2016 else if (right_stopped)
2018 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2019 frame = graphic_info[graphic].anim_frames - 1;
2024 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2025 else if (mask_mode == USE_MASKING)
2026 DrawGraphicThruMask(x, y, graphic, frame);
2028 DrawGraphic(x, y, graphic, frame);
2031 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2032 int cut_mode, int mask_mode)
2034 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2035 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2036 cut_mode, mask_mode);
2039 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2042 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2045 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2048 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2051 void DrawLevelElementThruMask(int x, int y, int element)
2053 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2056 void DrawLevelFieldThruMask(int x, int y)
2058 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2061 // !!! implementation of quicksand is totally broken !!!
2062 #define IS_CRUMBLED_TILE(x, y, e) \
2063 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2064 !IS_MOVING(x, y) || \
2065 (e) == EL_QUICKSAND_EMPTYING || \
2066 (e) == EL_QUICKSAND_FAST_EMPTYING))
2068 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2073 int width, height, cx, cy;
2074 int sx = SCREENX(x), sy = SCREENY(y);
2075 int crumbled_border_size = graphic_info[graphic].border_size;
2076 int crumbled_tile_size = graphic_info[graphic].tile_size;
2077 int crumbled_border_size_var =
2078 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2081 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2083 for (i = 1; i < 4; i++)
2085 int dxx = (i & 1 ? dx : 0);
2086 int dyy = (i & 2 ? dy : 0);
2089 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2092 // check if neighbour field is of same crumble type
2093 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2094 graphic_info[graphic].class ==
2095 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2097 // return if check prevents inner corner
2098 if (same == (dxx == dx && dyy == dy))
2102 // if we reach this point, we have an inner corner
2104 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2106 width = crumbled_border_size_var;
2107 height = crumbled_border_size_var;
2108 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2109 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2111 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2112 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2115 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2120 int width, height, bx, by, cx, cy;
2121 int sx = SCREENX(x), sy = SCREENY(y);
2122 int crumbled_border_size = graphic_info[graphic].border_size;
2123 int crumbled_tile_size = graphic_info[graphic].tile_size;
2124 int crumbled_border_size_var =
2125 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2126 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2129 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2131 // draw simple, sloppy, non-corner-accurate crumbled border
2133 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2134 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2135 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2136 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2138 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2139 FX + sx * TILEX_VAR + cx,
2140 FY + sy * TILEY_VAR + cy);
2142 // (remaining middle border part must be at least as big as corner part)
2143 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2144 crumbled_border_size_var >= TILESIZE_VAR / 3)
2147 // correct corners of crumbled border, if needed
2149 for (i = -1; i <= 1; i += 2)
2151 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2152 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2153 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2156 // check if neighbour field is of same crumble type
2157 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2158 graphic_info[graphic].class ==
2159 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2161 // no crumbled corner, but continued crumbled border
2163 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2164 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2165 int b1 = (i == 1 ? crumbled_border_size_var :
2166 TILESIZE_VAR - 2 * crumbled_border_size_var);
2168 width = crumbled_border_size_var;
2169 height = crumbled_border_size_var;
2171 if (dir == 1 || dir == 2)
2186 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2188 FX + sx * TILEX_VAR + cx,
2189 FY + sy * TILEY_VAR + cy);
2194 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2196 int sx = SCREENX(x), sy = SCREENY(y);
2199 static int xy[4][2] =
2207 if (!IN_LEV_FIELD(x, y))
2210 element = TILE_GFX_ELEMENT(x, y);
2212 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2214 if (!IN_SCR_FIELD(sx, sy))
2217 // crumble field borders towards direct neighbour fields
2218 for (i = 0; i < 4; i++)
2220 int xx = x + xy[i][0];
2221 int yy = y + xy[i][1];
2223 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2226 // check if neighbour field is of same crumble type
2227 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2228 graphic_info[graphic].class ==
2229 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2232 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2235 // crumble inner field corners towards corner neighbour fields
2236 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2237 graphic_info[graphic].anim_frames == 2)
2239 for (i = 0; i < 4; i++)
2241 int dx = (i & 1 ? +1 : -1);
2242 int dy = (i & 2 ? +1 : -1);
2244 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2248 MarkTileDirty(sx, sy);
2250 else // center field is not crumbled -- crumble neighbour fields
2252 // crumble field borders of direct neighbour fields
2253 for (i = 0; i < 4; i++)
2255 int xx = x + xy[i][0];
2256 int yy = y + xy[i][1];
2257 int sxx = sx + xy[i][0];
2258 int syy = sy + xy[i][1];
2260 if (!IN_LEV_FIELD(xx, yy) ||
2261 !IN_SCR_FIELD(sxx, syy))
2264 // do not crumble fields that are being digged or snapped
2265 if (Feld[xx][yy] == EL_EMPTY ||
2266 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2269 element = TILE_GFX_ELEMENT(xx, yy);
2271 if (!IS_CRUMBLED_TILE(xx, yy, element))
2274 graphic = el_act2crm(element, ACTION_DEFAULT);
2276 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2278 MarkTileDirty(sxx, syy);
2281 // crumble inner field corners of corner neighbour fields
2282 for (i = 0; i < 4; i++)
2284 int dx = (i & 1 ? +1 : -1);
2285 int dy = (i & 2 ? +1 : -1);
2291 if (!IN_LEV_FIELD(xx, yy) ||
2292 !IN_SCR_FIELD(sxx, syy))
2295 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2298 element = TILE_GFX_ELEMENT(xx, yy);
2300 if (!IS_CRUMBLED_TILE(xx, yy, element))
2303 graphic = el_act2crm(element, ACTION_DEFAULT);
2305 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2306 graphic_info[graphic].anim_frames == 2)
2307 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2309 MarkTileDirty(sxx, syy);
2314 void DrawLevelFieldCrumbled(int x, int y)
2318 if (!IN_LEV_FIELD(x, y))
2321 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2322 GfxElement[x][y] != EL_UNDEFINED &&
2323 GFX_CRUMBLED(GfxElement[x][y]))
2325 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2330 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2332 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2335 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2338 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2339 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2340 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2341 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2342 int sx = SCREENX(x), sy = SCREENY(y);
2344 DrawGraphic(sx, sy, graphic1, frame1);
2345 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2348 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2350 int sx = SCREENX(x), sy = SCREENY(y);
2351 static int xy[4][2] =
2360 // crumble direct neighbour fields (required for field borders)
2361 for (i = 0; i < 4; i++)
2363 int xx = x + xy[i][0];
2364 int yy = y + xy[i][1];
2365 int sxx = sx + xy[i][0];
2366 int syy = sy + xy[i][1];
2368 if (!IN_LEV_FIELD(xx, yy) ||
2369 !IN_SCR_FIELD(sxx, syy) ||
2370 !GFX_CRUMBLED(Feld[xx][yy]) ||
2374 DrawLevelField(xx, yy);
2377 // crumble corner neighbour fields (required for inner field corners)
2378 for (i = 0; i < 4; i++)
2380 int dx = (i & 1 ? +1 : -1);
2381 int dy = (i & 2 ? +1 : -1);
2387 if (!IN_LEV_FIELD(xx, yy) ||
2388 !IN_SCR_FIELD(sxx, syy) ||
2389 !GFX_CRUMBLED(Feld[xx][yy]) ||
2393 int element = TILE_GFX_ELEMENT(xx, yy);
2394 int graphic = el_act2crm(element, ACTION_DEFAULT);
2396 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2397 graphic_info[graphic].anim_frames == 2)
2398 DrawLevelField(xx, yy);
2402 static int getBorderElement(int x, int y)
2406 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2407 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2408 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2409 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2410 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2411 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2412 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2414 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2415 int steel_position = (x == -1 && y == -1 ? 0 :
2416 x == lev_fieldx && y == -1 ? 1 :
2417 x == -1 && y == lev_fieldy ? 2 :
2418 x == lev_fieldx && y == lev_fieldy ? 3 :
2419 x == -1 || x == lev_fieldx ? 4 :
2420 y == -1 || y == lev_fieldy ? 5 : 6);
2422 return border[steel_position][steel_type];
2425 void DrawScreenElement(int x, int y, int element)
2427 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2428 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2431 void DrawLevelElement(int x, int y, int element)
2433 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2434 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2437 void DrawScreenField(int x, int y)
2439 int lx = LEVELX(x), ly = LEVELY(y);
2440 int element, content;
2442 if (!IN_LEV_FIELD(lx, ly))
2444 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2447 element = getBorderElement(lx, ly);
2449 DrawScreenElement(x, y, element);
2454 element = Feld[lx][ly];
2455 content = Store[lx][ly];
2457 if (IS_MOVING(lx, ly))
2459 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2460 boolean cut_mode = NO_CUTTING;
2462 if (element == EL_QUICKSAND_EMPTYING ||
2463 element == EL_QUICKSAND_FAST_EMPTYING ||
2464 element == EL_MAGIC_WALL_EMPTYING ||
2465 element == EL_BD_MAGIC_WALL_EMPTYING ||
2466 element == EL_DC_MAGIC_WALL_EMPTYING ||
2467 element == EL_AMOEBA_DROPPING)
2468 cut_mode = CUT_ABOVE;
2469 else if (element == EL_QUICKSAND_FILLING ||
2470 element == EL_QUICKSAND_FAST_FILLING ||
2471 element == EL_MAGIC_WALL_FILLING ||
2472 element == EL_BD_MAGIC_WALL_FILLING ||
2473 element == EL_DC_MAGIC_WALL_FILLING)
2474 cut_mode = CUT_BELOW;
2476 if (cut_mode == CUT_ABOVE)
2477 DrawScreenElement(x, y, element);
2479 DrawScreenElement(x, y, EL_EMPTY);
2482 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2483 else if (cut_mode == NO_CUTTING)
2484 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2487 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2489 if (cut_mode == CUT_BELOW &&
2490 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2491 DrawLevelElement(lx, ly + 1, element);
2494 if (content == EL_ACID)
2496 int dir = MovDir[lx][ly];
2497 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2498 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2500 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2502 // prevent target field from being drawn again (but without masking)
2503 // (this would happen if target field is scanned after moving element)
2504 Stop[newlx][newly] = TRUE;
2507 else if (IS_BLOCKED(lx, ly))
2512 boolean cut_mode = NO_CUTTING;
2513 int element_old, content_old;
2515 Blocked2Moving(lx, ly, &oldx, &oldy);
2518 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2519 MovDir[oldx][oldy] == MV_RIGHT);
2521 element_old = Feld[oldx][oldy];
2522 content_old = Store[oldx][oldy];
2524 if (element_old == EL_QUICKSAND_EMPTYING ||
2525 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2526 element_old == EL_MAGIC_WALL_EMPTYING ||
2527 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2528 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2529 element_old == EL_AMOEBA_DROPPING)
2530 cut_mode = CUT_ABOVE;
2532 DrawScreenElement(x, y, EL_EMPTY);
2535 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2537 else if (cut_mode == NO_CUTTING)
2538 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2541 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2544 else if (IS_DRAWABLE(element))
2545 DrawScreenElement(x, y, element);
2547 DrawScreenElement(x, y, EL_EMPTY);
2550 void DrawLevelField(int x, int y)
2552 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2553 DrawScreenField(SCREENX(x), SCREENY(y));
2554 else if (IS_MOVING(x, y))
2558 Moving2Blocked(x, y, &newx, &newy);
2559 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2560 DrawScreenField(SCREENX(newx), SCREENY(newy));
2562 else if (IS_BLOCKED(x, y))
2566 Blocked2Moving(x, y, &oldx, &oldy);
2567 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2568 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2572 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2573 int (*el2img_function)(int), boolean masked,
2574 int element_bits_draw)
2576 int element_base = map_mm_wall_element(element);
2577 int element_bits = (IS_DF_WALL(element) ?
2578 element - EL_DF_WALL_START :
2579 IS_MM_WALL(element) ?
2580 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2581 int graphic = el2img_function(element_base);
2582 int tilesize_draw = tilesize / 2;
2587 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2589 for (i = 0; i < 4; i++)
2591 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2592 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2594 if (!(element_bits_draw & (1 << i)))
2597 if (element_bits & (1 << i))
2600 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2601 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2603 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2604 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2609 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2610 tilesize_draw, tilesize_draw);
2615 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2616 boolean masked, int element_bits_draw)
2618 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2619 element, tilesize, el2edimg, masked, element_bits_draw);
2622 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2623 int (*el2img_function)(int))
2625 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2629 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2632 if (IS_MM_WALL(element))
2634 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2635 element, tilesize, el2edimg, masked, 0x000f);
2639 int graphic = el2edimg(element);
2642 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2644 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2648 void DrawSizedElement(int x, int y, int element, int tilesize)
2650 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2653 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2655 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2658 void DrawMiniElement(int x, int y, int element)
2662 graphic = el2edimg(element);
2663 DrawMiniGraphic(x, y, graphic);
2666 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2669 int x = sx + scroll_x, y = sy + scroll_y;
2671 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2672 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2673 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2674 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2676 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2679 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2681 int x = sx + scroll_x, y = sy + scroll_y;
2683 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2684 DrawMiniElement(sx, sy, EL_EMPTY);
2685 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2686 DrawMiniElement(sx, sy, Feld[x][y]);
2688 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2691 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2692 int x, int y, int xsize, int ysize,
2693 int tile_width, int tile_height)
2697 int dst_x = startx + x * tile_width;
2698 int dst_y = starty + y * tile_height;
2699 int width = graphic_info[graphic].width;
2700 int height = graphic_info[graphic].height;
2701 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2702 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2703 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2704 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2705 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2706 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2707 boolean draw_masked = graphic_info[graphic].draw_masked;
2709 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2711 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2713 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2717 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2718 inner_sx + (x - 1) * tile_width % inner_width);
2719 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2720 inner_sy + (y - 1) * tile_height % inner_height);
2723 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2726 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2730 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2731 int x, int y, int xsize, int ysize,
2734 int font_width = getFontWidth(font_nr);
2735 int font_height = getFontHeight(font_nr);
2737 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2738 font_width, font_height);
2741 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2743 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2744 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2745 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2746 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2747 boolean no_delay = (tape.warp_forward);
2748 unsigned int anim_delay = 0;
2749 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2750 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2751 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2752 int font_width = getFontWidth(font_nr);
2753 int font_height = getFontHeight(font_nr);
2754 int max_xsize = level.envelope[envelope_nr].xsize;
2755 int max_ysize = level.envelope[envelope_nr].ysize;
2756 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2757 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2758 int xend = max_xsize;
2759 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2760 int xstep = (xstart < xend ? 1 : 0);
2761 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2763 int end = MAX(xend - xstart, yend - ystart);
2766 for (i = start; i <= end; i++)
2768 int last_frame = end; // last frame of this "for" loop
2769 int x = xstart + i * xstep;
2770 int y = ystart + i * ystep;
2771 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2772 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2773 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2774 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2777 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2779 BlitScreenToBitmap(backbuffer);
2781 SetDrawtoField(DRAW_TO_BACKBUFFER);
2783 for (yy = 0; yy < ysize; yy++)
2784 for (xx = 0; xx < xsize; xx++)
2785 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2787 DrawTextBuffer(sx + font_width, sy + font_height,
2788 level.envelope[envelope_nr].text, font_nr, max_xsize,
2789 xsize - 2, ysize - 2, 0, mask_mode,
2790 level.envelope[envelope_nr].autowrap,
2791 level.envelope[envelope_nr].centered, FALSE);
2793 redraw_mask |= REDRAW_FIELD;
2796 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2799 ClearAutoRepeatKeyEvents();
2802 void ShowEnvelope(int envelope_nr)
2804 int element = EL_ENVELOPE_1 + envelope_nr;
2805 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2806 int sound_opening = element_info[element].sound[ACTION_OPENING];
2807 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2808 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2809 boolean no_delay = (tape.warp_forward);
2810 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2811 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2812 int anim_mode = graphic_info[graphic].anim_mode;
2813 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2814 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2816 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2818 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2820 if (anim_mode == ANIM_DEFAULT)
2821 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2823 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2826 Delay_WithScreenUpdates(wait_delay_value);
2828 WaitForEventToContinue();
2830 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2832 if (anim_mode != ANIM_NONE)
2833 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2835 if (anim_mode == ANIM_DEFAULT)
2836 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2838 game.envelope_active = FALSE;
2840 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2842 redraw_mask |= REDRAW_FIELD;
2846 static void setRequestBasePosition(int *x, int *y)
2848 int sx_base, sy_base;
2850 if (request.x != -1)
2851 sx_base = request.x;
2852 else if (request.align == ALIGN_LEFT)
2854 else if (request.align == ALIGN_RIGHT)
2855 sx_base = SX + SXSIZE;
2857 sx_base = SX + SXSIZE / 2;
2859 if (request.y != -1)
2860 sy_base = request.y;
2861 else if (request.valign == VALIGN_TOP)
2863 else if (request.valign == VALIGN_BOTTOM)
2864 sy_base = SY + SYSIZE;
2866 sy_base = SY + SYSIZE / 2;
2872 static void setRequestPositionExt(int *x, int *y, int width, int height,
2873 boolean add_border_size)
2875 int border_size = request.border_size;
2876 int sx_base, sy_base;
2879 setRequestBasePosition(&sx_base, &sy_base);
2881 if (request.align == ALIGN_LEFT)
2883 else if (request.align == ALIGN_RIGHT)
2884 sx = sx_base - width;
2886 sx = sx_base - width / 2;
2888 if (request.valign == VALIGN_TOP)
2890 else if (request.valign == VALIGN_BOTTOM)
2891 sy = sy_base - height;
2893 sy = sy_base - height / 2;
2895 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2896 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2898 if (add_border_size)
2908 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2910 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2913 static void DrawEnvelopeRequest(char *text)
2915 char *text_final = text;
2916 char *text_door_style = NULL;
2917 int graphic = IMG_BACKGROUND_REQUEST;
2918 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2919 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2920 int font_nr = FONT_REQUEST;
2921 int font_width = getFontWidth(font_nr);
2922 int font_height = getFontHeight(font_nr);
2923 int border_size = request.border_size;
2924 int line_spacing = request.line_spacing;
2925 int line_height = font_height + line_spacing;
2926 int max_text_width = request.width - 2 * border_size;
2927 int max_text_height = request.height - 2 * border_size;
2928 int line_length = max_text_width / font_width;
2929 int max_lines = max_text_height / line_height;
2930 int text_width = line_length * font_width;
2931 int width = request.width;
2932 int height = request.height;
2933 int tile_size = MAX(request.step_offset, 1);
2934 int x_steps = width / tile_size;
2935 int y_steps = height / tile_size;
2936 int sx_offset = border_size;
2937 int sy_offset = border_size;
2941 if (request.centered)
2942 sx_offset = (request.width - text_width) / 2;
2944 if (request.wrap_single_words && !request.autowrap)
2946 char *src_text_ptr, *dst_text_ptr;
2948 text_door_style = checked_malloc(2 * strlen(text) + 1);
2950 src_text_ptr = text;
2951 dst_text_ptr = text_door_style;
2953 while (*src_text_ptr)
2955 if (*src_text_ptr == ' ' ||
2956 *src_text_ptr == '?' ||
2957 *src_text_ptr == '!')
2958 *dst_text_ptr++ = '\n';
2960 if (*src_text_ptr != ' ')
2961 *dst_text_ptr++ = *src_text_ptr;
2966 *dst_text_ptr = '\0';
2968 text_final = text_door_style;
2971 setRequestPosition(&sx, &sy, FALSE);
2973 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2975 for (y = 0; y < y_steps; y++)
2976 for (x = 0; x < x_steps; x++)
2977 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2978 x, y, x_steps, y_steps,
2979 tile_size, tile_size);
2981 // force DOOR font inside door area
2982 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2984 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2985 line_length, -1, max_lines, line_spacing, mask_mode,
2986 request.autowrap, request.centered, FALSE);
2990 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2991 RedrawGadget(tool_gadget[i]);
2993 // store readily prepared envelope request for later use when animating
2994 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2996 if (text_door_style)
2997 free(text_door_style);
3000 static void AnimateEnvelopeRequest(int anim_mode, int action)
3002 int graphic = IMG_BACKGROUND_REQUEST;
3003 boolean draw_masked = graphic_info[graphic].draw_masked;
3004 int delay_value_normal = request.step_delay;
3005 int delay_value_fast = delay_value_normal / 2;
3006 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3007 boolean no_delay = (tape.warp_forward);
3008 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3009 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3010 unsigned int anim_delay = 0;
3012 int tile_size = MAX(request.step_offset, 1);
3013 int max_xsize = request.width / tile_size;
3014 int max_ysize = request.height / tile_size;
3015 int max_xsize_inner = max_xsize - 2;
3016 int max_ysize_inner = max_ysize - 2;
3018 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3019 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3020 int xend = max_xsize_inner;
3021 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3022 int xstep = (xstart < xend ? 1 : 0);
3023 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3025 int end = MAX(xend - xstart, yend - ystart);
3028 if (setup.quick_doors)
3035 for (i = start; i <= end; i++)
3037 int last_frame = end; // last frame of this "for" loop
3038 int x = xstart + i * xstep;
3039 int y = ystart + i * ystep;
3040 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3041 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3042 int xsize_size_left = (xsize - 1) * tile_size;
3043 int ysize_size_top = (ysize - 1) * tile_size;
3044 int max_xsize_pos = (max_xsize - 1) * tile_size;
3045 int max_ysize_pos = (max_ysize - 1) * tile_size;
3046 int width = xsize * tile_size;
3047 int height = ysize * tile_size;
3052 setRequestPosition(&src_x, &src_y, FALSE);
3053 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3055 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3057 for (yy = 0; yy < 2; yy++)
3059 for (xx = 0; xx < 2; xx++)
3061 int src_xx = src_x + xx * max_xsize_pos;
3062 int src_yy = src_y + yy * max_ysize_pos;
3063 int dst_xx = dst_x + xx * xsize_size_left;
3064 int dst_yy = dst_y + yy * ysize_size_top;
3065 int xx_size = (xx ? tile_size : xsize_size_left);
3066 int yy_size = (yy ? tile_size : ysize_size_top);
3069 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3070 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3072 BlitBitmap(bitmap_db_store_2, backbuffer,
3073 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3077 redraw_mask |= REDRAW_FIELD;
3081 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3084 ClearAutoRepeatKeyEvents();
3087 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3089 int graphic = IMG_BACKGROUND_REQUEST;
3090 int sound_opening = SND_REQUEST_OPENING;
3091 int sound_closing = SND_REQUEST_CLOSING;
3092 int anim_mode_1 = request.anim_mode; // (higher priority)
3093 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3094 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3095 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3096 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3098 if (game_status == GAME_MODE_PLAYING)
3099 BlitScreenToBitmap(backbuffer);
3101 SetDrawtoField(DRAW_TO_BACKBUFFER);
3103 // SetDrawBackgroundMask(REDRAW_NONE);
3105 if (action == ACTION_OPENING)
3107 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3109 if (req_state & REQ_ASK)
3111 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3112 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3113 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3114 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3116 else if (req_state & REQ_CONFIRM)
3118 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3119 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3121 else if (req_state & REQ_PLAYER)
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3124 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3125 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3126 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3129 DrawEnvelopeRequest(text);
3132 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3134 if (action == ACTION_OPENING)
3136 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3138 if (anim_mode == ANIM_DEFAULT)
3139 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3141 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3145 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3147 if (anim_mode != ANIM_NONE)
3148 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3150 if (anim_mode == ANIM_DEFAULT)
3151 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3154 game.envelope_active = FALSE;
3156 if (action == ACTION_CLOSING)
3157 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3159 // SetDrawBackgroundMask(last_draw_background_mask);
3161 redraw_mask |= REDRAW_FIELD;
3165 if (action == ACTION_CLOSING &&
3166 game_status == GAME_MODE_PLAYING &&
3167 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3168 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3171 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3173 if (IS_MM_WALL(element))
3175 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3181 int graphic = el2preimg(element);
3183 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3184 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3189 void DrawLevel(int draw_background_mask)
3193 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3194 SetDrawBackgroundMask(draw_background_mask);
3198 for (x = BX1; x <= BX2; x++)
3199 for (y = BY1; y <= BY2; y++)
3200 DrawScreenField(x, y);
3202 redraw_mask |= REDRAW_FIELD;
3205 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3210 for (x = 0; x < size_x; x++)
3211 for (y = 0; y < size_y; y++)
3212 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3214 redraw_mask |= REDRAW_FIELD;
3217 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3221 for (x = 0; x < size_x; x++)
3222 for (y = 0; y < size_y; y++)
3223 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3225 redraw_mask |= REDRAW_FIELD;
3228 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3230 boolean show_level_border = (BorderElement != EL_EMPTY);
3231 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3232 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3233 int tile_size = preview.tile_size;
3234 int preview_width = preview.xsize * tile_size;
3235 int preview_height = preview.ysize * tile_size;
3236 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3237 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3238 int real_preview_width = real_preview_xsize * tile_size;
3239 int real_preview_height = real_preview_ysize * tile_size;
3240 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3241 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3244 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3247 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3249 dst_x += (preview_width - real_preview_width) / 2;
3250 dst_y += (preview_height - real_preview_height) / 2;
3252 for (x = 0; x < real_preview_xsize; x++)
3254 for (y = 0; y < real_preview_ysize; y++)
3256 int lx = from_x + x + (show_level_border ? -1 : 0);
3257 int ly = from_y + y + (show_level_border ? -1 : 0);
3258 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3259 getBorderElement(lx, ly));
3261 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3262 element, tile_size);
3266 redraw_mask |= REDRAW_FIELD;
3269 #define MICROLABEL_EMPTY 0
3270 #define MICROLABEL_LEVEL_NAME 1
3271 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3272 #define MICROLABEL_LEVEL_AUTHOR 3
3273 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3274 #define MICROLABEL_IMPORTED_FROM 5
3275 #define MICROLABEL_IMPORTED_BY_HEAD 6
3276 #define MICROLABEL_IMPORTED_BY 7
3278 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3280 int max_text_width = SXSIZE;
3281 int font_width = getFontWidth(font_nr);
3283 if (pos->align == ALIGN_CENTER)
3284 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3285 else if (pos->align == ALIGN_RIGHT)
3286 max_text_width = pos->x;
3288 max_text_width = SXSIZE - pos->x;
3290 return max_text_width / font_width;
3293 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3295 char label_text[MAX_OUTPUT_LINESIZE + 1];
3296 int max_len_label_text;
3297 int font_nr = pos->font;
3300 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3303 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3304 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3305 mode == MICROLABEL_IMPORTED_BY_HEAD)
3306 font_nr = pos->font_alt;
3308 max_len_label_text = getMaxTextLength(pos, font_nr);
3310 if (pos->size != -1)
3311 max_len_label_text = pos->size;
3313 for (i = 0; i < max_len_label_text; i++)
3314 label_text[i] = ' ';
3315 label_text[max_len_label_text] = '\0';
3317 if (strlen(label_text) > 0)
3318 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3321 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3322 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3323 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3324 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3325 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3326 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3327 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3328 max_len_label_text);
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);
3334 redraw_mask |= REDRAW_FIELD;
3337 static void DrawPreviewLevelLabel(int mode)
3339 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3342 static void DrawPreviewLevelInfo(int mode)
3344 if (mode == MICROLABEL_LEVEL_NAME)
3345 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3346 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3347 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3350 static void DrawPreviewLevelExt(boolean restart)
3352 static unsigned int scroll_delay = 0;
3353 static unsigned int label_delay = 0;
3354 static int from_x, from_y, scroll_direction;
3355 static int label_state, label_counter;
3356 unsigned int scroll_delay_value = preview.step_delay;
3357 boolean show_level_border = (BorderElement != EL_EMPTY);
3358 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3359 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3366 if (preview.anim_mode == ANIM_CENTERED)
3368 if (level_xsize > preview.xsize)
3369 from_x = (level_xsize - preview.xsize) / 2;
3370 if (level_ysize > preview.ysize)
3371 from_y = (level_ysize - preview.ysize) / 2;
3374 from_x += preview.xoffset;
3375 from_y += preview.yoffset;
3377 scroll_direction = MV_RIGHT;
3381 DrawPreviewLevelPlayfield(from_x, from_y);
3382 DrawPreviewLevelLabel(label_state);
3384 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3385 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3387 // initialize delay counters
3388 DelayReached(&scroll_delay, 0);
3389 DelayReached(&label_delay, 0);
3391 if (leveldir_current->name)
3393 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3394 char label_text[MAX_OUTPUT_LINESIZE + 1];
3395 int font_nr = pos->font;
3396 int max_len_label_text = getMaxTextLength(pos, font_nr);
3398 if (pos->size != -1)
3399 max_len_label_text = pos->size;
3401 strncpy(label_text, leveldir_current->name, max_len_label_text);
3402 label_text[max_len_label_text] = '\0';
3404 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3405 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3411 // scroll preview level, if needed
3412 if (preview.anim_mode != ANIM_NONE &&
3413 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3414 DelayReached(&scroll_delay, scroll_delay_value))
3416 switch (scroll_direction)
3421 from_x -= preview.step_offset;
3422 from_x = (from_x < 0 ? 0 : from_x);
3425 scroll_direction = MV_UP;
3429 if (from_x < level_xsize - preview.xsize)
3431 from_x += preview.step_offset;
3432 from_x = (from_x > level_xsize - preview.xsize ?
3433 level_xsize - preview.xsize : from_x);
3436 scroll_direction = MV_DOWN;
3442 from_y -= preview.step_offset;
3443 from_y = (from_y < 0 ? 0 : from_y);
3446 scroll_direction = MV_RIGHT;
3450 if (from_y < level_ysize - preview.ysize)
3452 from_y += preview.step_offset;
3453 from_y = (from_y > level_ysize - preview.ysize ?
3454 level_ysize - preview.ysize : from_y);
3457 scroll_direction = MV_LEFT;
3464 DrawPreviewLevelPlayfield(from_x, from_y);
3467 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3468 // redraw micro level label, if needed
3469 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3470 !strEqual(level.author, ANONYMOUS_NAME) &&
3471 !strEqual(level.author, leveldir_current->name) &&
3472 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3474 int max_label_counter = 23;
3476 if (leveldir_current->imported_from != NULL &&
3477 strlen(leveldir_current->imported_from) > 0)
3478 max_label_counter += 14;
3479 if (leveldir_current->imported_by != NULL &&
3480 strlen(leveldir_current->imported_by) > 0)
3481 max_label_counter += 14;
3483 label_counter = (label_counter + 1) % max_label_counter;
3484 label_state = (label_counter >= 0 && label_counter <= 7 ?
3485 MICROLABEL_LEVEL_NAME :
3486 label_counter >= 9 && label_counter <= 12 ?
3487 MICROLABEL_LEVEL_AUTHOR_HEAD :
3488 label_counter >= 14 && label_counter <= 21 ?
3489 MICROLABEL_LEVEL_AUTHOR :
3490 label_counter >= 23 && label_counter <= 26 ?
3491 MICROLABEL_IMPORTED_FROM_HEAD :
3492 label_counter >= 28 && label_counter <= 35 ?
3493 MICROLABEL_IMPORTED_FROM :
3494 label_counter >= 37 && label_counter <= 40 ?
3495 MICROLABEL_IMPORTED_BY_HEAD :
3496 label_counter >= 42 && label_counter <= 49 ?
3497 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3499 if (leveldir_current->imported_from == NULL &&
3500 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3501 label_state == MICROLABEL_IMPORTED_FROM))
3502 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3503 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3505 DrawPreviewLevelLabel(label_state);
3509 void DrawPreviewPlayers(void)
3511 if (game_status != GAME_MODE_MAIN)
3514 // do not draw preview players if level preview redefined, but players aren't
3515 if (preview.redefined && !menu.main.preview_players.redefined)
3518 boolean player_found[MAX_PLAYERS];
3519 int num_players = 0;
3522 for (i = 0; i < MAX_PLAYERS; i++)
3523 player_found[i] = FALSE;
3525 // check which players can be found in the level (simple approach)
3526 for (x = 0; x < lev_fieldx; x++)
3528 for (y = 0; y < lev_fieldy; y++)
3530 int element = level.field[x][y];
3532 if (ELEM_IS_PLAYER(element))
3534 int player_nr = GET_PLAYER_NR(element);
3536 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3538 if (!player_found[player_nr])
3541 player_found[player_nr] = TRUE;
3546 struct TextPosInfo *pos = &menu.main.preview_players;
3547 int tile_size = pos->tile_size;
3548 int border_size = pos->border_size;
3549 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3550 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3551 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3552 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3553 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3554 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3555 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3556 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3557 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3558 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3559 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3560 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3562 // clear area in which the players will be drawn
3563 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3564 max_players_width, max_players_height);
3566 if (!network.enabled && !setup.team_mode)
3569 // only draw players if level is suited for team mode
3570 if (num_players < 2)
3573 // draw all players that were found in the level
3574 for (i = 0; i < MAX_PLAYERS; i++)
3576 if (player_found[i])
3578 int graphic = el2img(EL_PLAYER_1 + i);
3580 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3582 xpos += player_xoffset;
3583 ypos += player_yoffset;
3588 void DrawPreviewLevelInitial(void)
3590 DrawPreviewLevelExt(TRUE);
3591 DrawPreviewPlayers();
3594 void DrawPreviewLevelAnimation(void)
3596 DrawPreviewLevelExt(FALSE);
3599 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3600 int border_size, int font_nr)
3602 int graphic = el2img(EL_PLAYER_1 + player_nr);
3603 int font_height = getFontHeight(font_nr);
3604 int player_height = MAX(tile_size, font_height);
3605 int xoffset_text = tile_size + border_size;
3606 int yoffset_text = (player_height - font_height) / 2;
3607 int yoffset_graphic = (player_height - tile_size) / 2;
3608 char *player_name = getNetworkPlayerName(player_nr + 1);
3610 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3612 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3615 static void DrawNetworkPlayersExt(boolean force)
3617 if (game_status != GAME_MODE_MAIN)
3620 if (!network.connected && !force)
3623 // do not draw network players if level preview redefined, but players aren't
3624 if (preview.redefined && !menu.main.network_players.redefined)
3627 int num_players = 0;
3630 for (i = 0; i < MAX_PLAYERS; i++)
3631 if (stored_player[i].connected_network)
3634 struct TextPosInfo *pos = &menu.main.network_players;
3635 int tile_size = pos->tile_size;
3636 int border_size = pos->border_size;
3637 int xoffset_text = tile_size + border_size;
3638 int font_nr = pos->font;
3639 int font_width = getFontWidth(font_nr);
3640 int font_height = getFontHeight(font_nr);
3641 int player_height = MAX(tile_size, font_height);
3642 int player_yoffset = player_height + border_size;
3643 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3644 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3645 int all_players_height = num_players * player_yoffset - border_size;
3646 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3647 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3648 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3650 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3651 max_players_width, max_players_height);
3653 // first draw local network player ...
3654 for (i = 0; i < MAX_PLAYERS; i++)
3656 if (stored_player[i].connected_network &&
3657 stored_player[i].connected_locally)
3659 char *player_name = getNetworkPlayerName(i + 1);
3660 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3661 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3663 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3665 ypos += player_yoffset;
3669 // ... then draw all other network players
3670 for (i = 0; i < MAX_PLAYERS; i++)
3672 if (stored_player[i].connected_network &&
3673 !stored_player[i].connected_locally)
3675 char *player_name = getNetworkPlayerName(i + 1);
3676 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3677 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3679 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3681 ypos += player_yoffset;
3686 void DrawNetworkPlayers(void)
3688 DrawNetworkPlayersExt(FALSE);
3691 void ClearNetworkPlayers(void)
3693 DrawNetworkPlayersExt(TRUE);
3696 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3697 int graphic, int sync_frame,
3700 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3702 if (mask_mode == USE_MASKING)
3703 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3705 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3708 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3709 int graphic, int sync_frame, int mask_mode)
3711 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3713 if (mask_mode == USE_MASKING)
3714 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3716 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3719 static void DrawGraphicAnimation(int x, int y, int graphic)
3721 int lx = LEVELX(x), ly = LEVELY(y);
3723 if (!IN_SCR_FIELD(x, y))
3726 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3727 graphic, GfxFrame[lx][ly], NO_MASKING);
3729 MarkTileDirty(x, y);
3732 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3734 int lx = LEVELX(x), ly = LEVELY(y);
3736 if (!IN_SCR_FIELD(x, y))
3739 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3740 graphic, GfxFrame[lx][ly], NO_MASKING);
3741 MarkTileDirty(x, y);
3744 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3746 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3749 void DrawLevelElementAnimation(int x, int y, int element)
3751 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3753 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3756 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3758 int sx = SCREENX(x), sy = SCREENY(y);
3760 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3763 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3766 DrawGraphicAnimation(sx, sy, graphic);
3769 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3770 DrawLevelFieldCrumbled(x, y);
3772 if (GFX_CRUMBLED(Feld[x][y]))
3773 DrawLevelFieldCrumbled(x, y);
3777 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3779 int sx = SCREENX(x), sy = SCREENY(y);
3782 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3785 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3787 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3790 DrawGraphicAnimation(sx, sy, graphic);
3792 if (GFX_CRUMBLED(element))
3793 DrawLevelFieldCrumbled(x, y);
3796 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3798 if (player->use_murphy)
3800 // this works only because currently only one player can be "murphy" ...
3801 static int last_horizontal_dir = MV_LEFT;
3802 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3804 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3805 last_horizontal_dir = move_dir;
3807 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3809 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3811 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3817 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3820 static boolean equalGraphics(int graphic1, int graphic2)
3822 struct GraphicInfo *g1 = &graphic_info[graphic1];
3823 struct GraphicInfo *g2 = &graphic_info[graphic2];
3825 return (g1->bitmap == g2->bitmap &&
3826 g1->src_x == g2->src_x &&
3827 g1->src_y == g2->src_y &&
3828 g1->anim_frames == g2->anim_frames &&
3829 g1->anim_delay == g2->anim_delay &&
3830 g1->anim_mode == g2->anim_mode);
3833 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3837 DRAW_PLAYER_STAGE_INIT = 0,
3838 DRAW_PLAYER_STAGE_LAST_FIELD,
3839 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3840 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3841 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3842 DRAW_PLAYER_STAGE_PLAYER,
3844 DRAW_PLAYER_STAGE_PLAYER,
3845 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3847 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3848 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3850 NUM_DRAW_PLAYER_STAGES
3853 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3855 static int static_last_player_graphic[MAX_PLAYERS];
3856 static int static_last_player_frame[MAX_PLAYERS];
3857 static boolean static_player_is_opaque[MAX_PLAYERS];
3858 static boolean draw_player[MAX_PLAYERS];
3859 int pnr = player->index_nr;
3861 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3863 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3864 static_last_player_frame[pnr] = player->Frame;
3865 static_player_is_opaque[pnr] = FALSE;
3867 draw_player[pnr] = TRUE;
3870 if (!draw_player[pnr])
3874 if (!IN_LEV_FIELD(player->jx, player->jy))
3876 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3877 printf("DrawPlayerField(): This should never happen!\n");
3879 draw_player[pnr] = FALSE;
3885 int last_player_graphic = static_last_player_graphic[pnr];
3886 int last_player_frame = static_last_player_frame[pnr];
3887 boolean player_is_opaque = static_player_is_opaque[pnr];
3889 int jx = player->jx;
3890 int jy = player->jy;
3891 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3892 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3893 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3894 int last_jx = (player->is_moving ? jx - dx : jx);
3895 int last_jy = (player->is_moving ? jy - dy : jy);
3896 int next_jx = jx + dx;
3897 int next_jy = jy + dy;
3898 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3899 int sx = SCREENX(jx);
3900 int sy = SCREENY(jy);
3901 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3902 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3903 int element = Feld[jx][jy];
3904 int last_element = Feld[last_jx][last_jy];
3905 int action = (player->is_pushing ? ACTION_PUSHING :
3906 player->is_digging ? ACTION_DIGGING :
3907 player->is_collecting ? ACTION_COLLECTING :
3908 player->is_moving ? ACTION_MOVING :
3909 player->is_snapping ? ACTION_SNAPPING :
3910 player->is_dropping ? ACTION_DROPPING :
3911 player->is_waiting ? player->action_waiting :
3914 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3916 // ------------------------------------------------------------------------
3917 // initialize drawing the player
3918 // ------------------------------------------------------------------------
3920 draw_player[pnr] = FALSE;
3922 // GfxElement[][] is set to the element the player is digging or collecting;
3923 // remove also for off-screen player if the player is not moving anymore
3924 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3925 GfxElement[jx][jy] = EL_UNDEFINED;
3927 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3930 if (element == EL_EXPLOSION)
3933 InitPlayerGfxAnimation(player, action, move_dir);
3935 draw_player[pnr] = TRUE;
3937 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3939 // ------------------------------------------------------------------------
3940 // draw things in the field the player is leaving, if needed
3941 // ------------------------------------------------------------------------
3943 if (!IN_SCR_FIELD(sx, sy))
3944 draw_player[pnr] = FALSE;
3946 if (!player->is_moving)
3949 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3951 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3953 if (last_element == EL_DYNAMITE_ACTIVE ||
3954 last_element == EL_EM_DYNAMITE_ACTIVE ||
3955 last_element == EL_SP_DISK_RED_ACTIVE)
3956 DrawDynamite(last_jx, last_jy);
3958 DrawLevelFieldThruMask(last_jx, last_jy);
3960 else if (last_element == EL_DYNAMITE_ACTIVE ||
3961 last_element == EL_EM_DYNAMITE_ACTIVE ||
3962 last_element == EL_SP_DISK_RED_ACTIVE)
3963 DrawDynamite(last_jx, last_jy);
3965 DrawLevelField(last_jx, last_jy);
3967 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3968 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3970 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3972 // ------------------------------------------------------------------------
3973 // draw things behind the player, if needed
3974 // ------------------------------------------------------------------------
3978 DrawLevelElement(jx, jy, Back[jx][jy]);
3983 if (IS_ACTIVE_BOMB(element))
3985 DrawLevelElement(jx, jy, EL_EMPTY);
3990 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3992 int old_element = GfxElement[jx][jy];
3993 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3994 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3996 if (GFX_CRUMBLED(old_element))
3997 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3999 DrawGraphic(sx, sy, old_graphic, frame);
4001 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4002 static_player_is_opaque[pnr] = TRUE;
4006 GfxElement[jx][jy] = EL_UNDEFINED;
4008 // make sure that pushed elements are drawn with correct frame rate
4009 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4011 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4012 GfxFrame[jx][jy] = player->StepFrame;
4014 DrawLevelField(jx, jy);
4017 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4019 // ------------------------------------------------------------------------
4020 // draw things the player is pushing, if needed
4021 // ------------------------------------------------------------------------
4023 if (!player->is_pushing || !player->is_moving)
4026 int gfx_frame = GfxFrame[jx][jy];
4028 if (!IS_MOVING(jx, jy)) // push movement already finished
4030 element = Feld[next_jx][next_jy];
4031 gfx_frame = GfxFrame[next_jx][next_jy];
4034 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4035 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4036 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4038 // draw background element under pushed element (like the Sokoban field)
4039 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4041 // this allows transparent pushing animation over non-black background
4044 DrawLevelElement(jx, jy, Back[jx][jy]);
4046 DrawLevelElement(jx, jy, EL_EMPTY);
4048 if (Back[next_jx][next_jy])
4049 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4051 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4053 else if (Back[next_jx][next_jy])
4054 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4056 int px = SCREENX(jx), py = SCREENY(jy);
4057 int pxx = (TILEX - ABS(sxx)) * dx;
4058 int pyy = (TILEY - ABS(syy)) * dy;
4061 // do not draw (EM style) pushing animation when pushing is finished
4062 // (two-tile animations usually do not contain start and end frame)
4063 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4064 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4066 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4068 // masked drawing is needed for EMC style (double) movement graphics
4069 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4070 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4073 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4075 // ------------------------------------------------------------------------
4076 // draw player himself
4077 // ------------------------------------------------------------------------
4079 int graphic = getPlayerGraphic(player, move_dir);
4081 // in the case of changed player action or direction, prevent the current
4082 // animation frame from being restarted for identical animations
4083 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4084 player->Frame = last_player_frame;
4086 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4088 if (player_is_opaque)
4089 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4091 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4093 if (SHIELD_ON(player))
4095 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4096 IMG_SHIELD_NORMAL_ACTIVE);
4097 frame = getGraphicAnimationFrame(graphic, -1);
4099 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4102 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4104 // ------------------------------------------------------------------------
4105 // draw things in front of player (active dynamite or dynabombs)
4106 // ------------------------------------------------------------------------
4108 if (IS_ACTIVE_BOMB(element))
4110 int graphic = el2img(element);
4111 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4113 if (game.emulation == EMU_SUPAPLEX)
4114 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4116 DrawGraphicThruMask(sx, sy, graphic, frame);
4119 if (player_is_moving && last_element == EL_EXPLOSION)
4121 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4122 GfxElement[last_jx][last_jy] : EL_EMPTY);
4123 int graphic = el_act2img(element, ACTION_EXPLODING);
4124 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4125 int phase = ExplodePhase[last_jx][last_jy] - 1;
4126 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4129 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4132 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4134 // ------------------------------------------------------------------------
4135 // draw elements the player is just walking/passing through/under
4136 // ------------------------------------------------------------------------
4138 if (player_is_moving)
4140 // handle the field the player is leaving ...
4141 if (IS_ACCESSIBLE_INSIDE(last_element))
4142 DrawLevelField(last_jx, last_jy);
4143 else if (IS_ACCESSIBLE_UNDER(last_element))
4144 DrawLevelFieldThruMask(last_jx, last_jy);
4147 // do not redraw accessible elements if the player is just pushing them
4148 if (!player_is_moving || !player->is_pushing)
4150 // ... and the field the player is entering
4151 if (IS_ACCESSIBLE_INSIDE(element))
4152 DrawLevelField(jx, jy);
4153 else if (IS_ACCESSIBLE_UNDER(element))
4154 DrawLevelFieldThruMask(jx, jy);
4157 MarkTileDirty(sx, sy);
4161 void DrawPlayer(struct PlayerInfo *player)
4165 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4166 DrawPlayerExt(player, i);
4169 void DrawAllPlayers(void)
4173 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4174 for (j = 0; j < MAX_PLAYERS; j++)
4175 if (stored_player[j].active)
4176 DrawPlayerExt(&stored_player[j], i);
4179 void DrawPlayerField(int x, int y)
4181 if (!IS_PLAYER(x, y))
4184 DrawPlayer(PLAYERINFO(x, y));
4187 // ----------------------------------------------------------------------------
4189 void WaitForEventToContinue(void)
4191 boolean still_wait = TRUE;
4193 if (program.headless)
4196 // simulate releasing mouse button over last gadget, if still pressed
4198 HandleGadgets(-1, -1, 0);
4200 button_status = MB_RELEASED;
4208 if (NextValidEvent(&event))
4212 case EVENT_BUTTONRELEASE:
4213 case EVENT_KEYPRESS:
4214 case SDL_CONTROLLERBUTTONDOWN:
4215 case SDL_JOYBUTTONDOWN:
4219 case EVENT_KEYRELEASE:
4220 ClearPlayerAction();
4224 HandleOtherEvents(&event);
4228 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4237 #define MAX_REQUEST_LINES 13
4238 #define MAX_REQUEST_LINE_FONT1_LEN 7
4239 #define MAX_REQUEST_LINE_FONT2_LEN 10
4241 static int RequestHandleEvents(unsigned int req_state)
4243 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4245 int width = request.width;
4246 int height = request.height;
4250 // when showing request dialog after game ended, deactivate game panel
4251 if (game_just_ended)
4252 game.panel.active = FALSE;
4254 game.request_active = TRUE;
4256 setRequestPosition(&sx, &sy, FALSE);
4258 button_status = MB_RELEASED;
4260 request_gadget_id = -1;
4265 if (game_just_ended)
4267 // the MM game engine does not use a special (scrollable) field buffer
4268 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4269 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4271 HandleGameActions();
4273 SetDrawtoField(DRAW_TO_BACKBUFFER);
4275 if (global.use_envelope_request)
4277 // copy current state of request area to middle of playfield area
4278 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4286 while (NextValidEvent(&event))
4290 case EVENT_BUTTONPRESS:
4291 case EVENT_BUTTONRELEASE:
4292 case EVENT_MOTIONNOTIFY:
4296 if (event.type == EVENT_MOTIONNOTIFY)
4301 motion_status = TRUE;
4302 mx = ((MotionEvent *) &event)->x;
4303 my = ((MotionEvent *) &event)->y;
4307 motion_status = FALSE;
4308 mx = ((ButtonEvent *) &event)->x;
4309 my = ((ButtonEvent *) &event)->y;
4310 if (event.type == EVENT_BUTTONPRESS)
4311 button_status = ((ButtonEvent *) &event)->button;
4313 button_status = MB_RELEASED;
4316 // this sets 'request_gadget_id'
4317 HandleGadgets(mx, my, button_status);
4319 switch (request_gadget_id)
4321 case TOOL_CTRL_ID_YES:
4322 case TOOL_CTRL_ID_TOUCH_YES:
4325 case TOOL_CTRL_ID_NO:
4326 case TOOL_CTRL_ID_TOUCH_NO:
4329 case TOOL_CTRL_ID_CONFIRM:
4330 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4331 result = TRUE | FALSE;
4334 case TOOL_CTRL_ID_PLAYER_1:
4337 case TOOL_CTRL_ID_PLAYER_2:
4340 case TOOL_CTRL_ID_PLAYER_3:
4343 case TOOL_CTRL_ID_PLAYER_4:
4348 // only check clickable animations if no request gadget clicked
4349 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4356 case SDL_WINDOWEVENT:
4357 HandleWindowEvent((WindowEvent *) &event);
4360 case SDL_APP_WILLENTERBACKGROUND:
4361 case SDL_APP_DIDENTERBACKGROUND:
4362 case SDL_APP_WILLENTERFOREGROUND:
4363 case SDL_APP_DIDENTERFOREGROUND:
4364 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4367 case EVENT_KEYPRESS:
4369 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4374 if (req_state & REQ_CONFIRM)
4383 #if defined(KSYM_Rewind)
4384 case KSYM_Rewind: // for Amazon Fire TV remote
4393 #if defined(KSYM_FastForward)
4394 case KSYM_FastForward: // for Amazon Fire TV remote
4400 HandleKeysDebug(key, KEY_PRESSED);
4404 if (req_state & REQ_PLAYER)
4406 int old_player_nr = setup.network_player_nr;
4409 result = old_player_nr + 1;
4414 result = old_player_nr + 1;
4445 case EVENT_KEYRELEASE:
4446 ClearPlayerAction();
4449 case SDL_CONTROLLERBUTTONDOWN:
4450 switch (event.cbutton.button)
4452 case SDL_CONTROLLER_BUTTON_A:
4453 case SDL_CONTROLLER_BUTTON_X:
4454 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4455 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4459 case SDL_CONTROLLER_BUTTON_B:
4460 case SDL_CONTROLLER_BUTTON_Y:
4461 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4462 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4463 case SDL_CONTROLLER_BUTTON_BACK:
4468 if (req_state & REQ_PLAYER)
4470 int old_player_nr = setup.network_player_nr;
4473 result = old_player_nr + 1;
4475 switch (event.cbutton.button)
4477 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4478 case SDL_CONTROLLER_BUTTON_Y:
4482 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4483 case SDL_CONTROLLER_BUTTON_B:
4487 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4488 case SDL_CONTROLLER_BUTTON_A:
4492 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4493 case SDL_CONTROLLER_BUTTON_X:
4504 case SDL_CONTROLLERBUTTONUP:
4505 HandleJoystickEvent(&event);
4506 ClearPlayerAction();
4510 HandleOtherEvents(&event);
4515 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4517 int joy = AnyJoystick();
4519 if (joy & JOY_BUTTON_1)
4521 else if (joy & JOY_BUTTON_2)
4524 else if (AnyJoystick())
4526 int joy = AnyJoystick();
4528 if (req_state & REQ_PLAYER)
4532 else if (joy & JOY_RIGHT)
4534 else if (joy & JOY_DOWN)
4536 else if (joy & JOY_LEFT)
4541 if (game_just_ended)
4543 if (global.use_envelope_request)
4545 // copy back current state of pressed buttons inside request area
4546 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4553 game.request_active = FALSE;
4558 static boolean RequestDoor(char *text, unsigned int req_state)
4560 unsigned int old_door_state;
4561 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4562 int font_nr = FONT_TEXT_2;
4567 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4569 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4570 font_nr = FONT_TEXT_1;
4573 if (game_status == GAME_MODE_PLAYING)
4574 BlitScreenToBitmap(backbuffer);
4576 // disable deactivated drawing when quick-loading level tape recording
4577 if (tape.playing && tape.deactivate_display)
4578 TapeDeactivateDisplayOff(TRUE);
4580 SetMouseCursor(CURSOR_DEFAULT);
4582 // pause network game while waiting for request to answer
4583 if (network.enabled &&
4584 game_status == GAME_MODE_PLAYING &&
4585 !game.all_players_gone &&
4586 req_state & REQUEST_WAIT_FOR_INPUT)
4587 SendToServer_PausePlaying();
4589 old_door_state = GetDoorState();
4591 // simulate releasing mouse button over last gadget, if still pressed
4593 HandleGadgets(-1, -1, 0);
4597 // draw released gadget before proceeding
4600 if (old_door_state & DOOR_OPEN_1)
4602 CloseDoor(DOOR_CLOSE_1);
4604 // save old door content
4605 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4606 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4609 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4610 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4612 // clear door drawing field
4613 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4615 // force DOOR font inside door area
4616 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4618 // write text for request
4619 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4621 char text_line[max_request_line_len + 1];
4627 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4629 tc = *(text_ptr + tx);
4630 // if (!tc || tc == ' ')
4631 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4635 if ((tc == '?' || tc == '!') && tl == 0)
4645 strncpy(text_line, text_ptr, tl);
4648 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4649 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4650 text_line, font_nr);
4652 text_ptr += tl + (tc == ' ' ? 1 : 0);
4653 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4658 if (req_state & REQ_ASK)
4660 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4661 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4662 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4663 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4665 else if (req_state & REQ_CONFIRM)
4667 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4668 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4670 else if (req_state & REQ_PLAYER)
4672 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4673 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4674 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4675 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4678 // copy request gadgets to door backbuffer
4679 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4681 OpenDoor(DOOR_OPEN_1);
4683 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4685 if (game_status == GAME_MODE_PLAYING)
4687 SetPanelBackground();
4688 SetDrawBackgroundMask(REDRAW_DOOR_1);
4692 SetDrawBackgroundMask(REDRAW_FIELD);
4698 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4700 // ---------- handle request buttons ----------
4701 result = RequestHandleEvents(req_state);
4705 if (!(req_state & REQ_STAY_OPEN))
4707 CloseDoor(DOOR_CLOSE_1);
4709 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4710 (req_state & REQ_REOPEN))
4711 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4716 if (game_status == GAME_MODE_PLAYING)
4718 SetPanelBackground();
4719 SetDrawBackgroundMask(REDRAW_DOOR_1);
4723 SetDrawBackgroundMask(REDRAW_FIELD);
4726 // continue network game after request
4727 if (network.enabled &&
4728 game_status == GAME_MODE_PLAYING &&
4729 !game.all_players_gone &&
4730 req_state & REQUEST_WAIT_FOR_INPUT)
4731 SendToServer_ContinuePlaying();
4733 // restore deactivated drawing when quick-loading level tape recording
4734 if (tape.playing && tape.deactivate_display)
4735 TapeDeactivateDisplayOn();
4740 static boolean RequestEnvelope(char *text, unsigned int req_state)
4744 if (game_status == GAME_MODE_PLAYING)
4745 BlitScreenToBitmap(backbuffer);
4747 // disable deactivated drawing when quick-loading level tape recording
4748 if (tape.playing && tape.deactivate_display)
4749 TapeDeactivateDisplayOff(TRUE);
4751 SetMouseCursor(CURSOR_DEFAULT);
4753 // pause network game while waiting for request to answer
4754 if (network.enabled &&
4755 game_status == GAME_MODE_PLAYING &&
4756 !game.all_players_gone &&
4757 req_state & REQUEST_WAIT_FOR_INPUT)
4758 SendToServer_PausePlaying();
4760 // simulate releasing mouse button over last gadget, if still pressed
4762 HandleGadgets(-1, -1, 0);
4766 // (replace with setting corresponding request background)
4767 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4768 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4770 // clear door drawing field
4771 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4773 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4775 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4777 if (game_status == GAME_MODE_PLAYING)
4779 SetPanelBackground();
4780 SetDrawBackgroundMask(REDRAW_DOOR_1);
4784 SetDrawBackgroundMask(REDRAW_FIELD);
4790 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4792 // ---------- handle request buttons ----------
4793 result = RequestHandleEvents(req_state);
4797 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4801 if (game_status == GAME_MODE_PLAYING)
4803 SetPanelBackground();
4804 SetDrawBackgroundMask(REDRAW_DOOR_1);
4808 SetDrawBackgroundMask(REDRAW_FIELD);
4811 // continue network game after request
4812 if (network.enabled &&
4813 game_status == GAME_MODE_PLAYING &&
4814 !game.all_players_gone &&
4815 req_state & REQUEST_WAIT_FOR_INPUT)
4816 SendToServer_ContinuePlaying();
4818 // restore deactivated drawing when quick-loading level tape recording
4819 if (tape.playing && tape.deactivate_display)
4820 TapeDeactivateDisplayOn();
4825 boolean Request(char *text, unsigned int req_state)
4827 boolean overlay_enabled = GetOverlayEnabled();
4830 SetOverlayEnabled(FALSE);
4832 if (global.use_envelope_request)
4833 result = RequestEnvelope(text, req_state);
4835 result = RequestDoor(text, req_state);
4837 SetOverlayEnabled(overlay_enabled);
4842 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4844 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4845 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4848 if (dpo1->sort_priority != dpo2->sort_priority)
4849 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4851 compare_result = dpo1->nr - dpo2->nr;
4853 return compare_result;
4856 void InitGraphicCompatibilityInfo_Doors(void)
4862 struct DoorInfo *door;
4866 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4867 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4869 { -1, -1, -1, NULL }
4871 struct Rect door_rect_list[] =
4873 { DX, DY, DXSIZE, DYSIZE },
4874 { VX, VY, VXSIZE, VYSIZE }
4878 for (i = 0; doors[i].door_token != -1; i++)
4880 int door_token = doors[i].door_token;
4881 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4882 int part_1 = doors[i].part_1;
4883 int part_8 = doors[i].part_8;
4884 int part_2 = part_1 + 1;
4885 int part_3 = part_1 + 2;
4886 struct DoorInfo *door = doors[i].door;
4887 struct Rect *door_rect = &door_rect_list[door_index];
4888 boolean door_gfx_redefined = FALSE;
4890 // check if any door part graphic definitions have been redefined
4892 for (j = 0; door_part_controls[j].door_token != -1; j++)
4894 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4895 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4897 if (dpc->door_token == door_token && fi->redefined)
4898 door_gfx_redefined = TRUE;
4901 // check for old-style door graphic/animation modifications
4903 if (!door_gfx_redefined)
4905 if (door->anim_mode & ANIM_STATIC_PANEL)
4907 door->panel.step_xoffset = 0;
4908 door->panel.step_yoffset = 0;
4911 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4913 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4914 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4915 int num_door_steps, num_panel_steps;
4917 // remove door part graphics other than the two default wings
4919 for (j = 0; door_part_controls[j].door_token != -1; j++)
4921 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4922 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4924 if (dpc->graphic >= part_3 &&
4925 dpc->graphic <= part_8)
4929 // set graphics and screen positions of the default wings
4931 g_part_1->width = door_rect->width;
4932 g_part_1->height = door_rect->height;
4933 g_part_2->width = door_rect->width;
4934 g_part_2->height = door_rect->height;
4935 g_part_2->src_x = door_rect->width;
4936 g_part_2->src_y = g_part_1->src_y;
4938 door->part_2.x = door->part_1.x;
4939 door->part_2.y = door->part_1.y;
4941 if (door->width != -1)
4943 g_part_1->width = door->width;
4944 g_part_2->width = door->width;
4946 // special treatment for graphics and screen position of right wing
4947 g_part_2->src_x += door_rect->width - door->width;
4948 door->part_2.x += door_rect->width - door->width;
4951 if (door->height != -1)
4953 g_part_1->height = door->height;
4954 g_part_2->height = door->height;
4956 // special treatment for graphics and screen position of bottom wing
4957 g_part_2->src_y += door_rect->height - door->height;
4958 door->part_2.y += door_rect->height - door->height;
4961 // set animation delays for the default wings and panels
4963 door->part_1.step_delay = door->step_delay;
4964 door->part_2.step_delay = door->step_delay;
4965 door->panel.step_delay = door->step_delay;
4967 // set animation draw order for the default wings
4969 door->part_1.sort_priority = 2; // draw left wing over ...
4970 door->part_2.sort_priority = 1; // ... right wing
4972 // set animation draw offset for the default wings
4974 if (door->anim_mode & ANIM_HORIZONTAL)
4976 door->part_1.step_xoffset = door->step_offset;
4977 door->part_1.step_yoffset = 0;
4978 door->part_2.step_xoffset = door->step_offset * -1;
4979 door->part_2.step_yoffset = 0;
4981 num_door_steps = g_part_1->width / door->step_offset;
4983 else // ANIM_VERTICAL
4985 door->part_1.step_xoffset = 0;
4986 door->part_1.step_yoffset = door->step_offset;
4987 door->part_2.step_xoffset = 0;
4988 door->part_2.step_yoffset = door->step_offset * -1;
4990 num_door_steps = g_part_1->height / door->step_offset;
4993 // set animation draw offset for the default panels
4995 if (door->step_offset > 1)
4997 num_panel_steps = 2 * door_rect->height / door->step_offset;
4998 door->panel.start_step = num_panel_steps - num_door_steps;
4999 door->panel.start_step_closing = door->panel.start_step;
5003 num_panel_steps = door_rect->height / door->step_offset;
5004 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5005 door->panel.start_step_closing = door->panel.start_step;
5006 door->panel.step_delay *= 2;
5013 void InitDoors(void)
5017 for (i = 0; door_part_controls[i].door_token != -1; i++)
5019 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5020 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5022 // initialize "start_step_opening" and "start_step_closing", if needed
5023 if (dpc->pos->start_step_opening == 0 &&
5024 dpc->pos->start_step_closing == 0)
5026 // dpc->pos->start_step_opening = dpc->pos->start_step;
5027 dpc->pos->start_step_closing = dpc->pos->start_step;
5030 // fill structure for door part draw order (sorted below)
5032 dpo->sort_priority = dpc->pos->sort_priority;
5035 // sort door part controls according to sort_priority and graphic number
5036 qsort(door_part_order, MAX_DOOR_PARTS,
5037 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5040 unsigned int OpenDoor(unsigned int door_state)
5042 if (door_state & DOOR_COPY_BACK)
5044 if (door_state & DOOR_OPEN_1)
5045 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5046 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5048 if (door_state & DOOR_OPEN_2)
5049 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5050 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5052 door_state &= ~DOOR_COPY_BACK;
5055 return MoveDoor(door_state);
5058 unsigned int CloseDoor(unsigned int door_state)
5060 unsigned int old_door_state = GetDoorState();
5062 if (!(door_state & DOOR_NO_COPY_BACK))
5064 if (old_door_state & DOOR_OPEN_1)
5065 BlitBitmap(backbuffer, bitmap_db_door_1,
5066 DX, DY, DXSIZE, DYSIZE, 0, 0);
5068 if (old_door_state & DOOR_OPEN_2)
5069 BlitBitmap(backbuffer, bitmap_db_door_2,
5070 VX, VY, VXSIZE, VYSIZE, 0, 0);
5072 door_state &= ~DOOR_NO_COPY_BACK;
5075 return MoveDoor(door_state);
5078 unsigned int GetDoorState(void)
5080 return MoveDoor(DOOR_GET_STATE);
5083 unsigned int SetDoorState(unsigned int door_state)
5085 return MoveDoor(door_state | DOOR_SET_STATE);
5088 static int euclid(int a, int b)
5090 return (b ? euclid(b, a % b) : a);
5093 unsigned int MoveDoor(unsigned int door_state)
5095 struct Rect door_rect_list[] =
5097 { DX, DY, DXSIZE, DYSIZE },
5098 { VX, VY, VXSIZE, VYSIZE }
5100 static int door1 = DOOR_CLOSE_1;
5101 static int door2 = DOOR_CLOSE_2;
5102 unsigned int door_delay = 0;
5103 unsigned int door_delay_value;
5106 if (door_state == DOOR_GET_STATE)
5107 return (door1 | door2);
5109 if (door_state & DOOR_SET_STATE)
5111 if (door_state & DOOR_ACTION_1)
5112 door1 = door_state & DOOR_ACTION_1;
5113 if (door_state & DOOR_ACTION_2)
5114 door2 = door_state & DOOR_ACTION_2;
5116 return (door1 | door2);
5119 if (!(door_state & DOOR_FORCE_REDRAW))
5121 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5122 door_state &= ~DOOR_OPEN_1;
5123 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5124 door_state &= ~DOOR_CLOSE_1;
5125 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5126 door_state &= ~DOOR_OPEN_2;
5127 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5128 door_state &= ~DOOR_CLOSE_2;
5131 if (global.autoplay_leveldir)
5133 door_state |= DOOR_NO_DELAY;
5134 door_state &= ~DOOR_CLOSE_ALL;
5137 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5138 door_state |= DOOR_NO_DELAY;
5140 if (door_state & DOOR_ACTION)
5142 boolean door_panel_drawn[NUM_DOORS];
5143 boolean panel_has_doors[NUM_DOORS];
5144 boolean door_part_skip[MAX_DOOR_PARTS];
5145 boolean door_part_done[MAX_DOOR_PARTS];
5146 boolean door_part_done_all;
5147 int num_steps[MAX_DOOR_PARTS];
5148 int max_move_delay = 0; // delay for complete animations of all doors
5149 int max_step_delay = 0; // delay (ms) between two animation frames
5150 int num_move_steps = 0; // number of animation steps for all doors
5151 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5152 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5153 int current_move_delay = 0;
5157 for (i = 0; i < NUM_DOORS; i++)
5158 panel_has_doors[i] = FALSE;
5160 for (i = 0; i < MAX_DOOR_PARTS; i++)
5162 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5163 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5164 int door_token = dpc->door_token;
5166 door_part_done[i] = FALSE;
5167 door_part_skip[i] = (!(door_state & door_token) ||
5171 for (i = 0; i < MAX_DOOR_PARTS; i++)
5173 int nr = door_part_order[i].nr;
5174 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5175 struct DoorPartPosInfo *pos = dpc->pos;
5176 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5177 int door_token = dpc->door_token;
5178 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5179 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5180 int step_xoffset = ABS(pos->step_xoffset);
5181 int step_yoffset = ABS(pos->step_yoffset);
5182 int step_delay = pos->step_delay;
5183 int current_door_state = door_state & door_token;
5184 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5185 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5186 boolean part_opening = (is_panel ? door_closing : door_opening);
5187 int start_step = (part_opening ? pos->start_step_opening :
5188 pos->start_step_closing);
5189 float move_xsize = (step_xoffset ? g->width : 0);
5190 float move_ysize = (step_yoffset ? g->height : 0);
5191 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5192 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5193 int move_steps = (move_xsteps && move_ysteps ?
5194 MIN(move_xsteps, move_ysteps) :
5195 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5196 int move_delay = move_steps * step_delay;
5198 if (door_part_skip[nr])
5201 max_move_delay = MAX(max_move_delay, move_delay);
5202 max_step_delay = (max_step_delay == 0 ? step_delay :
5203 euclid(max_step_delay, step_delay));
5204 num_steps[nr] = move_steps;
5208 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5210 panel_has_doors[door_index] = TRUE;
5214 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5216 num_move_steps = max_move_delay / max_step_delay;
5217 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5219 door_delay_value = max_step_delay;
5221 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5223 start = num_move_steps - 1;
5227 // opening door sound has priority over simultaneously closing door
5228 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5230 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5232 if (door_state & DOOR_OPEN_1)
5233 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5234 if (door_state & DOOR_OPEN_2)
5235 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5237 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5239 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5241 if (door_state & DOOR_CLOSE_1)
5242 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5243 if (door_state & DOOR_CLOSE_2)
5244 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5248 for (k = start; k < num_move_steps; k++)
5250 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5252 door_part_done_all = TRUE;
5254 for (i = 0; i < NUM_DOORS; i++)
5255 door_panel_drawn[i] = FALSE;
5257 for (i = 0; i < MAX_DOOR_PARTS; i++)
5259 int nr = door_part_order[i].nr;
5260 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5261 struct DoorPartPosInfo *pos = dpc->pos;
5262 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5263 int door_token = dpc->door_token;
5264 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5265 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5266 boolean is_panel_and_door_has_closed = FALSE;
5267 struct Rect *door_rect = &door_rect_list[door_index];
5268 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5270 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5271 int current_door_state = door_state & door_token;
5272 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5273 boolean door_closing = !door_opening;
5274 boolean part_opening = (is_panel ? door_closing : door_opening);
5275 boolean part_closing = !part_opening;
5276 int start_step = (part_opening ? pos->start_step_opening :
5277 pos->start_step_closing);
5278 int step_delay = pos->step_delay;
5279 int step_factor = step_delay / max_step_delay;
5280 int k1 = (step_factor ? k / step_factor + 1 : k);
5281 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5282 int kk = MAX(0, k2);
5285 int src_x, src_y, src_xx, src_yy;
5286 int dst_x, dst_y, dst_xx, dst_yy;
5289 if (door_part_skip[nr])
5292 if (!(door_state & door_token))
5300 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5301 int kk_door = MAX(0, k2_door);
5302 int sync_frame = kk_door * door_delay_value;
5303 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5305 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5306 &g_src_x, &g_src_y);
5311 if (!door_panel_drawn[door_index])
5313 ClearRectangle(drawto, door_rect->x, door_rect->y,
5314 door_rect->width, door_rect->height);
5316 door_panel_drawn[door_index] = TRUE;
5319 // draw opening or closing door parts
5321 if (pos->step_xoffset < 0) // door part on right side
5324 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5327 if (dst_xx + width > door_rect->width)
5328 width = door_rect->width - dst_xx;
5330 else // door part on left side
5333 dst_xx = pos->x - kk * pos->step_xoffset;
5337 src_xx = ABS(dst_xx);
5341 width = g->width - src_xx;
5343 if (width > door_rect->width)
5344 width = door_rect->width;
5346 // printf("::: k == %d [%d] \n", k, start_step);
5349 if (pos->step_yoffset < 0) // door part on bottom side
5352 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5355 if (dst_yy + height > door_rect->height)
5356 height = door_rect->height - dst_yy;
5358 else // door part on top side
5361 dst_yy = pos->y - kk * pos->step_yoffset;
5365 src_yy = ABS(dst_yy);
5369 height = g->height - src_yy;
5372 src_x = g_src_x + src_xx;
5373 src_y = g_src_y + src_yy;
5375 dst_x = door_rect->x + dst_xx;
5376 dst_y = door_rect->y + dst_yy;
5378 is_panel_and_door_has_closed =
5381 panel_has_doors[door_index] &&
5382 k >= num_move_steps_doors_only - 1);
5384 if (width >= 0 && width <= g->width &&
5385 height >= 0 && height <= g->height &&
5386 !is_panel_and_door_has_closed)
5388 if (is_panel || !pos->draw_masked)
5389 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5392 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5396 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5398 if ((part_opening && (width < 0 || height < 0)) ||
5399 (part_closing && (width >= g->width && height >= g->height)))
5400 door_part_done[nr] = TRUE;
5402 // continue door part animations, but not panel after door has closed
5403 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5404 door_part_done_all = FALSE;
5407 if (!(door_state & DOOR_NO_DELAY))
5411 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5413 current_move_delay += max_step_delay;
5415 // prevent OS (Windows) from complaining about program not responding
5419 if (door_part_done_all)
5423 if (!(door_state & DOOR_NO_DELAY))
5425 // wait for specified door action post delay
5426 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5427 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5428 else if (door_state & DOOR_ACTION_1)
5429 door_delay_value = door_1.post_delay;
5430 else if (door_state & DOOR_ACTION_2)
5431 door_delay_value = door_2.post_delay;
5433 while (!DelayReached(&door_delay, door_delay_value))
5438 if (door_state & DOOR_ACTION_1)
5439 door1 = door_state & DOOR_ACTION_1;
5440 if (door_state & DOOR_ACTION_2)
5441 door2 = door_state & DOOR_ACTION_2;
5443 // draw masked border over door area
5444 DrawMaskedBorder(REDRAW_DOOR_1);
5445 DrawMaskedBorder(REDRAW_DOOR_2);
5447 ClearAutoRepeatKeyEvents();
5449 return (door1 | door2);
5452 static boolean useSpecialEditorDoor(void)
5454 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5455 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5457 // do not draw special editor door if editor border defined or redefined
5458 if (graphic_info[graphic].bitmap != NULL || redefined)
5461 // do not draw special editor door if global border defined to be empty
5462 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5465 // do not draw special editor door if viewport definitions do not match
5469 EY + EYSIZE != VY + VYSIZE)
5475 void DrawSpecialEditorDoor(void)
5477 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5478 int top_border_width = gfx1->width;
5479 int top_border_height = gfx1->height;
5480 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5481 int ex = EX - outer_border;
5482 int ey = EY - outer_border;
5483 int vy = VY - outer_border;
5484 int exsize = EXSIZE + 2 * outer_border;
5486 if (!useSpecialEditorDoor())
5489 // draw bigger level editor toolbox window
5490 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5491 top_border_width, top_border_height, ex, ey - top_border_height);
5492 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5493 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5495 redraw_mask |= REDRAW_ALL;
5498 void UndrawSpecialEditorDoor(void)
5500 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5501 int top_border_width = gfx1->width;
5502 int top_border_height = gfx1->height;
5503 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5504 int ex = EX - outer_border;
5505 int ey = EY - outer_border;
5506 int ey_top = ey - top_border_height;
5507 int exsize = EXSIZE + 2 * outer_border;
5508 int eysize = EYSIZE + 2 * outer_border;
5510 if (!useSpecialEditorDoor())
5513 // draw normal tape recorder window
5514 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5516 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5517 ex, ey_top, top_border_width, top_border_height,
5519 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5520 ex, ey, exsize, eysize, ex, ey);
5524 // if screen background is set to "[NONE]", clear editor toolbox window
5525 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5526 ClearRectangle(drawto, ex, ey, exsize, eysize);
5529 redraw_mask |= REDRAW_ALL;
5533 // ---------- new tool button stuff -------------------------------------------
5538 struct TextPosInfo *pos;
5540 boolean is_touch_button;
5542 } toolbutton_info[NUM_TOOL_BUTTONS] =
5545 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5546 TOOL_CTRL_ID_YES, FALSE, "yes"
5549 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5550 TOOL_CTRL_ID_NO, FALSE, "no"
5553 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5554 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5557 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5558 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5561 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5562 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5565 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5566 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5569 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5570 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5573 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5574 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5577 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5578 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5581 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5582 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5586 void CreateToolButtons(void)
5590 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5592 int graphic = toolbutton_info[i].graphic;
5593 struct GraphicInfo *gfx = &graphic_info[graphic];
5594 struct TextPosInfo *pos = toolbutton_info[i].pos;
5595 struct GadgetInfo *gi;
5596 Bitmap *deco_bitmap = None;
5597 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5598 unsigned int event_mask = GD_EVENT_RELEASED;
5599 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5600 int base_x = (is_touch_button ? 0 : DX);
5601 int base_y = (is_touch_button ? 0 : DY);
5602 int gd_x = gfx->src_x;
5603 int gd_y = gfx->src_y;
5604 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5605 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5610 if (global.use_envelope_request && !is_touch_button)
5612 setRequestPosition(&base_x, &base_y, TRUE);
5614 // check if request buttons are outside of envelope and fix, if needed
5615 if (x < 0 || x + gfx->width > request.width ||
5616 y < 0 || y + gfx->height > request.height)
5618 if (id == TOOL_CTRL_ID_YES)
5621 y = request.height - 2 * request.border_size - gfx->height;
5623 else if (id == TOOL_CTRL_ID_NO)
5625 x = request.width - 2 * request.border_size - gfx->width;
5626 y = request.height - 2 * request.border_size - gfx->height;
5628 else if (id == TOOL_CTRL_ID_CONFIRM)
5630 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5631 y = request.height - 2 * request.border_size - gfx->height;
5633 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5635 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5637 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5638 y = request.height - 2 * request.border_size - gfx->height * 2;
5640 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5641 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5646 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5648 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5650 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5651 pos->size, &deco_bitmap, &deco_x, &deco_y);
5652 deco_xpos = (gfx->width - pos->size) / 2;
5653 deco_ypos = (gfx->height - pos->size) / 2;
5656 gi = CreateGadget(GDI_CUSTOM_ID, id,
5657 GDI_IMAGE_ID, graphic,
5658 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5661 GDI_WIDTH, gfx->width,
5662 GDI_HEIGHT, gfx->height,
5663 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5664 GDI_STATE, GD_BUTTON_UNPRESSED,
5665 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5666 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5667 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5668 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5669 GDI_DECORATION_SIZE, pos->size, pos->size,
5670 GDI_DECORATION_SHIFTING, 1, 1,
5671 GDI_DIRECT_DRAW, FALSE,
5672 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5673 GDI_EVENT_MASK, event_mask,
5674 GDI_CALLBACK_ACTION, HandleToolButtons,
5678 Error(ERR_EXIT, "cannot create gadget");
5680 tool_gadget[id] = gi;
5684 void FreeToolButtons(void)
5688 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5689 FreeGadget(tool_gadget[i]);
5692 static void UnmapToolButtons(void)
5696 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5697 UnmapGadget(tool_gadget[i]);
5700 static void HandleToolButtons(struct GadgetInfo *gi)
5702 request_gadget_id = gi->custom_id;
5705 static struct Mapping_EM_to_RND_object
5708 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5709 boolean is_backside; // backside of moving element
5715 em_object_mapping_list[GAME_TILE_MAX + 1] =
5718 Zborder, FALSE, FALSE,
5722 Zplayer, FALSE, FALSE,
5731 Ztank, FALSE, FALSE,
5735 Zeater, FALSE, FALSE,
5739 Zdynamite, FALSE, FALSE,
5743 Zboom, FALSE, FALSE,
5748 Xchain, FALSE, FALSE,
5749 EL_DEFAULT, ACTION_EXPLODING, -1
5752 Xboom_bug, FALSE, FALSE,
5753 EL_BUG, ACTION_EXPLODING, -1
5756 Xboom_tank, FALSE, FALSE,
5757 EL_SPACESHIP, ACTION_EXPLODING, -1
5760 Xboom_android, FALSE, FALSE,
5761 EL_EMC_ANDROID, ACTION_OTHER, -1
5764 Xboom_1, FALSE, FALSE,
5765 EL_DEFAULT, ACTION_EXPLODING, -1
5768 Xboom_2, FALSE, FALSE,
5769 EL_DEFAULT, ACTION_EXPLODING, -1
5773 Xblank, TRUE, FALSE,
5778 Xsplash_e, FALSE, FALSE,
5779 EL_ACID_SPLASH_RIGHT, -1, -1
5782 Xsplash_w, FALSE, FALSE,
5783 EL_ACID_SPLASH_LEFT, -1, -1
5787 Xplant, TRUE, FALSE,
5788 EL_EMC_PLANT, -1, -1
5791 Yplant, FALSE, FALSE,
5792 EL_EMC_PLANT, -1, -1
5796 Xacid_1, TRUE, FALSE,
5800 Xacid_2, FALSE, FALSE,
5804 Xacid_3, FALSE, FALSE,
5808 Xacid_4, FALSE, FALSE,
5812 Xacid_5, FALSE, FALSE,
5816 Xacid_6, FALSE, FALSE,
5820 Xacid_7, FALSE, FALSE,
5824 Xacid_8, FALSE, FALSE,
5829 Xfake_acid_1, TRUE, FALSE,
5830 EL_EMC_FAKE_ACID, -1, -1
5833 Xfake_acid_2, FALSE, FALSE,
5834 EL_EMC_FAKE_ACID, -1, -1
5837 Xfake_acid_3, FALSE, FALSE,
5838 EL_EMC_FAKE_ACID, -1, -1
5841 Xfake_acid_4, FALSE, FALSE,
5842 EL_EMC_FAKE_ACID, -1, -1
5845 Xfake_acid_5, FALSE, FALSE,
5846 EL_EMC_FAKE_ACID, -1, -1
5849 Xfake_acid_6, FALSE, FALSE,
5850 EL_EMC_FAKE_ACID, -1, -1
5853 Xfake_acid_7, FALSE, FALSE,
5854 EL_EMC_FAKE_ACID, -1, -1
5857 Xfake_acid_8, FALSE, FALSE,
5858 EL_EMC_FAKE_ACID, -1, -1
5862 Xgrass, TRUE, FALSE,
5863 EL_EMC_GRASS, -1, -1
5866 Ygrass_nB, FALSE, FALSE,
5867 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5870 Ygrass_eB, FALSE, FALSE,
5871 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5874 Ygrass_sB, FALSE, FALSE,
5875 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5878 Ygrass_wB, FALSE, FALSE,
5879 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5887 Ydirt_nB, FALSE, FALSE,
5888 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5891 Ydirt_eB, FALSE, FALSE,
5892 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5895 Ydirt_sB, FALSE, FALSE,
5896 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5899 Ydirt_wB, FALSE, FALSE,
5900 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5904 Xandroid, TRUE, FALSE,
5905 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5908 Xandroid_1_n, FALSE, FALSE,
5909 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5912 Xandroid_2_n, FALSE, FALSE,
5913 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5916 Xandroid_1_e, FALSE, FALSE,
5917 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5920 Xandroid_2_e, FALSE, FALSE,
5921 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5924 Xandroid_1_w, FALSE, FALSE,
5925 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5928 Xandroid_2_w, FALSE, FALSE,
5929 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5932 Xandroid_1_s, FALSE, FALSE,
5933 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5936 Xandroid_2_s, FALSE, FALSE,
5937 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5940 Yandroid_n, FALSE, FALSE,
5941 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5944 Yandroid_nB, FALSE, TRUE,
5945 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5948 Yandroid_ne, FALSE, FALSE,
5949 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5952 Yandroid_neB, FALSE, TRUE,
5953 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5956 Yandroid_e, FALSE, FALSE,
5957 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5960 Yandroid_eB, FALSE, TRUE,
5961 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5964 Yandroid_se, FALSE, FALSE,
5965 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5968 Yandroid_seB, FALSE, TRUE,
5969 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5972 Yandroid_s, FALSE, FALSE,
5973 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5976 Yandroid_sB, FALSE, TRUE,
5977 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5980 Yandroid_sw, FALSE, FALSE,
5981 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5984 Yandroid_swB, FALSE, TRUE,
5985 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5988 Yandroid_w, FALSE, FALSE,
5989 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5992 Yandroid_wB, FALSE, TRUE,
5993 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5996 Yandroid_nw, FALSE, FALSE,
5997 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6000 Yandroid_nwB, FALSE, TRUE,
6001 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6005 Xeater_n, TRUE, FALSE,
6006 EL_YAMYAM_UP, -1, -1
6009 Xeater_e, TRUE, FALSE,
6010 EL_YAMYAM_RIGHT, -1, -1
6013 Xeater_w, TRUE, FALSE,
6014 EL_YAMYAM_LEFT, -1, -1
6017 Xeater_s, TRUE, FALSE,
6018 EL_YAMYAM_DOWN, -1, -1
6021 Yeater_n, FALSE, FALSE,
6022 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6025 Yeater_nB, FALSE, TRUE,
6026 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6029 Yeater_e, FALSE, FALSE,
6030 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6033 Yeater_eB, FALSE, TRUE,
6034 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6037 Yeater_s, FALSE, FALSE,
6038 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6041 Yeater_sB, FALSE, TRUE,
6042 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6045 Yeater_w, FALSE, FALSE,
6046 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6049 Yeater_wB, FALSE, TRUE,
6050 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6053 Yeater_stone, FALSE, FALSE,
6054 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6057 Yeater_spring, FALSE, FALSE,
6058 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6062 Xalien, TRUE, FALSE,
6066 Xalien_pause, FALSE, FALSE,
6070 Yalien_n, FALSE, FALSE,
6071 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6074 Yalien_nB, FALSE, TRUE,
6075 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6078 Yalien_e, FALSE, FALSE,
6079 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6082 Yalien_eB, FALSE, TRUE,
6083 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6086 Yalien_s, FALSE, FALSE,
6087 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6090 Yalien_sB, FALSE, TRUE,
6091 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6094 Yalien_w, FALSE, FALSE,
6095 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6098 Yalien_wB, FALSE, TRUE,
6099 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6102 Yalien_stone, FALSE, FALSE,
6103 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6106 Yalien_spring, FALSE, FALSE,
6107 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6111 Xbug_1_n, TRUE, FALSE,
6115 Xbug_1_e, TRUE, FALSE,
6116 EL_BUG_RIGHT, -1, -1
6119 Xbug_1_s, TRUE, FALSE,
6123 Xbug_1_w, TRUE, FALSE,
6127 Xbug_2_n, FALSE, FALSE,
6131 Xbug_2_e, FALSE, FALSE,
6132 EL_BUG_RIGHT, -1, -1
6135 Xbug_2_s, FALSE, FALSE,
6139 Xbug_2_w, FALSE, FALSE,
6143 Ybug_n, FALSE, FALSE,
6144 EL_BUG, ACTION_MOVING, MV_BIT_UP
6147 Ybug_nB, FALSE, TRUE,
6148 EL_BUG, ACTION_MOVING, MV_BIT_UP
6151 Ybug_e, FALSE, FALSE,
6152 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6155 Ybug_eB, FALSE, TRUE,
6156 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6159 Ybug_s, FALSE, FALSE,
6160 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6163 Ybug_sB, FALSE, TRUE,
6164 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6167 Ybug_w, FALSE, FALSE,
6168 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6171 Ybug_wB, FALSE, TRUE,
6172 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6175 Ybug_w_n, FALSE, FALSE,
6176 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6179 Ybug_n_e, FALSE, FALSE,
6180 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6183 Ybug_e_s, FALSE, FALSE,
6184 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6187 Ybug_s_w, FALSE, FALSE,
6188 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6191 Ybug_e_n, FALSE, FALSE,
6192 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6195 Ybug_s_e, FALSE, FALSE,
6196 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6199 Ybug_w_s, FALSE, FALSE,
6200 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6203 Ybug_n_w, FALSE, FALSE,
6204 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6207 Ybug_stone, FALSE, FALSE,
6208 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6211 Ybug_spring, FALSE, FALSE,
6212 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6216 Xtank_1_n, TRUE, FALSE,
6217 EL_SPACESHIP_UP, -1, -1
6220 Xtank_1_e, TRUE, FALSE,
6221 EL_SPACESHIP_RIGHT, -1, -1
6224 Xtank_1_s, TRUE, FALSE,
6225 EL_SPACESHIP_DOWN, -1, -1
6228 Xtank_1_w, TRUE, FALSE,
6229 EL_SPACESHIP_LEFT, -1, -1
6232 Xtank_2_n, FALSE, FALSE,
6233 EL_SPACESHIP_UP, -1, -1
6236 Xtank_2_e, FALSE, FALSE,
6237 EL_SPACESHIP_RIGHT, -1, -1
6240 Xtank_2_s, FALSE, FALSE,
6241 EL_SPACESHIP_DOWN, -1, -1
6244 Xtank_2_w, FALSE, FALSE,
6245 EL_SPACESHIP_LEFT, -1, -1
6248 Ytank_n, FALSE, FALSE,
6249 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6252 Ytank_nB, FALSE, TRUE,
6253 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6256 Ytank_e, FALSE, FALSE,
6257 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6260 Ytank_eB, FALSE, TRUE,
6261 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6264 Ytank_s, FALSE, FALSE,
6265 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6268 Ytank_sB, FALSE, TRUE,
6269 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6272 Ytank_w, FALSE, FALSE,
6273 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6276 Ytank_wB, FALSE, TRUE,
6277 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6280 Ytank_w_n, FALSE, FALSE,
6281 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6284 Ytank_n_e, FALSE, FALSE,
6285 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6288 Ytank_e_s, FALSE, FALSE,
6289 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6292 Ytank_s_w, FALSE, FALSE,
6293 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6296 Ytank_e_n, FALSE, FALSE,
6297 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6300 Ytank_s_e, FALSE, FALSE,
6301 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6304 Ytank_w_s, FALSE, FALSE,
6305 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6308 Ytank_n_w, FALSE, FALSE,
6309 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6312 Ytank_stone, FALSE, FALSE,
6313 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6316 Ytank_spring, FALSE, FALSE,
6317 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6321 Xemerald, TRUE, FALSE,
6325 Xemerald_pause, FALSE, FALSE,
6329 Xemerald_fall, FALSE, FALSE,
6333 Xemerald_shine, FALSE, FALSE,
6334 EL_EMERALD, ACTION_TWINKLING, -1
6337 Yemerald_s, FALSE, FALSE,
6338 EL_EMERALD, ACTION_FALLING, -1
6341 Yemerald_sB, FALSE, TRUE,
6342 EL_EMERALD, ACTION_FALLING, -1
6345 Yemerald_e, FALSE, FALSE,
6346 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6349 Yemerald_eB, FALSE, TRUE,
6350 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6353 Yemerald_w, FALSE, FALSE,
6354 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6357 Yemerald_wB, FALSE, TRUE,
6358 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6361 Yemerald_blank, FALSE, FALSE,
6362 EL_EMERALD, ACTION_COLLECTING, -1
6366 Xdiamond, TRUE, FALSE,
6370 Xdiamond_pause, FALSE, FALSE,
6374 Xdiamond_fall, FALSE, FALSE,
6378 Xdiamond_shine, FALSE, FALSE,
6379 EL_DIAMOND, ACTION_TWINKLING, -1
6382 Ydiamond_s, FALSE, FALSE,
6383 EL_DIAMOND, ACTION_FALLING, -1
6386 Ydiamond_sB, FALSE, TRUE,
6387 EL_DIAMOND, ACTION_FALLING, -1
6390 Ydiamond_e, FALSE, FALSE,
6391 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6394 Ydiamond_eB, FALSE, TRUE,
6395 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6398 Ydiamond_w, FALSE, FALSE,
6399 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6402 Ydiamond_wB, FALSE, TRUE,
6403 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6406 Ydiamond_blank, FALSE, FALSE,
6407 EL_DIAMOND, ACTION_COLLECTING, -1
6410 Ydiamond_stone, FALSE, FALSE,
6411 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6415 Xstone, TRUE, FALSE,
6419 Xstone_pause, FALSE, FALSE,
6423 Xstone_fall, FALSE, FALSE,
6427 Ystone_s, FALSE, FALSE,
6428 EL_ROCK, ACTION_FALLING, -1
6431 Ystone_sB, FALSE, TRUE,
6432 EL_ROCK, ACTION_FALLING, -1
6435 Ystone_e, FALSE, FALSE,
6436 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6439 Ystone_eB, FALSE, TRUE,
6440 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6443 Ystone_w, FALSE, FALSE,
6444 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6447 Ystone_wB, FALSE, TRUE,
6448 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6456 Xbomb_pause, FALSE, FALSE,
6460 Xbomb_fall, FALSE, FALSE,
6464 Ybomb_s, FALSE, FALSE,
6465 EL_BOMB, ACTION_FALLING, -1
6468 Ybomb_sB, FALSE, TRUE,
6469 EL_BOMB, ACTION_FALLING, -1
6472 Ybomb_e, FALSE, FALSE,
6473 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6476 Ybomb_eB, FALSE, TRUE,
6477 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6480 Ybomb_w, FALSE, FALSE,
6481 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6484 Ybomb_wB, FALSE, TRUE,
6485 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6488 Ybomb_blank, FALSE, FALSE,
6489 EL_BOMB, ACTION_ACTIVATING, -1
6497 Xnut_pause, FALSE, FALSE,
6501 Xnut_fall, FALSE, FALSE,
6505 Ynut_s, FALSE, FALSE,
6506 EL_NUT, ACTION_FALLING, -1
6509 Ynut_sB, FALSE, TRUE,
6510 EL_NUT, ACTION_FALLING, -1
6513 Ynut_e, FALSE, FALSE,
6514 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6517 Ynut_eB, FALSE, TRUE,
6518 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6521 Ynut_w, FALSE, FALSE,
6522 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6525 Ynut_wB, FALSE, TRUE,
6526 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6529 Ynut_stone, FALSE, FALSE,
6530 EL_NUT, ACTION_BREAKING, -1
6534 Xspring, TRUE, FALSE,
6538 Xspring_pause, FALSE, FALSE,
6542 Xspring_e, FALSE, FALSE,
6546 Xspring_w, FALSE, FALSE,
6550 Xspring_fall, FALSE, FALSE,
6554 Yspring_s, FALSE, FALSE,
6555 EL_SPRING, ACTION_FALLING, -1
6558 Yspring_sB, FALSE, TRUE,
6559 EL_SPRING, ACTION_FALLING, -1
6562 Yspring_e, FALSE, FALSE,
6563 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6566 Yspring_eB, FALSE, TRUE,
6567 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6570 Yspring_w, FALSE, FALSE,
6571 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6574 Yspring_wB, FALSE, TRUE,
6575 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6578 Yspring_alien_e, FALSE, FALSE,
6579 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6582 Yspring_alien_eB, FALSE, TRUE,
6583 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6586 Yspring_alien_w, FALSE, FALSE,
6587 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6590 Yspring_alien_wB, FALSE, TRUE,
6591 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6595 Xpush_emerald_e, FALSE, FALSE,
6596 EL_EMERALD, -1, MV_BIT_RIGHT
6599 Xpush_emerald_w, FALSE, FALSE,
6600 EL_EMERALD, -1, MV_BIT_LEFT
6603 Xpush_diamond_e, FALSE, FALSE,
6604 EL_DIAMOND, -1, MV_BIT_RIGHT
6607 Xpush_diamond_w, FALSE, FALSE,
6608 EL_DIAMOND, -1, MV_BIT_LEFT
6611 Xpush_stone_e, FALSE, FALSE,
6612 EL_ROCK, -1, MV_BIT_RIGHT
6615 Xpush_stone_w, FALSE, FALSE,
6616 EL_ROCK, -1, MV_BIT_LEFT
6619 Xpush_bomb_e, FALSE, FALSE,
6620 EL_BOMB, -1, MV_BIT_RIGHT
6623 Xpush_bomb_w, FALSE, FALSE,
6624 EL_BOMB, -1, MV_BIT_LEFT
6627 Xpush_nut_e, FALSE, FALSE,
6628 EL_NUT, -1, MV_BIT_RIGHT
6631 Xpush_nut_w, FALSE, FALSE,
6632 EL_NUT, -1, MV_BIT_LEFT
6635 Xpush_spring_e, FALSE, FALSE,
6636 EL_SPRING, -1, MV_BIT_RIGHT
6639 Xpush_spring_w, FALSE, FALSE,
6640 EL_SPRING, -1, MV_BIT_LEFT
6644 Xdynamite, TRUE, FALSE,
6645 EL_EM_DYNAMITE, -1, -1
6648 Ydynamite_blank, FALSE, FALSE,
6649 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6652 Xdynamite_1, TRUE, FALSE,
6653 EL_EM_DYNAMITE_ACTIVE, -1, -1
6656 Xdynamite_2, FALSE, FALSE,
6657 EL_EM_DYNAMITE_ACTIVE, -1, -1
6660 Xdynamite_3, FALSE, FALSE,
6661 EL_EM_DYNAMITE_ACTIVE, -1, -1
6664 Xdynamite_4, FALSE, FALSE,
6665 EL_EM_DYNAMITE_ACTIVE, -1, -1
6669 Xkey_1, TRUE, FALSE,
6673 Xkey_2, TRUE, FALSE,
6677 Xkey_3, TRUE, FALSE,
6681 Xkey_4, TRUE, FALSE,
6685 Xkey_5, TRUE, FALSE,
6686 EL_EMC_KEY_5, -1, -1
6689 Xkey_6, TRUE, FALSE,
6690 EL_EMC_KEY_6, -1, -1
6693 Xkey_7, TRUE, FALSE,
6694 EL_EMC_KEY_7, -1, -1
6697 Xkey_8, TRUE, FALSE,
6698 EL_EMC_KEY_8, -1, -1
6702 Xdoor_1, TRUE, FALSE,
6703 EL_EM_GATE_1, -1, -1
6706 Xdoor_2, TRUE, FALSE,
6707 EL_EM_GATE_2, -1, -1
6710 Xdoor_3, TRUE, FALSE,
6711 EL_EM_GATE_3, -1, -1
6714 Xdoor_4, TRUE, FALSE,
6715 EL_EM_GATE_4, -1, -1
6718 Xdoor_5, TRUE, FALSE,
6719 EL_EMC_GATE_5, -1, -1
6722 Xdoor_6, TRUE, FALSE,
6723 EL_EMC_GATE_6, -1, -1
6726 Xdoor_7, TRUE, FALSE,
6727 EL_EMC_GATE_7, -1, -1
6730 Xdoor_8, TRUE, FALSE,
6731 EL_EMC_GATE_8, -1, -1
6735 Xfake_door_1, TRUE, FALSE,
6736 EL_EM_GATE_1_GRAY, -1, -1
6739 Xfake_door_2, TRUE, FALSE,
6740 EL_EM_GATE_2_GRAY, -1, -1
6743 Xfake_door_3, TRUE, FALSE,
6744 EL_EM_GATE_3_GRAY, -1, -1
6747 Xfake_door_4, TRUE, FALSE,
6748 EL_EM_GATE_4_GRAY, -1, -1
6751 Xfake_door_5, TRUE, FALSE,
6752 EL_EMC_GATE_5_GRAY, -1, -1
6755 Xfake_door_6, TRUE, FALSE,
6756 EL_EMC_GATE_6_GRAY, -1, -1
6759 Xfake_door_7, TRUE, FALSE,
6760 EL_EMC_GATE_7_GRAY, -1, -1
6763 Xfake_door_8, TRUE, FALSE,
6764 EL_EMC_GATE_8_GRAY, -1, -1
6768 Xballoon, TRUE, FALSE,
6772 Yballoon_n, FALSE, FALSE,
6773 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6776 Yballoon_nB, FALSE, TRUE,
6777 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6780 Yballoon_e, FALSE, FALSE,
6781 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6784 Yballoon_eB, FALSE, TRUE,
6785 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6788 Yballoon_s, FALSE, FALSE,
6789 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6792 Yballoon_sB, FALSE, TRUE,
6793 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6796 Yballoon_w, FALSE, FALSE,
6797 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6800 Yballoon_wB, FALSE, TRUE,
6801 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6805 Xball_1, TRUE, FALSE,
6806 EL_EMC_MAGIC_BALL, -1, -1
6809 Yball_1, FALSE, FALSE,
6810 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6813 Xball_2, FALSE, FALSE,
6814 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6817 Yball_2, FALSE, FALSE,
6818 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6821 Yball_blank, FALSE, FALSE,
6822 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6826 Xamoeba_1, TRUE, FALSE,
6827 EL_AMOEBA_DRY, ACTION_OTHER, -1
6830 Xamoeba_2, FALSE, FALSE,
6831 EL_AMOEBA_DRY, ACTION_OTHER, -1
6834 Xamoeba_3, FALSE, FALSE,
6835 EL_AMOEBA_DRY, ACTION_OTHER, -1
6838 Xamoeba_4, FALSE, FALSE,
6839 EL_AMOEBA_DRY, ACTION_OTHER, -1
6842 Xamoeba_5, TRUE, FALSE,
6843 EL_AMOEBA_WET, ACTION_OTHER, -1
6846 Xamoeba_6, FALSE, FALSE,
6847 EL_AMOEBA_WET, ACTION_OTHER, -1
6850 Xamoeba_7, FALSE, FALSE,
6851 EL_AMOEBA_WET, ACTION_OTHER, -1
6854 Xamoeba_8, FALSE, FALSE,
6855 EL_AMOEBA_WET, ACTION_OTHER, -1
6859 Xdrip, FALSE, FALSE,
6860 EL_AMOEBA_DROP, ACTION_GROWING, -1
6863 Xdrip_fall, TRUE, FALSE,
6864 EL_AMOEBA_DROP, -1, -1
6867 Xdrip_stretch, FALSE, FALSE,
6868 EL_AMOEBA_DROP, ACTION_FALLING, -1
6871 Xdrip_stretchB, FALSE, TRUE,
6872 EL_AMOEBA_DROP, ACTION_FALLING, -1
6875 Ydrip_1_s, FALSE, FALSE,
6876 EL_AMOEBA_DROP, ACTION_FALLING, -1
6879 Ydrip_1_sB, FALSE, TRUE,
6880 EL_AMOEBA_DROP, ACTION_FALLING, -1
6883 Ydrip_2_s, FALSE, FALSE,
6884 EL_AMOEBA_DROP, ACTION_FALLING, -1
6887 Ydrip_2_sB, FALSE, TRUE,
6888 EL_AMOEBA_DROP, ACTION_FALLING, -1
6892 Xwonderwall, TRUE, FALSE,
6893 EL_MAGIC_WALL, -1, -1
6896 Ywonderwall, FALSE, FALSE,
6897 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6901 Xwheel, TRUE, FALSE,
6902 EL_ROBOT_WHEEL, -1, -1
6905 Ywheel, FALSE, FALSE,
6906 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6910 Xswitch, TRUE, FALSE,
6911 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6914 Yswitch, FALSE, FALSE,
6915 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6919 Xbumper, TRUE, FALSE,
6920 EL_EMC_SPRING_BUMPER, -1, -1
6923 Ybumper, FALSE, FALSE,
6924 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6928 Xacid_nw, TRUE, FALSE,
6929 EL_ACID_POOL_TOPLEFT, -1, -1
6932 Xacid_ne, TRUE, FALSE,
6933 EL_ACID_POOL_TOPRIGHT, -1, -1
6936 Xacid_sw, TRUE, FALSE,
6937 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6940 Xacid_s, TRUE, FALSE,
6941 EL_ACID_POOL_BOTTOM, -1, -1
6944 Xacid_se, TRUE, FALSE,
6945 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6949 Xfake_blank, TRUE, FALSE,
6950 EL_INVISIBLE_WALL, -1, -1
6953 Yfake_blank, FALSE, FALSE,
6954 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6958 Xfake_grass, TRUE, FALSE,
6959 EL_EMC_FAKE_GRASS, -1, -1
6962 Yfake_grass, FALSE, FALSE,
6963 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6967 Xfake_amoeba, TRUE, FALSE,
6968 EL_EMC_DRIPPER, -1, -1
6971 Yfake_amoeba, FALSE, FALSE,
6972 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6976 Xlenses, TRUE, FALSE,
6977 EL_EMC_LENSES, -1, -1
6981 Xmagnify, TRUE, FALSE,
6982 EL_EMC_MAGNIFIER, -1, -1
6987 EL_QUICKSAND_EMPTY, -1, -1
6990 Xsand_stone, TRUE, FALSE,
6991 EL_QUICKSAND_FULL, -1, -1
6994 Xsand_stonein_1, FALSE, TRUE,
6995 EL_ROCK, ACTION_FILLING, -1
6998 Xsand_stonein_2, FALSE, TRUE,
6999 EL_ROCK, ACTION_FILLING, -1
7002 Xsand_stonein_3, FALSE, TRUE,
7003 EL_ROCK, ACTION_FILLING, -1
7006 Xsand_stonein_4, FALSE, TRUE,
7007 EL_ROCK, ACTION_FILLING, -1
7010 Xsand_sandstone_1, FALSE, FALSE,
7011 EL_QUICKSAND_FILLING, -1, -1
7014 Xsand_sandstone_2, FALSE, FALSE,
7015 EL_QUICKSAND_FILLING, -1, -1
7018 Xsand_sandstone_3, FALSE, FALSE,
7019 EL_QUICKSAND_FILLING, -1, -1
7022 Xsand_sandstone_4, FALSE, FALSE,
7023 EL_QUICKSAND_FILLING, -1, -1
7026 Xsand_stonesand_1, FALSE, FALSE,
7027 EL_QUICKSAND_EMPTYING, -1, -1
7030 Xsand_stonesand_2, FALSE, FALSE,
7031 EL_QUICKSAND_EMPTYING, -1, -1
7034 Xsand_stonesand_3, FALSE, FALSE,
7035 EL_QUICKSAND_EMPTYING, -1, -1
7038 Xsand_stonesand_4, FALSE, FALSE,
7039 EL_QUICKSAND_EMPTYING, -1, -1
7042 Xsand_stoneout_1, FALSE, FALSE,
7043 EL_ROCK, ACTION_EMPTYING, -1
7046 Xsand_stoneout_2, FALSE, FALSE,
7047 EL_ROCK, ACTION_EMPTYING, -1
7050 Xsand_stonesand_quickout_1, FALSE, FALSE,
7051 EL_QUICKSAND_EMPTYING, -1, -1
7054 Xsand_stonesand_quickout_2, FALSE, FALSE,
7055 EL_QUICKSAND_EMPTYING, -1, -1
7059 Xslide_ns, TRUE, FALSE,
7060 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7063 Yslide_ns_blank, FALSE, FALSE,
7064 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7067 Xslide_ew, TRUE, FALSE,
7068 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7071 Yslide_ew_blank, FALSE, FALSE,
7072 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7076 Xwind_n, TRUE, FALSE,
7077 EL_BALLOON_SWITCH_UP, -1, -1
7080 Xwind_e, TRUE, FALSE,
7081 EL_BALLOON_SWITCH_RIGHT, -1, -1
7084 Xwind_s, TRUE, FALSE,
7085 EL_BALLOON_SWITCH_DOWN, -1, -1
7088 Xwind_w, TRUE, FALSE,
7089 EL_BALLOON_SWITCH_LEFT, -1, -1
7092 Xwind_any, TRUE, FALSE,
7093 EL_BALLOON_SWITCH_ANY, -1, -1
7096 Xwind_stop, TRUE, FALSE,
7097 EL_BALLOON_SWITCH_NONE, -1, -1
7102 EL_EM_EXIT_CLOSED, -1, -1
7105 Xexit_1, TRUE, FALSE,
7106 EL_EM_EXIT_OPEN, -1, -1
7109 Xexit_2, FALSE, FALSE,
7110 EL_EM_EXIT_OPEN, -1, -1
7113 Xexit_3, FALSE, FALSE,
7114 EL_EM_EXIT_OPEN, -1, -1
7118 Xpause, FALSE, FALSE,
7123 Xwall_1, TRUE, FALSE,
7127 Xwall_2, TRUE, FALSE,
7128 EL_EMC_WALL_14, -1, -1
7131 Xwall_3, TRUE, FALSE,
7132 EL_EMC_WALL_15, -1, -1
7135 Xwall_4, TRUE, FALSE,
7136 EL_EMC_WALL_16, -1, -1
7140 Xroundwall_1, TRUE, FALSE,
7141 EL_WALL_SLIPPERY, -1, -1
7144 Xroundwall_2, TRUE, FALSE,
7145 EL_EMC_WALL_SLIPPERY_2, -1, -1
7148 Xroundwall_3, TRUE, FALSE,
7149 EL_EMC_WALL_SLIPPERY_3, -1, -1
7152 Xroundwall_4, TRUE, FALSE,
7153 EL_EMC_WALL_SLIPPERY_4, -1, -1
7157 Xsteel_1, TRUE, FALSE,
7158 EL_STEELWALL, -1, -1
7161 Xsteel_2, TRUE, FALSE,
7162 EL_EMC_STEELWALL_2, -1, -1
7165 Xsteel_3, TRUE, FALSE,
7166 EL_EMC_STEELWALL_3, -1, -1
7169 Xsteel_4, TRUE, FALSE,
7170 EL_EMC_STEELWALL_4, -1, -1
7174 Xdecor_1, TRUE, FALSE,
7175 EL_EMC_WALL_8, -1, -1
7178 Xdecor_2, TRUE, FALSE,
7179 EL_EMC_WALL_6, -1, -1
7182 Xdecor_3, TRUE, FALSE,
7183 EL_EMC_WALL_4, -1, -1
7186 Xdecor_4, TRUE, FALSE,
7187 EL_EMC_WALL_7, -1, -1
7190 Xdecor_5, TRUE, FALSE,
7191 EL_EMC_WALL_5, -1, -1
7194 Xdecor_6, TRUE, FALSE,
7195 EL_EMC_WALL_9, -1, -1
7198 Xdecor_7, TRUE, FALSE,
7199 EL_EMC_WALL_10, -1, -1
7202 Xdecor_8, TRUE, FALSE,
7203 EL_EMC_WALL_1, -1, -1
7206 Xdecor_9, TRUE, FALSE,
7207 EL_EMC_WALL_2, -1, -1
7210 Xdecor_10, TRUE, FALSE,
7211 EL_EMC_WALL_3, -1, -1
7214 Xdecor_11, TRUE, FALSE,
7215 EL_EMC_WALL_11, -1, -1
7218 Xdecor_12, TRUE, FALSE,
7219 EL_EMC_WALL_12, -1, -1
7223 Xalpha_0, TRUE, FALSE,
7224 EL_CHAR('0'), -1, -1
7227 Xalpha_1, TRUE, FALSE,
7228 EL_CHAR('1'), -1, -1
7231 Xalpha_2, TRUE, FALSE,
7232 EL_CHAR('2'), -1, -1
7235 Xalpha_3, TRUE, FALSE,
7236 EL_CHAR('3'), -1, -1
7239 Xalpha_4, TRUE, FALSE,
7240 EL_CHAR('4'), -1, -1
7243 Xalpha_5, TRUE, FALSE,
7244 EL_CHAR('5'), -1, -1
7247 Xalpha_6, TRUE, FALSE,
7248 EL_CHAR('6'), -1, -1
7251 Xalpha_7, TRUE, FALSE,
7252 EL_CHAR('7'), -1, -1
7255 Xalpha_8, TRUE, FALSE,
7256 EL_CHAR('8'), -1, -1
7259 Xalpha_9, TRUE, FALSE,
7260 EL_CHAR('9'), -1, -1
7263 Xalpha_excla, TRUE, FALSE,
7264 EL_CHAR('!'), -1, -1
7267 Xalpha_apost, TRUE, FALSE,
7268 EL_CHAR('\''), -1, -1
7271 Xalpha_comma, TRUE, FALSE,
7272 EL_CHAR(','), -1, -1
7275 Xalpha_minus, TRUE, FALSE,
7276 EL_CHAR('-'), -1, -1
7279 Xalpha_perio, TRUE, FALSE,
7280 EL_CHAR('.'), -1, -1
7283 Xalpha_colon, TRUE, FALSE,
7284 EL_CHAR(':'), -1, -1
7287 Xalpha_quest, TRUE, FALSE,
7288 EL_CHAR('?'), -1, -1
7291 Xalpha_a, TRUE, FALSE,
7292 EL_CHAR('A'), -1, -1
7295 Xalpha_b, TRUE, FALSE,
7296 EL_CHAR('B'), -1, -1
7299 Xalpha_c, TRUE, FALSE,
7300 EL_CHAR('C'), -1, -1
7303 Xalpha_d, TRUE, FALSE,
7304 EL_CHAR('D'), -1, -1
7307 Xalpha_e, TRUE, FALSE,
7308 EL_CHAR('E'), -1, -1
7311 Xalpha_f, TRUE, FALSE,
7312 EL_CHAR('F'), -1, -1
7315 Xalpha_g, TRUE, FALSE,
7316 EL_CHAR('G'), -1, -1
7319 Xalpha_h, TRUE, FALSE,
7320 EL_CHAR('H'), -1, -1
7323 Xalpha_i, TRUE, FALSE,
7324 EL_CHAR('I'), -1, -1
7327 Xalpha_j, TRUE, FALSE,
7328 EL_CHAR('J'), -1, -1
7331 Xalpha_k, TRUE, FALSE,
7332 EL_CHAR('K'), -1, -1
7335 Xalpha_l, TRUE, FALSE,
7336 EL_CHAR('L'), -1, -1
7339 Xalpha_m, TRUE, FALSE,
7340 EL_CHAR('M'), -1, -1
7343 Xalpha_n, TRUE, FALSE,
7344 EL_CHAR('N'), -1, -1
7347 Xalpha_o, TRUE, FALSE,
7348 EL_CHAR('O'), -1, -1
7351 Xalpha_p, TRUE, FALSE,
7352 EL_CHAR('P'), -1, -1
7355 Xalpha_q, TRUE, FALSE,
7356 EL_CHAR('Q'), -1, -1
7359 Xalpha_r, TRUE, FALSE,
7360 EL_CHAR('R'), -1, -1
7363 Xalpha_s, TRUE, FALSE,
7364 EL_CHAR('S'), -1, -1
7367 Xalpha_t, TRUE, FALSE,
7368 EL_CHAR('T'), -1, -1
7371 Xalpha_u, TRUE, FALSE,
7372 EL_CHAR('U'), -1, -1
7375 Xalpha_v, TRUE, FALSE,
7376 EL_CHAR('V'), -1, -1
7379 Xalpha_w, TRUE, FALSE,
7380 EL_CHAR('W'), -1, -1
7383 Xalpha_x, TRUE, FALSE,
7384 EL_CHAR('X'), -1, -1
7387 Xalpha_y, TRUE, FALSE,
7388 EL_CHAR('Y'), -1, -1
7391 Xalpha_z, TRUE, FALSE,
7392 EL_CHAR('Z'), -1, -1
7395 Xalpha_arrow_e, TRUE, FALSE,
7396 EL_CHAR('>'), -1, -1
7399 Xalpha_arrow_w, TRUE, FALSE,
7400 EL_CHAR('<'), -1, -1
7403 Xalpha_copyr, TRUE, FALSE,
7404 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7408 Ykey_1_blank, FALSE, FALSE,
7409 EL_EM_KEY_1, ACTION_COLLECTING, -1
7412 Ykey_2_blank, FALSE, FALSE,
7413 EL_EM_KEY_2, ACTION_COLLECTING, -1
7416 Ykey_3_blank, FALSE, FALSE,
7417 EL_EM_KEY_3, ACTION_COLLECTING, -1
7420 Ykey_4_blank, FALSE, FALSE,
7421 EL_EM_KEY_4, ACTION_COLLECTING, -1
7424 Ykey_5_blank, FALSE, FALSE,
7425 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7428 Ykey_6_blank, FALSE, FALSE,
7429 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7432 Ykey_7_blank, FALSE, FALSE,
7433 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7436 Ykey_8_blank, FALSE, FALSE,
7437 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7440 Ylenses_blank, FALSE, FALSE,
7441 EL_EMC_LENSES, ACTION_COLLECTING, -1
7444 Ymagnify_blank, FALSE, FALSE,
7445 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7448 Ygrass_blank, FALSE, FALSE,
7449 EL_EMC_GRASS, ACTION_SNAPPING, -1
7452 Ydirt_blank, FALSE, FALSE,
7453 EL_SAND, ACTION_SNAPPING, -1
7462 static struct Mapping_EM_to_RND_player
7471 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7475 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7479 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7483 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7487 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7491 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7495 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7499 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7503 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7507 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7511 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7515 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7519 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7523 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7527 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7531 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7535 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7539 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7543 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7547 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7551 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7555 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7559 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7563 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7567 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7571 EL_PLAYER_1, ACTION_DEFAULT, -1,
7575 EL_PLAYER_2, ACTION_DEFAULT, -1,
7579 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7583 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7587 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7591 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7595 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7599 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7603 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7607 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7611 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7615 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7619 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7623 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7627 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7631 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7635 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7639 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7643 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7647 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7651 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7655 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7659 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7663 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7667 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7671 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7675 EL_PLAYER_3, ACTION_DEFAULT, -1,
7679 EL_PLAYER_4, ACTION_DEFAULT, -1,
7688 int map_element_RND_to_EM_cave(int element_rnd)
7690 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7691 static boolean mapping_initialized = FALSE;
7693 if (!mapping_initialized)
7697 // return "Xalpha_quest" for all undefined elements in mapping array
7698 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7699 mapping_RND_to_EM[i] = Xalpha_quest;
7701 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7702 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7703 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7704 em_object_mapping_list[i].element_em;
7706 mapping_initialized = TRUE;
7709 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7711 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7716 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7719 int map_element_EM_to_RND_cave(int element_em_cave)
7721 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7722 static boolean mapping_initialized = FALSE;
7724 if (!mapping_initialized)
7728 // return "EL_UNKNOWN" for all undefined elements in mapping array
7729 for (i = 0; i < GAME_TILE_MAX; i++)
7730 mapping_EM_to_RND[i] = EL_UNKNOWN;
7732 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7733 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7734 em_object_mapping_list[i].element_rnd;
7736 mapping_initialized = TRUE;
7739 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7741 Error(ERR_WARN, "invalid EM cave element %d", element_em_cave);
7746 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7749 int map_element_EM_to_RND_game(int element_em_game)
7751 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7752 static boolean mapping_initialized = FALSE;
7754 if (!mapping_initialized)
7758 // return "EL_UNKNOWN" for all undefined elements in mapping array
7759 for (i = 0; i < GAME_TILE_MAX; i++)
7760 mapping_EM_to_RND[i] = EL_UNKNOWN;
7762 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7763 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7764 em_object_mapping_list[i].element_rnd;
7766 mapping_initialized = TRUE;
7769 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7771 Error(ERR_WARN, "invalid EM game element %d", element_em_game);
7776 return mapping_EM_to_RND[element_em_game];
7779 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7781 struct LevelInfo_EM *level_em = level->native_em_level;
7782 struct CAVE *cav = level_em->cav;
7785 for (i = 0; i < GAME_TILE_MAX; i++)
7786 cav->android_array[i] = Cblank;
7788 for (i = 0; i < level->num_android_clone_elements; i++)
7790 int element_rnd = level->android_clone_element[i];
7791 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7793 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7794 if (em_object_mapping_list[j].element_rnd == element_rnd)
7795 cav->android_array[em_object_mapping_list[j].element_em] =
7800 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7802 struct LevelInfo_EM *level_em = level->native_em_level;
7803 struct CAVE *cav = level_em->cav;
7806 level->num_android_clone_elements = 0;
7808 for (i = 0; i < GAME_TILE_MAX; i++)
7810 int element_em_cave = cav->android_array[i];
7812 boolean element_found = FALSE;
7814 if (element_em_cave == Cblank)
7817 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7819 for (j = 0; j < level->num_android_clone_elements; j++)
7820 if (level->android_clone_element[j] == element_rnd)
7821 element_found = TRUE;
7825 level->android_clone_element[level->num_android_clone_elements++] =
7828 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7833 if (level->num_android_clone_elements == 0)
7835 level->num_android_clone_elements = 1;
7836 level->android_clone_element[0] = EL_EMPTY;
7840 int map_direction_RND_to_EM(int direction)
7842 return (direction == MV_UP ? 0 :
7843 direction == MV_RIGHT ? 1 :
7844 direction == MV_DOWN ? 2 :
7845 direction == MV_LEFT ? 3 :
7849 int map_direction_EM_to_RND(int direction)
7851 return (direction == 0 ? MV_UP :
7852 direction == 1 ? MV_RIGHT :
7853 direction == 2 ? MV_DOWN :
7854 direction == 3 ? MV_LEFT :
7858 int map_element_RND_to_SP(int element_rnd)
7860 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7862 if (element_rnd >= EL_SP_START &&
7863 element_rnd <= EL_SP_END)
7864 element_sp = element_rnd - EL_SP_START;
7865 else if (element_rnd == EL_EMPTY_SPACE)
7867 else if (element_rnd == EL_INVISIBLE_WALL)
7873 int map_element_SP_to_RND(int element_sp)
7875 int element_rnd = EL_UNKNOWN;
7877 if (element_sp >= 0x00 &&
7879 element_rnd = EL_SP_START + element_sp;
7880 else if (element_sp == 0x28)
7881 element_rnd = EL_INVISIBLE_WALL;
7886 int map_action_SP_to_RND(int action_sp)
7890 case actActive: return ACTION_ACTIVE;
7891 case actImpact: return ACTION_IMPACT;
7892 case actExploding: return ACTION_EXPLODING;
7893 case actDigging: return ACTION_DIGGING;
7894 case actSnapping: return ACTION_SNAPPING;
7895 case actCollecting: return ACTION_COLLECTING;
7896 case actPassing: return ACTION_PASSING;
7897 case actPushing: return ACTION_PUSHING;
7898 case actDropping: return ACTION_DROPPING;
7900 default: return ACTION_DEFAULT;
7904 int map_element_RND_to_MM(int element_rnd)
7906 return (element_rnd >= EL_MM_START_1 &&
7907 element_rnd <= EL_MM_END_1 ?
7908 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7910 element_rnd >= EL_MM_START_2 &&
7911 element_rnd <= EL_MM_END_2 ?
7912 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7914 element_rnd >= EL_CHAR_START &&
7915 element_rnd <= EL_CHAR_END ?
7916 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7918 element_rnd >= EL_MM_RUNTIME_START &&
7919 element_rnd <= EL_MM_RUNTIME_END ?
7920 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7922 element_rnd >= EL_MM_DUMMY_START &&
7923 element_rnd <= EL_MM_DUMMY_END ?
7924 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7926 EL_MM_EMPTY_NATIVE);
7929 int map_element_MM_to_RND(int element_mm)
7931 return (element_mm == EL_MM_EMPTY_NATIVE ||
7932 element_mm == EL_DF_EMPTY_NATIVE ?
7935 element_mm >= EL_MM_START_1_NATIVE &&
7936 element_mm <= EL_MM_END_1_NATIVE ?
7937 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7939 element_mm >= EL_MM_START_2_NATIVE &&
7940 element_mm <= EL_MM_END_2_NATIVE ?
7941 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7943 element_mm >= EL_MM_CHAR_START_NATIVE &&
7944 element_mm <= EL_MM_CHAR_END_NATIVE ?
7945 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7947 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7948 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7949 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7951 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7952 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7953 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7958 int map_action_MM_to_RND(int action_mm)
7960 // all MM actions are defined to exactly match their RND counterparts
7964 int map_sound_MM_to_RND(int sound_mm)
7968 case SND_MM_GAME_LEVELTIME_CHARGING:
7969 return SND_GAME_LEVELTIME_CHARGING;
7971 case SND_MM_GAME_HEALTH_CHARGING:
7972 return SND_GAME_HEALTH_CHARGING;
7975 return SND_UNDEFINED;
7979 int map_mm_wall_element(int element)
7981 return (element >= EL_MM_STEEL_WALL_START &&
7982 element <= EL_MM_STEEL_WALL_END ?
7985 element >= EL_MM_WOODEN_WALL_START &&
7986 element <= EL_MM_WOODEN_WALL_END ?
7989 element >= EL_MM_ICE_WALL_START &&
7990 element <= EL_MM_ICE_WALL_END ?
7993 element >= EL_MM_AMOEBA_WALL_START &&
7994 element <= EL_MM_AMOEBA_WALL_END ?
7997 element >= EL_DF_STEEL_WALL_START &&
7998 element <= EL_DF_STEEL_WALL_END ?
8001 element >= EL_DF_WOODEN_WALL_START &&
8002 element <= EL_DF_WOODEN_WALL_END ?
8008 int map_mm_wall_element_editor(int element)
8012 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8013 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8014 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8015 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8016 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8017 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8019 default: return element;
8023 int get_next_element(int element)
8027 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8028 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8029 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8030 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8031 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8032 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8033 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8034 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8035 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8036 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8037 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8039 default: return element;
8043 int el2img_mm(int element_mm)
8045 return el2img(map_element_MM_to_RND(element_mm));
8048 int el_act_dir2img(int element, int action, int direction)
8050 element = GFX_ELEMENT(element);
8051 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8053 // direction_graphic[][] == graphic[] for undefined direction graphics
8054 return element_info[element].direction_graphic[action][direction];
8057 static int el_act_dir2crm(int element, int action, int direction)
8059 element = GFX_ELEMENT(element);
8060 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8062 // direction_graphic[][] == graphic[] for undefined direction graphics
8063 return element_info[element].direction_crumbled[action][direction];
8066 int el_act2img(int element, int action)
8068 element = GFX_ELEMENT(element);
8070 return element_info[element].graphic[action];
8073 int el_act2crm(int element, int action)
8075 element = GFX_ELEMENT(element);
8077 return element_info[element].crumbled[action];
8080 int el_dir2img(int element, int direction)
8082 element = GFX_ELEMENT(element);
8084 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8087 int el2baseimg(int element)
8089 return element_info[element].graphic[ACTION_DEFAULT];
8092 int el2img(int element)
8094 element = GFX_ELEMENT(element);
8096 return element_info[element].graphic[ACTION_DEFAULT];
8099 int el2edimg(int element)
8101 element = GFX_ELEMENT(element);
8103 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8106 int el2preimg(int element)
8108 element = GFX_ELEMENT(element);
8110 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8113 int el2panelimg(int element)
8115 element = GFX_ELEMENT(element);
8117 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8120 int font2baseimg(int font_nr)
8122 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8125 int getBeltNrFromBeltElement(int element)
8127 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8128 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8129 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8132 int getBeltNrFromBeltActiveElement(int element)
8134 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8135 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8136 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8139 int getBeltNrFromBeltSwitchElement(int element)
8141 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8142 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8143 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8146 int getBeltDirNrFromBeltElement(int element)
8148 static int belt_base_element[4] =
8150 EL_CONVEYOR_BELT_1_LEFT,
8151 EL_CONVEYOR_BELT_2_LEFT,
8152 EL_CONVEYOR_BELT_3_LEFT,
8153 EL_CONVEYOR_BELT_4_LEFT
8156 int belt_nr = getBeltNrFromBeltElement(element);
8157 int belt_dir_nr = element - belt_base_element[belt_nr];
8159 return (belt_dir_nr % 3);
8162 int getBeltDirNrFromBeltSwitchElement(int element)
8164 static int belt_base_element[4] =
8166 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8167 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8168 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8169 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8172 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8173 int belt_dir_nr = element - belt_base_element[belt_nr];
8175 return (belt_dir_nr % 3);
8178 int getBeltDirFromBeltElement(int element)
8180 static int belt_move_dir[3] =
8187 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8189 return belt_move_dir[belt_dir_nr];
8192 int getBeltDirFromBeltSwitchElement(int element)
8194 static int belt_move_dir[3] =
8201 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8203 return belt_move_dir[belt_dir_nr];
8206 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8208 static int belt_base_element[4] =
8210 EL_CONVEYOR_BELT_1_LEFT,
8211 EL_CONVEYOR_BELT_2_LEFT,
8212 EL_CONVEYOR_BELT_3_LEFT,
8213 EL_CONVEYOR_BELT_4_LEFT
8216 return belt_base_element[belt_nr] + belt_dir_nr;
8219 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8221 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8223 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8226 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8228 static int belt_base_element[4] =
8230 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8231 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8232 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8233 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8236 return belt_base_element[belt_nr] + belt_dir_nr;
8239 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8241 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8243 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8246 boolean swapTiles_EM(boolean is_pre_emc_cave)
8248 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8251 boolean getTeamMode_EM(void)
8253 return game.team_mode || network_playing;
8256 boolean isActivePlayer_EM(int player_nr)
8258 return stored_player[player_nr].active;
8261 unsigned int InitRND(int seed)
8263 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8264 return InitEngineRandom_EM(seed);
8265 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8266 return InitEngineRandom_SP(seed);
8267 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8268 return InitEngineRandom_MM(seed);
8270 return InitEngineRandom_RND(seed);
8273 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8274 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8276 static int get_effective_element_EM(int tile, int frame_em)
8278 int element = object_mapping[tile].element_rnd;
8279 int action = object_mapping[tile].action;
8280 boolean is_backside = object_mapping[tile].is_backside;
8281 boolean action_removing = (action == ACTION_DIGGING ||
8282 action == ACTION_SNAPPING ||
8283 action == ACTION_COLLECTING);
8291 return (frame_em > 5 ? EL_EMPTY : element);
8297 else // frame_em == 7
8308 case Ydiamond_stone:
8312 case Xdrip_stretchB:
8328 case Ymagnify_blank:
8331 case Xsand_stonein_1:
8332 case Xsand_stonein_2:
8333 case Xsand_stonein_3:
8334 case Xsand_stonein_4:
8338 return (is_backside || action_removing ? EL_EMPTY : element);
8343 static boolean check_linear_animation_EM(int tile)
8347 case Xsand_stonesand_1:
8348 case Xsand_stonesand_quickout_1:
8349 case Xsand_sandstone_1:
8350 case Xsand_stonein_1:
8351 case Xsand_stoneout_1:
8379 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8380 boolean has_crumbled_graphics,
8381 int crumbled, int sync_frame)
8383 // if element can be crumbled, but certain action graphics are just empty
8384 // space (like instantly snapping sand to empty space in 1 frame), do not
8385 // treat these empty space graphics as crumbled graphics in EMC engine
8386 if (crumbled == IMG_EMPTY_SPACE)
8387 has_crumbled_graphics = FALSE;
8389 if (has_crumbled_graphics)
8391 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8392 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8393 g_crumbled->anim_delay,
8394 g_crumbled->anim_mode,
8395 g_crumbled->anim_start_frame,
8398 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8399 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8401 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8402 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8404 g_em->has_crumbled_graphics = TRUE;
8408 g_em->crumbled_bitmap = NULL;
8409 g_em->crumbled_src_x = 0;
8410 g_em->crumbled_src_y = 0;
8411 g_em->crumbled_border_size = 0;
8412 g_em->crumbled_tile_size = 0;
8414 g_em->has_crumbled_graphics = FALSE;
8419 void ResetGfxAnimation_EM(int x, int y, int tile)
8425 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8426 int tile, int frame_em, int x, int y)
8428 int action = object_mapping[tile].action;
8429 int direction = object_mapping[tile].direction;
8430 int effective_element = get_effective_element_EM(tile, frame_em);
8431 int graphic = (direction == MV_NONE ?
8432 el_act2img(effective_element, action) :
8433 el_act_dir2img(effective_element, action, direction));
8434 struct GraphicInfo *g = &graphic_info[graphic];
8436 boolean action_removing = (action == ACTION_DIGGING ||
8437 action == ACTION_SNAPPING ||
8438 action == ACTION_COLLECTING);
8439 boolean action_moving = (action == ACTION_FALLING ||
8440 action == ACTION_MOVING ||
8441 action == ACTION_PUSHING ||
8442 action == ACTION_EATING ||
8443 action == ACTION_FILLING ||
8444 action == ACTION_EMPTYING);
8445 boolean action_falling = (action == ACTION_FALLING ||
8446 action == ACTION_FILLING ||
8447 action == ACTION_EMPTYING);
8449 // special case: graphic uses "2nd movement tile" and has defined
8450 // 7 frames for movement animation (or less) => use default graphic
8451 // for last (8th) frame which ends the movement animation
8452 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8454 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8455 graphic = (direction == MV_NONE ?
8456 el_act2img(effective_element, action) :
8457 el_act_dir2img(effective_element, action, direction));
8459 g = &graphic_info[graphic];
8462 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8466 else if (action_moving)
8468 boolean is_backside = object_mapping[tile].is_backside;
8472 int direction = object_mapping[tile].direction;
8473 int move_dir = (action_falling ? MV_DOWN : direction);
8478 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8479 if (g->double_movement && frame_em == 0)
8483 if (move_dir == MV_LEFT)
8484 GfxFrame[x - 1][y] = GfxFrame[x][y];
8485 else if (move_dir == MV_RIGHT)
8486 GfxFrame[x + 1][y] = GfxFrame[x][y];
8487 else if (move_dir == MV_UP)
8488 GfxFrame[x][y - 1] = GfxFrame[x][y];
8489 else if (move_dir == MV_DOWN)
8490 GfxFrame[x][y + 1] = GfxFrame[x][y];
8497 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8498 if (tile == Xsand_stonesand_quickout_1 ||
8499 tile == Xsand_stonesand_quickout_2)
8503 if (graphic_info[graphic].anim_global_sync)
8504 sync_frame = FrameCounter;
8505 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8506 sync_frame = GfxFrame[x][y];
8508 sync_frame = 0; // playfield border (pseudo steel)
8510 SetRandomAnimationValue(x, y);
8512 int frame = getAnimationFrame(g->anim_frames,
8515 g->anim_start_frame,
8518 g_em->unique_identifier =
8519 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8522 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8523 int tile, int frame_em, int x, int y)
8525 int action = object_mapping[tile].action;
8526 int direction = object_mapping[tile].direction;
8527 boolean is_backside = object_mapping[tile].is_backside;
8528 int effective_element = get_effective_element_EM(tile, frame_em);
8529 int effective_action = action;
8530 int graphic = (direction == MV_NONE ?
8531 el_act2img(effective_element, effective_action) :
8532 el_act_dir2img(effective_element, effective_action,
8534 int crumbled = (direction == MV_NONE ?
8535 el_act2crm(effective_element, effective_action) :
8536 el_act_dir2crm(effective_element, effective_action,
8538 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8539 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8540 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8541 struct GraphicInfo *g = &graphic_info[graphic];
8544 // special case: graphic uses "2nd movement tile" and has defined
8545 // 7 frames for movement animation (or less) => use default graphic
8546 // for last (8th) frame which ends the movement animation
8547 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8549 effective_action = ACTION_DEFAULT;
8550 graphic = (direction == MV_NONE ?
8551 el_act2img(effective_element, effective_action) :
8552 el_act_dir2img(effective_element, effective_action,
8554 crumbled = (direction == MV_NONE ?
8555 el_act2crm(effective_element, effective_action) :
8556 el_act_dir2crm(effective_element, effective_action,
8559 g = &graphic_info[graphic];
8562 if (graphic_info[graphic].anim_global_sync)
8563 sync_frame = FrameCounter;
8564 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8565 sync_frame = GfxFrame[x][y];
8567 sync_frame = 0; // playfield border (pseudo steel)
8569 SetRandomAnimationValue(x, y);
8571 int frame = getAnimationFrame(g->anim_frames,
8574 g->anim_start_frame,
8577 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8578 g->double_movement && is_backside);
8580 // (updating the "crumbled" graphic definitions is probably not really needed,
8581 // as animations for crumbled graphics can't be longer than one EMC cycle)
8582 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8586 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8587 int player_nr, int anim, int frame_em)
8589 int element = player_mapping[player_nr][anim].element_rnd;
8590 int action = player_mapping[player_nr][anim].action;
8591 int direction = player_mapping[player_nr][anim].direction;
8592 int graphic = (direction == MV_NONE ?
8593 el_act2img(element, action) :
8594 el_act_dir2img(element, action, direction));
8595 struct GraphicInfo *g = &graphic_info[graphic];
8598 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8600 stored_player[player_nr].StepFrame = frame_em;
8602 sync_frame = stored_player[player_nr].Frame;
8604 int frame = getAnimationFrame(g->anim_frames,
8607 g->anim_start_frame,
8610 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8611 &g_em->src_x, &g_em->src_y, FALSE);
8614 void InitGraphicInfo_EM(void)
8618 // always start with reliable default values
8619 for (i = 0; i < GAME_TILE_MAX; i++)
8621 object_mapping[i].element_rnd = EL_UNKNOWN;
8622 object_mapping[i].is_backside = FALSE;
8623 object_mapping[i].action = ACTION_DEFAULT;
8624 object_mapping[i].direction = MV_NONE;
8627 // always start with reliable default values
8628 for (p = 0; p < MAX_PLAYERS; p++)
8630 for (i = 0; i < PLY_MAX; i++)
8632 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8633 player_mapping[p][i].action = ACTION_DEFAULT;
8634 player_mapping[p][i].direction = MV_NONE;
8638 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8640 int e = em_object_mapping_list[i].element_em;
8642 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8643 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8645 if (em_object_mapping_list[i].action != -1)
8646 object_mapping[e].action = em_object_mapping_list[i].action;
8648 if (em_object_mapping_list[i].direction != -1)
8649 object_mapping[e].direction =
8650 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8653 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8655 int a = em_player_mapping_list[i].action_em;
8656 int p = em_player_mapping_list[i].player_nr;
8658 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8660 if (em_player_mapping_list[i].action != -1)
8661 player_mapping[p][a].action = em_player_mapping_list[i].action;
8663 if (em_player_mapping_list[i].direction != -1)
8664 player_mapping[p][a].direction =
8665 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8668 for (i = 0; i < GAME_TILE_MAX; i++)
8670 int element = object_mapping[i].element_rnd;
8671 int action = object_mapping[i].action;
8672 int direction = object_mapping[i].direction;
8673 boolean is_backside = object_mapping[i].is_backside;
8674 boolean action_exploding = ((action == ACTION_EXPLODING ||
8675 action == ACTION_SMASHED_BY_ROCK ||
8676 action == ACTION_SMASHED_BY_SPRING) &&
8677 element != EL_DIAMOND);
8678 boolean action_active = (action == ACTION_ACTIVE);
8679 boolean action_other = (action == ACTION_OTHER);
8681 for (j = 0; j < 8; j++)
8683 int effective_element = get_effective_element_EM(i, j);
8684 int effective_action = (j < 7 ? action :
8685 i == Xdrip_stretch ? action :
8686 i == Xdrip_stretchB ? action :
8687 i == Ydrip_1_s ? action :
8688 i == Ydrip_1_sB ? action :
8689 i == Yball_1 ? action :
8690 i == Xball_2 ? action :
8691 i == Yball_2 ? action :
8692 i == Yball_blank ? action :
8693 i == Ykey_1_blank ? action :
8694 i == Ykey_2_blank ? action :
8695 i == Ykey_3_blank ? action :
8696 i == Ykey_4_blank ? action :
8697 i == Ykey_5_blank ? action :
8698 i == Ykey_6_blank ? action :
8699 i == Ykey_7_blank ? action :
8700 i == Ykey_8_blank ? action :
8701 i == Ylenses_blank ? action :
8702 i == Ymagnify_blank ? action :
8703 i == Ygrass_blank ? action :
8704 i == Ydirt_blank ? action :
8705 i == Xsand_stonein_1 ? action :
8706 i == Xsand_stonein_2 ? action :
8707 i == Xsand_stonein_3 ? action :
8708 i == Xsand_stonein_4 ? action :
8709 i == Xsand_stoneout_1 ? action :
8710 i == Xsand_stoneout_2 ? action :
8711 i == Xboom_android ? ACTION_EXPLODING :
8712 action_exploding ? ACTION_EXPLODING :
8713 action_active ? action :
8714 action_other ? action :
8716 int graphic = (el_act_dir2img(effective_element, effective_action,
8718 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8720 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8721 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8722 boolean has_action_graphics = (graphic != base_graphic);
8723 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8724 struct GraphicInfo *g = &graphic_info[graphic];
8725 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8728 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8729 boolean special_animation = (action != ACTION_DEFAULT &&
8730 g->anim_frames == 3 &&
8731 g->anim_delay == 2 &&
8732 g->anim_mode & ANIM_LINEAR);
8733 int sync_frame = (i == Xdrip_stretch ? 7 :
8734 i == Xdrip_stretchB ? 7 :
8735 i == Ydrip_2_s ? j + 8 :
8736 i == Ydrip_2_sB ? j + 8 :
8745 i == Xfake_acid_1 ? 0 :
8746 i == Xfake_acid_2 ? 10 :
8747 i == Xfake_acid_3 ? 20 :
8748 i == Xfake_acid_4 ? 30 :
8749 i == Xfake_acid_5 ? 40 :
8750 i == Xfake_acid_6 ? 50 :
8751 i == Xfake_acid_7 ? 60 :
8752 i == Xfake_acid_8 ? 70 :
8754 i == Yball_2 ? j + 8 :
8755 i == Yball_blank ? j + 1 :
8756 i == Ykey_1_blank ? j + 1 :
8757 i == Ykey_2_blank ? j + 1 :
8758 i == Ykey_3_blank ? j + 1 :
8759 i == Ykey_4_blank ? j + 1 :
8760 i == Ykey_5_blank ? j + 1 :
8761 i == Ykey_6_blank ? j + 1 :
8762 i == Ykey_7_blank ? j + 1 :
8763 i == Ykey_8_blank ? j + 1 :
8764 i == Ylenses_blank ? j + 1 :
8765 i == Ymagnify_blank ? j + 1 :
8766 i == Ygrass_blank ? j + 1 :
8767 i == Ydirt_blank ? j + 1 :
8768 i == Xamoeba_1 ? 0 :
8769 i == Xamoeba_2 ? 1 :
8770 i == Xamoeba_3 ? 2 :
8771 i == Xamoeba_4 ? 3 :
8772 i == Xamoeba_5 ? 0 :
8773 i == Xamoeba_6 ? 1 :
8774 i == Xamoeba_7 ? 2 :
8775 i == Xamoeba_8 ? 3 :
8776 i == Xexit_2 ? j + 8 :
8777 i == Xexit_3 ? j + 16 :
8778 i == Xdynamite_1 ? 0 :
8779 i == Xdynamite_2 ? 8 :
8780 i == Xdynamite_3 ? 16 :
8781 i == Xdynamite_4 ? 24 :
8782 i == Xsand_stonein_1 ? j + 1 :
8783 i == Xsand_stonein_2 ? j + 9 :
8784 i == Xsand_stonein_3 ? j + 17 :
8785 i == Xsand_stonein_4 ? j + 25 :
8786 i == Xsand_stoneout_1 && j == 0 ? 0 :
8787 i == Xsand_stoneout_1 && j == 1 ? 0 :
8788 i == Xsand_stoneout_1 && j == 2 ? 1 :
8789 i == Xsand_stoneout_1 && j == 3 ? 2 :
8790 i == Xsand_stoneout_1 && j == 4 ? 2 :
8791 i == Xsand_stoneout_1 && j == 5 ? 3 :
8792 i == Xsand_stoneout_1 && j == 6 ? 4 :
8793 i == Xsand_stoneout_1 && j == 7 ? 4 :
8794 i == Xsand_stoneout_2 && j == 0 ? 5 :
8795 i == Xsand_stoneout_2 && j == 1 ? 6 :
8796 i == Xsand_stoneout_2 && j == 2 ? 7 :
8797 i == Xsand_stoneout_2 && j == 3 ? 8 :
8798 i == Xsand_stoneout_2 && j == 4 ? 9 :
8799 i == Xsand_stoneout_2 && j == 5 ? 11 :
8800 i == Xsand_stoneout_2 && j == 6 ? 13 :
8801 i == Xsand_stoneout_2 && j == 7 ? 15 :
8802 i == Xboom_bug && j == 1 ? 2 :
8803 i == Xboom_bug && j == 2 ? 2 :
8804 i == Xboom_bug && j == 3 ? 4 :
8805 i == Xboom_bug && j == 4 ? 4 :
8806 i == Xboom_bug && j == 5 ? 2 :
8807 i == Xboom_bug && j == 6 ? 2 :
8808 i == Xboom_bug && j == 7 ? 0 :
8809 i == Xboom_tank && j == 1 ? 2 :
8810 i == Xboom_tank && j == 2 ? 2 :
8811 i == Xboom_tank && j == 3 ? 4 :
8812 i == Xboom_tank && j == 4 ? 4 :
8813 i == Xboom_tank && j == 5 ? 2 :
8814 i == Xboom_tank && j == 6 ? 2 :
8815 i == Xboom_tank && j == 7 ? 0 :
8816 i == Xboom_android && j == 7 ? 6 :
8817 i == Xboom_1 && j == 1 ? 2 :
8818 i == Xboom_1 && j == 2 ? 2 :
8819 i == Xboom_1 && j == 3 ? 4 :
8820 i == Xboom_1 && j == 4 ? 4 :
8821 i == Xboom_1 && j == 5 ? 6 :
8822 i == Xboom_1 && j == 6 ? 6 :
8823 i == Xboom_1 && j == 7 ? 8 :
8824 i == Xboom_2 && j == 0 ? 8 :
8825 i == Xboom_2 && j == 1 ? 8 :
8826 i == Xboom_2 && j == 2 ? 10 :
8827 i == Xboom_2 && j == 3 ? 10 :
8828 i == Xboom_2 && j == 4 ? 10 :
8829 i == Xboom_2 && j == 5 ? 12 :
8830 i == Xboom_2 && j == 6 ? 12 :
8831 i == Xboom_2 && j == 7 ? 12 :
8832 special_animation && j == 4 ? 3 :
8833 effective_action != action ? 0 :
8835 int frame = getAnimationFrame(g->anim_frames,
8838 g->anim_start_frame,
8841 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8842 g->double_movement && is_backside);
8844 g_em->bitmap = src_bitmap;
8845 g_em->src_x = src_x;
8846 g_em->src_y = src_y;
8847 g_em->src_offset_x = 0;
8848 g_em->src_offset_y = 0;
8849 g_em->dst_offset_x = 0;
8850 g_em->dst_offset_y = 0;
8851 g_em->width = TILEX;
8852 g_em->height = TILEY;
8854 g_em->preserve_background = FALSE;
8856 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8859 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8860 effective_action == ACTION_MOVING ||
8861 effective_action == ACTION_PUSHING ||
8862 effective_action == ACTION_EATING)) ||
8863 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8864 effective_action == ACTION_EMPTYING)))
8867 (effective_action == ACTION_FALLING ||
8868 effective_action == ACTION_FILLING ||
8869 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8870 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8871 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8872 int num_steps = (i == Ydrip_1_s ? 16 :
8873 i == Ydrip_1_sB ? 16 :
8874 i == Ydrip_2_s ? 16 :
8875 i == Ydrip_2_sB ? 16 :
8876 i == Xsand_stonein_1 ? 32 :
8877 i == Xsand_stonein_2 ? 32 :
8878 i == Xsand_stonein_3 ? 32 :
8879 i == Xsand_stonein_4 ? 32 :
8880 i == Xsand_stoneout_1 ? 16 :
8881 i == Xsand_stoneout_2 ? 16 : 8);
8882 int cx = ABS(dx) * (TILEX / num_steps);
8883 int cy = ABS(dy) * (TILEY / num_steps);
8884 int step_frame = (i == Ydrip_2_s ? j + 8 :
8885 i == Ydrip_2_sB ? j + 8 :
8886 i == Xsand_stonein_2 ? j + 8 :
8887 i == Xsand_stonein_3 ? j + 16 :
8888 i == Xsand_stonein_4 ? j + 24 :
8889 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8890 int step = (is_backside ? step_frame : num_steps - step_frame);
8892 if (is_backside) // tile where movement starts
8894 if (dx < 0 || dy < 0)
8896 g_em->src_offset_x = cx * step;
8897 g_em->src_offset_y = cy * step;
8901 g_em->dst_offset_x = cx * step;
8902 g_em->dst_offset_y = cy * step;
8905 else // tile where movement ends
8907 if (dx < 0 || dy < 0)
8909 g_em->dst_offset_x = cx * step;
8910 g_em->dst_offset_y = cy * step;
8914 g_em->src_offset_x = cx * step;
8915 g_em->src_offset_y = cy * step;
8919 g_em->width = TILEX - cx * step;
8920 g_em->height = TILEY - cy * step;
8923 // create unique graphic identifier to decide if tile must be redrawn
8924 /* bit 31 - 16 (16 bit): EM style graphic
8925 bit 15 - 12 ( 4 bit): EM style frame
8926 bit 11 - 6 ( 6 bit): graphic width
8927 bit 5 - 0 ( 6 bit): graphic height */
8928 g_em->unique_identifier =
8929 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8933 for (i = 0; i < GAME_TILE_MAX; i++)
8935 for (j = 0; j < 8; j++)
8937 int element = object_mapping[i].element_rnd;
8938 int action = object_mapping[i].action;
8939 int direction = object_mapping[i].direction;
8940 boolean is_backside = object_mapping[i].is_backside;
8941 int graphic_action = el_act_dir2img(element, action, direction);
8942 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8944 if ((action == ACTION_SMASHED_BY_ROCK ||
8945 action == ACTION_SMASHED_BY_SPRING ||
8946 action == ACTION_EATING) &&
8947 graphic_action == graphic_default)
8949 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8950 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8951 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8952 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8955 // no separate animation for "smashed by rock" -- use rock instead
8956 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8957 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8959 g_em->bitmap = g_xx->bitmap;
8960 g_em->src_x = g_xx->src_x;
8961 g_em->src_y = g_xx->src_y;
8962 g_em->src_offset_x = g_xx->src_offset_x;
8963 g_em->src_offset_y = g_xx->src_offset_y;
8964 g_em->dst_offset_x = g_xx->dst_offset_x;
8965 g_em->dst_offset_y = g_xx->dst_offset_y;
8966 g_em->width = g_xx->width;
8967 g_em->height = g_xx->height;
8968 g_em->unique_identifier = g_xx->unique_identifier;
8971 g_em->preserve_background = TRUE;
8976 for (p = 0; p < MAX_PLAYERS; p++)
8978 for (i = 0; i < PLY_MAX; i++)
8980 int element = player_mapping[p][i].element_rnd;
8981 int action = player_mapping[p][i].action;
8982 int direction = player_mapping[p][i].direction;
8984 for (j = 0; j < 8; j++)
8986 int effective_element = element;
8987 int effective_action = action;
8988 int graphic = (direction == MV_NONE ?
8989 el_act2img(effective_element, effective_action) :
8990 el_act_dir2img(effective_element, effective_action,
8992 struct GraphicInfo *g = &graphic_info[graphic];
8993 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
8997 int frame = getAnimationFrame(g->anim_frames,
9000 g->anim_start_frame,
9003 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9005 g_em->bitmap = src_bitmap;
9006 g_em->src_x = src_x;
9007 g_em->src_y = src_y;
9008 g_em->src_offset_x = 0;
9009 g_em->src_offset_y = 0;
9010 g_em->dst_offset_x = 0;
9011 g_em->dst_offset_y = 0;
9012 g_em->width = TILEX;
9013 g_em->height = TILEY;
9019 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9020 boolean any_player_moving,
9021 boolean any_player_snapping,
9022 boolean any_player_dropping)
9024 if (frame == 7 && !any_player_dropping)
9026 if (!local_player->was_waiting)
9028 if (!CheckSaveEngineSnapshotToList())
9031 local_player->was_waiting = TRUE;
9034 else if (any_player_moving || any_player_snapping || any_player_dropping)
9036 local_player->was_waiting = FALSE;
9040 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9041 boolean murphy_is_dropping)
9043 if (murphy_is_waiting)
9045 if (!local_player->was_waiting)
9047 if (!CheckSaveEngineSnapshotToList())
9050 local_player->was_waiting = TRUE;
9055 local_player->was_waiting = FALSE;
9059 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9060 boolean button_released)
9062 if (button_released)
9064 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9065 CheckSaveEngineSnapshotToList();
9067 else if (element_clicked)
9069 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9070 CheckSaveEngineSnapshotToList();
9072 game.snapshot.changed_action = TRUE;
9076 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9077 boolean any_player_moving,
9078 boolean any_player_snapping,
9079 boolean any_player_dropping)
9081 if (tape.single_step && tape.recording && !tape.pausing)
9082 if (frame == 7 && !any_player_dropping)
9083 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9085 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9086 any_player_snapping, any_player_dropping);
9089 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9090 boolean murphy_is_dropping)
9092 boolean murphy_starts_dropping = FALSE;
9095 for (i = 0; i < MAX_PLAYERS; i++)
9096 if (stored_player[i].force_dropping)
9097 murphy_starts_dropping = TRUE;
9099 if (tape.single_step && tape.recording && !tape.pausing)
9100 if (murphy_is_waiting && !murphy_starts_dropping)
9101 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9103 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9106 void CheckSingleStepMode_MM(boolean element_clicked,
9107 boolean button_released)
9109 if (tape.single_step && tape.recording && !tape.pausing)
9110 if (button_released)
9111 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9113 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9116 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9117 int graphic, int sync_frame, int x, int y)
9119 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9121 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9124 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9126 return (IS_NEXT_FRAME(sync_frame, graphic));
9129 int getGraphicInfo_Delay(int graphic)
9131 return graphic_info[graphic].anim_delay;
9134 void PlayMenuSoundExt(int sound)
9136 if (sound == SND_UNDEFINED)
9139 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9140 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9143 if (IS_LOOP_SOUND(sound))
9144 PlaySoundLoop(sound);
9149 void PlayMenuSound(void)
9151 PlayMenuSoundExt(menu.sound[game_status]);
9154 void PlayMenuSoundStereo(int sound, int stereo_position)
9156 if (sound == SND_UNDEFINED)
9159 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9160 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9163 if (IS_LOOP_SOUND(sound))
9164 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9166 PlaySoundStereo(sound, stereo_position);
9169 void PlayMenuSoundIfLoopExt(int sound)
9171 if (sound == SND_UNDEFINED)
9174 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9175 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9178 if (IS_LOOP_SOUND(sound))
9179 PlaySoundLoop(sound);
9182 void PlayMenuSoundIfLoop(void)
9184 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9187 void PlayMenuMusicExt(int music)
9189 if (music == MUS_UNDEFINED)
9192 if (!setup.sound_music)
9195 if (IS_LOOP_MUSIC(music))
9196 PlayMusicLoop(music);
9201 void PlayMenuMusic(void)
9203 char *curr_music = getCurrentlyPlayingMusicFilename();
9204 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9206 if (!strEqual(curr_music, next_music))
9207 PlayMenuMusicExt(menu.music[game_status]);
9210 void PlayMenuSoundsAndMusic(void)
9216 static void FadeMenuSounds(void)
9221 static void FadeMenuMusic(void)
9223 char *curr_music = getCurrentlyPlayingMusicFilename();
9224 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9226 if (!strEqual(curr_music, next_music))
9230 void FadeMenuSoundsAndMusic(void)
9236 void PlaySoundActivating(void)
9239 PlaySound(SND_MENU_ITEM_ACTIVATING);
9243 void PlaySoundSelecting(void)
9246 PlaySound(SND_MENU_ITEM_SELECTING);
9250 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9252 boolean change_fullscreen = (setup.fullscreen !=
9253 video.fullscreen_enabled);
9254 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9255 setup.window_scaling_percent !=
9256 video.window_scaling_percent);
9258 if (change_window_scaling_percent && video.fullscreen_enabled)
9261 if (!change_window_scaling_percent && !video.fullscreen_available)
9264 if (change_window_scaling_percent)
9266 SDLSetWindowScaling(setup.window_scaling_percent);
9270 else if (change_fullscreen)
9272 SDLSetWindowFullscreen(setup.fullscreen);
9274 // set setup value according to successfully changed fullscreen mode
9275 setup.fullscreen = video.fullscreen_enabled;
9280 if (change_fullscreen ||
9281 change_window_scaling_percent)
9283 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9285 // save backbuffer content which gets lost when toggling fullscreen mode
9286 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9288 if (change_window_scaling_percent)
9290 // keep window mode, but change window scaling
9291 video.fullscreen_enabled = TRUE; // force new window scaling
9294 // toggle fullscreen
9295 ChangeVideoModeIfNeeded(setup.fullscreen);
9297 // set setup value according to successfully changed fullscreen mode
9298 setup.fullscreen = video.fullscreen_enabled;
9300 // restore backbuffer content from temporary backbuffer backup bitmap
9301 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9303 FreeBitmap(tmp_backbuffer);
9305 // update visible window/screen
9306 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9310 static void JoinRectangles(int *x, int *y, int *width, int *height,
9311 int x2, int y2, int width2, int height2)
9313 // do not join with "off-screen" rectangle
9314 if (x2 == -1 || y2 == -1)
9319 *width = MAX(*width, width2);
9320 *height = MAX(*height, height2);
9323 void SetAnimStatus(int anim_status_new)
9325 if (anim_status_new == GAME_MODE_MAIN)
9326 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9327 else if (anim_status_new == GAME_MODE_SCORES)
9328 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9330 global.anim_status_next = anim_status_new;
9332 // directly set screen modes that are entered without fading
9333 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9334 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9335 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9336 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9337 global.anim_status = global.anim_status_next;
9340 void SetGameStatus(int game_status_new)
9342 if (game_status_new != game_status)
9343 game_status_last_screen = game_status;
9345 game_status = game_status_new;
9347 SetAnimStatus(game_status_new);
9350 void SetFontStatus(int game_status_new)
9352 static int last_game_status = -1;
9354 if (game_status_new != -1)
9356 // set game status for font use after storing last game status
9357 last_game_status = game_status;
9358 game_status = game_status_new;
9362 // reset game status after font use from last stored game status
9363 game_status = last_game_status;
9367 void ResetFontStatus(void)
9372 void SetLevelSetInfo(char *identifier, int level_nr)
9374 setString(&levelset.identifier, identifier);
9376 levelset.level_nr = level_nr;
9379 boolean CheckIfAllViewportsHaveChanged(void)
9381 // if game status has not changed, viewports have not changed either
9382 if (game_status == game_status_last)
9385 // check if all viewports have changed with current game status
9387 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9388 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9389 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9390 int new_real_sx = vp_playfield->x;
9391 int new_real_sy = vp_playfield->y;
9392 int new_full_sxsize = vp_playfield->width;
9393 int new_full_sysize = vp_playfield->height;
9394 int new_dx = vp_door_1->x;
9395 int new_dy = vp_door_1->y;
9396 int new_dxsize = vp_door_1->width;
9397 int new_dysize = vp_door_1->height;
9398 int new_vx = vp_door_2->x;
9399 int new_vy = vp_door_2->y;
9400 int new_vxsize = vp_door_2->width;
9401 int new_vysize = vp_door_2->height;
9403 boolean playfield_viewport_has_changed =
9404 (new_real_sx != REAL_SX ||
9405 new_real_sy != REAL_SY ||
9406 new_full_sxsize != FULL_SXSIZE ||
9407 new_full_sysize != FULL_SYSIZE);
9409 boolean door_1_viewport_has_changed =
9412 new_dxsize != DXSIZE ||
9413 new_dysize != DYSIZE);
9415 boolean door_2_viewport_has_changed =
9418 new_vxsize != VXSIZE ||
9419 new_vysize != VYSIZE ||
9420 game_status_last == GAME_MODE_EDITOR);
9422 return (playfield_viewport_has_changed &&
9423 door_1_viewport_has_changed &&
9424 door_2_viewport_has_changed);
9427 boolean CheckFadeAll(void)
9429 return (CheckIfGlobalBorderHasChanged() ||
9430 CheckIfAllViewportsHaveChanged());
9433 void ChangeViewportPropertiesIfNeeded(void)
9435 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9436 FALSE : setup.small_game_graphics);
9437 int gfx_game_mode = game_status;
9438 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9440 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9441 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9442 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9443 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9444 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9445 int new_win_xsize = vp_window->width;
9446 int new_win_ysize = vp_window->height;
9447 int border_left = vp_playfield->border_left;
9448 int border_right = vp_playfield->border_right;
9449 int border_top = vp_playfield->border_top;
9450 int border_bottom = vp_playfield->border_bottom;
9451 int new_sx = vp_playfield->x + border_left;
9452 int new_sy = vp_playfield->y + border_top;
9453 int new_sxsize = vp_playfield->width - border_left - border_right;
9454 int new_sysize = vp_playfield->height - border_top - border_bottom;
9455 int new_real_sx = vp_playfield->x;
9456 int new_real_sy = vp_playfield->y;
9457 int new_full_sxsize = vp_playfield->width;
9458 int new_full_sysize = vp_playfield->height;
9459 int new_dx = vp_door_1->x;
9460 int new_dy = vp_door_1->y;
9461 int new_dxsize = vp_door_1->width;
9462 int new_dysize = vp_door_1->height;
9463 int new_vx = vp_door_2->x;
9464 int new_vy = vp_door_2->y;
9465 int new_vxsize = vp_door_2->width;
9466 int new_vysize = vp_door_2->height;
9467 int new_ex = vp_door_3->x;
9468 int new_ey = vp_door_3->y;
9469 int new_exsize = vp_door_3->width;
9470 int new_eysize = vp_door_3->height;
9471 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9472 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9473 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9474 int new_scr_fieldx = new_sxsize / tilesize;
9475 int new_scr_fieldy = new_sysize / tilesize;
9476 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9477 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9478 boolean init_gfx_buffers = FALSE;
9479 boolean init_video_buffer = FALSE;
9480 boolean init_gadgets_and_anims = FALSE;
9481 boolean init_em_graphics = FALSE;
9483 if (new_win_xsize != WIN_XSIZE ||
9484 new_win_ysize != WIN_YSIZE)
9486 WIN_XSIZE = new_win_xsize;
9487 WIN_YSIZE = new_win_ysize;
9489 init_video_buffer = TRUE;
9490 init_gfx_buffers = TRUE;
9491 init_gadgets_and_anims = TRUE;
9493 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9496 if (new_scr_fieldx != SCR_FIELDX ||
9497 new_scr_fieldy != SCR_FIELDY)
9499 // this always toggles between MAIN and GAME when using small tile size
9501 SCR_FIELDX = new_scr_fieldx;
9502 SCR_FIELDY = new_scr_fieldy;
9504 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9515 new_sxsize != SXSIZE ||
9516 new_sysize != SYSIZE ||
9517 new_dxsize != DXSIZE ||
9518 new_dysize != DYSIZE ||
9519 new_vxsize != VXSIZE ||
9520 new_vysize != VYSIZE ||
9521 new_exsize != EXSIZE ||
9522 new_eysize != EYSIZE ||
9523 new_real_sx != REAL_SX ||
9524 new_real_sy != REAL_SY ||
9525 new_full_sxsize != FULL_SXSIZE ||
9526 new_full_sysize != FULL_SYSIZE ||
9527 new_tilesize_var != TILESIZE_VAR
9530 // ------------------------------------------------------------------------
9531 // determine next fading area for changed viewport definitions
9532 // ------------------------------------------------------------------------
9534 // start with current playfield area (default fading area)
9537 FADE_SXSIZE = FULL_SXSIZE;
9538 FADE_SYSIZE = FULL_SYSIZE;
9540 // add new playfield area if position or size has changed
9541 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9542 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9544 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9545 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9548 // add current and new door 1 area if position or size has changed
9549 if (new_dx != DX || new_dy != DY ||
9550 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9552 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9553 DX, DY, DXSIZE, DYSIZE);
9554 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9555 new_dx, new_dy, new_dxsize, new_dysize);
9558 // add current and new door 2 area if position or size has changed
9559 if (new_vx != VX || new_vy != VY ||
9560 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9562 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9563 VX, VY, VXSIZE, VYSIZE);
9564 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9565 new_vx, new_vy, new_vxsize, new_vysize);
9568 // ------------------------------------------------------------------------
9569 // handle changed tile size
9570 // ------------------------------------------------------------------------
9572 if (new_tilesize_var != TILESIZE_VAR)
9574 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9576 // changing tile size invalidates scroll values of engine snapshots
9577 FreeEngineSnapshotSingle();
9579 // changing tile size requires update of graphic mapping for EM engine
9580 init_em_graphics = TRUE;
9591 SXSIZE = new_sxsize;
9592 SYSIZE = new_sysize;
9593 DXSIZE = new_dxsize;
9594 DYSIZE = new_dysize;
9595 VXSIZE = new_vxsize;
9596 VYSIZE = new_vysize;
9597 EXSIZE = new_exsize;
9598 EYSIZE = new_eysize;
9599 REAL_SX = new_real_sx;
9600 REAL_SY = new_real_sy;
9601 FULL_SXSIZE = new_full_sxsize;
9602 FULL_SYSIZE = new_full_sysize;
9603 TILESIZE_VAR = new_tilesize_var;
9605 init_gfx_buffers = TRUE;
9606 init_gadgets_and_anims = TRUE;
9608 // printf("::: viewports: init_gfx_buffers\n");
9609 // printf("::: viewports: init_gadgets_and_anims\n");
9612 if (init_gfx_buffers)
9614 // printf("::: init_gfx_buffers\n");
9616 SCR_FIELDX = new_scr_fieldx_buffers;
9617 SCR_FIELDY = new_scr_fieldy_buffers;
9621 SCR_FIELDX = new_scr_fieldx;
9622 SCR_FIELDY = new_scr_fieldy;
9624 SetDrawDeactivationMask(REDRAW_NONE);
9625 SetDrawBackgroundMask(REDRAW_FIELD);
9628 if (init_video_buffer)
9630 // printf("::: init_video_buffer\n");
9632 FreeAllImageTextures(); // needs old renderer to free the textures
9634 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9635 InitImageTextures();
9638 if (init_gadgets_and_anims)
9640 // printf("::: init_gadgets_and_anims\n");
9643 InitGlobalAnimations();
9646 if (init_em_graphics)
9648 InitGraphicInfo_EM();