1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 // currently not available
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 // never draw masked screen borders on borderless screens
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD(void)
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void DrawTileCursor(int draw_target)
634 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
639 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
644 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
647 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
652 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653 BlitScreenToBitmap_EM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655 BlitScreenToBitmap_SP(target_bitmap);
656 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657 BlitScreenToBitmap_MM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659 BlitScreenToBitmap_RND(target_bitmap);
661 redraw_mask |= REDRAW_FIELD;
664 static void DrawFramesPerSecond(void)
667 int font_nr = FONT_TEXT_2;
668 int font_width = getFontWidth(font_nr);
669 int draw_deactivation_mask = GetDrawDeactivationMask();
670 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
672 // draw FPS with leading space (needed if field buffer deactivated)
673 sprintf(text, " %04.1f fps", global.frames_per_second);
675 // override draw deactivation mask (required for invisible warp mode)
676 SetDrawDeactivationMask(REDRAW_NONE);
678 // draw opaque FPS if field buffer deactivated, else draw masked FPS
679 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
682 // set draw deactivation mask to previous value
683 SetDrawDeactivationMask(draw_deactivation_mask);
685 // force full-screen redraw in this frame
686 redraw_mask = REDRAW_ALL;
690 static void PrintFrameTimeDebugging(void)
692 static unsigned int last_counter = 0;
693 unsigned int counter = Counter();
694 int diff_1 = counter - last_counter;
695 int diff_2 = diff_1 - GAME_FRAME_DELAY;
697 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698 char diff_bar[2 * diff_2_max + 5];
702 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
704 for (i = 0; i < diff_2_max; i++)
705 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706 i >= diff_2_max - diff_2_cut ? '-' : ' ');
708 diff_bar[pos++] = '|';
710 for (i = 0; i < diff_2_max; i++)
711 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
713 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
715 diff_bar[pos++] = '\0';
717 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
720 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
723 last_counter = counter;
727 static int unifiedRedrawMask(int mask)
729 if (mask & REDRAW_ALL)
732 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
740 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
743 void BackToFront(void)
745 static int last_redraw_mask = REDRAW_NONE;
747 // force screen redraw in every frame to continue drawing global animations
748 // (but always use the last redraw mask to prevent unwanted side effects)
749 if (redraw_mask == REDRAW_NONE)
750 redraw_mask = last_redraw_mask;
752 last_redraw_mask = redraw_mask;
755 // masked border now drawn immediately when blitting backbuffer to window
757 // draw masked border to all viewports, if defined
758 DrawMaskedBorder(redraw_mask);
761 // draw frames per second (only if debug mode is enabled)
762 if (redraw_mask & REDRAW_FPS)
763 DrawFramesPerSecond();
765 // remove playfield redraw before potentially merging with doors redraw
766 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767 redraw_mask &= ~REDRAW_FIELD;
769 // redraw complete window if both playfield and (some) doors need redraw
770 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771 redraw_mask = REDRAW_ALL;
773 /* although redrawing the whole window would be fine for normal gameplay,
774 being able to only redraw the playfield is required for deactivating
775 certain drawing areas (mainly playfield) to work, which is needed for
776 warp-forward to be fast enough (by skipping redraw of most frames) */
778 if (redraw_mask & REDRAW_ALL)
780 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
782 else if (redraw_mask & REDRAW_FIELD)
784 BlitBitmap(backbuffer, window,
785 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
787 else if (redraw_mask & REDRAW_DOORS)
789 // merge door areas to prevent calling screen redraw more than once
795 if (redraw_mask & REDRAW_DOOR_1)
799 x2 = MAX(x2, DX + DXSIZE);
800 y2 = MAX(y2, DY + DYSIZE);
803 if (redraw_mask & REDRAW_DOOR_2)
807 x2 = MAX(x2, VX + VXSIZE);
808 y2 = MAX(y2, VY + VYSIZE);
811 if (redraw_mask & REDRAW_DOOR_3)
815 x2 = MAX(x2, EX + EXSIZE);
816 y2 = MAX(y2, EY + EYSIZE);
819 // make sure that at least one pixel is blitted, and inside the screen
820 // (else nothing is blitted, causing the animations not to be updated)
821 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823 x2 = MIN(MAX(1, x2), WIN_XSIZE);
824 y2 = MIN(MAX(1, y2), WIN_YSIZE);
826 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
829 redraw_mask = REDRAW_NONE;
832 PrintFrameTimeDebugging();
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
838 unsigned int frame_delay_value_old = GetVideoFrameDelay();
840 SetVideoFrameDelay(frame_delay_value);
844 SetVideoFrameDelay(frame_delay_value_old);
847 static int fade_type_skip = FADE_TYPE_NONE;
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
851 void (*draw_border_function)(void) = NULL;
852 int x, y, width, height;
853 int fade_delay, post_delay;
855 if (fade_type == FADE_TYPE_FADE_OUT)
857 if (fade_type_skip != FADE_TYPE_NONE)
859 // skip all fade operations until specified fade operation
860 if (fade_type & fade_type_skip)
861 fade_type_skip = FADE_TYPE_NONE;
866 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
870 redraw_mask |= fade_mask;
872 if (fade_type == FADE_TYPE_SKIP)
874 fade_type_skip = fade_mode;
879 fade_delay = fading.fade_delay;
880 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
882 if (fade_type_skip != FADE_TYPE_NONE)
884 // skip all fade operations until specified fade operation
885 if (fade_type & fade_type_skip)
886 fade_type_skip = FADE_TYPE_NONE;
891 if (global.autoplay_leveldir)
896 if (fade_mask == REDRAW_FIELD)
901 height = FADE_SYSIZE;
903 if (border.draw_masked_when_fading)
904 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
906 DrawMaskedBorder_FIELD(); // draw once
916 // when switching screens without fading, set fade delay to zero
917 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
920 // do not display black frame when fading out without fade delay
921 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
924 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925 draw_border_function);
927 redraw_mask &= ~fade_mask;
929 ClearAutoRepeatKeyEvents();
932 static void SetScreenStates_BeforeFadingIn(void)
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
940 // set screen mode for animations back to fading
941 global.anim_status = GAME_MODE_PSEUDO_FADING;
944 static void SetScreenStates_AfterFadingIn(void)
946 // store new source screen (to use correct masked border for fading)
947 gfx.fade_border_source_status = global.border_status;
949 global.anim_status = global.anim_status_next;
952 static void SetScreenStates_BeforeFadingOut(void)
954 // store new target screen (to use correct masked border for fading)
955 gfx.fade_border_target_status = game_status;
957 // set screen mode for animations to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 // store backbuffer with all animations that will be stopped for fading out
961 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
964 static void SetScreenStates_AfterFadingOut(void)
966 global.border_status = game_status;
969 void FadeIn(int fade_mask)
971 SetScreenStates_BeforeFadingIn();
974 DrawMaskedBorder(REDRAW_ALL);
977 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
980 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
984 FADE_SXSIZE = FULL_SXSIZE;
985 FADE_SYSIZE = FULL_SYSIZE;
987 // activate virtual buttons depending on upcoming game status
988 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989 game_status == GAME_MODE_PLAYING && !tape.playing)
990 SetOverlayActive(TRUE);
992 SetScreenStates_AfterFadingIn();
994 // force update of global animation status in case of rapid screen changes
995 redraw_mask = REDRAW_ALL;
999 void FadeOut(int fade_mask)
1001 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1006 SetScreenStates_BeforeFadingOut();
1008 SetTileCursorActive(FALSE);
1009 SetOverlayActive(FALSE);
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1018 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1020 SetScreenStates_AfterFadingOut();
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1025 static struct TitleFadingInfo fading_leave_stored;
1028 fading_leave_stored = fading_leave;
1030 fading = fading_leave_stored;
1033 void FadeSetEnterMenu(void)
1035 fading = menu.enter_menu;
1037 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1040 void FadeSetLeaveMenu(void)
1042 fading = menu.leave_menu;
1044 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetEnterScreen(void)
1049 fading = menu.enter_screen[game_status];
1051 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1054 void FadeSetNextScreen(void)
1056 fading = menu.next_screen[game_status];
1058 // (do not overwrite fade mode set by FadeSetEnterScreen)
1059 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1062 void FadeSetLeaveScreen(void)
1064 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1067 void FadeSetFromType(int type)
1069 if (type & TYPE_ENTER_SCREEN)
1070 FadeSetEnterScreen();
1071 else if (type & TYPE_ENTER)
1073 else if (type & TYPE_LEAVE)
1077 void FadeSetDisabled(void)
1079 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1081 fading = fading_none;
1084 void FadeSkipNextFadeIn(void)
1086 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1089 void FadeSkipNextFadeOut(void)
1091 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1096 if (graphic == IMG_UNDEFINED)
1099 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1101 return (graphic_info[graphic].bitmap != NULL || redefined ?
1102 graphic_info[graphic].bitmap :
1103 graphic_info[default_graphic].bitmap);
1106 static Bitmap *getBackgroundBitmap(int graphic)
1108 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1113 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1119 (status == GAME_MODE_MAIN ||
1120 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1121 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1122 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1123 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1126 return getGlobalBorderBitmap(graphic);
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1131 if (graphic_info[graphic].bitmap)
1132 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1135 void SetMainBackgroundImageIfDefined(int graphic)
1137 if (graphic_info[graphic].bitmap)
1138 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1143 if (graphic_info[graphic].bitmap)
1144 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1147 void SetWindowBackgroundImage(int graphic)
1149 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetMainBackgroundImage(int graphic)
1154 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetDoorBackgroundImage(int graphic)
1159 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetPanelBackground(void)
1164 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1166 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1169 SetDoorBackgroundBitmap(bitmap_db_panel);
1172 void DrawBackground(int x, int y, int width, int height)
1174 // "drawto" might still point to playfield buffer here (hall of fame)
1175 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1177 if (IN_GFX_FIELD_FULL(x, y))
1178 redraw_mask |= REDRAW_FIELD;
1179 else if (IN_GFX_DOOR_1(x, y))
1180 redraw_mask |= REDRAW_DOOR_1;
1181 else if (IN_GFX_DOOR_2(x, y))
1182 redraw_mask |= REDRAW_DOOR_2;
1183 else if (IN_GFX_DOOR_3(x, y))
1184 redraw_mask |= REDRAW_DOOR_3;
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1189 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1191 if (font->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1199 struct GraphicInfo *g = &graphic_info[graphic];
1201 if (g->bitmap == NULL)
1204 DrawBackground(x, y, width, height);
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1219 boolean CheckIfGlobalBorderHasChanged(void)
1221 // if game status has not changed, global border has not changed either
1222 if (game_status == game_status_last)
1225 // determine and store new global border bitmap for current game status
1226 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1228 return (global_border_bitmap_last != global_border_bitmap);
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1236 // if game status has not changed, nothing has to be redrawn
1237 if (game_status == game_status_last)
1240 // redraw if last screen was title screen
1241 if (game_status_last == GAME_MODE_TITLE)
1244 // redraw if global screen border has changed
1245 if (CheckIfGlobalBorderHasChanged())
1248 // redraw if position or size of playfield area has changed
1249 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1253 // redraw if position or size of door area has changed
1254 if (dx_last != DX || dy_last != DY ||
1255 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1258 // redraw if position or size of tape area has changed
1259 if (vx_last != VX || vy_last != VY ||
1260 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1263 // redraw if position or size of editor area has changed
1264 if (ex_last != EX || ey_last != EY ||
1265 exsize_last != EXSIZE || eysize_last != EYSIZE)
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1275 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1277 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1280 void RedrawGlobalBorder(void)
1282 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 RedrawGlobalBorderFromBitmap(bitmap);
1286 redraw_mask = REDRAW_ALL;
1289 static void RedrawGlobalBorderIfNeeded(void)
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 if (game_status == game_status_last)
1296 // copy current draw buffer to later copy back areas that have not changed
1297 if (game_status_last != GAME_MODE_TITLE)
1298 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301 if (CheckIfGlobalBorderRedrawIsNeeded())
1303 // determine and store new global border bitmap for current game status
1304 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1307 // redraw global screen border (or clear, if defined to be empty)
1308 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1310 if (game_status == GAME_MODE_EDITOR)
1311 DrawSpecialEditorDoor();
1313 // copy previous playfield and door areas, if they are defined on both
1314 // previous and current screen and if they still have the same size
1316 if (real_sx_last != -1 && real_sy_last != -1 &&
1317 REAL_SX != -1 && REAL_SY != -1 &&
1318 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1323 if (dx_last != -1 && dy_last != -1 &&
1324 DX != -1 && DY != -1 &&
1325 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326 BlitBitmap(bitmap_db_store_1, backbuffer,
1327 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1329 if (game_status != GAME_MODE_EDITOR)
1331 if (vx_last != -1 && vy_last != -1 &&
1332 VX != -1 && VY != -1 &&
1333 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334 BlitBitmap(bitmap_db_store_1, backbuffer,
1335 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1339 if (ex_last != -1 && ey_last != -1 &&
1340 EX != -1 && EY != -1 &&
1341 exsize_last == EXSIZE && eysize_last == EYSIZE)
1342 BlitBitmap(bitmap_db_store_1, backbuffer,
1343 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1346 redraw_mask = REDRAW_ALL;
1349 game_status_last = game_status;
1351 global_border_bitmap_last = global_border_bitmap;
1353 real_sx_last = REAL_SX;
1354 real_sy_last = REAL_SY;
1355 full_sxsize_last = FULL_SXSIZE;
1356 full_sysize_last = FULL_SYSIZE;
1359 dxsize_last = DXSIZE;
1360 dysize_last = DYSIZE;
1363 vxsize_last = VXSIZE;
1364 vysize_last = VYSIZE;
1367 exsize_last = EXSIZE;
1368 eysize_last = EYSIZE;
1371 void ClearField(void)
1373 RedrawGlobalBorderIfNeeded();
1375 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376 // (when entering hall of fame after playing)
1377 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1379 // !!! maybe this should be done before clearing the background !!!
1380 if (game_status == GAME_MODE_PLAYING)
1382 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1387 SetDrawtoField(DRAW_TO_BACKBUFFER);
1391 void MarkTileDirty(int x, int y)
1393 redraw_mask |= REDRAW_FIELD;
1396 void SetBorderElement(void)
1400 BorderElement = EL_EMPTY;
1402 // only the R'n'D game engine may use an additional steelwall border
1403 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1406 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1408 for (x = 0; x < lev_fieldx; x++)
1410 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411 BorderElement = EL_STEELWALL;
1413 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1419 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1420 int max_array_fieldx, int max_array_fieldy,
1421 short field[max_array_fieldx][max_array_fieldy],
1422 int max_fieldx, int max_fieldy)
1426 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1427 static int safety = 0;
1429 // check if starting field still has the desired content
1430 if (field[from_x][from_y] == fill_element)
1435 if (safety > max_fieldx * max_fieldy)
1436 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1438 old_element = field[from_x][from_y];
1439 field[from_x][from_y] = fill_element;
1441 for (i = 0; i < 4; i++)
1443 x = from_x + check[i][0];
1444 y = from_y + check[i][1];
1446 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1447 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1448 field, max_fieldx, max_fieldy);
1454 void FloodFillLevel(int from_x, int from_y, int fill_element,
1455 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1456 int max_fieldx, int max_fieldy)
1458 FloodFillLevelExt(from_x, from_y, fill_element,
1459 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1460 max_fieldx, max_fieldy);
1463 void SetRandomAnimationValue(int x, int y)
1465 gfx.anim_random_frame = GfxRandom[x][y];
1468 int getGraphicAnimationFrame(int graphic, int sync_frame)
1470 // animation synchronized with global frame counter, not move position
1471 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1472 sync_frame = FrameCounter;
1474 return getAnimationFrame(graphic_info[graphic].anim_frames,
1475 graphic_info[graphic].anim_delay,
1476 graphic_info[graphic].anim_mode,
1477 graphic_info[graphic].anim_start_frame,
1481 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1483 struct GraphicInfo *g = &graphic_info[graphic];
1484 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1486 if (tilesize == gfx.standard_tile_size)
1487 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1488 else if (tilesize == game.tile_size)
1489 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1491 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1494 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1495 boolean get_backside)
1497 struct GraphicInfo *g = &graphic_info[graphic];
1498 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1499 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1501 if (g->offset_y == 0) // frames are ordered horizontally
1503 int max_width = g->anim_frames_per_line * g->width;
1504 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1506 *x = pos % max_width;
1507 *y = src_y % g->height + pos / max_width * g->height;
1509 else if (g->offset_x == 0) // frames are ordered vertically
1511 int max_height = g->anim_frames_per_line * g->height;
1512 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1514 *x = src_x % g->width + pos / max_height * g->width;
1515 *y = pos % max_height;
1517 else // frames are ordered diagonally
1519 *x = src_x + frame * g->offset_x;
1520 *y = src_y + frame * g->offset_y;
1524 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1525 Bitmap **bitmap, int *x, int *y,
1526 boolean get_backside)
1528 struct GraphicInfo *g = &graphic_info[graphic];
1530 // if no graphics defined at all, use fallback graphics
1531 if (g->bitmaps == NULL)
1532 *g = graphic_info[IMG_CHAR_EXCLAM];
1534 // if no in-game graphics defined, always use standard graphic size
1535 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1536 tilesize = TILESIZE;
1538 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1539 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1541 *x = *x * tilesize / g->tile_size;
1542 *y = *y * tilesize / g->tile_size;
1545 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1546 Bitmap **bitmap, int *x, int *y)
1548 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1551 void getFixedGraphicSource(int graphic, int frame,
1552 Bitmap **bitmap, int *x, int *y)
1554 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1557 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1559 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1562 void getGlobalAnimGraphicSource(int graphic, int frame,
1563 Bitmap **bitmap, int *x, int *y)
1565 struct GraphicInfo *g = &graphic_info[graphic];
1567 // if no graphics defined at all, use fallback graphics
1568 if (g->bitmaps == NULL)
1569 *g = graphic_info[IMG_CHAR_EXCLAM];
1571 // use original size graphics, if existing, else use standard size graphics
1572 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1573 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1575 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1577 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1580 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1581 int *x, int *y, boolean get_backside)
1583 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1587 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1589 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1592 void DrawGraphic(int x, int y, int graphic, int frame)
1595 if (!IN_SCR_FIELD(x, y))
1597 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1598 Debug("draw:DrawGraphic", "This should never happen!");
1604 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1607 MarkTileDirty(x, y);
1610 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1613 if (!IN_SCR_FIELD(x, y))
1615 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1617 Debug("draw:DrawFixedGraphic", "This should never happen!");
1623 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1625 MarkTileDirty(x, y);
1628 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1634 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1636 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1639 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1645 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1646 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1649 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1652 if (!IN_SCR_FIELD(x, y))
1654 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1656 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1662 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1665 MarkTileDirty(x, y);
1668 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1671 if (!IN_SCR_FIELD(x, y))
1673 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1675 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1681 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1683 MarkTileDirty(x, y);
1686 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1692 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1694 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1698 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1699 int graphic, int frame)
1704 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1706 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1710 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1712 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1714 MarkTileDirty(x / tilesize, y / tilesize);
1717 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1720 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1721 graphic, frame, tilesize);
1722 MarkTileDirty(x / tilesize, y / tilesize);
1725 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1731 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1732 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1735 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1736 int frame, int tilesize)
1741 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1742 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1745 void DrawMiniGraphic(int x, int y, int graphic)
1747 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1748 MarkTileDirty(x / 2, y / 2);
1751 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1756 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1757 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1760 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1761 int graphic, int frame,
1762 int cut_mode, int mask_mode)
1767 int width = TILEX, height = TILEY;
1770 if (dx || dy) // shifted graphic
1772 if (x < BX1) // object enters playfield from the left
1779 else if (x > BX2) // object enters playfield from the right
1785 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1791 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1793 else if (dx) // general horizontal movement
1794 MarkTileDirty(x + SIGN(dx), y);
1796 if (y < BY1) // object enters playfield from the top
1798 if (cut_mode == CUT_BELOW) // object completely above top border
1806 else if (y > BY2) // object enters playfield from the bottom
1812 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1818 else if (dy > 0 && cut_mode == CUT_ABOVE)
1820 if (y == BY2) // object completely above bottom border
1826 MarkTileDirty(x, y + 1);
1827 } // object leaves playfield to the bottom
1828 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1830 else if (dy) // general vertical movement
1831 MarkTileDirty(x, y + SIGN(dy));
1835 if (!IN_SCR_FIELD(x, y))
1837 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1839 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1845 width = width * TILESIZE_VAR / TILESIZE;
1846 height = height * TILESIZE_VAR / TILESIZE;
1847 cx = cx * TILESIZE_VAR / TILESIZE;
1848 cy = cy * TILESIZE_VAR / TILESIZE;
1849 dx = dx * TILESIZE_VAR / TILESIZE;
1850 dy = dy * TILESIZE_VAR / TILESIZE;
1852 if (width > 0 && height > 0)
1854 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1859 dst_x = FX + x * TILEX_VAR + dx;
1860 dst_y = FY + y * TILEY_VAR + dy;
1862 if (mask_mode == USE_MASKING)
1863 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1866 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1869 MarkTileDirty(x, y);
1873 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1874 int graphic, int frame,
1875 int cut_mode, int mask_mode)
1880 int width = TILEX_VAR, height = TILEY_VAR;
1883 int x2 = x + SIGN(dx);
1884 int y2 = y + SIGN(dy);
1886 // movement with two-tile animations must be sync'ed with movement position,
1887 // not with current GfxFrame (which can be higher when using slow movement)
1888 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1889 int anim_frames = graphic_info[graphic].anim_frames;
1891 // (we also need anim_delay here for movement animations with less frames)
1892 int anim_delay = graphic_info[graphic].anim_delay;
1893 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1895 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1896 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1898 // re-calculate animation frame for two-tile movement animation
1899 frame = getGraphicAnimationFrame(graphic, sync_frame);
1901 // check if movement start graphic inside screen area and should be drawn
1902 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1904 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1906 dst_x = FX + x1 * TILEX_VAR;
1907 dst_y = FY + y1 * TILEY_VAR;
1909 if (mask_mode == USE_MASKING)
1910 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1913 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1916 MarkTileDirty(x1, y1);
1919 // check if movement end graphic inside screen area and should be drawn
1920 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1922 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1924 dst_x = FX + x2 * TILEX_VAR;
1925 dst_y = FY + y2 * TILEY_VAR;
1927 if (mask_mode == USE_MASKING)
1928 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1931 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1934 MarkTileDirty(x2, y2);
1938 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1939 int graphic, int frame,
1940 int cut_mode, int mask_mode)
1944 DrawGraphic(x, y, graphic, frame);
1949 if (graphic_info[graphic].double_movement) // EM style movement images
1950 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1952 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1955 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1956 int graphic, int frame, int cut_mode)
1958 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1961 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1962 int cut_mode, int mask_mode)
1964 int lx = LEVELX(x), ly = LEVELY(y);
1968 if (IN_LEV_FIELD(lx, ly))
1970 SetRandomAnimationValue(lx, ly);
1972 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1973 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1975 // do not use double (EM style) movement graphic when not moving
1976 if (graphic_info[graphic].double_movement && !dx && !dy)
1978 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1979 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1982 if (game.use_masked_elements && (dx || dy))
1983 mask_mode = USE_MASKING;
1985 else // border element
1987 graphic = el2img(element);
1988 frame = getGraphicAnimationFrame(graphic, -1);
1991 if (element == EL_EXPANDABLE_WALL)
1993 boolean left_stopped = FALSE, right_stopped = FALSE;
1995 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1996 left_stopped = TRUE;
1997 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
1998 right_stopped = TRUE;
2000 if (left_stopped && right_stopped)
2002 else if (left_stopped)
2004 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2005 frame = graphic_info[graphic].anim_frames - 1;
2007 else if (right_stopped)
2009 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2010 frame = graphic_info[graphic].anim_frames - 1;
2015 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2016 else if (mask_mode == USE_MASKING)
2017 DrawGraphicThruMask(x, y, graphic, frame);
2019 DrawGraphic(x, y, graphic, frame);
2022 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2023 int cut_mode, int mask_mode)
2025 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2026 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2027 cut_mode, mask_mode);
2030 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2033 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2036 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2039 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2042 void DrawLevelElementThruMask(int x, int y, int element)
2044 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2047 void DrawLevelFieldThruMask(int x, int y)
2049 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2052 // !!! implementation of quicksand is totally broken !!!
2053 #define IS_CRUMBLED_TILE(x, y, e) \
2054 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2055 !IS_MOVING(x, y) || \
2056 (e) == EL_QUICKSAND_EMPTYING || \
2057 (e) == EL_QUICKSAND_FAST_EMPTYING))
2059 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2064 int width, height, cx, cy;
2065 int sx = SCREENX(x), sy = SCREENY(y);
2066 int crumbled_border_size = graphic_info[graphic].border_size;
2067 int crumbled_tile_size = graphic_info[graphic].tile_size;
2068 int crumbled_border_size_var =
2069 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2072 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2074 for (i = 1; i < 4; i++)
2076 int dxx = (i & 1 ? dx : 0);
2077 int dyy = (i & 2 ? dy : 0);
2080 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2083 // check if neighbour field is of same crumble type
2084 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2085 graphic_info[graphic].class ==
2086 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2088 // return if check prevents inner corner
2089 if (same == (dxx == dx && dyy == dy))
2093 // if we reach this point, we have an inner corner
2095 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2097 width = crumbled_border_size_var;
2098 height = crumbled_border_size_var;
2099 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2100 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2102 if (game.use_masked_elements)
2104 int graphic0 = el2img(EL_EMPTY);
2105 int frame0 = getGraphicAnimationFrame(graphic0, GfxFrame[x][y]);
2106 Bitmap *src_bitmap0;
2109 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2111 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2113 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2115 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2117 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2120 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2122 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2125 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2130 int width, height, bx, by, cx, cy;
2131 int sx = SCREENX(x), sy = SCREENY(y);
2132 int crumbled_border_size = graphic_info[graphic].border_size;
2133 int crumbled_tile_size = graphic_info[graphic].tile_size;
2134 int crumbled_border_size_var =
2135 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2136 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2139 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2141 // only needed when using masked elements
2142 int graphic0 = el2img(EL_EMPTY);
2143 int frame0 = getGraphicAnimationFrame(graphic0, GfxFrame[x][y]);
2144 Bitmap *src_bitmap0;
2147 if (game.use_masked_elements)
2148 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2150 // draw simple, sloppy, non-corner-accurate crumbled border
2152 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2153 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2154 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2155 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2157 if (game.use_masked_elements)
2159 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2161 FX + sx * TILEX_VAR + cx,
2162 FY + sy * TILEY_VAR + cy);
2164 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2166 FX + sx * TILEX_VAR + cx,
2167 FY + sy * TILEY_VAR + cy);
2170 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2172 FX + sx * TILEX_VAR + cx,
2173 FY + sy * TILEY_VAR + cy);
2175 // (remaining middle border part must be at least as big as corner part)
2176 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2177 crumbled_border_size_var >= TILESIZE_VAR / 3)
2180 // correct corners of crumbled border, if needed
2182 for (i = -1; i <= 1; i += 2)
2184 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2185 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2186 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2189 // check if neighbour field is of same crumble type
2190 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2191 graphic_info[graphic].class ==
2192 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2194 // no crumbled corner, but continued crumbled border
2196 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2197 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2198 int b1 = (i == 1 ? crumbled_border_size_var :
2199 TILESIZE_VAR - 2 * crumbled_border_size_var);
2201 width = crumbled_border_size_var;
2202 height = crumbled_border_size_var;
2204 if (dir == 1 || dir == 2)
2219 if (game.use_masked_elements)
2221 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2223 FX + sx * TILEX_VAR + cx,
2224 FY + sy * TILEY_VAR + cy);
2226 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2228 FX + sx * TILEX_VAR + cx,
2229 FY + sy * TILEY_VAR + cy);
2232 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2234 FX + sx * TILEX_VAR + cx,
2235 FY + sy * TILEY_VAR + cy);
2240 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2242 int sx = SCREENX(x), sy = SCREENY(y);
2245 static int xy[4][2] =
2253 if (!IN_LEV_FIELD(x, y))
2256 element = TILE_GFX_ELEMENT(x, y);
2258 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2260 if (!IN_SCR_FIELD(sx, sy))
2263 // crumble field borders towards direct neighbour fields
2264 for (i = 0; i < 4; i++)
2266 int xx = x + xy[i][0];
2267 int yy = y + xy[i][1];
2269 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2272 // check if neighbour field is of same crumble type
2273 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2274 graphic_info[graphic].class ==
2275 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2278 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2281 // crumble inner field corners towards corner neighbour fields
2282 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2283 graphic_info[graphic].anim_frames == 2)
2285 for (i = 0; i < 4; i++)
2287 int dx = (i & 1 ? +1 : -1);
2288 int dy = (i & 2 ? +1 : -1);
2290 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2294 MarkTileDirty(sx, sy);
2296 else // center field is not crumbled -- crumble neighbour fields
2298 // crumble field borders of direct neighbour fields
2299 for (i = 0; i < 4; i++)
2301 int xx = x + xy[i][0];
2302 int yy = y + xy[i][1];
2303 int sxx = sx + xy[i][0];
2304 int syy = sy + xy[i][1];
2306 if (!IN_LEV_FIELD(xx, yy) ||
2307 !IN_SCR_FIELD(sxx, syy))
2310 // do not crumble fields that are being digged or snapped
2311 if (Tile[xx][yy] == EL_EMPTY ||
2312 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2315 element = TILE_GFX_ELEMENT(xx, yy);
2317 if (!IS_CRUMBLED_TILE(xx, yy, element))
2320 graphic = el_act2crm(element, ACTION_DEFAULT);
2322 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2324 MarkTileDirty(sxx, syy);
2327 // crumble inner field corners of corner neighbour fields
2328 for (i = 0; i < 4; i++)
2330 int dx = (i & 1 ? +1 : -1);
2331 int dy = (i & 2 ? +1 : -1);
2337 if (!IN_LEV_FIELD(xx, yy) ||
2338 !IN_SCR_FIELD(sxx, syy))
2341 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2344 element = TILE_GFX_ELEMENT(xx, yy);
2346 if (!IS_CRUMBLED_TILE(xx, yy, element))
2349 graphic = el_act2crm(element, ACTION_DEFAULT);
2351 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2352 graphic_info[graphic].anim_frames == 2)
2353 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2355 MarkTileDirty(sxx, syy);
2360 void DrawLevelFieldCrumbled(int x, int y)
2364 if (!IN_LEV_FIELD(x, y))
2367 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2368 GfxElement[x][y] != EL_UNDEFINED &&
2369 GFX_CRUMBLED(GfxElement[x][y]))
2371 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2376 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2378 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2381 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2384 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2385 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2386 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2387 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2388 int sx = SCREENX(x), sy = SCREENY(y);
2390 DrawScreenGraphic(sx, sy, graphic1, frame1);
2391 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2394 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2396 int sx = SCREENX(x), sy = SCREENY(y);
2397 static int xy[4][2] =
2406 // crumble direct neighbour fields (required for field borders)
2407 for (i = 0; i < 4; i++)
2409 int xx = x + xy[i][0];
2410 int yy = y + xy[i][1];
2411 int sxx = sx + xy[i][0];
2412 int syy = sy + xy[i][1];
2414 if (!IN_LEV_FIELD(xx, yy) ||
2415 !IN_SCR_FIELD(sxx, syy) ||
2416 !GFX_CRUMBLED(Tile[xx][yy]) ||
2420 DrawLevelField(xx, yy);
2423 // crumble corner neighbour fields (required for inner field corners)
2424 for (i = 0; i < 4; i++)
2426 int dx = (i & 1 ? +1 : -1);
2427 int dy = (i & 2 ? +1 : -1);
2433 if (!IN_LEV_FIELD(xx, yy) ||
2434 !IN_SCR_FIELD(sxx, syy) ||
2435 !GFX_CRUMBLED(Tile[xx][yy]) ||
2439 int element = TILE_GFX_ELEMENT(xx, yy);
2440 int graphic = el_act2crm(element, ACTION_DEFAULT);
2442 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2443 graphic_info[graphic].anim_frames == 2)
2444 DrawLevelField(xx, yy);
2448 static int getBorderElement(int x, int y)
2452 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2453 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2454 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2455 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2456 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2457 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2458 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2460 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2461 int steel_position = (x == -1 && y == -1 ? 0 :
2462 x == lev_fieldx && y == -1 ? 1 :
2463 x == -1 && y == lev_fieldy ? 2 :
2464 x == lev_fieldx && y == lev_fieldy ? 3 :
2465 x == -1 || x == lev_fieldx ? 4 :
2466 y == -1 || y == lev_fieldy ? 5 : 6);
2468 return border[steel_position][steel_type];
2471 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2473 if (game.use_masked_elements)
2475 if (graphic != el2img(EL_EMPTY))
2476 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2478 DrawGraphicThruMask(x, y, graphic, frame);
2482 DrawGraphic(x, y, graphic, frame);
2486 void DrawScreenElement(int x, int y, int element)
2488 int mask_mode = NO_MASKING;
2490 if (game.use_masked_elements)
2492 int lx = LEVELX(x), ly = LEVELY(y);
2494 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2496 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2498 mask_mode = USE_MASKING;
2502 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2503 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2506 void DrawLevelElement(int x, int y, int element)
2508 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2509 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2512 void DrawScreenField(int x, int y)
2514 int lx = LEVELX(x), ly = LEVELY(y);
2515 int element, content;
2517 if (!IN_LEV_FIELD(lx, ly))
2519 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2522 element = getBorderElement(lx, ly);
2524 DrawScreenElement(x, y, element);
2529 element = Tile[lx][ly];
2530 content = Store[lx][ly];
2532 if (IS_MOVING(lx, ly))
2534 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2535 boolean cut_mode = NO_CUTTING;
2537 if (element == EL_QUICKSAND_EMPTYING ||
2538 element == EL_QUICKSAND_FAST_EMPTYING ||
2539 element == EL_MAGIC_WALL_EMPTYING ||
2540 element == EL_BD_MAGIC_WALL_EMPTYING ||
2541 element == EL_DC_MAGIC_WALL_EMPTYING ||
2542 element == EL_AMOEBA_DROPPING)
2543 cut_mode = CUT_ABOVE;
2544 else if (element == EL_QUICKSAND_FILLING ||
2545 element == EL_QUICKSAND_FAST_FILLING ||
2546 element == EL_MAGIC_WALL_FILLING ||
2547 element == EL_BD_MAGIC_WALL_FILLING ||
2548 element == EL_DC_MAGIC_WALL_FILLING)
2549 cut_mode = CUT_BELOW;
2551 if (cut_mode == CUT_ABOVE)
2552 DrawScreenElement(x, y, element);
2554 DrawScreenElement(x, y, EL_EMPTY);
2556 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2558 int dir = MovDir[lx][ly];
2559 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2560 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2562 if (IN_SCR_FIELD(newx, newy))
2563 DrawScreenElement(newx, newy, EL_EMPTY);
2567 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2568 else if (cut_mode == NO_CUTTING)
2569 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2572 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2574 if (cut_mode == CUT_BELOW &&
2575 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2576 DrawLevelElement(lx, ly + 1, element);
2579 if (content == EL_ACID)
2581 int dir = MovDir[lx][ly];
2582 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2583 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2585 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2587 // prevent target field from being drawn again (but without masking)
2588 // (this would happen if target field is scanned after moving element)
2589 Stop[newlx][newly] = TRUE;
2592 else if (IS_BLOCKED(lx, ly))
2597 boolean cut_mode = NO_CUTTING;
2598 int element_old, content_old;
2600 Blocked2Moving(lx, ly, &oldx, &oldy);
2603 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2604 MovDir[oldx][oldy] == MV_RIGHT);
2606 element_old = Tile[oldx][oldy];
2607 content_old = Store[oldx][oldy];
2609 if (element_old == EL_QUICKSAND_EMPTYING ||
2610 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2611 element_old == EL_MAGIC_WALL_EMPTYING ||
2612 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2613 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2614 element_old == EL_AMOEBA_DROPPING)
2615 cut_mode = CUT_ABOVE;
2617 DrawScreenElement(x, y, EL_EMPTY);
2620 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2622 else if (cut_mode == NO_CUTTING)
2623 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2626 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2629 else if (IS_DRAWABLE(element))
2630 DrawScreenElement(x, y, element);
2632 DrawScreenElement(x, y, EL_EMPTY);
2635 void DrawLevelField(int x, int y)
2637 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2638 DrawScreenField(SCREENX(x), SCREENY(y));
2639 else if (IS_MOVING(x, y))
2643 Moving2Blocked(x, y, &newx, &newy);
2644 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2645 DrawScreenField(SCREENX(newx), SCREENY(newy));
2647 else if (IS_BLOCKED(x, y))
2651 Blocked2Moving(x, y, &oldx, &oldy);
2652 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2653 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2657 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2658 int (*el2img_function)(int), boolean masked,
2659 int element_bits_draw)
2661 int element_base = map_mm_wall_element(element);
2662 int element_bits = (IS_DF_WALL(element) ?
2663 element - EL_DF_WALL_START :
2664 IS_MM_WALL(element) ?
2665 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2666 int graphic = el2img_function(element_base);
2667 int tilesize_draw = tilesize / 2;
2672 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2674 for (i = 0; i < 4; i++)
2676 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2677 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2679 if (!(element_bits_draw & (1 << i)))
2682 if (element_bits & (1 << i))
2685 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2686 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2688 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2689 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2694 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2695 tilesize_draw, tilesize_draw);
2700 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2701 boolean masked, int element_bits_draw)
2703 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2704 element, tilesize, el2edimg, masked, element_bits_draw);
2707 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2708 int (*el2img_function)(int))
2710 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2714 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2717 if (IS_MM_WALL(element))
2719 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2720 element, tilesize, el2edimg, masked, 0x000f);
2724 int graphic = el2edimg(element);
2727 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2729 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2733 void DrawSizedElement(int x, int y, int element, int tilesize)
2735 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2738 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2740 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2743 void DrawMiniElement(int x, int y, int element)
2747 graphic = el2edimg(element);
2748 DrawMiniGraphic(x, y, graphic);
2751 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2754 int x = sx + scroll_x, y = sy + scroll_y;
2756 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2757 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2758 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2759 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2761 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2764 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2766 int x = sx + scroll_x, y = sy + scroll_y;
2768 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2769 DrawMiniElement(sx, sy, EL_EMPTY);
2770 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2771 DrawMiniElement(sx, sy, Tile[x][y]);
2773 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2776 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2777 int x, int y, int xsize, int ysize,
2778 int tile_width, int tile_height)
2782 int dst_x = startx + x * tile_width;
2783 int dst_y = starty + y * tile_height;
2784 int width = graphic_info[graphic].width;
2785 int height = graphic_info[graphic].height;
2786 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2787 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2788 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2789 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2790 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2791 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2792 boolean draw_masked = graphic_info[graphic].draw_masked;
2794 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2796 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2798 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2802 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2803 inner_sx + (x - 1) * tile_width % inner_width);
2804 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2805 inner_sy + (y - 1) * tile_height % inner_height);
2808 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2811 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2815 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2816 int x, int y, int xsize, int ysize,
2819 int font_width = getFontWidth(font_nr);
2820 int font_height = getFontHeight(font_nr);
2822 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2823 font_width, font_height);
2826 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2828 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2829 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2830 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2831 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2832 boolean no_delay = (tape.warp_forward);
2833 unsigned int anim_delay = 0;
2834 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2835 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2836 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2837 int font_width = getFontWidth(font_nr);
2838 int font_height = getFontHeight(font_nr);
2839 int max_xsize = level.envelope[envelope_nr].xsize;
2840 int max_ysize = level.envelope[envelope_nr].ysize;
2841 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2842 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2843 int xend = max_xsize;
2844 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2845 int xstep = (xstart < xend ? 1 : 0);
2846 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2848 int end = MAX(xend - xstart, yend - ystart);
2851 for (i = start; i <= end; i++)
2853 int last_frame = end; // last frame of this "for" loop
2854 int x = xstart + i * xstep;
2855 int y = ystart + i * ystep;
2856 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2857 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2858 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2859 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2862 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2864 BlitScreenToBitmap(backbuffer);
2866 SetDrawtoField(DRAW_TO_BACKBUFFER);
2868 for (yy = 0; yy < ysize; yy++)
2869 for (xx = 0; xx < xsize; xx++)
2870 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2872 DrawTextBuffer(sx + font_width, sy + font_height,
2873 level.envelope[envelope_nr].text, font_nr, max_xsize,
2874 xsize - 2, ysize - 2, 0, mask_mode,
2875 level.envelope[envelope_nr].autowrap,
2876 level.envelope[envelope_nr].centered, FALSE);
2878 redraw_mask |= REDRAW_FIELD;
2881 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2884 ClearAutoRepeatKeyEvents();
2887 void ShowEnvelope(int envelope_nr)
2889 int element = EL_ENVELOPE_1 + envelope_nr;
2890 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2891 int sound_opening = element_info[element].sound[ACTION_OPENING];
2892 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2893 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2894 boolean no_delay = (tape.warp_forward);
2895 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2896 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2897 int anim_mode = graphic_info[graphic].anim_mode;
2898 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2899 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2900 boolean overlay_enabled = GetOverlayEnabled();
2902 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2904 SetOverlayEnabled(FALSE);
2907 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2909 if (anim_mode == ANIM_DEFAULT)
2910 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2912 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2915 Delay_WithScreenUpdates(wait_delay_value);
2917 WaitForEventToContinue();
2920 SetOverlayEnabled(overlay_enabled);
2922 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2924 if (anim_mode != ANIM_NONE)
2925 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2927 if (anim_mode == ANIM_DEFAULT)
2928 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2930 game.envelope_active = FALSE;
2932 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2934 redraw_mask |= REDRAW_FIELD;
2938 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2939 int xsize, int ysize)
2941 if (!global.use_envelope_request ||
2942 request.sort_priority <= 0)
2945 if (request.bitmap == NULL ||
2946 xsize > request.xsize ||
2947 ysize > request.ysize)
2949 if (request.bitmap != NULL)
2950 FreeBitmap(request.bitmap);
2952 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2954 SDL_Surface *surface = request.bitmap->surface;
2956 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2957 Fail("SDLGetNativeSurface() failed");
2960 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2962 SDLFreeBitmapTextures(request.bitmap);
2963 SDLCreateBitmapTextures(request.bitmap);
2965 // set envelope request run-time values
2968 request.xsize = xsize;
2969 request.ysize = ysize;
2972 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2974 if (global.use_envelope_request &&
2975 game.request_active_or_moving &&
2976 request.sort_priority > 0 &&
2977 drawing_target == DRAW_TO_SCREEN &&
2978 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2980 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2981 request.sx, request.sy);
2985 static void setRequestBasePosition(int *x, int *y)
2987 int sx_base, sy_base;
2989 if (request.x != -1)
2990 sx_base = request.x;
2991 else if (request.align == ALIGN_LEFT)
2993 else if (request.align == ALIGN_RIGHT)
2994 sx_base = SX + SXSIZE;
2996 sx_base = SX + SXSIZE / 2;
2998 if (request.y != -1)
2999 sy_base = request.y;
3000 else if (request.valign == VALIGN_TOP)
3002 else if (request.valign == VALIGN_BOTTOM)
3003 sy_base = SY + SYSIZE;
3005 sy_base = SY + SYSIZE / 2;
3011 static void setRequestPositionExt(int *x, int *y, int width, int height,
3012 boolean add_border_size)
3014 int border_size = request.border_size;
3015 int sx_base, sy_base;
3018 setRequestBasePosition(&sx_base, &sy_base);
3020 if (request.align == ALIGN_LEFT)
3022 else if (request.align == ALIGN_RIGHT)
3023 sx = sx_base - width;
3025 sx = sx_base - width / 2;
3027 if (request.valign == VALIGN_TOP)
3029 else if (request.valign == VALIGN_BOTTOM)
3030 sy = sy_base - height;
3032 sy = sy_base - height / 2;
3034 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3035 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3037 if (add_border_size)
3047 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3049 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3052 static void DrawEnvelopeRequest(char *text)
3054 char *text_final = text;
3055 char *text_door_style = NULL;
3056 int graphic = IMG_BACKGROUND_REQUEST;
3057 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3058 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3059 int font_nr = FONT_REQUEST;
3060 int font_width = getFontWidth(font_nr);
3061 int font_height = getFontHeight(font_nr);
3062 int border_size = request.border_size;
3063 int line_spacing = request.line_spacing;
3064 int line_height = font_height + line_spacing;
3065 int max_text_width = request.width - 2 * border_size;
3066 int max_text_height = request.height - 2 * border_size;
3067 int line_length = max_text_width / font_width;
3068 int max_lines = max_text_height / line_height;
3069 int text_width = line_length * font_width;
3070 int width = request.width;
3071 int height = request.height;
3072 int tile_size = MAX(request.step_offset, 1);
3073 int x_steps = width / tile_size;
3074 int y_steps = height / tile_size;
3075 int sx_offset = border_size;
3076 int sy_offset = border_size;
3080 if (request.centered)
3081 sx_offset = (request.width - text_width) / 2;
3083 if (request.wrap_single_words && !request.autowrap)
3085 char *src_text_ptr, *dst_text_ptr;
3087 text_door_style = checked_malloc(2 * strlen(text) + 1);
3089 src_text_ptr = text;
3090 dst_text_ptr = text_door_style;
3092 while (*src_text_ptr)
3094 if (*src_text_ptr == ' ' ||
3095 *src_text_ptr == '?' ||
3096 *src_text_ptr == '!')
3097 *dst_text_ptr++ = '\n';
3099 if (*src_text_ptr != ' ')
3100 *dst_text_ptr++ = *src_text_ptr;
3105 *dst_text_ptr = '\0';
3107 text_final = text_door_style;
3110 setRequestPosition(&sx, &sy, FALSE);
3112 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3114 for (y = 0; y < y_steps; y++)
3115 for (x = 0; x < x_steps; x++)
3116 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3117 x, y, x_steps, y_steps,
3118 tile_size, tile_size);
3120 // force DOOR font inside door area
3121 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3123 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3124 line_length, -1, max_lines, line_spacing, mask_mode,
3125 request.autowrap, request.centered, FALSE);
3129 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3130 RedrawGadget(tool_gadget[i]);
3132 // store readily prepared envelope request for later use when animating
3133 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3135 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3137 if (text_door_style)
3138 free(text_door_style);
3141 static void AnimateEnvelopeRequest(int anim_mode, int action)
3143 int graphic = IMG_BACKGROUND_REQUEST;
3144 boolean draw_masked = graphic_info[graphic].draw_masked;
3145 int delay_value_normal = request.step_delay;
3146 int delay_value_fast = delay_value_normal / 2;
3147 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3148 boolean no_delay = (tape.warp_forward);
3149 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3150 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3151 unsigned int anim_delay = 0;
3153 int tile_size = MAX(request.step_offset, 1);
3154 int max_xsize = request.width / tile_size;
3155 int max_ysize = request.height / tile_size;
3156 int max_xsize_inner = max_xsize - 2;
3157 int max_ysize_inner = max_ysize - 2;
3159 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3160 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3161 int xend = max_xsize_inner;
3162 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3163 int xstep = (xstart < xend ? 1 : 0);
3164 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3166 int end = MAX(xend - xstart, yend - ystart);
3169 if (setup.quick_doors)
3176 for (i = start; i <= end; i++)
3178 int last_frame = end; // last frame of this "for" loop
3179 int x = xstart + i * xstep;
3180 int y = ystart + i * ystep;
3181 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3182 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3183 int xsize_size_left = (xsize - 1) * tile_size;
3184 int ysize_size_top = (ysize - 1) * tile_size;
3185 int max_xsize_pos = (max_xsize - 1) * tile_size;
3186 int max_ysize_pos = (max_ysize - 1) * tile_size;
3187 int width = xsize * tile_size;
3188 int height = ysize * tile_size;
3193 setRequestPosition(&src_x, &src_y, FALSE);
3194 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3196 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3198 for (yy = 0; yy < 2; yy++)
3200 for (xx = 0; xx < 2; xx++)
3202 int src_xx = src_x + xx * max_xsize_pos;
3203 int src_yy = src_y + yy * max_ysize_pos;
3204 int dst_xx = dst_x + xx * xsize_size_left;
3205 int dst_yy = dst_y + yy * ysize_size_top;
3206 int xx_size = (xx ? tile_size : xsize_size_left);
3207 int yy_size = (yy ? tile_size : ysize_size_top);
3210 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3211 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3213 BlitBitmap(bitmap_db_store_2, backbuffer,
3214 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3218 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3220 redraw_mask |= REDRAW_FIELD;
3224 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3227 ClearAutoRepeatKeyEvents();
3230 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3232 int graphic = IMG_BACKGROUND_REQUEST;
3233 int sound_opening = SND_REQUEST_OPENING;
3234 int sound_closing = SND_REQUEST_CLOSING;
3235 int anim_mode_1 = request.anim_mode; // (higher priority)
3236 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3237 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3238 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3239 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3241 if (game_status == GAME_MODE_PLAYING)
3242 BlitScreenToBitmap(backbuffer);
3244 SetDrawtoField(DRAW_TO_BACKBUFFER);
3246 // SetDrawBackgroundMask(REDRAW_NONE);
3248 if (action == ACTION_OPENING)
3250 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3252 if (req_state & REQ_ASK)
3254 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3255 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3256 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3257 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3259 else if (req_state & REQ_CONFIRM)
3261 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3262 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3264 else if (req_state & REQ_PLAYER)
3266 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3267 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3268 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3269 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3272 DrawEnvelopeRequest(text);
3275 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3277 if (action == ACTION_OPENING)
3279 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3281 if (anim_mode == ANIM_DEFAULT)
3282 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3284 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3288 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3290 if (anim_mode != ANIM_NONE)
3291 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3293 if (anim_mode == ANIM_DEFAULT)
3294 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3297 game.envelope_active = FALSE;
3299 if (action == ACTION_CLOSING)
3300 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3302 // SetDrawBackgroundMask(last_draw_background_mask);
3304 redraw_mask |= REDRAW_FIELD;
3308 if (action == ACTION_CLOSING &&
3309 game_status == GAME_MODE_PLAYING &&
3310 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3311 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3314 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3316 if (IS_MM_WALL(element))
3318 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3324 int graphic = el2preimg(element);
3326 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3327 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3332 void DrawLevel(int draw_background_mask)
3336 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3337 SetDrawBackgroundMask(draw_background_mask);
3341 for (x = BX1; x <= BX2; x++)
3342 for (y = BY1; y <= BY2; y++)
3343 DrawScreenField(x, y);
3345 redraw_mask |= REDRAW_FIELD;
3348 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3353 for (x = 0; x < size_x; x++)
3354 for (y = 0; y < size_y; y++)
3355 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3357 redraw_mask |= REDRAW_FIELD;
3360 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3364 for (x = 0; x < size_x; x++)
3365 for (y = 0; y < size_y; y++)
3366 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3368 redraw_mask |= REDRAW_FIELD;
3371 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3373 boolean show_level_border = (BorderElement != EL_EMPTY);
3374 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3375 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3376 int tile_size = preview.tile_size;
3377 int preview_width = preview.xsize * tile_size;
3378 int preview_height = preview.ysize * tile_size;
3379 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3380 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3381 int real_preview_width = real_preview_xsize * tile_size;
3382 int real_preview_height = real_preview_ysize * tile_size;
3383 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3384 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3387 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3390 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3392 dst_x += (preview_width - real_preview_width) / 2;
3393 dst_y += (preview_height - real_preview_height) / 2;
3395 for (x = 0; x < real_preview_xsize; x++)
3397 for (y = 0; y < real_preview_ysize; y++)
3399 int lx = from_x + x + (show_level_border ? -1 : 0);
3400 int ly = from_y + y + (show_level_border ? -1 : 0);
3401 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3402 getBorderElement(lx, ly));
3404 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3405 element, tile_size);
3409 redraw_mask |= REDRAW_FIELD;
3412 #define MICROLABEL_EMPTY 0
3413 #define MICROLABEL_LEVEL_NAME 1
3414 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3415 #define MICROLABEL_LEVEL_AUTHOR 3
3416 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3417 #define MICROLABEL_IMPORTED_FROM 5
3418 #define MICROLABEL_IMPORTED_BY_HEAD 6
3419 #define MICROLABEL_IMPORTED_BY 7
3421 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3423 int max_text_width = SXSIZE;
3424 int font_width = getFontWidth(font_nr);
3426 if (pos->align == ALIGN_CENTER)
3427 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3428 else if (pos->align == ALIGN_RIGHT)
3429 max_text_width = pos->x;
3431 max_text_width = SXSIZE - pos->x;
3433 return max_text_width / font_width;
3436 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3438 char label_text[MAX_OUTPUT_LINESIZE + 1];
3439 int max_len_label_text;
3440 int font_nr = pos->font;
3443 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3446 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3447 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3448 mode == MICROLABEL_IMPORTED_BY_HEAD)
3449 font_nr = pos->font_alt;
3451 max_len_label_text = getMaxTextLength(pos, font_nr);
3453 if (pos->size != -1)
3454 max_len_label_text = pos->size;
3456 for (i = 0; i < max_len_label_text; i++)
3457 label_text[i] = ' ';
3458 label_text[max_len_label_text] = '\0';
3460 if (strlen(label_text) > 0)
3461 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3464 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3465 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3466 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3467 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3468 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3469 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3470 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3471 max_len_label_text);
3472 label_text[max_len_label_text] = '\0';
3474 if (strlen(label_text) > 0)
3475 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3477 redraw_mask |= REDRAW_FIELD;
3480 static void DrawPreviewLevelLabel(int mode)
3482 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3485 static void DrawPreviewLevelInfo(int mode)
3487 if (mode == MICROLABEL_LEVEL_NAME)
3488 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3489 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3490 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3493 static void DrawPreviewLevelExt(boolean restart)
3495 static unsigned int scroll_delay = 0;
3496 static unsigned int label_delay = 0;
3497 static int from_x, from_y, scroll_direction;
3498 static int label_state, label_counter;
3499 unsigned int scroll_delay_value = preview.step_delay;
3500 boolean show_level_border = (BorderElement != EL_EMPTY);
3501 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3502 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3509 if (preview.anim_mode == ANIM_CENTERED)
3511 if (level_xsize > preview.xsize)
3512 from_x = (level_xsize - preview.xsize) / 2;
3513 if (level_ysize > preview.ysize)
3514 from_y = (level_ysize - preview.ysize) / 2;
3517 from_x += preview.xoffset;
3518 from_y += preview.yoffset;
3520 scroll_direction = MV_RIGHT;
3524 DrawPreviewLevelPlayfield(from_x, from_y);
3525 DrawPreviewLevelLabel(label_state);
3527 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3528 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3530 // initialize delay counters
3531 DelayReached(&scroll_delay, 0);
3532 DelayReached(&label_delay, 0);
3534 if (leveldir_current->name)
3536 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3537 char label_text[MAX_OUTPUT_LINESIZE + 1];
3538 int font_nr = pos->font;
3539 int max_len_label_text = getMaxTextLength(pos, font_nr);
3541 if (pos->size != -1)
3542 max_len_label_text = pos->size;
3544 strncpy(label_text, leveldir_current->name, max_len_label_text);
3545 label_text[max_len_label_text] = '\0';
3547 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3548 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3554 // scroll preview level, if needed
3555 if (preview.anim_mode != ANIM_NONE &&
3556 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3557 DelayReached(&scroll_delay, scroll_delay_value))
3559 switch (scroll_direction)
3564 from_x -= preview.step_offset;
3565 from_x = (from_x < 0 ? 0 : from_x);
3568 scroll_direction = MV_UP;
3572 if (from_x < level_xsize - preview.xsize)
3574 from_x += preview.step_offset;
3575 from_x = (from_x > level_xsize - preview.xsize ?
3576 level_xsize - preview.xsize : from_x);
3579 scroll_direction = MV_DOWN;
3585 from_y -= preview.step_offset;
3586 from_y = (from_y < 0 ? 0 : from_y);
3589 scroll_direction = MV_RIGHT;
3593 if (from_y < level_ysize - preview.ysize)
3595 from_y += preview.step_offset;
3596 from_y = (from_y > level_ysize - preview.ysize ?
3597 level_ysize - preview.ysize : from_y);
3600 scroll_direction = MV_LEFT;
3607 DrawPreviewLevelPlayfield(from_x, from_y);
3610 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3611 // redraw micro level label, if needed
3612 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3613 !strEqual(level.author, ANONYMOUS_NAME) &&
3614 !strEqual(level.author, leveldir_current->name) &&
3615 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3617 int max_label_counter = 23;
3619 if (leveldir_current->imported_from != NULL &&
3620 strlen(leveldir_current->imported_from) > 0)
3621 max_label_counter += 14;
3622 if (leveldir_current->imported_by != NULL &&
3623 strlen(leveldir_current->imported_by) > 0)
3624 max_label_counter += 14;
3626 label_counter = (label_counter + 1) % max_label_counter;
3627 label_state = (label_counter >= 0 && label_counter <= 7 ?
3628 MICROLABEL_LEVEL_NAME :
3629 label_counter >= 9 && label_counter <= 12 ?
3630 MICROLABEL_LEVEL_AUTHOR_HEAD :
3631 label_counter >= 14 && label_counter <= 21 ?
3632 MICROLABEL_LEVEL_AUTHOR :
3633 label_counter >= 23 && label_counter <= 26 ?
3634 MICROLABEL_IMPORTED_FROM_HEAD :
3635 label_counter >= 28 && label_counter <= 35 ?
3636 MICROLABEL_IMPORTED_FROM :
3637 label_counter >= 37 && label_counter <= 40 ?
3638 MICROLABEL_IMPORTED_BY_HEAD :
3639 label_counter >= 42 && label_counter <= 49 ?
3640 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3642 if (leveldir_current->imported_from == NULL &&
3643 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3644 label_state == MICROLABEL_IMPORTED_FROM))
3645 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3646 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3648 DrawPreviewLevelLabel(label_state);
3652 void DrawPreviewPlayers(void)
3654 if (game_status != GAME_MODE_MAIN)
3657 // do not draw preview players if level preview redefined, but players aren't
3658 if (preview.redefined && !menu.main.preview_players.redefined)
3661 boolean player_found[MAX_PLAYERS];
3662 int num_players = 0;
3665 for (i = 0; i < MAX_PLAYERS; i++)
3666 player_found[i] = FALSE;
3668 // check which players can be found in the level (simple approach)
3669 for (x = 0; x < lev_fieldx; x++)
3671 for (y = 0; y < lev_fieldy; y++)
3673 int element = level.field[x][y];
3675 if (ELEM_IS_PLAYER(element))
3677 int player_nr = GET_PLAYER_NR(element);
3679 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3681 if (!player_found[player_nr])
3684 player_found[player_nr] = TRUE;
3689 struct TextPosInfo *pos = &menu.main.preview_players;
3690 int tile_size = pos->tile_size;
3691 int border_size = pos->border_size;
3692 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3693 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3694 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3695 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3696 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3697 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3698 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3699 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3700 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3701 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3702 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3703 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3705 // clear area in which the players will be drawn
3706 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3707 max_players_width, max_players_height);
3709 if (!network.enabled && !setup.team_mode)
3712 // only draw players if level is suited for team mode
3713 if (num_players < 2)
3716 // draw all players that were found in the level
3717 for (i = 0; i < MAX_PLAYERS; i++)
3719 if (player_found[i])
3721 int graphic = el2img(EL_PLAYER_1 + i);
3723 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3725 xpos += player_xoffset;
3726 ypos += player_yoffset;
3731 void DrawPreviewLevelInitial(void)
3733 DrawPreviewLevelExt(TRUE);
3734 DrawPreviewPlayers();
3737 void DrawPreviewLevelAnimation(void)
3739 DrawPreviewLevelExt(FALSE);
3742 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3743 int border_size, int font_nr)
3745 int graphic = el2img(EL_PLAYER_1 + player_nr);
3746 int font_height = getFontHeight(font_nr);
3747 int player_height = MAX(tile_size, font_height);
3748 int xoffset_text = tile_size + border_size;
3749 int yoffset_text = (player_height - font_height) / 2;
3750 int yoffset_graphic = (player_height - tile_size) / 2;
3751 char *player_name = getNetworkPlayerName(player_nr + 1);
3753 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3755 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3758 static void DrawNetworkPlayersExt(boolean force)
3760 if (game_status != GAME_MODE_MAIN)
3763 if (!network.connected && !force)
3766 // do not draw network players if level preview redefined, but players aren't
3767 if (preview.redefined && !menu.main.network_players.redefined)
3770 int num_players = 0;
3773 for (i = 0; i < MAX_PLAYERS; i++)
3774 if (stored_player[i].connected_network)
3777 struct TextPosInfo *pos = &menu.main.network_players;
3778 int tile_size = pos->tile_size;
3779 int border_size = pos->border_size;
3780 int xoffset_text = tile_size + border_size;
3781 int font_nr = pos->font;
3782 int font_width = getFontWidth(font_nr);
3783 int font_height = getFontHeight(font_nr);
3784 int player_height = MAX(tile_size, font_height);
3785 int player_yoffset = player_height + border_size;
3786 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3787 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3788 int all_players_height = num_players * player_yoffset - border_size;
3789 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3790 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3791 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3793 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3794 max_players_width, max_players_height);
3796 // first draw local network player ...
3797 for (i = 0; i < MAX_PLAYERS; i++)
3799 if (stored_player[i].connected_network &&
3800 stored_player[i].connected_locally)
3802 char *player_name = getNetworkPlayerName(i + 1);
3803 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3804 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3806 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3808 ypos += player_yoffset;
3812 // ... then draw all other network players
3813 for (i = 0; i < MAX_PLAYERS; i++)
3815 if (stored_player[i].connected_network &&
3816 !stored_player[i].connected_locally)
3818 char *player_name = getNetworkPlayerName(i + 1);
3819 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3820 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3822 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3824 ypos += player_yoffset;
3829 void DrawNetworkPlayers(void)
3831 DrawNetworkPlayersExt(FALSE);
3834 void ClearNetworkPlayers(void)
3836 DrawNetworkPlayersExt(TRUE);
3839 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3840 int graphic, int sync_frame,
3843 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3845 if (mask_mode == USE_MASKING)
3846 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3848 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3851 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3852 int graphic, int sync_frame, int mask_mode)
3854 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3856 if (mask_mode == USE_MASKING)
3857 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3859 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3862 static void DrawGraphicAnimation(int x, int y, int graphic)
3864 int lx = LEVELX(x), ly = LEVELY(y);
3865 int mask_mode = NO_MASKING;
3867 if (!IN_SCR_FIELD(x, y))
3870 if (game.use_masked_elements)
3872 if (Tile[lx][ly] != EL_EMPTY)
3874 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3876 mask_mode = USE_MASKING;
3880 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3881 graphic, GfxFrame[lx][ly], mask_mode);
3883 MarkTileDirty(x, y);
3886 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3888 int lx = LEVELX(x), ly = LEVELY(y);
3889 int mask_mode = NO_MASKING;
3891 if (!IN_SCR_FIELD(x, y))
3894 if (game.use_masked_elements)
3896 if (Tile[lx][ly] != EL_EMPTY)
3898 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3900 mask_mode = USE_MASKING;
3904 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3905 graphic, GfxFrame[lx][ly], mask_mode);
3907 MarkTileDirty(x, y);
3910 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3912 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3915 void DrawLevelElementAnimation(int x, int y, int element)
3917 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3919 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3922 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3924 int sx = SCREENX(x), sy = SCREENY(y);
3926 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3929 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3932 DrawGraphicAnimation(sx, sy, graphic);
3935 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3936 DrawLevelFieldCrumbled(x, y);
3938 if (GFX_CRUMBLED(Tile[x][y]))
3939 DrawLevelFieldCrumbled(x, y);
3943 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3945 int sx = SCREENX(x), sy = SCREENY(y);
3948 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3951 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3953 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3956 DrawGraphicAnimation(sx, sy, graphic);
3958 if (GFX_CRUMBLED(element))
3959 DrawLevelFieldCrumbled(x, y);
3962 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3964 if (player->use_murphy)
3966 // this works only because currently only one player can be "murphy" ...
3967 static int last_horizontal_dir = MV_LEFT;
3968 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3970 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3971 last_horizontal_dir = move_dir;
3973 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3975 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3977 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3983 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3986 static boolean equalGraphics(int graphic1, int graphic2)
3988 struct GraphicInfo *g1 = &graphic_info[graphic1];
3989 struct GraphicInfo *g2 = &graphic_info[graphic2];
3991 return (g1->bitmap == g2->bitmap &&
3992 g1->src_x == g2->src_x &&
3993 g1->src_y == g2->src_y &&
3994 g1->anim_frames == g2->anim_frames &&
3995 g1->anim_delay == g2->anim_delay &&
3996 g1->anim_mode == g2->anim_mode);
3999 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4003 DRAW_PLAYER_STAGE_INIT = 0,
4004 DRAW_PLAYER_STAGE_LAST_FIELD,
4005 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4006 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4007 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4008 DRAW_PLAYER_STAGE_PLAYER,
4010 DRAW_PLAYER_STAGE_PLAYER,
4011 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4013 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4014 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4016 NUM_DRAW_PLAYER_STAGES
4019 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4021 static int static_last_player_graphic[MAX_PLAYERS];
4022 static int static_last_player_frame[MAX_PLAYERS];
4023 static boolean static_player_is_opaque[MAX_PLAYERS];
4024 static boolean draw_player[MAX_PLAYERS];
4025 int pnr = player->index_nr;
4027 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4029 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4030 static_last_player_frame[pnr] = player->Frame;
4031 static_player_is_opaque[pnr] = FALSE;
4033 draw_player[pnr] = TRUE;
4036 if (!draw_player[pnr])
4040 if (!IN_LEV_FIELD(player->jx, player->jy))
4042 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4043 Debug("draw:DrawPlayerExt", "This should never happen!");
4045 draw_player[pnr] = FALSE;
4051 int last_player_graphic = static_last_player_graphic[pnr];
4052 int last_player_frame = static_last_player_frame[pnr];
4053 boolean player_is_opaque = static_player_is_opaque[pnr];
4055 int jx = player->jx;
4056 int jy = player->jy;
4057 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4058 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4059 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4060 int last_jx = (player->is_moving ? jx - dx : jx);
4061 int last_jy = (player->is_moving ? jy - dy : jy);
4062 int next_jx = jx + dx;
4063 int next_jy = jy + dy;
4064 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4065 int sx = SCREENX(jx);
4066 int sy = SCREENY(jy);
4067 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4068 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4069 int element = Tile[jx][jy];
4070 int last_element = Tile[last_jx][last_jy];
4071 int action = (player->is_pushing ? ACTION_PUSHING :
4072 player->is_digging ? ACTION_DIGGING :
4073 player->is_collecting ? ACTION_COLLECTING :
4074 player->is_moving ? ACTION_MOVING :
4075 player->is_snapping ? ACTION_SNAPPING :
4076 player->is_dropping ? ACTION_DROPPING :
4077 player->is_waiting ? player->action_waiting :
4080 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4082 // ------------------------------------------------------------------------
4083 // initialize drawing the player
4084 // ------------------------------------------------------------------------
4086 draw_player[pnr] = FALSE;
4088 // GfxElement[][] is set to the element the player is digging or collecting;
4089 // remove also for off-screen player if the player is not moving anymore
4090 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4091 GfxElement[jx][jy] = EL_UNDEFINED;
4093 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4096 if (element == EL_EXPLOSION)
4099 InitPlayerGfxAnimation(player, action, move_dir);
4101 draw_player[pnr] = TRUE;
4103 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4105 // ------------------------------------------------------------------------
4106 // draw things in the field the player is leaving, if needed
4107 // ------------------------------------------------------------------------
4109 if (!IN_SCR_FIELD(sx, sy))
4110 draw_player[pnr] = FALSE;
4112 if (!player->is_moving)
4115 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4117 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4119 if (last_element == EL_DYNAMITE_ACTIVE ||
4120 last_element == EL_EM_DYNAMITE_ACTIVE ||
4121 last_element == EL_SP_DISK_RED_ACTIVE)
4122 DrawDynamite(last_jx, last_jy);
4124 DrawLevelFieldThruMask(last_jx, last_jy);
4126 else if (last_element == EL_DYNAMITE_ACTIVE ||
4127 last_element == EL_EM_DYNAMITE_ACTIVE ||
4128 last_element == EL_SP_DISK_RED_ACTIVE)
4129 DrawDynamite(last_jx, last_jy);
4131 DrawLevelField(last_jx, last_jy);
4133 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4134 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4136 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4138 // ------------------------------------------------------------------------
4139 // draw things behind the player, if needed
4140 // ------------------------------------------------------------------------
4144 DrawLevelElement(jx, jy, Back[jx][jy]);
4149 if (IS_ACTIVE_BOMB(element))
4151 DrawLevelElement(jx, jy, EL_EMPTY);
4156 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4158 int old_element = GfxElement[jx][jy];
4159 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4160 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4162 if (GFX_CRUMBLED(old_element))
4163 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4165 DrawScreenGraphic(sx, sy, old_graphic, frame);
4167 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4168 static_player_is_opaque[pnr] = TRUE;
4172 GfxElement[jx][jy] = EL_UNDEFINED;
4174 // make sure that pushed elements are drawn with correct frame rate
4175 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4177 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4178 GfxFrame[jx][jy] = player->StepFrame;
4180 DrawLevelField(jx, jy);
4183 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4185 // ------------------------------------------------------------------------
4186 // draw things the player is pushing, if needed
4187 // ------------------------------------------------------------------------
4189 if (!player->is_pushing || !player->is_moving)
4192 int gfx_frame = GfxFrame[jx][jy];
4194 if (!IS_MOVING(jx, jy)) // push movement already finished
4196 element = Tile[next_jx][next_jy];
4197 gfx_frame = GfxFrame[next_jx][next_jy];
4200 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4201 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4202 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4204 // draw background element under pushed element (like the Sokoban field)
4205 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4207 // this allows transparent pushing animation over non-black background
4210 DrawLevelElement(jx, jy, Back[jx][jy]);
4212 DrawLevelElement(jx, jy, EL_EMPTY);
4214 if (Back[next_jx][next_jy])
4215 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4217 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4219 else if (Back[next_jx][next_jy])
4220 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4222 int px = SCREENX(jx), py = SCREENY(jy);
4223 int pxx = (TILEX - ABS(sxx)) * dx;
4224 int pyy = (TILEY - ABS(syy)) * dy;
4227 // do not draw (EM style) pushing animation when pushing is finished
4228 // (two-tile animations usually do not contain start and end frame)
4229 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4230 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4232 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4234 // masked drawing is needed for EMC style (double) movement graphics
4235 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4236 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4239 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4241 // ------------------------------------------------------------------------
4242 // draw player himself
4243 // ------------------------------------------------------------------------
4245 int graphic = getPlayerGraphic(player, move_dir);
4247 // in the case of changed player action or direction, prevent the current
4248 // animation frame from being restarted for identical animations
4249 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4250 player->Frame = last_player_frame;
4252 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4254 if (player_is_opaque)
4255 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4257 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4259 if (SHIELD_ON(player))
4261 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4262 IMG_SHIELD_NORMAL_ACTIVE);
4263 frame = getGraphicAnimationFrame(graphic, -1);
4265 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4268 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4270 // ------------------------------------------------------------------------
4271 // draw things in front of player (active dynamite or dynabombs)
4272 // ------------------------------------------------------------------------
4274 if (IS_ACTIVE_BOMB(element))
4276 int graphic = el2img(element);
4277 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4279 if (game.emulation == EMU_SUPAPLEX)
4280 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4282 DrawGraphicThruMask(sx, sy, graphic, frame);
4285 if (player_is_moving && last_element == EL_EXPLOSION)
4287 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4288 GfxElement[last_jx][last_jy] : EL_EMPTY);
4289 int graphic = el_act2img(element, ACTION_EXPLODING);
4290 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4291 int phase = ExplodePhase[last_jx][last_jy] - 1;
4292 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4295 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4298 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4300 // ------------------------------------------------------------------------
4301 // draw elements the player is just walking/passing through/under
4302 // ------------------------------------------------------------------------
4304 if (player_is_moving)
4306 // handle the field the player is leaving ...
4307 if (IS_ACCESSIBLE_INSIDE(last_element))
4308 DrawLevelField(last_jx, last_jy);
4309 else if (IS_ACCESSIBLE_UNDER(last_element))
4310 DrawLevelFieldThruMask(last_jx, last_jy);
4313 // do not redraw accessible elements if the player is just pushing them
4314 if (!player_is_moving || !player->is_pushing)
4316 // ... and the field the player is entering
4317 if (IS_ACCESSIBLE_INSIDE(element))
4318 DrawLevelField(jx, jy);
4319 else if (IS_ACCESSIBLE_UNDER(element))
4320 DrawLevelFieldThruMask(jx, jy);
4323 MarkTileDirty(sx, sy);
4327 void DrawPlayer(struct PlayerInfo *player)
4331 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4332 DrawPlayerExt(player, i);
4335 void DrawAllPlayers(void)
4339 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4340 for (j = 0; j < MAX_PLAYERS; j++)
4341 if (stored_player[j].active)
4342 DrawPlayerExt(&stored_player[j], i);
4345 void DrawPlayerField(int x, int y)
4347 if (!IS_PLAYER(x, y))
4350 DrawPlayer(PLAYERINFO(x, y));
4353 // ----------------------------------------------------------------------------
4355 void WaitForEventToContinue(void)
4357 boolean first_wait = TRUE;
4358 boolean still_wait = TRUE;
4360 if (program.headless)
4363 // simulate releasing mouse button over last gadget, if still pressed
4365 HandleGadgets(-1, -1, 0);
4367 button_status = MB_RELEASED;
4370 ClearPlayerAction();
4376 if (NextValidEvent(&event))
4380 case EVENT_BUTTONPRESS:
4381 case EVENT_FINGERPRESS:
4385 case EVENT_BUTTONRELEASE:
4386 case EVENT_FINGERRELEASE:
4387 still_wait = first_wait;
4390 case EVENT_KEYPRESS:
4391 case SDL_CONTROLLERBUTTONDOWN:
4392 case SDL_JOYBUTTONDOWN:
4397 HandleOtherEvents(&event);
4401 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4406 if (!PendingEvent())
4411 #define MAX_REQUEST_LINES 13
4412 #define MAX_REQUEST_LINE_FONT1_LEN 7
4413 #define MAX_REQUEST_LINE_FONT2_LEN 10
4415 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4417 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4419 int draw_buffer_last = GetDrawtoField();
4420 int width = request.width;
4421 int height = request.height;
4425 // when showing request dialog after game ended, deactivate game panel
4426 if (game_just_ended)
4427 game.panel.active = FALSE;
4429 game.request_active = TRUE;
4431 setRequestPosition(&sx, &sy, FALSE);
4433 button_status = MB_RELEASED;
4435 request_gadget_id = -1;
4440 boolean event_handled = FALSE;
4442 if (game_just_ended)
4444 SetDrawtoField(draw_buffer_game);
4446 HandleGameActions();
4448 SetDrawtoField(DRAW_TO_BACKBUFFER);
4450 if (global.use_envelope_request)
4452 // copy current state of request area to middle of playfield area
4453 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4461 while (NextValidEvent(&event))
4463 event_handled = TRUE;
4467 case EVENT_BUTTONPRESS:
4468 case EVENT_BUTTONRELEASE:
4469 case EVENT_MOTIONNOTIFY:
4473 if (event.type == EVENT_MOTIONNOTIFY)
4478 motion_status = TRUE;
4479 mx = ((MotionEvent *) &event)->x;
4480 my = ((MotionEvent *) &event)->y;
4484 motion_status = FALSE;
4485 mx = ((ButtonEvent *) &event)->x;
4486 my = ((ButtonEvent *) &event)->y;
4487 if (event.type == EVENT_BUTTONPRESS)
4488 button_status = ((ButtonEvent *) &event)->button;
4490 button_status = MB_RELEASED;
4493 // this sets 'request_gadget_id'
4494 HandleGadgets(mx, my, button_status);
4496 switch (request_gadget_id)
4498 case TOOL_CTRL_ID_YES:
4499 case TOOL_CTRL_ID_TOUCH_YES:
4502 case TOOL_CTRL_ID_NO:
4503 case TOOL_CTRL_ID_TOUCH_NO:
4506 case TOOL_CTRL_ID_CONFIRM:
4507 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4508 result = TRUE | FALSE;
4511 case TOOL_CTRL_ID_PLAYER_1:
4514 case TOOL_CTRL_ID_PLAYER_2:
4517 case TOOL_CTRL_ID_PLAYER_3:
4520 case TOOL_CTRL_ID_PLAYER_4:
4525 // only check clickable animations if no request gadget clicked
4526 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4533 case SDL_WINDOWEVENT:
4534 HandleWindowEvent((WindowEvent *) &event);
4537 case SDL_APP_WILLENTERBACKGROUND:
4538 case SDL_APP_DIDENTERBACKGROUND:
4539 case SDL_APP_WILLENTERFOREGROUND:
4540 case SDL_APP_DIDENTERFOREGROUND:
4541 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4544 case EVENT_KEYPRESS:
4546 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4551 if (req_state & REQ_CONFIRM)
4560 #if defined(KSYM_Rewind)
4561 case KSYM_Rewind: // for Amazon Fire TV remote
4570 #if defined(KSYM_FastForward)
4571 case KSYM_FastForward: // for Amazon Fire TV remote
4577 HandleKeysDebug(key, KEY_PRESSED);
4581 if (req_state & REQ_PLAYER)
4583 int old_player_nr = setup.network_player_nr;
4586 result = old_player_nr + 1;
4591 result = old_player_nr + 1;
4622 case EVENT_FINGERRELEASE:
4623 case EVENT_KEYRELEASE:
4624 ClearPlayerAction();
4627 case SDL_CONTROLLERBUTTONDOWN:
4628 switch (event.cbutton.button)
4630 case SDL_CONTROLLER_BUTTON_A:
4631 case SDL_CONTROLLER_BUTTON_X:
4632 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4633 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4637 case SDL_CONTROLLER_BUTTON_B:
4638 case SDL_CONTROLLER_BUTTON_Y:
4639 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4640 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4641 case SDL_CONTROLLER_BUTTON_BACK:
4646 if (req_state & REQ_PLAYER)
4648 int old_player_nr = setup.network_player_nr;
4651 result = old_player_nr + 1;
4653 switch (event.cbutton.button)
4655 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4656 case SDL_CONTROLLER_BUTTON_Y:
4660 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4661 case SDL_CONTROLLER_BUTTON_B:
4665 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4666 case SDL_CONTROLLER_BUTTON_A:
4670 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4671 case SDL_CONTROLLER_BUTTON_X:
4682 case SDL_CONTROLLERBUTTONUP:
4683 HandleJoystickEvent(&event);
4684 ClearPlayerAction();
4688 HandleOtherEvents(&event);
4693 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4695 int joy = AnyJoystick();
4697 if (joy & JOY_BUTTON_1)
4699 else if (joy & JOY_BUTTON_2)
4702 else if (AnyJoystick())
4704 int joy = AnyJoystick();
4706 if (req_state & REQ_PLAYER)
4710 else if (joy & JOY_RIGHT)
4712 else if (joy & JOY_DOWN)
4714 else if (joy & JOY_LEFT)
4721 if (game_just_ended)
4723 if (global.use_envelope_request)
4725 // copy back current state of pressed buttons inside request area
4726 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4730 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4736 SetDrawtoField(draw_buffer_last);
4738 game.request_active = FALSE;
4743 static boolean RequestDoor(char *text, unsigned int req_state)
4745 int draw_buffer_last = GetDrawtoField();
4746 unsigned int old_door_state;
4747 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4748 int font_nr = FONT_TEXT_2;
4753 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4755 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4756 font_nr = FONT_TEXT_1;
4759 if (game_status == GAME_MODE_PLAYING)
4760 BlitScreenToBitmap(backbuffer);
4762 // disable deactivated drawing when quick-loading level tape recording
4763 if (tape.playing && tape.deactivate_display)
4764 TapeDeactivateDisplayOff(TRUE);
4766 SetMouseCursor(CURSOR_DEFAULT);
4768 // pause network game while waiting for request to answer
4769 if (network.enabled &&
4770 game_status == GAME_MODE_PLAYING &&
4771 !game.all_players_gone &&
4772 req_state & REQUEST_WAIT_FOR_INPUT)
4773 SendToServer_PausePlaying();
4775 old_door_state = GetDoorState();
4777 // simulate releasing mouse button over last gadget, if still pressed
4779 HandleGadgets(-1, -1, 0);
4783 // draw released gadget before proceeding
4786 if (old_door_state & DOOR_OPEN_1)
4788 CloseDoor(DOOR_CLOSE_1);
4790 // save old door content
4791 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4792 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4795 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4796 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4798 // clear door drawing field
4799 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4801 // force DOOR font inside door area
4802 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4804 // write text for request
4805 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4807 char text_line[max_request_line_len + 1];
4813 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4815 tc = *(text_ptr + tx);
4816 // if (!tc || tc == ' ')
4817 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4821 if ((tc == '?' || tc == '!') && tl == 0)
4831 strncpy(text_line, text_ptr, tl);
4834 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4835 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4836 text_line, font_nr);
4838 text_ptr += tl + (tc == ' ' ? 1 : 0);
4839 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4844 if (req_state & REQ_ASK)
4846 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4847 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4848 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4849 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4851 else if (req_state & REQ_CONFIRM)
4853 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4854 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4856 else if (req_state & REQ_PLAYER)
4858 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4859 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4860 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4861 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4864 // copy request gadgets to door backbuffer
4865 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4867 OpenDoor(DOOR_OPEN_1);
4869 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4871 if (game_status == GAME_MODE_PLAYING)
4873 SetPanelBackground();
4874 SetDrawBackgroundMask(REDRAW_DOOR_1);
4878 SetDrawBackgroundMask(REDRAW_FIELD);
4884 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4886 // ---------- handle request buttons ----------
4887 result = RequestHandleEvents(req_state, draw_buffer_last);
4891 if (!(req_state & REQ_STAY_OPEN))
4893 CloseDoor(DOOR_CLOSE_1);
4895 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4896 (req_state & REQ_REOPEN))
4897 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4902 if (game_status == GAME_MODE_PLAYING)
4904 SetPanelBackground();
4905 SetDrawBackgroundMask(REDRAW_DOOR_1);
4909 SetDrawBackgroundMask(REDRAW_FIELD);
4912 // continue network game after request
4913 if (network.enabled &&
4914 game_status == GAME_MODE_PLAYING &&
4915 !game.all_players_gone &&
4916 req_state & REQUEST_WAIT_FOR_INPUT)
4917 SendToServer_ContinuePlaying();
4919 // restore deactivated drawing when quick-loading level tape recording
4920 if (tape.playing && tape.deactivate_display)
4921 TapeDeactivateDisplayOn();
4926 static boolean RequestEnvelope(char *text, unsigned int req_state)
4928 int draw_buffer_last = GetDrawtoField();
4931 if (game_status == GAME_MODE_PLAYING)
4932 BlitScreenToBitmap(backbuffer);
4934 // disable deactivated drawing when quick-loading level tape recording
4935 if (tape.playing && tape.deactivate_display)
4936 TapeDeactivateDisplayOff(TRUE);
4938 SetMouseCursor(CURSOR_DEFAULT);
4940 // pause network game while waiting for request to answer
4941 if (network.enabled &&
4942 game_status == GAME_MODE_PLAYING &&
4943 !game.all_players_gone &&
4944 req_state & REQUEST_WAIT_FOR_INPUT)
4945 SendToServer_PausePlaying();
4947 // simulate releasing mouse button over last gadget, if still pressed
4949 HandleGadgets(-1, -1, 0);
4953 // (replace with setting corresponding request background)
4954 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4955 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4957 // clear door drawing field
4958 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4960 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4962 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4964 if (game_status == GAME_MODE_PLAYING)
4966 SetPanelBackground();
4967 SetDrawBackgroundMask(REDRAW_DOOR_1);
4971 SetDrawBackgroundMask(REDRAW_FIELD);
4977 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4979 // ---------- handle request buttons ----------
4980 result = RequestHandleEvents(req_state, draw_buffer_last);
4984 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4988 if (game_status == GAME_MODE_PLAYING)
4990 SetPanelBackground();
4991 SetDrawBackgroundMask(REDRAW_DOOR_1);
4995 SetDrawBackgroundMask(REDRAW_FIELD);
4998 // continue network game after request
4999 if (network.enabled &&
5000 game_status == GAME_MODE_PLAYING &&
5001 !game.all_players_gone &&
5002 req_state & REQUEST_WAIT_FOR_INPUT)
5003 SendToServer_ContinuePlaying();
5005 // restore deactivated drawing when quick-loading level tape recording
5006 if (tape.playing && tape.deactivate_display)
5007 TapeDeactivateDisplayOn();
5012 boolean Request(char *text, unsigned int req_state)
5014 boolean overlay_enabled = GetOverlayEnabled();
5017 game.request_active_or_moving = TRUE;
5019 SetOverlayEnabled(FALSE);
5021 if (global.use_envelope_request)
5022 result = RequestEnvelope(text, req_state);
5024 result = RequestDoor(text, req_state);
5026 SetOverlayEnabled(overlay_enabled);
5028 game.request_active_or_moving = FALSE;
5033 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5035 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5036 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5039 if (dpo1->sort_priority != dpo2->sort_priority)
5040 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5042 compare_result = dpo1->nr - dpo2->nr;
5044 return compare_result;
5047 void InitGraphicCompatibilityInfo_Doors(void)
5053 struct DoorInfo *door;
5057 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5058 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5060 { -1, -1, -1, NULL }
5062 struct Rect door_rect_list[] =
5064 { DX, DY, DXSIZE, DYSIZE },
5065 { VX, VY, VXSIZE, VYSIZE }
5069 for (i = 0; doors[i].door_token != -1; i++)
5071 int door_token = doors[i].door_token;
5072 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5073 int part_1 = doors[i].part_1;
5074 int part_8 = doors[i].part_8;
5075 int part_2 = part_1 + 1;
5076 int part_3 = part_1 + 2;
5077 struct DoorInfo *door = doors[i].door;
5078 struct Rect *door_rect = &door_rect_list[door_index];
5079 boolean door_gfx_redefined = FALSE;
5081 // check if any door part graphic definitions have been redefined
5083 for (j = 0; door_part_controls[j].door_token != -1; j++)
5085 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5086 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5088 if (dpc->door_token == door_token && fi->redefined)
5089 door_gfx_redefined = TRUE;
5092 // check for old-style door graphic/animation modifications
5094 if (!door_gfx_redefined)
5096 if (door->anim_mode & ANIM_STATIC_PANEL)
5098 door->panel.step_xoffset = 0;
5099 door->panel.step_yoffset = 0;
5102 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5104 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5105 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5106 int num_door_steps, num_panel_steps;
5108 // remove door part graphics other than the two default wings
5110 for (j = 0; door_part_controls[j].door_token != -1; j++)
5112 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5113 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5115 if (dpc->graphic >= part_3 &&
5116 dpc->graphic <= part_8)
5120 // set graphics and screen positions of the default wings
5122 g_part_1->width = door_rect->width;
5123 g_part_1->height = door_rect->height;
5124 g_part_2->width = door_rect->width;
5125 g_part_2->height = door_rect->height;
5126 g_part_2->src_x = door_rect->width;
5127 g_part_2->src_y = g_part_1->src_y;
5129 door->part_2.x = door->part_1.x;
5130 door->part_2.y = door->part_1.y;
5132 if (door->width != -1)
5134 g_part_1->width = door->width;
5135 g_part_2->width = door->width;
5137 // special treatment for graphics and screen position of right wing
5138 g_part_2->src_x += door_rect->width - door->width;
5139 door->part_2.x += door_rect->width - door->width;
5142 if (door->height != -1)
5144 g_part_1->height = door->height;
5145 g_part_2->height = door->height;
5147 // special treatment for graphics and screen position of bottom wing
5148 g_part_2->src_y += door_rect->height - door->height;
5149 door->part_2.y += door_rect->height - door->height;
5152 // set animation delays for the default wings and panels
5154 door->part_1.step_delay = door->step_delay;
5155 door->part_2.step_delay = door->step_delay;
5156 door->panel.step_delay = door->step_delay;
5158 // set animation draw order for the default wings
5160 door->part_1.sort_priority = 2; // draw left wing over ...
5161 door->part_2.sort_priority = 1; // ... right wing
5163 // set animation draw offset for the default wings
5165 if (door->anim_mode & ANIM_HORIZONTAL)
5167 door->part_1.step_xoffset = door->step_offset;
5168 door->part_1.step_yoffset = 0;
5169 door->part_2.step_xoffset = door->step_offset * -1;
5170 door->part_2.step_yoffset = 0;
5172 num_door_steps = g_part_1->width / door->step_offset;
5174 else // ANIM_VERTICAL
5176 door->part_1.step_xoffset = 0;
5177 door->part_1.step_yoffset = door->step_offset;
5178 door->part_2.step_xoffset = 0;
5179 door->part_2.step_yoffset = door->step_offset * -1;
5181 num_door_steps = g_part_1->height / door->step_offset;
5184 // set animation draw offset for the default panels
5186 if (door->step_offset > 1)
5188 num_panel_steps = 2 * door_rect->height / door->step_offset;
5189 door->panel.start_step = num_panel_steps - num_door_steps;
5190 door->panel.start_step_closing = door->panel.start_step;
5194 num_panel_steps = door_rect->height / door->step_offset;
5195 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5196 door->panel.start_step_closing = door->panel.start_step;
5197 door->panel.step_delay *= 2;
5204 void InitDoors(void)
5208 for (i = 0; door_part_controls[i].door_token != -1; i++)
5210 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5211 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5213 // initialize "start_step_opening" and "start_step_closing", if needed
5214 if (dpc->pos->start_step_opening == 0 &&
5215 dpc->pos->start_step_closing == 0)
5217 // dpc->pos->start_step_opening = dpc->pos->start_step;
5218 dpc->pos->start_step_closing = dpc->pos->start_step;
5221 // fill structure for door part draw order (sorted below)
5223 dpo->sort_priority = dpc->pos->sort_priority;
5226 // sort door part controls according to sort_priority and graphic number
5227 qsort(door_part_order, MAX_DOOR_PARTS,
5228 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5231 unsigned int OpenDoor(unsigned int door_state)
5233 if (door_state & DOOR_COPY_BACK)
5235 if (door_state & DOOR_OPEN_1)
5236 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5237 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5239 if (door_state & DOOR_OPEN_2)
5240 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5241 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5243 door_state &= ~DOOR_COPY_BACK;
5246 return MoveDoor(door_state);
5249 unsigned int CloseDoor(unsigned int door_state)
5251 unsigned int old_door_state = GetDoorState();
5253 if (!(door_state & DOOR_NO_COPY_BACK))
5255 if (old_door_state & DOOR_OPEN_1)
5256 BlitBitmap(backbuffer, bitmap_db_door_1,
5257 DX, DY, DXSIZE, DYSIZE, 0, 0);
5259 if (old_door_state & DOOR_OPEN_2)
5260 BlitBitmap(backbuffer, bitmap_db_door_2,
5261 VX, VY, VXSIZE, VYSIZE, 0, 0);
5263 door_state &= ~DOOR_NO_COPY_BACK;
5266 return MoveDoor(door_state);
5269 unsigned int GetDoorState(void)
5271 return MoveDoor(DOOR_GET_STATE);
5274 unsigned int SetDoorState(unsigned int door_state)
5276 return MoveDoor(door_state | DOOR_SET_STATE);
5279 static int euclid(int a, int b)
5281 return (b ? euclid(b, a % b) : a);
5284 unsigned int MoveDoor(unsigned int door_state)
5286 struct Rect door_rect_list[] =
5288 { DX, DY, DXSIZE, DYSIZE },
5289 { VX, VY, VXSIZE, VYSIZE }
5291 static int door1 = DOOR_CLOSE_1;
5292 static int door2 = DOOR_CLOSE_2;
5293 unsigned int door_delay = 0;
5294 unsigned int door_delay_value;
5297 if (door_state == DOOR_GET_STATE)
5298 return (door1 | door2);
5300 if (door_state & DOOR_SET_STATE)
5302 if (door_state & DOOR_ACTION_1)
5303 door1 = door_state & DOOR_ACTION_1;
5304 if (door_state & DOOR_ACTION_2)
5305 door2 = door_state & DOOR_ACTION_2;
5307 return (door1 | door2);
5310 if (!(door_state & DOOR_FORCE_REDRAW))
5312 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5313 door_state &= ~DOOR_OPEN_1;
5314 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5315 door_state &= ~DOOR_CLOSE_1;
5316 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5317 door_state &= ~DOOR_OPEN_2;
5318 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5319 door_state &= ~DOOR_CLOSE_2;
5322 if (global.autoplay_leveldir)
5324 door_state |= DOOR_NO_DELAY;
5325 door_state &= ~DOOR_CLOSE_ALL;
5328 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5329 door_state |= DOOR_NO_DELAY;
5331 if (door_state & DOOR_ACTION)
5333 boolean door_panel_drawn[NUM_DOORS];
5334 boolean panel_has_doors[NUM_DOORS];
5335 boolean door_part_skip[MAX_DOOR_PARTS];
5336 boolean door_part_done[MAX_DOOR_PARTS];
5337 boolean door_part_done_all;
5338 int num_steps[MAX_DOOR_PARTS];
5339 int max_move_delay = 0; // delay for complete animations of all doors
5340 int max_step_delay = 0; // delay (ms) between two animation frames
5341 int num_move_steps = 0; // number of animation steps for all doors
5342 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5343 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5344 int current_move_delay = 0;
5348 for (i = 0; i < NUM_DOORS; i++)
5349 panel_has_doors[i] = FALSE;
5351 for (i = 0; i < MAX_DOOR_PARTS; i++)
5353 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5354 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5355 int door_token = dpc->door_token;
5357 door_part_done[i] = FALSE;
5358 door_part_skip[i] = (!(door_state & door_token) ||
5362 for (i = 0; i < MAX_DOOR_PARTS; i++)
5364 int nr = door_part_order[i].nr;
5365 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5366 struct DoorPartPosInfo *pos = dpc->pos;
5367 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5368 int door_token = dpc->door_token;
5369 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5370 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5371 int step_xoffset = ABS(pos->step_xoffset);
5372 int step_yoffset = ABS(pos->step_yoffset);
5373 int step_delay = pos->step_delay;
5374 int current_door_state = door_state & door_token;
5375 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5376 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5377 boolean part_opening = (is_panel ? door_closing : door_opening);
5378 int start_step = (part_opening ? pos->start_step_opening :
5379 pos->start_step_closing);
5380 float move_xsize = (step_xoffset ? g->width : 0);
5381 float move_ysize = (step_yoffset ? g->height : 0);
5382 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5383 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5384 int move_steps = (move_xsteps && move_ysteps ?
5385 MIN(move_xsteps, move_ysteps) :
5386 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5387 int move_delay = move_steps * step_delay;
5389 if (door_part_skip[nr])
5392 max_move_delay = MAX(max_move_delay, move_delay);
5393 max_step_delay = (max_step_delay == 0 ? step_delay :
5394 euclid(max_step_delay, step_delay));
5395 num_steps[nr] = move_steps;
5399 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5401 panel_has_doors[door_index] = TRUE;
5405 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5407 num_move_steps = max_move_delay / max_step_delay;
5408 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5410 door_delay_value = max_step_delay;
5412 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5414 start = num_move_steps - 1;
5418 // opening door sound has priority over simultaneously closing door
5419 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5421 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5423 if (door_state & DOOR_OPEN_1)
5424 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5425 if (door_state & DOOR_OPEN_2)
5426 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5428 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5430 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5432 if (door_state & DOOR_CLOSE_1)
5433 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5434 if (door_state & DOOR_CLOSE_2)
5435 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5439 for (k = start; k < num_move_steps; k++)
5441 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5443 door_part_done_all = TRUE;
5445 for (i = 0; i < NUM_DOORS; i++)
5446 door_panel_drawn[i] = FALSE;
5448 for (i = 0; i < MAX_DOOR_PARTS; i++)
5450 int nr = door_part_order[i].nr;
5451 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5452 struct DoorPartPosInfo *pos = dpc->pos;
5453 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5454 int door_token = dpc->door_token;
5455 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5456 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5457 boolean is_panel_and_door_has_closed = FALSE;
5458 struct Rect *door_rect = &door_rect_list[door_index];
5459 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5461 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5462 int current_door_state = door_state & door_token;
5463 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5464 boolean door_closing = !door_opening;
5465 boolean part_opening = (is_panel ? door_closing : door_opening);
5466 boolean part_closing = !part_opening;
5467 int start_step = (part_opening ? pos->start_step_opening :
5468 pos->start_step_closing);
5469 int step_delay = pos->step_delay;
5470 int step_factor = step_delay / max_step_delay;
5471 int k1 = (step_factor ? k / step_factor + 1 : k);
5472 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5473 int kk = MAX(0, k2);
5476 int src_x, src_y, src_xx, src_yy;
5477 int dst_x, dst_y, dst_xx, dst_yy;
5480 if (door_part_skip[nr])
5483 if (!(door_state & door_token))
5491 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5492 int kk_door = MAX(0, k2_door);
5493 int sync_frame = kk_door * door_delay_value;
5494 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5496 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5497 &g_src_x, &g_src_y);
5502 if (!door_panel_drawn[door_index])
5504 ClearRectangle(drawto, door_rect->x, door_rect->y,
5505 door_rect->width, door_rect->height);
5507 door_panel_drawn[door_index] = TRUE;
5510 // draw opening or closing door parts
5512 if (pos->step_xoffset < 0) // door part on right side
5515 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5518 if (dst_xx + width > door_rect->width)
5519 width = door_rect->width - dst_xx;
5521 else // door part on left side
5524 dst_xx = pos->x - kk * pos->step_xoffset;
5528 src_xx = ABS(dst_xx);
5532 width = g->width - src_xx;
5534 if (width > door_rect->width)
5535 width = door_rect->width;
5537 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5540 if (pos->step_yoffset < 0) // door part on bottom side
5543 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5546 if (dst_yy + height > door_rect->height)
5547 height = door_rect->height - dst_yy;
5549 else // door part on top side
5552 dst_yy = pos->y - kk * pos->step_yoffset;
5556 src_yy = ABS(dst_yy);
5560 height = g->height - src_yy;
5563 src_x = g_src_x + src_xx;
5564 src_y = g_src_y + src_yy;
5566 dst_x = door_rect->x + dst_xx;
5567 dst_y = door_rect->y + dst_yy;
5569 is_panel_and_door_has_closed =
5572 panel_has_doors[door_index] &&
5573 k >= num_move_steps_doors_only - 1);
5575 if (width >= 0 && width <= g->width &&
5576 height >= 0 && height <= g->height &&
5577 !is_panel_and_door_has_closed)
5579 if (is_panel || !pos->draw_masked)
5580 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5583 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5587 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5589 if ((part_opening && (width < 0 || height < 0)) ||
5590 (part_closing && (width >= g->width && height >= g->height)))
5591 door_part_done[nr] = TRUE;
5593 // continue door part animations, but not panel after door has closed
5594 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5595 door_part_done_all = FALSE;
5598 if (!(door_state & DOOR_NO_DELAY))
5602 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5604 current_move_delay += max_step_delay;
5606 // prevent OS (Windows) from complaining about program not responding
5610 if (door_part_done_all)
5614 if (!(door_state & DOOR_NO_DELAY))
5616 // wait for specified door action post delay
5617 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5618 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5619 else if (door_state & DOOR_ACTION_1)
5620 door_delay_value = door_1.post_delay;
5621 else if (door_state & DOOR_ACTION_2)
5622 door_delay_value = door_2.post_delay;
5624 while (!DelayReached(&door_delay, door_delay_value))
5629 if (door_state & DOOR_ACTION_1)
5630 door1 = door_state & DOOR_ACTION_1;
5631 if (door_state & DOOR_ACTION_2)
5632 door2 = door_state & DOOR_ACTION_2;
5634 // draw masked border over door area
5635 DrawMaskedBorder(REDRAW_DOOR_1);
5636 DrawMaskedBorder(REDRAW_DOOR_2);
5638 ClearAutoRepeatKeyEvents();
5640 return (door1 | door2);
5643 static boolean useSpecialEditorDoor(void)
5645 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5646 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5648 // do not draw special editor door if editor border defined or redefined
5649 if (graphic_info[graphic].bitmap != NULL || redefined)
5652 // do not draw special editor door if global border defined to be empty
5653 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5656 // do not draw special editor door if viewport definitions do not match
5660 EY + EYSIZE != VY + VYSIZE)
5666 void DrawSpecialEditorDoor(void)
5668 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5669 int top_border_width = gfx1->width;
5670 int top_border_height = gfx1->height;
5671 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5672 int ex = EX - outer_border;
5673 int ey = EY - outer_border;
5674 int vy = VY - outer_border;
5675 int exsize = EXSIZE + 2 * outer_border;
5677 if (!useSpecialEditorDoor())
5680 // draw bigger level editor toolbox window
5681 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5682 top_border_width, top_border_height, ex, ey - top_border_height);
5683 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5684 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5686 redraw_mask |= REDRAW_ALL;
5689 void UndrawSpecialEditorDoor(void)
5691 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5692 int top_border_width = gfx1->width;
5693 int top_border_height = gfx1->height;
5694 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5695 int ex = EX - outer_border;
5696 int ey = EY - outer_border;
5697 int ey_top = ey - top_border_height;
5698 int exsize = EXSIZE + 2 * outer_border;
5699 int eysize = EYSIZE + 2 * outer_border;
5701 if (!useSpecialEditorDoor())
5704 // draw normal tape recorder window
5705 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5707 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5708 ex, ey_top, top_border_width, top_border_height,
5710 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5711 ex, ey, exsize, eysize, ex, ey);
5715 // if screen background is set to "[NONE]", clear editor toolbox window
5716 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5717 ClearRectangle(drawto, ex, ey, exsize, eysize);
5720 redraw_mask |= REDRAW_ALL;
5724 // ---------- new tool button stuff -------------------------------------------
5729 struct TextPosInfo *pos;
5731 boolean is_touch_button;
5733 } toolbutton_info[NUM_TOOL_BUTTONS] =
5736 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5737 TOOL_CTRL_ID_YES, FALSE, "yes"
5740 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5741 TOOL_CTRL_ID_NO, FALSE, "no"
5744 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5745 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5748 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5749 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5752 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5753 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5756 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5757 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5760 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5761 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5764 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5765 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5768 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5769 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5772 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5773 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5777 void CreateToolButtons(void)
5781 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5783 int graphic = toolbutton_info[i].graphic;
5784 struct GraphicInfo *gfx = &graphic_info[graphic];
5785 struct TextPosInfo *pos = toolbutton_info[i].pos;
5786 struct GadgetInfo *gi;
5787 Bitmap *deco_bitmap = None;
5788 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5789 unsigned int event_mask = GD_EVENT_RELEASED;
5790 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5791 int base_x = (is_touch_button ? 0 : DX);
5792 int base_y = (is_touch_button ? 0 : DY);
5793 int gd_x = gfx->src_x;
5794 int gd_y = gfx->src_y;
5795 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5796 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5801 if (global.use_envelope_request && !is_touch_button)
5803 setRequestPosition(&base_x, &base_y, TRUE);
5805 // check if request buttons are outside of envelope and fix, if needed
5806 if (x < 0 || x + gfx->width > request.width ||
5807 y < 0 || y + gfx->height > request.height)
5809 if (id == TOOL_CTRL_ID_YES)
5812 y = request.height - 2 * request.border_size - gfx->height;
5814 else if (id == TOOL_CTRL_ID_NO)
5816 x = request.width - 2 * request.border_size - gfx->width;
5817 y = request.height - 2 * request.border_size - gfx->height;
5819 else if (id == TOOL_CTRL_ID_CONFIRM)
5821 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5822 y = request.height - 2 * request.border_size - gfx->height;
5824 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5826 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5828 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5829 y = request.height - 2 * request.border_size - gfx->height * 2;
5831 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5832 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5837 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5839 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5841 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5842 pos->size, &deco_bitmap, &deco_x, &deco_y);
5843 deco_xpos = (gfx->width - pos->size) / 2;
5844 deco_ypos = (gfx->height - pos->size) / 2;
5847 gi = CreateGadget(GDI_CUSTOM_ID, id,
5848 GDI_IMAGE_ID, graphic,
5849 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5852 GDI_WIDTH, gfx->width,
5853 GDI_HEIGHT, gfx->height,
5854 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5855 GDI_STATE, GD_BUTTON_UNPRESSED,
5856 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5857 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5858 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5859 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5860 GDI_DECORATION_SIZE, pos->size, pos->size,
5861 GDI_DECORATION_SHIFTING, 1, 1,
5862 GDI_DIRECT_DRAW, FALSE,
5863 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5864 GDI_EVENT_MASK, event_mask,
5865 GDI_CALLBACK_ACTION, HandleToolButtons,
5869 Fail("cannot create gadget");
5871 tool_gadget[id] = gi;
5875 void FreeToolButtons(void)
5879 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5880 FreeGadget(tool_gadget[i]);
5883 static void UnmapToolButtons(void)
5887 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5888 UnmapGadget(tool_gadget[i]);
5891 static void HandleToolButtons(struct GadgetInfo *gi)
5893 request_gadget_id = gi->custom_id;
5896 static struct Mapping_EM_to_RND_object
5899 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5900 boolean is_backside; // backside of moving element
5906 em_object_mapping_list[GAME_TILE_MAX + 1] =
5909 Zborder, FALSE, FALSE,
5913 Zplayer, FALSE, FALSE,
5922 Ztank, FALSE, FALSE,
5926 Zeater, FALSE, FALSE,
5930 Zdynamite, FALSE, FALSE,
5934 Zboom, FALSE, FALSE,
5939 Xchain, FALSE, FALSE,
5940 EL_DEFAULT, ACTION_EXPLODING, -1
5943 Xboom_bug, FALSE, FALSE,
5944 EL_BUG, ACTION_EXPLODING, -1
5947 Xboom_tank, FALSE, FALSE,
5948 EL_SPACESHIP, ACTION_EXPLODING, -1
5951 Xboom_android, FALSE, FALSE,
5952 EL_EMC_ANDROID, ACTION_OTHER, -1
5955 Xboom_1, FALSE, FALSE,
5956 EL_DEFAULT, ACTION_EXPLODING, -1
5959 Xboom_2, FALSE, FALSE,
5960 EL_DEFAULT, ACTION_EXPLODING, -1
5964 Xblank, TRUE, FALSE,
5969 Xsplash_e, FALSE, FALSE,
5970 EL_ACID_SPLASH_RIGHT, -1, -1
5973 Xsplash_w, FALSE, FALSE,
5974 EL_ACID_SPLASH_LEFT, -1, -1
5978 Xplant, TRUE, FALSE,
5979 EL_EMC_PLANT, -1, -1
5982 Yplant, FALSE, FALSE,
5983 EL_EMC_PLANT, -1, -1
5987 Xacid_1, TRUE, FALSE,
5991 Xacid_2, FALSE, FALSE,
5995 Xacid_3, FALSE, FALSE,
5999 Xacid_4, FALSE, FALSE,
6003 Xacid_5, FALSE, FALSE,
6007 Xacid_6, FALSE, FALSE,
6011 Xacid_7, FALSE, FALSE,
6015 Xacid_8, FALSE, FALSE,
6020 Xfake_acid_1, TRUE, FALSE,
6021 EL_EMC_FAKE_ACID, -1, -1
6024 Xfake_acid_2, FALSE, FALSE,
6025 EL_EMC_FAKE_ACID, -1, -1
6028 Xfake_acid_3, FALSE, FALSE,
6029 EL_EMC_FAKE_ACID, -1, -1
6032 Xfake_acid_4, FALSE, FALSE,
6033 EL_EMC_FAKE_ACID, -1, -1
6036 Xfake_acid_5, FALSE, FALSE,
6037 EL_EMC_FAKE_ACID, -1, -1
6040 Xfake_acid_6, FALSE, FALSE,
6041 EL_EMC_FAKE_ACID, -1, -1
6044 Xfake_acid_7, FALSE, FALSE,
6045 EL_EMC_FAKE_ACID, -1, -1
6048 Xfake_acid_8, FALSE, FALSE,
6049 EL_EMC_FAKE_ACID, -1, -1
6053 Xfake_acid_1_player, FALSE, FALSE,
6054 EL_EMC_FAKE_ACID, -1, -1
6057 Xfake_acid_2_player, FALSE, FALSE,
6058 EL_EMC_FAKE_ACID, -1, -1
6061 Xfake_acid_3_player, FALSE, FALSE,
6062 EL_EMC_FAKE_ACID, -1, -1
6065 Xfake_acid_4_player, FALSE, FALSE,
6066 EL_EMC_FAKE_ACID, -1, -1
6069 Xfake_acid_5_player, FALSE, FALSE,
6070 EL_EMC_FAKE_ACID, -1, -1
6073 Xfake_acid_6_player, FALSE, FALSE,
6074 EL_EMC_FAKE_ACID, -1, -1
6077 Xfake_acid_7_player, FALSE, FALSE,
6078 EL_EMC_FAKE_ACID, -1, -1
6081 Xfake_acid_8_player, FALSE, FALSE,
6082 EL_EMC_FAKE_ACID, -1, -1
6086 Xgrass, TRUE, FALSE,
6087 EL_EMC_GRASS, -1, -1
6090 Ygrass_nB, FALSE, FALSE,
6091 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6094 Ygrass_eB, FALSE, FALSE,
6095 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6098 Ygrass_sB, FALSE, FALSE,
6099 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6102 Ygrass_wB, FALSE, FALSE,
6103 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6111 Ydirt_nB, FALSE, FALSE,
6112 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6115 Ydirt_eB, FALSE, FALSE,
6116 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6119 Ydirt_sB, FALSE, FALSE,
6120 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6123 Ydirt_wB, FALSE, FALSE,
6124 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6128 Xandroid, TRUE, FALSE,
6129 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6132 Xandroid_1_n, FALSE, FALSE,
6133 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6136 Xandroid_2_n, FALSE, FALSE,
6137 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6140 Xandroid_1_e, FALSE, FALSE,
6141 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6144 Xandroid_2_e, FALSE, FALSE,
6145 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6148 Xandroid_1_w, FALSE, FALSE,
6149 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6152 Xandroid_2_w, FALSE, FALSE,
6153 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6156 Xandroid_1_s, FALSE, FALSE,
6157 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6160 Xandroid_2_s, FALSE, FALSE,
6161 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6164 Yandroid_n, FALSE, FALSE,
6165 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6168 Yandroid_nB, FALSE, TRUE,
6169 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6172 Yandroid_ne, FALSE, FALSE,
6173 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6176 Yandroid_neB, FALSE, TRUE,
6177 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6180 Yandroid_e, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6184 Yandroid_eB, FALSE, TRUE,
6185 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6188 Yandroid_se, FALSE, FALSE,
6189 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6192 Yandroid_seB, FALSE, TRUE,
6193 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6196 Yandroid_s, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6200 Yandroid_sB, FALSE, TRUE,
6201 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6204 Yandroid_sw, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6208 Yandroid_swB, FALSE, TRUE,
6209 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6212 Yandroid_w, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6216 Yandroid_wB, FALSE, TRUE,
6217 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6220 Yandroid_nw, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6224 Yandroid_nwB, FALSE, TRUE,
6225 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6229 Xeater_n, TRUE, FALSE,
6230 EL_YAMYAM_UP, -1, -1
6233 Xeater_e, TRUE, FALSE,
6234 EL_YAMYAM_RIGHT, -1, -1
6237 Xeater_w, TRUE, FALSE,
6238 EL_YAMYAM_LEFT, -1, -1
6241 Xeater_s, TRUE, FALSE,
6242 EL_YAMYAM_DOWN, -1, -1
6245 Yeater_n, FALSE, FALSE,
6246 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6249 Yeater_nB, FALSE, TRUE,
6250 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6253 Yeater_e, FALSE, FALSE,
6254 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6257 Yeater_eB, FALSE, TRUE,
6258 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6261 Yeater_s, FALSE, FALSE,
6262 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6265 Yeater_sB, FALSE, TRUE,
6266 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6269 Yeater_w, FALSE, FALSE,
6270 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6273 Yeater_wB, FALSE, TRUE,
6274 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6277 Yeater_stone, FALSE, FALSE,
6278 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6281 Yeater_spring, FALSE, FALSE,
6282 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6286 Xalien, TRUE, FALSE,
6290 Xalien_pause, FALSE, FALSE,
6294 Yalien_n, FALSE, FALSE,
6295 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6298 Yalien_nB, FALSE, TRUE,
6299 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6302 Yalien_e, FALSE, FALSE,
6303 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6306 Yalien_eB, FALSE, TRUE,
6307 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6310 Yalien_s, FALSE, FALSE,
6311 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6314 Yalien_sB, FALSE, TRUE,
6315 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6318 Yalien_w, FALSE, FALSE,
6319 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6322 Yalien_wB, FALSE, TRUE,
6323 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6326 Yalien_stone, FALSE, FALSE,
6327 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6330 Yalien_spring, FALSE, FALSE,
6331 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6335 Xbug_1_n, TRUE, FALSE,
6339 Xbug_1_e, TRUE, FALSE,
6340 EL_BUG_RIGHT, -1, -1
6343 Xbug_1_s, TRUE, FALSE,
6347 Xbug_1_w, TRUE, FALSE,
6351 Xbug_2_n, FALSE, FALSE,
6355 Xbug_2_e, FALSE, FALSE,
6356 EL_BUG_RIGHT, -1, -1
6359 Xbug_2_s, FALSE, FALSE,
6363 Xbug_2_w, FALSE, FALSE,
6367 Ybug_n, FALSE, FALSE,
6368 EL_BUG, ACTION_MOVING, MV_BIT_UP
6371 Ybug_nB, FALSE, TRUE,
6372 EL_BUG, ACTION_MOVING, MV_BIT_UP
6375 Ybug_e, FALSE, FALSE,
6376 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6379 Ybug_eB, FALSE, TRUE,
6380 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6383 Ybug_s, FALSE, FALSE,
6384 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6387 Ybug_sB, FALSE, TRUE,
6388 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6391 Ybug_w, FALSE, FALSE,
6392 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6395 Ybug_wB, FALSE, TRUE,
6396 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6399 Ybug_w_n, FALSE, FALSE,
6400 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6403 Ybug_n_e, FALSE, FALSE,
6404 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6407 Ybug_e_s, FALSE, FALSE,
6408 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6411 Ybug_s_w, FALSE, FALSE,
6412 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6415 Ybug_e_n, FALSE, FALSE,
6416 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6419 Ybug_s_e, FALSE, FALSE,
6420 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6423 Ybug_w_s, FALSE, FALSE,
6424 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6427 Ybug_n_w, FALSE, FALSE,
6428 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6431 Ybug_stone, FALSE, FALSE,
6432 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6435 Ybug_spring, FALSE, FALSE,
6436 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6440 Xtank_1_n, TRUE, FALSE,
6441 EL_SPACESHIP_UP, -1, -1
6444 Xtank_1_e, TRUE, FALSE,
6445 EL_SPACESHIP_RIGHT, -1, -1
6448 Xtank_1_s, TRUE, FALSE,
6449 EL_SPACESHIP_DOWN, -1, -1
6452 Xtank_1_w, TRUE, FALSE,
6453 EL_SPACESHIP_LEFT, -1, -1
6456 Xtank_2_n, FALSE, FALSE,
6457 EL_SPACESHIP_UP, -1, -1
6460 Xtank_2_e, FALSE, FALSE,
6461 EL_SPACESHIP_RIGHT, -1, -1
6464 Xtank_2_s, FALSE, FALSE,
6465 EL_SPACESHIP_DOWN, -1, -1
6468 Xtank_2_w, FALSE, FALSE,
6469 EL_SPACESHIP_LEFT, -1, -1
6472 Ytank_n, FALSE, FALSE,
6473 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6476 Ytank_nB, FALSE, TRUE,
6477 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6480 Ytank_e, FALSE, FALSE,
6481 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6484 Ytank_eB, FALSE, TRUE,
6485 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6488 Ytank_s, FALSE, FALSE,
6489 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6492 Ytank_sB, FALSE, TRUE,
6493 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6496 Ytank_w, FALSE, FALSE,
6497 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6500 Ytank_wB, FALSE, TRUE,
6501 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6504 Ytank_w_n, FALSE, FALSE,
6505 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6508 Ytank_n_e, FALSE, FALSE,
6509 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6512 Ytank_e_s, FALSE, FALSE,
6513 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6516 Ytank_s_w, FALSE, FALSE,
6517 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6520 Ytank_e_n, FALSE, FALSE,
6521 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6524 Ytank_s_e, FALSE, FALSE,
6525 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6528 Ytank_w_s, FALSE, FALSE,
6529 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6532 Ytank_n_w, FALSE, FALSE,
6533 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6536 Ytank_stone, FALSE, FALSE,
6537 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6540 Ytank_spring, FALSE, FALSE,
6541 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6545 Xemerald, TRUE, FALSE,
6549 Xemerald_pause, FALSE, FALSE,
6553 Xemerald_fall, FALSE, FALSE,
6557 Xemerald_shine, FALSE, FALSE,
6558 EL_EMERALD, ACTION_TWINKLING, -1
6561 Yemerald_s, FALSE, FALSE,
6562 EL_EMERALD, ACTION_FALLING, -1
6565 Yemerald_sB, FALSE, TRUE,
6566 EL_EMERALD, ACTION_FALLING, -1
6569 Yemerald_e, FALSE, FALSE,
6570 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6573 Yemerald_eB, FALSE, TRUE,
6574 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6577 Yemerald_w, FALSE, FALSE,
6578 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6581 Yemerald_wB, FALSE, TRUE,
6582 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6585 Yemerald_blank, FALSE, FALSE,
6586 EL_EMERALD, ACTION_COLLECTING, -1
6590 Xdiamond, TRUE, FALSE,
6594 Xdiamond_pause, FALSE, FALSE,
6598 Xdiamond_fall, FALSE, FALSE,
6602 Xdiamond_shine, FALSE, FALSE,
6603 EL_DIAMOND, ACTION_TWINKLING, -1
6606 Ydiamond_s, FALSE, FALSE,
6607 EL_DIAMOND, ACTION_FALLING, -1
6610 Ydiamond_sB, FALSE, TRUE,
6611 EL_DIAMOND, ACTION_FALLING, -1
6614 Ydiamond_e, FALSE, FALSE,
6615 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6618 Ydiamond_eB, FALSE, TRUE,
6619 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6622 Ydiamond_w, FALSE, FALSE,
6623 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6626 Ydiamond_wB, FALSE, TRUE,
6627 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6630 Ydiamond_blank, FALSE, FALSE,
6631 EL_DIAMOND, ACTION_COLLECTING, -1
6634 Ydiamond_stone, FALSE, FALSE,
6635 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6639 Xstone, TRUE, FALSE,
6643 Xstone_pause, FALSE, FALSE,
6647 Xstone_fall, FALSE, FALSE,
6651 Ystone_s, FALSE, FALSE,
6652 EL_ROCK, ACTION_FALLING, -1
6655 Ystone_sB, FALSE, TRUE,
6656 EL_ROCK, ACTION_FALLING, -1
6659 Ystone_e, FALSE, FALSE,
6660 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6663 Ystone_eB, FALSE, TRUE,
6664 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6667 Ystone_w, FALSE, FALSE,
6668 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6671 Ystone_wB, FALSE, TRUE,
6672 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6680 Xbomb_pause, FALSE, FALSE,
6684 Xbomb_fall, FALSE, FALSE,
6688 Ybomb_s, FALSE, FALSE,
6689 EL_BOMB, ACTION_FALLING, -1
6692 Ybomb_sB, FALSE, TRUE,
6693 EL_BOMB, ACTION_FALLING, -1
6696 Ybomb_e, FALSE, FALSE,
6697 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6700 Ybomb_eB, FALSE, TRUE,
6701 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6704 Ybomb_w, FALSE, FALSE,
6705 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6708 Ybomb_wB, FALSE, TRUE,
6709 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6712 Ybomb_blank, FALSE, FALSE,
6713 EL_BOMB, ACTION_ACTIVATING, -1
6721 Xnut_pause, FALSE, FALSE,
6725 Xnut_fall, FALSE, FALSE,
6729 Ynut_s, FALSE, FALSE,
6730 EL_NUT, ACTION_FALLING, -1
6733 Ynut_sB, FALSE, TRUE,
6734 EL_NUT, ACTION_FALLING, -1
6737 Ynut_e, FALSE, FALSE,
6738 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6741 Ynut_eB, FALSE, TRUE,
6742 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6745 Ynut_w, FALSE, FALSE,
6746 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6749 Ynut_wB, FALSE, TRUE,
6750 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6753 Ynut_stone, FALSE, FALSE,
6754 EL_NUT, ACTION_BREAKING, -1
6758 Xspring, TRUE, FALSE,
6762 Xspring_pause, FALSE, FALSE,
6766 Xspring_e, TRUE, FALSE,
6767 EL_SPRING_RIGHT, -1, -1
6770 Xspring_w, TRUE, FALSE,
6771 EL_SPRING_LEFT, -1, -1
6774 Xspring_fall, FALSE, FALSE,
6778 Yspring_s, FALSE, FALSE,
6779 EL_SPRING, ACTION_FALLING, -1
6782 Yspring_sB, FALSE, TRUE,
6783 EL_SPRING, ACTION_FALLING, -1
6786 Yspring_e, FALSE, FALSE,
6787 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6790 Yspring_eB, FALSE, TRUE,
6791 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6794 Yspring_w, FALSE, FALSE,
6795 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6798 Yspring_wB, FALSE, TRUE,
6799 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6802 Yspring_alien_e, FALSE, FALSE,
6803 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6806 Yspring_alien_eB, FALSE, TRUE,
6807 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6810 Yspring_alien_w, FALSE, FALSE,
6811 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6814 Yspring_alien_wB, FALSE, TRUE,
6815 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6819 Xpush_emerald_e, FALSE, FALSE,
6820 EL_EMERALD, -1, MV_BIT_RIGHT
6823 Xpush_emerald_w, FALSE, FALSE,
6824 EL_EMERALD, -1, MV_BIT_LEFT
6827 Xpush_diamond_e, FALSE, FALSE,
6828 EL_DIAMOND, -1, MV_BIT_RIGHT
6831 Xpush_diamond_w, FALSE, FALSE,
6832 EL_DIAMOND, -1, MV_BIT_LEFT
6835 Xpush_stone_e, FALSE, FALSE,
6836 EL_ROCK, -1, MV_BIT_RIGHT
6839 Xpush_stone_w, FALSE, FALSE,
6840 EL_ROCK, -1, MV_BIT_LEFT
6843 Xpush_bomb_e, FALSE, FALSE,
6844 EL_BOMB, -1, MV_BIT_RIGHT
6847 Xpush_bomb_w, FALSE, FALSE,
6848 EL_BOMB, -1, MV_BIT_LEFT
6851 Xpush_nut_e, FALSE, FALSE,
6852 EL_NUT, -1, MV_BIT_RIGHT
6855 Xpush_nut_w, FALSE, FALSE,
6856 EL_NUT, -1, MV_BIT_LEFT
6859 Xpush_spring_e, FALSE, FALSE,
6860 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6863 Xpush_spring_w, FALSE, FALSE,
6864 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6868 Xdynamite, TRUE, FALSE,
6869 EL_EM_DYNAMITE, -1, -1
6872 Ydynamite_blank, FALSE, FALSE,
6873 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6876 Xdynamite_1, TRUE, FALSE,
6877 EL_EM_DYNAMITE_ACTIVE, -1, -1
6880 Xdynamite_2, FALSE, FALSE,
6881 EL_EM_DYNAMITE_ACTIVE, -1, -1
6884 Xdynamite_3, FALSE, FALSE,
6885 EL_EM_DYNAMITE_ACTIVE, -1, -1
6888 Xdynamite_4, FALSE, FALSE,
6889 EL_EM_DYNAMITE_ACTIVE, -1, -1
6893 Xkey_1, TRUE, FALSE,
6897 Xkey_2, TRUE, FALSE,
6901 Xkey_3, TRUE, FALSE,
6905 Xkey_4, TRUE, FALSE,
6909 Xkey_5, TRUE, FALSE,
6910 EL_EMC_KEY_5, -1, -1
6913 Xkey_6, TRUE, FALSE,
6914 EL_EMC_KEY_6, -1, -1
6917 Xkey_7, TRUE, FALSE,
6918 EL_EMC_KEY_7, -1, -1
6921 Xkey_8, TRUE, FALSE,
6922 EL_EMC_KEY_8, -1, -1
6926 Xdoor_1, TRUE, FALSE,
6927 EL_EM_GATE_1, -1, -1
6930 Xdoor_2, TRUE, FALSE,
6931 EL_EM_GATE_2, -1, -1
6934 Xdoor_3, TRUE, FALSE,
6935 EL_EM_GATE_3, -1, -1
6938 Xdoor_4, TRUE, FALSE,
6939 EL_EM_GATE_4, -1, -1
6942 Xdoor_5, TRUE, FALSE,
6943 EL_EMC_GATE_5, -1, -1
6946 Xdoor_6, TRUE, FALSE,
6947 EL_EMC_GATE_6, -1, -1
6950 Xdoor_7, TRUE, FALSE,
6951 EL_EMC_GATE_7, -1, -1
6954 Xdoor_8, TRUE, FALSE,
6955 EL_EMC_GATE_8, -1, -1
6959 Xfake_door_1, TRUE, FALSE,
6960 EL_EM_GATE_1_GRAY, -1, -1
6963 Xfake_door_2, TRUE, FALSE,
6964 EL_EM_GATE_2_GRAY, -1, -1
6967 Xfake_door_3, TRUE, FALSE,
6968 EL_EM_GATE_3_GRAY, -1, -1
6971 Xfake_door_4, TRUE, FALSE,
6972 EL_EM_GATE_4_GRAY, -1, -1
6975 Xfake_door_5, TRUE, FALSE,
6976 EL_EMC_GATE_5_GRAY, -1, -1
6979 Xfake_door_6, TRUE, FALSE,
6980 EL_EMC_GATE_6_GRAY, -1, -1
6983 Xfake_door_7, TRUE, FALSE,
6984 EL_EMC_GATE_7_GRAY, -1, -1
6987 Xfake_door_8, TRUE, FALSE,
6988 EL_EMC_GATE_8_GRAY, -1, -1
6992 Xballoon, TRUE, FALSE,
6996 Yballoon_n, FALSE, FALSE,
6997 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7000 Yballoon_nB, FALSE, TRUE,
7001 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7004 Yballoon_e, FALSE, FALSE,
7005 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7008 Yballoon_eB, FALSE, TRUE,
7009 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7012 Yballoon_s, FALSE, FALSE,
7013 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7016 Yballoon_sB, FALSE, TRUE,
7017 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7020 Yballoon_w, FALSE, FALSE,
7021 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7024 Yballoon_wB, FALSE, TRUE,
7025 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7029 Xball_1, TRUE, FALSE,
7030 EL_EMC_MAGIC_BALL, -1, -1
7033 Yball_1, FALSE, FALSE,
7034 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7037 Xball_2, FALSE, FALSE,
7038 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7041 Yball_2, FALSE, FALSE,
7042 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7045 Yball_blank, FALSE, FALSE,
7046 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7050 Xamoeba_1, TRUE, FALSE,
7051 EL_AMOEBA_DRY, ACTION_OTHER, -1
7054 Xamoeba_2, FALSE, FALSE,
7055 EL_AMOEBA_DRY, ACTION_OTHER, -1
7058 Xamoeba_3, FALSE, FALSE,
7059 EL_AMOEBA_DRY, ACTION_OTHER, -1
7062 Xamoeba_4, FALSE, FALSE,
7063 EL_AMOEBA_DRY, ACTION_OTHER, -1
7066 Xamoeba_5, TRUE, FALSE,
7067 EL_AMOEBA_WET, ACTION_OTHER, -1
7070 Xamoeba_6, FALSE, FALSE,
7071 EL_AMOEBA_WET, ACTION_OTHER, -1
7074 Xamoeba_7, FALSE, FALSE,
7075 EL_AMOEBA_WET, ACTION_OTHER, -1
7078 Xamoeba_8, FALSE, FALSE,
7079 EL_AMOEBA_WET, ACTION_OTHER, -1
7084 EL_AMOEBA_DROP, ACTION_GROWING, -1
7087 Xdrip_fall, FALSE, FALSE,
7088 EL_AMOEBA_DROP, -1, -1
7091 Xdrip_stretch, FALSE, FALSE,
7092 EL_AMOEBA_DROP, ACTION_FALLING, -1
7095 Xdrip_stretchB, FALSE, TRUE,
7096 EL_AMOEBA_DROP, ACTION_FALLING, -1
7099 Ydrip_1_s, FALSE, FALSE,
7100 EL_AMOEBA_DROP, ACTION_FALLING, -1
7103 Ydrip_1_sB, FALSE, TRUE,
7104 EL_AMOEBA_DROP, ACTION_FALLING, -1
7107 Ydrip_2_s, FALSE, FALSE,
7108 EL_AMOEBA_DROP, ACTION_FALLING, -1
7111 Ydrip_2_sB, FALSE, TRUE,
7112 EL_AMOEBA_DROP, ACTION_FALLING, -1
7116 Xwonderwall, TRUE, FALSE,
7117 EL_MAGIC_WALL, -1, -1
7120 Ywonderwall, FALSE, FALSE,
7121 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7125 Xwheel, TRUE, FALSE,
7126 EL_ROBOT_WHEEL, -1, -1
7129 Ywheel, FALSE, FALSE,
7130 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7134 Xswitch, TRUE, FALSE,
7135 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7138 Yswitch, FALSE, FALSE,
7139 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7143 Xbumper, TRUE, FALSE,
7144 EL_EMC_SPRING_BUMPER, -1, -1
7147 Ybumper, FALSE, FALSE,
7148 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7152 Xacid_nw, TRUE, FALSE,
7153 EL_ACID_POOL_TOPLEFT, -1, -1
7156 Xacid_ne, TRUE, FALSE,
7157 EL_ACID_POOL_TOPRIGHT, -1, -1
7160 Xacid_sw, TRUE, FALSE,
7161 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7164 Xacid_s, TRUE, FALSE,
7165 EL_ACID_POOL_BOTTOM, -1, -1
7168 Xacid_se, TRUE, FALSE,
7169 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7173 Xfake_blank, TRUE, FALSE,
7174 EL_INVISIBLE_WALL, -1, -1
7177 Yfake_blank, FALSE, FALSE,
7178 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7182 Xfake_grass, TRUE, FALSE,
7183 EL_EMC_FAKE_GRASS, -1, -1
7186 Yfake_grass, FALSE, FALSE,
7187 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7191 Xfake_amoeba, TRUE, FALSE,
7192 EL_EMC_DRIPPER, -1, -1
7195 Yfake_amoeba, FALSE, FALSE,
7196 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7200 Xlenses, TRUE, FALSE,
7201 EL_EMC_LENSES, -1, -1
7205 Xmagnify, TRUE, FALSE,
7206 EL_EMC_MAGNIFIER, -1, -1
7211 EL_QUICKSAND_EMPTY, -1, -1
7214 Xsand_stone, TRUE, FALSE,
7215 EL_QUICKSAND_FULL, -1, -1
7218 Xsand_stonein_1, FALSE, TRUE,
7219 EL_ROCK, ACTION_FILLING, -1
7222 Xsand_stonein_2, FALSE, TRUE,
7223 EL_ROCK, ACTION_FILLING, -1
7226 Xsand_stonein_3, FALSE, TRUE,
7227 EL_ROCK, ACTION_FILLING, -1
7230 Xsand_stonein_4, FALSE, TRUE,
7231 EL_ROCK, ACTION_FILLING, -1
7234 Xsand_sandstone_1, FALSE, FALSE,
7235 EL_QUICKSAND_FILLING, -1, -1
7238 Xsand_sandstone_2, FALSE, FALSE,
7239 EL_QUICKSAND_FILLING, -1, -1
7242 Xsand_sandstone_3, FALSE, FALSE,
7243 EL_QUICKSAND_FILLING, -1, -1
7246 Xsand_sandstone_4, FALSE, FALSE,
7247 EL_QUICKSAND_FILLING, -1, -1
7250 Xsand_stonesand_1, FALSE, FALSE,
7251 EL_QUICKSAND_EMPTYING, -1, -1
7254 Xsand_stonesand_2, FALSE, FALSE,
7255 EL_QUICKSAND_EMPTYING, -1, -1
7258 Xsand_stonesand_3, FALSE, FALSE,
7259 EL_QUICKSAND_EMPTYING, -1, -1
7262 Xsand_stonesand_4, FALSE, FALSE,
7263 EL_QUICKSAND_EMPTYING, -1, -1
7266 Xsand_stoneout_1, FALSE, FALSE,
7267 EL_ROCK, ACTION_EMPTYING, -1
7270 Xsand_stoneout_2, FALSE, FALSE,
7271 EL_ROCK, ACTION_EMPTYING, -1
7274 Xsand_stonesand_quickout_1, FALSE, FALSE,
7275 EL_QUICKSAND_EMPTYING, -1, -1
7278 Xsand_stonesand_quickout_2, FALSE, FALSE,
7279 EL_QUICKSAND_EMPTYING, -1, -1
7283 Xslide_ns, TRUE, FALSE,
7284 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7287 Yslide_ns_blank, FALSE, FALSE,
7288 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7291 Xslide_ew, TRUE, FALSE,
7292 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7295 Yslide_ew_blank, FALSE, FALSE,
7296 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7300 Xwind_n, TRUE, FALSE,
7301 EL_BALLOON_SWITCH_UP, -1, -1
7304 Xwind_e, TRUE, FALSE,
7305 EL_BALLOON_SWITCH_RIGHT, -1, -1
7308 Xwind_s, TRUE, FALSE,
7309 EL_BALLOON_SWITCH_DOWN, -1, -1
7312 Xwind_w, TRUE, FALSE,
7313 EL_BALLOON_SWITCH_LEFT, -1, -1
7316 Xwind_any, TRUE, FALSE,
7317 EL_BALLOON_SWITCH_ANY, -1, -1
7320 Xwind_stop, TRUE, FALSE,
7321 EL_BALLOON_SWITCH_NONE, -1, -1
7326 EL_EM_EXIT_CLOSED, -1, -1
7329 Xexit_1, TRUE, FALSE,
7330 EL_EM_EXIT_OPEN, -1, -1
7333 Xexit_2, FALSE, FALSE,
7334 EL_EM_EXIT_OPEN, -1, -1
7337 Xexit_3, FALSE, FALSE,
7338 EL_EM_EXIT_OPEN, -1, -1
7342 Xpause, FALSE, FALSE,
7347 Xwall_1, TRUE, FALSE,
7351 Xwall_2, TRUE, FALSE,
7352 EL_EMC_WALL_14, -1, -1
7355 Xwall_3, TRUE, FALSE,
7356 EL_EMC_WALL_15, -1, -1
7359 Xwall_4, TRUE, FALSE,
7360 EL_EMC_WALL_16, -1, -1
7364 Xroundwall_1, TRUE, FALSE,
7365 EL_WALL_SLIPPERY, -1, -1
7368 Xroundwall_2, TRUE, FALSE,
7369 EL_EMC_WALL_SLIPPERY_2, -1, -1
7372 Xroundwall_3, TRUE, FALSE,
7373 EL_EMC_WALL_SLIPPERY_3, -1, -1
7376 Xroundwall_4, TRUE, FALSE,
7377 EL_EMC_WALL_SLIPPERY_4, -1, -1
7381 Xsteel_1, TRUE, FALSE,
7382 EL_STEELWALL, -1, -1
7385 Xsteel_2, TRUE, FALSE,
7386 EL_EMC_STEELWALL_2, -1, -1
7389 Xsteel_3, TRUE, FALSE,
7390 EL_EMC_STEELWALL_3, -1, -1
7393 Xsteel_4, TRUE, FALSE,
7394 EL_EMC_STEELWALL_4, -1, -1
7398 Xdecor_1, TRUE, FALSE,
7399 EL_EMC_WALL_8, -1, -1
7402 Xdecor_2, TRUE, FALSE,
7403 EL_EMC_WALL_6, -1, -1
7406 Xdecor_3, TRUE, FALSE,
7407 EL_EMC_WALL_4, -1, -1
7410 Xdecor_4, TRUE, FALSE,
7411 EL_EMC_WALL_7, -1, -1
7414 Xdecor_5, TRUE, FALSE,
7415 EL_EMC_WALL_5, -1, -1
7418 Xdecor_6, TRUE, FALSE,
7419 EL_EMC_WALL_9, -1, -1
7422 Xdecor_7, TRUE, FALSE,
7423 EL_EMC_WALL_10, -1, -1
7426 Xdecor_8, TRUE, FALSE,
7427 EL_EMC_WALL_1, -1, -1
7430 Xdecor_9, TRUE, FALSE,
7431 EL_EMC_WALL_2, -1, -1
7434 Xdecor_10, TRUE, FALSE,
7435 EL_EMC_WALL_3, -1, -1
7438 Xdecor_11, TRUE, FALSE,
7439 EL_EMC_WALL_11, -1, -1
7442 Xdecor_12, TRUE, FALSE,
7443 EL_EMC_WALL_12, -1, -1
7447 Xalpha_0, TRUE, FALSE,
7448 EL_CHAR('0'), -1, -1
7451 Xalpha_1, TRUE, FALSE,
7452 EL_CHAR('1'), -1, -1
7455 Xalpha_2, TRUE, FALSE,
7456 EL_CHAR('2'), -1, -1
7459 Xalpha_3, TRUE, FALSE,
7460 EL_CHAR('3'), -1, -1
7463 Xalpha_4, TRUE, FALSE,
7464 EL_CHAR('4'), -1, -1
7467 Xalpha_5, TRUE, FALSE,
7468 EL_CHAR('5'), -1, -1
7471 Xalpha_6, TRUE, FALSE,
7472 EL_CHAR('6'), -1, -1
7475 Xalpha_7, TRUE, FALSE,
7476 EL_CHAR('7'), -1, -1
7479 Xalpha_8, TRUE, FALSE,
7480 EL_CHAR('8'), -1, -1
7483 Xalpha_9, TRUE, FALSE,
7484 EL_CHAR('9'), -1, -1
7487 Xalpha_excla, TRUE, FALSE,
7488 EL_CHAR('!'), -1, -1
7491 Xalpha_apost, TRUE, FALSE,
7492 EL_CHAR('\''), -1, -1
7495 Xalpha_comma, TRUE, FALSE,
7496 EL_CHAR(','), -1, -1
7499 Xalpha_minus, TRUE, FALSE,
7500 EL_CHAR('-'), -1, -1
7503 Xalpha_perio, TRUE, FALSE,
7504 EL_CHAR('.'), -1, -1
7507 Xalpha_colon, TRUE, FALSE,
7508 EL_CHAR(':'), -1, -1
7511 Xalpha_quest, TRUE, FALSE,
7512 EL_CHAR('?'), -1, -1
7515 Xalpha_a, TRUE, FALSE,
7516 EL_CHAR('A'), -1, -1
7519 Xalpha_b, TRUE, FALSE,
7520 EL_CHAR('B'), -1, -1
7523 Xalpha_c, TRUE, FALSE,
7524 EL_CHAR('C'), -1, -1
7527 Xalpha_d, TRUE, FALSE,
7528 EL_CHAR('D'), -1, -1
7531 Xalpha_e, TRUE, FALSE,
7532 EL_CHAR('E'), -1, -1
7535 Xalpha_f, TRUE, FALSE,
7536 EL_CHAR('F'), -1, -1
7539 Xalpha_g, TRUE, FALSE,
7540 EL_CHAR('G'), -1, -1
7543 Xalpha_h, TRUE, FALSE,
7544 EL_CHAR('H'), -1, -1
7547 Xalpha_i, TRUE, FALSE,
7548 EL_CHAR('I'), -1, -1
7551 Xalpha_j, TRUE, FALSE,
7552 EL_CHAR('J'), -1, -1
7555 Xalpha_k, TRUE, FALSE,
7556 EL_CHAR('K'), -1, -1
7559 Xalpha_l, TRUE, FALSE,
7560 EL_CHAR('L'), -1, -1
7563 Xalpha_m, TRUE, FALSE,
7564 EL_CHAR('M'), -1, -1
7567 Xalpha_n, TRUE, FALSE,
7568 EL_CHAR('N'), -1, -1
7571 Xalpha_o, TRUE, FALSE,
7572 EL_CHAR('O'), -1, -1
7575 Xalpha_p, TRUE, FALSE,
7576 EL_CHAR('P'), -1, -1
7579 Xalpha_q, TRUE, FALSE,
7580 EL_CHAR('Q'), -1, -1
7583 Xalpha_r, TRUE, FALSE,
7584 EL_CHAR('R'), -1, -1
7587 Xalpha_s, TRUE, FALSE,
7588 EL_CHAR('S'), -1, -1
7591 Xalpha_t, TRUE, FALSE,
7592 EL_CHAR('T'), -1, -1
7595 Xalpha_u, TRUE, FALSE,
7596 EL_CHAR('U'), -1, -1
7599 Xalpha_v, TRUE, FALSE,
7600 EL_CHAR('V'), -1, -1
7603 Xalpha_w, TRUE, FALSE,
7604 EL_CHAR('W'), -1, -1
7607 Xalpha_x, TRUE, FALSE,
7608 EL_CHAR('X'), -1, -1
7611 Xalpha_y, TRUE, FALSE,
7612 EL_CHAR('Y'), -1, -1
7615 Xalpha_z, TRUE, FALSE,
7616 EL_CHAR('Z'), -1, -1
7619 Xalpha_arrow_e, TRUE, FALSE,
7620 EL_CHAR('>'), -1, -1
7623 Xalpha_arrow_w, TRUE, FALSE,
7624 EL_CHAR('<'), -1, -1
7627 Xalpha_copyr, TRUE, FALSE,
7628 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7632 Ykey_1_blank, FALSE, FALSE,
7633 EL_EM_KEY_1, ACTION_COLLECTING, -1
7636 Ykey_2_blank, FALSE, FALSE,
7637 EL_EM_KEY_2, ACTION_COLLECTING, -1
7640 Ykey_3_blank, FALSE, FALSE,
7641 EL_EM_KEY_3, ACTION_COLLECTING, -1
7644 Ykey_4_blank, FALSE, FALSE,
7645 EL_EM_KEY_4, ACTION_COLLECTING, -1
7648 Ykey_5_blank, FALSE, FALSE,
7649 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7652 Ykey_6_blank, FALSE, FALSE,
7653 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7656 Ykey_7_blank, FALSE, FALSE,
7657 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7660 Ykey_8_blank, FALSE, FALSE,
7661 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7664 Ylenses_blank, FALSE, FALSE,
7665 EL_EMC_LENSES, ACTION_COLLECTING, -1
7668 Ymagnify_blank, FALSE, FALSE,
7669 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7672 Ygrass_blank, FALSE, FALSE,
7673 EL_EMC_GRASS, ACTION_SNAPPING, -1
7676 Ydirt_blank, FALSE, FALSE,
7677 EL_SAND, ACTION_SNAPPING, -1
7686 static struct Mapping_EM_to_RND_player
7695 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7699 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7703 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7707 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7711 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7715 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7719 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7723 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7727 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7731 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7735 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7739 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7743 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7747 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7751 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7755 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7759 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7763 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7767 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7771 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7775 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7779 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7783 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7787 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7791 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7795 EL_PLAYER_1, ACTION_DEFAULT, -1,
7799 EL_PLAYER_2, ACTION_DEFAULT, -1,
7803 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7807 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7811 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7815 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7819 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7823 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7827 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7831 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7835 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7839 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7843 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7847 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7851 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7855 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7859 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7863 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7867 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7871 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7875 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7879 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7883 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7887 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7891 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7895 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7899 EL_PLAYER_3, ACTION_DEFAULT, -1,
7903 EL_PLAYER_4, ACTION_DEFAULT, -1,
7912 int map_element_RND_to_EM_cave(int element_rnd)
7914 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7915 static boolean mapping_initialized = FALSE;
7917 if (!mapping_initialized)
7921 // return "Xalpha_quest" for all undefined elements in mapping array
7922 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7923 mapping_RND_to_EM[i] = Xalpha_quest;
7925 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7926 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7927 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7928 em_object_mapping_list[i].element_em;
7930 mapping_initialized = TRUE;
7933 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7935 Warn("invalid RND level element %d", element_rnd);
7940 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7943 int map_element_EM_to_RND_cave(int element_em_cave)
7945 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7946 static boolean mapping_initialized = FALSE;
7948 if (!mapping_initialized)
7952 // return "EL_UNKNOWN" for all undefined elements in mapping array
7953 for (i = 0; i < GAME_TILE_MAX; i++)
7954 mapping_EM_to_RND[i] = EL_UNKNOWN;
7956 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7957 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7958 em_object_mapping_list[i].element_rnd;
7960 mapping_initialized = TRUE;
7963 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7965 Warn("invalid EM cave element %d", element_em_cave);
7970 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7973 int map_element_EM_to_RND_game(int element_em_game)
7975 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7976 static boolean mapping_initialized = FALSE;
7978 if (!mapping_initialized)
7982 // return "EL_UNKNOWN" for all undefined elements in mapping array
7983 for (i = 0; i < GAME_TILE_MAX; i++)
7984 mapping_EM_to_RND[i] = EL_UNKNOWN;
7986 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7987 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7988 em_object_mapping_list[i].element_rnd;
7990 mapping_initialized = TRUE;
7993 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7995 Warn("invalid EM game element %d", element_em_game);
8000 return mapping_EM_to_RND[element_em_game];
8003 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8005 struct LevelInfo_EM *level_em = level->native_em_level;
8006 struct CAVE *cav = level_em->cav;
8009 for (i = 0; i < GAME_TILE_MAX; i++)
8010 cav->android_array[i] = Cblank;
8012 for (i = 0; i < level->num_android_clone_elements; i++)
8014 int element_rnd = level->android_clone_element[i];
8015 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8017 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8018 if (em_object_mapping_list[j].element_rnd == element_rnd)
8019 cav->android_array[em_object_mapping_list[j].element_em] =
8024 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8026 struct LevelInfo_EM *level_em = level->native_em_level;
8027 struct CAVE *cav = level_em->cav;
8030 level->num_android_clone_elements = 0;
8032 for (i = 0; i < GAME_TILE_MAX; i++)
8034 int element_em_cave = cav->android_array[i];
8036 boolean element_found = FALSE;
8038 if (element_em_cave == Cblank)
8041 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8043 for (j = 0; j < level->num_android_clone_elements; j++)
8044 if (level->android_clone_element[j] == element_rnd)
8045 element_found = TRUE;
8049 level->android_clone_element[level->num_android_clone_elements++] =
8052 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8057 if (level->num_android_clone_elements == 0)
8059 level->num_android_clone_elements = 1;
8060 level->android_clone_element[0] = EL_EMPTY;
8064 int map_direction_RND_to_EM(int direction)
8066 return (direction == MV_UP ? 0 :
8067 direction == MV_RIGHT ? 1 :
8068 direction == MV_DOWN ? 2 :
8069 direction == MV_LEFT ? 3 :
8073 int map_direction_EM_to_RND(int direction)
8075 return (direction == 0 ? MV_UP :
8076 direction == 1 ? MV_RIGHT :
8077 direction == 2 ? MV_DOWN :
8078 direction == 3 ? MV_LEFT :
8082 int map_element_RND_to_SP(int element_rnd)
8084 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8086 if (element_rnd >= EL_SP_START &&
8087 element_rnd <= EL_SP_END)
8088 element_sp = element_rnd - EL_SP_START;
8089 else if (element_rnd == EL_EMPTY_SPACE)
8091 else if (element_rnd == EL_INVISIBLE_WALL)
8097 int map_element_SP_to_RND(int element_sp)
8099 int element_rnd = EL_UNKNOWN;
8101 if (element_sp >= 0x00 &&
8103 element_rnd = EL_SP_START + element_sp;
8104 else if (element_sp == 0x28)
8105 element_rnd = EL_INVISIBLE_WALL;
8110 int map_action_SP_to_RND(int action_sp)
8114 case actActive: return ACTION_ACTIVE;
8115 case actImpact: return ACTION_IMPACT;
8116 case actExploding: return ACTION_EXPLODING;
8117 case actDigging: return ACTION_DIGGING;
8118 case actSnapping: return ACTION_SNAPPING;
8119 case actCollecting: return ACTION_COLLECTING;
8120 case actPassing: return ACTION_PASSING;
8121 case actPushing: return ACTION_PUSHING;
8122 case actDropping: return ACTION_DROPPING;
8124 default: return ACTION_DEFAULT;
8128 int map_element_RND_to_MM(int element_rnd)
8130 return (element_rnd >= EL_MM_START_1 &&
8131 element_rnd <= EL_MM_END_1 ?
8132 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8134 element_rnd >= EL_MM_START_2 &&
8135 element_rnd <= EL_MM_END_2 ?
8136 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8138 element_rnd >= EL_CHAR_START &&
8139 element_rnd <= EL_CHAR_END ?
8140 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8142 element_rnd >= EL_MM_RUNTIME_START &&
8143 element_rnd <= EL_MM_RUNTIME_END ?
8144 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8146 element_rnd >= EL_MM_DUMMY_START &&
8147 element_rnd <= EL_MM_DUMMY_END ?
8148 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8150 EL_MM_EMPTY_NATIVE);
8153 int map_element_MM_to_RND(int element_mm)
8155 return (element_mm == EL_MM_EMPTY_NATIVE ||
8156 element_mm == EL_DF_EMPTY_NATIVE ?
8159 element_mm >= EL_MM_START_1_NATIVE &&
8160 element_mm <= EL_MM_END_1_NATIVE ?
8161 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8163 element_mm >= EL_MM_START_2_NATIVE &&
8164 element_mm <= EL_MM_END_2_NATIVE ?
8165 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8167 element_mm >= EL_MM_CHAR_START_NATIVE &&
8168 element_mm <= EL_MM_CHAR_END_NATIVE ?
8169 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8171 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8172 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8173 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8175 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8176 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8177 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8182 int map_action_MM_to_RND(int action_mm)
8184 // all MM actions are defined to exactly match their RND counterparts
8188 int map_sound_MM_to_RND(int sound_mm)
8192 case SND_MM_GAME_LEVELTIME_CHARGING:
8193 return SND_GAME_LEVELTIME_CHARGING;
8195 case SND_MM_GAME_HEALTH_CHARGING:
8196 return SND_GAME_HEALTH_CHARGING;
8199 return SND_UNDEFINED;
8203 int map_mm_wall_element(int element)
8205 return (element >= EL_MM_STEEL_WALL_START &&
8206 element <= EL_MM_STEEL_WALL_END ?
8209 element >= EL_MM_WOODEN_WALL_START &&
8210 element <= EL_MM_WOODEN_WALL_END ?
8213 element >= EL_MM_ICE_WALL_START &&
8214 element <= EL_MM_ICE_WALL_END ?
8217 element >= EL_MM_AMOEBA_WALL_START &&
8218 element <= EL_MM_AMOEBA_WALL_END ?
8221 element >= EL_DF_STEEL_WALL_START &&
8222 element <= EL_DF_STEEL_WALL_END ?
8225 element >= EL_DF_WOODEN_WALL_START &&
8226 element <= EL_DF_WOODEN_WALL_END ?
8232 int map_mm_wall_element_editor(int element)
8236 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8237 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8238 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8239 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8240 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8241 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8243 default: return element;
8247 int get_next_element(int element)
8251 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8252 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8253 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8254 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8255 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8256 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8257 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8258 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8259 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8260 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8261 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8263 default: return element;
8267 int el2img_mm(int element_mm)
8269 return el2img(map_element_MM_to_RND(element_mm));
8272 int el_act_dir2img(int element, int action, int direction)
8274 element = GFX_ELEMENT(element);
8275 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8277 // direction_graphic[][] == graphic[] for undefined direction graphics
8278 return element_info[element].direction_graphic[action][direction];
8281 static int el_act_dir2crm(int element, int action, int direction)
8283 element = GFX_ELEMENT(element);
8284 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8286 // direction_graphic[][] == graphic[] for undefined direction graphics
8287 return element_info[element].direction_crumbled[action][direction];
8290 int el_act2img(int element, int action)
8292 element = GFX_ELEMENT(element);
8294 return element_info[element].graphic[action];
8297 int el_act2crm(int element, int action)
8299 element = GFX_ELEMENT(element);
8301 return element_info[element].crumbled[action];
8304 int el_dir2img(int element, int direction)
8306 element = GFX_ELEMENT(element);
8308 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8311 int el2baseimg(int element)
8313 return element_info[element].graphic[ACTION_DEFAULT];
8316 int el2img(int element)
8318 element = GFX_ELEMENT(element);
8320 return element_info[element].graphic[ACTION_DEFAULT];
8323 int el2edimg(int element)
8325 element = GFX_ELEMENT(element);
8327 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8330 int el2preimg(int element)
8332 element = GFX_ELEMENT(element);
8334 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8337 int el2panelimg(int element)
8339 element = GFX_ELEMENT(element);
8341 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8344 int font2baseimg(int font_nr)
8346 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8349 int getBeltNrFromBeltElement(int element)
8351 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8352 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8353 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8356 int getBeltNrFromBeltActiveElement(int element)
8358 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8359 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8360 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8363 int getBeltNrFromBeltSwitchElement(int element)
8365 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8366 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8367 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8370 int getBeltDirNrFromBeltElement(int element)
8372 static int belt_base_element[4] =
8374 EL_CONVEYOR_BELT_1_LEFT,
8375 EL_CONVEYOR_BELT_2_LEFT,
8376 EL_CONVEYOR_BELT_3_LEFT,
8377 EL_CONVEYOR_BELT_4_LEFT
8380 int belt_nr = getBeltNrFromBeltElement(element);
8381 int belt_dir_nr = element - belt_base_element[belt_nr];
8383 return (belt_dir_nr % 3);
8386 int getBeltDirNrFromBeltSwitchElement(int element)
8388 static int belt_base_element[4] =
8390 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8391 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8392 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8393 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8396 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8397 int belt_dir_nr = element - belt_base_element[belt_nr];
8399 return (belt_dir_nr % 3);
8402 int getBeltDirFromBeltElement(int element)
8404 static int belt_move_dir[3] =
8411 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8413 return belt_move_dir[belt_dir_nr];
8416 int getBeltDirFromBeltSwitchElement(int element)
8418 static int belt_move_dir[3] =
8425 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8427 return belt_move_dir[belt_dir_nr];
8430 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8432 static int belt_base_element[4] =
8434 EL_CONVEYOR_BELT_1_LEFT,
8435 EL_CONVEYOR_BELT_2_LEFT,
8436 EL_CONVEYOR_BELT_3_LEFT,
8437 EL_CONVEYOR_BELT_4_LEFT
8440 return belt_base_element[belt_nr] + belt_dir_nr;
8443 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8445 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8447 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8450 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8452 static int belt_base_element[4] =
8454 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8455 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8456 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8457 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8460 return belt_base_element[belt_nr] + belt_dir_nr;
8463 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8465 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8467 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8470 boolean swapTiles_EM(boolean is_pre_emc_cave)
8472 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8475 boolean getTeamMode_EM(void)
8477 return game.team_mode || network_playing;
8480 boolean isActivePlayer_EM(int player_nr)
8482 return stored_player[player_nr].active;
8485 unsigned int InitRND(int seed)
8487 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8488 return InitEngineRandom_EM(seed);
8489 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8490 return InitEngineRandom_SP(seed);
8491 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8492 return InitEngineRandom_MM(seed);
8494 return InitEngineRandom_RND(seed);
8497 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8498 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8500 static int get_effective_element_EM(int tile, int frame_em)
8502 int element = object_mapping[tile].element_rnd;
8503 int action = object_mapping[tile].action;
8504 boolean is_backside = object_mapping[tile].is_backside;
8505 boolean action_removing = (action == ACTION_DIGGING ||
8506 action == ACTION_SNAPPING ||
8507 action == ACTION_COLLECTING);
8515 return (frame_em > 5 ? EL_EMPTY : element);
8521 else // frame_em == 7
8532 case Ydiamond_stone:
8536 case Xdrip_stretchB:
8552 case Ymagnify_blank:
8555 case Xsand_stonein_1:
8556 case Xsand_stonein_2:
8557 case Xsand_stonein_3:
8558 case Xsand_stonein_4:
8562 return (is_backside || action_removing ? EL_EMPTY : element);
8567 static boolean check_linear_animation_EM(int tile)
8571 case Xsand_stonesand_1:
8572 case Xsand_stonesand_quickout_1:
8573 case Xsand_sandstone_1:
8574 case Xsand_stonein_1:
8575 case Xsand_stoneout_1:
8603 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8604 boolean has_crumbled_graphics,
8605 int crumbled, int sync_frame)
8607 // if element can be crumbled, but certain action graphics are just empty
8608 // space (like instantly snapping sand to empty space in 1 frame), do not
8609 // treat these empty space graphics as crumbled graphics in EMC engine
8610 if (crumbled == IMG_EMPTY_SPACE)
8611 has_crumbled_graphics = FALSE;
8613 if (has_crumbled_graphics)
8615 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8616 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8617 g_crumbled->anim_delay,
8618 g_crumbled->anim_mode,
8619 g_crumbled->anim_start_frame,
8622 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8623 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8625 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8626 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8628 g_em->has_crumbled_graphics = TRUE;
8632 g_em->crumbled_bitmap = NULL;
8633 g_em->crumbled_src_x = 0;
8634 g_em->crumbled_src_y = 0;
8635 g_em->crumbled_border_size = 0;
8636 g_em->crumbled_tile_size = 0;
8638 g_em->has_crumbled_graphics = FALSE;
8643 void ResetGfxAnimation_EM(int x, int y, int tile)
8649 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8650 int tile, int frame_em, int x, int y)
8652 int action = object_mapping[tile].action;
8653 int direction = object_mapping[tile].direction;
8654 int effective_element = get_effective_element_EM(tile, frame_em);
8655 int graphic = (direction == MV_NONE ?
8656 el_act2img(effective_element, action) :
8657 el_act_dir2img(effective_element, action, direction));
8658 struct GraphicInfo *g = &graphic_info[graphic];
8660 boolean action_removing = (action == ACTION_DIGGING ||
8661 action == ACTION_SNAPPING ||
8662 action == ACTION_COLLECTING);
8663 boolean action_moving = (action == ACTION_FALLING ||
8664 action == ACTION_MOVING ||
8665 action == ACTION_PUSHING ||
8666 action == ACTION_EATING ||
8667 action == ACTION_FILLING ||
8668 action == ACTION_EMPTYING);
8669 boolean action_falling = (action == ACTION_FALLING ||
8670 action == ACTION_FILLING ||
8671 action == ACTION_EMPTYING);
8673 // special case: graphic uses "2nd movement tile" and has defined
8674 // 7 frames for movement animation (or less) => use default graphic
8675 // for last (8th) frame which ends the movement animation
8676 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8678 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8679 graphic = (direction == MV_NONE ?
8680 el_act2img(effective_element, action) :
8681 el_act_dir2img(effective_element, action, direction));
8683 g = &graphic_info[graphic];
8686 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8690 else if (action_moving)
8692 boolean is_backside = object_mapping[tile].is_backside;
8696 int direction = object_mapping[tile].direction;
8697 int move_dir = (action_falling ? MV_DOWN : direction);
8702 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8703 if (g->double_movement && frame_em == 0)
8707 if (move_dir == MV_LEFT)
8708 GfxFrame[x - 1][y] = GfxFrame[x][y];
8709 else if (move_dir == MV_RIGHT)
8710 GfxFrame[x + 1][y] = GfxFrame[x][y];
8711 else if (move_dir == MV_UP)
8712 GfxFrame[x][y - 1] = GfxFrame[x][y];
8713 else if (move_dir == MV_DOWN)
8714 GfxFrame[x][y + 1] = GfxFrame[x][y];
8721 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8722 if (tile == Xsand_stonesand_quickout_1 ||
8723 tile == Xsand_stonesand_quickout_2)
8727 if (graphic_info[graphic].anim_global_sync)
8728 sync_frame = FrameCounter;
8729 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8730 sync_frame = GfxFrame[x][y];
8732 sync_frame = 0; // playfield border (pseudo steel)
8734 SetRandomAnimationValue(x, y);
8736 int frame = getAnimationFrame(g->anim_frames,
8739 g->anim_start_frame,
8742 g_em->unique_identifier =
8743 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8746 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8747 int tile, int frame_em, int x, int y)
8749 int action = object_mapping[tile].action;
8750 int direction = object_mapping[tile].direction;
8751 boolean is_backside = object_mapping[tile].is_backside;
8752 int effective_element = get_effective_element_EM(tile, frame_em);
8753 int effective_action = action;
8754 int graphic = (direction == MV_NONE ?
8755 el_act2img(effective_element, effective_action) :
8756 el_act_dir2img(effective_element, effective_action,
8758 int crumbled = (direction == MV_NONE ?
8759 el_act2crm(effective_element, effective_action) :
8760 el_act_dir2crm(effective_element, effective_action,
8762 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8763 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8764 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8765 struct GraphicInfo *g = &graphic_info[graphic];
8768 // special case: graphic uses "2nd movement tile" and has defined
8769 // 7 frames for movement animation (or less) => use default graphic
8770 // for last (8th) frame which ends the movement animation
8771 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8773 effective_action = ACTION_DEFAULT;
8774 graphic = (direction == MV_NONE ?
8775 el_act2img(effective_element, effective_action) :
8776 el_act_dir2img(effective_element, effective_action,
8778 crumbled = (direction == MV_NONE ?
8779 el_act2crm(effective_element, effective_action) :
8780 el_act_dir2crm(effective_element, effective_action,
8783 g = &graphic_info[graphic];
8786 if (graphic_info[graphic].anim_global_sync)
8787 sync_frame = FrameCounter;
8788 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8789 sync_frame = GfxFrame[x][y];
8791 sync_frame = 0; // playfield border (pseudo steel)
8793 SetRandomAnimationValue(x, y);
8795 int frame = getAnimationFrame(g->anim_frames,
8798 g->anim_start_frame,
8801 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8802 g->double_movement && is_backside);
8804 // (updating the "crumbled" graphic definitions is probably not really needed,
8805 // as animations for crumbled graphics can't be longer than one EMC cycle)
8806 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8810 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8811 int player_nr, int anim, int frame_em)
8813 int element = player_mapping[player_nr][anim].element_rnd;
8814 int action = player_mapping[player_nr][anim].action;
8815 int direction = player_mapping[player_nr][anim].direction;
8816 int graphic = (direction == MV_NONE ?
8817 el_act2img(element, action) :
8818 el_act_dir2img(element, action, direction));
8819 struct GraphicInfo *g = &graphic_info[graphic];
8822 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8824 stored_player[player_nr].StepFrame = frame_em;
8826 sync_frame = stored_player[player_nr].Frame;
8828 int frame = getAnimationFrame(g->anim_frames,
8831 g->anim_start_frame,
8834 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8835 &g_em->src_x, &g_em->src_y, FALSE);
8838 void InitGraphicInfo_EM(void)
8842 // always start with reliable default values
8843 for (i = 0; i < GAME_TILE_MAX; i++)
8845 object_mapping[i].element_rnd = EL_UNKNOWN;
8846 object_mapping[i].is_backside = FALSE;
8847 object_mapping[i].action = ACTION_DEFAULT;
8848 object_mapping[i].direction = MV_NONE;
8851 // always start with reliable default values
8852 for (p = 0; p < MAX_PLAYERS; p++)
8854 for (i = 0; i < PLY_MAX; i++)
8856 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8857 player_mapping[p][i].action = ACTION_DEFAULT;
8858 player_mapping[p][i].direction = MV_NONE;
8862 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8864 int e = em_object_mapping_list[i].element_em;
8866 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8867 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8869 if (em_object_mapping_list[i].action != -1)
8870 object_mapping[e].action = em_object_mapping_list[i].action;
8872 if (em_object_mapping_list[i].direction != -1)
8873 object_mapping[e].direction =
8874 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8877 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8879 int a = em_player_mapping_list[i].action_em;
8880 int p = em_player_mapping_list[i].player_nr;
8882 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8884 if (em_player_mapping_list[i].action != -1)
8885 player_mapping[p][a].action = em_player_mapping_list[i].action;
8887 if (em_player_mapping_list[i].direction != -1)
8888 player_mapping[p][a].direction =
8889 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8892 for (i = 0; i < GAME_TILE_MAX; i++)
8894 int element = object_mapping[i].element_rnd;
8895 int action = object_mapping[i].action;
8896 int direction = object_mapping[i].direction;
8897 boolean is_backside = object_mapping[i].is_backside;
8898 boolean action_exploding = ((action == ACTION_EXPLODING ||
8899 action == ACTION_SMASHED_BY_ROCK ||
8900 action == ACTION_SMASHED_BY_SPRING) &&
8901 element != EL_DIAMOND);
8902 boolean action_active = (action == ACTION_ACTIVE);
8903 boolean action_other = (action == ACTION_OTHER);
8905 for (j = 0; j < 8; j++)
8907 int effective_element = get_effective_element_EM(i, j);
8908 int effective_action = (j < 7 ? action :
8909 i == Xdrip_stretch ? action :
8910 i == Xdrip_stretchB ? action :
8911 i == Ydrip_1_s ? action :
8912 i == Ydrip_1_sB ? action :
8913 i == Yball_1 ? action :
8914 i == Xball_2 ? action :
8915 i == Yball_2 ? action :
8916 i == Yball_blank ? action :
8917 i == Ykey_1_blank ? action :
8918 i == Ykey_2_blank ? action :
8919 i == Ykey_3_blank ? action :
8920 i == Ykey_4_blank ? action :
8921 i == Ykey_5_blank ? action :
8922 i == Ykey_6_blank ? action :
8923 i == Ykey_7_blank ? action :
8924 i == Ykey_8_blank ? action :
8925 i == Ylenses_blank ? action :
8926 i == Ymagnify_blank ? action :
8927 i == Ygrass_blank ? action :
8928 i == Ydirt_blank ? action :
8929 i == Xsand_stonein_1 ? action :
8930 i == Xsand_stonein_2 ? action :
8931 i == Xsand_stonein_3 ? action :
8932 i == Xsand_stonein_4 ? action :
8933 i == Xsand_stoneout_1 ? action :
8934 i == Xsand_stoneout_2 ? action :
8935 i == Xboom_android ? ACTION_EXPLODING :
8936 action_exploding ? ACTION_EXPLODING :
8937 action_active ? action :
8938 action_other ? action :
8940 int graphic = (el_act_dir2img(effective_element, effective_action,
8942 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8944 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8945 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8946 boolean has_action_graphics = (graphic != base_graphic);
8947 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8948 struct GraphicInfo *g = &graphic_info[graphic];
8949 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8952 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8953 boolean special_animation = (action != ACTION_DEFAULT &&
8954 g->anim_frames == 3 &&
8955 g->anim_delay == 2 &&
8956 g->anim_mode & ANIM_LINEAR);
8957 int sync_frame = (i == Xdrip_stretch ? 7 :
8958 i == Xdrip_stretchB ? 7 :
8959 i == Ydrip_2_s ? j + 8 :
8960 i == Ydrip_2_sB ? j + 8 :
8969 i == Xfake_acid_1 ? 0 :
8970 i == Xfake_acid_2 ? 10 :
8971 i == Xfake_acid_3 ? 20 :
8972 i == Xfake_acid_4 ? 30 :
8973 i == Xfake_acid_5 ? 40 :
8974 i == Xfake_acid_6 ? 50 :
8975 i == Xfake_acid_7 ? 60 :
8976 i == Xfake_acid_8 ? 70 :
8977 i == Xfake_acid_1_player ? 0 :
8978 i == Xfake_acid_2_player ? 10 :
8979 i == Xfake_acid_3_player ? 20 :
8980 i == Xfake_acid_4_player ? 30 :
8981 i == Xfake_acid_5_player ? 40 :
8982 i == Xfake_acid_6_player ? 50 :
8983 i == Xfake_acid_7_player ? 60 :
8984 i == Xfake_acid_8_player ? 70 :
8986 i == Yball_2 ? j + 8 :
8987 i == Yball_blank ? j + 1 :
8988 i == Ykey_1_blank ? j + 1 :
8989 i == Ykey_2_blank ? j + 1 :
8990 i == Ykey_3_blank ? j + 1 :
8991 i == Ykey_4_blank ? j + 1 :
8992 i == Ykey_5_blank ? j + 1 :
8993 i == Ykey_6_blank ? j + 1 :
8994 i == Ykey_7_blank ? j + 1 :
8995 i == Ykey_8_blank ? j + 1 :
8996 i == Ylenses_blank ? j + 1 :
8997 i == Ymagnify_blank ? j + 1 :
8998 i == Ygrass_blank ? j + 1 :
8999 i == Ydirt_blank ? j + 1 :
9000 i == Xamoeba_1 ? 0 :
9001 i == Xamoeba_2 ? 1 :
9002 i == Xamoeba_3 ? 2 :
9003 i == Xamoeba_4 ? 3 :
9004 i == Xamoeba_5 ? 0 :
9005 i == Xamoeba_6 ? 1 :
9006 i == Xamoeba_7 ? 2 :
9007 i == Xamoeba_8 ? 3 :
9008 i == Xexit_2 ? j + 8 :
9009 i == Xexit_3 ? j + 16 :
9010 i == Xdynamite_1 ? 0 :
9011 i == Xdynamite_2 ? 8 :
9012 i == Xdynamite_3 ? 16 :
9013 i == Xdynamite_4 ? 24 :
9014 i == Xsand_stonein_1 ? j + 1 :
9015 i == Xsand_stonein_2 ? j + 9 :
9016 i == Xsand_stonein_3 ? j + 17 :
9017 i == Xsand_stonein_4 ? j + 25 :
9018 i == Xsand_stoneout_1 && j == 0 ? 0 :
9019 i == Xsand_stoneout_1 && j == 1 ? 0 :
9020 i == Xsand_stoneout_1 && j == 2 ? 1 :
9021 i == Xsand_stoneout_1 && j == 3 ? 2 :
9022 i == Xsand_stoneout_1 && j == 4 ? 2 :
9023 i == Xsand_stoneout_1 && j == 5 ? 3 :
9024 i == Xsand_stoneout_1 && j == 6 ? 4 :
9025 i == Xsand_stoneout_1 && j == 7 ? 4 :
9026 i == Xsand_stoneout_2 && j == 0 ? 5 :
9027 i == Xsand_stoneout_2 && j == 1 ? 6 :
9028 i == Xsand_stoneout_2 && j == 2 ? 7 :
9029 i == Xsand_stoneout_2 && j == 3 ? 8 :
9030 i == Xsand_stoneout_2 && j == 4 ? 9 :
9031 i == Xsand_stoneout_2 && j == 5 ? 11 :
9032 i == Xsand_stoneout_2 && j == 6 ? 13 :
9033 i == Xsand_stoneout_2 && j == 7 ? 15 :
9034 i == Xboom_bug && j == 1 ? 2 :
9035 i == Xboom_bug && j == 2 ? 2 :
9036 i == Xboom_bug && j == 3 ? 4 :
9037 i == Xboom_bug && j == 4 ? 4 :
9038 i == Xboom_bug && j == 5 ? 2 :
9039 i == Xboom_bug && j == 6 ? 2 :
9040 i == Xboom_bug && j == 7 ? 0 :
9041 i == Xboom_tank && j == 1 ? 2 :
9042 i == Xboom_tank && j == 2 ? 2 :
9043 i == Xboom_tank && j == 3 ? 4 :
9044 i == Xboom_tank && j == 4 ? 4 :
9045 i == Xboom_tank && j == 5 ? 2 :
9046 i == Xboom_tank && j == 6 ? 2 :
9047 i == Xboom_tank && j == 7 ? 0 :
9048 i == Xboom_android && j == 7 ? 6 :
9049 i == Xboom_1 && j == 1 ? 2 :
9050 i == Xboom_1 && j == 2 ? 2 :
9051 i == Xboom_1 && j == 3 ? 4 :
9052 i == Xboom_1 && j == 4 ? 4 :
9053 i == Xboom_1 && j == 5 ? 6 :
9054 i == Xboom_1 && j == 6 ? 6 :
9055 i == Xboom_1 && j == 7 ? 8 :
9056 i == Xboom_2 && j == 0 ? 8 :
9057 i == Xboom_2 && j == 1 ? 8 :
9058 i == Xboom_2 && j == 2 ? 10 :
9059 i == Xboom_2 && j == 3 ? 10 :
9060 i == Xboom_2 && j == 4 ? 10 :
9061 i == Xboom_2 && j == 5 ? 12 :
9062 i == Xboom_2 && j == 6 ? 12 :
9063 i == Xboom_2 && j == 7 ? 12 :
9064 special_animation && j == 4 ? 3 :
9065 effective_action != action ? 0 :
9067 int frame = getAnimationFrame(g->anim_frames,
9070 g->anim_start_frame,
9073 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9074 g->double_movement && is_backside);
9076 g_em->bitmap = src_bitmap;
9077 g_em->src_x = src_x;
9078 g_em->src_y = src_y;
9079 g_em->src_offset_x = 0;
9080 g_em->src_offset_y = 0;
9081 g_em->dst_offset_x = 0;
9082 g_em->dst_offset_y = 0;
9083 g_em->width = TILEX;
9084 g_em->height = TILEY;
9086 g_em->preserve_background = FALSE;
9088 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9091 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9092 effective_action == ACTION_MOVING ||
9093 effective_action == ACTION_PUSHING ||
9094 effective_action == ACTION_EATING)) ||
9095 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9096 effective_action == ACTION_EMPTYING)))
9099 (effective_action == ACTION_FALLING ||
9100 effective_action == ACTION_FILLING ||
9101 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9102 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9103 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9104 int num_steps = (i == Ydrip_1_s ? 16 :
9105 i == Ydrip_1_sB ? 16 :
9106 i == Ydrip_2_s ? 16 :
9107 i == Ydrip_2_sB ? 16 :
9108 i == Xsand_stonein_1 ? 32 :
9109 i == Xsand_stonein_2 ? 32 :
9110 i == Xsand_stonein_3 ? 32 :
9111 i == Xsand_stonein_4 ? 32 :
9112 i == Xsand_stoneout_1 ? 16 :
9113 i == Xsand_stoneout_2 ? 16 : 8);
9114 int cx = ABS(dx) * (TILEX / num_steps);
9115 int cy = ABS(dy) * (TILEY / num_steps);
9116 int step_frame = (i == Ydrip_2_s ? j + 8 :
9117 i == Ydrip_2_sB ? j + 8 :
9118 i == Xsand_stonein_2 ? j + 8 :
9119 i == Xsand_stonein_3 ? j + 16 :
9120 i == Xsand_stonein_4 ? j + 24 :
9121 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9122 int step = (is_backside ? step_frame : num_steps - step_frame);
9124 if (is_backside) // tile where movement starts
9126 if (dx < 0 || dy < 0)
9128 g_em->src_offset_x = cx * step;
9129 g_em->src_offset_y = cy * step;
9133 g_em->dst_offset_x = cx * step;
9134 g_em->dst_offset_y = cy * step;
9137 else // tile where movement ends
9139 if (dx < 0 || dy < 0)
9141 g_em->dst_offset_x = cx * step;
9142 g_em->dst_offset_y = cy * step;
9146 g_em->src_offset_x = cx * step;
9147 g_em->src_offset_y = cy * step;
9151 g_em->width = TILEX - cx * step;
9152 g_em->height = TILEY - cy * step;
9155 // create unique graphic identifier to decide if tile must be redrawn
9156 /* bit 31 - 16 (16 bit): EM style graphic
9157 bit 15 - 12 ( 4 bit): EM style frame
9158 bit 11 - 6 ( 6 bit): graphic width
9159 bit 5 - 0 ( 6 bit): graphic height */
9160 g_em->unique_identifier =
9161 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9165 for (i = 0; i < GAME_TILE_MAX; i++)
9167 for (j = 0; j < 8; j++)
9169 int element = object_mapping[i].element_rnd;
9170 int action = object_mapping[i].action;
9171 int direction = object_mapping[i].direction;
9172 boolean is_backside = object_mapping[i].is_backside;
9173 int graphic_action = el_act_dir2img(element, action, direction);
9174 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9176 if ((action == ACTION_SMASHED_BY_ROCK ||
9177 action == ACTION_SMASHED_BY_SPRING ||
9178 action == ACTION_EATING) &&
9179 graphic_action == graphic_default)
9181 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9182 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9183 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9184 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9187 // no separate animation for "smashed by rock" -- use rock instead
9188 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9189 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9191 g_em->bitmap = g_xx->bitmap;
9192 g_em->src_x = g_xx->src_x;
9193 g_em->src_y = g_xx->src_y;
9194 g_em->src_offset_x = g_xx->src_offset_x;
9195 g_em->src_offset_y = g_xx->src_offset_y;
9196 g_em->dst_offset_x = g_xx->dst_offset_x;
9197 g_em->dst_offset_y = g_xx->dst_offset_y;
9198 g_em->width = g_xx->width;
9199 g_em->height = g_xx->height;
9200 g_em->unique_identifier = g_xx->unique_identifier;
9203 g_em->preserve_background = TRUE;
9208 for (p = 0; p < MAX_PLAYERS; p++)
9210 for (i = 0; i < PLY_MAX; i++)
9212 int element = player_mapping[p][i].element_rnd;
9213 int action = player_mapping[p][i].action;
9214 int direction = player_mapping[p][i].direction;
9216 for (j = 0; j < 8; j++)
9218 int effective_element = element;
9219 int effective_action = action;
9220 int graphic = (direction == MV_NONE ?
9221 el_act2img(effective_element, effective_action) :
9222 el_act_dir2img(effective_element, effective_action,
9224 struct GraphicInfo *g = &graphic_info[graphic];
9225 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9229 int frame = getAnimationFrame(g->anim_frames,
9232 g->anim_start_frame,
9235 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9237 g_em->bitmap = src_bitmap;
9238 g_em->src_x = src_x;
9239 g_em->src_y = src_y;
9240 g_em->src_offset_x = 0;
9241 g_em->src_offset_y = 0;
9242 g_em->dst_offset_x = 0;
9243 g_em->dst_offset_y = 0;
9244 g_em->width = TILEX;
9245 g_em->height = TILEY;
9251 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9252 boolean any_player_moving,
9253 boolean any_player_snapping,
9254 boolean any_player_dropping)
9256 if (frame == 7 && !any_player_dropping)
9258 if (!local_player->was_waiting)
9260 if (!CheckSaveEngineSnapshotToList())
9263 local_player->was_waiting = TRUE;
9266 else if (any_player_moving || any_player_snapping || any_player_dropping)
9268 local_player->was_waiting = FALSE;
9272 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9273 boolean murphy_is_dropping)
9275 if (murphy_is_waiting)
9277 if (!local_player->was_waiting)
9279 if (!CheckSaveEngineSnapshotToList())
9282 local_player->was_waiting = TRUE;
9287 local_player->was_waiting = FALSE;
9291 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9292 boolean button_released)
9294 if (button_released)
9296 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9297 CheckSaveEngineSnapshotToList();
9299 else if (element_clicked)
9301 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9302 CheckSaveEngineSnapshotToList();
9304 game.snapshot.changed_action = TRUE;
9308 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9309 boolean any_player_moving,
9310 boolean any_player_snapping,
9311 boolean any_player_dropping)
9313 if (tape.single_step && tape.recording && !tape.pausing)
9314 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9315 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9317 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9318 any_player_snapping, any_player_dropping);
9320 return tape.pausing;
9323 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9324 boolean murphy_is_dropping)
9326 boolean murphy_starts_dropping = FALSE;
9329 for (i = 0; i < MAX_PLAYERS; i++)
9330 if (stored_player[i].force_dropping)
9331 murphy_starts_dropping = TRUE;
9333 if (tape.single_step && tape.recording && !tape.pausing)
9334 if (murphy_is_waiting && !murphy_starts_dropping)
9335 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9337 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9340 void CheckSingleStepMode_MM(boolean element_clicked,
9341 boolean button_released)
9343 if (tape.single_step && tape.recording && !tape.pausing)
9344 if (button_released)
9345 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9347 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9350 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9351 int graphic, int sync_frame, int x, int y)
9353 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9355 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9358 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9360 return (IS_NEXT_FRAME(sync_frame, graphic));
9363 int getGraphicInfo_Delay(int graphic)
9365 return graphic_info[graphic].anim_delay;
9368 void PlayMenuSoundExt(int sound)
9370 if (sound == SND_UNDEFINED)
9373 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9374 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9377 if (IS_LOOP_SOUND(sound))
9378 PlaySoundLoop(sound);
9383 void PlayMenuSound(void)
9385 PlayMenuSoundExt(menu.sound[game_status]);
9388 void PlayMenuSoundStereo(int sound, int stereo_position)
9390 if (sound == SND_UNDEFINED)
9393 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9394 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9397 if (IS_LOOP_SOUND(sound))
9398 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9400 PlaySoundStereo(sound, stereo_position);
9403 void PlayMenuSoundIfLoopExt(int sound)
9405 if (sound == SND_UNDEFINED)
9408 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9409 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9412 if (IS_LOOP_SOUND(sound))
9413 PlaySoundLoop(sound);
9416 void PlayMenuSoundIfLoop(void)
9418 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9421 void PlayMenuMusicExt(int music)
9423 if (music == MUS_UNDEFINED)
9426 if (!setup.sound_music)
9429 if (IS_LOOP_MUSIC(music))
9430 PlayMusicLoop(music);
9435 void PlayMenuMusic(void)
9437 char *curr_music = getCurrentlyPlayingMusicFilename();
9438 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9440 if (!strEqual(curr_music, next_music))
9441 PlayMenuMusicExt(menu.music[game_status]);
9444 void PlayMenuSoundsAndMusic(void)
9450 static void FadeMenuSounds(void)
9455 static void FadeMenuMusic(void)
9457 char *curr_music = getCurrentlyPlayingMusicFilename();
9458 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9460 if (!strEqual(curr_music, next_music))
9464 void FadeMenuSoundsAndMusic(void)
9470 void PlaySoundActivating(void)
9473 PlaySound(SND_MENU_ITEM_ACTIVATING);
9477 void PlaySoundSelecting(void)
9480 PlaySound(SND_MENU_ITEM_SELECTING);
9484 void ToggleFullscreenIfNeeded(void)
9486 // if setup and video fullscreen state are already matching, nothing do do
9487 if (setup.fullscreen == video.fullscreen_enabled ||
9488 !video.fullscreen_available)
9491 SDLSetWindowFullscreen(setup.fullscreen);
9493 // set setup value according to successfully changed fullscreen mode
9494 setup.fullscreen = video.fullscreen_enabled;
9497 void ChangeWindowScalingIfNeeded(void)
9499 // if setup and video window scaling are already matching, nothing do do
9500 if (setup.window_scaling_percent == video.window_scaling_percent ||
9501 video.fullscreen_enabled)
9504 SDLSetWindowScaling(setup.window_scaling_percent);
9506 // set setup value according to successfully changed window scaling
9507 setup.window_scaling_percent = video.window_scaling_percent;
9510 void ChangeVsyncModeIfNeeded(void)
9512 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9513 int video_vsync_mode = video.vsync_mode;
9515 // if setup and video vsync mode are already matching, nothing do do
9516 if (setup_vsync_mode == video_vsync_mode)
9519 // if renderer is using OpenGL, vsync mode can directly be changed
9520 SDLSetScreenVsyncMode(setup.vsync_mode);
9522 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9523 if (video.vsync_mode == video_vsync_mode)
9525 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9527 // save backbuffer content which gets lost when re-creating screen
9528 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9530 // force re-creating screen and renderer to set new vsync mode
9531 video.fullscreen_enabled = !setup.fullscreen;
9533 // when creating new renderer, destroy textures linked to old renderer
9534 FreeAllImageTextures(); // needs old renderer to free the textures
9536 // re-create screen and renderer (including change of vsync mode)
9537 ChangeVideoModeIfNeeded(setup.fullscreen);
9539 // set setup value according to successfully changed fullscreen mode
9540 setup.fullscreen = video.fullscreen_enabled;
9542 // restore backbuffer content from temporary backbuffer backup bitmap
9543 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9544 FreeBitmap(tmp_backbuffer);
9546 // update visible window/screen
9547 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9549 // when changing vsync mode, re-create textures for new renderer
9550 InitImageTextures();
9553 // set setup value according to successfully changed vsync mode
9554 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9557 static void JoinRectangles(int *x, int *y, int *width, int *height,
9558 int x2, int y2, int width2, int height2)
9560 // do not join with "off-screen" rectangle
9561 if (x2 == -1 || y2 == -1)
9566 *width = MAX(*width, width2);
9567 *height = MAX(*height, height2);
9570 void SetAnimStatus(int anim_status_new)
9572 if (anim_status_new == GAME_MODE_MAIN)
9573 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9574 else if (anim_status_new == GAME_MODE_NAMES)
9575 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9576 else if (anim_status_new == GAME_MODE_SCORES)
9577 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9579 global.anim_status_next = anim_status_new;
9581 // directly set screen modes that are entered without fading
9582 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9583 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9584 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9585 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9586 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9587 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9588 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9589 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9590 global.anim_status = global.anim_status_next;
9593 void SetGameStatus(int game_status_new)
9595 if (game_status_new != game_status)
9596 game_status_last_screen = game_status;
9598 game_status = game_status_new;
9600 SetAnimStatus(game_status_new);
9603 void SetFontStatus(int game_status_new)
9605 static int last_game_status = -1;
9607 if (game_status_new != -1)
9609 // set game status for font use after storing last game status
9610 last_game_status = game_status;
9611 game_status = game_status_new;
9615 // reset game status after font use from last stored game status
9616 game_status = last_game_status;
9620 void ResetFontStatus(void)
9625 void SetLevelSetInfo(char *identifier, int level_nr)
9627 setString(&levelset.identifier, identifier);
9629 levelset.level_nr = level_nr;
9632 boolean CheckIfAllViewportsHaveChanged(void)
9634 // if game status has not changed, viewports have not changed either
9635 if (game_status == game_status_last)
9638 // check if all viewports have changed with current game status
9640 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9641 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9642 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9643 int new_real_sx = vp_playfield->x;
9644 int new_real_sy = vp_playfield->y;
9645 int new_full_sxsize = vp_playfield->width;
9646 int new_full_sysize = vp_playfield->height;
9647 int new_dx = vp_door_1->x;
9648 int new_dy = vp_door_1->y;
9649 int new_dxsize = vp_door_1->width;
9650 int new_dysize = vp_door_1->height;
9651 int new_vx = vp_door_2->x;
9652 int new_vy = vp_door_2->y;
9653 int new_vxsize = vp_door_2->width;
9654 int new_vysize = vp_door_2->height;
9656 boolean playfield_viewport_has_changed =
9657 (new_real_sx != REAL_SX ||
9658 new_real_sy != REAL_SY ||
9659 new_full_sxsize != FULL_SXSIZE ||
9660 new_full_sysize != FULL_SYSIZE);
9662 boolean door_1_viewport_has_changed =
9665 new_dxsize != DXSIZE ||
9666 new_dysize != DYSIZE);
9668 boolean door_2_viewport_has_changed =
9671 new_vxsize != VXSIZE ||
9672 new_vysize != VYSIZE ||
9673 game_status_last == GAME_MODE_EDITOR);
9675 return (playfield_viewport_has_changed &&
9676 door_1_viewport_has_changed &&
9677 door_2_viewport_has_changed);
9680 boolean CheckFadeAll(void)
9682 return (CheckIfGlobalBorderHasChanged() ||
9683 CheckIfAllViewportsHaveChanged());
9686 void ChangeViewportPropertiesIfNeeded(void)
9688 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9689 FALSE : setup.small_game_graphics);
9690 int gfx_game_mode = game_status;
9691 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9693 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9694 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9695 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9696 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9697 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9698 int new_win_xsize = vp_window->width;
9699 int new_win_ysize = vp_window->height;
9700 int border_left = vp_playfield->border_left;
9701 int border_right = vp_playfield->border_right;
9702 int border_top = vp_playfield->border_top;
9703 int border_bottom = vp_playfield->border_bottom;
9704 int new_sx = vp_playfield->x + border_left;
9705 int new_sy = vp_playfield->y + border_top;
9706 int new_sxsize = vp_playfield->width - border_left - border_right;
9707 int new_sysize = vp_playfield->height - border_top - border_bottom;
9708 int new_real_sx = vp_playfield->x;
9709 int new_real_sy = vp_playfield->y;
9710 int new_full_sxsize = vp_playfield->width;
9711 int new_full_sysize = vp_playfield->height;
9712 int new_dx = vp_door_1->x;
9713 int new_dy = vp_door_1->y;
9714 int new_dxsize = vp_door_1->width;
9715 int new_dysize = vp_door_1->height;
9716 int new_vx = vp_door_2->x;
9717 int new_vy = vp_door_2->y;
9718 int new_vxsize = vp_door_2->width;
9719 int new_vysize = vp_door_2->height;
9720 int new_ex = vp_door_3->x;
9721 int new_ey = vp_door_3->y;
9722 int new_exsize = vp_door_3->width;
9723 int new_eysize = vp_door_3->height;
9724 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9725 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9726 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9727 int new_scr_fieldx = new_sxsize / tilesize;
9728 int new_scr_fieldy = new_sysize / tilesize;
9729 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9730 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9731 boolean init_gfx_buffers = FALSE;
9732 boolean init_video_buffer = FALSE;
9733 boolean init_gadgets_and_anims = FALSE;
9734 boolean init_em_graphics = FALSE;
9736 if (new_win_xsize != WIN_XSIZE ||
9737 new_win_ysize != WIN_YSIZE)
9739 WIN_XSIZE = new_win_xsize;
9740 WIN_YSIZE = new_win_ysize;
9742 init_video_buffer = TRUE;
9743 init_gfx_buffers = TRUE;
9744 init_gadgets_and_anims = TRUE;
9746 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9749 if (new_scr_fieldx != SCR_FIELDX ||
9750 new_scr_fieldy != SCR_FIELDY)
9752 // this always toggles between MAIN and GAME when using small tile size
9754 SCR_FIELDX = new_scr_fieldx;
9755 SCR_FIELDY = new_scr_fieldy;
9757 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9768 new_sxsize != SXSIZE ||
9769 new_sysize != SYSIZE ||
9770 new_dxsize != DXSIZE ||
9771 new_dysize != DYSIZE ||
9772 new_vxsize != VXSIZE ||
9773 new_vysize != VYSIZE ||
9774 new_exsize != EXSIZE ||
9775 new_eysize != EYSIZE ||
9776 new_real_sx != REAL_SX ||
9777 new_real_sy != REAL_SY ||
9778 new_full_sxsize != FULL_SXSIZE ||
9779 new_full_sysize != FULL_SYSIZE ||
9780 new_tilesize_var != TILESIZE_VAR
9783 // ------------------------------------------------------------------------
9784 // determine next fading area for changed viewport definitions
9785 // ------------------------------------------------------------------------
9787 // start with current playfield area (default fading area)
9790 FADE_SXSIZE = FULL_SXSIZE;
9791 FADE_SYSIZE = FULL_SYSIZE;
9793 // add new playfield area if position or size has changed
9794 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9795 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9797 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9798 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9801 // add current and new door 1 area if position or size has changed
9802 if (new_dx != DX || new_dy != DY ||
9803 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9805 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9806 DX, DY, DXSIZE, DYSIZE);
9807 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9808 new_dx, new_dy, new_dxsize, new_dysize);
9811 // add current and new door 2 area if position or size has changed
9812 if (new_vx != VX || new_vy != VY ||
9813 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9815 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9816 VX, VY, VXSIZE, VYSIZE);
9817 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9818 new_vx, new_vy, new_vxsize, new_vysize);
9821 // ------------------------------------------------------------------------
9822 // handle changed tile size
9823 // ------------------------------------------------------------------------
9825 if (new_tilesize_var != TILESIZE_VAR)
9827 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9829 // changing tile size invalidates scroll values of engine snapshots
9830 FreeEngineSnapshotSingle();
9832 // changing tile size requires update of graphic mapping for EM engine
9833 init_em_graphics = TRUE;
9844 SXSIZE = new_sxsize;
9845 SYSIZE = new_sysize;
9846 DXSIZE = new_dxsize;
9847 DYSIZE = new_dysize;
9848 VXSIZE = new_vxsize;
9849 VYSIZE = new_vysize;
9850 EXSIZE = new_exsize;
9851 EYSIZE = new_eysize;
9852 REAL_SX = new_real_sx;
9853 REAL_SY = new_real_sy;
9854 FULL_SXSIZE = new_full_sxsize;
9855 FULL_SYSIZE = new_full_sysize;
9856 TILESIZE_VAR = new_tilesize_var;
9858 init_gfx_buffers = TRUE;
9859 init_gadgets_and_anims = TRUE;
9861 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9862 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9865 if (init_gfx_buffers)
9867 // Debug("tools:viewport", "init_gfx_buffers");
9869 SCR_FIELDX = new_scr_fieldx_buffers;
9870 SCR_FIELDY = new_scr_fieldy_buffers;
9874 SCR_FIELDX = new_scr_fieldx;
9875 SCR_FIELDY = new_scr_fieldy;
9877 SetDrawDeactivationMask(REDRAW_NONE);
9878 SetDrawBackgroundMask(REDRAW_FIELD);
9881 if (init_video_buffer)
9883 // Debug("tools:viewport", "init_video_buffer");
9885 FreeAllImageTextures(); // needs old renderer to free the textures
9887 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9888 InitImageTextures();
9891 if (init_gadgets_and_anims)
9893 // Debug("tools:viewport", "init_gadgets_and_anims");
9896 InitGlobalAnimations();
9899 if (init_em_graphics)
9901 InitGraphicInfo_EM();