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 start_x, int start_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)
1424 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1425 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1426 int old_element = field[start_x][start_y];
1429 // do nothing if start field already has the desired content
1430 if (old_element == fill_element)
1433 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1435 while (stack_pos > 0)
1437 struct XY current = stack_buffer[--stack_pos];
1440 field[current.x][current.y] = fill_element;
1442 for (i = 0; i < 4; i++)
1444 int x = current.x + check[i].x;
1445 int y = current.y + check[i].y;
1447 // check for stack buffer overflow (should not happen)
1448 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1449 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1451 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1452 stack_buffer[stack_pos++] = (struct XY){ x, y };
1457 void FloodFillLevel(int from_x, int from_y, int fill_element,
1458 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1459 int max_fieldx, int max_fieldy)
1461 FloodFillLevelExt(from_x, from_y, fill_element,
1462 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1463 max_fieldx, max_fieldy);
1466 void SetRandomAnimationValue(int x, int y)
1468 gfx.anim_random_frame = GfxRandom[x][y];
1471 int getGraphicAnimationFrame(int graphic, int sync_frame)
1473 // animation synchronized with global frame counter, not move position
1474 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1475 sync_frame = FrameCounter;
1477 return getAnimationFrame(graphic_info[graphic].anim_frames,
1478 graphic_info[graphic].anim_delay,
1479 graphic_info[graphic].anim_mode,
1480 graphic_info[graphic].anim_start_frame,
1484 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1486 struct GraphicInfo *g = &graphic_info[graphic];
1487 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1489 if (tilesize == gfx.standard_tile_size)
1490 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1491 else if (tilesize == game.tile_size)
1492 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1494 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1497 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1498 boolean get_backside)
1500 struct GraphicInfo *g = &graphic_info[graphic];
1501 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1502 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1504 if (g->offset_y == 0) // frames are ordered horizontally
1506 int max_width = g->anim_frames_per_line * g->width;
1507 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1509 *x = pos % max_width;
1510 *y = src_y % g->height + pos / max_width * g->height;
1512 else if (g->offset_x == 0) // frames are ordered vertically
1514 int max_height = g->anim_frames_per_line * g->height;
1515 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1517 *x = src_x % g->width + pos / max_height * g->width;
1518 *y = pos % max_height;
1520 else // frames are ordered diagonally
1522 *x = src_x + frame * g->offset_x;
1523 *y = src_y + frame * g->offset_y;
1527 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1528 Bitmap **bitmap, int *x, int *y,
1529 boolean get_backside)
1531 struct GraphicInfo *g = &graphic_info[graphic];
1533 // if no graphics defined at all, use fallback graphics
1534 if (g->bitmaps == NULL)
1535 *g = graphic_info[IMG_CHAR_EXCLAM];
1537 // if no in-game graphics defined, always use standard graphic size
1538 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1539 tilesize = TILESIZE;
1541 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1542 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1544 *x = *x * tilesize / g->tile_size;
1545 *y = *y * tilesize / g->tile_size;
1548 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1549 Bitmap **bitmap, int *x, int *y)
1551 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1554 void getFixedGraphicSource(int graphic, int frame,
1555 Bitmap **bitmap, int *x, int *y)
1557 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1560 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1562 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1565 void getGlobalAnimGraphicSource(int graphic, int frame,
1566 Bitmap **bitmap, int *x, int *y)
1568 struct GraphicInfo *g = &graphic_info[graphic];
1570 // if no graphics defined at all, use fallback graphics
1571 if (g->bitmaps == NULL)
1572 *g = graphic_info[IMG_CHAR_EXCLAM];
1574 // use original size graphics, if existing, else use standard size graphics
1575 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1576 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1578 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1580 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1583 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1584 int *x, int *y, boolean get_backside)
1586 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1590 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1592 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1595 void DrawGraphic(int x, int y, int graphic, int frame)
1598 if (!IN_SCR_FIELD(x, y))
1600 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1601 Debug("draw:DrawGraphic", "This should never happen!");
1607 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1610 MarkTileDirty(x, y);
1613 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1616 if (!IN_SCR_FIELD(x, y))
1618 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1620 Debug("draw:DrawFixedGraphic", "This should never happen!");
1626 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1628 MarkTileDirty(x, y);
1631 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1637 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1639 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1642 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1648 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1649 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1652 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1655 if (!IN_SCR_FIELD(x, y))
1657 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1659 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1665 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1668 MarkTileDirty(x, y);
1671 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1674 if (!IN_SCR_FIELD(x, y))
1676 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1678 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1684 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1686 MarkTileDirty(x, y);
1689 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1695 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1697 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1701 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1702 int graphic, int frame)
1707 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1709 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1713 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1715 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1717 MarkTileDirty(x / tilesize, y / tilesize);
1720 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1723 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1724 graphic, frame, tilesize);
1725 MarkTileDirty(x / tilesize, y / tilesize);
1728 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1734 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1735 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1738 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1739 int frame, int tilesize)
1744 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1745 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1748 void DrawMiniGraphic(int x, int y, int graphic)
1750 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1751 MarkTileDirty(x / 2, y / 2);
1754 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1759 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1760 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1763 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1764 int graphic, int frame,
1765 int cut_mode, int mask_mode)
1770 int width = TILEX, height = TILEY;
1773 if (dx || dy) // shifted graphic
1775 if (x < BX1) // object enters playfield from the left
1782 else if (x > BX2) // object enters playfield from the right
1788 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1794 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1796 else if (dx) // general horizontal movement
1797 MarkTileDirty(x + SIGN(dx), y);
1799 if (y < BY1) // object enters playfield from the top
1801 if (cut_mode == CUT_BELOW) // object completely above top border
1809 else if (y > BY2) // object enters playfield from the bottom
1815 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1821 else if (dy > 0 && cut_mode == CUT_ABOVE)
1823 if (y == BY2) // object completely above bottom border
1829 MarkTileDirty(x, y + 1);
1830 } // object leaves playfield to the bottom
1831 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1833 else if (dy) // general vertical movement
1834 MarkTileDirty(x, y + SIGN(dy));
1838 if (!IN_SCR_FIELD(x, y))
1840 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1842 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1848 width = width * TILESIZE_VAR / TILESIZE;
1849 height = height * TILESIZE_VAR / TILESIZE;
1850 cx = cx * TILESIZE_VAR / TILESIZE;
1851 cy = cy * TILESIZE_VAR / TILESIZE;
1852 dx = dx * TILESIZE_VAR / TILESIZE;
1853 dy = dy * TILESIZE_VAR / TILESIZE;
1855 if (width > 0 && height > 0)
1857 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1862 dst_x = FX + x * TILEX_VAR + dx;
1863 dst_y = FY + y * TILEY_VAR + dy;
1865 if (mask_mode == USE_MASKING)
1866 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1869 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1872 MarkTileDirty(x, y);
1876 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1877 int graphic, int frame,
1878 int cut_mode, int mask_mode)
1883 int width = TILEX_VAR, height = TILEY_VAR;
1886 int x2 = x + SIGN(dx);
1887 int y2 = y + SIGN(dy);
1889 // movement with two-tile animations must be sync'ed with movement position,
1890 // not with current GfxFrame (which can be higher when using slow movement)
1891 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1892 int anim_frames = graphic_info[graphic].anim_frames;
1894 // (we also need anim_delay here for movement animations with less frames)
1895 int anim_delay = graphic_info[graphic].anim_delay;
1896 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1898 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1899 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1901 // re-calculate animation frame for two-tile movement animation
1902 frame = getGraphicAnimationFrame(graphic, sync_frame);
1904 // check if movement start graphic inside screen area and should be drawn
1905 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1907 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1909 dst_x = FX + x1 * TILEX_VAR;
1910 dst_y = FY + y1 * TILEY_VAR;
1912 if (mask_mode == USE_MASKING)
1913 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1916 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1919 MarkTileDirty(x1, y1);
1922 // check if movement end graphic inside screen area and should be drawn
1923 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1925 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1927 dst_x = FX + x2 * TILEX_VAR;
1928 dst_y = FY + y2 * TILEY_VAR;
1930 if (mask_mode == USE_MASKING)
1931 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1934 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1937 MarkTileDirty(x2, y2);
1941 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1942 int graphic, int frame,
1943 int cut_mode, int mask_mode)
1947 DrawGraphic(x, y, graphic, frame);
1952 if (graphic_info[graphic].double_movement) // EM style movement images
1953 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1955 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1958 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1959 int graphic, int frame, int cut_mode)
1961 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1964 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1965 int cut_mode, int mask_mode)
1967 int lx = LEVELX(x), ly = LEVELY(y);
1971 if (IN_LEV_FIELD(lx, ly))
1973 SetRandomAnimationValue(lx, ly);
1975 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1976 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1978 // do not use double (EM style) movement graphic when not moving
1979 if (graphic_info[graphic].double_movement && !dx && !dy)
1981 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1982 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1985 if (game.use_masked_elements && (dx || dy))
1986 mask_mode = USE_MASKING;
1988 else // border element
1990 graphic = el2img(element);
1991 frame = getGraphicAnimationFrame(graphic, -1);
1994 if (element == EL_EXPANDABLE_WALL)
1996 boolean left_stopped = FALSE, right_stopped = FALSE;
1998 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1999 left_stopped = TRUE;
2000 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2001 right_stopped = TRUE;
2003 if (left_stopped && right_stopped)
2005 else if (left_stopped)
2007 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2008 frame = graphic_info[graphic].anim_frames - 1;
2010 else if (right_stopped)
2012 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2013 frame = graphic_info[graphic].anim_frames - 1;
2018 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2019 else if (mask_mode == USE_MASKING)
2020 DrawGraphicThruMask(x, y, graphic, frame);
2022 DrawGraphic(x, y, graphic, frame);
2025 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2026 int cut_mode, int mask_mode)
2028 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2029 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2030 cut_mode, mask_mode);
2033 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2036 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2039 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2042 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2045 void DrawLevelElementThruMask(int x, int y, int element)
2047 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2050 void DrawLevelFieldThruMask(int x, int y)
2052 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2055 // !!! implementation of quicksand is totally broken !!!
2056 #define IS_CRUMBLED_TILE(x, y, e) \
2057 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2058 !IS_MOVING(x, y) || \
2059 (e) == EL_QUICKSAND_EMPTYING || \
2060 (e) == EL_QUICKSAND_FAST_EMPTYING))
2062 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2067 int width, height, cx, cy;
2068 int sx = SCREENX(x), sy = SCREENY(y);
2069 int crumbled_border_size = graphic_info[graphic].border_size;
2070 int crumbled_tile_size = graphic_info[graphic].tile_size;
2071 int crumbled_border_size_var =
2072 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2075 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2077 for (i = 1; i < 4; i++)
2079 int dxx = (i & 1 ? dx : 0);
2080 int dyy = (i & 2 ? dy : 0);
2083 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2086 // check if neighbour field is of same crumble type
2087 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2088 graphic_info[graphic].class ==
2089 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2091 // return if check prevents inner corner
2092 if (same == (dxx == dx && dyy == dy))
2096 // if we reach this point, we have an inner corner
2098 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2100 width = crumbled_border_size_var;
2101 height = crumbled_border_size_var;
2102 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2103 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2105 if (game.use_masked_elements)
2107 int graphic0 = el2img(EL_EMPTY);
2108 int frame0 = getGraphicAnimationFrame(graphic0, GfxFrame[x][y]);
2109 Bitmap *src_bitmap0;
2112 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2114 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2116 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2118 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2120 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2123 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2125 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2128 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2133 int width, height, bx, by, cx, cy;
2134 int sx = SCREENX(x), sy = SCREENY(y);
2135 int crumbled_border_size = graphic_info[graphic].border_size;
2136 int crumbled_tile_size = graphic_info[graphic].tile_size;
2137 int crumbled_border_size_var =
2138 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2139 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2142 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2144 // only needed when using masked elements
2145 int graphic0 = el2img(EL_EMPTY);
2146 int frame0 = getGraphicAnimationFrame(graphic0, GfxFrame[x][y]);
2147 Bitmap *src_bitmap0;
2150 if (game.use_masked_elements)
2151 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2153 // draw simple, sloppy, non-corner-accurate crumbled border
2155 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2156 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2157 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2158 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2160 if (game.use_masked_elements)
2162 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2164 FX + sx * TILEX_VAR + cx,
2165 FY + sy * TILEY_VAR + cy);
2167 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2169 FX + sx * TILEX_VAR + cx,
2170 FY + sy * TILEY_VAR + cy);
2173 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2175 FX + sx * TILEX_VAR + cx,
2176 FY + sy * TILEY_VAR + cy);
2178 // (remaining middle border part must be at least as big as corner part)
2179 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2180 crumbled_border_size_var >= TILESIZE_VAR / 3)
2183 // correct corners of crumbled border, if needed
2185 for (i = -1; i <= 1; i += 2)
2187 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2188 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2189 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2192 // check if neighbour field is of same crumble type
2193 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2194 graphic_info[graphic].class ==
2195 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2197 // no crumbled corner, but continued crumbled border
2199 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2200 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2201 int b1 = (i == 1 ? crumbled_border_size_var :
2202 TILESIZE_VAR - 2 * crumbled_border_size_var);
2204 width = crumbled_border_size_var;
2205 height = crumbled_border_size_var;
2207 if (dir == 1 || dir == 2)
2222 if (game.use_masked_elements)
2224 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2226 FX + sx * TILEX_VAR + cx,
2227 FY + sy * TILEY_VAR + cy);
2229 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2231 FX + sx * TILEX_VAR + cx,
2232 FY + sy * TILEY_VAR + cy);
2235 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2237 FX + sx * TILEX_VAR + cx,
2238 FY + sy * TILEY_VAR + cy);
2243 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2245 int sx = SCREENX(x), sy = SCREENY(y);
2248 static int xy[4][2] =
2256 if (!IN_LEV_FIELD(x, y))
2259 element = TILE_GFX_ELEMENT(x, y);
2261 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2263 if (!IN_SCR_FIELD(sx, sy))
2266 // crumble field borders towards direct neighbour fields
2267 for (i = 0; i < 4; i++)
2269 int xx = x + xy[i][0];
2270 int yy = y + xy[i][1];
2272 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2275 // check if neighbour field is of same crumble type
2276 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2277 graphic_info[graphic].class ==
2278 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2281 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2284 // crumble inner field corners towards corner neighbour fields
2285 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2286 graphic_info[graphic].anim_frames == 2)
2288 for (i = 0; i < 4; i++)
2290 int dx = (i & 1 ? +1 : -1);
2291 int dy = (i & 2 ? +1 : -1);
2293 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2297 MarkTileDirty(sx, sy);
2299 else // center field is not crumbled -- crumble neighbour fields
2301 // crumble field borders of direct neighbour fields
2302 for (i = 0; i < 4; i++)
2304 int xx = x + xy[i][0];
2305 int yy = y + xy[i][1];
2306 int sxx = sx + xy[i][0];
2307 int syy = sy + xy[i][1];
2309 if (!IN_LEV_FIELD(xx, yy) ||
2310 !IN_SCR_FIELD(sxx, syy))
2313 // do not crumble fields that are being digged or snapped
2314 if (Tile[xx][yy] == EL_EMPTY ||
2315 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2318 element = TILE_GFX_ELEMENT(xx, yy);
2320 if (!IS_CRUMBLED_TILE(xx, yy, element))
2323 graphic = el_act2crm(element, ACTION_DEFAULT);
2325 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2327 MarkTileDirty(sxx, syy);
2330 // crumble inner field corners of corner neighbour fields
2331 for (i = 0; i < 4; i++)
2333 int dx = (i & 1 ? +1 : -1);
2334 int dy = (i & 2 ? +1 : -1);
2340 if (!IN_LEV_FIELD(xx, yy) ||
2341 !IN_SCR_FIELD(sxx, syy))
2344 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2347 element = TILE_GFX_ELEMENT(xx, yy);
2349 if (!IS_CRUMBLED_TILE(xx, yy, element))
2352 graphic = el_act2crm(element, ACTION_DEFAULT);
2354 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2355 graphic_info[graphic].anim_frames == 2)
2356 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2358 MarkTileDirty(sxx, syy);
2363 void DrawLevelFieldCrumbled(int x, int y)
2367 if (!IN_LEV_FIELD(x, y))
2370 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2371 GfxElement[x][y] != EL_UNDEFINED &&
2372 GFX_CRUMBLED(GfxElement[x][y]))
2374 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2379 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2381 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2384 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2387 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2388 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2389 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2390 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2391 int sx = SCREENX(x), sy = SCREENY(y);
2393 DrawScreenGraphic(sx, sy, graphic1, frame1);
2394 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2397 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2399 int sx = SCREENX(x), sy = SCREENY(y);
2400 static int xy[4][2] =
2409 // crumble direct neighbour fields (required for field borders)
2410 for (i = 0; i < 4; i++)
2412 int xx = x + xy[i][0];
2413 int yy = y + xy[i][1];
2414 int sxx = sx + xy[i][0];
2415 int syy = sy + xy[i][1];
2417 if (!IN_LEV_FIELD(xx, yy) ||
2418 !IN_SCR_FIELD(sxx, syy) ||
2419 !GFX_CRUMBLED(Tile[xx][yy]) ||
2423 DrawLevelField(xx, yy);
2426 // crumble corner neighbour fields (required for inner field corners)
2427 for (i = 0; i < 4; i++)
2429 int dx = (i & 1 ? +1 : -1);
2430 int dy = (i & 2 ? +1 : -1);
2436 if (!IN_LEV_FIELD(xx, yy) ||
2437 !IN_SCR_FIELD(sxx, syy) ||
2438 !GFX_CRUMBLED(Tile[xx][yy]) ||
2442 int element = TILE_GFX_ELEMENT(xx, yy);
2443 int graphic = el_act2crm(element, ACTION_DEFAULT);
2445 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2446 graphic_info[graphic].anim_frames == 2)
2447 DrawLevelField(xx, yy);
2451 static int getBorderElement(int x, int y)
2455 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2456 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2457 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2458 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2459 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2460 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2461 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2463 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2464 int steel_position = (x == -1 && y == -1 ? 0 :
2465 x == lev_fieldx && y == -1 ? 1 :
2466 x == -1 && y == lev_fieldy ? 2 :
2467 x == lev_fieldx && y == lev_fieldy ? 3 :
2468 x == -1 || x == lev_fieldx ? 4 :
2469 y == -1 || y == lev_fieldy ? 5 : 6);
2471 return border[steel_position][steel_type];
2474 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2476 if (game.use_masked_elements)
2478 if (graphic != el2img(EL_EMPTY))
2479 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2481 DrawGraphicThruMask(x, y, graphic, frame);
2485 DrawGraphic(x, y, graphic, frame);
2489 void DrawScreenElement(int x, int y, int element)
2491 int mask_mode = NO_MASKING;
2493 if (game.use_masked_elements)
2495 int lx = LEVELX(x), ly = LEVELY(y);
2497 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2499 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2501 mask_mode = USE_MASKING;
2505 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2506 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2509 void DrawLevelElement(int x, int y, int element)
2511 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2512 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2515 void DrawScreenField(int x, int y)
2517 int lx = LEVELX(x), ly = LEVELY(y);
2518 int element, content;
2520 if (!IN_LEV_FIELD(lx, ly))
2522 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2525 element = getBorderElement(lx, ly);
2527 DrawScreenElement(x, y, element);
2532 element = Tile[lx][ly];
2533 content = Store[lx][ly];
2535 if (IS_MOVING(lx, ly))
2537 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2538 boolean cut_mode = NO_CUTTING;
2540 if (element == EL_QUICKSAND_EMPTYING ||
2541 element == EL_QUICKSAND_FAST_EMPTYING ||
2542 element == EL_MAGIC_WALL_EMPTYING ||
2543 element == EL_BD_MAGIC_WALL_EMPTYING ||
2544 element == EL_DC_MAGIC_WALL_EMPTYING ||
2545 element == EL_AMOEBA_DROPPING)
2546 cut_mode = CUT_ABOVE;
2547 else if (element == EL_QUICKSAND_FILLING ||
2548 element == EL_QUICKSAND_FAST_FILLING ||
2549 element == EL_MAGIC_WALL_FILLING ||
2550 element == EL_BD_MAGIC_WALL_FILLING ||
2551 element == EL_DC_MAGIC_WALL_FILLING)
2552 cut_mode = CUT_BELOW;
2554 if (cut_mode == CUT_ABOVE)
2555 DrawScreenElement(x, y, element);
2557 DrawScreenElement(x, y, EL_EMPTY);
2559 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2561 int dir = MovDir[lx][ly];
2562 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2563 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2565 if (IN_SCR_FIELD(newx, newy))
2566 DrawScreenElement(newx, newy, EL_EMPTY);
2570 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2571 else if (cut_mode == NO_CUTTING)
2572 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2575 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2577 if (cut_mode == CUT_BELOW &&
2578 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2579 DrawLevelElement(lx, ly + 1, element);
2582 if (content == EL_ACID)
2584 int dir = MovDir[lx][ly];
2585 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2586 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2588 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2590 // prevent target field from being drawn again (but without masking)
2591 // (this would happen if target field is scanned after moving element)
2592 Stop[newlx][newly] = TRUE;
2595 else if (IS_BLOCKED(lx, ly))
2600 boolean cut_mode = NO_CUTTING;
2601 int element_old, content_old;
2603 Blocked2Moving(lx, ly, &oldx, &oldy);
2606 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2607 MovDir[oldx][oldy] == MV_RIGHT);
2609 element_old = Tile[oldx][oldy];
2610 content_old = Store[oldx][oldy];
2612 if (element_old == EL_QUICKSAND_EMPTYING ||
2613 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2614 element_old == EL_MAGIC_WALL_EMPTYING ||
2615 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2616 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2617 element_old == EL_AMOEBA_DROPPING)
2618 cut_mode = CUT_ABOVE;
2620 DrawScreenElement(x, y, EL_EMPTY);
2623 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2625 else if (cut_mode == NO_CUTTING)
2626 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2629 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2632 else if (IS_DRAWABLE(element))
2633 DrawScreenElement(x, y, element);
2635 DrawScreenElement(x, y, EL_EMPTY);
2638 void DrawLevelField(int x, int y)
2640 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2641 DrawScreenField(SCREENX(x), SCREENY(y));
2642 else if (IS_MOVING(x, y))
2646 Moving2Blocked(x, y, &newx, &newy);
2647 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2648 DrawScreenField(SCREENX(newx), SCREENY(newy));
2650 else if (IS_BLOCKED(x, y))
2654 Blocked2Moving(x, y, &oldx, &oldy);
2655 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2656 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2660 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2661 int (*el2img_function)(int), boolean masked,
2662 int element_bits_draw)
2664 int element_base = map_mm_wall_element(element);
2665 int element_bits = (IS_DF_WALL(element) ?
2666 element - EL_DF_WALL_START :
2667 IS_MM_WALL(element) ?
2668 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2669 int graphic = el2img_function(element_base);
2670 int tilesize_draw = tilesize / 2;
2675 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2677 for (i = 0; i < 4; i++)
2679 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2680 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2682 if (!(element_bits_draw & (1 << i)))
2685 if (element_bits & (1 << i))
2688 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2689 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2691 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2692 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2697 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2698 tilesize_draw, tilesize_draw);
2703 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2704 boolean masked, int element_bits_draw)
2706 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2707 element, tilesize, el2edimg, masked, element_bits_draw);
2710 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2711 int (*el2img_function)(int))
2713 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2717 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2720 if (IS_MM_WALL(element))
2722 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2723 element, tilesize, el2edimg, masked, 0x000f);
2727 int graphic = el2edimg(element);
2730 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2732 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2736 void DrawSizedElement(int x, int y, int element, int tilesize)
2738 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2741 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2743 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2746 void DrawMiniElement(int x, int y, int element)
2750 graphic = el2edimg(element);
2751 DrawMiniGraphic(x, y, graphic);
2754 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2757 int x = sx + scroll_x, y = sy + scroll_y;
2759 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2760 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2761 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2762 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2764 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2767 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2769 int x = sx + scroll_x, y = sy + scroll_y;
2771 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2772 DrawMiniElement(sx, sy, EL_EMPTY);
2773 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2774 DrawMiniElement(sx, sy, Tile[x][y]);
2776 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2779 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2780 int x, int y, int xsize, int ysize,
2781 int tile_width, int tile_height)
2785 int dst_x = startx + x * tile_width;
2786 int dst_y = starty + y * tile_height;
2787 int width = graphic_info[graphic].width;
2788 int height = graphic_info[graphic].height;
2789 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2790 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2791 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2792 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2793 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2794 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2795 boolean draw_masked = graphic_info[graphic].draw_masked;
2797 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2799 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2801 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2805 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2806 inner_sx + (x - 1) * tile_width % inner_width);
2807 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2808 inner_sy + (y - 1) * tile_height % inner_height);
2811 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2814 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2818 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2819 int x, int y, int xsize, int ysize,
2822 int font_width = getFontWidth(font_nr);
2823 int font_height = getFontHeight(font_nr);
2825 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2826 font_width, font_height);
2829 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2831 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2832 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2833 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2834 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2835 boolean no_delay = (tape.warp_forward);
2836 unsigned int anim_delay = 0;
2837 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2838 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2839 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2840 int font_width = getFontWidth(font_nr);
2841 int font_height = getFontHeight(font_nr);
2842 int max_xsize = level.envelope[envelope_nr].xsize;
2843 int max_ysize = level.envelope[envelope_nr].ysize;
2844 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2845 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2846 int xend = max_xsize;
2847 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2848 int xstep = (xstart < xend ? 1 : 0);
2849 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2851 int end = MAX(xend - xstart, yend - ystart);
2854 for (i = start; i <= end; i++)
2856 int last_frame = end; // last frame of this "for" loop
2857 int x = xstart + i * xstep;
2858 int y = ystart + i * ystep;
2859 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2860 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2861 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2862 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2865 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2867 BlitScreenToBitmap(backbuffer);
2869 SetDrawtoField(DRAW_TO_BACKBUFFER);
2871 for (yy = 0; yy < ysize; yy++)
2872 for (xx = 0; xx < xsize; xx++)
2873 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2875 DrawTextBuffer(sx + font_width, sy + font_height,
2876 level.envelope[envelope_nr].text, font_nr, max_xsize,
2877 xsize - 2, ysize - 2, 0, mask_mode,
2878 level.envelope[envelope_nr].autowrap,
2879 level.envelope[envelope_nr].centered, FALSE);
2881 redraw_mask |= REDRAW_FIELD;
2884 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2887 ClearAutoRepeatKeyEvents();
2890 void ShowEnvelope(int envelope_nr)
2892 int element = EL_ENVELOPE_1 + envelope_nr;
2893 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2894 int sound_opening = element_info[element].sound[ACTION_OPENING];
2895 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2896 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2897 boolean no_delay = (tape.warp_forward);
2898 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2899 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2900 int anim_mode = graphic_info[graphic].anim_mode;
2901 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2902 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2903 boolean overlay_enabled = GetOverlayEnabled();
2905 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2907 SetOverlayEnabled(FALSE);
2910 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2912 if (anim_mode == ANIM_DEFAULT)
2913 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2915 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2918 Delay_WithScreenUpdates(wait_delay_value);
2920 WaitForEventToContinue();
2923 SetOverlayEnabled(overlay_enabled);
2925 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2927 if (anim_mode != ANIM_NONE)
2928 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2930 if (anim_mode == ANIM_DEFAULT)
2931 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2933 game.envelope_active = FALSE;
2935 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2937 redraw_mask |= REDRAW_FIELD;
2941 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2942 int xsize, int ysize)
2944 if (!global.use_envelope_request ||
2945 request.sort_priority <= 0)
2948 if (request.bitmap == NULL ||
2949 xsize > request.xsize ||
2950 ysize > request.ysize)
2952 if (request.bitmap != NULL)
2953 FreeBitmap(request.bitmap);
2955 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2957 SDL_Surface *surface = request.bitmap->surface;
2959 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2960 Fail("SDLGetNativeSurface() failed");
2963 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2965 SDLFreeBitmapTextures(request.bitmap);
2966 SDLCreateBitmapTextures(request.bitmap);
2968 // set envelope request run-time values
2971 request.xsize = xsize;
2972 request.ysize = ysize;
2975 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2977 if (global.use_envelope_request &&
2978 game.request_active_or_moving &&
2979 request.sort_priority > 0 &&
2980 drawing_target == DRAW_TO_SCREEN &&
2981 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2983 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2984 request.sx, request.sy);
2988 static void setRequestBasePosition(int *x, int *y)
2990 int sx_base, sy_base;
2992 if (request.x != -1)
2993 sx_base = request.x;
2994 else if (request.align == ALIGN_LEFT)
2996 else if (request.align == ALIGN_RIGHT)
2997 sx_base = SX + SXSIZE;
2999 sx_base = SX + SXSIZE / 2;
3001 if (request.y != -1)
3002 sy_base = request.y;
3003 else if (request.valign == VALIGN_TOP)
3005 else if (request.valign == VALIGN_BOTTOM)
3006 sy_base = SY + SYSIZE;
3008 sy_base = SY + SYSIZE / 2;
3014 static void setRequestPositionExt(int *x, int *y, int width, int height,
3015 boolean add_border_size)
3017 int border_size = request.border_size;
3018 int sx_base, sy_base;
3021 setRequestBasePosition(&sx_base, &sy_base);
3023 if (request.align == ALIGN_LEFT)
3025 else if (request.align == ALIGN_RIGHT)
3026 sx = sx_base - width;
3028 sx = sx_base - width / 2;
3030 if (request.valign == VALIGN_TOP)
3032 else if (request.valign == VALIGN_BOTTOM)
3033 sy = sy_base - height;
3035 sy = sy_base - height / 2;
3037 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3038 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3040 if (add_border_size)
3050 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3052 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3055 static void DrawEnvelopeRequest(char *text)
3057 char *text_final = text;
3058 char *text_door_style = NULL;
3059 int graphic = IMG_BACKGROUND_REQUEST;
3060 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3061 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3062 int font_nr = FONT_REQUEST;
3063 int font_width = getFontWidth(font_nr);
3064 int font_height = getFontHeight(font_nr);
3065 int border_size = request.border_size;
3066 int line_spacing = request.line_spacing;
3067 int line_height = font_height + line_spacing;
3068 int max_text_width = request.width - 2 * border_size;
3069 int max_text_height = request.height - 2 * border_size;
3070 int line_length = max_text_width / font_width;
3071 int max_lines = max_text_height / line_height;
3072 int text_width = line_length * font_width;
3073 int width = request.width;
3074 int height = request.height;
3075 int tile_size = MAX(request.step_offset, 1);
3076 int x_steps = width / tile_size;
3077 int y_steps = height / tile_size;
3078 int sx_offset = border_size;
3079 int sy_offset = border_size;
3083 if (request.centered)
3084 sx_offset = (request.width - text_width) / 2;
3086 if (request.wrap_single_words && !request.autowrap)
3088 char *src_text_ptr, *dst_text_ptr;
3090 text_door_style = checked_malloc(2 * strlen(text) + 1);
3092 src_text_ptr = text;
3093 dst_text_ptr = text_door_style;
3095 while (*src_text_ptr)
3097 if (*src_text_ptr == ' ' ||
3098 *src_text_ptr == '?' ||
3099 *src_text_ptr == '!')
3100 *dst_text_ptr++ = '\n';
3102 if (*src_text_ptr != ' ')
3103 *dst_text_ptr++ = *src_text_ptr;
3108 *dst_text_ptr = '\0';
3110 text_final = text_door_style;
3113 setRequestPosition(&sx, &sy, FALSE);
3115 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3117 for (y = 0; y < y_steps; y++)
3118 for (x = 0; x < x_steps; x++)
3119 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3120 x, y, x_steps, y_steps,
3121 tile_size, tile_size);
3123 // force DOOR font inside door area
3124 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3126 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3127 line_length, -1, max_lines, line_spacing, mask_mode,
3128 request.autowrap, request.centered, FALSE);
3132 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3133 RedrawGadget(tool_gadget[i]);
3135 // store readily prepared envelope request for later use when animating
3136 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3138 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3140 if (text_door_style)
3141 free(text_door_style);
3144 static void AnimateEnvelopeRequest(int anim_mode, int action)
3146 int graphic = IMG_BACKGROUND_REQUEST;
3147 boolean draw_masked = graphic_info[graphic].draw_masked;
3148 int delay_value_normal = request.step_delay;
3149 int delay_value_fast = delay_value_normal / 2;
3150 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3151 boolean no_delay = (tape.warp_forward);
3152 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3153 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3154 unsigned int anim_delay = 0;
3156 int tile_size = MAX(request.step_offset, 1);
3157 int max_xsize = request.width / tile_size;
3158 int max_ysize = request.height / tile_size;
3159 int max_xsize_inner = max_xsize - 2;
3160 int max_ysize_inner = max_ysize - 2;
3162 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3163 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3164 int xend = max_xsize_inner;
3165 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3166 int xstep = (xstart < xend ? 1 : 0);
3167 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3169 int end = MAX(xend - xstart, yend - ystart);
3172 if (setup.quick_doors)
3179 for (i = start; i <= end; i++)
3181 int last_frame = end; // last frame of this "for" loop
3182 int x = xstart + i * xstep;
3183 int y = ystart + i * ystep;
3184 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3185 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3186 int xsize_size_left = (xsize - 1) * tile_size;
3187 int ysize_size_top = (ysize - 1) * tile_size;
3188 int max_xsize_pos = (max_xsize - 1) * tile_size;
3189 int max_ysize_pos = (max_ysize - 1) * tile_size;
3190 int width = xsize * tile_size;
3191 int height = ysize * tile_size;
3196 setRequestPosition(&src_x, &src_y, FALSE);
3197 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3199 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3201 for (yy = 0; yy < 2; yy++)
3203 for (xx = 0; xx < 2; xx++)
3205 int src_xx = src_x + xx * max_xsize_pos;
3206 int src_yy = src_y + yy * max_ysize_pos;
3207 int dst_xx = dst_x + xx * xsize_size_left;
3208 int dst_yy = dst_y + yy * ysize_size_top;
3209 int xx_size = (xx ? tile_size : xsize_size_left);
3210 int yy_size = (yy ? tile_size : ysize_size_top);
3213 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3214 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3216 BlitBitmap(bitmap_db_store_2, backbuffer,
3217 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3221 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3223 redraw_mask |= REDRAW_FIELD;
3227 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3230 ClearAutoRepeatKeyEvents();
3233 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3235 int graphic = IMG_BACKGROUND_REQUEST;
3236 int sound_opening = SND_REQUEST_OPENING;
3237 int sound_closing = SND_REQUEST_CLOSING;
3238 int anim_mode_1 = request.anim_mode; // (higher priority)
3239 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3240 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3241 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3242 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3244 if (game_status == GAME_MODE_PLAYING)
3245 BlitScreenToBitmap(backbuffer);
3247 SetDrawtoField(DRAW_TO_BACKBUFFER);
3249 // SetDrawBackgroundMask(REDRAW_NONE);
3251 if (action == ACTION_OPENING)
3253 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3255 if (req_state & REQ_ASK)
3257 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3258 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3259 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3260 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3262 else if (req_state & REQ_CONFIRM)
3264 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3265 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3267 else if (req_state & REQ_PLAYER)
3269 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3270 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3271 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3272 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3275 DrawEnvelopeRequest(text);
3278 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3280 if (action == ACTION_OPENING)
3282 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3284 if (anim_mode == ANIM_DEFAULT)
3285 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3287 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3291 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3293 if (anim_mode != ANIM_NONE)
3294 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3296 if (anim_mode == ANIM_DEFAULT)
3297 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3300 game.envelope_active = FALSE;
3302 if (action == ACTION_CLOSING)
3303 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3305 // SetDrawBackgroundMask(last_draw_background_mask);
3307 redraw_mask |= REDRAW_FIELD;
3311 if (action == ACTION_CLOSING &&
3312 game_status == GAME_MODE_PLAYING &&
3313 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3314 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3317 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3319 if (IS_MM_WALL(element))
3321 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3327 int graphic = el2preimg(element);
3329 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3330 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3335 void DrawLevel(int draw_background_mask)
3339 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3340 SetDrawBackgroundMask(draw_background_mask);
3344 for (x = BX1; x <= BX2; x++)
3345 for (y = BY1; y <= BY2; y++)
3346 DrawScreenField(x, y);
3348 redraw_mask |= REDRAW_FIELD;
3351 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3356 for (x = 0; x < size_x; x++)
3357 for (y = 0; y < size_y; y++)
3358 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3360 redraw_mask |= REDRAW_FIELD;
3363 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3367 for (x = 0; x < size_x; x++)
3368 for (y = 0; y < size_y; y++)
3369 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3371 redraw_mask |= REDRAW_FIELD;
3374 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3376 boolean show_level_border = (BorderElement != EL_EMPTY);
3377 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3378 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3379 int tile_size = preview.tile_size;
3380 int preview_width = preview.xsize * tile_size;
3381 int preview_height = preview.ysize * tile_size;
3382 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3383 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3384 int real_preview_width = real_preview_xsize * tile_size;
3385 int real_preview_height = real_preview_ysize * tile_size;
3386 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3387 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3390 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3393 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3395 dst_x += (preview_width - real_preview_width) / 2;
3396 dst_y += (preview_height - real_preview_height) / 2;
3398 for (x = 0; x < real_preview_xsize; x++)
3400 for (y = 0; y < real_preview_ysize; y++)
3402 int lx = from_x + x + (show_level_border ? -1 : 0);
3403 int ly = from_y + y + (show_level_border ? -1 : 0);
3404 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3405 getBorderElement(lx, ly));
3407 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3408 element, tile_size);
3412 redraw_mask |= REDRAW_FIELD;
3415 #define MICROLABEL_EMPTY 0
3416 #define MICROLABEL_LEVEL_NAME 1
3417 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3418 #define MICROLABEL_LEVEL_AUTHOR 3
3419 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3420 #define MICROLABEL_IMPORTED_FROM 5
3421 #define MICROLABEL_IMPORTED_BY_HEAD 6
3422 #define MICROLABEL_IMPORTED_BY 7
3424 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3426 int max_text_width = SXSIZE;
3427 int font_width = getFontWidth(font_nr);
3429 if (pos->align == ALIGN_CENTER)
3430 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3431 else if (pos->align == ALIGN_RIGHT)
3432 max_text_width = pos->x;
3434 max_text_width = SXSIZE - pos->x;
3436 return max_text_width / font_width;
3439 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3441 char label_text[MAX_OUTPUT_LINESIZE + 1];
3442 int max_len_label_text;
3443 int font_nr = pos->font;
3446 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3449 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3450 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3451 mode == MICROLABEL_IMPORTED_BY_HEAD)
3452 font_nr = pos->font_alt;
3454 max_len_label_text = getMaxTextLength(pos, font_nr);
3456 if (pos->size != -1)
3457 max_len_label_text = pos->size;
3459 for (i = 0; i < max_len_label_text; i++)
3460 label_text[i] = ' ';
3461 label_text[max_len_label_text] = '\0';
3463 if (strlen(label_text) > 0)
3464 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3467 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3468 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3469 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3470 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3471 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3472 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3473 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3474 max_len_label_text);
3475 label_text[max_len_label_text] = '\0';
3477 if (strlen(label_text) > 0)
3478 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3480 redraw_mask |= REDRAW_FIELD;
3483 static void DrawPreviewLevelLabel(int mode)
3485 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3488 static void DrawPreviewLevelInfo(int mode)
3490 if (mode == MICROLABEL_LEVEL_NAME)
3491 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3492 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3493 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3496 static void DrawPreviewLevelExt(boolean restart)
3498 static unsigned int scroll_delay = 0;
3499 static unsigned int label_delay = 0;
3500 static int from_x, from_y, scroll_direction;
3501 static int label_state, label_counter;
3502 unsigned int scroll_delay_value = preview.step_delay;
3503 boolean show_level_border = (BorderElement != EL_EMPTY);
3504 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3505 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3512 if (preview.anim_mode == ANIM_CENTERED)
3514 if (level_xsize > preview.xsize)
3515 from_x = (level_xsize - preview.xsize) / 2;
3516 if (level_ysize > preview.ysize)
3517 from_y = (level_ysize - preview.ysize) / 2;
3520 from_x += preview.xoffset;
3521 from_y += preview.yoffset;
3523 scroll_direction = MV_RIGHT;
3527 DrawPreviewLevelPlayfield(from_x, from_y);
3528 DrawPreviewLevelLabel(label_state);
3530 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3531 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3533 // initialize delay counters
3534 ResetDelayCounter(&scroll_delay);
3535 ResetDelayCounter(&label_delay);
3537 if (leveldir_current->name)
3539 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3540 char label_text[MAX_OUTPUT_LINESIZE + 1];
3541 int font_nr = pos->font;
3542 int max_len_label_text = getMaxTextLength(pos, font_nr);
3544 if (pos->size != -1)
3545 max_len_label_text = pos->size;
3547 strncpy(label_text, leveldir_current->name, max_len_label_text);
3548 label_text[max_len_label_text] = '\0';
3550 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3551 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3557 // scroll preview level, if needed
3558 if (preview.anim_mode != ANIM_NONE &&
3559 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3560 DelayReached(&scroll_delay, scroll_delay_value))
3562 switch (scroll_direction)
3567 from_x -= preview.step_offset;
3568 from_x = (from_x < 0 ? 0 : from_x);
3571 scroll_direction = MV_UP;
3575 if (from_x < level_xsize - preview.xsize)
3577 from_x += preview.step_offset;
3578 from_x = (from_x > level_xsize - preview.xsize ?
3579 level_xsize - preview.xsize : from_x);
3582 scroll_direction = MV_DOWN;
3588 from_y -= preview.step_offset;
3589 from_y = (from_y < 0 ? 0 : from_y);
3592 scroll_direction = MV_RIGHT;
3596 if (from_y < level_ysize - preview.ysize)
3598 from_y += preview.step_offset;
3599 from_y = (from_y > level_ysize - preview.ysize ?
3600 level_ysize - preview.ysize : from_y);
3603 scroll_direction = MV_LEFT;
3610 DrawPreviewLevelPlayfield(from_x, from_y);
3613 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3614 // redraw micro level label, if needed
3615 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3616 !strEqual(level.author, ANONYMOUS_NAME) &&
3617 !strEqual(level.author, leveldir_current->name) &&
3618 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3620 int max_label_counter = 23;
3622 if (leveldir_current->imported_from != NULL &&
3623 strlen(leveldir_current->imported_from) > 0)
3624 max_label_counter += 14;
3625 if (leveldir_current->imported_by != NULL &&
3626 strlen(leveldir_current->imported_by) > 0)
3627 max_label_counter += 14;
3629 label_counter = (label_counter + 1) % max_label_counter;
3630 label_state = (label_counter >= 0 && label_counter <= 7 ?
3631 MICROLABEL_LEVEL_NAME :
3632 label_counter >= 9 && label_counter <= 12 ?
3633 MICROLABEL_LEVEL_AUTHOR_HEAD :
3634 label_counter >= 14 && label_counter <= 21 ?
3635 MICROLABEL_LEVEL_AUTHOR :
3636 label_counter >= 23 && label_counter <= 26 ?
3637 MICROLABEL_IMPORTED_FROM_HEAD :
3638 label_counter >= 28 && label_counter <= 35 ?
3639 MICROLABEL_IMPORTED_FROM :
3640 label_counter >= 37 && label_counter <= 40 ?
3641 MICROLABEL_IMPORTED_BY_HEAD :
3642 label_counter >= 42 && label_counter <= 49 ?
3643 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3645 if (leveldir_current->imported_from == NULL &&
3646 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3647 label_state == MICROLABEL_IMPORTED_FROM))
3648 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3649 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3651 DrawPreviewLevelLabel(label_state);
3655 void DrawPreviewPlayers(void)
3657 if (game_status != GAME_MODE_MAIN)
3660 // do not draw preview players if level preview redefined, but players aren't
3661 if (preview.redefined && !menu.main.preview_players.redefined)
3664 boolean player_found[MAX_PLAYERS];
3665 int num_players = 0;
3668 for (i = 0; i < MAX_PLAYERS; i++)
3669 player_found[i] = FALSE;
3671 // check which players can be found in the level (simple approach)
3672 for (x = 0; x < lev_fieldx; x++)
3674 for (y = 0; y < lev_fieldy; y++)
3676 int element = level.field[x][y];
3678 if (IS_PLAYER_ELEMENT(element))
3680 int player_nr = GET_PLAYER_NR(element);
3682 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3684 if (!player_found[player_nr])
3687 player_found[player_nr] = TRUE;
3692 struct TextPosInfo *pos = &menu.main.preview_players;
3693 int tile_size = pos->tile_size;
3694 int border_size = pos->border_size;
3695 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3696 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3697 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3698 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3699 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3700 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3701 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3702 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3703 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3704 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3705 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3706 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3708 // clear area in which the players will be drawn
3709 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3710 max_players_width, max_players_height);
3712 if (!network.enabled && !setup.team_mode)
3715 // only draw players if level is suited for team mode
3716 if (num_players < 2)
3719 // draw all players that were found in the level
3720 for (i = 0; i < MAX_PLAYERS; i++)
3722 if (player_found[i])
3724 int graphic = el2img(EL_PLAYER_1 + i);
3726 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3728 xpos += player_xoffset;
3729 ypos += player_yoffset;
3734 void DrawPreviewLevelInitial(void)
3736 DrawPreviewLevelExt(TRUE);
3737 DrawPreviewPlayers();
3740 void DrawPreviewLevelAnimation(void)
3742 DrawPreviewLevelExt(FALSE);
3745 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3746 int border_size, int font_nr)
3748 int graphic = el2img(EL_PLAYER_1 + player_nr);
3749 int font_height = getFontHeight(font_nr);
3750 int player_height = MAX(tile_size, font_height);
3751 int xoffset_text = tile_size + border_size;
3752 int yoffset_text = (player_height - font_height) / 2;
3753 int yoffset_graphic = (player_height - tile_size) / 2;
3754 char *player_name = getNetworkPlayerName(player_nr + 1);
3756 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3758 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3761 static void DrawNetworkPlayersExt(boolean force)
3763 if (game_status != GAME_MODE_MAIN)
3766 if (!network.connected && !force)
3769 // do not draw network players if level preview redefined, but players aren't
3770 if (preview.redefined && !menu.main.network_players.redefined)
3773 int num_players = 0;
3776 for (i = 0; i < MAX_PLAYERS; i++)
3777 if (stored_player[i].connected_network)
3780 struct TextPosInfo *pos = &menu.main.network_players;
3781 int tile_size = pos->tile_size;
3782 int border_size = pos->border_size;
3783 int xoffset_text = tile_size + border_size;
3784 int font_nr = pos->font;
3785 int font_width = getFontWidth(font_nr);
3786 int font_height = getFontHeight(font_nr);
3787 int player_height = MAX(tile_size, font_height);
3788 int player_yoffset = player_height + border_size;
3789 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3790 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3791 int all_players_height = num_players * player_yoffset - border_size;
3792 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3793 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3794 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3796 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3797 max_players_width, max_players_height);
3799 // first draw local network player ...
3800 for (i = 0; i < MAX_PLAYERS; i++)
3802 if (stored_player[i].connected_network &&
3803 stored_player[i].connected_locally)
3805 char *player_name = getNetworkPlayerName(i + 1);
3806 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3807 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3809 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3811 ypos += player_yoffset;
3815 // ... then draw all other network players
3816 for (i = 0; i < MAX_PLAYERS; i++)
3818 if (stored_player[i].connected_network &&
3819 !stored_player[i].connected_locally)
3821 char *player_name = getNetworkPlayerName(i + 1);
3822 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3823 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3825 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3827 ypos += player_yoffset;
3832 void DrawNetworkPlayers(void)
3834 DrawNetworkPlayersExt(FALSE);
3837 void ClearNetworkPlayers(void)
3839 DrawNetworkPlayersExt(TRUE);
3842 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3843 int graphic, int sync_frame,
3846 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3848 if (mask_mode == USE_MASKING)
3849 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3851 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3854 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3855 int graphic, int sync_frame, int mask_mode)
3857 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3859 if (mask_mode == USE_MASKING)
3860 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3862 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3865 static void DrawGraphicAnimation(int x, int y, int graphic)
3867 int lx = LEVELX(x), ly = LEVELY(y);
3868 int mask_mode = NO_MASKING;
3870 if (!IN_SCR_FIELD(x, y))
3873 if (game.use_masked_elements)
3875 if (Tile[lx][ly] != EL_EMPTY)
3877 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3879 mask_mode = USE_MASKING;
3883 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3884 graphic, GfxFrame[lx][ly], mask_mode);
3886 MarkTileDirty(x, y);
3889 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3891 int lx = LEVELX(x), ly = LEVELY(y);
3892 int mask_mode = NO_MASKING;
3894 if (!IN_SCR_FIELD(x, y))
3897 if (game.use_masked_elements)
3899 if (Tile[lx][ly] != EL_EMPTY)
3901 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3903 mask_mode = USE_MASKING;
3907 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3908 graphic, GfxFrame[lx][ly], mask_mode);
3910 MarkTileDirty(x, y);
3913 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3915 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3918 void DrawLevelElementAnimation(int x, int y, int element)
3920 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3922 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3925 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3927 int sx = SCREENX(x), sy = SCREENY(y);
3929 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3932 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3935 DrawGraphicAnimation(sx, sy, graphic);
3938 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3939 DrawLevelFieldCrumbled(x, y);
3941 if (GFX_CRUMBLED(Tile[x][y]))
3942 DrawLevelFieldCrumbled(x, y);
3946 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3948 int sx = SCREENX(x), sy = SCREENY(y);
3951 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3954 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3956 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3959 DrawGraphicAnimation(sx, sy, graphic);
3961 if (GFX_CRUMBLED(element))
3962 DrawLevelFieldCrumbled(x, y);
3965 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3967 if (player->use_murphy)
3969 // this works only because currently only one player can be "murphy" ...
3970 static int last_horizontal_dir = MV_LEFT;
3971 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3973 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3974 last_horizontal_dir = move_dir;
3976 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3978 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3980 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3986 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3989 static boolean equalGraphics(int graphic1, int graphic2)
3991 struct GraphicInfo *g1 = &graphic_info[graphic1];
3992 struct GraphicInfo *g2 = &graphic_info[graphic2];
3994 return (g1->bitmap == g2->bitmap &&
3995 g1->src_x == g2->src_x &&
3996 g1->src_y == g2->src_y &&
3997 g1->anim_frames == g2->anim_frames &&
3998 g1->anim_delay == g2->anim_delay &&
3999 g1->anim_mode == g2->anim_mode);
4002 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4006 DRAW_PLAYER_STAGE_INIT = 0,
4007 DRAW_PLAYER_STAGE_LAST_FIELD,
4008 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4009 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4010 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4011 DRAW_PLAYER_STAGE_PLAYER,
4013 DRAW_PLAYER_STAGE_PLAYER,
4014 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4016 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4017 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4019 NUM_DRAW_PLAYER_STAGES
4022 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4024 static int static_last_player_graphic[MAX_PLAYERS];
4025 static int static_last_player_frame[MAX_PLAYERS];
4026 static boolean static_player_is_opaque[MAX_PLAYERS];
4027 static boolean draw_player[MAX_PLAYERS];
4028 int pnr = player->index_nr;
4030 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4032 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4033 static_last_player_frame[pnr] = player->Frame;
4034 static_player_is_opaque[pnr] = FALSE;
4036 draw_player[pnr] = TRUE;
4039 if (!draw_player[pnr])
4043 if (!IN_LEV_FIELD(player->jx, player->jy))
4045 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4046 Debug("draw:DrawPlayerExt", "This should never happen!");
4048 draw_player[pnr] = FALSE;
4054 int last_player_graphic = static_last_player_graphic[pnr];
4055 int last_player_frame = static_last_player_frame[pnr];
4056 boolean player_is_opaque = static_player_is_opaque[pnr];
4058 int jx = player->jx;
4059 int jy = player->jy;
4060 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4061 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4062 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4063 int last_jx = (player->is_moving ? jx - dx : jx);
4064 int last_jy = (player->is_moving ? jy - dy : jy);
4065 int next_jx = jx + dx;
4066 int next_jy = jy + dy;
4067 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4068 int sx = SCREENX(jx);
4069 int sy = SCREENY(jy);
4070 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4071 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4072 int element = Tile[jx][jy];
4073 int last_element = Tile[last_jx][last_jy];
4074 int action = (player->is_pushing ? ACTION_PUSHING :
4075 player->is_digging ? ACTION_DIGGING :
4076 player->is_collecting ? ACTION_COLLECTING :
4077 player->is_moving ? ACTION_MOVING :
4078 player->is_snapping ? ACTION_SNAPPING :
4079 player->is_dropping ? ACTION_DROPPING :
4080 player->is_waiting ? player->action_waiting :
4083 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4085 // ------------------------------------------------------------------------
4086 // initialize drawing the player
4087 // ------------------------------------------------------------------------
4089 draw_player[pnr] = FALSE;
4091 // GfxElement[][] is set to the element the player is digging or collecting;
4092 // remove also for off-screen player if the player is not moving anymore
4093 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4094 GfxElement[jx][jy] = EL_UNDEFINED;
4096 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4099 if (element == EL_EXPLOSION)
4102 InitPlayerGfxAnimation(player, action, move_dir);
4104 draw_player[pnr] = TRUE;
4106 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4108 // ------------------------------------------------------------------------
4109 // draw things in the field the player is leaving, if needed
4110 // ------------------------------------------------------------------------
4112 if (!IN_SCR_FIELD(sx, sy))
4113 draw_player[pnr] = FALSE;
4115 if (!player->is_moving)
4118 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4120 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4122 if (last_element == EL_DYNAMITE_ACTIVE ||
4123 last_element == EL_EM_DYNAMITE_ACTIVE ||
4124 last_element == EL_SP_DISK_RED_ACTIVE)
4125 DrawDynamite(last_jx, last_jy);
4127 DrawLevelFieldThruMask(last_jx, last_jy);
4129 else if (last_element == EL_DYNAMITE_ACTIVE ||
4130 last_element == EL_EM_DYNAMITE_ACTIVE ||
4131 last_element == EL_SP_DISK_RED_ACTIVE)
4132 DrawDynamite(last_jx, last_jy);
4134 DrawLevelField(last_jx, last_jy);
4136 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4137 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4139 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4141 // ------------------------------------------------------------------------
4142 // draw things behind the player, if needed
4143 // ------------------------------------------------------------------------
4147 DrawLevelElement(jx, jy, Back[jx][jy]);
4152 if (IS_ACTIVE_BOMB(element))
4154 DrawLevelElement(jx, jy, EL_EMPTY);
4159 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4161 int old_element = GfxElement[jx][jy];
4162 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4163 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4165 if (GFX_CRUMBLED(old_element))
4166 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4168 DrawScreenGraphic(sx, sy, old_graphic, frame);
4170 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4171 static_player_is_opaque[pnr] = TRUE;
4175 GfxElement[jx][jy] = EL_UNDEFINED;
4177 // make sure that pushed elements are drawn with correct frame rate
4178 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4180 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4181 GfxFrame[jx][jy] = player->StepFrame;
4183 DrawLevelField(jx, jy);
4186 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4188 // ------------------------------------------------------------------------
4189 // draw things the player is pushing, if needed
4190 // ------------------------------------------------------------------------
4192 if (!player->is_pushing || !player->is_moving)
4195 int gfx_frame = GfxFrame[jx][jy];
4197 if (!IS_MOVING(jx, jy)) // push movement already finished
4199 element = Tile[next_jx][next_jy];
4200 gfx_frame = GfxFrame[next_jx][next_jy];
4203 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4204 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4205 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4207 // draw background element under pushed element (like the Sokoban field)
4208 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4210 // this allows transparent pushing animation over non-black background
4213 DrawLevelElement(jx, jy, Back[jx][jy]);
4215 DrawLevelElement(jx, jy, EL_EMPTY);
4217 if (Back[next_jx][next_jy])
4218 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4220 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4222 else if (Back[next_jx][next_jy])
4223 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4225 int px = SCREENX(jx), py = SCREENY(jy);
4226 int pxx = (TILEX - ABS(sxx)) * dx;
4227 int pyy = (TILEY - ABS(syy)) * dy;
4230 // do not draw (EM style) pushing animation when pushing is finished
4231 // (two-tile animations usually do not contain start and end frame)
4232 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4233 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4235 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4237 // masked drawing is needed for EMC style (double) movement graphics
4238 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4239 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4242 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4244 // ------------------------------------------------------------------------
4245 // draw player himself
4246 // ------------------------------------------------------------------------
4248 int graphic = getPlayerGraphic(player, move_dir);
4250 // in the case of changed player action or direction, prevent the current
4251 // animation frame from being restarted for identical animations
4252 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4253 player->Frame = last_player_frame;
4255 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4257 if (player_is_opaque)
4258 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4260 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4262 if (SHIELD_ON(player))
4264 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4265 IMG_SHIELD_NORMAL_ACTIVE);
4266 frame = getGraphicAnimationFrame(graphic, -1);
4268 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4271 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4273 // ------------------------------------------------------------------------
4274 // draw things in front of player (active dynamite or dynabombs)
4275 // ------------------------------------------------------------------------
4277 if (IS_ACTIVE_BOMB(element))
4279 int graphic = el2img(element);
4280 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4282 if (game.emulation == EMU_SUPAPLEX)
4283 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4285 DrawGraphicThruMask(sx, sy, graphic, frame);
4288 if (player_is_moving && last_element == EL_EXPLOSION)
4290 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4291 GfxElement[last_jx][last_jy] : EL_EMPTY);
4292 int graphic = el_act2img(element, ACTION_EXPLODING);
4293 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4294 int phase = ExplodePhase[last_jx][last_jy] - 1;
4295 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4298 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4301 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4303 // ------------------------------------------------------------------------
4304 // draw elements the player is just walking/passing through/under
4305 // ------------------------------------------------------------------------
4307 if (player_is_moving)
4309 // handle the field the player is leaving ...
4310 if (IS_ACCESSIBLE_INSIDE(last_element))
4311 DrawLevelField(last_jx, last_jy);
4312 else if (IS_ACCESSIBLE_UNDER(last_element))
4313 DrawLevelFieldThruMask(last_jx, last_jy);
4316 // do not redraw accessible elements if the player is just pushing them
4317 if (!player_is_moving || !player->is_pushing)
4319 // ... and the field the player is entering
4320 if (IS_ACCESSIBLE_INSIDE(element))
4321 DrawLevelField(jx, jy);
4322 else if (IS_ACCESSIBLE_UNDER(element))
4323 DrawLevelFieldThruMask(jx, jy);
4326 MarkTileDirty(sx, sy);
4330 void DrawPlayer(struct PlayerInfo *player)
4334 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4335 DrawPlayerExt(player, i);
4338 void DrawAllPlayers(void)
4342 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4343 for (j = 0; j < MAX_PLAYERS; j++)
4344 if (stored_player[j].active)
4345 DrawPlayerExt(&stored_player[j], i);
4348 void DrawPlayerField(int x, int y)
4350 if (!IS_PLAYER(x, y))
4353 DrawPlayer(PLAYERINFO(x, y));
4356 // ----------------------------------------------------------------------------
4358 void WaitForEventToContinue(void)
4360 boolean first_wait = TRUE;
4361 boolean still_wait = TRUE;
4363 if (program.headless)
4366 // simulate releasing mouse button over last gadget, if still pressed
4368 HandleGadgets(-1, -1, 0);
4370 button_status = MB_RELEASED;
4373 ClearPlayerAction();
4379 if (NextValidEvent(&event))
4383 case EVENT_BUTTONPRESS:
4384 case EVENT_FINGERPRESS:
4388 case EVENT_BUTTONRELEASE:
4389 case EVENT_FINGERRELEASE:
4390 still_wait = first_wait;
4393 case EVENT_KEYPRESS:
4394 case SDL_CONTROLLERBUTTONDOWN:
4395 case SDL_JOYBUTTONDOWN:
4400 HandleOtherEvents(&event);
4404 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4409 if (!PendingEvent())
4414 #define MAX_REQUEST_LINES 13
4415 #define MAX_REQUEST_LINE_FONT1_LEN 7
4416 #define MAX_REQUEST_LINE_FONT2_LEN 10
4418 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4420 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4422 int draw_buffer_last = GetDrawtoField();
4423 int width = request.width;
4424 int height = request.height;
4428 // when showing request dialog after game ended, deactivate game panel
4429 if (game_just_ended)
4430 game.panel.active = FALSE;
4432 game.request_active = TRUE;
4434 setRequestPosition(&sx, &sy, FALSE);
4436 button_status = MB_RELEASED;
4438 request_gadget_id = -1;
4443 boolean event_handled = FALSE;
4445 if (game_just_ended)
4447 SetDrawtoField(draw_buffer_game);
4449 HandleGameActions();
4451 SetDrawtoField(DRAW_TO_BACKBUFFER);
4453 if (global.use_envelope_request)
4455 // copy current state of request area to middle of playfield area
4456 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4464 while (NextValidEvent(&event))
4466 event_handled = TRUE;
4470 case EVENT_BUTTONPRESS:
4471 case EVENT_BUTTONRELEASE:
4472 case EVENT_MOTIONNOTIFY:
4476 if (event.type == EVENT_MOTIONNOTIFY)
4481 motion_status = TRUE;
4482 mx = ((MotionEvent *) &event)->x;
4483 my = ((MotionEvent *) &event)->y;
4487 motion_status = FALSE;
4488 mx = ((ButtonEvent *) &event)->x;
4489 my = ((ButtonEvent *) &event)->y;
4490 if (event.type == EVENT_BUTTONPRESS)
4491 button_status = ((ButtonEvent *) &event)->button;
4493 button_status = MB_RELEASED;
4496 // this sets 'request_gadget_id'
4497 HandleGadgets(mx, my, button_status);
4499 switch (request_gadget_id)
4501 case TOOL_CTRL_ID_YES:
4502 case TOOL_CTRL_ID_TOUCH_YES:
4505 case TOOL_CTRL_ID_NO:
4506 case TOOL_CTRL_ID_TOUCH_NO:
4509 case TOOL_CTRL_ID_CONFIRM:
4510 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4511 result = TRUE | FALSE;
4514 case TOOL_CTRL_ID_PLAYER_1:
4517 case TOOL_CTRL_ID_PLAYER_2:
4520 case TOOL_CTRL_ID_PLAYER_3:
4523 case TOOL_CTRL_ID_PLAYER_4:
4528 // only check clickable animations if no request gadget clicked
4529 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4536 case SDL_WINDOWEVENT:
4537 HandleWindowEvent((WindowEvent *) &event);
4540 case SDL_APP_WILLENTERBACKGROUND:
4541 case SDL_APP_DIDENTERBACKGROUND:
4542 case SDL_APP_WILLENTERFOREGROUND:
4543 case SDL_APP_DIDENTERFOREGROUND:
4544 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4547 case EVENT_KEYPRESS:
4549 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4554 if (req_state & REQ_CONFIRM)
4563 #if defined(KSYM_Rewind)
4564 case KSYM_Rewind: // for Amazon Fire TV remote
4573 #if defined(KSYM_FastForward)
4574 case KSYM_FastForward: // for Amazon Fire TV remote
4580 HandleKeysDebug(key, KEY_PRESSED);
4584 if (req_state & REQ_PLAYER)
4586 int old_player_nr = setup.network_player_nr;
4589 result = old_player_nr + 1;
4594 result = old_player_nr + 1;
4625 case EVENT_FINGERRELEASE:
4626 case EVENT_KEYRELEASE:
4627 ClearPlayerAction();
4630 case SDL_CONTROLLERBUTTONDOWN:
4631 switch (event.cbutton.button)
4633 case SDL_CONTROLLER_BUTTON_A:
4634 case SDL_CONTROLLER_BUTTON_X:
4635 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4636 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4640 case SDL_CONTROLLER_BUTTON_B:
4641 case SDL_CONTROLLER_BUTTON_Y:
4642 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4643 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4644 case SDL_CONTROLLER_BUTTON_BACK:
4649 if (req_state & REQ_PLAYER)
4651 int old_player_nr = setup.network_player_nr;
4654 result = old_player_nr + 1;
4656 switch (event.cbutton.button)
4658 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4659 case SDL_CONTROLLER_BUTTON_Y:
4663 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4664 case SDL_CONTROLLER_BUTTON_B:
4668 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4669 case SDL_CONTROLLER_BUTTON_A:
4673 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4674 case SDL_CONTROLLER_BUTTON_X:
4685 case SDL_CONTROLLERBUTTONUP:
4686 HandleJoystickEvent(&event);
4687 ClearPlayerAction();
4691 HandleOtherEvents(&event);
4696 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4698 int joy = AnyJoystick();
4700 if (joy & JOY_BUTTON_1)
4702 else if (joy & JOY_BUTTON_2)
4705 else if (AnyJoystick())
4707 int joy = AnyJoystick();
4709 if (req_state & REQ_PLAYER)
4713 else if (joy & JOY_RIGHT)
4715 else if (joy & JOY_DOWN)
4717 else if (joy & JOY_LEFT)
4724 if (game_just_ended)
4726 if (global.use_envelope_request)
4728 // copy back current state of pressed buttons inside request area
4729 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4733 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4739 SetDrawtoField(draw_buffer_last);
4741 game.request_active = FALSE;
4746 static boolean RequestDoor(char *text, unsigned int req_state)
4748 int draw_buffer_last = GetDrawtoField();
4749 unsigned int old_door_state;
4750 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4751 int font_nr = FONT_TEXT_2;
4756 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4758 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4759 font_nr = FONT_TEXT_1;
4762 if (game_status == GAME_MODE_PLAYING)
4763 BlitScreenToBitmap(backbuffer);
4765 // disable deactivated drawing when quick-loading level tape recording
4766 if (tape.playing && tape.deactivate_display)
4767 TapeDeactivateDisplayOff(TRUE);
4769 SetMouseCursor(CURSOR_DEFAULT);
4771 // pause network game while waiting for request to answer
4772 if (network.enabled &&
4773 game_status == GAME_MODE_PLAYING &&
4774 !game.all_players_gone &&
4775 req_state & REQUEST_WAIT_FOR_INPUT)
4776 SendToServer_PausePlaying();
4778 old_door_state = GetDoorState();
4780 // simulate releasing mouse button over last gadget, if still pressed
4782 HandleGadgets(-1, -1, 0);
4786 // draw released gadget before proceeding
4789 if (old_door_state & DOOR_OPEN_1)
4791 CloseDoor(DOOR_CLOSE_1);
4793 // save old door content
4794 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4795 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4798 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4799 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4801 // clear door drawing field
4802 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4804 // force DOOR font inside door area
4805 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4807 // write text for request
4808 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4810 char text_line[max_request_line_len + 1];
4816 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4818 tc = *(text_ptr + tx);
4819 // if (!tc || tc == ' ')
4820 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4824 if ((tc == '?' || tc == '!') && tl == 0)
4834 strncpy(text_line, text_ptr, tl);
4837 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4838 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4839 text_line, font_nr);
4841 text_ptr += tl + (tc == ' ' ? 1 : 0);
4842 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4847 if (req_state & REQ_ASK)
4849 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4850 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4851 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4852 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4854 else if (req_state & REQ_CONFIRM)
4856 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4857 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4859 else if (req_state & REQ_PLAYER)
4861 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4862 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4863 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4864 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4867 // copy request gadgets to door backbuffer
4868 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4870 OpenDoor(DOOR_OPEN_1);
4872 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4874 if (game_status == GAME_MODE_PLAYING)
4876 SetPanelBackground();
4877 SetDrawBackgroundMask(REDRAW_DOOR_1);
4881 SetDrawBackgroundMask(REDRAW_FIELD);
4887 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4889 // ---------- handle request buttons ----------
4890 result = RequestHandleEvents(req_state, draw_buffer_last);
4894 if (!(req_state & REQ_STAY_OPEN))
4896 CloseDoor(DOOR_CLOSE_1);
4898 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4899 (req_state & REQ_REOPEN))
4900 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4905 if (game_status == GAME_MODE_PLAYING)
4907 SetPanelBackground();
4908 SetDrawBackgroundMask(REDRAW_DOOR_1);
4912 SetDrawBackgroundMask(REDRAW_FIELD);
4915 // continue network game after request
4916 if (network.enabled &&
4917 game_status == GAME_MODE_PLAYING &&
4918 !game.all_players_gone &&
4919 req_state & REQUEST_WAIT_FOR_INPUT)
4920 SendToServer_ContinuePlaying();
4922 // restore deactivated drawing when quick-loading level tape recording
4923 if (tape.playing && tape.deactivate_display)
4924 TapeDeactivateDisplayOn();
4929 static boolean RequestEnvelope(char *text, unsigned int req_state)
4931 int draw_buffer_last = GetDrawtoField();
4934 if (game_status == GAME_MODE_PLAYING)
4935 BlitScreenToBitmap(backbuffer);
4937 // disable deactivated drawing when quick-loading level tape recording
4938 if (tape.playing && tape.deactivate_display)
4939 TapeDeactivateDisplayOff(TRUE);
4941 SetMouseCursor(CURSOR_DEFAULT);
4943 // pause network game while waiting for request to answer
4944 if (network.enabled &&
4945 game_status == GAME_MODE_PLAYING &&
4946 !game.all_players_gone &&
4947 req_state & REQUEST_WAIT_FOR_INPUT)
4948 SendToServer_PausePlaying();
4950 // simulate releasing mouse button over last gadget, if still pressed
4952 HandleGadgets(-1, -1, 0);
4956 // (replace with setting corresponding request background)
4957 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4958 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4960 // clear door drawing field
4961 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4963 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4965 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4967 if (game_status == GAME_MODE_PLAYING)
4969 SetPanelBackground();
4970 SetDrawBackgroundMask(REDRAW_DOOR_1);
4974 SetDrawBackgroundMask(REDRAW_FIELD);
4980 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4982 // ---------- handle request buttons ----------
4983 result = RequestHandleEvents(req_state, draw_buffer_last);
4987 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4991 if (game_status == GAME_MODE_PLAYING)
4993 SetPanelBackground();
4994 SetDrawBackgroundMask(REDRAW_DOOR_1);
4998 SetDrawBackgroundMask(REDRAW_FIELD);
5001 // continue network game after request
5002 if (network.enabled &&
5003 game_status == GAME_MODE_PLAYING &&
5004 !game.all_players_gone &&
5005 req_state & REQUEST_WAIT_FOR_INPUT)
5006 SendToServer_ContinuePlaying();
5008 // restore deactivated drawing when quick-loading level tape recording
5009 if (tape.playing && tape.deactivate_display)
5010 TapeDeactivateDisplayOn();
5015 boolean Request(char *text, unsigned int req_state)
5017 boolean overlay_enabled = GetOverlayEnabled();
5020 game.request_active_or_moving = TRUE;
5022 SetOverlayEnabled(FALSE);
5024 if (global.use_envelope_request)
5025 result = RequestEnvelope(text, req_state);
5027 result = RequestDoor(text, req_state);
5029 SetOverlayEnabled(overlay_enabled);
5031 game.request_active_or_moving = FALSE;
5036 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5038 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5039 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5042 if (dpo1->sort_priority != dpo2->sort_priority)
5043 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5045 compare_result = dpo1->nr - dpo2->nr;
5047 return compare_result;
5050 void InitGraphicCompatibilityInfo_Doors(void)
5056 struct DoorInfo *door;
5060 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5061 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5063 { -1, -1, -1, NULL }
5065 struct Rect door_rect_list[] =
5067 { DX, DY, DXSIZE, DYSIZE },
5068 { VX, VY, VXSIZE, VYSIZE }
5072 for (i = 0; doors[i].door_token != -1; i++)
5074 int door_token = doors[i].door_token;
5075 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5076 int part_1 = doors[i].part_1;
5077 int part_8 = doors[i].part_8;
5078 int part_2 = part_1 + 1;
5079 int part_3 = part_1 + 2;
5080 struct DoorInfo *door = doors[i].door;
5081 struct Rect *door_rect = &door_rect_list[door_index];
5082 boolean door_gfx_redefined = FALSE;
5084 // check if any door part graphic definitions have been redefined
5086 for (j = 0; door_part_controls[j].door_token != -1; j++)
5088 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5089 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5091 if (dpc->door_token == door_token && fi->redefined)
5092 door_gfx_redefined = TRUE;
5095 // check for old-style door graphic/animation modifications
5097 if (!door_gfx_redefined)
5099 if (door->anim_mode & ANIM_STATIC_PANEL)
5101 door->panel.step_xoffset = 0;
5102 door->panel.step_yoffset = 0;
5105 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5107 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5108 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5109 int num_door_steps, num_panel_steps;
5111 // remove door part graphics other than the two default wings
5113 for (j = 0; door_part_controls[j].door_token != -1; j++)
5115 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5116 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5118 if (dpc->graphic >= part_3 &&
5119 dpc->graphic <= part_8)
5123 // set graphics and screen positions of the default wings
5125 g_part_1->width = door_rect->width;
5126 g_part_1->height = door_rect->height;
5127 g_part_2->width = door_rect->width;
5128 g_part_2->height = door_rect->height;
5129 g_part_2->src_x = door_rect->width;
5130 g_part_2->src_y = g_part_1->src_y;
5132 door->part_2.x = door->part_1.x;
5133 door->part_2.y = door->part_1.y;
5135 if (door->width != -1)
5137 g_part_1->width = door->width;
5138 g_part_2->width = door->width;
5140 // special treatment for graphics and screen position of right wing
5141 g_part_2->src_x += door_rect->width - door->width;
5142 door->part_2.x += door_rect->width - door->width;
5145 if (door->height != -1)
5147 g_part_1->height = door->height;
5148 g_part_2->height = door->height;
5150 // special treatment for graphics and screen position of bottom wing
5151 g_part_2->src_y += door_rect->height - door->height;
5152 door->part_2.y += door_rect->height - door->height;
5155 // set animation delays for the default wings and panels
5157 door->part_1.step_delay = door->step_delay;
5158 door->part_2.step_delay = door->step_delay;
5159 door->panel.step_delay = door->step_delay;
5161 // set animation draw order for the default wings
5163 door->part_1.sort_priority = 2; // draw left wing over ...
5164 door->part_2.sort_priority = 1; // ... right wing
5166 // set animation draw offset for the default wings
5168 if (door->anim_mode & ANIM_HORIZONTAL)
5170 door->part_1.step_xoffset = door->step_offset;
5171 door->part_1.step_yoffset = 0;
5172 door->part_2.step_xoffset = door->step_offset * -1;
5173 door->part_2.step_yoffset = 0;
5175 num_door_steps = g_part_1->width / door->step_offset;
5177 else // ANIM_VERTICAL
5179 door->part_1.step_xoffset = 0;
5180 door->part_1.step_yoffset = door->step_offset;
5181 door->part_2.step_xoffset = 0;
5182 door->part_2.step_yoffset = door->step_offset * -1;
5184 num_door_steps = g_part_1->height / door->step_offset;
5187 // set animation draw offset for the default panels
5189 if (door->step_offset > 1)
5191 num_panel_steps = 2 * door_rect->height / door->step_offset;
5192 door->panel.start_step = num_panel_steps - num_door_steps;
5193 door->panel.start_step_closing = door->panel.start_step;
5197 num_panel_steps = door_rect->height / door->step_offset;
5198 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5199 door->panel.start_step_closing = door->panel.start_step;
5200 door->panel.step_delay *= 2;
5207 void InitDoors(void)
5211 for (i = 0; door_part_controls[i].door_token != -1; i++)
5213 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5214 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5216 // initialize "start_step_opening" and "start_step_closing", if needed
5217 if (dpc->pos->start_step_opening == 0 &&
5218 dpc->pos->start_step_closing == 0)
5220 // dpc->pos->start_step_opening = dpc->pos->start_step;
5221 dpc->pos->start_step_closing = dpc->pos->start_step;
5224 // fill structure for door part draw order (sorted below)
5226 dpo->sort_priority = dpc->pos->sort_priority;
5229 // sort door part controls according to sort_priority and graphic number
5230 qsort(door_part_order, MAX_DOOR_PARTS,
5231 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5234 unsigned int OpenDoor(unsigned int door_state)
5236 if (door_state & DOOR_COPY_BACK)
5238 if (door_state & DOOR_OPEN_1)
5239 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5240 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5242 if (door_state & DOOR_OPEN_2)
5243 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5244 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5246 door_state &= ~DOOR_COPY_BACK;
5249 return MoveDoor(door_state);
5252 unsigned int CloseDoor(unsigned int door_state)
5254 unsigned int old_door_state = GetDoorState();
5256 if (!(door_state & DOOR_NO_COPY_BACK))
5258 if (old_door_state & DOOR_OPEN_1)
5259 BlitBitmap(backbuffer, bitmap_db_door_1,
5260 DX, DY, DXSIZE, DYSIZE, 0, 0);
5262 if (old_door_state & DOOR_OPEN_2)
5263 BlitBitmap(backbuffer, bitmap_db_door_2,
5264 VX, VY, VXSIZE, VYSIZE, 0, 0);
5266 door_state &= ~DOOR_NO_COPY_BACK;
5269 return MoveDoor(door_state);
5272 unsigned int GetDoorState(void)
5274 return MoveDoor(DOOR_GET_STATE);
5277 unsigned int SetDoorState(unsigned int door_state)
5279 return MoveDoor(door_state | DOOR_SET_STATE);
5282 static int euclid(int a, int b)
5284 return (b ? euclid(b, a % b) : a);
5287 unsigned int MoveDoor(unsigned int door_state)
5289 struct Rect door_rect_list[] =
5291 { DX, DY, DXSIZE, DYSIZE },
5292 { VX, VY, VXSIZE, VYSIZE }
5294 static int door1 = DOOR_CLOSE_1;
5295 static int door2 = DOOR_CLOSE_2;
5296 unsigned int door_delay = 0;
5297 unsigned int door_delay_value;
5300 if (door_state == DOOR_GET_STATE)
5301 return (door1 | door2);
5303 if (door_state & DOOR_SET_STATE)
5305 if (door_state & DOOR_ACTION_1)
5306 door1 = door_state & DOOR_ACTION_1;
5307 if (door_state & DOOR_ACTION_2)
5308 door2 = door_state & DOOR_ACTION_2;
5310 return (door1 | door2);
5313 if (!(door_state & DOOR_FORCE_REDRAW))
5315 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5316 door_state &= ~DOOR_OPEN_1;
5317 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5318 door_state &= ~DOOR_CLOSE_1;
5319 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5320 door_state &= ~DOOR_OPEN_2;
5321 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5322 door_state &= ~DOOR_CLOSE_2;
5325 if (global.autoplay_leveldir)
5327 door_state |= DOOR_NO_DELAY;
5328 door_state &= ~DOOR_CLOSE_ALL;
5331 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5332 door_state |= DOOR_NO_DELAY;
5334 if (door_state & DOOR_ACTION)
5336 boolean door_panel_drawn[NUM_DOORS];
5337 boolean panel_has_doors[NUM_DOORS];
5338 boolean door_part_skip[MAX_DOOR_PARTS];
5339 boolean door_part_done[MAX_DOOR_PARTS];
5340 boolean door_part_done_all;
5341 int num_steps[MAX_DOOR_PARTS];
5342 int max_move_delay = 0; // delay for complete animations of all doors
5343 int max_step_delay = 0; // delay (ms) between two animation frames
5344 int num_move_steps = 0; // number of animation steps for all doors
5345 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5346 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5347 int current_move_delay = 0;
5351 for (i = 0; i < NUM_DOORS; i++)
5352 panel_has_doors[i] = FALSE;
5354 for (i = 0; i < MAX_DOOR_PARTS; i++)
5356 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5357 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5358 int door_token = dpc->door_token;
5360 door_part_done[i] = FALSE;
5361 door_part_skip[i] = (!(door_state & door_token) ||
5365 for (i = 0; i < MAX_DOOR_PARTS; i++)
5367 int nr = door_part_order[i].nr;
5368 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5369 struct DoorPartPosInfo *pos = dpc->pos;
5370 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5371 int door_token = dpc->door_token;
5372 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5373 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5374 int step_xoffset = ABS(pos->step_xoffset);
5375 int step_yoffset = ABS(pos->step_yoffset);
5376 int step_delay = pos->step_delay;
5377 int current_door_state = door_state & door_token;
5378 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5379 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5380 boolean part_opening = (is_panel ? door_closing : door_opening);
5381 int start_step = (part_opening ? pos->start_step_opening :
5382 pos->start_step_closing);
5383 float move_xsize = (step_xoffset ? g->width : 0);
5384 float move_ysize = (step_yoffset ? g->height : 0);
5385 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5386 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5387 int move_steps = (move_xsteps && move_ysteps ?
5388 MIN(move_xsteps, move_ysteps) :
5389 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5390 int move_delay = move_steps * step_delay;
5392 if (door_part_skip[nr])
5395 max_move_delay = MAX(max_move_delay, move_delay);
5396 max_step_delay = (max_step_delay == 0 ? step_delay :
5397 euclid(max_step_delay, step_delay));
5398 num_steps[nr] = move_steps;
5402 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5404 panel_has_doors[door_index] = TRUE;
5408 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5410 num_move_steps = max_move_delay / max_step_delay;
5411 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5413 door_delay_value = max_step_delay;
5415 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5417 start = num_move_steps - 1;
5421 // opening door sound has priority over simultaneously closing door
5422 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5424 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5426 if (door_state & DOOR_OPEN_1)
5427 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5428 if (door_state & DOOR_OPEN_2)
5429 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5431 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5433 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5435 if (door_state & DOOR_CLOSE_1)
5436 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5437 if (door_state & DOOR_CLOSE_2)
5438 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5442 for (k = start; k < num_move_steps; k++)
5444 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5446 door_part_done_all = TRUE;
5448 for (i = 0; i < NUM_DOORS; i++)
5449 door_panel_drawn[i] = FALSE;
5451 for (i = 0; i < MAX_DOOR_PARTS; i++)
5453 int nr = door_part_order[i].nr;
5454 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5455 struct DoorPartPosInfo *pos = dpc->pos;
5456 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5457 int door_token = dpc->door_token;
5458 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5459 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5460 boolean is_panel_and_door_has_closed = FALSE;
5461 struct Rect *door_rect = &door_rect_list[door_index];
5462 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5464 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5465 int current_door_state = door_state & door_token;
5466 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5467 boolean door_closing = !door_opening;
5468 boolean part_opening = (is_panel ? door_closing : door_opening);
5469 boolean part_closing = !part_opening;
5470 int start_step = (part_opening ? pos->start_step_opening :
5471 pos->start_step_closing);
5472 int step_delay = pos->step_delay;
5473 int step_factor = step_delay / max_step_delay;
5474 int k1 = (step_factor ? k / step_factor + 1 : k);
5475 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5476 int kk = MAX(0, k2);
5479 int src_x, src_y, src_xx, src_yy;
5480 int dst_x, dst_y, dst_xx, dst_yy;
5483 if (door_part_skip[nr])
5486 if (!(door_state & door_token))
5494 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5495 int kk_door = MAX(0, k2_door);
5496 int sync_frame = kk_door * door_delay_value;
5497 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5499 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5500 &g_src_x, &g_src_y);
5505 if (!door_panel_drawn[door_index])
5507 ClearRectangle(drawto, door_rect->x, door_rect->y,
5508 door_rect->width, door_rect->height);
5510 door_panel_drawn[door_index] = TRUE;
5513 // draw opening or closing door parts
5515 if (pos->step_xoffset < 0) // door part on right side
5518 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5521 if (dst_xx + width > door_rect->width)
5522 width = door_rect->width - dst_xx;
5524 else // door part on left side
5527 dst_xx = pos->x - kk * pos->step_xoffset;
5531 src_xx = ABS(dst_xx);
5535 width = g->width - src_xx;
5537 if (width > door_rect->width)
5538 width = door_rect->width;
5540 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5543 if (pos->step_yoffset < 0) // door part on bottom side
5546 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5549 if (dst_yy + height > door_rect->height)
5550 height = door_rect->height - dst_yy;
5552 else // door part on top side
5555 dst_yy = pos->y - kk * pos->step_yoffset;
5559 src_yy = ABS(dst_yy);
5563 height = g->height - src_yy;
5566 src_x = g_src_x + src_xx;
5567 src_y = g_src_y + src_yy;
5569 dst_x = door_rect->x + dst_xx;
5570 dst_y = door_rect->y + dst_yy;
5572 is_panel_and_door_has_closed =
5575 panel_has_doors[door_index] &&
5576 k >= num_move_steps_doors_only - 1);
5578 if (width >= 0 && width <= g->width &&
5579 height >= 0 && height <= g->height &&
5580 !is_panel_and_door_has_closed)
5582 if (is_panel || !pos->draw_masked)
5583 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5586 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5590 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5592 if ((part_opening && (width < 0 || height < 0)) ||
5593 (part_closing && (width >= g->width && height >= g->height)))
5594 door_part_done[nr] = TRUE;
5596 // continue door part animations, but not panel after door has closed
5597 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5598 door_part_done_all = FALSE;
5601 if (!(door_state & DOOR_NO_DELAY))
5605 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5607 current_move_delay += max_step_delay;
5609 // prevent OS (Windows) from complaining about program not responding
5613 if (door_part_done_all)
5617 if (!(door_state & DOOR_NO_DELAY))
5619 // wait for specified door action post delay
5620 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5621 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5622 else if (door_state & DOOR_ACTION_1)
5623 door_delay_value = door_1.post_delay;
5624 else if (door_state & DOOR_ACTION_2)
5625 door_delay_value = door_2.post_delay;
5627 while (!DelayReached(&door_delay, door_delay_value))
5632 if (door_state & DOOR_ACTION_1)
5633 door1 = door_state & DOOR_ACTION_1;
5634 if (door_state & DOOR_ACTION_2)
5635 door2 = door_state & DOOR_ACTION_2;
5637 // draw masked border over door area
5638 DrawMaskedBorder(REDRAW_DOOR_1);
5639 DrawMaskedBorder(REDRAW_DOOR_2);
5641 ClearAutoRepeatKeyEvents();
5643 return (door1 | door2);
5646 static boolean useSpecialEditorDoor(void)
5648 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5649 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5651 // do not draw special editor door if editor border defined or redefined
5652 if (graphic_info[graphic].bitmap != NULL || redefined)
5655 // do not draw special editor door if global border defined to be empty
5656 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5659 // do not draw special editor door if viewport definitions do not match
5663 EY + EYSIZE != VY + VYSIZE)
5669 void DrawSpecialEditorDoor(void)
5671 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5672 int top_border_width = gfx1->width;
5673 int top_border_height = gfx1->height;
5674 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5675 int ex = EX - outer_border;
5676 int ey = EY - outer_border;
5677 int vy = VY - outer_border;
5678 int exsize = EXSIZE + 2 * outer_border;
5680 if (!useSpecialEditorDoor())
5683 // draw bigger level editor toolbox window
5684 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5685 top_border_width, top_border_height, ex, ey - top_border_height);
5686 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5687 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5689 redraw_mask |= REDRAW_ALL;
5692 void UndrawSpecialEditorDoor(void)
5694 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5695 int top_border_width = gfx1->width;
5696 int top_border_height = gfx1->height;
5697 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5698 int ex = EX - outer_border;
5699 int ey = EY - outer_border;
5700 int ey_top = ey - top_border_height;
5701 int exsize = EXSIZE + 2 * outer_border;
5702 int eysize = EYSIZE + 2 * outer_border;
5704 if (!useSpecialEditorDoor())
5707 // draw normal tape recorder window
5708 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5710 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5711 ex, ey_top, top_border_width, top_border_height,
5713 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5714 ex, ey, exsize, eysize, ex, ey);
5718 // if screen background is set to "[NONE]", clear editor toolbox window
5719 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5720 ClearRectangle(drawto, ex, ey, exsize, eysize);
5723 redraw_mask |= REDRAW_ALL;
5727 // ---------- new tool button stuff -------------------------------------------
5732 struct TextPosInfo *pos;
5734 boolean is_touch_button;
5736 } toolbutton_info[NUM_TOOL_BUTTONS] =
5739 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5740 TOOL_CTRL_ID_YES, FALSE, "yes"
5743 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5744 TOOL_CTRL_ID_NO, FALSE, "no"
5747 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5748 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5751 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5752 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5755 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5756 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5759 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5760 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5763 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5764 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5767 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5768 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5771 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5772 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5775 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5776 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5780 void CreateToolButtons(void)
5784 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5786 int graphic = toolbutton_info[i].graphic;
5787 struct GraphicInfo *gfx = &graphic_info[graphic];
5788 struct TextPosInfo *pos = toolbutton_info[i].pos;
5789 struct GadgetInfo *gi;
5790 Bitmap *deco_bitmap = None;
5791 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5792 unsigned int event_mask = GD_EVENT_RELEASED;
5793 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5794 int base_x = (is_touch_button ? 0 : DX);
5795 int base_y = (is_touch_button ? 0 : DY);
5796 int gd_x = gfx->src_x;
5797 int gd_y = gfx->src_y;
5798 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5799 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5804 if (global.use_envelope_request && !is_touch_button)
5806 setRequestPosition(&base_x, &base_y, TRUE);
5808 // check if request buttons are outside of envelope and fix, if needed
5809 if (x < 0 || x + gfx->width > request.width ||
5810 y < 0 || y + gfx->height > request.height)
5812 if (id == TOOL_CTRL_ID_YES)
5815 y = request.height - 2 * request.border_size - gfx->height;
5817 else if (id == TOOL_CTRL_ID_NO)
5819 x = request.width - 2 * request.border_size - gfx->width;
5820 y = request.height - 2 * request.border_size - gfx->height;
5822 else if (id == TOOL_CTRL_ID_CONFIRM)
5824 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5825 y = request.height - 2 * request.border_size - gfx->height;
5827 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5829 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5831 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5832 y = request.height - 2 * request.border_size - gfx->height * 2;
5834 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5835 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5840 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5842 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5844 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5845 pos->size, &deco_bitmap, &deco_x, &deco_y);
5846 deco_xpos = (gfx->width - pos->size) / 2;
5847 deco_ypos = (gfx->height - pos->size) / 2;
5850 gi = CreateGadget(GDI_CUSTOM_ID, id,
5851 GDI_IMAGE_ID, graphic,
5852 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5855 GDI_WIDTH, gfx->width,
5856 GDI_HEIGHT, gfx->height,
5857 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5858 GDI_STATE, GD_BUTTON_UNPRESSED,
5859 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5860 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5861 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5862 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5863 GDI_DECORATION_SIZE, pos->size, pos->size,
5864 GDI_DECORATION_SHIFTING, 1, 1,
5865 GDI_DIRECT_DRAW, FALSE,
5866 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5867 GDI_EVENT_MASK, event_mask,
5868 GDI_CALLBACK_ACTION, HandleToolButtons,
5872 Fail("cannot create gadget");
5874 tool_gadget[id] = gi;
5878 void FreeToolButtons(void)
5882 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5883 FreeGadget(tool_gadget[i]);
5886 static void UnmapToolButtons(void)
5890 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5891 UnmapGadget(tool_gadget[i]);
5894 static void HandleToolButtons(struct GadgetInfo *gi)
5896 request_gadget_id = gi->custom_id;
5899 static struct Mapping_EM_to_RND_object
5902 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5903 boolean is_backside; // backside of moving element
5909 em_object_mapping_list[GAME_TILE_MAX + 1] =
5912 Zborder, FALSE, FALSE,
5916 Zplayer, FALSE, FALSE,
5925 Ztank, FALSE, FALSE,
5929 Zeater, FALSE, FALSE,
5933 Zdynamite, FALSE, FALSE,
5937 Zboom, FALSE, FALSE,
5942 Xchain, FALSE, FALSE,
5943 EL_DEFAULT, ACTION_EXPLODING, -1
5946 Xboom_bug, FALSE, FALSE,
5947 EL_BUG, ACTION_EXPLODING, -1
5950 Xboom_tank, FALSE, FALSE,
5951 EL_SPACESHIP, ACTION_EXPLODING, -1
5954 Xboom_android, FALSE, FALSE,
5955 EL_EMC_ANDROID, ACTION_OTHER, -1
5958 Xboom_1, FALSE, FALSE,
5959 EL_DEFAULT, ACTION_EXPLODING, -1
5962 Xboom_2, FALSE, FALSE,
5963 EL_DEFAULT, ACTION_EXPLODING, -1
5967 Xblank, TRUE, FALSE,
5972 Xsplash_e, FALSE, FALSE,
5973 EL_ACID_SPLASH_RIGHT, -1, -1
5976 Xsplash_w, FALSE, FALSE,
5977 EL_ACID_SPLASH_LEFT, -1, -1
5981 Xplant, TRUE, FALSE,
5982 EL_EMC_PLANT, -1, -1
5985 Yplant, FALSE, FALSE,
5986 EL_EMC_PLANT, -1, -1
5990 Xacid_1, TRUE, FALSE,
5994 Xacid_2, FALSE, FALSE,
5998 Xacid_3, FALSE, FALSE,
6002 Xacid_4, FALSE, FALSE,
6006 Xacid_5, FALSE, FALSE,
6010 Xacid_6, FALSE, FALSE,
6014 Xacid_7, FALSE, FALSE,
6018 Xacid_8, FALSE, FALSE,
6023 Xfake_acid_1, TRUE, FALSE,
6024 EL_EMC_FAKE_ACID, -1, -1
6027 Xfake_acid_2, FALSE, FALSE,
6028 EL_EMC_FAKE_ACID, -1, -1
6031 Xfake_acid_3, FALSE, FALSE,
6032 EL_EMC_FAKE_ACID, -1, -1
6035 Xfake_acid_4, FALSE, FALSE,
6036 EL_EMC_FAKE_ACID, -1, -1
6039 Xfake_acid_5, FALSE, FALSE,
6040 EL_EMC_FAKE_ACID, -1, -1
6043 Xfake_acid_6, FALSE, FALSE,
6044 EL_EMC_FAKE_ACID, -1, -1
6047 Xfake_acid_7, FALSE, FALSE,
6048 EL_EMC_FAKE_ACID, -1, -1
6051 Xfake_acid_8, FALSE, FALSE,
6052 EL_EMC_FAKE_ACID, -1, -1
6056 Xfake_acid_1_player, FALSE, FALSE,
6057 EL_EMC_FAKE_ACID, -1, -1
6060 Xfake_acid_2_player, FALSE, FALSE,
6061 EL_EMC_FAKE_ACID, -1, -1
6064 Xfake_acid_3_player, FALSE, FALSE,
6065 EL_EMC_FAKE_ACID, -1, -1
6068 Xfake_acid_4_player, FALSE, FALSE,
6069 EL_EMC_FAKE_ACID, -1, -1
6072 Xfake_acid_5_player, FALSE, FALSE,
6073 EL_EMC_FAKE_ACID, -1, -1
6076 Xfake_acid_6_player, FALSE, FALSE,
6077 EL_EMC_FAKE_ACID, -1, -1
6080 Xfake_acid_7_player, FALSE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_8_player, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6089 Xgrass, TRUE, FALSE,
6090 EL_EMC_GRASS, -1, -1
6093 Ygrass_nB, FALSE, FALSE,
6094 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6097 Ygrass_eB, FALSE, FALSE,
6098 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6101 Ygrass_sB, FALSE, FALSE,
6102 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6105 Ygrass_wB, FALSE, FALSE,
6106 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6114 Ydirt_nB, FALSE, FALSE,
6115 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6118 Ydirt_eB, FALSE, FALSE,
6119 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6122 Ydirt_sB, FALSE, FALSE,
6123 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6126 Ydirt_wB, FALSE, FALSE,
6127 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6131 Xandroid, TRUE, FALSE,
6132 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6135 Xandroid_1_n, FALSE, FALSE,
6136 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6139 Xandroid_2_n, FALSE, FALSE,
6140 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6143 Xandroid_1_e, FALSE, FALSE,
6144 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6147 Xandroid_2_e, FALSE, FALSE,
6148 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6151 Xandroid_1_w, FALSE, FALSE,
6152 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6155 Xandroid_2_w, FALSE, FALSE,
6156 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6159 Xandroid_1_s, FALSE, FALSE,
6160 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6163 Xandroid_2_s, FALSE, FALSE,
6164 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6167 Yandroid_n, FALSE, FALSE,
6168 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6171 Yandroid_nB, FALSE, TRUE,
6172 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6175 Yandroid_ne, FALSE, FALSE,
6176 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6179 Yandroid_neB, FALSE, TRUE,
6180 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6183 Yandroid_e, FALSE, FALSE,
6184 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6187 Yandroid_eB, FALSE, TRUE,
6188 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6191 Yandroid_se, FALSE, FALSE,
6192 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6195 Yandroid_seB, FALSE, TRUE,
6196 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6199 Yandroid_s, FALSE, FALSE,
6200 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6203 Yandroid_sB, FALSE, TRUE,
6204 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6207 Yandroid_sw, FALSE, FALSE,
6208 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6211 Yandroid_swB, FALSE, TRUE,
6212 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6215 Yandroid_w, FALSE, FALSE,
6216 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6219 Yandroid_wB, FALSE, TRUE,
6220 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6223 Yandroid_nw, FALSE, FALSE,
6224 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6227 Yandroid_nwB, FALSE, TRUE,
6228 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6232 Xeater_n, TRUE, FALSE,
6233 EL_YAMYAM_UP, -1, -1
6236 Xeater_e, TRUE, FALSE,
6237 EL_YAMYAM_RIGHT, -1, -1
6240 Xeater_w, TRUE, FALSE,
6241 EL_YAMYAM_LEFT, -1, -1
6244 Xeater_s, TRUE, FALSE,
6245 EL_YAMYAM_DOWN, -1, -1
6248 Yeater_n, FALSE, FALSE,
6249 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6252 Yeater_nB, FALSE, TRUE,
6253 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6256 Yeater_e, FALSE, FALSE,
6257 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6260 Yeater_eB, FALSE, TRUE,
6261 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6264 Yeater_s, FALSE, FALSE,
6265 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6268 Yeater_sB, FALSE, TRUE,
6269 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6272 Yeater_w, FALSE, FALSE,
6273 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6276 Yeater_wB, FALSE, TRUE,
6277 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6280 Yeater_stone, FALSE, FALSE,
6281 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6284 Yeater_spring, FALSE, FALSE,
6285 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6289 Xalien, TRUE, FALSE,
6293 Xalien_pause, FALSE, FALSE,
6297 Yalien_n, FALSE, FALSE,
6298 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6301 Yalien_nB, FALSE, TRUE,
6302 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6305 Yalien_e, FALSE, FALSE,
6306 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6309 Yalien_eB, FALSE, TRUE,
6310 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6313 Yalien_s, FALSE, FALSE,
6314 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6317 Yalien_sB, FALSE, TRUE,
6318 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6321 Yalien_w, FALSE, FALSE,
6322 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6325 Yalien_wB, FALSE, TRUE,
6326 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6329 Yalien_stone, FALSE, FALSE,
6330 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6333 Yalien_spring, FALSE, FALSE,
6334 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6338 Xbug_1_n, TRUE, FALSE,
6342 Xbug_1_e, TRUE, FALSE,
6343 EL_BUG_RIGHT, -1, -1
6346 Xbug_1_s, TRUE, FALSE,
6350 Xbug_1_w, TRUE, FALSE,
6354 Xbug_2_n, FALSE, FALSE,
6358 Xbug_2_e, FALSE, FALSE,
6359 EL_BUG_RIGHT, -1, -1
6362 Xbug_2_s, FALSE, FALSE,
6366 Xbug_2_w, FALSE, FALSE,
6370 Ybug_n, FALSE, FALSE,
6371 EL_BUG, ACTION_MOVING, MV_BIT_UP
6374 Ybug_nB, FALSE, TRUE,
6375 EL_BUG, ACTION_MOVING, MV_BIT_UP
6378 Ybug_e, FALSE, FALSE,
6379 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6382 Ybug_eB, FALSE, TRUE,
6383 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6386 Ybug_s, FALSE, FALSE,
6387 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6390 Ybug_sB, FALSE, TRUE,
6391 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6394 Ybug_w, FALSE, FALSE,
6395 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6398 Ybug_wB, FALSE, TRUE,
6399 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6402 Ybug_w_n, FALSE, FALSE,
6403 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6406 Ybug_n_e, FALSE, FALSE,
6407 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6410 Ybug_e_s, FALSE, FALSE,
6411 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6414 Ybug_s_w, FALSE, FALSE,
6415 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6418 Ybug_e_n, FALSE, FALSE,
6419 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6422 Ybug_s_e, FALSE, FALSE,
6423 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6426 Ybug_w_s, FALSE, FALSE,
6427 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6430 Ybug_n_w, FALSE, FALSE,
6431 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6434 Ybug_stone, FALSE, FALSE,
6435 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6438 Ybug_spring, FALSE, FALSE,
6439 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6443 Xtank_1_n, TRUE, FALSE,
6444 EL_SPACESHIP_UP, -1, -1
6447 Xtank_1_e, TRUE, FALSE,
6448 EL_SPACESHIP_RIGHT, -1, -1
6451 Xtank_1_s, TRUE, FALSE,
6452 EL_SPACESHIP_DOWN, -1, -1
6455 Xtank_1_w, TRUE, FALSE,
6456 EL_SPACESHIP_LEFT, -1, -1
6459 Xtank_2_n, FALSE, FALSE,
6460 EL_SPACESHIP_UP, -1, -1
6463 Xtank_2_e, FALSE, FALSE,
6464 EL_SPACESHIP_RIGHT, -1, -1
6467 Xtank_2_s, FALSE, FALSE,
6468 EL_SPACESHIP_DOWN, -1, -1
6471 Xtank_2_w, FALSE, FALSE,
6472 EL_SPACESHIP_LEFT, -1, -1
6475 Ytank_n, FALSE, FALSE,
6476 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6479 Ytank_nB, FALSE, TRUE,
6480 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6483 Ytank_e, FALSE, FALSE,
6484 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6487 Ytank_eB, FALSE, TRUE,
6488 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6491 Ytank_s, FALSE, FALSE,
6492 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6495 Ytank_sB, FALSE, TRUE,
6496 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6499 Ytank_w, FALSE, FALSE,
6500 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6503 Ytank_wB, FALSE, TRUE,
6504 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6507 Ytank_w_n, FALSE, FALSE,
6508 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6511 Ytank_n_e, FALSE, FALSE,
6512 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6515 Ytank_e_s, FALSE, FALSE,
6516 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6519 Ytank_s_w, FALSE, FALSE,
6520 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6523 Ytank_e_n, FALSE, FALSE,
6524 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6527 Ytank_s_e, FALSE, FALSE,
6528 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6531 Ytank_w_s, FALSE, FALSE,
6532 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6535 Ytank_n_w, FALSE, FALSE,
6536 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6539 Ytank_stone, FALSE, FALSE,
6540 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6543 Ytank_spring, FALSE, FALSE,
6544 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6548 Xemerald, TRUE, FALSE,
6552 Xemerald_pause, FALSE, FALSE,
6556 Xemerald_fall, FALSE, FALSE,
6560 Xemerald_shine, FALSE, FALSE,
6561 EL_EMERALD, ACTION_TWINKLING, -1
6564 Yemerald_s, FALSE, FALSE,
6565 EL_EMERALD, ACTION_FALLING, -1
6568 Yemerald_sB, FALSE, TRUE,
6569 EL_EMERALD, ACTION_FALLING, -1
6572 Yemerald_e, FALSE, FALSE,
6573 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6576 Yemerald_eB, FALSE, TRUE,
6577 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6580 Yemerald_w, FALSE, FALSE,
6581 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6584 Yemerald_wB, FALSE, TRUE,
6585 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6588 Yemerald_blank, FALSE, FALSE,
6589 EL_EMERALD, ACTION_COLLECTING, -1
6593 Xdiamond, TRUE, FALSE,
6597 Xdiamond_pause, FALSE, FALSE,
6601 Xdiamond_fall, FALSE, FALSE,
6605 Xdiamond_shine, FALSE, FALSE,
6606 EL_DIAMOND, ACTION_TWINKLING, -1
6609 Ydiamond_s, FALSE, FALSE,
6610 EL_DIAMOND, ACTION_FALLING, -1
6613 Ydiamond_sB, FALSE, TRUE,
6614 EL_DIAMOND, ACTION_FALLING, -1
6617 Ydiamond_e, FALSE, FALSE,
6618 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6621 Ydiamond_eB, FALSE, TRUE,
6622 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6625 Ydiamond_w, FALSE, FALSE,
6626 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6629 Ydiamond_wB, FALSE, TRUE,
6630 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6633 Ydiamond_blank, FALSE, FALSE,
6634 EL_DIAMOND, ACTION_COLLECTING, -1
6637 Ydiamond_stone, FALSE, FALSE,
6638 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6642 Xstone, TRUE, FALSE,
6646 Xstone_pause, FALSE, FALSE,
6650 Xstone_fall, FALSE, FALSE,
6654 Ystone_s, FALSE, FALSE,
6655 EL_ROCK, ACTION_FALLING, -1
6658 Ystone_sB, FALSE, TRUE,
6659 EL_ROCK, ACTION_FALLING, -1
6662 Ystone_e, FALSE, FALSE,
6663 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6666 Ystone_eB, FALSE, TRUE,
6667 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6670 Ystone_w, FALSE, FALSE,
6671 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6674 Ystone_wB, FALSE, TRUE,
6675 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6683 Xbomb_pause, FALSE, FALSE,
6687 Xbomb_fall, FALSE, FALSE,
6691 Ybomb_s, FALSE, FALSE,
6692 EL_BOMB, ACTION_FALLING, -1
6695 Ybomb_sB, FALSE, TRUE,
6696 EL_BOMB, ACTION_FALLING, -1
6699 Ybomb_e, FALSE, FALSE,
6700 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6703 Ybomb_eB, FALSE, TRUE,
6704 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6707 Ybomb_w, FALSE, FALSE,
6708 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6711 Ybomb_wB, FALSE, TRUE,
6712 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6715 Ybomb_blank, FALSE, FALSE,
6716 EL_BOMB, ACTION_ACTIVATING, -1
6724 Xnut_pause, FALSE, FALSE,
6728 Xnut_fall, FALSE, FALSE,
6732 Ynut_s, FALSE, FALSE,
6733 EL_NUT, ACTION_FALLING, -1
6736 Ynut_sB, FALSE, TRUE,
6737 EL_NUT, ACTION_FALLING, -1
6740 Ynut_e, FALSE, FALSE,
6741 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6744 Ynut_eB, FALSE, TRUE,
6745 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6748 Ynut_w, FALSE, FALSE,
6749 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6752 Ynut_wB, FALSE, TRUE,
6753 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6756 Ynut_stone, FALSE, FALSE,
6757 EL_NUT, ACTION_BREAKING, -1
6761 Xspring, TRUE, FALSE,
6765 Xspring_pause, FALSE, FALSE,
6769 Xspring_e, TRUE, FALSE,
6770 EL_SPRING_RIGHT, -1, -1
6773 Xspring_w, TRUE, FALSE,
6774 EL_SPRING_LEFT, -1, -1
6777 Xspring_fall, FALSE, FALSE,
6781 Yspring_s, FALSE, FALSE,
6782 EL_SPRING, ACTION_FALLING, -1
6785 Yspring_sB, FALSE, TRUE,
6786 EL_SPRING, ACTION_FALLING, -1
6789 Yspring_e, FALSE, FALSE,
6790 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6793 Yspring_eB, FALSE, TRUE,
6794 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6797 Yspring_w, FALSE, FALSE,
6798 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6801 Yspring_wB, FALSE, TRUE,
6802 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6805 Yspring_alien_e, FALSE, FALSE,
6806 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6809 Yspring_alien_eB, FALSE, TRUE,
6810 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6813 Yspring_alien_w, FALSE, FALSE,
6814 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6817 Yspring_alien_wB, FALSE, TRUE,
6818 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6822 Xpush_emerald_e, FALSE, FALSE,
6823 EL_EMERALD, -1, MV_BIT_RIGHT
6826 Xpush_emerald_w, FALSE, FALSE,
6827 EL_EMERALD, -1, MV_BIT_LEFT
6830 Xpush_diamond_e, FALSE, FALSE,
6831 EL_DIAMOND, -1, MV_BIT_RIGHT
6834 Xpush_diamond_w, FALSE, FALSE,
6835 EL_DIAMOND, -1, MV_BIT_LEFT
6838 Xpush_stone_e, FALSE, FALSE,
6839 EL_ROCK, -1, MV_BIT_RIGHT
6842 Xpush_stone_w, FALSE, FALSE,
6843 EL_ROCK, -1, MV_BIT_LEFT
6846 Xpush_bomb_e, FALSE, FALSE,
6847 EL_BOMB, -1, MV_BIT_RIGHT
6850 Xpush_bomb_w, FALSE, FALSE,
6851 EL_BOMB, -1, MV_BIT_LEFT
6854 Xpush_nut_e, FALSE, FALSE,
6855 EL_NUT, -1, MV_BIT_RIGHT
6858 Xpush_nut_w, FALSE, FALSE,
6859 EL_NUT, -1, MV_BIT_LEFT
6862 Xpush_spring_e, FALSE, FALSE,
6863 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6866 Xpush_spring_w, FALSE, FALSE,
6867 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6871 Xdynamite, TRUE, FALSE,
6872 EL_EM_DYNAMITE, -1, -1
6875 Ydynamite_blank, FALSE, FALSE,
6876 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6879 Xdynamite_1, TRUE, FALSE,
6880 EL_EM_DYNAMITE_ACTIVE, -1, -1
6883 Xdynamite_2, FALSE, FALSE,
6884 EL_EM_DYNAMITE_ACTIVE, -1, -1
6887 Xdynamite_3, FALSE, FALSE,
6888 EL_EM_DYNAMITE_ACTIVE, -1, -1
6891 Xdynamite_4, FALSE, FALSE,
6892 EL_EM_DYNAMITE_ACTIVE, -1, -1
6896 Xkey_1, TRUE, FALSE,
6900 Xkey_2, TRUE, FALSE,
6904 Xkey_3, TRUE, FALSE,
6908 Xkey_4, TRUE, FALSE,
6912 Xkey_5, TRUE, FALSE,
6913 EL_EMC_KEY_5, -1, -1
6916 Xkey_6, TRUE, FALSE,
6917 EL_EMC_KEY_6, -1, -1
6920 Xkey_7, TRUE, FALSE,
6921 EL_EMC_KEY_7, -1, -1
6924 Xkey_8, TRUE, FALSE,
6925 EL_EMC_KEY_8, -1, -1
6929 Xdoor_1, TRUE, FALSE,
6930 EL_EM_GATE_1, -1, -1
6933 Xdoor_2, TRUE, FALSE,
6934 EL_EM_GATE_2, -1, -1
6937 Xdoor_3, TRUE, FALSE,
6938 EL_EM_GATE_3, -1, -1
6941 Xdoor_4, TRUE, FALSE,
6942 EL_EM_GATE_4, -1, -1
6945 Xdoor_5, TRUE, FALSE,
6946 EL_EMC_GATE_5, -1, -1
6949 Xdoor_6, TRUE, FALSE,
6950 EL_EMC_GATE_6, -1, -1
6953 Xdoor_7, TRUE, FALSE,
6954 EL_EMC_GATE_7, -1, -1
6957 Xdoor_8, TRUE, FALSE,
6958 EL_EMC_GATE_8, -1, -1
6962 Xfake_door_1, TRUE, FALSE,
6963 EL_EM_GATE_1_GRAY, -1, -1
6966 Xfake_door_2, TRUE, FALSE,
6967 EL_EM_GATE_2_GRAY, -1, -1
6970 Xfake_door_3, TRUE, FALSE,
6971 EL_EM_GATE_3_GRAY, -1, -1
6974 Xfake_door_4, TRUE, FALSE,
6975 EL_EM_GATE_4_GRAY, -1, -1
6978 Xfake_door_5, TRUE, FALSE,
6979 EL_EMC_GATE_5_GRAY, -1, -1
6982 Xfake_door_6, TRUE, FALSE,
6983 EL_EMC_GATE_6_GRAY, -1, -1
6986 Xfake_door_7, TRUE, FALSE,
6987 EL_EMC_GATE_7_GRAY, -1, -1
6990 Xfake_door_8, TRUE, FALSE,
6991 EL_EMC_GATE_8_GRAY, -1, -1
6995 Xballoon, TRUE, FALSE,
6999 Yballoon_n, FALSE, FALSE,
7000 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7003 Yballoon_nB, FALSE, TRUE,
7004 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7007 Yballoon_e, FALSE, FALSE,
7008 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7011 Yballoon_eB, FALSE, TRUE,
7012 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7015 Yballoon_s, FALSE, FALSE,
7016 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7019 Yballoon_sB, FALSE, TRUE,
7020 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7023 Yballoon_w, FALSE, FALSE,
7024 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7027 Yballoon_wB, FALSE, TRUE,
7028 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7032 Xball_1, TRUE, FALSE,
7033 EL_EMC_MAGIC_BALL, -1, -1
7036 Yball_1, FALSE, FALSE,
7037 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7040 Xball_2, FALSE, FALSE,
7041 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7044 Yball_2, FALSE, FALSE,
7045 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7048 Yball_blank, FALSE, FALSE,
7049 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7053 Xamoeba_1, TRUE, FALSE,
7054 EL_AMOEBA_DRY, ACTION_OTHER, -1
7057 Xamoeba_2, FALSE, FALSE,
7058 EL_AMOEBA_DRY, ACTION_OTHER, -1
7061 Xamoeba_3, FALSE, FALSE,
7062 EL_AMOEBA_DRY, ACTION_OTHER, -1
7065 Xamoeba_4, FALSE, FALSE,
7066 EL_AMOEBA_DRY, ACTION_OTHER, -1
7069 Xamoeba_5, TRUE, FALSE,
7070 EL_AMOEBA_WET, ACTION_OTHER, -1
7073 Xamoeba_6, FALSE, FALSE,
7074 EL_AMOEBA_WET, ACTION_OTHER, -1
7077 Xamoeba_7, FALSE, FALSE,
7078 EL_AMOEBA_WET, ACTION_OTHER, -1
7081 Xamoeba_8, FALSE, FALSE,
7082 EL_AMOEBA_WET, ACTION_OTHER, -1
7087 EL_AMOEBA_DROP, ACTION_GROWING, -1
7090 Xdrip_fall, FALSE, FALSE,
7091 EL_AMOEBA_DROP, -1, -1
7094 Xdrip_stretch, FALSE, FALSE,
7095 EL_AMOEBA_DROP, ACTION_FALLING, -1
7098 Xdrip_stretchB, FALSE, TRUE,
7099 EL_AMOEBA_DROP, ACTION_FALLING, -1
7102 Ydrip_1_s, FALSE, FALSE,
7103 EL_AMOEBA_DROP, ACTION_FALLING, -1
7106 Ydrip_1_sB, FALSE, TRUE,
7107 EL_AMOEBA_DROP, ACTION_FALLING, -1
7110 Ydrip_2_s, FALSE, FALSE,
7111 EL_AMOEBA_DROP, ACTION_FALLING, -1
7114 Ydrip_2_sB, FALSE, TRUE,
7115 EL_AMOEBA_DROP, ACTION_FALLING, -1
7119 Xwonderwall, TRUE, FALSE,
7120 EL_MAGIC_WALL, -1, -1
7123 Ywonderwall, FALSE, FALSE,
7124 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7128 Xwheel, TRUE, FALSE,
7129 EL_ROBOT_WHEEL, -1, -1
7132 Ywheel, FALSE, FALSE,
7133 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7137 Xswitch, TRUE, FALSE,
7138 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7141 Yswitch, FALSE, FALSE,
7142 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7146 Xbumper, TRUE, FALSE,
7147 EL_EMC_SPRING_BUMPER, -1, -1
7150 Ybumper, FALSE, FALSE,
7151 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7155 Xacid_nw, TRUE, FALSE,
7156 EL_ACID_POOL_TOPLEFT, -1, -1
7159 Xacid_ne, TRUE, FALSE,
7160 EL_ACID_POOL_TOPRIGHT, -1, -1
7163 Xacid_sw, TRUE, FALSE,
7164 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7167 Xacid_s, TRUE, FALSE,
7168 EL_ACID_POOL_BOTTOM, -1, -1
7171 Xacid_se, TRUE, FALSE,
7172 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7176 Xfake_blank, TRUE, FALSE,
7177 EL_INVISIBLE_WALL, -1, -1
7180 Yfake_blank, FALSE, FALSE,
7181 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7185 Xfake_grass, TRUE, FALSE,
7186 EL_EMC_FAKE_GRASS, -1, -1
7189 Yfake_grass, FALSE, FALSE,
7190 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7194 Xfake_amoeba, TRUE, FALSE,
7195 EL_EMC_DRIPPER, -1, -1
7198 Yfake_amoeba, FALSE, FALSE,
7199 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7203 Xlenses, TRUE, FALSE,
7204 EL_EMC_LENSES, -1, -1
7208 Xmagnify, TRUE, FALSE,
7209 EL_EMC_MAGNIFIER, -1, -1
7214 EL_QUICKSAND_EMPTY, -1, -1
7217 Xsand_stone, TRUE, FALSE,
7218 EL_QUICKSAND_FULL, -1, -1
7221 Xsand_stonein_1, FALSE, TRUE,
7222 EL_ROCK, ACTION_FILLING, -1
7225 Xsand_stonein_2, FALSE, TRUE,
7226 EL_ROCK, ACTION_FILLING, -1
7229 Xsand_stonein_3, FALSE, TRUE,
7230 EL_ROCK, ACTION_FILLING, -1
7233 Xsand_stonein_4, FALSE, TRUE,
7234 EL_ROCK, ACTION_FILLING, -1
7237 Xsand_sandstone_1, FALSE, FALSE,
7238 EL_QUICKSAND_FILLING, -1, -1
7241 Xsand_sandstone_2, FALSE, FALSE,
7242 EL_QUICKSAND_FILLING, -1, -1
7245 Xsand_sandstone_3, FALSE, FALSE,
7246 EL_QUICKSAND_FILLING, -1, -1
7249 Xsand_sandstone_4, FALSE, FALSE,
7250 EL_QUICKSAND_FILLING, -1, -1
7253 Xsand_stonesand_1, FALSE, FALSE,
7254 EL_QUICKSAND_EMPTYING, -1, -1
7257 Xsand_stonesand_2, FALSE, FALSE,
7258 EL_QUICKSAND_EMPTYING, -1, -1
7261 Xsand_stonesand_3, FALSE, FALSE,
7262 EL_QUICKSAND_EMPTYING, -1, -1
7265 Xsand_stonesand_4, FALSE, FALSE,
7266 EL_QUICKSAND_EMPTYING, -1, -1
7269 Xsand_stoneout_1, FALSE, FALSE,
7270 EL_ROCK, ACTION_EMPTYING, -1
7273 Xsand_stoneout_2, FALSE, FALSE,
7274 EL_ROCK, ACTION_EMPTYING, -1
7277 Xsand_stonesand_quickout_1, FALSE, FALSE,
7278 EL_QUICKSAND_EMPTYING, -1, -1
7281 Xsand_stonesand_quickout_2, FALSE, FALSE,
7282 EL_QUICKSAND_EMPTYING, -1, -1
7286 Xslide_ns, TRUE, FALSE,
7287 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7290 Yslide_ns_blank, FALSE, FALSE,
7291 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7294 Xslide_ew, TRUE, FALSE,
7295 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7298 Yslide_ew_blank, FALSE, FALSE,
7299 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7303 Xwind_n, TRUE, FALSE,
7304 EL_BALLOON_SWITCH_UP, -1, -1
7307 Xwind_e, TRUE, FALSE,
7308 EL_BALLOON_SWITCH_RIGHT, -1, -1
7311 Xwind_s, TRUE, FALSE,
7312 EL_BALLOON_SWITCH_DOWN, -1, -1
7315 Xwind_w, TRUE, FALSE,
7316 EL_BALLOON_SWITCH_LEFT, -1, -1
7319 Xwind_any, TRUE, FALSE,
7320 EL_BALLOON_SWITCH_ANY, -1, -1
7323 Xwind_stop, TRUE, FALSE,
7324 EL_BALLOON_SWITCH_NONE, -1, -1
7329 EL_EM_EXIT_CLOSED, -1, -1
7332 Xexit_1, TRUE, FALSE,
7333 EL_EM_EXIT_OPEN, -1, -1
7336 Xexit_2, FALSE, FALSE,
7337 EL_EM_EXIT_OPEN, -1, -1
7340 Xexit_3, FALSE, FALSE,
7341 EL_EM_EXIT_OPEN, -1, -1
7345 Xpause, FALSE, FALSE,
7350 Xwall_1, TRUE, FALSE,
7354 Xwall_2, TRUE, FALSE,
7355 EL_EMC_WALL_14, -1, -1
7358 Xwall_3, TRUE, FALSE,
7359 EL_EMC_WALL_15, -1, -1
7362 Xwall_4, TRUE, FALSE,
7363 EL_EMC_WALL_16, -1, -1
7367 Xroundwall_1, TRUE, FALSE,
7368 EL_WALL_SLIPPERY, -1, -1
7371 Xroundwall_2, TRUE, FALSE,
7372 EL_EMC_WALL_SLIPPERY_2, -1, -1
7375 Xroundwall_3, TRUE, FALSE,
7376 EL_EMC_WALL_SLIPPERY_3, -1, -1
7379 Xroundwall_4, TRUE, FALSE,
7380 EL_EMC_WALL_SLIPPERY_4, -1, -1
7384 Xsteel_1, TRUE, FALSE,
7385 EL_STEELWALL, -1, -1
7388 Xsteel_2, TRUE, FALSE,
7389 EL_EMC_STEELWALL_2, -1, -1
7392 Xsteel_3, TRUE, FALSE,
7393 EL_EMC_STEELWALL_3, -1, -1
7396 Xsteel_4, TRUE, FALSE,
7397 EL_EMC_STEELWALL_4, -1, -1
7401 Xdecor_1, TRUE, FALSE,
7402 EL_EMC_WALL_8, -1, -1
7405 Xdecor_2, TRUE, FALSE,
7406 EL_EMC_WALL_6, -1, -1
7409 Xdecor_3, TRUE, FALSE,
7410 EL_EMC_WALL_4, -1, -1
7413 Xdecor_4, TRUE, FALSE,
7414 EL_EMC_WALL_7, -1, -1
7417 Xdecor_5, TRUE, FALSE,
7418 EL_EMC_WALL_5, -1, -1
7421 Xdecor_6, TRUE, FALSE,
7422 EL_EMC_WALL_9, -1, -1
7425 Xdecor_7, TRUE, FALSE,
7426 EL_EMC_WALL_10, -1, -1
7429 Xdecor_8, TRUE, FALSE,
7430 EL_EMC_WALL_1, -1, -1
7433 Xdecor_9, TRUE, FALSE,
7434 EL_EMC_WALL_2, -1, -1
7437 Xdecor_10, TRUE, FALSE,
7438 EL_EMC_WALL_3, -1, -1
7441 Xdecor_11, TRUE, FALSE,
7442 EL_EMC_WALL_11, -1, -1
7445 Xdecor_12, TRUE, FALSE,
7446 EL_EMC_WALL_12, -1, -1
7450 Xalpha_0, TRUE, FALSE,
7451 EL_CHAR('0'), -1, -1
7454 Xalpha_1, TRUE, FALSE,
7455 EL_CHAR('1'), -1, -1
7458 Xalpha_2, TRUE, FALSE,
7459 EL_CHAR('2'), -1, -1
7462 Xalpha_3, TRUE, FALSE,
7463 EL_CHAR('3'), -1, -1
7466 Xalpha_4, TRUE, FALSE,
7467 EL_CHAR('4'), -1, -1
7470 Xalpha_5, TRUE, FALSE,
7471 EL_CHAR('5'), -1, -1
7474 Xalpha_6, TRUE, FALSE,
7475 EL_CHAR('6'), -1, -1
7478 Xalpha_7, TRUE, FALSE,
7479 EL_CHAR('7'), -1, -1
7482 Xalpha_8, TRUE, FALSE,
7483 EL_CHAR('8'), -1, -1
7486 Xalpha_9, TRUE, FALSE,
7487 EL_CHAR('9'), -1, -1
7490 Xalpha_excla, TRUE, FALSE,
7491 EL_CHAR('!'), -1, -1
7494 Xalpha_apost, TRUE, FALSE,
7495 EL_CHAR('\''), -1, -1
7498 Xalpha_comma, TRUE, FALSE,
7499 EL_CHAR(','), -1, -1
7502 Xalpha_minus, TRUE, FALSE,
7503 EL_CHAR('-'), -1, -1
7506 Xalpha_perio, TRUE, FALSE,
7507 EL_CHAR('.'), -1, -1
7510 Xalpha_colon, TRUE, FALSE,
7511 EL_CHAR(':'), -1, -1
7514 Xalpha_quest, TRUE, FALSE,
7515 EL_CHAR('?'), -1, -1
7518 Xalpha_a, TRUE, FALSE,
7519 EL_CHAR('A'), -1, -1
7522 Xalpha_b, TRUE, FALSE,
7523 EL_CHAR('B'), -1, -1
7526 Xalpha_c, TRUE, FALSE,
7527 EL_CHAR('C'), -1, -1
7530 Xalpha_d, TRUE, FALSE,
7531 EL_CHAR('D'), -1, -1
7534 Xalpha_e, TRUE, FALSE,
7535 EL_CHAR('E'), -1, -1
7538 Xalpha_f, TRUE, FALSE,
7539 EL_CHAR('F'), -1, -1
7542 Xalpha_g, TRUE, FALSE,
7543 EL_CHAR('G'), -1, -1
7546 Xalpha_h, TRUE, FALSE,
7547 EL_CHAR('H'), -1, -1
7550 Xalpha_i, TRUE, FALSE,
7551 EL_CHAR('I'), -1, -1
7554 Xalpha_j, TRUE, FALSE,
7555 EL_CHAR('J'), -1, -1
7558 Xalpha_k, TRUE, FALSE,
7559 EL_CHAR('K'), -1, -1
7562 Xalpha_l, TRUE, FALSE,
7563 EL_CHAR('L'), -1, -1
7566 Xalpha_m, TRUE, FALSE,
7567 EL_CHAR('M'), -1, -1
7570 Xalpha_n, TRUE, FALSE,
7571 EL_CHAR('N'), -1, -1
7574 Xalpha_o, TRUE, FALSE,
7575 EL_CHAR('O'), -1, -1
7578 Xalpha_p, TRUE, FALSE,
7579 EL_CHAR('P'), -1, -1
7582 Xalpha_q, TRUE, FALSE,
7583 EL_CHAR('Q'), -1, -1
7586 Xalpha_r, TRUE, FALSE,
7587 EL_CHAR('R'), -1, -1
7590 Xalpha_s, TRUE, FALSE,
7591 EL_CHAR('S'), -1, -1
7594 Xalpha_t, TRUE, FALSE,
7595 EL_CHAR('T'), -1, -1
7598 Xalpha_u, TRUE, FALSE,
7599 EL_CHAR('U'), -1, -1
7602 Xalpha_v, TRUE, FALSE,
7603 EL_CHAR('V'), -1, -1
7606 Xalpha_w, TRUE, FALSE,
7607 EL_CHAR('W'), -1, -1
7610 Xalpha_x, TRUE, FALSE,
7611 EL_CHAR('X'), -1, -1
7614 Xalpha_y, TRUE, FALSE,
7615 EL_CHAR('Y'), -1, -1
7618 Xalpha_z, TRUE, FALSE,
7619 EL_CHAR('Z'), -1, -1
7622 Xalpha_arrow_e, TRUE, FALSE,
7623 EL_CHAR('>'), -1, -1
7626 Xalpha_arrow_w, TRUE, FALSE,
7627 EL_CHAR('<'), -1, -1
7630 Xalpha_copyr, TRUE, FALSE,
7631 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7635 Ykey_1_blank, FALSE, FALSE,
7636 EL_EM_KEY_1, ACTION_COLLECTING, -1
7639 Ykey_2_blank, FALSE, FALSE,
7640 EL_EM_KEY_2, ACTION_COLLECTING, -1
7643 Ykey_3_blank, FALSE, FALSE,
7644 EL_EM_KEY_3, ACTION_COLLECTING, -1
7647 Ykey_4_blank, FALSE, FALSE,
7648 EL_EM_KEY_4, ACTION_COLLECTING, -1
7651 Ykey_5_blank, FALSE, FALSE,
7652 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7655 Ykey_6_blank, FALSE, FALSE,
7656 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7659 Ykey_7_blank, FALSE, FALSE,
7660 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7663 Ykey_8_blank, FALSE, FALSE,
7664 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7667 Ylenses_blank, FALSE, FALSE,
7668 EL_EMC_LENSES, ACTION_COLLECTING, -1
7671 Ymagnify_blank, FALSE, FALSE,
7672 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7675 Ygrass_blank, FALSE, FALSE,
7676 EL_EMC_GRASS, ACTION_SNAPPING, -1
7679 Ydirt_blank, FALSE, FALSE,
7680 EL_SAND, ACTION_SNAPPING, -1
7689 static struct Mapping_EM_to_RND_player
7698 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7702 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7706 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7710 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7714 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7718 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7722 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7726 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7730 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7734 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7738 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7742 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7746 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7750 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7754 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7758 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7762 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7766 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7770 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7774 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7778 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7782 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7786 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7790 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7794 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7798 EL_PLAYER_1, ACTION_DEFAULT, -1,
7802 EL_PLAYER_2, ACTION_DEFAULT, -1,
7806 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7810 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7814 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7818 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7822 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7826 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7830 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7834 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7838 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7842 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7846 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7850 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7854 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7858 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7862 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7866 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7870 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7874 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7878 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7882 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7886 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7890 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7894 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7898 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7902 EL_PLAYER_3, ACTION_DEFAULT, -1,
7906 EL_PLAYER_4, ACTION_DEFAULT, -1,
7915 int map_element_RND_to_EM_cave(int element_rnd)
7917 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7918 static boolean mapping_initialized = FALSE;
7920 if (!mapping_initialized)
7924 // return "Xalpha_quest" for all undefined elements in mapping array
7925 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7926 mapping_RND_to_EM[i] = Xalpha_quest;
7928 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7929 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7930 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7931 em_object_mapping_list[i].element_em;
7933 mapping_initialized = TRUE;
7936 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7938 Warn("invalid RND level element %d", element_rnd);
7943 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7946 int map_element_EM_to_RND_cave(int element_em_cave)
7948 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7949 static boolean mapping_initialized = FALSE;
7951 if (!mapping_initialized)
7955 // return "EL_UNKNOWN" for all undefined elements in mapping array
7956 for (i = 0; i < GAME_TILE_MAX; i++)
7957 mapping_EM_to_RND[i] = EL_UNKNOWN;
7959 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7960 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7961 em_object_mapping_list[i].element_rnd;
7963 mapping_initialized = TRUE;
7966 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7968 Warn("invalid EM cave element %d", element_em_cave);
7973 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7976 int map_element_EM_to_RND_game(int element_em_game)
7978 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7979 static boolean mapping_initialized = FALSE;
7981 if (!mapping_initialized)
7985 // return "EL_UNKNOWN" for all undefined elements in mapping array
7986 for (i = 0; i < GAME_TILE_MAX; i++)
7987 mapping_EM_to_RND[i] = EL_UNKNOWN;
7989 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7990 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7991 em_object_mapping_list[i].element_rnd;
7993 mapping_initialized = TRUE;
7996 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7998 Warn("invalid EM game element %d", element_em_game);
8003 return mapping_EM_to_RND[element_em_game];
8006 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8008 struct LevelInfo_EM *level_em = level->native_em_level;
8009 struct CAVE *cav = level_em->cav;
8012 for (i = 0; i < GAME_TILE_MAX; i++)
8013 cav->android_array[i] = Cblank;
8015 for (i = 0; i < level->num_android_clone_elements; i++)
8017 int element_rnd = level->android_clone_element[i];
8018 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8020 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8021 if (em_object_mapping_list[j].element_rnd == element_rnd)
8022 cav->android_array[em_object_mapping_list[j].element_em] =
8027 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8029 struct LevelInfo_EM *level_em = level->native_em_level;
8030 struct CAVE *cav = level_em->cav;
8033 level->num_android_clone_elements = 0;
8035 for (i = 0; i < GAME_TILE_MAX; i++)
8037 int element_em_cave = cav->android_array[i];
8039 boolean element_found = FALSE;
8041 if (element_em_cave == Cblank)
8044 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8046 for (j = 0; j < level->num_android_clone_elements; j++)
8047 if (level->android_clone_element[j] == element_rnd)
8048 element_found = TRUE;
8052 level->android_clone_element[level->num_android_clone_elements++] =
8055 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8060 if (level->num_android_clone_elements == 0)
8062 level->num_android_clone_elements = 1;
8063 level->android_clone_element[0] = EL_EMPTY;
8067 int map_direction_RND_to_EM(int direction)
8069 return (direction == MV_UP ? 0 :
8070 direction == MV_RIGHT ? 1 :
8071 direction == MV_DOWN ? 2 :
8072 direction == MV_LEFT ? 3 :
8076 int map_direction_EM_to_RND(int direction)
8078 return (direction == 0 ? MV_UP :
8079 direction == 1 ? MV_RIGHT :
8080 direction == 2 ? MV_DOWN :
8081 direction == 3 ? MV_LEFT :
8085 int map_element_RND_to_SP(int element_rnd)
8087 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8089 if (element_rnd >= EL_SP_START &&
8090 element_rnd <= EL_SP_END)
8091 element_sp = element_rnd - EL_SP_START;
8092 else if (element_rnd == EL_EMPTY_SPACE)
8094 else if (element_rnd == EL_INVISIBLE_WALL)
8100 int map_element_SP_to_RND(int element_sp)
8102 int element_rnd = EL_UNKNOWN;
8104 if (element_sp >= 0x00 &&
8106 element_rnd = EL_SP_START + element_sp;
8107 else if (element_sp == 0x28)
8108 element_rnd = EL_INVISIBLE_WALL;
8113 int map_action_SP_to_RND(int action_sp)
8117 case actActive: return ACTION_ACTIVE;
8118 case actImpact: return ACTION_IMPACT;
8119 case actExploding: return ACTION_EXPLODING;
8120 case actDigging: return ACTION_DIGGING;
8121 case actSnapping: return ACTION_SNAPPING;
8122 case actCollecting: return ACTION_COLLECTING;
8123 case actPassing: return ACTION_PASSING;
8124 case actPushing: return ACTION_PUSHING;
8125 case actDropping: return ACTION_DROPPING;
8127 default: return ACTION_DEFAULT;
8131 int map_element_RND_to_MM(int element_rnd)
8133 return (element_rnd >= EL_MM_START_1 &&
8134 element_rnd <= EL_MM_END_1 ?
8135 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8137 element_rnd >= EL_MM_START_2 &&
8138 element_rnd <= EL_MM_END_2 ?
8139 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8141 element_rnd >= EL_CHAR_START &&
8142 element_rnd <= EL_CHAR_END ?
8143 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8145 element_rnd >= EL_MM_RUNTIME_START &&
8146 element_rnd <= EL_MM_RUNTIME_END ?
8147 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8149 element_rnd >= EL_MM_DUMMY_START &&
8150 element_rnd <= EL_MM_DUMMY_END ?
8151 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8153 EL_MM_EMPTY_NATIVE);
8156 int map_element_MM_to_RND(int element_mm)
8158 return (element_mm == EL_MM_EMPTY_NATIVE ||
8159 element_mm == EL_DF_EMPTY_NATIVE ?
8162 element_mm >= EL_MM_START_1_NATIVE &&
8163 element_mm <= EL_MM_END_1_NATIVE ?
8164 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8166 element_mm >= EL_MM_START_2_NATIVE &&
8167 element_mm <= EL_MM_END_2_NATIVE ?
8168 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8170 element_mm >= EL_MM_CHAR_START_NATIVE &&
8171 element_mm <= EL_MM_CHAR_END_NATIVE ?
8172 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8174 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8175 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8176 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8178 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8179 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8180 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8185 int map_action_MM_to_RND(int action_mm)
8187 // all MM actions are defined to exactly match their RND counterparts
8191 int map_sound_MM_to_RND(int sound_mm)
8195 case SND_MM_GAME_LEVELTIME_CHARGING:
8196 return SND_GAME_LEVELTIME_CHARGING;
8198 case SND_MM_GAME_HEALTH_CHARGING:
8199 return SND_GAME_HEALTH_CHARGING;
8202 return SND_UNDEFINED;
8206 int map_mm_wall_element(int element)
8208 return (element >= EL_MM_STEEL_WALL_START &&
8209 element <= EL_MM_STEEL_WALL_END ?
8212 element >= EL_MM_WOODEN_WALL_START &&
8213 element <= EL_MM_WOODEN_WALL_END ?
8216 element >= EL_MM_ICE_WALL_START &&
8217 element <= EL_MM_ICE_WALL_END ?
8220 element >= EL_MM_AMOEBA_WALL_START &&
8221 element <= EL_MM_AMOEBA_WALL_END ?
8224 element >= EL_DF_STEEL_WALL_START &&
8225 element <= EL_DF_STEEL_WALL_END ?
8228 element >= EL_DF_WOODEN_WALL_START &&
8229 element <= EL_DF_WOODEN_WALL_END ?
8235 int map_mm_wall_element_editor(int element)
8239 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8240 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8241 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8242 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8243 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8244 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8246 default: return element;
8250 int get_next_element(int element)
8254 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8255 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8256 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8257 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8258 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8259 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8260 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8261 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8262 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8263 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8264 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8266 default: return element;
8270 int el2img_mm(int element_mm)
8272 return el2img(map_element_MM_to_RND(element_mm));
8275 int el_act_dir2img(int element, int action, int direction)
8277 element = GFX_ELEMENT(element);
8278 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8280 // direction_graphic[][] == graphic[] for undefined direction graphics
8281 return element_info[element].direction_graphic[action][direction];
8284 static int el_act_dir2crm(int element, int action, int direction)
8286 element = GFX_ELEMENT(element);
8287 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8289 // direction_graphic[][] == graphic[] for undefined direction graphics
8290 return element_info[element].direction_crumbled[action][direction];
8293 int el_act2img(int element, int action)
8295 element = GFX_ELEMENT(element);
8297 return element_info[element].graphic[action];
8300 int el_act2crm(int element, int action)
8302 element = GFX_ELEMENT(element);
8304 return element_info[element].crumbled[action];
8307 int el_dir2img(int element, int direction)
8309 element = GFX_ELEMENT(element);
8311 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8314 int el2baseimg(int element)
8316 return element_info[element].graphic[ACTION_DEFAULT];
8319 int el2img(int element)
8321 element = GFX_ELEMENT(element);
8323 return element_info[element].graphic[ACTION_DEFAULT];
8326 int el2edimg(int element)
8328 element = GFX_ELEMENT(element);
8330 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8333 int el2preimg(int element)
8335 element = GFX_ELEMENT(element);
8337 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8340 int el2panelimg(int element)
8342 element = GFX_ELEMENT(element);
8344 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8347 int font2baseimg(int font_nr)
8349 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8352 int getBeltNrFromBeltElement(int element)
8354 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8355 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8356 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8359 int getBeltNrFromBeltActiveElement(int element)
8361 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8362 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8363 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8366 int getBeltNrFromBeltSwitchElement(int element)
8368 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8369 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8370 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8373 int getBeltDirNrFromBeltElement(int element)
8375 static int belt_base_element[4] =
8377 EL_CONVEYOR_BELT_1_LEFT,
8378 EL_CONVEYOR_BELT_2_LEFT,
8379 EL_CONVEYOR_BELT_3_LEFT,
8380 EL_CONVEYOR_BELT_4_LEFT
8383 int belt_nr = getBeltNrFromBeltElement(element);
8384 int belt_dir_nr = element - belt_base_element[belt_nr];
8386 return (belt_dir_nr % 3);
8389 int getBeltDirNrFromBeltSwitchElement(int element)
8391 static int belt_base_element[4] =
8393 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8394 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8395 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8396 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8399 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8400 int belt_dir_nr = element - belt_base_element[belt_nr];
8402 return (belt_dir_nr % 3);
8405 int getBeltDirFromBeltElement(int element)
8407 static int belt_move_dir[3] =
8414 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8416 return belt_move_dir[belt_dir_nr];
8419 int getBeltDirFromBeltSwitchElement(int element)
8421 static int belt_move_dir[3] =
8428 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8430 return belt_move_dir[belt_dir_nr];
8433 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8435 static int belt_base_element[4] =
8437 EL_CONVEYOR_BELT_1_LEFT,
8438 EL_CONVEYOR_BELT_2_LEFT,
8439 EL_CONVEYOR_BELT_3_LEFT,
8440 EL_CONVEYOR_BELT_4_LEFT
8443 return belt_base_element[belt_nr] + belt_dir_nr;
8446 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8448 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8450 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8453 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8455 static int belt_base_element[4] =
8457 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8458 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8459 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8460 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8463 return belt_base_element[belt_nr] + belt_dir_nr;
8466 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8468 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8470 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8473 boolean swapTiles_EM(boolean is_pre_emc_cave)
8475 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8478 boolean getTeamMode_EM(void)
8480 return game.team_mode || network_playing;
8483 boolean isActivePlayer_EM(int player_nr)
8485 return stored_player[player_nr].active;
8488 unsigned int InitRND(int seed)
8490 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8491 return InitEngineRandom_EM(seed);
8492 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8493 return InitEngineRandom_SP(seed);
8494 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8495 return InitEngineRandom_MM(seed);
8497 return InitEngineRandom_RND(seed);
8500 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8501 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8503 static int get_effective_element_EM(int tile, int frame_em)
8505 int element = object_mapping[tile].element_rnd;
8506 int action = object_mapping[tile].action;
8507 boolean is_backside = object_mapping[tile].is_backside;
8508 boolean action_removing = (action == ACTION_DIGGING ||
8509 action == ACTION_SNAPPING ||
8510 action == ACTION_COLLECTING);
8518 return (frame_em > 5 ? EL_EMPTY : element);
8524 else // frame_em == 7
8535 case Ydiamond_stone:
8539 case Xdrip_stretchB:
8555 case Ymagnify_blank:
8558 case Xsand_stonein_1:
8559 case Xsand_stonein_2:
8560 case Xsand_stonein_3:
8561 case Xsand_stonein_4:
8565 return (is_backside || action_removing ? EL_EMPTY : element);
8570 static boolean check_linear_animation_EM(int tile)
8574 case Xsand_stonesand_1:
8575 case Xsand_stonesand_quickout_1:
8576 case Xsand_sandstone_1:
8577 case Xsand_stonein_1:
8578 case Xsand_stoneout_1:
8606 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8607 boolean has_crumbled_graphics,
8608 int crumbled, int sync_frame)
8610 // if element can be crumbled, but certain action graphics are just empty
8611 // space (like instantly snapping sand to empty space in 1 frame), do not
8612 // treat these empty space graphics as crumbled graphics in EMC engine
8613 if (crumbled == IMG_EMPTY_SPACE)
8614 has_crumbled_graphics = FALSE;
8616 if (has_crumbled_graphics)
8618 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8619 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8620 g_crumbled->anim_delay,
8621 g_crumbled->anim_mode,
8622 g_crumbled->anim_start_frame,
8625 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8626 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8628 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8629 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8631 g_em->has_crumbled_graphics = TRUE;
8635 g_em->crumbled_bitmap = NULL;
8636 g_em->crumbled_src_x = 0;
8637 g_em->crumbled_src_y = 0;
8638 g_em->crumbled_border_size = 0;
8639 g_em->crumbled_tile_size = 0;
8641 g_em->has_crumbled_graphics = FALSE;
8646 void ResetGfxAnimation_EM(int x, int y, int tile)
8652 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8653 int tile, int frame_em, int x, int y)
8655 int action = object_mapping[tile].action;
8656 int direction = object_mapping[tile].direction;
8657 int effective_element = get_effective_element_EM(tile, frame_em);
8658 int graphic = (direction == MV_NONE ?
8659 el_act2img(effective_element, action) :
8660 el_act_dir2img(effective_element, action, direction));
8661 struct GraphicInfo *g = &graphic_info[graphic];
8663 boolean action_removing = (action == ACTION_DIGGING ||
8664 action == ACTION_SNAPPING ||
8665 action == ACTION_COLLECTING);
8666 boolean action_moving = (action == ACTION_FALLING ||
8667 action == ACTION_MOVING ||
8668 action == ACTION_PUSHING ||
8669 action == ACTION_EATING ||
8670 action == ACTION_FILLING ||
8671 action == ACTION_EMPTYING);
8672 boolean action_falling = (action == ACTION_FALLING ||
8673 action == ACTION_FILLING ||
8674 action == ACTION_EMPTYING);
8676 // special case: graphic uses "2nd movement tile" and has defined
8677 // 7 frames for movement animation (or less) => use default graphic
8678 // for last (8th) frame which ends the movement animation
8679 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8681 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8682 graphic = (direction == MV_NONE ?
8683 el_act2img(effective_element, action) :
8684 el_act_dir2img(effective_element, action, direction));
8686 g = &graphic_info[graphic];
8689 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8693 else if (action_moving)
8695 boolean is_backside = object_mapping[tile].is_backside;
8699 int direction = object_mapping[tile].direction;
8700 int move_dir = (action_falling ? MV_DOWN : direction);
8705 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8706 if (g->double_movement && frame_em == 0)
8710 if (move_dir == MV_LEFT)
8711 GfxFrame[x - 1][y] = GfxFrame[x][y];
8712 else if (move_dir == MV_RIGHT)
8713 GfxFrame[x + 1][y] = GfxFrame[x][y];
8714 else if (move_dir == MV_UP)
8715 GfxFrame[x][y - 1] = GfxFrame[x][y];
8716 else if (move_dir == MV_DOWN)
8717 GfxFrame[x][y + 1] = GfxFrame[x][y];
8724 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8725 if (tile == Xsand_stonesand_quickout_1 ||
8726 tile == Xsand_stonesand_quickout_2)
8730 if (graphic_info[graphic].anim_global_sync)
8731 sync_frame = FrameCounter;
8732 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8733 sync_frame = GfxFrame[x][y];
8735 sync_frame = 0; // playfield border (pseudo steel)
8737 SetRandomAnimationValue(x, y);
8739 int frame = getAnimationFrame(g->anim_frames,
8742 g->anim_start_frame,
8745 g_em->unique_identifier =
8746 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8749 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8750 int tile, int frame_em, int x, int y)
8752 int action = object_mapping[tile].action;
8753 int direction = object_mapping[tile].direction;
8754 boolean is_backside = object_mapping[tile].is_backside;
8755 int effective_element = get_effective_element_EM(tile, frame_em);
8756 int effective_action = action;
8757 int graphic = (direction == MV_NONE ?
8758 el_act2img(effective_element, effective_action) :
8759 el_act_dir2img(effective_element, effective_action,
8761 int crumbled = (direction == MV_NONE ?
8762 el_act2crm(effective_element, effective_action) :
8763 el_act_dir2crm(effective_element, effective_action,
8765 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8766 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8767 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8768 struct GraphicInfo *g = &graphic_info[graphic];
8771 // special case: graphic uses "2nd movement tile" and has defined
8772 // 7 frames for movement animation (or less) => use default graphic
8773 // for last (8th) frame which ends the movement animation
8774 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8776 effective_action = ACTION_DEFAULT;
8777 graphic = (direction == MV_NONE ?
8778 el_act2img(effective_element, effective_action) :
8779 el_act_dir2img(effective_element, effective_action,
8781 crumbled = (direction == MV_NONE ?
8782 el_act2crm(effective_element, effective_action) :
8783 el_act_dir2crm(effective_element, effective_action,
8786 g = &graphic_info[graphic];
8789 if (graphic_info[graphic].anim_global_sync)
8790 sync_frame = FrameCounter;
8791 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8792 sync_frame = GfxFrame[x][y];
8794 sync_frame = 0; // playfield border (pseudo steel)
8796 SetRandomAnimationValue(x, y);
8798 int frame = getAnimationFrame(g->anim_frames,
8801 g->anim_start_frame,
8804 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8805 g->double_movement && is_backside);
8807 // (updating the "crumbled" graphic definitions is probably not really needed,
8808 // as animations for crumbled graphics can't be longer than one EMC cycle)
8809 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8813 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8814 int player_nr, int anim, int frame_em)
8816 int element = player_mapping[player_nr][anim].element_rnd;
8817 int action = player_mapping[player_nr][anim].action;
8818 int direction = player_mapping[player_nr][anim].direction;
8819 int graphic = (direction == MV_NONE ?
8820 el_act2img(element, action) :
8821 el_act_dir2img(element, action, direction));
8822 struct GraphicInfo *g = &graphic_info[graphic];
8825 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8827 stored_player[player_nr].StepFrame = frame_em;
8829 sync_frame = stored_player[player_nr].Frame;
8831 int frame = getAnimationFrame(g->anim_frames,
8834 g->anim_start_frame,
8837 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8838 &g_em->src_x, &g_em->src_y, FALSE);
8841 void InitGraphicInfo_EM(void)
8845 // always start with reliable default values
8846 for (i = 0; i < GAME_TILE_MAX; i++)
8848 object_mapping[i].element_rnd = EL_UNKNOWN;
8849 object_mapping[i].is_backside = FALSE;
8850 object_mapping[i].action = ACTION_DEFAULT;
8851 object_mapping[i].direction = MV_NONE;
8854 // always start with reliable default values
8855 for (p = 0; p < MAX_PLAYERS; p++)
8857 for (i = 0; i < PLY_MAX; i++)
8859 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8860 player_mapping[p][i].action = ACTION_DEFAULT;
8861 player_mapping[p][i].direction = MV_NONE;
8865 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8867 int e = em_object_mapping_list[i].element_em;
8869 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8870 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8872 if (em_object_mapping_list[i].action != -1)
8873 object_mapping[e].action = em_object_mapping_list[i].action;
8875 if (em_object_mapping_list[i].direction != -1)
8876 object_mapping[e].direction =
8877 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8880 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8882 int a = em_player_mapping_list[i].action_em;
8883 int p = em_player_mapping_list[i].player_nr;
8885 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8887 if (em_player_mapping_list[i].action != -1)
8888 player_mapping[p][a].action = em_player_mapping_list[i].action;
8890 if (em_player_mapping_list[i].direction != -1)
8891 player_mapping[p][a].direction =
8892 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8895 for (i = 0; i < GAME_TILE_MAX; i++)
8897 int element = object_mapping[i].element_rnd;
8898 int action = object_mapping[i].action;
8899 int direction = object_mapping[i].direction;
8900 boolean is_backside = object_mapping[i].is_backside;
8901 boolean action_exploding = ((action == ACTION_EXPLODING ||
8902 action == ACTION_SMASHED_BY_ROCK ||
8903 action == ACTION_SMASHED_BY_SPRING) &&
8904 element != EL_DIAMOND);
8905 boolean action_active = (action == ACTION_ACTIVE);
8906 boolean action_other = (action == ACTION_OTHER);
8908 for (j = 0; j < 8; j++)
8910 int effective_element = get_effective_element_EM(i, j);
8911 int effective_action = (j < 7 ? action :
8912 i == Xdrip_stretch ? action :
8913 i == Xdrip_stretchB ? action :
8914 i == Ydrip_1_s ? action :
8915 i == Ydrip_1_sB ? action :
8916 i == Yball_1 ? action :
8917 i == Xball_2 ? action :
8918 i == Yball_2 ? action :
8919 i == Yball_blank ? action :
8920 i == Ykey_1_blank ? action :
8921 i == Ykey_2_blank ? action :
8922 i == Ykey_3_blank ? action :
8923 i == Ykey_4_blank ? action :
8924 i == Ykey_5_blank ? action :
8925 i == Ykey_6_blank ? action :
8926 i == Ykey_7_blank ? action :
8927 i == Ykey_8_blank ? action :
8928 i == Ylenses_blank ? action :
8929 i == Ymagnify_blank ? action :
8930 i == Ygrass_blank ? action :
8931 i == Ydirt_blank ? action :
8932 i == Xsand_stonein_1 ? action :
8933 i == Xsand_stonein_2 ? action :
8934 i == Xsand_stonein_3 ? action :
8935 i == Xsand_stonein_4 ? action :
8936 i == Xsand_stoneout_1 ? action :
8937 i == Xsand_stoneout_2 ? action :
8938 i == Xboom_android ? ACTION_EXPLODING :
8939 action_exploding ? ACTION_EXPLODING :
8940 action_active ? action :
8941 action_other ? action :
8943 int graphic = (el_act_dir2img(effective_element, effective_action,
8945 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8947 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8948 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8949 boolean has_action_graphics = (graphic != base_graphic);
8950 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8951 struct GraphicInfo *g = &graphic_info[graphic];
8952 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8955 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8956 boolean special_animation = (action != ACTION_DEFAULT &&
8957 g->anim_frames == 3 &&
8958 g->anim_delay == 2 &&
8959 g->anim_mode & ANIM_LINEAR);
8960 int sync_frame = (i == Xdrip_stretch ? 7 :
8961 i == Xdrip_stretchB ? 7 :
8962 i == Ydrip_2_s ? j + 8 :
8963 i == Ydrip_2_sB ? j + 8 :
8972 i == Xfake_acid_1 ? 0 :
8973 i == Xfake_acid_2 ? 10 :
8974 i == Xfake_acid_3 ? 20 :
8975 i == Xfake_acid_4 ? 30 :
8976 i == Xfake_acid_5 ? 40 :
8977 i == Xfake_acid_6 ? 50 :
8978 i == Xfake_acid_7 ? 60 :
8979 i == Xfake_acid_8 ? 70 :
8980 i == Xfake_acid_1_player ? 0 :
8981 i == Xfake_acid_2_player ? 10 :
8982 i == Xfake_acid_3_player ? 20 :
8983 i == Xfake_acid_4_player ? 30 :
8984 i == Xfake_acid_5_player ? 40 :
8985 i == Xfake_acid_6_player ? 50 :
8986 i == Xfake_acid_7_player ? 60 :
8987 i == Xfake_acid_8_player ? 70 :
8989 i == Yball_2 ? j + 8 :
8990 i == Yball_blank ? j + 1 :
8991 i == Ykey_1_blank ? j + 1 :
8992 i == Ykey_2_blank ? j + 1 :
8993 i == Ykey_3_blank ? j + 1 :
8994 i == Ykey_4_blank ? j + 1 :
8995 i == Ykey_5_blank ? j + 1 :
8996 i == Ykey_6_blank ? j + 1 :
8997 i == Ykey_7_blank ? j + 1 :
8998 i == Ykey_8_blank ? j + 1 :
8999 i == Ylenses_blank ? j + 1 :
9000 i == Ymagnify_blank ? j + 1 :
9001 i == Ygrass_blank ? j + 1 :
9002 i == Ydirt_blank ? j + 1 :
9003 i == Xamoeba_1 ? 0 :
9004 i == Xamoeba_2 ? 1 :
9005 i == Xamoeba_3 ? 2 :
9006 i == Xamoeba_4 ? 3 :
9007 i == Xamoeba_5 ? 0 :
9008 i == Xamoeba_6 ? 1 :
9009 i == Xamoeba_7 ? 2 :
9010 i == Xamoeba_8 ? 3 :
9011 i == Xexit_2 ? j + 8 :
9012 i == Xexit_3 ? j + 16 :
9013 i == Xdynamite_1 ? 0 :
9014 i == Xdynamite_2 ? 8 :
9015 i == Xdynamite_3 ? 16 :
9016 i == Xdynamite_4 ? 24 :
9017 i == Xsand_stonein_1 ? j + 1 :
9018 i == Xsand_stonein_2 ? j + 9 :
9019 i == Xsand_stonein_3 ? j + 17 :
9020 i == Xsand_stonein_4 ? j + 25 :
9021 i == Xsand_stoneout_1 && j == 0 ? 0 :
9022 i == Xsand_stoneout_1 && j == 1 ? 0 :
9023 i == Xsand_stoneout_1 && j == 2 ? 1 :
9024 i == Xsand_stoneout_1 && j == 3 ? 2 :
9025 i == Xsand_stoneout_1 && j == 4 ? 2 :
9026 i == Xsand_stoneout_1 && j == 5 ? 3 :
9027 i == Xsand_stoneout_1 && j == 6 ? 4 :
9028 i == Xsand_stoneout_1 && j == 7 ? 4 :
9029 i == Xsand_stoneout_2 && j == 0 ? 5 :
9030 i == Xsand_stoneout_2 && j == 1 ? 6 :
9031 i == Xsand_stoneout_2 && j == 2 ? 7 :
9032 i == Xsand_stoneout_2 && j == 3 ? 8 :
9033 i == Xsand_stoneout_2 && j == 4 ? 9 :
9034 i == Xsand_stoneout_2 && j == 5 ? 11 :
9035 i == Xsand_stoneout_2 && j == 6 ? 13 :
9036 i == Xsand_stoneout_2 && j == 7 ? 15 :
9037 i == Xboom_bug && j == 1 ? 2 :
9038 i == Xboom_bug && j == 2 ? 2 :
9039 i == Xboom_bug && j == 3 ? 4 :
9040 i == Xboom_bug && j == 4 ? 4 :
9041 i == Xboom_bug && j == 5 ? 2 :
9042 i == Xboom_bug && j == 6 ? 2 :
9043 i == Xboom_bug && j == 7 ? 0 :
9044 i == Xboom_tank && j == 1 ? 2 :
9045 i == Xboom_tank && j == 2 ? 2 :
9046 i == Xboom_tank && j == 3 ? 4 :
9047 i == Xboom_tank && j == 4 ? 4 :
9048 i == Xboom_tank && j == 5 ? 2 :
9049 i == Xboom_tank && j == 6 ? 2 :
9050 i == Xboom_tank && j == 7 ? 0 :
9051 i == Xboom_android && j == 7 ? 6 :
9052 i == Xboom_1 && j == 1 ? 2 :
9053 i == Xboom_1 && j == 2 ? 2 :
9054 i == Xboom_1 && j == 3 ? 4 :
9055 i == Xboom_1 && j == 4 ? 4 :
9056 i == Xboom_1 && j == 5 ? 6 :
9057 i == Xboom_1 && j == 6 ? 6 :
9058 i == Xboom_1 && j == 7 ? 8 :
9059 i == Xboom_2 && j == 0 ? 8 :
9060 i == Xboom_2 && j == 1 ? 8 :
9061 i == Xboom_2 && j == 2 ? 10 :
9062 i == Xboom_2 && j == 3 ? 10 :
9063 i == Xboom_2 && j == 4 ? 10 :
9064 i == Xboom_2 && j == 5 ? 12 :
9065 i == Xboom_2 && j == 6 ? 12 :
9066 i == Xboom_2 && j == 7 ? 12 :
9067 special_animation && j == 4 ? 3 :
9068 effective_action != action ? 0 :
9070 int frame = getAnimationFrame(g->anim_frames,
9073 g->anim_start_frame,
9076 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9077 g->double_movement && is_backside);
9079 g_em->bitmap = src_bitmap;
9080 g_em->src_x = src_x;
9081 g_em->src_y = src_y;
9082 g_em->src_offset_x = 0;
9083 g_em->src_offset_y = 0;
9084 g_em->dst_offset_x = 0;
9085 g_em->dst_offset_y = 0;
9086 g_em->width = TILEX;
9087 g_em->height = TILEY;
9089 g_em->preserve_background = FALSE;
9091 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9094 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9095 effective_action == ACTION_MOVING ||
9096 effective_action == ACTION_PUSHING ||
9097 effective_action == ACTION_EATING)) ||
9098 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9099 effective_action == ACTION_EMPTYING)))
9102 (effective_action == ACTION_FALLING ||
9103 effective_action == ACTION_FILLING ||
9104 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9105 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9106 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9107 int num_steps = (i == Ydrip_1_s ? 16 :
9108 i == Ydrip_1_sB ? 16 :
9109 i == Ydrip_2_s ? 16 :
9110 i == Ydrip_2_sB ? 16 :
9111 i == Xsand_stonein_1 ? 32 :
9112 i == Xsand_stonein_2 ? 32 :
9113 i == Xsand_stonein_3 ? 32 :
9114 i == Xsand_stonein_4 ? 32 :
9115 i == Xsand_stoneout_1 ? 16 :
9116 i == Xsand_stoneout_2 ? 16 : 8);
9117 int cx = ABS(dx) * (TILEX / num_steps);
9118 int cy = ABS(dy) * (TILEY / num_steps);
9119 int step_frame = (i == Ydrip_2_s ? j + 8 :
9120 i == Ydrip_2_sB ? j + 8 :
9121 i == Xsand_stonein_2 ? j + 8 :
9122 i == Xsand_stonein_3 ? j + 16 :
9123 i == Xsand_stonein_4 ? j + 24 :
9124 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9125 int step = (is_backside ? step_frame : num_steps - step_frame);
9127 if (is_backside) // tile where movement starts
9129 if (dx < 0 || dy < 0)
9131 g_em->src_offset_x = cx * step;
9132 g_em->src_offset_y = cy * step;
9136 g_em->dst_offset_x = cx * step;
9137 g_em->dst_offset_y = cy * step;
9140 else // tile where movement ends
9142 if (dx < 0 || dy < 0)
9144 g_em->dst_offset_x = cx * step;
9145 g_em->dst_offset_y = cy * step;
9149 g_em->src_offset_x = cx * step;
9150 g_em->src_offset_y = cy * step;
9154 g_em->width = TILEX - cx * step;
9155 g_em->height = TILEY - cy * step;
9158 // create unique graphic identifier to decide if tile must be redrawn
9159 /* bit 31 - 16 (16 bit): EM style graphic
9160 bit 15 - 12 ( 4 bit): EM style frame
9161 bit 11 - 6 ( 6 bit): graphic width
9162 bit 5 - 0 ( 6 bit): graphic height */
9163 g_em->unique_identifier =
9164 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9168 for (i = 0; i < GAME_TILE_MAX; i++)
9170 for (j = 0; j < 8; j++)
9172 int element = object_mapping[i].element_rnd;
9173 int action = object_mapping[i].action;
9174 int direction = object_mapping[i].direction;
9175 boolean is_backside = object_mapping[i].is_backside;
9176 int graphic_action = el_act_dir2img(element, action, direction);
9177 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9179 if ((action == ACTION_SMASHED_BY_ROCK ||
9180 action == ACTION_SMASHED_BY_SPRING ||
9181 action == ACTION_EATING) &&
9182 graphic_action == graphic_default)
9184 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9185 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9186 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9187 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9190 // no separate animation for "smashed by rock" -- use rock instead
9191 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9192 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9194 g_em->bitmap = g_xx->bitmap;
9195 g_em->src_x = g_xx->src_x;
9196 g_em->src_y = g_xx->src_y;
9197 g_em->src_offset_x = g_xx->src_offset_x;
9198 g_em->src_offset_y = g_xx->src_offset_y;
9199 g_em->dst_offset_x = g_xx->dst_offset_x;
9200 g_em->dst_offset_y = g_xx->dst_offset_y;
9201 g_em->width = g_xx->width;
9202 g_em->height = g_xx->height;
9203 g_em->unique_identifier = g_xx->unique_identifier;
9206 g_em->preserve_background = TRUE;
9211 for (p = 0; p < MAX_PLAYERS; p++)
9213 for (i = 0; i < PLY_MAX; i++)
9215 int element = player_mapping[p][i].element_rnd;
9216 int action = player_mapping[p][i].action;
9217 int direction = player_mapping[p][i].direction;
9219 for (j = 0; j < 8; j++)
9221 int effective_element = element;
9222 int effective_action = action;
9223 int graphic = (direction == MV_NONE ?
9224 el_act2img(effective_element, effective_action) :
9225 el_act_dir2img(effective_element, effective_action,
9227 struct GraphicInfo *g = &graphic_info[graphic];
9228 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9232 int frame = getAnimationFrame(g->anim_frames,
9235 g->anim_start_frame,
9238 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9240 g_em->bitmap = src_bitmap;
9241 g_em->src_x = src_x;
9242 g_em->src_y = src_y;
9243 g_em->src_offset_x = 0;
9244 g_em->src_offset_y = 0;
9245 g_em->dst_offset_x = 0;
9246 g_em->dst_offset_y = 0;
9247 g_em->width = TILEX;
9248 g_em->height = TILEY;
9254 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9255 boolean any_player_moving,
9256 boolean any_player_snapping,
9257 boolean any_player_dropping)
9259 if (frame == 7 && !any_player_dropping)
9261 if (!local_player->was_waiting)
9263 if (!CheckSaveEngineSnapshotToList())
9266 local_player->was_waiting = TRUE;
9269 else if (any_player_moving || any_player_snapping || any_player_dropping)
9271 local_player->was_waiting = FALSE;
9275 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9276 boolean murphy_is_dropping)
9278 if (murphy_is_waiting)
9280 if (!local_player->was_waiting)
9282 if (!CheckSaveEngineSnapshotToList())
9285 local_player->was_waiting = TRUE;
9290 local_player->was_waiting = FALSE;
9294 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9295 boolean button_released)
9297 if (button_released)
9299 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9300 CheckSaveEngineSnapshotToList();
9302 else if (element_clicked)
9304 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9305 CheckSaveEngineSnapshotToList();
9307 game.snapshot.changed_action = TRUE;
9311 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9312 boolean any_player_moving,
9313 boolean any_player_snapping,
9314 boolean any_player_dropping)
9316 if (tape.single_step && tape.recording && !tape.pausing)
9317 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9318 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9320 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9321 any_player_snapping, any_player_dropping);
9323 return tape.pausing;
9326 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9327 boolean murphy_is_dropping)
9329 boolean murphy_starts_dropping = FALSE;
9332 for (i = 0; i < MAX_PLAYERS; i++)
9333 if (stored_player[i].force_dropping)
9334 murphy_starts_dropping = TRUE;
9336 if (tape.single_step && tape.recording && !tape.pausing)
9337 if (murphy_is_waiting && !murphy_starts_dropping)
9338 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9340 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9343 void CheckSingleStepMode_MM(boolean element_clicked,
9344 boolean button_released)
9346 if (tape.single_step && tape.recording && !tape.pausing)
9347 if (button_released)
9348 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9350 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9353 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9354 int graphic, int sync_frame, int x, int y)
9356 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9358 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9361 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9363 return (IS_NEXT_FRAME(sync_frame, graphic));
9366 int getGraphicInfo_Delay(int graphic)
9368 return graphic_info[graphic].anim_delay;
9371 void PlayMenuSoundExt(int sound)
9373 if (sound == SND_UNDEFINED)
9376 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9377 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9380 if (IS_LOOP_SOUND(sound))
9381 PlaySoundLoop(sound);
9386 void PlayMenuSound(void)
9388 PlayMenuSoundExt(menu.sound[game_status]);
9391 void PlayMenuSoundStereo(int sound, int stereo_position)
9393 if (sound == SND_UNDEFINED)
9396 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9397 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9400 if (IS_LOOP_SOUND(sound))
9401 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9403 PlaySoundStereo(sound, stereo_position);
9406 void PlayMenuSoundIfLoopExt(int sound)
9408 if (sound == SND_UNDEFINED)
9411 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9412 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9415 if (IS_LOOP_SOUND(sound))
9416 PlaySoundLoop(sound);
9419 void PlayMenuSoundIfLoop(void)
9421 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9424 void PlayMenuMusicExt(int music)
9426 if (music == MUS_UNDEFINED)
9429 if (!setup.sound_music)
9432 if (IS_LOOP_MUSIC(music))
9433 PlayMusicLoop(music);
9438 void PlayMenuMusic(void)
9440 char *curr_music = getCurrentlyPlayingMusicFilename();
9441 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9443 if (!strEqual(curr_music, next_music))
9444 PlayMenuMusicExt(menu.music[game_status]);
9447 void PlayMenuSoundsAndMusic(void)
9453 static void FadeMenuSounds(void)
9458 static void FadeMenuMusic(void)
9460 char *curr_music = getCurrentlyPlayingMusicFilename();
9461 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9463 if (!strEqual(curr_music, next_music))
9467 void FadeMenuSoundsAndMusic(void)
9473 void PlaySoundActivating(void)
9476 PlaySound(SND_MENU_ITEM_ACTIVATING);
9480 void PlaySoundSelecting(void)
9483 PlaySound(SND_MENU_ITEM_SELECTING);
9487 void ToggleFullscreenIfNeeded(void)
9489 // if setup and video fullscreen state are already matching, nothing do do
9490 if (setup.fullscreen == video.fullscreen_enabled ||
9491 !video.fullscreen_available)
9494 SDLSetWindowFullscreen(setup.fullscreen);
9496 // set setup value according to successfully changed fullscreen mode
9497 setup.fullscreen = video.fullscreen_enabled;
9500 void ChangeWindowScalingIfNeeded(void)
9502 // if setup and video window scaling are already matching, nothing do do
9503 if (setup.window_scaling_percent == video.window_scaling_percent ||
9504 video.fullscreen_enabled)
9507 SDLSetWindowScaling(setup.window_scaling_percent);
9509 // set setup value according to successfully changed window scaling
9510 setup.window_scaling_percent = video.window_scaling_percent;
9513 void ChangeVsyncModeIfNeeded(void)
9515 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9516 int video_vsync_mode = video.vsync_mode;
9518 // if setup and video vsync mode are already matching, nothing do do
9519 if (setup_vsync_mode == video_vsync_mode)
9522 // if renderer is using OpenGL, vsync mode can directly be changed
9523 SDLSetScreenVsyncMode(setup.vsync_mode);
9525 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9526 if (video.vsync_mode == video_vsync_mode)
9528 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9530 // save backbuffer content which gets lost when re-creating screen
9531 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9533 // force re-creating screen and renderer to set new vsync mode
9534 video.fullscreen_enabled = !setup.fullscreen;
9536 // when creating new renderer, destroy textures linked to old renderer
9537 FreeAllImageTextures(); // needs old renderer to free the textures
9539 // re-create screen and renderer (including change of vsync mode)
9540 ChangeVideoModeIfNeeded(setup.fullscreen);
9542 // set setup value according to successfully changed fullscreen mode
9543 setup.fullscreen = video.fullscreen_enabled;
9545 // restore backbuffer content from temporary backbuffer backup bitmap
9546 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9547 FreeBitmap(tmp_backbuffer);
9549 // update visible window/screen
9550 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9552 // when changing vsync mode, re-create textures for new renderer
9553 InitImageTextures();
9556 // set setup value according to successfully changed vsync mode
9557 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9560 static void JoinRectangles(int *x, int *y, int *width, int *height,
9561 int x2, int y2, int width2, int height2)
9563 // do not join with "off-screen" rectangle
9564 if (x2 == -1 || y2 == -1)
9569 *width = MAX(*width, width2);
9570 *height = MAX(*height, height2);
9573 void SetAnimStatus(int anim_status_new)
9575 if (anim_status_new == GAME_MODE_MAIN)
9576 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9577 else if (anim_status_new == GAME_MODE_NAMES)
9578 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9579 else if (anim_status_new == GAME_MODE_SCORES)
9580 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9582 global.anim_status_next = anim_status_new;
9584 // directly set screen modes that are entered without fading
9585 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9586 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9587 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9588 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9589 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9590 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9591 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9592 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9593 global.anim_status = global.anim_status_next;
9596 void SetGameStatus(int game_status_new)
9598 if (game_status_new != game_status)
9599 game_status_last_screen = game_status;
9601 game_status = game_status_new;
9603 SetAnimStatus(game_status_new);
9606 void SetFontStatus(int game_status_new)
9608 static int last_game_status = -1;
9610 if (game_status_new != -1)
9612 // set game status for font use after storing last game status
9613 last_game_status = game_status;
9614 game_status = game_status_new;
9618 // reset game status after font use from last stored game status
9619 game_status = last_game_status;
9623 void ResetFontStatus(void)
9628 void SetLevelSetInfo(char *identifier, int level_nr)
9630 setString(&levelset.identifier, identifier);
9632 levelset.level_nr = level_nr;
9635 boolean CheckIfAllViewportsHaveChanged(void)
9637 // if game status has not changed, viewports have not changed either
9638 if (game_status == game_status_last)
9641 // check if all viewports have changed with current game status
9643 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9644 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9645 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9646 int new_real_sx = vp_playfield->x;
9647 int new_real_sy = vp_playfield->y;
9648 int new_full_sxsize = vp_playfield->width;
9649 int new_full_sysize = vp_playfield->height;
9650 int new_dx = vp_door_1->x;
9651 int new_dy = vp_door_1->y;
9652 int new_dxsize = vp_door_1->width;
9653 int new_dysize = vp_door_1->height;
9654 int new_vx = vp_door_2->x;
9655 int new_vy = vp_door_2->y;
9656 int new_vxsize = vp_door_2->width;
9657 int new_vysize = vp_door_2->height;
9659 boolean playfield_viewport_has_changed =
9660 (new_real_sx != REAL_SX ||
9661 new_real_sy != REAL_SY ||
9662 new_full_sxsize != FULL_SXSIZE ||
9663 new_full_sysize != FULL_SYSIZE);
9665 boolean door_1_viewport_has_changed =
9668 new_dxsize != DXSIZE ||
9669 new_dysize != DYSIZE);
9671 boolean door_2_viewport_has_changed =
9674 new_vxsize != VXSIZE ||
9675 new_vysize != VYSIZE ||
9676 game_status_last == GAME_MODE_EDITOR);
9678 return (playfield_viewport_has_changed &&
9679 door_1_viewport_has_changed &&
9680 door_2_viewport_has_changed);
9683 boolean CheckFadeAll(void)
9685 return (CheckIfGlobalBorderHasChanged() ||
9686 CheckIfAllViewportsHaveChanged());
9689 void ChangeViewportPropertiesIfNeeded(void)
9691 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9692 FALSE : setup.small_game_graphics);
9693 int gfx_game_mode = game_status;
9694 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9696 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9697 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9698 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9699 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9700 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9701 int new_win_xsize = vp_window->width;
9702 int new_win_ysize = vp_window->height;
9703 int border_left = vp_playfield->border_left;
9704 int border_right = vp_playfield->border_right;
9705 int border_top = vp_playfield->border_top;
9706 int border_bottom = vp_playfield->border_bottom;
9707 int new_sx = vp_playfield->x + border_left;
9708 int new_sy = vp_playfield->y + border_top;
9709 int new_sxsize = vp_playfield->width - border_left - border_right;
9710 int new_sysize = vp_playfield->height - border_top - border_bottom;
9711 int new_real_sx = vp_playfield->x;
9712 int new_real_sy = vp_playfield->y;
9713 int new_full_sxsize = vp_playfield->width;
9714 int new_full_sysize = vp_playfield->height;
9715 int new_dx = vp_door_1->x;
9716 int new_dy = vp_door_1->y;
9717 int new_dxsize = vp_door_1->width;
9718 int new_dysize = vp_door_1->height;
9719 int new_vx = vp_door_2->x;
9720 int new_vy = vp_door_2->y;
9721 int new_vxsize = vp_door_2->width;
9722 int new_vysize = vp_door_2->height;
9723 int new_ex = vp_door_3->x;
9724 int new_ey = vp_door_3->y;
9725 int new_exsize = vp_door_3->width;
9726 int new_eysize = vp_door_3->height;
9727 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9728 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9729 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9730 int new_scr_fieldx = new_sxsize / tilesize;
9731 int new_scr_fieldy = new_sysize / tilesize;
9732 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9733 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9734 boolean init_gfx_buffers = FALSE;
9735 boolean init_video_buffer = FALSE;
9736 boolean init_gadgets_and_anims = FALSE;
9737 boolean init_em_graphics = FALSE;
9739 if (new_win_xsize != WIN_XSIZE ||
9740 new_win_ysize != WIN_YSIZE)
9742 WIN_XSIZE = new_win_xsize;
9743 WIN_YSIZE = new_win_ysize;
9745 init_video_buffer = TRUE;
9746 init_gfx_buffers = TRUE;
9747 init_gadgets_and_anims = TRUE;
9749 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9752 if (new_scr_fieldx != SCR_FIELDX ||
9753 new_scr_fieldy != SCR_FIELDY)
9755 // this always toggles between MAIN and GAME when using small tile size
9757 SCR_FIELDX = new_scr_fieldx;
9758 SCR_FIELDY = new_scr_fieldy;
9760 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9771 new_sxsize != SXSIZE ||
9772 new_sysize != SYSIZE ||
9773 new_dxsize != DXSIZE ||
9774 new_dysize != DYSIZE ||
9775 new_vxsize != VXSIZE ||
9776 new_vysize != VYSIZE ||
9777 new_exsize != EXSIZE ||
9778 new_eysize != EYSIZE ||
9779 new_real_sx != REAL_SX ||
9780 new_real_sy != REAL_SY ||
9781 new_full_sxsize != FULL_SXSIZE ||
9782 new_full_sysize != FULL_SYSIZE ||
9783 new_tilesize_var != TILESIZE_VAR
9786 // ------------------------------------------------------------------------
9787 // determine next fading area for changed viewport definitions
9788 // ------------------------------------------------------------------------
9790 // start with current playfield area (default fading area)
9793 FADE_SXSIZE = FULL_SXSIZE;
9794 FADE_SYSIZE = FULL_SYSIZE;
9796 // add new playfield area if position or size has changed
9797 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9798 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9800 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9801 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9804 // add current and new door 1 area if position or size has changed
9805 if (new_dx != DX || new_dy != DY ||
9806 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9808 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9809 DX, DY, DXSIZE, DYSIZE);
9810 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9811 new_dx, new_dy, new_dxsize, new_dysize);
9814 // add current and new door 2 area if position or size has changed
9815 if (new_vx != VX || new_vy != VY ||
9816 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9818 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9819 VX, VY, VXSIZE, VYSIZE);
9820 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9821 new_vx, new_vy, new_vxsize, new_vysize);
9824 // ------------------------------------------------------------------------
9825 // handle changed tile size
9826 // ------------------------------------------------------------------------
9828 if (new_tilesize_var != TILESIZE_VAR)
9830 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9832 // changing tile size invalidates scroll values of engine snapshots
9833 FreeEngineSnapshotSingle();
9835 // changing tile size requires update of graphic mapping for EM engine
9836 init_em_graphics = TRUE;
9847 SXSIZE = new_sxsize;
9848 SYSIZE = new_sysize;
9849 DXSIZE = new_dxsize;
9850 DYSIZE = new_dysize;
9851 VXSIZE = new_vxsize;
9852 VYSIZE = new_vysize;
9853 EXSIZE = new_exsize;
9854 EYSIZE = new_eysize;
9855 REAL_SX = new_real_sx;
9856 REAL_SY = new_real_sy;
9857 FULL_SXSIZE = new_full_sxsize;
9858 FULL_SYSIZE = new_full_sysize;
9859 TILESIZE_VAR = new_tilesize_var;
9861 init_gfx_buffers = TRUE;
9862 init_gadgets_and_anims = TRUE;
9864 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9865 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9868 if (init_gfx_buffers)
9870 // Debug("tools:viewport", "init_gfx_buffers");
9872 SCR_FIELDX = new_scr_fieldx_buffers;
9873 SCR_FIELDY = new_scr_fieldy_buffers;
9877 SCR_FIELDX = new_scr_fieldx;
9878 SCR_FIELDY = new_scr_fieldy;
9880 SetDrawDeactivationMask(REDRAW_NONE);
9881 SetDrawBackgroundMask(REDRAW_FIELD);
9884 if (init_video_buffer)
9886 // Debug("tools:viewport", "init_video_buffer");
9888 FreeAllImageTextures(); // needs old renderer to free the textures
9890 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9891 InitImageTextures();
9894 if (init_gadgets_and_anims)
9896 // Debug("tools:viewport", "init_gadgets_and_anims");
9899 InitGlobalAnimations();
9902 if (init_em_graphics)
9904 InitGraphicInfo_EM();