1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 // currently not available
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 // never draw masked screen borders on borderless screens
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD(void)
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void DrawTileCursor(int draw_target)
634 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
639 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
644 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
647 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
652 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653 BlitScreenToBitmap_EM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655 BlitScreenToBitmap_SP(target_bitmap);
656 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657 BlitScreenToBitmap_MM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659 BlitScreenToBitmap_RND(target_bitmap);
661 redraw_mask |= REDRAW_FIELD;
664 static void DrawFramesPerSecond(void)
667 int font_nr = FONT_TEXT_2;
668 int font_width = getFontWidth(font_nr);
669 int draw_deactivation_mask = GetDrawDeactivationMask();
670 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
672 // draw FPS with leading space (needed if field buffer deactivated)
673 sprintf(text, " %04.1f fps", global.frames_per_second);
675 // override draw deactivation mask (required for invisible warp mode)
676 SetDrawDeactivationMask(REDRAW_NONE);
678 // draw opaque FPS if field buffer deactivated, else draw masked FPS
679 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
682 // set draw deactivation mask to previous value
683 SetDrawDeactivationMask(draw_deactivation_mask);
685 // force full-screen redraw in this frame
686 redraw_mask = REDRAW_ALL;
690 static void PrintFrameTimeDebugging(void)
692 static unsigned int last_counter = 0;
693 unsigned int counter = Counter();
694 int diff_1 = counter - last_counter;
695 int diff_2 = diff_1 - GAME_FRAME_DELAY;
697 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698 char diff_bar[2 * diff_2_max + 5];
702 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
704 for (i = 0; i < diff_2_max; i++)
705 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706 i >= diff_2_max - diff_2_cut ? '-' : ' ');
708 diff_bar[pos++] = '|';
710 for (i = 0; i < diff_2_max; i++)
711 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
713 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
715 diff_bar[pos++] = '\0';
717 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
720 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
723 last_counter = counter;
727 static int unifiedRedrawMask(int mask)
729 if (mask & REDRAW_ALL)
732 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
740 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
743 void BackToFront(void)
745 static int last_redraw_mask = REDRAW_NONE;
747 // force screen redraw in every frame to continue drawing global animations
748 // (but always use the last redraw mask to prevent unwanted side effects)
749 if (redraw_mask == REDRAW_NONE)
750 redraw_mask = last_redraw_mask;
752 last_redraw_mask = redraw_mask;
755 // masked border now drawn immediately when blitting backbuffer to window
757 // draw masked border to all viewports, if defined
758 DrawMaskedBorder(redraw_mask);
761 // draw frames per second (only if debug mode is enabled)
762 if (redraw_mask & REDRAW_FPS)
763 DrawFramesPerSecond();
765 // remove playfield redraw before potentially merging with doors redraw
766 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767 redraw_mask &= ~REDRAW_FIELD;
769 // redraw complete window if both playfield and (some) doors need redraw
770 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771 redraw_mask = REDRAW_ALL;
773 /* although redrawing the whole window would be fine for normal gameplay,
774 being able to only redraw the playfield is required for deactivating
775 certain drawing areas (mainly playfield) to work, which is needed for
776 warp-forward to be fast enough (by skipping redraw of most frames) */
778 if (redraw_mask & REDRAW_ALL)
780 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
782 else if (redraw_mask & REDRAW_FIELD)
784 BlitBitmap(backbuffer, window,
785 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
787 else if (redraw_mask & REDRAW_DOORS)
789 // merge door areas to prevent calling screen redraw more than once
795 if (redraw_mask & REDRAW_DOOR_1)
799 x2 = MAX(x2, DX + DXSIZE);
800 y2 = MAX(y2, DY + DYSIZE);
803 if (redraw_mask & REDRAW_DOOR_2)
807 x2 = MAX(x2, VX + VXSIZE);
808 y2 = MAX(y2, VY + VYSIZE);
811 if (redraw_mask & REDRAW_DOOR_3)
815 x2 = MAX(x2, EX + EXSIZE);
816 y2 = MAX(y2, EY + EYSIZE);
819 // make sure that at least one pixel is blitted, and inside the screen
820 // (else nothing is blitted, causing the animations not to be updated)
821 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823 x2 = MIN(MAX(1, x2), WIN_XSIZE);
824 y2 = MIN(MAX(1, y2), WIN_YSIZE);
826 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
829 redraw_mask = REDRAW_NONE;
832 PrintFrameTimeDebugging();
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
838 unsigned int frame_delay_value_old = GetVideoFrameDelay();
840 SetVideoFrameDelay(frame_delay_value);
844 SetVideoFrameDelay(frame_delay_value_old);
847 static int fade_type_skip = FADE_TYPE_NONE;
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
851 void (*draw_border_function)(void) = NULL;
852 int x, y, width, height;
853 int fade_delay, post_delay;
855 if (fade_type == FADE_TYPE_FADE_OUT)
857 if (fade_type_skip != FADE_TYPE_NONE)
859 // skip all fade operations until specified fade operation
860 if (fade_type & fade_type_skip)
861 fade_type_skip = FADE_TYPE_NONE;
866 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
870 redraw_mask |= fade_mask;
872 if (fade_type == FADE_TYPE_SKIP)
874 fade_type_skip = fade_mode;
879 fade_delay = fading.fade_delay;
880 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
882 if (fade_type_skip != FADE_TYPE_NONE)
884 // skip all fade operations until specified fade operation
885 if (fade_type & fade_type_skip)
886 fade_type_skip = FADE_TYPE_NONE;
891 if (global.autoplay_leveldir)
896 if (fade_mask == REDRAW_FIELD)
901 height = FADE_SYSIZE;
903 if (border.draw_masked_when_fading)
904 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
906 DrawMaskedBorder_FIELD(); // draw once
916 // when switching screens without fading, set fade delay to zero
917 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
920 // do not display black frame when fading out without fade delay
921 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
924 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925 draw_border_function);
927 redraw_mask &= ~fade_mask;
929 ClearAutoRepeatKeyEvents();
932 static void SetScreenStates_BeforeFadingIn(void)
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
940 // set screen mode for animations back to fading
941 global.anim_status = GAME_MODE_PSEUDO_FADING;
944 static void SetScreenStates_AfterFadingIn(void)
946 // store new source screen (to use correct masked border for fading)
947 gfx.fade_border_source_status = global.border_status;
949 global.anim_status = global.anim_status_next;
952 static void SetScreenStates_BeforeFadingOut(void)
954 // store new target screen (to use correct masked border for fading)
955 gfx.fade_border_target_status = game_status;
957 // set screen mode for animations to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 // store backbuffer with all animations that will be stopped for fading out
961 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
964 static void SetScreenStates_AfterFadingOut(void)
966 global.border_status = game_status;
969 void FadeIn(int fade_mask)
971 SetScreenStates_BeforeFadingIn();
974 DrawMaskedBorder(REDRAW_ALL);
977 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
980 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
984 FADE_SXSIZE = FULL_SXSIZE;
985 FADE_SYSIZE = FULL_SYSIZE;
987 // activate virtual buttons depending on upcoming game status
988 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989 game_status == GAME_MODE_PLAYING && !tape.playing)
990 SetOverlayActive(TRUE);
992 SetScreenStates_AfterFadingIn();
994 // force update of global animation status in case of rapid screen changes
995 redraw_mask = REDRAW_ALL;
999 void FadeOut(int fade_mask)
1001 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1006 SetScreenStates_BeforeFadingOut();
1008 SetTileCursorActive(FALSE);
1009 SetOverlayActive(FALSE);
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1018 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1020 SetScreenStates_AfterFadingOut();
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1025 static struct TitleFadingInfo fading_leave_stored;
1028 fading_leave_stored = fading_leave;
1030 fading = fading_leave_stored;
1033 void FadeSetEnterMenu(void)
1035 fading = menu.enter_menu;
1037 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1040 void FadeSetLeaveMenu(void)
1042 fading = menu.leave_menu;
1044 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetEnterScreen(void)
1049 fading = menu.enter_screen[game_status];
1051 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1054 void FadeSetNextScreen(void)
1056 fading = menu.next_screen[game_status];
1058 // (do not overwrite fade mode set by FadeSetEnterScreen)
1059 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1062 void FadeSetLeaveScreen(void)
1064 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1067 void FadeSetFromType(int type)
1069 if (type & TYPE_ENTER_SCREEN)
1070 FadeSetEnterScreen();
1071 else if (type & TYPE_ENTER)
1073 else if (type & TYPE_LEAVE)
1077 void FadeSetDisabled(void)
1079 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1081 fading = fading_none;
1084 void FadeSkipNextFadeIn(void)
1086 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1089 void FadeSkipNextFadeOut(void)
1091 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1096 if (graphic == IMG_UNDEFINED)
1099 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1101 return (graphic_info[graphic].bitmap != NULL || redefined ?
1102 graphic_info[graphic].bitmap :
1103 graphic_info[default_graphic].bitmap);
1106 static Bitmap *getBackgroundBitmap(int graphic)
1108 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1113 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1119 (status == GAME_MODE_MAIN ||
1120 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1121 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1122 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1123 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1126 return getGlobalBorderBitmap(graphic);
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1131 if (graphic_info[graphic].bitmap)
1132 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1135 void SetMainBackgroundImageIfDefined(int graphic)
1137 if (graphic_info[graphic].bitmap)
1138 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1143 if (graphic_info[graphic].bitmap)
1144 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1147 void SetWindowBackgroundImage(int graphic)
1149 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetMainBackgroundImage(int graphic)
1154 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetDoorBackgroundImage(int graphic)
1159 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetPanelBackground(void)
1164 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1166 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1169 SetDoorBackgroundBitmap(bitmap_db_panel);
1172 void DrawBackground(int x, int y, int width, int height)
1174 // "drawto" might still point to playfield buffer here (hall of fame)
1175 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1177 if (IN_GFX_FIELD_FULL(x, y))
1178 redraw_mask |= REDRAW_FIELD;
1179 else if (IN_GFX_DOOR_1(x, y))
1180 redraw_mask |= REDRAW_DOOR_1;
1181 else if (IN_GFX_DOOR_2(x, y))
1182 redraw_mask |= REDRAW_DOOR_2;
1183 else if (IN_GFX_DOOR_3(x, y))
1184 redraw_mask |= REDRAW_DOOR_3;
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1189 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1191 if (font->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1199 struct GraphicInfo *g = &graphic_info[graphic];
1201 if (g->bitmap == NULL)
1204 DrawBackground(x, y, width, height);
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1219 boolean CheckIfGlobalBorderHasChanged(void)
1221 // if game status has not changed, global border has not changed either
1222 if (game_status == game_status_last)
1225 // determine and store new global border bitmap for current game status
1226 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1228 return (global_border_bitmap_last != global_border_bitmap);
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1236 // if game status has not changed, nothing has to be redrawn
1237 if (game_status == game_status_last)
1240 // redraw if last screen was title screen
1241 if (game_status_last == GAME_MODE_TITLE)
1244 // redraw if global screen border has changed
1245 if (CheckIfGlobalBorderHasChanged())
1248 // redraw if position or size of playfield area has changed
1249 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1253 // redraw if position or size of door area has changed
1254 if (dx_last != DX || dy_last != DY ||
1255 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1258 // redraw if position or size of tape area has changed
1259 if (vx_last != VX || vy_last != VY ||
1260 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1263 // redraw if position or size of editor area has changed
1264 if (ex_last != EX || ey_last != EY ||
1265 exsize_last != EXSIZE || eysize_last != EYSIZE)
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1275 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1277 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1280 void RedrawGlobalBorder(void)
1282 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 RedrawGlobalBorderFromBitmap(bitmap);
1286 redraw_mask = REDRAW_ALL;
1289 static void RedrawGlobalBorderIfNeeded(void)
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 if (game_status == game_status_last)
1296 // copy current draw buffer to later copy back areas that have not changed
1297 if (game_status_last != GAME_MODE_TITLE)
1298 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301 if (CheckIfGlobalBorderRedrawIsNeeded())
1303 // determine and store new global border bitmap for current game status
1304 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1307 // redraw global screen border (or clear, if defined to be empty)
1308 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1310 if (game_status == GAME_MODE_EDITOR)
1311 DrawSpecialEditorDoor();
1313 // copy previous playfield and door areas, if they are defined on both
1314 // previous and current screen and if they still have the same size
1316 if (real_sx_last != -1 && real_sy_last != -1 &&
1317 REAL_SX != -1 && REAL_SY != -1 &&
1318 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1323 if (dx_last != -1 && dy_last != -1 &&
1324 DX != -1 && DY != -1 &&
1325 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326 BlitBitmap(bitmap_db_store_1, backbuffer,
1327 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1329 if (game_status != GAME_MODE_EDITOR)
1331 if (vx_last != -1 && vy_last != -1 &&
1332 VX != -1 && VY != -1 &&
1333 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334 BlitBitmap(bitmap_db_store_1, backbuffer,
1335 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1339 if (ex_last != -1 && ey_last != -1 &&
1340 EX != -1 && EY != -1 &&
1341 exsize_last == EXSIZE && eysize_last == EYSIZE)
1342 BlitBitmap(bitmap_db_store_1, backbuffer,
1343 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1346 redraw_mask = REDRAW_ALL;
1349 game_status_last = game_status;
1351 global_border_bitmap_last = global_border_bitmap;
1353 real_sx_last = REAL_SX;
1354 real_sy_last = REAL_SY;
1355 full_sxsize_last = FULL_SXSIZE;
1356 full_sysize_last = FULL_SYSIZE;
1359 dxsize_last = DXSIZE;
1360 dysize_last = DYSIZE;
1363 vxsize_last = VXSIZE;
1364 vysize_last = VYSIZE;
1367 exsize_last = EXSIZE;
1368 eysize_last = EYSIZE;
1371 void ClearField(void)
1373 RedrawGlobalBorderIfNeeded();
1375 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376 // (when entering hall of fame after playing)
1377 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1379 // !!! maybe this should be done before clearing the background !!!
1380 if (game_status == GAME_MODE_PLAYING)
1382 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1387 SetDrawtoField(DRAW_TO_BACKBUFFER);
1391 void MarkTileDirty(int x, int y)
1393 redraw_mask |= REDRAW_FIELD;
1396 void SetBorderElement(void)
1400 BorderElement = EL_EMPTY;
1402 // only the R'n'D game engine may use an additional steelwall border
1403 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1406 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1408 for (x = 0; x < lev_fieldx; x++)
1410 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411 BorderElement = EL_STEELWALL;
1413 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1419 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1420 int max_array_fieldx, int max_array_fieldy,
1421 short field[max_array_fieldx][max_array_fieldy],
1422 int max_fieldx, int max_fieldy)
1426 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1427 static int safety = 0;
1429 // check if starting field still has the desired content
1430 if (field[from_x][from_y] == fill_element)
1435 if (safety > max_fieldx * max_fieldy)
1436 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1438 old_element = field[from_x][from_y];
1439 field[from_x][from_y] = fill_element;
1441 for (i = 0; i < 4; i++)
1443 x = from_x + check[i][0];
1444 y = from_y + check[i][1];
1446 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1447 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1448 field, max_fieldx, max_fieldy);
1454 void FloodFillLevel(int from_x, int from_y, int fill_element,
1455 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1456 int max_fieldx, int max_fieldy)
1458 FloodFillLevelExt(from_x, from_y, fill_element,
1459 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1460 max_fieldx, max_fieldy);
1463 void SetRandomAnimationValue(int x, int y)
1465 gfx.anim_random_frame = GfxRandom[x][y];
1468 int getGraphicAnimationFrame(int graphic, int sync_frame)
1470 // animation synchronized with global frame counter, not move position
1471 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1472 sync_frame = FrameCounter;
1474 return getAnimationFrame(graphic_info[graphic].anim_frames,
1475 graphic_info[graphic].anim_delay,
1476 graphic_info[graphic].anim_mode,
1477 graphic_info[graphic].anim_start_frame,
1481 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1483 struct GraphicInfo *g = &graphic_info[graphic];
1484 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1486 if (tilesize == gfx.standard_tile_size)
1487 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1488 else if (tilesize == game.tile_size)
1489 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1491 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1494 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1495 boolean get_backside)
1497 struct GraphicInfo *g = &graphic_info[graphic];
1498 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1499 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1501 if (g->offset_y == 0) // frames are ordered horizontally
1503 int max_width = g->anim_frames_per_line * g->width;
1504 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1506 *x = pos % max_width;
1507 *y = src_y % g->height + pos / max_width * g->height;
1509 else if (g->offset_x == 0) // frames are ordered vertically
1511 int max_height = g->anim_frames_per_line * g->height;
1512 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1514 *x = src_x % g->width + pos / max_height * g->width;
1515 *y = pos % max_height;
1517 else // frames are ordered diagonally
1519 *x = src_x + frame * g->offset_x;
1520 *y = src_y + frame * g->offset_y;
1524 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1525 Bitmap **bitmap, int *x, int *y,
1526 boolean get_backside)
1528 struct GraphicInfo *g = &graphic_info[graphic];
1530 // if no graphics defined at all, use fallback graphics
1531 if (g->bitmaps == NULL)
1532 *g = graphic_info[IMG_CHAR_EXCLAM];
1534 // if no in-game graphics defined, always use standard graphic size
1535 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1536 tilesize = TILESIZE;
1538 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1539 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1541 *x = *x * tilesize / g->tile_size;
1542 *y = *y * tilesize / g->tile_size;
1545 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1546 Bitmap **bitmap, int *x, int *y)
1548 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1551 void getFixedGraphicSource(int graphic, int frame,
1552 Bitmap **bitmap, int *x, int *y)
1554 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1557 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1559 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1562 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1563 int *x, int *y, boolean get_backside)
1565 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1569 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1571 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1574 void DrawGraphic(int x, int y, int graphic, int frame)
1577 if (!IN_SCR_FIELD(x, y))
1579 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1580 Debug("draw:DrawGraphic", "This should never happen!");
1586 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1589 MarkTileDirty(x, y);
1592 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1595 if (!IN_SCR_FIELD(x, y))
1597 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1599 Debug("draw:DrawFixedGraphic", "This should never happen!");
1605 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1607 MarkTileDirty(x, y);
1610 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1616 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1618 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1621 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1627 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1628 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1631 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1634 if (!IN_SCR_FIELD(x, y))
1636 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1638 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1644 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1647 MarkTileDirty(x, y);
1650 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1653 if (!IN_SCR_FIELD(x, y))
1655 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1657 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1663 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1665 MarkTileDirty(x, y);
1668 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1674 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1676 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1680 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1681 int graphic, int frame)
1686 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1688 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1692 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1694 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1696 MarkTileDirty(x / tilesize, y / tilesize);
1699 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1702 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1703 graphic, frame, tilesize);
1704 MarkTileDirty(x / tilesize, y / tilesize);
1707 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1713 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1714 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1717 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1718 int frame, int tilesize)
1723 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1724 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1727 void DrawMiniGraphic(int x, int y, int graphic)
1729 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1730 MarkTileDirty(x / 2, y / 2);
1733 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1738 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1739 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1742 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1743 int graphic, int frame,
1744 int cut_mode, int mask_mode)
1749 int width = TILEX, height = TILEY;
1752 if (dx || dy) // shifted graphic
1754 if (x < BX1) // object enters playfield from the left
1761 else if (x > BX2) // object enters playfield from the right
1767 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1773 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1775 else if (dx) // general horizontal movement
1776 MarkTileDirty(x + SIGN(dx), y);
1778 if (y < BY1) // object enters playfield from the top
1780 if (cut_mode == CUT_BELOW) // object completely above top border
1788 else if (y > BY2) // object enters playfield from the bottom
1794 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1800 else if (dy > 0 && cut_mode == CUT_ABOVE)
1802 if (y == BY2) // object completely above bottom border
1808 MarkTileDirty(x, y + 1);
1809 } // object leaves playfield to the bottom
1810 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1812 else if (dy) // general vertical movement
1813 MarkTileDirty(x, y + SIGN(dy));
1817 if (!IN_SCR_FIELD(x, y))
1819 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1821 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1827 width = width * TILESIZE_VAR / TILESIZE;
1828 height = height * TILESIZE_VAR / TILESIZE;
1829 cx = cx * TILESIZE_VAR / TILESIZE;
1830 cy = cy * TILESIZE_VAR / TILESIZE;
1831 dx = dx * TILESIZE_VAR / TILESIZE;
1832 dy = dy * TILESIZE_VAR / TILESIZE;
1834 if (width > 0 && height > 0)
1836 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1841 dst_x = FX + x * TILEX_VAR + dx;
1842 dst_y = FY + y * TILEY_VAR + dy;
1844 if (mask_mode == USE_MASKING)
1845 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1848 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1851 MarkTileDirty(x, y);
1855 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1856 int graphic, int frame,
1857 int cut_mode, int mask_mode)
1862 int width = TILEX_VAR, height = TILEY_VAR;
1865 int x2 = x + SIGN(dx);
1866 int y2 = y + SIGN(dy);
1868 // movement with two-tile animations must be sync'ed with movement position,
1869 // not with current GfxFrame (which can be higher when using slow movement)
1870 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1871 int anim_frames = graphic_info[graphic].anim_frames;
1873 // (we also need anim_delay here for movement animations with less frames)
1874 int anim_delay = graphic_info[graphic].anim_delay;
1875 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1877 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1878 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1880 // re-calculate animation frame for two-tile movement animation
1881 frame = getGraphicAnimationFrame(graphic, sync_frame);
1883 // check if movement start graphic inside screen area and should be drawn
1884 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1886 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1888 dst_x = FX + x1 * TILEX_VAR;
1889 dst_y = FY + y1 * TILEY_VAR;
1891 if (mask_mode == USE_MASKING)
1892 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1895 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1898 MarkTileDirty(x1, y1);
1901 // check if movement end graphic inside screen area and should be drawn
1902 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1904 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1906 dst_x = FX + x2 * TILEX_VAR;
1907 dst_y = FY + y2 * TILEY_VAR;
1909 if (mask_mode == USE_MASKING)
1910 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1913 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1916 MarkTileDirty(x2, y2);
1920 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1921 int graphic, int frame,
1922 int cut_mode, int mask_mode)
1926 DrawGraphic(x, y, graphic, frame);
1931 if (graphic_info[graphic].double_movement) // EM style movement images
1932 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1934 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1937 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1938 int graphic, int frame, int cut_mode)
1940 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1943 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1944 int cut_mode, int mask_mode)
1946 int lx = LEVELX(x), ly = LEVELY(y);
1950 if (IN_LEV_FIELD(lx, ly))
1952 SetRandomAnimationValue(lx, ly);
1954 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1955 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1957 // do not use double (EM style) movement graphic when not moving
1958 if (graphic_info[graphic].double_movement && !dx && !dy)
1960 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1961 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1964 if (game.use_masked_elements && (dx || dy))
1965 mask_mode = USE_MASKING;
1967 else // border element
1969 graphic = el2img(element);
1970 frame = getGraphicAnimationFrame(graphic, -1);
1973 if (element == EL_EXPANDABLE_WALL)
1975 boolean left_stopped = FALSE, right_stopped = FALSE;
1977 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1978 left_stopped = TRUE;
1979 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
1980 right_stopped = TRUE;
1982 if (left_stopped && right_stopped)
1984 else if (left_stopped)
1986 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1987 frame = graphic_info[graphic].anim_frames - 1;
1989 else if (right_stopped)
1991 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1992 frame = graphic_info[graphic].anim_frames - 1;
1997 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1998 else if (mask_mode == USE_MASKING)
1999 DrawGraphicThruMask(x, y, graphic, frame);
2001 DrawGraphic(x, y, graphic, frame);
2004 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2005 int cut_mode, int mask_mode)
2007 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2008 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2009 cut_mode, mask_mode);
2012 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2015 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2018 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2021 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2024 void DrawLevelElementThruMask(int x, int y, int element)
2026 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2029 void DrawLevelFieldThruMask(int x, int y)
2031 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2034 // !!! implementation of quicksand is totally broken !!!
2035 #define IS_CRUMBLED_TILE(x, y, e) \
2036 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2037 !IS_MOVING(x, y) || \
2038 (e) == EL_QUICKSAND_EMPTYING || \
2039 (e) == EL_QUICKSAND_FAST_EMPTYING))
2041 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2046 int width, height, cx, cy;
2047 int sx = SCREENX(x), sy = SCREENY(y);
2048 int crumbled_border_size = graphic_info[graphic].border_size;
2049 int crumbled_tile_size = graphic_info[graphic].tile_size;
2050 int crumbled_border_size_var =
2051 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2054 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2056 for (i = 1; i < 4; i++)
2058 int dxx = (i & 1 ? dx : 0);
2059 int dyy = (i & 2 ? dy : 0);
2062 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2065 // check if neighbour field is of same crumble type
2066 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2067 graphic_info[graphic].class ==
2068 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2070 // return if check prevents inner corner
2071 if (same == (dxx == dx && dyy == dy))
2075 // if we reach this point, we have an inner corner
2077 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2079 width = crumbled_border_size_var;
2080 height = crumbled_border_size_var;
2081 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2082 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2084 if (game.use_masked_elements)
2086 int graphic0 = el2img(EL_EMPTY);
2087 int frame0 = getGraphicAnimationFrame(graphic0, GfxFrame[x][y]);
2088 Bitmap *src_bitmap0;
2091 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2093 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2095 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2097 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2099 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2102 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2104 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2107 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2112 int width, height, bx, by, cx, cy;
2113 int sx = SCREENX(x), sy = SCREENY(y);
2114 int crumbled_border_size = graphic_info[graphic].border_size;
2115 int crumbled_tile_size = graphic_info[graphic].tile_size;
2116 int crumbled_border_size_var =
2117 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2118 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2121 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2123 // only needed when using masked elements
2124 int graphic0 = el2img(EL_EMPTY);
2125 int frame0 = getGraphicAnimationFrame(graphic0, GfxFrame[x][y]);
2126 Bitmap *src_bitmap0;
2129 if (game.use_masked_elements)
2130 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2132 // draw simple, sloppy, non-corner-accurate crumbled border
2134 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2135 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2136 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2137 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2139 if (game.use_masked_elements)
2141 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2143 FX + sx * TILEX_VAR + cx,
2144 FY + sy * TILEY_VAR + cy);
2146 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2148 FX + sx * TILEX_VAR + cx,
2149 FY + sy * TILEY_VAR + cy);
2152 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2154 FX + sx * TILEX_VAR + cx,
2155 FY + sy * TILEY_VAR + cy);
2157 // (remaining middle border part must be at least as big as corner part)
2158 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2159 crumbled_border_size_var >= TILESIZE_VAR / 3)
2162 // correct corners of crumbled border, if needed
2164 for (i = -1; i <= 1; i += 2)
2166 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2167 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2168 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2171 // check if neighbour field is of same crumble type
2172 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2173 graphic_info[graphic].class ==
2174 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2176 // no crumbled corner, but continued crumbled border
2178 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2179 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2180 int b1 = (i == 1 ? crumbled_border_size_var :
2181 TILESIZE_VAR - 2 * crumbled_border_size_var);
2183 width = crumbled_border_size_var;
2184 height = crumbled_border_size_var;
2186 if (dir == 1 || dir == 2)
2201 if (game.use_masked_elements)
2203 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2205 FX + sx * TILEX_VAR + cx,
2206 FY + sy * TILEY_VAR + cy);
2208 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2210 FX + sx * TILEX_VAR + cx,
2211 FY + sy * TILEY_VAR + cy);
2214 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2216 FX + sx * TILEX_VAR + cx,
2217 FY + sy * TILEY_VAR + cy);
2222 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2224 int sx = SCREENX(x), sy = SCREENY(y);
2227 static int xy[4][2] =
2235 if (!IN_LEV_FIELD(x, y))
2238 element = TILE_GFX_ELEMENT(x, y);
2240 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2242 if (!IN_SCR_FIELD(sx, sy))
2245 // crumble field borders towards direct neighbour fields
2246 for (i = 0; i < 4; i++)
2248 int xx = x + xy[i][0];
2249 int yy = y + xy[i][1];
2251 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2254 // check if neighbour field is of same crumble type
2255 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2256 graphic_info[graphic].class ==
2257 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2260 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2263 // crumble inner field corners towards corner neighbour fields
2264 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2265 graphic_info[graphic].anim_frames == 2)
2267 for (i = 0; i < 4; i++)
2269 int dx = (i & 1 ? +1 : -1);
2270 int dy = (i & 2 ? +1 : -1);
2272 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2276 MarkTileDirty(sx, sy);
2278 else // center field is not crumbled -- crumble neighbour fields
2280 // crumble field borders of direct neighbour fields
2281 for (i = 0; i < 4; i++)
2283 int xx = x + xy[i][0];
2284 int yy = y + xy[i][1];
2285 int sxx = sx + xy[i][0];
2286 int syy = sy + xy[i][1];
2288 if (!IN_LEV_FIELD(xx, yy) ||
2289 !IN_SCR_FIELD(sxx, syy))
2292 // do not crumble fields that are being digged or snapped
2293 if (Tile[xx][yy] == EL_EMPTY ||
2294 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2297 element = TILE_GFX_ELEMENT(xx, yy);
2299 if (!IS_CRUMBLED_TILE(xx, yy, element))
2302 graphic = el_act2crm(element, ACTION_DEFAULT);
2304 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2306 MarkTileDirty(sxx, syy);
2309 // crumble inner field corners of corner neighbour fields
2310 for (i = 0; i < 4; i++)
2312 int dx = (i & 1 ? +1 : -1);
2313 int dy = (i & 2 ? +1 : -1);
2319 if (!IN_LEV_FIELD(xx, yy) ||
2320 !IN_SCR_FIELD(sxx, syy))
2323 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2326 element = TILE_GFX_ELEMENT(xx, yy);
2328 if (!IS_CRUMBLED_TILE(xx, yy, element))
2331 graphic = el_act2crm(element, ACTION_DEFAULT);
2333 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2334 graphic_info[graphic].anim_frames == 2)
2335 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2337 MarkTileDirty(sxx, syy);
2342 void DrawLevelFieldCrumbled(int x, int y)
2346 if (!IN_LEV_FIELD(x, y))
2349 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2350 GfxElement[x][y] != EL_UNDEFINED &&
2351 GFX_CRUMBLED(GfxElement[x][y]))
2353 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2358 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2360 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2363 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2366 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2367 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2368 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2369 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2370 int sx = SCREENX(x), sy = SCREENY(y);
2372 DrawScreenGraphic(sx, sy, graphic1, frame1);
2373 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2376 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2378 int sx = SCREENX(x), sy = SCREENY(y);
2379 static int xy[4][2] =
2388 // crumble direct neighbour fields (required for field borders)
2389 for (i = 0; i < 4; i++)
2391 int xx = x + xy[i][0];
2392 int yy = y + xy[i][1];
2393 int sxx = sx + xy[i][0];
2394 int syy = sy + xy[i][1];
2396 if (!IN_LEV_FIELD(xx, yy) ||
2397 !IN_SCR_FIELD(sxx, syy) ||
2398 !GFX_CRUMBLED(Tile[xx][yy]) ||
2402 DrawLevelField(xx, yy);
2405 // crumble corner neighbour fields (required for inner field corners)
2406 for (i = 0; i < 4; i++)
2408 int dx = (i & 1 ? +1 : -1);
2409 int dy = (i & 2 ? +1 : -1);
2415 if (!IN_LEV_FIELD(xx, yy) ||
2416 !IN_SCR_FIELD(sxx, syy) ||
2417 !GFX_CRUMBLED(Tile[xx][yy]) ||
2421 int element = TILE_GFX_ELEMENT(xx, yy);
2422 int graphic = el_act2crm(element, ACTION_DEFAULT);
2424 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2425 graphic_info[graphic].anim_frames == 2)
2426 DrawLevelField(xx, yy);
2430 static int getBorderElement(int x, int y)
2434 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2435 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2436 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2437 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2438 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2439 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2440 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2442 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2443 int steel_position = (x == -1 && y == -1 ? 0 :
2444 x == lev_fieldx && y == -1 ? 1 :
2445 x == -1 && y == lev_fieldy ? 2 :
2446 x == lev_fieldx && y == lev_fieldy ? 3 :
2447 x == -1 || x == lev_fieldx ? 4 :
2448 y == -1 || y == lev_fieldy ? 5 : 6);
2450 return border[steel_position][steel_type];
2453 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2455 if (game.use_masked_elements)
2457 if (graphic != el2img(EL_EMPTY))
2458 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2460 DrawGraphicThruMask(x, y, graphic, frame);
2464 DrawGraphic(x, y, graphic, frame);
2468 void DrawScreenElement(int x, int y, int element)
2470 int mask_mode = NO_MASKING;
2472 if (game.use_masked_elements)
2474 int lx = LEVELX(x), ly = LEVELY(y);
2476 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2478 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2480 mask_mode = USE_MASKING;
2484 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2485 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2488 void DrawLevelElement(int x, int y, int element)
2490 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2491 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2494 void DrawScreenField(int x, int y)
2496 int lx = LEVELX(x), ly = LEVELY(y);
2497 int element, content;
2499 if (!IN_LEV_FIELD(lx, ly))
2501 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2504 element = getBorderElement(lx, ly);
2506 DrawScreenElement(x, y, element);
2511 element = Tile[lx][ly];
2512 content = Store[lx][ly];
2514 if (IS_MOVING(lx, ly))
2516 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2517 boolean cut_mode = NO_CUTTING;
2519 if (element == EL_QUICKSAND_EMPTYING ||
2520 element == EL_QUICKSAND_FAST_EMPTYING ||
2521 element == EL_MAGIC_WALL_EMPTYING ||
2522 element == EL_BD_MAGIC_WALL_EMPTYING ||
2523 element == EL_DC_MAGIC_WALL_EMPTYING ||
2524 element == EL_AMOEBA_DROPPING)
2525 cut_mode = CUT_ABOVE;
2526 else if (element == EL_QUICKSAND_FILLING ||
2527 element == EL_QUICKSAND_FAST_FILLING ||
2528 element == EL_MAGIC_WALL_FILLING ||
2529 element == EL_BD_MAGIC_WALL_FILLING ||
2530 element == EL_DC_MAGIC_WALL_FILLING)
2531 cut_mode = CUT_BELOW;
2533 if (cut_mode == CUT_ABOVE)
2534 DrawScreenElement(x, y, element);
2536 DrawScreenElement(x, y, EL_EMPTY);
2539 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2540 else if (cut_mode == NO_CUTTING)
2541 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2544 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2546 if (cut_mode == CUT_BELOW &&
2547 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2548 DrawLevelElement(lx, ly + 1, element);
2551 if (content == EL_ACID)
2553 int dir = MovDir[lx][ly];
2554 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2555 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2557 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2559 // prevent target field from being drawn again (but without masking)
2560 // (this would happen if target field is scanned after moving element)
2561 Stop[newlx][newly] = TRUE;
2564 else if (IS_BLOCKED(lx, ly))
2569 boolean cut_mode = NO_CUTTING;
2570 int element_old, content_old;
2572 Blocked2Moving(lx, ly, &oldx, &oldy);
2575 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2576 MovDir[oldx][oldy] == MV_RIGHT);
2578 element_old = Tile[oldx][oldy];
2579 content_old = Store[oldx][oldy];
2581 if (element_old == EL_QUICKSAND_EMPTYING ||
2582 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2583 element_old == EL_MAGIC_WALL_EMPTYING ||
2584 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2585 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2586 element_old == EL_AMOEBA_DROPPING)
2587 cut_mode = CUT_ABOVE;
2589 DrawScreenElement(x, y, EL_EMPTY);
2592 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2594 else if (cut_mode == NO_CUTTING)
2595 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2598 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2601 else if (IS_DRAWABLE(element))
2602 DrawScreenElement(x, y, element);
2604 DrawScreenElement(x, y, EL_EMPTY);
2607 void DrawLevelField(int x, int y)
2609 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2610 DrawScreenField(SCREENX(x), SCREENY(y));
2611 else if (IS_MOVING(x, y))
2615 Moving2Blocked(x, y, &newx, &newy);
2616 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2617 DrawScreenField(SCREENX(newx), SCREENY(newy));
2619 else if (IS_BLOCKED(x, y))
2623 Blocked2Moving(x, y, &oldx, &oldy);
2624 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2625 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2629 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2630 int (*el2img_function)(int), boolean masked,
2631 int element_bits_draw)
2633 int element_base = map_mm_wall_element(element);
2634 int element_bits = (IS_DF_WALL(element) ?
2635 element - EL_DF_WALL_START :
2636 IS_MM_WALL(element) ?
2637 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2638 int graphic = el2img_function(element_base);
2639 int tilesize_draw = tilesize / 2;
2644 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2646 for (i = 0; i < 4; i++)
2648 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2649 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2651 if (!(element_bits_draw & (1 << i)))
2654 if (element_bits & (1 << i))
2657 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2658 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2660 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2661 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2666 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2667 tilesize_draw, tilesize_draw);
2672 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2673 boolean masked, int element_bits_draw)
2675 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2676 element, tilesize, el2edimg, masked, element_bits_draw);
2679 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2680 int (*el2img_function)(int))
2682 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2686 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2689 if (IS_MM_WALL(element))
2691 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2692 element, tilesize, el2edimg, masked, 0x000f);
2696 int graphic = el2edimg(element);
2699 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2701 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2705 void DrawSizedElement(int x, int y, int element, int tilesize)
2707 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2710 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2712 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2715 void DrawMiniElement(int x, int y, int element)
2719 graphic = el2edimg(element);
2720 DrawMiniGraphic(x, y, graphic);
2723 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2726 int x = sx + scroll_x, y = sy + scroll_y;
2728 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2729 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2730 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2731 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2733 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2736 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2738 int x = sx + scroll_x, y = sy + scroll_y;
2740 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2741 DrawMiniElement(sx, sy, EL_EMPTY);
2742 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2743 DrawMiniElement(sx, sy, Tile[x][y]);
2745 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2748 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2749 int x, int y, int xsize, int ysize,
2750 int tile_width, int tile_height)
2754 int dst_x = startx + x * tile_width;
2755 int dst_y = starty + y * tile_height;
2756 int width = graphic_info[graphic].width;
2757 int height = graphic_info[graphic].height;
2758 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2759 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2760 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2761 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2762 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2763 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2764 boolean draw_masked = graphic_info[graphic].draw_masked;
2766 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2768 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2770 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2774 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2775 inner_sx + (x - 1) * tile_width % inner_width);
2776 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2777 inner_sy + (y - 1) * tile_height % inner_height);
2780 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2783 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2787 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2788 int x, int y, int xsize, int ysize,
2791 int font_width = getFontWidth(font_nr);
2792 int font_height = getFontHeight(font_nr);
2794 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2795 font_width, font_height);
2798 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2800 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2801 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2802 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2803 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2804 boolean no_delay = (tape.warp_forward);
2805 unsigned int anim_delay = 0;
2806 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2807 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2808 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2809 int font_width = getFontWidth(font_nr);
2810 int font_height = getFontHeight(font_nr);
2811 int max_xsize = level.envelope[envelope_nr].xsize;
2812 int max_ysize = level.envelope[envelope_nr].ysize;
2813 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2814 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2815 int xend = max_xsize;
2816 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2817 int xstep = (xstart < xend ? 1 : 0);
2818 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2820 int end = MAX(xend - xstart, yend - ystart);
2823 for (i = start; i <= end; i++)
2825 int last_frame = end; // last frame of this "for" loop
2826 int x = xstart + i * xstep;
2827 int y = ystart + i * ystep;
2828 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2829 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2830 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2831 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2834 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2836 BlitScreenToBitmap(backbuffer);
2838 SetDrawtoField(DRAW_TO_BACKBUFFER);
2840 for (yy = 0; yy < ysize; yy++)
2841 for (xx = 0; xx < xsize; xx++)
2842 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2844 DrawTextBuffer(sx + font_width, sy + font_height,
2845 level.envelope[envelope_nr].text, font_nr, max_xsize,
2846 xsize - 2, ysize - 2, 0, mask_mode,
2847 level.envelope[envelope_nr].autowrap,
2848 level.envelope[envelope_nr].centered, FALSE);
2850 redraw_mask |= REDRAW_FIELD;
2853 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2856 ClearAutoRepeatKeyEvents();
2859 void ShowEnvelope(int envelope_nr)
2861 int element = EL_ENVELOPE_1 + envelope_nr;
2862 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2863 int sound_opening = element_info[element].sound[ACTION_OPENING];
2864 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2865 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2866 boolean no_delay = (tape.warp_forward);
2867 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2868 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2869 int anim_mode = graphic_info[graphic].anim_mode;
2870 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2871 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2872 boolean overlay_enabled = GetOverlayEnabled();
2874 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2876 SetOverlayEnabled(FALSE);
2879 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2881 if (anim_mode == ANIM_DEFAULT)
2882 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2884 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2887 Delay_WithScreenUpdates(wait_delay_value);
2889 WaitForEventToContinue();
2892 SetOverlayEnabled(overlay_enabled);
2894 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2896 if (anim_mode != ANIM_NONE)
2897 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2899 if (anim_mode == ANIM_DEFAULT)
2900 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2902 game.envelope_active = FALSE;
2904 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2906 redraw_mask |= REDRAW_FIELD;
2910 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2911 int xsize, int ysize)
2913 if (!global.use_envelope_request ||
2914 request.sort_priority <= 0)
2917 if (request.bitmap == NULL ||
2918 xsize > request.xsize ||
2919 ysize > request.ysize)
2921 if (request.bitmap != NULL)
2922 FreeBitmap(request.bitmap);
2924 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2926 SDL_Surface *surface = request.bitmap->surface;
2928 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2929 Fail("SDLGetNativeSurface() failed");
2932 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2934 SDLFreeBitmapTextures(request.bitmap);
2935 SDLCreateBitmapTextures(request.bitmap);
2937 // set envelope request run-time values
2940 request.xsize = xsize;
2941 request.ysize = ysize;
2944 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2946 if (global.use_envelope_request &&
2947 game.request_active_or_moving &&
2948 request.sort_priority > 0 &&
2949 drawing_target == DRAW_TO_SCREEN &&
2950 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2952 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2953 request.sx, request.sy);
2957 static void setRequestBasePosition(int *x, int *y)
2959 int sx_base, sy_base;
2961 if (request.x != -1)
2962 sx_base = request.x;
2963 else if (request.align == ALIGN_LEFT)
2965 else if (request.align == ALIGN_RIGHT)
2966 sx_base = SX + SXSIZE;
2968 sx_base = SX + SXSIZE / 2;
2970 if (request.y != -1)
2971 sy_base = request.y;
2972 else if (request.valign == VALIGN_TOP)
2974 else if (request.valign == VALIGN_BOTTOM)
2975 sy_base = SY + SYSIZE;
2977 sy_base = SY + SYSIZE / 2;
2983 static void setRequestPositionExt(int *x, int *y, int width, int height,
2984 boolean add_border_size)
2986 int border_size = request.border_size;
2987 int sx_base, sy_base;
2990 setRequestBasePosition(&sx_base, &sy_base);
2992 if (request.align == ALIGN_LEFT)
2994 else if (request.align == ALIGN_RIGHT)
2995 sx = sx_base - width;
2997 sx = sx_base - width / 2;
2999 if (request.valign == VALIGN_TOP)
3001 else if (request.valign == VALIGN_BOTTOM)
3002 sy = sy_base - height;
3004 sy = sy_base - height / 2;
3006 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3007 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3009 if (add_border_size)
3019 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3021 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3024 static void DrawEnvelopeRequest(char *text)
3026 char *text_final = text;
3027 char *text_door_style = NULL;
3028 int graphic = IMG_BACKGROUND_REQUEST;
3029 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3030 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3031 int font_nr = FONT_REQUEST;
3032 int font_width = getFontWidth(font_nr);
3033 int font_height = getFontHeight(font_nr);
3034 int border_size = request.border_size;
3035 int line_spacing = request.line_spacing;
3036 int line_height = font_height + line_spacing;
3037 int max_text_width = request.width - 2 * border_size;
3038 int max_text_height = request.height - 2 * border_size;
3039 int line_length = max_text_width / font_width;
3040 int max_lines = max_text_height / line_height;
3041 int text_width = line_length * font_width;
3042 int width = request.width;
3043 int height = request.height;
3044 int tile_size = MAX(request.step_offset, 1);
3045 int x_steps = width / tile_size;
3046 int y_steps = height / tile_size;
3047 int sx_offset = border_size;
3048 int sy_offset = border_size;
3052 if (request.centered)
3053 sx_offset = (request.width - text_width) / 2;
3055 if (request.wrap_single_words && !request.autowrap)
3057 char *src_text_ptr, *dst_text_ptr;
3059 text_door_style = checked_malloc(2 * strlen(text) + 1);
3061 src_text_ptr = text;
3062 dst_text_ptr = text_door_style;
3064 while (*src_text_ptr)
3066 if (*src_text_ptr == ' ' ||
3067 *src_text_ptr == '?' ||
3068 *src_text_ptr == '!')
3069 *dst_text_ptr++ = '\n';
3071 if (*src_text_ptr != ' ')
3072 *dst_text_ptr++ = *src_text_ptr;
3077 *dst_text_ptr = '\0';
3079 text_final = text_door_style;
3082 setRequestPosition(&sx, &sy, FALSE);
3084 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3086 for (y = 0; y < y_steps; y++)
3087 for (x = 0; x < x_steps; x++)
3088 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3089 x, y, x_steps, y_steps,
3090 tile_size, tile_size);
3092 // force DOOR font inside door area
3093 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3095 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3096 line_length, -1, max_lines, line_spacing, mask_mode,
3097 request.autowrap, request.centered, FALSE);
3101 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3102 RedrawGadget(tool_gadget[i]);
3104 // store readily prepared envelope request for later use when animating
3105 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3107 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3109 if (text_door_style)
3110 free(text_door_style);
3113 static void AnimateEnvelopeRequest(int anim_mode, int action)
3115 int graphic = IMG_BACKGROUND_REQUEST;
3116 boolean draw_masked = graphic_info[graphic].draw_masked;
3117 int delay_value_normal = request.step_delay;
3118 int delay_value_fast = delay_value_normal / 2;
3119 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3120 boolean no_delay = (tape.warp_forward);
3121 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3122 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3123 unsigned int anim_delay = 0;
3125 int tile_size = MAX(request.step_offset, 1);
3126 int max_xsize = request.width / tile_size;
3127 int max_ysize = request.height / tile_size;
3128 int max_xsize_inner = max_xsize - 2;
3129 int max_ysize_inner = max_ysize - 2;
3131 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3132 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3133 int xend = max_xsize_inner;
3134 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3135 int xstep = (xstart < xend ? 1 : 0);
3136 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3138 int end = MAX(xend - xstart, yend - ystart);
3141 if (setup.quick_doors)
3148 for (i = start; i <= end; i++)
3150 int last_frame = end; // last frame of this "for" loop
3151 int x = xstart + i * xstep;
3152 int y = ystart + i * ystep;
3153 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3154 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3155 int xsize_size_left = (xsize - 1) * tile_size;
3156 int ysize_size_top = (ysize - 1) * tile_size;
3157 int max_xsize_pos = (max_xsize - 1) * tile_size;
3158 int max_ysize_pos = (max_ysize - 1) * tile_size;
3159 int width = xsize * tile_size;
3160 int height = ysize * tile_size;
3165 setRequestPosition(&src_x, &src_y, FALSE);
3166 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3168 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3170 for (yy = 0; yy < 2; yy++)
3172 for (xx = 0; xx < 2; xx++)
3174 int src_xx = src_x + xx * max_xsize_pos;
3175 int src_yy = src_y + yy * max_ysize_pos;
3176 int dst_xx = dst_x + xx * xsize_size_left;
3177 int dst_yy = dst_y + yy * ysize_size_top;
3178 int xx_size = (xx ? tile_size : xsize_size_left);
3179 int yy_size = (yy ? tile_size : ysize_size_top);
3182 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3183 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3185 BlitBitmap(bitmap_db_store_2, backbuffer,
3186 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3190 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3192 redraw_mask |= REDRAW_FIELD;
3196 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3199 ClearAutoRepeatKeyEvents();
3202 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3204 int graphic = IMG_BACKGROUND_REQUEST;
3205 int sound_opening = SND_REQUEST_OPENING;
3206 int sound_closing = SND_REQUEST_CLOSING;
3207 int anim_mode_1 = request.anim_mode; // (higher priority)
3208 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3209 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3210 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3211 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3213 if (game_status == GAME_MODE_PLAYING)
3214 BlitScreenToBitmap(backbuffer);
3216 SetDrawtoField(DRAW_TO_BACKBUFFER);
3218 // SetDrawBackgroundMask(REDRAW_NONE);
3220 if (action == ACTION_OPENING)
3222 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3224 if (req_state & REQ_ASK)
3226 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3227 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3228 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3229 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3231 else if (req_state & REQ_CONFIRM)
3233 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3234 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3236 else if (req_state & REQ_PLAYER)
3238 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3239 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3240 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3241 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3244 DrawEnvelopeRequest(text);
3247 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3249 if (action == ACTION_OPENING)
3251 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3253 if (anim_mode == ANIM_DEFAULT)
3254 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3256 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3260 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3262 if (anim_mode != ANIM_NONE)
3263 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3265 if (anim_mode == ANIM_DEFAULT)
3266 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3269 game.envelope_active = FALSE;
3271 if (action == ACTION_CLOSING)
3272 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3274 // SetDrawBackgroundMask(last_draw_background_mask);
3276 redraw_mask |= REDRAW_FIELD;
3280 if (action == ACTION_CLOSING &&
3281 game_status == GAME_MODE_PLAYING &&
3282 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3283 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3286 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3288 if (IS_MM_WALL(element))
3290 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3296 int graphic = el2preimg(element);
3298 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3299 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3304 void DrawLevel(int draw_background_mask)
3308 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3309 SetDrawBackgroundMask(draw_background_mask);
3313 for (x = BX1; x <= BX2; x++)
3314 for (y = BY1; y <= BY2; y++)
3315 DrawScreenField(x, y);
3317 redraw_mask |= REDRAW_FIELD;
3320 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3325 for (x = 0; x < size_x; x++)
3326 for (y = 0; y < size_y; y++)
3327 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3329 redraw_mask |= REDRAW_FIELD;
3332 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3336 for (x = 0; x < size_x; x++)
3337 for (y = 0; y < size_y; y++)
3338 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3340 redraw_mask |= REDRAW_FIELD;
3343 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3345 boolean show_level_border = (BorderElement != EL_EMPTY);
3346 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3347 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3348 int tile_size = preview.tile_size;
3349 int preview_width = preview.xsize * tile_size;
3350 int preview_height = preview.ysize * tile_size;
3351 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3352 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3353 int real_preview_width = real_preview_xsize * tile_size;
3354 int real_preview_height = real_preview_ysize * tile_size;
3355 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3356 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3359 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3362 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3364 dst_x += (preview_width - real_preview_width) / 2;
3365 dst_y += (preview_height - real_preview_height) / 2;
3367 for (x = 0; x < real_preview_xsize; x++)
3369 for (y = 0; y < real_preview_ysize; y++)
3371 int lx = from_x + x + (show_level_border ? -1 : 0);
3372 int ly = from_y + y + (show_level_border ? -1 : 0);
3373 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3374 getBorderElement(lx, ly));
3376 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3377 element, tile_size);
3381 redraw_mask |= REDRAW_FIELD;
3384 #define MICROLABEL_EMPTY 0
3385 #define MICROLABEL_LEVEL_NAME 1
3386 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3387 #define MICROLABEL_LEVEL_AUTHOR 3
3388 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3389 #define MICROLABEL_IMPORTED_FROM 5
3390 #define MICROLABEL_IMPORTED_BY_HEAD 6
3391 #define MICROLABEL_IMPORTED_BY 7
3393 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3395 int max_text_width = SXSIZE;
3396 int font_width = getFontWidth(font_nr);
3398 if (pos->align == ALIGN_CENTER)
3399 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3400 else if (pos->align == ALIGN_RIGHT)
3401 max_text_width = pos->x;
3403 max_text_width = SXSIZE - pos->x;
3405 return max_text_width / font_width;
3408 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3410 char label_text[MAX_OUTPUT_LINESIZE + 1];
3411 int max_len_label_text;
3412 int font_nr = pos->font;
3415 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3418 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3419 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3420 mode == MICROLABEL_IMPORTED_BY_HEAD)
3421 font_nr = pos->font_alt;
3423 max_len_label_text = getMaxTextLength(pos, font_nr);
3425 if (pos->size != -1)
3426 max_len_label_text = pos->size;
3428 for (i = 0; i < max_len_label_text; i++)
3429 label_text[i] = ' ';
3430 label_text[max_len_label_text] = '\0';
3432 if (strlen(label_text) > 0)
3433 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3436 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3437 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3438 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3439 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3440 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3441 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3442 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3443 max_len_label_text);
3444 label_text[max_len_label_text] = '\0';
3446 if (strlen(label_text) > 0)
3447 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3449 redraw_mask |= REDRAW_FIELD;
3452 static void DrawPreviewLevelLabel(int mode)
3454 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3457 static void DrawPreviewLevelInfo(int mode)
3459 if (mode == MICROLABEL_LEVEL_NAME)
3460 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3461 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3462 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3465 static void DrawPreviewLevelExt(boolean restart)
3467 static unsigned int scroll_delay = 0;
3468 static unsigned int label_delay = 0;
3469 static int from_x, from_y, scroll_direction;
3470 static int label_state, label_counter;
3471 unsigned int scroll_delay_value = preview.step_delay;
3472 boolean show_level_border = (BorderElement != EL_EMPTY);
3473 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3474 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3481 if (preview.anim_mode == ANIM_CENTERED)
3483 if (level_xsize > preview.xsize)
3484 from_x = (level_xsize - preview.xsize) / 2;
3485 if (level_ysize > preview.ysize)
3486 from_y = (level_ysize - preview.ysize) / 2;
3489 from_x += preview.xoffset;
3490 from_y += preview.yoffset;
3492 scroll_direction = MV_RIGHT;
3496 DrawPreviewLevelPlayfield(from_x, from_y);
3497 DrawPreviewLevelLabel(label_state);
3499 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3500 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3502 // initialize delay counters
3503 DelayReached(&scroll_delay, 0);
3504 DelayReached(&label_delay, 0);
3506 if (leveldir_current->name)
3508 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3509 char label_text[MAX_OUTPUT_LINESIZE + 1];
3510 int font_nr = pos->font;
3511 int max_len_label_text = getMaxTextLength(pos, font_nr);
3513 if (pos->size != -1)
3514 max_len_label_text = pos->size;
3516 strncpy(label_text, leveldir_current->name, max_len_label_text);
3517 label_text[max_len_label_text] = '\0';
3519 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3520 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3526 // scroll preview level, if needed
3527 if (preview.anim_mode != ANIM_NONE &&
3528 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3529 DelayReached(&scroll_delay, scroll_delay_value))
3531 switch (scroll_direction)
3536 from_x -= preview.step_offset;
3537 from_x = (from_x < 0 ? 0 : from_x);
3540 scroll_direction = MV_UP;
3544 if (from_x < level_xsize - preview.xsize)
3546 from_x += preview.step_offset;
3547 from_x = (from_x > level_xsize - preview.xsize ?
3548 level_xsize - preview.xsize : from_x);
3551 scroll_direction = MV_DOWN;
3557 from_y -= preview.step_offset;
3558 from_y = (from_y < 0 ? 0 : from_y);
3561 scroll_direction = MV_RIGHT;
3565 if (from_y < level_ysize - preview.ysize)
3567 from_y += preview.step_offset;
3568 from_y = (from_y > level_ysize - preview.ysize ?
3569 level_ysize - preview.ysize : from_y);
3572 scroll_direction = MV_LEFT;
3579 DrawPreviewLevelPlayfield(from_x, from_y);
3582 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3583 // redraw micro level label, if needed
3584 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3585 !strEqual(level.author, ANONYMOUS_NAME) &&
3586 !strEqual(level.author, leveldir_current->name) &&
3587 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3589 int max_label_counter = 23;
3591 if (leveldir_current->imported_from != NULL &&
3592 strlen(leveldir_current->imported_from) > 0)
3593 max_label_counter += 14;
3594 if (leveldir_current->imported_by != NULL &&
3595 strlen(leveldir_current->imported_by) > 0)
3596 max_label_counter += 14;
3598 label_counter = (label_counter + 1) % max_label_counter;
3599 label_state = (label_counter >= 0 && label_counter <= 7 ?
3600 MICROLABEL_LEVEL_NAME :
3601 label_counter >= 9 && label_counter <= 12 ?
3602 MICROLABEL_LEVEL_AUTHOR_HEAD :
3603 label_counter >= 14 && label_counter <= 21 ?
3604 MICROLABEL_LEVEL_AUTHOR :
3605 label_counter >= 23 && label_counter <= 26 ?
3606 MICROLABEL_IMPORTED_FROM_HEAD :
3607 label_counter >= 28 && label_counter <= 35 ?
3608 MICROLABEL_IMPORTED_FROM :
3609 label_counter >= 37 && label_counter <= 40 ?
3610 MICROLABEL_IMPORTED_BY_HEAD :
3611 label_counter >= 42 && label_counter <= 49 ?
3612 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3614 if (leveldir_current->imported_from == NULL &&
3615 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3616 label_state == MICROLABEL_IMPORTED_FROM))
3617 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3618 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3620 DrawPreviewLevelLabel(label_state);
3624 void DrawPreviewPlayers(void)
3626 if (game_status != GAME_MODE_MAIN)
3629 // do not draw preview players if level preview redefined, but players aren't
3630 if (preview.redefined && !menu.main.preview_players.redefined)
3633 boolean player_found[MAX_PLAYERS];
3634 int num_players = 0;
3637 for (i = 0; i < MAX_PLAYERS; i++)
3638 player_found[i] = FALSE;
3640 // check which players can be found in the level (simple approach)
3641 for (x = 0; x < lev_fieldx; x++)
3643 for (y = 0; y < lev_fieldy; y++)
3645 int element = level.field[x][y];
3647 if (ELEM_IS_PLAYER(element))
3649 int player_nr = GET_PLAYER_NR(element);
3651 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3653 if (!player_found[player_nr])
3656 player_found[player_nr] = TRUE;
3661 struct TextPosInfo *pos = &menu.main.preview_players;
3662 int tile_size = pos->tile_size;
3663 int border_size = pos->border_size;
3664 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3665 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3666 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3667 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3668 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3669 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3670 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3671 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3672 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3673 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3674 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3675 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3677 // clear area in which the players will be drawn
3678 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3679 max_players_width, max_players_height);
3681 if (!network.enabled && !setup.team_mode)
3684 // only draw players if level is suited for team mode
3685 if (num_players < 2)
3688 // draw all players that were found in the level
3689 for (i = 0; i < MAX_PLAYERS; i++)
3691 if (player_found[i])
3693 int graphic = el2img(EL_PLAYER_1 + i);
3695 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3697 xpos += player_xoffset;
3698 ypos += player_yoffset;
3703 void DrawPreviewLevelInitial(void)
3705 DrawPreviewLevelExt(TRUE);
3706 DrawPreviewPlayers();
3709 void DrawPreviewLevelAnimation(void)
3711 DrawPreviewLevelExt(FALSE);
3714 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3715 int border_size, int font_nr)
3717 int graphic = el2img(EL_PLAYER_1 + player_nr);
3718 int font_height = getFontHeight(font_nr);
3719 int player_height = MAX(tile_size, font_height);
3720 int xoffset_text = tile_size + border_size;
3721 int yoffset_text = (player_height - font_height) / 2;
3722 int yoffset_graphic = (player_height - tile_size) / 2;
3723 char *player_name = getNetworkPlayerName(player_nr + 1);
3725 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3727 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3730 static void DrawNetworkPlayersExt(boolean force)
3732 if (game_status != GAME_MODE_MAIN)
3735 if (!network.connected && !force)
3738 // do not draw network players if level preview redefined, but players aren't
3739 if (preview.redefined && !menu.main.network_players.redefined)
3742 int num_players = 0;
3745 for (i = 0; i < MAX_PLAYERS; i++)
3746 if (stored_player[i].connected_network)
3749 struct TextPosInfo *pos = &menu.main.network_players;
3750 int tile_size = pos->tile_size;
3751 int border_size = pos->border_size;
3752 int xoffset_text = tile_size + border_size;
3753 int font_nr = pos->font;
3754 int font_width = getFontWidth(font_nr);
3755 int font_height = getFontHeight(font_nr);
3756 int player_height = MAX(tile_size, font_height);
3757 int player_yoffset = player_height + border_size;
3758 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3759 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3760 int all_players_height = num_players * player_yoffset - border_size;
3761 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3762 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3763 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3765 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3766 max_players_width, max_players_height);
3768 // first draw local network player ...
3769 for (i = 0; i < MAX_PLAYERS; i++)
3771 if (stored_player[i].connected_network &&
3772 stored_player[i].connected_locally)
3774 char *player_name = getNetworkPlayerName(i + 1);
3775 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3776 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3778 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3780 ypos += player_yoffset;
3784 // ... then draw all other network players
3785 for (i = 0; i < MAX_PLAYERS; i++)
3787 if (stored_player[i].connected_network &&
3788 !stored_player[i].connected_locally)
3790 char *player_name = getNetworkPlayerName(i + 1);
3791 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3792 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3794 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3796 ypos += player_yoffset;
3801 void DrawNetworkPlayers(void)
3803 DrawNetworkPlayersExt(FALSE);
3806 void ClearNetworkPlayers(void)
3808 DrawNetworkPlayersExt(TRUE);
3811 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3812 int graphic, int sync_frame,
3815 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3817 if (mask_mode == USE_MASKING)
3818 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3820 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3823 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3824 int graphic, int sync_frame, int mask_mode)
3826 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3828 if (mask_mode == USE_MASKING)
3829 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3831 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3834 static void DrawGraphicAnimation(int x, int y, int graphic)
3836 int lx = LEVELX(x), ly = LEVELY(y);
3837 int mask_mode = NO_MASKING;
3839 if (!IN_SCR_FIELD(x, y))
3842 if (game.use_masked_elements)
3844 if (Tile[lx][ly] != EL_EMPTY)
3846 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3848 mask_mode = USE_MASKING;
3852 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3853 graphic, GfxFrame[lx][ly], mask_mode);
3855 MarkTileDirty(x, y);
3858 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3860 int lx = LEVELX(x), ly = LEVELY(y);
3861 int mask_mode = NO_MASKING;
3863 if (!IN_SCR_FIELD(x, y))
3866 if (game.use_masked_elements)
3868 if (Tile[lx][ly] != EL_EMPTY)
3870 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3872 mask_mode = USE_MASKING;
3876 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3877 graphic, GfxFrame[lx][ly], mask_mode);
3879 MarkTileDirty(x, y);
3882 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3884 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3887 void DrawLevelElementAnimation(int x, int y, int element)
3889 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3891 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3894 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3896 int sx = SCREENX(x), sy = SCREENY(y);
3898 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3901 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3904 DrawGraphicAnimation(sx, sy, graphic);
3907 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3908 DrawLevelFieldCrumbled(x, y);
3910 if (GFX_CRUMBLED(Tile[x][y]))
3911 DrawLevelFieldCrumbled(x, y);
3915 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3917 int sx = SCREENX(x), sy = SCREENY(y);
3920 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3923 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3925 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3928 DrawGraphicAnimation(sx, sy, graphic);
3930 if (GFX_CRUMBLED(element))
3931 DrawLevelFieldCrumbled(x, y);
3934 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3936 if (player->use_murphy)
3938 // this works only because currently only one player can be "murphy" ...
3939 static int last_horizontal_dir = MV_LEFT;
3940 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3942 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3943 last_horizontal_dir = move_dir;
3945 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3947 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3949 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3955 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3958 static boolean equalGraphics(int graphic1, int graphic2)
3960 struct GraphicInfo *g1 = &graphic_info[graphic1];
3961 struct GraphicInfo *g2 = &graphic_info[graphic2];
3963 return (g1->bitmap == g2->bitmap &&
3964 g1->src_x == g2->src_x &&
3965 g1->src_y == g2->src_y &&
3966 g1->anim_frames == g2->anim_frames &&
3967 g1->anim_delay == g2->anim_delay &&
3968 g1->anim_mode == g2->anim_mode);
3971 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3975 DRAW_PLAYER_STAGE_INIT = 0,
3976 DRAW_PLAYER_STAGE_LAST_FIELD,
3977 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3978 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3979 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3980 DRAW_PLAYER_STAGE_PLAYER,
3982 DRAW_PLAYER_STAGE_PLAYER,
3983 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3985 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3986 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3988 NUM_DRAW_PLAYER_STAGES
3991 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3993 static int static_last_player_graphic[MAX_PLAYERS];
3994 static int static_last_player_frame[MAX_PLAYERS];
3995 static boolean static_player_is_opaque[MAX_PLAYERS];
3996 static boolean draw_player[MAX_PLAYERS];
3997 int pnr = player->index_nr;
3999 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4001 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4002 static_last_player_frame[pnr] = player->Frame;
4003 static_player_is_opaque[pnr] = FALSE;
4005 draw_player[pnr] = TRUE;
4008 if (!draw_player[pnr])
4012 if (!IN_LEV_FIELD(player->jx, player->jy))
4014 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4015 Debug("draw:DrawPlayerExt", "This should never happen!");
4017 draw_player[pnr] = FALSE;
4023 int last_player_graphic = static_last_player_graphic[pnr];
4024 int last_player_frame = static_last_player_frame[pnr];
4025 boolean player_is_opaque = static_player_is_opaque[pnr];
4027 int jx = player->jx;
4028 int jy = player->jy;
4029 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4030 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4031 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4032 int last_jx = (player->is_moving ? jx - dx : jx);
4033 int last_jy = (player->is_moving ? jy - dy : jy);
4034 int next_jx = jx + dx;
4035 int next_jy = jy + dy;
4036 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4037 int sx = SCREENX(jx);
4038 int sy = SCREENY(jy);
4039 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4040 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4041 int element = Tile[jx][jy];
4042 int last_element = Tile[last_jx][last_jy];
4043 int action = (player->is_pushing ? ACTION_PUSHING :
4044 player->is_digging ? ACTION_DIGGING :
4045 player->is_collecting ? ACTION_COLLECTING :
4046 player->is_moving ? ACTION_MOVING :
4047 player->is_snapping ? ACTION_SNAPPING :
4048 player->is_dropping ? ACTION_DROPPING :
4049 player->is_waiting ? player->action_waiting :
4052 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4054 // ------------------------------------------------------------------------
4055 // initialize drawing the player
4056 // ------------------------------------------------------------------------
4058 draw_player[pnr] = FALSE;
4060 // GfxElement[][] is set to the element the player is digging or collecting;
4061 // remove also for off-screen player if the player is not moving anymore
4062 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4063 GfxElement[jx][jy] = EL_UNDEFINED;
4065 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4068 if (element == EL_EXPLOSION)
4071 InitPlayerGfxAnimation(player, action, move_dir);
4073 draw_player[pnr] = TRUE;
4075 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4077 // ------------------------------------------------------------------------
4078 // draw things in the field the player is leaving, if needed
4079 // ------------------------------------------------------------------------
4081 if (!IN_SCR_FIELD(sx, sy))
4082 draw_player[pnr] = FALSE;
4084 if (!player->is_moving)
4087 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4089 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4091 if (last_element == EL_DYNAMITE_ACTIVE ||
4092 last_element == EL_EM_DYNAMITE_ACTIVE ||
4093 last_element == EL_SP_DISK_RED_ACTIVE)
4094 DrawDynamite(last_jx, last_jy);
4096 DrawLevelFieldThruMask(last_jx, last_jy);
4098 else if (last_element == EL_DYNAMITE_ACTIVE ||
4099 last_element == EL_EM_DYNAMITE_ACTIVE ||
4100 last_element == EL_SP_DISK_RED_ACTIVE)
4101 DrawDynamite(last_jx, last_jy);
4103 DrawLevelField(last_jx, last_jy);
4105 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4106 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4108 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4110 // ------------------------------------------------------------------------
4111 // draw things behind the player, if needed
4112 // ------------------------------------------------------------------------
4116 DrawLevelElement(jx, jy, Back[jx][jy]);
4121 if (IS_ACTIVE_BOMB(element))
4123 DrawLevelElement(jx, jy, EL_EMPTY);
4128 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4130 int old_element = GfxElement[jx][jy];
4131 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4132 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4134 if (GFX_CRUMBLED(old_element))
4135 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4137 DrawScreenGraphic(sx, sy, old_graphic, frame);
4139 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4140 static_player_is_opaque[pnr] = TRUE;
4144 GfxElement[jx][jy] = EL_UNDEFINED;
4146 // make sure that pushed elements are drawn with correct frame rate
4147 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4149 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4150 GfxFrame[jx][jy] = player->StepFrame;
4152 DrawLevelField(jx, jy);
4155 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4157 // ------------------------------------------------------------------------
4158 // draw things the player is pushing, if needed
4159 // ------------------------------------------------------------------------
4161 if (!player->is_pushing || !player->is_moving)
4164 int gfx_frame = GfxFrame[jx][jy];
4166 if (!IS_MOVING(jx, jy)) // push movement already finished
4168 element = Tile[next_jx][next_jy];
4169 gfx_frame = GfxFrame[next_jx][next_jy];
4172 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4173 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4174 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4176 // draw background element under pushed element (like the Sokoban field)
4177 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4179 // this allows transparent pushing animation over non-black background
4182 DrawLevelElement(jx, jy, Back[jx][jy]);
4184 DrawLevelElement(jx, jy, EL_EMPTY);
4186 if (Back[next_jx][next_jy])
4187 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4189 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4191 else if (Back[next_jx][next_jy])
4192 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4194 int px = SCREENX(jx), py = SCREENY(jy);
4195 int pxx = (TILEX - ABS(sxx)) * dx;
4196 int pyy = (TILEY - ABS(syy)) * dy;
4199 // do not draw (EM style) pushing animation when pushing is finished
4200 // (two-tile animations usually do not contain start and end frame)
4201 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4202 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4204 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4206 // masked drawing is needed for EMC style (double) movement graphics
4207 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4208 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4211 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4213 // ------------------------------------------------------------------------
4214 // draw player himself
4215 // ------------------------------------------------------------------------
4217 int graphic = getPlayerGraphic(player, move_dir);
4219 // in the case of changed player action or direction, prevent the current
4220 // animation frame from being restarted for identical animations
4221 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4222 player->Frame = last_player_frame;
4224 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4226 if (player_is_opaque)
4227 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4229 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4231 if (SHIELD_ON(player))
4233 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4234 IMG_SHIELD_NORMAL_ACTIVE);
4235 frame = getGraphicAnimationFrame(graphic, -1);
4237 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4240 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4242 // ------------------------------------------------------------------------
4243 // draw things in front of player (active dynamite or dynabombs)
4244 // ------------------------------------------------------------------------
4246 if (IS_ACTIVE_BOMB(element))
4248 int graphic = el2img(element);
4249 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4251 if (game.emulation == EMU_SUPAPLEX)
4252 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4254 DrawGraphicThruMask(sx, sy, graphic, frame);
4257 if (player_is_moving && last_element == EL_EXPLOSION)
4259 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4260 GfxElement[last_jx][last_jy] : EL_EMPTY);
4261 int graphic = el_act2img(element, ACTION_EXPLODING);
4262 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4263 int phase = ExplodePhase[last_jx][last_jy] - 1;
4264 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4267 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4270 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4272 // ------------------------------------------------------------------------
4273 // draw elements the player is just walking/passing through/under
4274 // ------------------------------------------------------------------------
4276 if (player_is_moving)
4278 // handle the field the player is leaving ...
4279 if (IS_ACCESSIBLE_INSIDE(last_element))
4280 DrawLevelField(last_jx, last_jy);
4281 else if (IS_ACCESSIBLE_UNDER(last_element))
4282 DrawLevelFieldThruMask(last_jx, last_jy);
4285 // do not redraw accessible elements if the player is just pushing them
4286 if (!player_is_moving || !player->is_pushing)
4288 // ... and the field the player is entering
4289 if (IS_ACCESSIBLE_INSIDE(element))
4290 DrawLevelField(jx, jy);
4291 else if (IS_ACCESSIBLE_UNDER(element))
4292 DrawLevelFieldThruMask(jx, jy);
4295 MarkTileDirty(sx, sy);
4299 void DrawPlayer(struct PlayerInfo *player)
4303 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4304 DrawPlayerExt(player, i);
4307 void DrawAllPlayers(void)
4311 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4312 for (j = 0; j < MAX_PLAYERS; j++)
4313 if (stored_player[j].active)
4314 DrawPlayerExt(&stored_player[j], i);
4317 void DrawPlayerField(int x, int y)
4319 if (!IS_PLAYER(x, y))
4322 DrawPlayer(PLAYERINFO(x, y));
4325 // ----------------------------------------------------------------------------
4327 void WaitForEventToContinue(void)
4329 boolean first_wait = TRUE;
4330 boolean still_wait = TRUE;
4332 if (program.headless)
4335 // simulate releasing mouse button over last gadget, if still pressed
4337 HandleGadgets(-1, -1, 0);
4339 button_status = MB_RELEASED;
4342 ClearPlayerAction();
4348 if (NextValidEvent(&event))
4352 case EVENT_BUTTONPRESS:
4353 case EVENT_FINGERPRESS:
4357 case EVENT_BUTTONRELEASE:
4358 case EVENT_FINGERRELEASE:
4359 still_wait = first_wait;
4362 case EVENT_KEYPRESS:
4363 case SDL_CONTROLLERBUTTONDOWN:
4364 case SDL_JOYBUTTONDOWN:
4369 HandleOtherEvents(&event);
4373 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4378 if (!PendingEvent())
4383 #define MAX_REQUEST_LINES 13
4384 #define MAX_REQUEST_LINE_FONT1_LEN 7
4385 #define MAX_REQUEST_LINE_FONT2_LEN 10
4387 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4389 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4391 int draw_buffer_last = GetDrawtoField();
4392 int width = request.width;
4393 int height = request.height;
4397 // when showing request dialog after game ended, deactivate game panel
4398 if (game_just_ended)
4399 game.panel.active = FALSE;
4401 game.request_active = TRUE;
4403 setRequestPosition(&sx, &sy, FALSE);
4405 button_status = MB_RELEASED;
4407 request_gadget_id = -1;
4412 boolean event_handled = FALSE;
4414 if (game_just_ended)
4416 SetDrawtoField(draw_buffer_game);
4418 HandleGameActions();
4420 SetDrawtoField(DRAW_TO_BACKBUFFER);
4422 if (global.use_envelope_request)
4424 // copy current state of request area to middle of playfield area
4425 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4433 while (NextValidEvent(&event))
4435 event_handled = TRUE;
4439 case EVENT_BUTTONPRESS:
4440 case EVENT_BUTTONRELEASE:
4441 case EVENT_MOTIONNOTIFY:
4445 if (event.type == EVENT_MOTIONNOTIFY)
4450 motion_status = TRUE;
4451 mx = ((MotionEvent *) &event)->x;
4452 my = ((MotionEvent *) &event)->y;
4456 motion_status = FALSE;
4457 mx = ((ButtonEvent *) &event)->x;
4458 my = ((ButtonEvent *) &event)->y;
4459 if (event.type == EVENT_BUTTONPRESS)
4460 button_status = ((ButtonEvent *) &event)->button;
4462 button_status = MB_RELEASED;
4465 // this sets 'request_gadget_id'
4466 HandleGadgets(mx, my, button_status);
4468 switch (request_gadget_id)
4470 case TOOL_CTRL_ID_YES:
4471 case TOOL_CTRL_ID_TOUCH_YES:
4474 case TOOL_CTRL_ID_NO:
4475 case TOOL_CTRL_ID_TOUCH_NO:
4478 case TOOL_CTRL_ID_CONFIRM:
4479 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4480 result = TRUE | FALSE;
4483 case TOOL_CTRL_ID_PLAYER_1:
4486 case TOOL_CTRL_ID_PLAYER_2:
4489 case TOOL_CTRL_ID_PLAYER_3:
4492 case TOOL_CTRL_ID_PLAYER_4:
4497 // only check clickable animations if no request gadget clicked
4498 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4505 case SDL_WINDOWEVENT:
4506 HandleWindowEvent((WindowEvent *) &event);
4509 case SDL_APP_WILLENTERBACKGROUND:
4510 case SDL_APP_DIDENTERBACKGROUND:
4511 case SDL_APP_WILLENTERFOREGROUND:
4512 case SDL_APP_DIDENTERFOREGROUND:
4513 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4516 case EVENT_KEYPRESS:
4518 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4523 if (req_state & REQ_CONFIRM)
4532 #if defined(KSYM_Rewind)
4533 case KSYM_Rewind: // for Amazon Fire TV remote
4542 #if defined(KSYM_FastForward)
4543 case KSYM_FastForward: // for Amazon Fire TV remote
4549 HandleKeysDebug(key, KEY_PRESSED);
4553 if (req_state & REQ_PLAYER)
4555 int old_player_nr = setup.network_player_nr;
4558 result = old_player_nr + 1;
4563 result = old_player_nr + 1;
4594 case EVENT_FINGERRELEASE:
4595 case EVENT_KEYRELEASE:
4596 ClearPlayerAction();
4599 case SDL_CONTROLLERBUTTONDOWN:
4600 switch (event.cbutton.button)
4602 case SDL_CONTROLLER_BUTTON_A:
4603 case SDL_CONTROLLER_BUTTON_X:
4604 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4605 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4609 case SDL_CONTROLLER_BUTTON_B:
4610 case SDL_CONTROLLER_BUTTON_Y:
4611 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4612 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4613 case SDL_CONTROLLER_BUTTON_BACK:
4618 if (req_state & REQ_PLAYER)
4620 int old_player_nr = setup.network_player_nr;
4623 result = old_player_nr + 1;
4625 switch (event.cbutton.button)
4627 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4628 case SDL_CONTROLLER_BUTTON_Y:
4632 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4633 case SDL_CONTROLLER_BUTTON_B:
4637 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4638 case SDL_CONTROLLER_BUTTON_A:
4642 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4643 case SDL_CONTROLLER_BUTTON_X:
4654 case SDL_CONTROLLERBUTTONUP:
4655 HandleJoystickEvent(&event);
4656 ClearPlayerAction();
4660 HandleOtherEvents(&event);
4665 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4667 int joy = AnyJoystick();
4669 if (joy & JOY_BUTTON_1)
4671 else if (joy & JOY_BUTTON_2)
4674 else if (AnyJoystick())
4676 int joy = AnyJoystick();
4678 if (req_state & REQ_PLAYER)
4682 else if (joy & JOY_RIGHT)
4684 else if (joy & JOY_DOWN)
4686 else if (joy & JOY_LEFT)
4693 if (game_just_ended)
4695 if (global.use_envelope_request)
4697 // copy back current state of pressed buttons inside request area
4698 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4702 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4708 SetDrawtoField(draw_buffer_last);
4710 game.request_active = FALSE;
4715 static boolean RequestDoor(char *text, unsigned int req_state)
4717 int draw_buffer_last = GetDrawtoField();
4718 unsigned int old_door_state;
4719 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4720 int font_nr = FONT_TEXT_2;
4725 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4727 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4728 font_nr = FONT_TEXT_1;
4731 if (game_status == GAME_MODE_PLAYING)
4732 BlitScreenToBitmap(backbuffer);
4734 // disable deactivated drawing when quick-loading level tape recording
4735 if (tape.playing && tape.deactivate_display)
4736 TapeDeactivateDisplayOff(TRUE);
4738 SetMouseCursor(CURSOR_DEFAULT);
4740 // pause network game while waiting for request to answer
4741 if (network.enabled &&
4742 game_status == GAME_MODE_PLAYING &&
4743 !game.all_players_gone &&
4744 req_state & REQUEST_WAIT_FOR_INPUT)
4745 SendToServer_PausePlaying();
4747 old_door_state = GetDoorState();
4749 // simulate releasing mouse button over last gadget, if still pressed
4751 HandleGadgets(-1, -1, 0);
4755 // draw released gadget before proceeding
4758 if (old_door_state & DOOR_OPEN_1)
4760 CloseDoor(DOOR_CLOSE_1);
4762 // save old door content
4763 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4764 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4767 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4768 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4770 // clear door drawing field
4771 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4773 // force DOOR font inside door area
4774 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4776 // write text for request
4777 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4779 char text_line[max_request_line_len + 1];
4785 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4787 tc = *(text_ptr + tx);
4788 // if (!tc || tc == ' ')
4789 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4793 if ((tc == '?' || tc == '!') && tl == 0)
4803 strncpy(text_line, text_ptr, tl);
4806 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4807 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4808 text_line, font_nr);
4810 text_ptr += tl + (tc == ' ' ? 1 : 0);
4811 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4816 if (req_state & REQ_ASK)
4818 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4819 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4820 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4821 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4823 else if (req_state & REQ_CONFIRM)
4825 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4826 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4828 else if (req_state & REQ_PLAYER)
4830 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4831 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4832 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4833 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4836 // copy request gadgets to door backbuffer
4837 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4839 OpenDoor(DOOR_OPEN_1);
4841 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4843 if (game_status == GAME_MODE_PLAYING)
4845 SetPanelBackground();
4846 SetDrawBackgroundMask(REDRAW_DOOR_1);
4850 SetDrawBackgroundMask(REDRAW_FIELD);
4856 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4858 // ---------- handle request buttons ----------
4859 result = RequestHandleEvents(req_state, draw_buffer_last);
4863 if (!(req_state & REQ_STAY_OPEN))
4865 CloseDoor(DOOR_CLOSE_1);
4867 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4868 (req_state & REQ_REOPEN))
4869 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4874 if (game_status == GAME_MODE_PLAYING)
4876 SetPanelBackground();
4877 SetDrawBackgroundMask(REDRAW_DOOR_1);
4881 SetDrawBackgroundMask(REDRAW_FIELD);
4884 // continue network game after request
4885 if (network.enabled &&
4886 game_status == GAME_MODE_PLAYING &&
4887 !game.all_players_gone &&
4888 req_state & REQUEST_WAIT_FOR_INPUT)
4889 SendToServer_ContinuePlaying();
4891 // restore deactivated drawing when quick-loading level tape recording
4892 if (tape.playing && tape.deactivate_display)
4893 TapeDeactivateDisplayOn();
4898 static boolean RequestEnvelope(char *text, unsigned int req_state)
4900 int draw_buffer_last = GetDrawtoField();
4903 if (game_status == GAME_MODE_PLAYING)
4904 BlitScreenToBitmap(backbuffer);
4906 // disable deactivated drawing when quick-loading level tape recording
4907 if (tape.playing && tape.deactivate_display)
4908 TapeDeactivateDisplayOff(TRUE);
4910 SetMouseCursor(CURSOR_DEFAULT);
4912 // pause network game while waiting for request to answer
4913 if (network.enabled &&
4914 game_status == GAME_MODE_PLAYING &&
4915 !game.all_players_gone &&
4916 req_state & REQUEST_WAIT_FOR_INPUT)
4917 SendToServer_PausePlaying();
4919 // simulate releasing mouse button over last gadget, if still pressed
4921 HandleGadgets(-1, -1, 0);
4925 // (replace with setting corresponding request background)
4926 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4927 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4929 // clear door drawing field
4930 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4932 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4934 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4936 if (game_status == GAME_MODE_PLAYING)
4938 SetPanelBackground();
4939 SetDrawBackgroundMask(REDRAW_DOOR_1);
4943 SetDrawBackgroundMask(REDRAW_FIELD);
4949 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4951 // ---------- handle request buttons ----------
4952 result = RequestHandleEvents(req_state, draw_buffer_last);
4956 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4960 if (game_status == GAME_MODE_PLAYING)
4962 SetPanelBackground();
4963 SetDrawBackgroundMask(REDRAW_DOOR_1);
4967 SetDrawBackgroundMask(REDRAW_FIELD);
4970 // continue network game after request
4971 if (network.enabled &&
4972 game_status == GAME_MODE_PLAYING &&
4973 !game.all_players_gone &&
4974 req_state & REQUEST_WAIT_FOR_INPUT)
4975 SendToServer_ContinuePlaying();
4977 // restore deactivated drawing when quick-loading level tape recording
4978 if (tape.playing && tape.deactivate_display)
4979 TapeDeactivateDisplayOn();
4984 boolean Request(char *text, unsigned int req_state)
4986 boolean overlay_enabled = GetOverlayEnabled();
4989 game.request_active_or_moving = TRUE;
4991 SetOverlayEnabled(FALSE);
4993 if (global.use_envelope_request)
4994 result = RequestEnvelope(text, req_state);
4996 result = RequestDoor(text, req_state);
4998 SetOverlayEnabled(overlay_enabled);
5000 game.request_active_or_moving = FALSE;
5005 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5007 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5008 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5011 if (dpo1->sort_priority != dpo2->sort_priority)
5012 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5014 compare_result = dpo1->nr - dpo2->nr;
5016 return compare_result;
5019 void InitGraphicCompatibilityInfo_Doors(void)
5025 struct DoorInfo *door;
5029 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5030 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5032 { -1, -1, -1, NULL }
5034 struct Rect door_rect_list[] =
5036 { DX, DY, DXSIZE, DYSIZE },
5037 { VX, VY, VXSIZE, VYSIZE }
5041 for (i = 0; doors[i].door_token != -1; i++)
5043 int door_token = doors[i].door_token;
5044 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5045 int part_1 = doors[i].part_1;
5046 int part_8 = doors[i].part_8;
5047 int part_2 = part_1 + 1;
5048 int part_3 = part_1 + 2;
5049 struct DoorInfo *door = doors[i].door;
5050 struct Rect *door_rect = &door_rect_list[door_index];
5051 boolean door_gfx_redefined = FALSE;
5053 // check if any door part graphic definitions have been redefined
5055 for (j = 0; door_part_controls[j].door_token != -1; j++)
5057 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5058 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5060 if (dpc->door_token == door_token && fi->redefined)
5061 door_gfx_redefined = TRUE;
5064 // check for old-style door graphic/animation modifications
5066 if (!door_gfx_redefined)
5068 if (door->anim_mode & ANIM_STATIC_PANEL)
5070 door->panel.step_xoffset = 0;
5071 door->panel.step_yoffset = 0;
5074 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5076 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5077 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5078 int num_door_steps, num_panel_steps;
5080 // remove door part graphics other than the two default wings
5082 for (j = 0; door_part_controls[j].door_token != -1; j++)
5084 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5085 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5087 if (dpc->graphic >= part_3 &&
5088 dpc->graphic <= part_8)
5092 // set graphics and screen positions of the default wings
5094 g_part_1->width = door_rect->width;
5095 g_part_1->height = door_rect->height;
5096 g_part_2->width = door_rect->width;
5097 g_part_2->height = door_rect->height;
5098 g_part_2->src_x = door_rect->width;
5099 g_part_2->src_y = g_part_1->src_y;
5101 door->part_2.x = door->part_1.x;
5102 door->part_2.y = door->part_1.y;
5104 if (door->width != -1)
5106 g_part_1->width = door->width;
5107 g_part_2->width = door->width;
5109 // special treatment for graphics and screen position of right wing
5110 g_part_2->src_x += door_rect->width - door->width;
5111 door->part_2.x += door_rect->width - door->width;
5114 if (door->height != -1)
5116 g_part_1->height = door->height;
5117 g_part_2->height = door->height;
5119 // special treatment for graphics and screen position of bottom wing
5120 g_part_2->src_y += door_rect->height - door->height;
5121 door->part_2.y += door_rect->height - door->height;
5124 // set animation delays for the default wings and panels
5126 door->part_1.step_delay = door->step_delay;
5127 door->part_2.step_delay = door->step_delay;
5128 door->panel.step_delay = door->step_delay;
5130 // set animation draw order for the default wings
5132 door->part_1.sort_priority = 2; // draw left wing over ...
5133 door->part_2.sort_priority = 1; // ... right wing
5135 // set animation draw offset for the default wings
5137 if (door->anim_mode & ANIM_HORIZONTAL)
5139 door->part_1.step_xoffset = door->step_offset;
5140 door->part_1.step_yoffset = 0;
5141 door->part_2.step_xoffset = door->step_offset * -1;
5142 door->part_2.step_yoffset = 0;
5144 num_door_steps = g_part_1->width / door->step_offset;
5146 else // ANIM_VERTICAL
5148 door->part_1.step_xoffset = 0;
5149 door->part_1.step_yoffset = door->step_offset;
5150 door->part_2.step_xoffset = 0;
5151 door->part_2.step_yoffset = door->step_offset * -1;
5153 num_door_steps = g_part_1->height / door->step_offset;
5156 // set animation draw offset for the default panels
5158 if (door->step_offset > 1)
5160 num_panel_steps = 2 * door_rect->height / door->step_offset;
5161 door->panel.start_step = num_panel_steps - num_door_steps;
5162 door->panel.start_step_closing = door->panel.start_step;
5166 num_panel_steps = door_rect->height / door->step_offset;
5167 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5168 door->panel.start_step_closing = door->panel.start_step;
5169 door->panel.step_delay *= 2;
5176 void InitDoors(void)
5180 for (i = 0; door_part_controls[i].door_token != -1; i++)
5182 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5183 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5185 // initialize "start_step_opening" and "start_step_closing", if needed
5186 if (dpc->pos->start_step_opening == 0 &&
5187 dpc->pos->start_step_closing == 0)
5189 // dpc->pos->start_step_opening = dpc->pos->start_step;
5190 dpc->pos->start_step_closing = dpc->pos->start_step;
5193 // fill structure for door part draw order (sorted below)
5195 dpo->sort_priority = dpc->pos->sort_priority;
5198 // sort door part controls according to sort_priority and graphic number
5199 qsort(door_part_order, MAX_DOOR_PARTS,
5200 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5203 unsigned int OpenDoor(unsigned int door_state)
5205 if (door_state & DOOR_COPY_BACK)
5207 if (door_state & DOOR_OPEN_1)
5208 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5209 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5211 if (door_state & DOOR_OPEN_2)
5212 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5213 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5215 door_state &= ~DOOR_COPY_BACK;
5218 return MoveDoor(door_state);
5221 unsigned int CloseDoor(unsigned int door_state)
5223 unsigned int old_door_state = GetDoorState();
5225 if (!(door_state & DOOR_NO_COPY_BACK))
5227 if (old_door_state & DOOR_OPEN_1)
5228 BlitBitmap(backbuffer, bitmap_db_door_1,
5229 DX, DY, DXSIZE, DYSIZE, 0, 0);
5231 if (old_door_state & DOOR_OPEN_2)
5232 BlitBitmap(backbuffer, bitmap_db_door_2,
5233 VX, VY, VXSIZE, VYSIZE, 0, 0);
5235 door_state &= ~DOOR_NO_COPY_BACK;
5238 return MoveDoor(door_state);
5241 unsigned int GetDoorState(void)
5243 return MoveDoor(DOOR_GET_STATE);
5246 unsigned int SetDoorState(unsigned int door_state)
5248 return MoveDoor(door_state | DOOR_SET_STATE);
5251 static int euclid(int a, int b)
5253 return (b ? euclid(b, a % b) : a);
5256 unsigned int MoveDoor(unsigned int door_state)
5258 struct Rect door_rect_list[] =
5260 { DX, DY, DXSIZE, DYSIZE },
5261 { VX, VY, VXSIZE, VYSIZE }
5263 static int door1 = DOOR_CLOSE_1;
5264 static int door2 = DOOR_CLOSE_2;
5265 unsigned int door_delay = 0;
5266 unsigned int door_delay_value;
5269 if (door_state == DOOR_GET_STATE)
5270 return (door1 | door2);
5272 if (door_state & DOOR_SET_STATE)
5274 if (door_state & DOOR_ACTION_1)
5275 door1 = door_state & DOOR_ACTION_1;
5276 if (door_state & DOOR_ACTION_2)
5277 door2 = door_state & DOOR_ACTION_2;
5279 return (door1 | door2);
5282 if (!(door_state & DOOR_FORCE_REDRAW))
5284 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5285 door_state &= ~DOOR_OPEN_1;
5286 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5287 door_state &= ~DOOR_CLOSE_1;
5288 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5289 door_state &= ~DOOR_OPEN_2;
5290 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5291 door_state &= ~DOOR_CLOSE_2;
5294 if (global.autoplay_leveldir)
5296 door_state |= DOOR_NO_DELAY;
5297 door_state &= ~DOOR_CLOSE_ALL;
5300 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5301 door_state |= DOOR_NO_DELAY;
5303 if (door_state & DOOR_ACTION)
5305 boolean door_panel_drawn[NUM_DOORS];
5306 boolean panel_has_doors[NUM_DOORS];
5307 boolean door_part_skip[MAX_DOOR_PARTS];
5308 boolean door_part_done[MAX_DOOR_PARTS];
5309 boolean door_part_done_all;
5310 int num_steps[MAX_DOOR_PARTS];
5311 int max_move_delay = 0; // delay for complete animations of all doors
5312 int max_step_delay = 0; // delay (ms) between two animation frames
5313 int num_move_steps = 0; // number of animation steps for all doors
5314 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5315 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5316 int current_move_delay = 0;
5320 for (i = 0; i < NUM_DOORS; i++)
5321 panel_has_doors[i] = FALSE;
5323 for (i = 0; i < MAX_DOOR_PARTS; i++)
5325 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5326 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5327 int door_token = dpc->door_token;
5329 door_part_done[i] = FALSE;
5330 door_part_skip[i] = (!(door_state & door_token) ||
5334 for (i = 0; i < MAX_DOOR_PARTS; i++)
5336 int nr = door_part_order[i].nr;
5337 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5338 struct DoorPartPosInfo *pos = dpc->pos;
5339 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5340 int door_token = dpc->door_token;
5341 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5342 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5343 int step_xoffset = ABS(pos->step_xoffset);
5344 int step_yoffset = ABS(pos->step_yoffset);
5345 int step_delay = pos->step_delay;
5346 int current_door_state = door_state & door_token;
5347 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5348 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5349 boolean part_opening = (is_panel ? door_closing : door_opening);
5350 int start_step = (part_opening ? pos->start_step_opening :
5351 pos->start_step_closing);
5352 float move_xsize = (step_xoffset ? g->width : 0);
5353 float move_ysize = (step_yoffset ? g->height : 0);
5354 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5355 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5356 int move_steps = (move_xsteps && move_ysteps ?
5357 MIN(move_xsteps, move_ysteps) :
5358 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5359 int move_delay = move_steps * step_delay;
5361 if (door_part_skip[nr])
5364 max_move_delay = MAX(max_move_delay, move_delay);
5365 max_step_delay = (max_step_delay == 0 ? step_delay :
5366 euclid(max_step_delay, step_delay));
5367 num_steps[nr] = move_steps;
5371 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5373 panel_has_doors[door_index] = TRUE;
5377 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5379 num_move_steps = max_move_delay / max_step_delay;
5380 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5382 door_delay_value = max_step_delay;
5384 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5386 start = num_move_steps - 1;
5390 // opening door sound has priority over simultaneously closing door
5391 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5393 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5395 if (door_state & DOOR_OPEN_1)
5396 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5397 if (door_state & DOOR_OPEN_2)
5398 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5400 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5402 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5404 if (door_state & DOOR_CLOSE_1)
5405 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5406 if (door_state & DOOR_CLOSE_2)
5407 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5411 for (k = start; k < num_move_steps; k++)
5413 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5415 door_part_done_all = TRUE;
5417 for (i = 0; i < NUM_DOORS; i++)
5418 door_panel_drawn[i] = FALSE;
5420 for (i = 0; i < MAX_DOOR_PARTS; i++)
5422 int nr = door_part_order[i].nr;
5423 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5424 struct DoorPartPosInfo *pos = dpc->pos;
5425 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5426 int door_token = dpc->door_token;
5427 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5428 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5429 boolean is_panel_and_door_has_closed = FALSE;
5430 struct Rect *door_rect = &door_rect_list[door_index];
5431 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5433 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5434 int current_door_state = door_state & door_token;
5435 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5436 boolean door_closing = !door_opening;
5437 boolean part_opening = (is_panel ? door_closing : door_opening);
5438 boolean part_closing = !part_opening;
5439 int start_step = (part_opening ? pos->start_step_opening :
5440 pos->start_step_closing);
5441 int step_delay = pos->step_delay;
5442 int step_factor = step_delay / max_step_delay;
5443 int k1 = (step_factor ? k / step_factor + 1 : k);
5444 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5445 int kk = MAX(0, k2);
5448 int src_x, src_y, src_xx, src_yy;
5449 int dst_x, dst_y, dst_xx, dst_yy;
5452 if (door_part_skip[nr])
5455 if (!(door_state & door_token))
5463 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5464 int kk_door = MAX(0, k2_door);
5465 int sync_frame = kk_door * door_delay_value;
5466 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5468 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5469 &g_src_x, &g_src_y);
5474 if (!door_panel_drawn[door_index])
5476 ClearRectangle(drawto, door_rect->x, door_rect->y,
5477 door_rect->width, door_rect->height);
5479 door_panel_drawn[door_index] = TRUE;
5482 // draw opening or closing door parts
5484 if (pos->step_xoffset < 0) // door part on right side
5487 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5490 if (dst_xx + width > door_rect->width)
5491 width = door_rect->width - dst_xx;
5493 else // door part on left side
5496 dst_xx = pos->x - kk * pos->step_xoffset;
5500 src_xx = ABS(dst_xx);
5504 width = g->width - src_xx;
5506 if (width > door_rect->width)
5507 width = door_rect->width;
5509 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5512 if (pos->step_yoffset < 0) // door part on bottom side
5515 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5518 if (dst_yy + height > door_rect->height)
5519 height = door_rect->height - dst_yy;
5521 else // door part on top side
5524 dst_yy = pos->y - kk * pos->step_yoffset;
5528 src_yy = ABS(dst_yy);
5532 height = g->height - src_yy;
5535 src_x = g_src_x + src_xx;
5536 src_y = g_src_y + src_yy;
5538 dst_x = door_rect->x + dst_xx;
5539 dst_y = door_rect->y + dst_yy;
5541 is_panel_and_door_has_closed =
5544 panel_has_doors[door_index] &&
5545 k >= num_move_steps_doors_only - 1);
5547 if (width >= 0 && width <= g->width &&
5548 height >= 0 && height <= g->height &&
5549 !is_panel_and_door_has_closed)
5551 if (is_panel || !pos->draw_masked)
5552 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5555 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5559 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5561 if ((part_opening && (width < 0 || height < 0)) ||
5562 (part_closing && (width >= g->width && height >= g->height)))
5563 door_part_done[nr] = TRUE;
5565 // continue door part animations, but not panel after door has closed
5566 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5567 door_part_done_all = FALSE;
5570 if (!(door_state & DOOR_NO_DELAY))
5574 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5576 current_move_delay += max_step_delay;
5578 // prevent OS (Windows) from complaining about program not responding
5582 if (door_part_done_all)
5586 if (!(door_state & DOOR_NO_DELAY))
5588 // wait for specified door action post delay
5589 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5590 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5591 else if (door_state & DOOR_ACTION_1)
5592 door_delay_value = door_1.post_delay;
5593 else if (door_state & DOOR_ACTION_2)
5594 door_delay_value = door_2.post_delay;
5596 while (!DelayReached(&door_delay, door_delay_value))
5601 if (door_state & DOOR_ACTION_1)
5602 door1 = door_state & DOOR_ACTION_1;
5603 if (door_state & DOOR_ACTION_2)
5604 door2 = door_state & DOOR_ACTION_2;
5606 // draw masked border over door area
5607 DrawMaskedBorder(REDRAW_DOOR_1);
5608 DrawMaskedBorder(REDRAW_DOOR_2);
5610 ClearAutoRepeatKeyEvents();
5612 return (door1 | door2);
5615 static boolean useSpecialEditorDoor(void)
5617 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5618 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5620 // do not draw special editor door if editor border defined or redefined
5621 if (graphic_info[graphic].bitmap != NULL || redefined)
5624 // do not draw special editor door if global border defined to be empty
5625 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5628 // do not draw special editor door if viewport definitions do not match
5632 EY + EYSIZE != VY + VYSIZE)
5638 void DrawSpecialEditorDoor(void)
5640 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5641 int top_border_width = gfx1->width;
5642 int top_border_height = gfx1->height;
5643 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5644 int ex = EX - outer_border;
5645 int ey = EY - outer_border;
5646 int vy = VY - outer_border;
5647 int exsize = EXSIZE + 2 * outer_border;
5649 if (!useSpecialEditorDoor())
5652 // draw bigger level editor toolbox window
5653 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5654 top_border_width, top_border_height, ex, ey - top_border_height);
5655 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5656 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5658 redraw_mask |= REDRAW_ALL;
5661 void UndrawSpecialEditorDoor(void)
5663 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5664 int top_border_width = gfx1->width;
5665 int top_border_height = gfx1->height;
5666 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5667 int ex = EX - outer_border;
5668 int ey = EY - outer_border;
5669 int ey_top = ey - top_border_height;
5670 int exsize = EXSIZE + 2 * outer_border;
5671 int eysize = EYSIZE + 2 * outer_border;
5673 if (!useSpecialEditorDoor())
5676 // draw normal tape recorder window
5677 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5679 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5680 ex, ey_top, top_border_width, top_border_height,
5682 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5683 ex, ey, exsize, eysize, ex, ey);
5687 // if screen background is set to "[NONE]", clear editor toolbox window
5688 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5689 ClearRectangle(drawto, ex, ey, exsize, eysize);
5692 redraw_mask |= REDRAW_ALL;
5696 // ---------- new tool button stuff -------------------------------------------
5701 struct TextPosInfo *pos;
5703 boolean is_touch_button;
5705 } toolbutton_info[NUM_TOOL_BUTTONS] =
5708 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5709 TOOL_CTRL_ID_YES, FALSE, "yes"
5712 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5713 TOOL_CTRL_ID_NO, FALSE, "no"
5716 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5717 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5720 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5721 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5724 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5725 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5728 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5729 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5732 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5733 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5736 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5737 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5740 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5741 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5744 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5745 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5749 void CreateToolButtons(void)
5753 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5755 int graphic = toolbutton_info[i].graphic;
5756 struct GraphicInfo *gfx = &graphic_info[graphic];
5757 struct TextPosInfo *pos = toolbutton_info[i].pos;
5758 struct GadgetInfo *gi;
5759 Bitmap *deco_bitmap = None;
5760 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5761 unsigned int event_mask = GD_EVENT_RELEASED;
5762 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5763 int base_x = (is_touch_button ? 0 : DX);
5764 int base_y = (is_touch_button ? 0 : DY);
5765 int gd_x = gfx->src_x;
5766 int gd_y = gfx->src_y;
5767 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5768 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5773 if (global.use_envelope_request && !is_touch_button)
5775 setRequestPosition(&base_x, &base_y, TRUE);
5777 // check if request buttons are outside of envelope and fix, if needed
5778 if (x < 0 || x + gfx->width > request.width ||
5779 y < 0 || y + gfx->height > request.height)
5781 if (id == TOOL_CTRL_ID_YES)
5784 y = request.height - 2 * request.border_size - gfx->height;
5786 else if (id == TOOL_CTRL_ID_NO)
5788 x = request.width - 2 * request.border_size - gfx->width;
5789 y = request.height - 2 * request.border_size - gfx->height;
5791 else if (id == TOOL_CTRL_ID_CONFIRM)
5793 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5794 y = request.height - 2 * request.border_size - gfx->height;
5796 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5798 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5800 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5801 y = request.height - 2 * request.border_size - gfx->height * 2;
5803 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5804 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5809 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5811 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5813 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5814 pos->size, &deco_bitmap, &deco_x, &deco_y);
5815 deco_xpos = (gfx->width - pos->size) / 2;
5816 deco_ypos = (gfx->height - pos->size) / 2;
5819 gi = CreateGadget(GDI_CUSTOM_ID, id,
5820 GDI_IMAGE_ID, graphic,
5821 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5824 GDI_WIDTH, gfx->width,
5825 GDI_HEIGHT, gfx->height,
5826 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5827 GDI_STATE, GD_BUTTON_UNPRESSED,
5828 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5829 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5830 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5831 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5832 GDI_DECORATION_SIZE, pos->size, pos->size,
5833 GDI_DECORATION_SHIFTING, 1, 1,
5834 GDI_DIRECT_DRAW, FALSE,
5835 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5836 GDI_EVENT_MASK, event_mask,
5837 GDI_CALLBACK_ACTION, HandleToolButtons,
5841 Fail("cannot create gadget");
5843 tool_gadget[id] = gi;
5847 void FreeToolButtons(void)
5851 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5852 FreeGadget(tool_gadget[i]);
5855 static void UnmapToolButtons(void)
5859 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5860 UnmapGadget(tool_gadget[i]);
5863 static void HandleToolButtons(struct GadgetInfo *gi)
5865 request_gadget_id = gi->custom_id;
5868 static struct Mapping_EM_to_RND_object
5871 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5872 boolean is_backside; // backside of moving element
5878 em_object_mapping_list[GAME_TILE_MAX + 1] =
5881 Zborder, FALSE, FALSE,
5885 Zplayer, FALSE, FALSE,
5894 Ztank, FALSE, FALSE,
5898 Zeater, FALSE, FALSE,
5902 Zdynamite, FALSE, FALSE,
5906 Zboom, FALSE, FALSE,
5911 Xchain, FALSE, FALSE,
5912 EL_DEFAULT, ACTION_EXPLODING, -1
5915 Xboom_bug, FALSE, FALSE,
5916 EL_BUG, ACTION_EXPLODING, -1
5919 Xboom_tank, FALSE, FALSE,
5920 EL_SPACESHIP, ACTION_EXPLODING, -1
5923 Xboom_android, FALSE, FALSE,
5924 EL_EMC_ANDROID, ACTION_OTHER, -1
5927 Xboom_1, FALSE, FALSE,
5928 EL_DEFAULT, ACTION_EXPLODING, -1
5931 Xboom_2, FALSE, FALSE,
5932 EL_DEFAULT, ACTION_EXPLODING, -1
5936 Xblank, TRUE, FALSE,
5941 Xsplash_e, FALSE, FALSE,
5942 EL_ACID_SPLASH_RIGHT, -1, -1
5945 Xsplash_w, FALSE, FALSE,
5946 EL_ACID_SPLASH_LEFT, -1, -1
5950 Xplant, TRUE, FALSE,
5951 EL_EMC_PLANT, -1, -1
5954 Yplant, FALSE, FALSE,
5955 EL_EMC_PLANT, -1, -1
5959 Xacid_1, TRUE, FALSE,
5963 Xacid_2, FALSE, FALSE,
5967 Xacid_3, FALSE, FALSE,
5971 Xacid_4, FALSE, FALSE,
5975 Xacid_5, FALSE, FALSE,
5979 Xacid_6, FALSE, FALSE,
5983 Xacid_7, FALSE, FALSE,
5987 Xacid_8, FALSE, FALSE,
5992 Xfake_acid_1, TRUE, FALSE,
5993 EL_EMC_FAKE_ACID, -1, -1
5996 Xfake_acid_2, FALSE, FALSE,
5997 EL_EMC_FAKE_ACID, -1, -1
6000 Xfake_acid_3, FALSE, FALSE,
6001 EL_EMC_FAKE_ACID, -1, -1
6004 Xfake_acid_4, FALSE, FALSE,
6005 EL_EMC_FAKE_ACID, -1, -1
6008 Xfake_acid_5, FALSE, FALSE,
6009 EL_EMC_FAKE_ACID, -1, -1
6012 Xfake_acid_6, FALSE, FALSE,
6013 EL_EMC_FAKE_ACID, -1, -1
6016 Xfake_acid_7, FALSE, FALSE,
6017 EL_EMC_FAKE_ACID, -1, -1
6020 Xfake_acid_8, FALSE, FALSE,
6021 EL_EMC_FAKE_ACID, -1, -1
6025 Xfake_acid_1_player, FALSE, FALSE,
6026 EL_EMC_FAKE_ACID, -1, -1
6029 Xfake_acid_2_player, FALSE, FALSE,
6030 EL_EMC_FAKE_ACID, -1, -1
6033 Xfake_acid_3_player, FALSE, FALSE,
6034 EL_EMC_FAKE_ACID, -1, -1
6037 Xfake_acid_4_player, FALSE, FALSE,
6038 EL_EMC_FAKE_ACID, -1, -1
6041 Xfake_acid_5_player, FALSE, FALSE,
6042 EL_EMC_FAKE_ACID, -1, -1
6045 Xfake_acid_6_player, FALSE, FALSE,
6046 EL_EMC_FAKE_ACID, -1, -1
6049 Xfake_acid_7_player, FALSE, FALSE,
6050 EL_EMC_FAKE_ACID, -1, -1
6053 Xfake_acid_8_player, FALSE, FALSE,
6054 EL_EMC_FAKE_ACID, -1, -1
6058 Xgrass, TRUE, FALSE,
6059 EL_EMC_GRASS, -1, -1
6062 Ygrass_nB, FALSE, FALSE,
6063 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6066 Ygrass_eB, FALSE, FALSE,
6067 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6070 Ygrass_sB, FALSE, FALSE,
6071 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6074 Ygrass_wB, FALSE, FALSE,
6075 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6083 Ydirt_nB, FALSE, FALSE,
6084 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6087 Ydirt_eB, FALSE, FALSE,
6088 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6091 Ydirt_sB, FALSE, FALSE,
6092 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6095 Ydirt_wB, FALSE, FALSE,
6096 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6100 Xandroid, TRUE, FALSE,
6101 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6104 Xandroid_1_n, FALSE, FALSE,
6105 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6108 Xandroid_2_n, FALSE, FALSE,
6109 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6112 Xandroid_1_e, FALSE, FALSE,
6113 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6116 Xandroid_2_e, FALSE, FALSE,
6117 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6120 Xandroid_1_w, FALSE, FALSE,
6121 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6124 Xandroid_2_w, FALSE, FALSE,
6125 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6128 Xandroid_1_s, FALSE, FALSE,
6129 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6132 Xandroid_2_s, FALSE, FALSE,
6133 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6136 Yandroid_n, FALSE, FALSE,
6137 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6140 Yandroid_nB, FALSE, TRUE,
6141 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6144 Yandroid_ne, FALSE, FALSE,
6145 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6148 Yandroid_neB, FALSE, TRUE,
6149 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6152 Yandroid_e, FALSE, FALSE,
6153 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6156 Yandroid_eB, FALSE, TRUE,
6157 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6160 Yandroid_se, FALSE, FALSE,
6161 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6164 Yandroid_seB, FALSE, TRUE,
6165 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6168 Yandroid_s, FALSE, FALSE,
6169 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6172 Yandroid_sB, FALSE, TRUE,
6173 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6176 Yandroid_sw, FALSE, FALSE,
6177 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6180 Yandroid_swB, FALSE, TRUE,
6181 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6184 Yandroid_w, FALSE, FALSE,
6185 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6188 Yandroid_wB, FALSE, TRUE,
6189 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6192 Yandroid_nw, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6196 Yandroid_nwB, FALSE, TRUE,
6197 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6201 Xeater_n, TRUE, FALSE,
6202 EL_YAMYAM_UP, -1, -1
6205 Xeater_e, TRUE, FALSE,
6206 EL_YAMYAM_RIGHT, -1, -1
6209 Xeater_w, TRUE, FALSE,
6210 EL_YAMYAM_LEFT, -1, -1
6213 Xeater_s, TRUE, FALSE,
6214 EL_YAMYAM_DOWN, -1, -1
6217 Yeater_n, FALSE, FALSE,
6218 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6221 Yeater_nB, FALSE, TRUE,
6222 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6225 Yeater_e, FALSE, FALSE,
6226 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6229 Yeater_eB, FALSE, TRUE,
6230 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6233 Yeater_s, FALSE, FALSE,
6234 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6237 Yeater_sB, FALSE, TRUE,
6238 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6241 Yeater_w, FALSE, FALSE,
6242 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6245 Yeater_wB, FALSE, TRUE,
6246 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6249 Yeater_stone, FALSE, FALSE,
6250 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6253 Yeater_spring, FALSE, FALSE,
6254 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6258 Xalien, TRUE, FALSE,
6262 Xalien_pause, FALSE, FALSE,
6266 Yalien_n, FALSE, FALSE,
6267 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6270 Yalien_nB, FALSE, TRUE,
6271 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6274 Yalien_e, FALSE, FALSE,
6275 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6278 Yalien_eB, FALSE, TRUE,
6279 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6282 Yalien_s, FALSE, FALSE,
6283 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6286 Yalien_sB, FALSE, TRUE,
6287 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6290 Yalien_w, FALSE, FALSE,
6291 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6294 Yalien_wB, FALSE, TRUE,
6295 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6298 Yalien_stone, FALSE, FALSE,
6299 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6302 Yalien_spring, FALSE, FALSE,
6303 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6307 Xbug_1_n, TRUE, FALSE,
6311 Xbug_1_e, TRUE, FALSE,
6312 EL_BUG_RIGHT, -1, -1
6315 Xbug_1_s, TRUE, FALSE,
6319 Xbug_1_w, TRUE, FALSE,
6323 Xbug_2_n, FALSE, FALSE,
6327 Xbug_2_e, FALSE, FALSE,
6328 EL_BUG_RIGHT, -1, -1
6331 Xbug_2_s, FALSE, FALSE,
6335 Xbug_2_w, FALSE, FALSE,
6339 Ybug_n, FALSE, FALSE,
6340 EL_BUG, ACTION_MOVING, MV_BIT_UP
6343 Ybug_nB, FALSE, TRUE,
6344 EL_BUG, ACTION_MOVING, MV_BIT_UP
6347 Ybug_e, FALSE, FALSE,
6348 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6351 Ybug_eB, FALSE, TRUE,
6352 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6355 Ybug_s, FALSE, FALSE,
6356 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6359 Ybug_sB, FALSE, TRUE,
6360 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6363 Ybug_w, FALSE, FALSE,
6364 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6367 Ybug_wB, FALSE, TRUE,
6368 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6371 Ybug_w_n, FALSE, FALSE,
6372 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6375 Ybug_n_e, FALSE, FALSE,
6376 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6379 Ybug_e_s, FALSE, FALSE,
6380 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6383 Ybug_s_w, FALSE, FALSE,
6384 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6387 Ybug_e_n, FALSE, FALSE,
6388 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6391 Ybug_s_e, FALSE, FALSE,
6392 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6395 Ybug_w_s, FALSE, FALSE,
6396 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6399 Ybug_n_w, FALSE, FALSE,
6400 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6403 Ybug_stone, FALSE, FALSE,
6404 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6407 Ybug_spring, FALSE, FALSE,
6408 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6412 Xtank_1_n, TRUE, FALSE,
6413 EL_SPACESHIP_UP, -1, -1
6416 Xtank_1_e, TRUE, FALSE,
6417 EL_SPACESHIP_RIGHT, -1, -1
6420 Xtank_1_s, TRUE, FALSE,
6421 EL_SPACESHIP_DOWN, -1, -1
6424 Xtank_1_w, TRUE, FALSE,
6425 EL_SPACESHIP_LEFT, -1, -1
6428 Xtank_2_n, FALSE, FALSE,
6429 EL_SPACESHIP_UP, -1, -1
6432 Xtank_2_e, FALSE, FALSE,
6433 EL_SPACESHIP_RIGHT, -1, -1
6436 Xtank_2_s, FALSE, FALSE,
6437 EL_SPACESHIP_DOWN, -1, -1
6440 Xtank_2_w, FALSE, FALSE,
6441 EL_SPACESHIP_LEFT, -1, -1
6444 Ytank_n, FALSE, FALSE,
6445 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6448 Ytank_nB, FALSE, TRUE,
6449 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6452 Ytank_e, FALSE, FALSE,
6453 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6456 Ytank_eB, FALSE, TRUE,
6457 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6460 Ytank_s, FALSE, FALSE,
6461 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6464 Ytank_sB, FALSE, TRUE,
6465 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6468 Ytank_w, FALSE, FALSE,
6469 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6472 Ytank_wB, FALSE, TRUE,
6473 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6476 Ytank_w_n, FALSE, FALSE,
6477 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6480 Ytank_n_e, FALSE, FALSE,
6481 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6484 Ytank_e_s, FALSE, FALSE,
6485 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6488 Ytank_s_w, FALSE, FALSE,
6489 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6492 Ytank_e_n, FALSE, FALSE,
6493 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6496 Ytank_s_e, FALSE, FALSE,
6497 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6500 Ytank_w_s, FALSE, FALSE,
6501 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6504 Ytank_n_w, FALSE, FALSE,
6505 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6508 Ytank_stone, FALSE, FALSE,
6509 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6512 Ytank_spring, FALSE, FALSE,
6513 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6517 Xemerald, TRUE, FALSE,
6521 Xemerald_pause, FALSE, FALSE,
6525 Xemerald_fall, FALSE, FALSE,
6529 Xemerald_shine, FALSE, FALSE,
6530 EL_EMERALD, ACTION_TWINKLING, -1
6533 Yemerald_s, FALSE, FALSE,
6534 EL_EMERALD, ACTION_FALLING, -1
6537 Yemerald_sB, FALSE, TRUE,
6538 EL_EMERALD, ACTION_FALLING, -1
6541 Yemerald_e, FALSE, FALSE,
6542 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6545 Yemerald_eB, FALSE, TRUE,
6546 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6549 Yemerald_w, FALSE, FALSE,
6550 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6553 Yemerald_wB, FALSE, TRUE,
6554 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6557 Yemerald_blank, FALSE, FALSE,
6558 EL_EMERALD, ACTION_COLLECTING, -1
6562 Xdiamond, TRUE, FALSE,
6566 Xdiamond_pause, FALSE, FALSE,
6570 Xdiamond_fall, FALSE, FALSE,
6574 Xdiamond_shine, FALSE, FALSE,
6575 EL_DIAMOND, ACTION_TWINKLING, -1
6578 Ydiamond_s, FALSE, FALSE,
6579 EL_DIAMOND, ACTION_FALLING, -1
6582 Ydiamond_sB, FALSE, TRUE,
6583 EL_DIAMOND, ACTION_FALLING, -1
6586 Ydiamond_e, FALSE, FALSE,
6587 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6590 Ydiamond_eB, FALSE, TRUE,
6591 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6594 Ydiamond_w, FALSE, FALSE,
6595 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6598 Ydiamond_wB, FALSE, TRUE,
6599 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6602 Ydiamond_blank, FALSE, FALSE,
6603 EL_DIAMOND, ACTION_COLLECTING, -1
6606 Ydiamond_stone, FALSE, FALSE,
6607 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6611 Xstone, TRUE, FALSE,
6615 Xstone_pause, FALSE, FALSE,
6619 Xstone_fall, FALSE, FALSE,
6623 Ystone_s, FALSE, FALSE,
6624 EL_ROCK, ACTION_FALLING, -1
6627 Ystone_sB, FALSE, TRUE,
6628 EL_ROCK, ACTION_FALLING, -1
6631 Ystone_e, FALSE, FALSE,
6632 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6635 Ystone_eB, FALSE, TRUE,
6636 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6639 Ystone_w, FALSE, FALSE,
6640 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6643 Ystone_wB, FALSE, TRUE,
6644 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6652 Xbomb_pause, FALSE, FALSE,
6656 Xbomb_fall, FALSE, FALSE,
6660 Ybomb_s, FALSE, FALSE,
6661 EL_BOMB, ACTION_FALLING, -1
6664 Ybomb_sB, FALSE, TRUE,
6665 EL_BOMB, ACTION_FALLING, -1
6668 Ybomb_e, FALSE, FALSE,
6669 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6672 Ybomb_eB, FALSE, TRUE,
6673 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6676 Ybomb_w, FALSE, FALSE,
6677 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6680 Ybomb_wB, FALSE, TRUE,
6681 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6684 Ybomb_blank, FALSE, FALSE,
6685 EL_BOMB, ACTION_ACTIVATING, -1
6693 Xnut_pause, FALSE, FALSE,
6697 Xnut_fall, FALSE, FALSE,
6701 Ynut_s, FALSE, FALSE,
6702 EL_NUT, ACTION_FALLING, -1
6705 Ynut_sB, FALSE, TRUE,
6706 EL_NUT, ACTION_FALLING, -1
6709 Ynut_e, FALSE, FALSE,
6710 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6713 Ynut_eB, FALSE, TRUE,
6714 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6717 Ynut_w, FALSE, FALSE,
6718 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6721 Ynut_wB, FALSE, TRUE,
6722 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6725 Ynut_stone, FALSE, FALSE,
6726 EL_NUT, ACTION_BREAKING, -1
6730 Xspring, TRUE, FALSE,
6734 Xspring_pause, FALSE, FALSE,
6738 Xspring_e, TRUE, FALSE,
6739 EL_SPRING_RIGHT, -1, -1
6742 Xspring_w, TRUE, FALSE,
6743 EL_SPRING_LEFT, -1, -1
6746 Xspring_fall, FALSE, FALSE,
6750 Yspring_s, FALSE, FALSE,
6751 EL_SPRING, ACTION_FALLING, -1
6754 Yspring_sB, FALSE, TRUE,
6755 EL_SPRING, ACTION_FALLING, -1
6758 Yspring_e, FALSE, FALSE,
6759 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6762 Yspring_eB, FALSE, TRUE,
6763 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6766 Yspring_w, FALSE, FALSE,
6767 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6770 Yspring_wB, FALSE, TRUE,
6771 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6774 Yspring_alien_e, FALSE, FALSE,
6775 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6778 Yspring_alien_eB, FALSE, TRUE,
6779 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6782 Yspring_alien_w, FALSE, FALSE,
6783 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6786 Yspring_alien_wB, FALSE, TRUE,
6787 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6791 Xpush_emerald_e, FALSE, FALSE,
6792 EL_EMERALD, -1, MV_BIT_RIGHT
6795 Xpush_emerald_w, FALSE, FALSE,
6796 EL_EMERALD, -1, MV_BIT_LEFT
6799 Xpush_diamond_e, FALSE, FALSE,
6800 EL_DIAMOND, -1, MV_BIT_RIGHT
6803 Xpush_diamond_w, FALSE, FALSE,
6804 EL_DIAMOND, -1, MV_BIT_LEFT
6807 Xpush_stone_e, FALSE, FALSE,
6808 EL_ROCK, -1, MV_BIT_RIGHT
6811 Xpush_stone_w, FALSE, FALSE,
6812 EL_ROCK, -1, MV_BIT_LEFT
6815 Xpush_bomb_e, FALSE, FALSE,
6816 EL_BOMB, -1, MV_BIT_RIGHT
6819 Xpush_bomb_w, FALSE, FALSE,
6820 EL_BOMB, -1, MV_BIT_LEFT
6823 Xpush_nut_e, FALSE, FALSE,
6824 EL_NUT, -1, MV_BIT_RIGHT
6827 Xpush_nut_w, FALSE, FALSE,
6828 EL_NUT, -1, MV_BIT_LEFT
6831 Xpush_spring_e, FALSE, FALSE,
6832 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6835 Xpush_spring_w, FALSE, FALSE,
6836 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6840 Xdynamite, TRUE, FALSE,
6841 EL_EM_DYNAMITE, -1, -1
6844 Ydynamite_blank, FALSE, FALSE,
6845 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6848 Xdynamite_1, TRUE, FALSE,
6849 EL_EM_DYNAMITE_ACTIVE, -1, -1
6852 Xdynamite_2, FALSE, FALSE,
6853 EL_EM_DYNAMITE_ACTIVE, -1, -1
6856 Xdynamite_3, FALSE, FALSE,
6857 EL_EM_DYNAMITE_ACTIVE, -1, -1
6860 Xdynamite_4, FALSE, FALSE,
6861 EL_EM_DYNAMITE_ACTIVE, -1, -1
6865 Xkey_1, TRUE, FALSE,
6869 Xkey_2, TRUE, FALSE,
6873 Xkey_3, TRUE, FALSE,
6877 Xkey_4, TRUE, FALSE,
6881 Xkey_5, TRUE, FALSE,
6882 EL_EMC_KEY_5, -1, -1
6885 Xkey_6, TRUE, FALSE,
6886 EL_EMC_KEY_6, -1, -1
6889 Xkey_7, TRUE, FALSE,
6890 EL_EMC_KEY_7, -1, -1
6893 Xkey_8, TRUE, FALSE,
6894 EL_EMC_KEY_8, -1, -1
6898 Xdoor_1, TRUE, FALSE,
6899 EL_EM_GATE_1, -1, -1
6902 Xdoor_2, TRUE, FALSE,
6903 EL_EM_GATE_2, -1, -1
6906 Xdoor_3, TRUE, FALSE,
6907 EL_EM_GATE_3, -1, -1
6910 Xdoor_4, TRUE, FALSE,
6911 EL_EM_GATE_4, -1, -1
6914 Xdoor_5, TRUE, FALSE,
6915 EL_EMC_GATE_5, -1, -1
6918 Xdoor_6, TRUE, FALSE,
6919 EL_EMC_GATE_6, -1, -1
6922 Xdoor_7, TRUE, FALSE,
6923 EL_EMC_GATE_7, -1, -1
6926 Xdoor_8, TRUE, FALSE,
6927 EL_EMC_GATE_8, -1, -1
6931 Xfake_door_1, TRUE, FALSE,
6932 EL_EM_GATE_1_GRAY, -1, -1
6935 Xfake_door_2, TRUE, FALSE,
6936 EL_EM_GATE_2_GRAY, -1, -1
6939 Xfake_door_3, TRUE, FALSE,
6940 EL_EM_GATE_3_GRAY, -1, -1
6943 Xfake_door_4, TRUE, FALSE,
6944 EL_EM_GATE_4_GRAY, -1, -1
6947 Xfake_door_5, TRUE, FALSE,
6948 EL_EMC_GATE_5_GRAY, -1, -1
6951 Xfake_door_6, TRUE, FALSE,
6952 EL_EMC_GATE_6_GRAY, -1, -1
6955 Xfake_door_7, TRUE, FALSE,
6956 EL_EMC_GATE_7_GRAY, -1, -1
6959 Xfake_door_8, TRUE, FALSE,
6960 EL_EMC_GATE_8_GRAY, -1, -1
6964 Xballoon, TRUE, FALSE,
6968 Yballoon_n, FALSE, FALSE,
6969 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6972 Yballoon_nB, FALSE, TRUE,
6973 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6976 Yballoon_e, FALSE, FALSE,
6977 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6980 Yballoon_eB, FALSE, TRUE,
6981 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6984 Yballoon_s, FALSE, FALSE,
6985 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6988 Yballoon_sB, FALSE, TRUE,
6989 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6992 Yballoon_w, FALSE, FALSE,
6993 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6996 Yballoon_wB, FALSE, TRUE,
6997 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7001 Xball_1, TRUE, FALSE,
7002 EL_EMC_MAGIC_BALL, -1, -1
7005 Yball_1, FALSE, FALSE,
7006 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7009 Xball_2, FALSE, FALSE,
7010 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7013 Yball_2, FALSE, FALSE,
7014 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7017 Yball_blank, FALSE, FALSE,
7018 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7022 Xamoeba_1, TRUE, FALSE,
7023 EL_AMOEBA_DRY, ACTION_OTHER, -1
7026 Xamoeba_2, FALSE, FALSE,
7027 EL_AMOEBA_DRY, ACTION_OTHER, -1
7030 Xamoeba_3, FALSE, FALSE,
7031 EL_AMOEBA_DRY, ACTION_OTHER, -1
7034 Xamoeba_4, FALSE, FALSE,
7035 EL_AMOEBA_DRY, ACTION_OTHER, -1
7038 Xamoeba_5, TRUE, FALSE,
7039 EL_AMOEBA_WET, ACTION_OTHER, -1
7042 Xamoeba_6, FALSE, FALSE,
7043 EL_AMOEBA_WET, ACTION_OTHER, -1
7046 Xamoeba_7, FALSE, FALSE,
7047 EL_AMOEBA_WET, ACTION_OTHER, -1
7050 Xamoeba_8, FALSE, FALSE,
7051 EL_AMOEBA_WET, ACTION_OTHER, -1
7056 EL_AMOEBA_DROP, ACTION_GROWING, -1
7059 Xdrip_fall, FALSE, FALSE,
7060 EL_AMOEBA_DROP, -1, -1
7063 Xdrip_stretch, FALSE, FALSE,
7064 EL_AMOEBA_DROP, ACTION_FALLING, -1
7067 Xdrip_stretchB, FALSE, TRUE,
7068 EL_AMOEBA_DROP, ACTION_FALLING, -1
7071 Ydrip_1_s, FALSE, FALSE,
7072 EL_AMOEBA_DROP, ACTION_FALLING, -1
7075 Ydrip_1_sB, FALSE, TRUE,
7076 EL_AMOEBA_DROP, ACTION_FALLING, -1
7079 Ydrip_2_s, FALSE, FALSE,
7080 EL_AMOEBA_DROP, ACTION_FALLING, -1
7083 Ydrip_2_sB, FALSE, TRUE,
7084 EL_AMOEBA_DROP, ACTION_FALLING, -1
7088 Xwonderwall, TRUE, FALSE,
7089 EL_MAGIC_WALL, -1, -1
7092 Ywonderwall, FALSE, FALSE,
7093 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7097 Xwheel, TRUE, FALSE,
7098 EL_ROBOT_WHEEL, -1, -1
7101 Ywheel, FALSE, FALSE,
7102 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7106 Xswitch, TRUE, FALSE,
7107 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7110 Yswitch, FALSE, FALSE,
7111 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7115 Xbumper, TRUE, FALSE,
7116 EL_EMC_SPRING_BUMPER, -1, -1
7119 Ybumper, FALSE, FALSE,
7120 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7124 Xacid_nw, TRUE, FALSE,
7125 EL_ACID_POOL_TOPLEFT, -1, -1
7128 Xacid_ne, TRUE, FALSE,
7129 EL_ACID_POOL_TOPRIGHT, -1, -1
7132 Xacid_sw, TRUE, FALSE,
7133 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7136 Xacid_s, TRUE, FALSE,
7137 EL_ACID_POOL_BOTTOM, -1, -1
7140 Xacid_se, TRUE, FALSE,
7141 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7145 Xfake_blank, TRUE, FALSE,
7146 EL_INVISIBLE_WALL, -1, -1
7149 Yfake_blank, FALSE, FALSE,
7150 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7154 Xfake_grass, TRUE, FALSE,
7155 EL_EMC_FAKE_GRASS, -1, -1
7158 Yfake_grass, FALSE, FALSE,
7159 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7163 Xfake_amoeba, TRUE, FALSE,
7164 EL_EMC_DRIPPER, -1, -1
7167 Yfake_amoeba, FALSE, FALSE,
7168 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7172 Xlenses, TRUE, FALSE,
7173 EL_EMC_LENSES, -1, -1
7177 Xmagnify, TRUE, FALSE,
7178 EL_EMC_MAGNIFIER, -1, -1
7183 EL_QUICKSAND_EMPTY, -1, -1
7186 Xsand_stone, TRUE, FALSE,
7187 EL_QUICKSAND_FULL, -1, -1
7190 Xsand_stonein_1, FALSE, TRUE,
7191 EL_ROCK, ACTION_FILLING, -1
7194 Xsand_stonein_2, FALSE, TRUE,
7195 EL_ROCK, ACTION_FILLING, -1
7198 Xsand_stonein_3, FALSE, TRUE,
7199 EL_ROCK, ACTION_FILLING, -1
7202 Xsand_stonein_4, FALSE, TRUE,
7203 EL_ROCK, ACTION_FILLING, -1
7206 Xsand_sandstone_1, FALSE, FALSE,
7207 EL_QUICKSAND_FILLING, -1, -1
7210 Xsand_sandstone_2, FALSE, FALSE,
7211 EL_QUICKSAND_FILLING, -1, -1
7214 Xsand_sandstone_3, FALSE, FALSE,
7215 EL_QUICKSAND_FILLING, -1, -1
7218 Xsand_sandstone_4, FALSE, FALSE,
7219 EL_QUICKSAND_FILLING, -1, -1
7222 Xsand_stonesand_1, FALSE, FALSE,
7223 EL_QUICKSAND_EMPTYING, -1, -1
7226 Xsand_stonesand_2, FALSE, FALSE,
7227 EL_QUICKSAND_EMPTYING, -1, -1
7230 Xsand_stonesand_3, FALSE, FALSE,
7231 EL_QUICKSAND_EMPTYING, -1, -1
7234 Xsand_stonesand_4, FALSE, FALSE,
7235 EL_QUICKSAND_EMPTYING, -1, -1
7238 Xsand_stoneout_1, FALSE, FALSE,
7239 EL_ROCK, ACTION_EMPTYING, -1
7242 Xsand_stoneout_2, FALSE, FALSE,
7243 EL_ROCK, ACTION_EMPTYING, -1
7246 Xsand_stonesand_quickout_1, FALSE, FALSE,
7247 EL_QUICKSAND_EMPTYING, -1, -1
7250 Xsand_stonesand_quickout_2, FALSE, FALSE,
7251 EL_QUICKSAND_EMPTYING, -1, -1
7255 Xslide_ns, TRUE, FALSE,
7256 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7259 Yslide_ns_blank, FALSE, FALSE,
7260 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7263 Xslide_ew, TRUE, FALSE,
7264 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7267 Yslide_ew_blank, FALSE, FALSE,
7268 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7272 Xwind_n, TRUE, FALSE,
7273 EL_BALLOON_SWITCH_UP, -1, -1
7276 Xwind_e, TRUE, FALSE,
7277 EL_BALLOON_SWITCH_RIGHT, -1, -1
7280 Xwind_s, TRUE, FALSE,
7281 EL_BALLOON_SWITCH_DOWN, -1, -1
7284 Xwind_w, TRUE, FALSE,
7285 EL_BALLOON_SWITCH_LEFT, -1, -1
7288 Xwind_any, TRUE, FALSE,
7289 EL_BALLOON_SWITCH_ANY, -1, -1
7292 Xwind_stop, TRUE, FALSE,
7293 EL_BALLOON_SWITCH_NONE, -1, -1
7298 EL_EM_EXIT_CLOSED, -1, -1
7301 Xexit_1, TRUE, FALSE,
7302 EL_EM_EXIT_OPEN, -1, -1
7305 Xexit_2, FALSE, FALSE,
7306 EL_EM_EXIT_OPEN, -1, -1
7309 Xexit_3, FALSE, FALSE,
7310 EL_EM_EXIT_OPEN, -1, -1
7314 Xpause, FALSE, FALSE,
7319 Xwall_1, TRUE, FALSE,
7323 Xwall_2, TRUE, FALSE,
7324 EL_EMC_WALL_14, -1, -1
7327 Xwall_3, TRUE, FALSE,
7328 EL_EMC_WALL_15, -1, -1
7331 Xwall_4, TRUE, FALSE,
7332 EL_EMC_WALL_16, -1, -1
7336 Xroundwall_1, TRUE, FALSE,
7337 EL_WALL_SLIPPERY, -1, -1
7340 Xroundwall_2, TRUE, FALSE,
7341 EL_EMC_WALL_SLIPPERY_2, -1, -1
7344 Xroundwall_3, TRUE, FALSE,
7345 EL_EMC_WALL_SLIPPERY_3, -1, -1
7348 Xroundwall_4, TRUE, FALSE,
7349 EL_EMC_WALL_SLIPPERY_4, -1, -1
7353 Xsteel_1, TRUE, FALSE,
7354 EL_STEELWALL, -1, -1
7357 Xsteel_2, TRUE, FALSE,
7358 EL_EMC_STEELWALL_2, -1, -1
7361 Xsteel_3, TRUE, FALSE,
7362 EL_EMC_STEELWALL_3, -1, -1
7365 Xsteel_4, TRUE, FALSE,
7366 EL_EMC_STEELWALL_4, -1, -1
7370 Xdecor_1, TRUE, FALSE,
7371 EL_EMC_WALL_8, -1, -1
7374 Xdecor_2, TRUE, FALSE,
7375 EL_EMC_WALL_6, -1, -1
7378 Xdecor_3, TRUE, FALSE,
7379 EL_EMC_WALL_4, -1, -1
7382 Xdecor_4, TRUE, FALSE,
7383 EL_EMC_WALL_7, -1, -1
7386 Xdecor_5, TRUE, FALSE,
7387 EL_EMC_WALL_5, -1, -1
7390 Xdecor_6, TRUE, FALSE,
7391 EL_EMC_WALL_9, -1, -1
7394 Xdecor_7, TRUE, FALSE,
7395 EL_EMC_WALL_10, -1, -1
7398 Xdecor_8, TRUE, FALSE,
7399 EL_EMC_WALL_1, -1, -1
7402 Xdecor_9, TRUE, FALSE,
7403 EL_EMC_WALL_2, -1, -1
7406 Xdecor_10, TRUE, FALSE,
7407 EL_EMC_WALL_3, -1, -1
7410 Xdecor_11, TRUE, FALSE,
7411 EL_EMC_WALL_11, -1, -1
7414 Xdecor_12, TRUE, FALSE,
7415 EL_EMC_WALL_12, -1, -1
7419 Xalpha_0, TRUE, FALSE,
7420 EL_CHAR('0'), -1, -1
7423 Xalpha_1, TRUE, FALSE,
7424 EL_CHAR('1'), -1, -1
7427 Xalpha_2, TRUE, FALSE,
7428 EL_CHAR('2'), -1, -1
7431 Xalpha_3, TRUE, FALSE,
7432 EL_CHAR('3'), -1, -1
7435 Xalpha_4, TRUE, FALSE,
7436 EL_CHAR('4'), -1, -1
7439 Xalpha_5, TRUE, FALSE,
7440 EL_CHAR('5'), -1, -1
7443 Xalpha_6, TRUE, FALSE,
7444 EL_CHAR('6'), -1, -1
7447 Xalpha_7, TRUE, FALSE,
7448 EL_CHAR('7'), -1, -1
7451 Xalpha_8, TRUE, FALSE,
7452 EL_CHAR('8'), -1, -1
7455 Xalpha_9, TRUE, FALSE,
7456 EL_CHAR('9'), -1, -1
7459 Xalpha_excla, TRUE, FALSE,
7460 EL_CHAR('!'), -1, -1
7463 Xalpha_apost, TRUE, FALSE,
7464 EL_CHAR('\''), -1, -1
7467 Xalpha_comma, TRUE, FALSE,
7468 EL_CHAR(','), -1, -1
7471 Xalpha_minus, TRUE, FALSE,
7472 EL_CHAR('-'), -1, -1
7475 Xalpha_perio, TRUE, FALSE,
7476 EL_CHAR('.'), -1, -1
7479 Xalpha_colon, TRUE, FALSE,
7480 EL_CHAR(':'), -1, -1
7483 Xalpha_quest, TRUE, FALSE,
7484 EL_CHAR('?'), -1, -1
7487 Xalpha_a, TRUE, FALSE,
7488 EL_CHAR('A'), -1, -1
7491 Xalpha_b, TRUE, FALSE,
7492 EL_CHAR('B'), -1, -1
7495 Xalpha_c, TRUE, FALSE,
7496 EL_CHAR('C'), -1, -1
7499 Xalpha_d, TRUE, FALSE,
7500 EL_CHAR('D'), -1, -1
7503 Xalpha_e, TRUE, FALSE,
7504 EL_CHAR('E'), -1, -1
7507 Xalpha_f, TRUE, FALSE,
7508 EL_CHAR('F'), -1, -1
7511 Xalpha_g, TRUE, FALSE,
7512 EL_CHAR('G'), -1, -1
7515 Xalpha_h, TRUE, FALSE,
7516 EL_CHAR('H'), -1, -1
7519 Xalpha_i, TRUE, FALSE,
7520 EL_CHAR('I'), -1, -1
7523 Xalpha_j, TRUE, FALSE,
7524 EL_CHAR('J'), -1, -1
7527 Xalpha_k, TRUE, FALSE,
7528 EL_CHAR('K'), -1, -1
7531 Xalpha_l, TRUE, FALSE,
7532 EL_CHAR('L'), -1, -1
7535 Xalpha_m, TRUE, FALSE,
7536 EL_CHAR('M'), -1, -1
7539 Xalpha_n, TRUE, FALSE,
7540 EL_CHAR('N'), -1, -1
7543 Xalpha_o, TRUE, FALSE,
7544 EL_CHAR('O'), -1, -1
7547 Xalpha_p, TRUE, FALSE,
7548 EL_CHAR('P'), -1, -1
7551 Xalpha_q, TRUE, FALSE,
7552 EL_CHAR('Q'), -1, -1
7555 Xalpha_r, TRUE, FALSE,
7556 EL_CHAR('R'), -1, -1
7559 Xalpha_s, TRUE, FALSE,
7560 EL_CHAR('S'), -1, -1
7563 Xalpha_t, TRUE, FALSE,
7564 EL_CHAR('T'), -1, -1
7567 Xalpha_u, TRUE, FALSE,
7568 EL_CHAR('U'), -1, -1
7571 Xalpha_v, TRUE, FALSE,
7572 EL_CHAR('V'), -1, -1
7575 Xalpha_w, TRUE, FALSE,
7576 EL_CHAR('W'), -1, -1
7579 Xalpha_x, TRUE, FALSE,
7580 EL_CHAR('X'), -1, -1
7583 Xalpha_y, TRUE, FALSE,
7584 EL_CHAR('Y'), -1, -1
7587 Xalpha_z, TRUE, FALSE,
7588 EL_CHAR('Z'), -1, -1
7591 Xalpha_arrow_e, TRUE, FALSE,
7592 EL_CHAR('>'), -1, -1
7595 Xalpha_arrow_w, TRUE, FALSE,
7596 EL_CHAR('<'), -1, -1
7599 Xalpha_copyr, TRUE, FALSE,
7600 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7604 Ykey_1_blank, FALSE, FALSE,
7605 EL_EM_KEY_1, ACTION_COLLECTING, -1
7608 Ykey_2_blank, FALSE, FALSE,
7609 EL_EM_KEY_2, ACTION_COLLECTING, -1
7612 Ykey_3_blank, FALSE, FALSE,
7613 EL_EM_KEY_3, ACTION_COLLECTING, -1
7616 Ykey_4_blank, FALSE, FALSE,
7617 EL_EM_KEY_4, ACTION_COLLECTING, -1
7620 Ykey_5_blank, FALSE, FALSE,
7621 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7624 Ykey_6_blank, FALSE, FALSE,
7625 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7628 Ykey_7_blank, FALSE, FALSE,
7629 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7632 Ykey_8_blank, FALSE, FALSE,
7633 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7636 Ylenses_blank, FALSE, FALSE,
7637 EL_EMC_LENSES, ACTION_COLLECTING, -1
7640 Ymagnify_blank, FALSE, FALSE,
7641 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7644 Ygrass_blank, FALSE, FALSE,
7645 EL_EMC_GRASS, ACTION_SNAPPING, -1
7648 Ydirt_blank, FALSE, FALSE,
7649 EL_SAND, ACTION_SNAPPING, -1
7658 static struct Mapping_EM_to_RND_player
7667 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7671 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7675 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7679 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7683 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7687 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7691 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7695 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7699 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7703 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7707 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7711 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7715 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7719 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7723 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7727 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7731 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7735 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7739 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7743 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7747 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7751 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7755 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7759 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7763 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7767 EL_PLAYER_1, ACTION_DEFAULT, -1,
7771 EL_PLAYER_2, ACTION_DEFAULT, -1,
7775 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7779 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7783 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7787 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7791 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7795 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7799 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7803 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7807 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7811 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7815 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7819 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7823 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7827 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7831 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7835 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7839 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7843 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7847 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7851 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7855 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7859 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7863 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7867 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7871 EL_PLAYER_3, ACTION_DEFAULT, -1,
7875 EL_PLAYER_4, ACTION_DEFAULT, -1,
7884 int map_element_RND_to_EM_cave(int element_rnd)
7886 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7887 static boolean mapping_initialized = FALSE;
7889 if (!mapping_initialized)
7893 // return "Xalpha_quest" for all undefined elements in mapping array
7894 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7895 mapping_RND_to_EM[i] = Xalpha_quest;
7897 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7898 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7899 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7900 em_object_mapping_list[i].element_em;
7902 mapping_initialized = TRUE;
7905 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7907 Warn("invalid RND level element %d", element_rnd);
7912 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7915 int map_element_EM_to_RND_cave(int element_em_cave)
7917 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7918 static boolean mapping_initialized = FALSE;
7920 if (!mapping_initialized)
7924 // return "EL_UNKNOWN" for all undefined elements in mapping array
7925 for (i = 0; i < GAME_TILE_MAX; i++)
7926 mapping_EM_to_RND[i] = EL_UNKNOWN;
7928 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7929 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7930 em_object_mapping_list[i].element_rnd;
7932 mapping_initialized = TRUE;
7935 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7937 Warn("invalid EM cave element %d", element_em_cave);
7942 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7945 int map_element_EM_to_RND_game(int element_em_game)
7947 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7948 static boolean mapping_initialized = FALSE;
7950 if (!mapping_initialized)
7954 // return "EL_UNKNOWN" for all undefined elements in mapping array
7955 for (i = 0; i < GAME_TILE_MAX; i++)
7956 mapping_EM_to_RND[i] = EL_UNKNOWN;
7958 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7959 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7960 em_object_mapping_list[i].element_rnd;
7962 mapping_initialized = TRUE;
7965 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7967 Warn("invalid EM game element %d", element_em_game);
7972 return mapping_EM_to_RND[element_em_game];
7975 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7977 struct LevelInfo_EM *level_em = level->native_em_level;
7978 struct CAVE *cav = level_em->cav;
7981 for (i = 0; i < GAME_TILE_MAX; i++)
7982 cav->android_array[i] = Cblank;
7984 for (i = 0; i < level->num_android_clone_elements; i++)
7986 int element_rnd = level->android_clone_element[i];
7987 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7989 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7990 if (em_object_mapping_list[j].element_rnd == element_rnd)
7991 cav->android_array[em_object_mapping_list[j].element_em] =
7996 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7998 struct LevelInfo_EM *level_em = level->native_em_level;
7999 struct CAVE *cav = level_em->cav;
8002 level->num_android_clone_elements = 0;
8004 for (i = 0; i < GAME_TILE_MAX; i++)
8006 int element_em_cave = cav->android_array[i];
8008 boolean element_found = FALSE;
8010 if (element_em_cave == Cblank)
8013 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8015 for (j = 0; j < level->num_android_clone_elements; j++)
8016 if (level->android_clone_element[j] == element_rnd)
8017 element_found = TRUE;
8021 level->android_clone_element[level->num_android_clone_elements++] =
8024 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8029 if (level->num_android_clone_elements == 0)
8031 level->num_android_clone_elements = 1;
8032 level->android_clone_element[0] = EL_EMPTY;
8036 int map_direction_RND_to_EM(int direction)
8038 return (direction == MV_UP ? 0 :
8039 direction == MV_RIGHT ? 1 :
8040 direction == MV_DOWN ? 2 :
8041 direction == MV_LEFT ? 3 :
8045 int map_direction_EM_to_RND(int direction)
8047 return (direction == 0 ? MV_UP :
8048 direction == 1 ? MV_RIGHT :
8049 direction == 2 ? MV_DOWN :
8050 direction == 3 ? MV_LEFT :
8054 int map_element_RND_to_SP(int element_rnd)
8056 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8058 if (element_rnd >= EL_SP_START &&
8059 element_rnd <= EL_SP_END)
8060 element_sp = element_rnd - EL_SP_START;
8061 else if (element_rnd == EL_EMPTY_SPACE)
8063 else if (element_rnd == EL_INVISIBLE_WALL)
8069 int map_element_SP_to_RND(int element_sp)
8071 int element_rnd = EL_UNKNOWN;
8073 if (element_sp >= 0x00 &&
8075 element_rnd = EL_SP_START + element_sp;
8076 else if (element_sp == 0x28)
8077 element_rnd = EL_INVISIBLE_WALL;
8082 int map_action_SP_to_RND(int action_sp)
8086 case actActive: return ACTION_ACTIVE;
8087 case actImpact: return ACTION_IMPACT;
8088 case actExploding: return ACTION_EXPLODING;
8089 case actDigging: return ACTION_DIGGING;
8090 case actSnapping: return ACTION_SNAPPING;
8091 case actCollecting: return ACTION_COLLECTING;
8092 case actPassing: return ACTION_PASSING;
8093 case actPushing: return ACTION_PUSHING;
8094 case actDropping: return ACTION_DROPPING;
8096 default: return ACTION_DEFAULT;
8100 int map_element_RND_to_MM(int element_rnd)
8102 return (element_rnd >= EL_MM_START_1 &&
8103 element_rnd <= EL_MM_END_1 ?
8104 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8106 element_rnd >= EL_MM_START_2 &&
8107 element_rnd <= EL_MM_END_2 ?
8108 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8110 element_rnd >= EL_CHAR_START &&
8111 element_rnd <= EL_CHAR_END ?
8112 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8114 element_rnd >= EL_MM_RUNTIME_START &&
8115 element_rnd <= EL_MM_RUNTIME_END ?
8116 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8118 element_rnd >= EL_MM_DUMMY_START &&
8119 element_rnd <= EL_MM_DUMMY_END ?
8120 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8122 EL_MM_EMPTY_NATIVE);
8125 int map_element_MM_to_RND(int element_mm)
8127 return (element_mm == EL_MM_EMPTY_NATIVE ||
8128 element_mm == EL_DF_EMPTY_NATIVE ?
8131 element_mm >= EL_MM_START_1_NATIVE &&
8132 element_mm <= EL_MM_END_1_NATIVE ?
8133 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8135 element_mm >= EL_MM_START_2_NATIVE &&
8136 element_mm <= EL_MM_END_2_NATIVE ?
8137 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8139 element_mm >= EL_MM_CHAR_START_NATIVE &&
8140 element_mm <= EL_MM_CHAR_END_NATIVE ?
8141 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8143 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8144 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8145 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8147 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8148 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8149 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8154 int map_action_MM_to_RND(int action_mm)
8156 // all MM actions are defined to exactly match their RND counterparts
8160 int map_sound_MM_to_RND(int sound_mm)
8164 case SND_MM_GAME_LEVELTIME_CHARGING:
8165 return SND_GAME_LEVELTIME_CHARGING;
8167 case SND_MM_GAME_HEALTH_CHARGING:
8168 return SND_GAME_HEALTH_CHARGING;
8171 return SND_UNDEFINED;
8175 int map_mm_wall_element(int element)
8177 return (element >= EL_MM_STEEL_WALL_START &&
8178 element <= EL_MM_STEEL_WALL_END ?
8181 element >= EL_MM_WOODEN_WALL_START &&
8182 element <= EL_MM_WOODEN_WALL_END ?
8185 element >= EL_MM_ICE_WALL_START &&
8186 element <= EL_MM_ICE_WALL_END ?
8189 element >= EL_MM_AMOEBA_WALL_START &&
8190 element <= EL_MM_AMOEBA_WALL_END ?
8193 element >= EL_DF_STEEL_WALL_START &&
8194 element <= EL_DF_STEEL_WALL_END ?
8197 element >= EL_DF_WOODEN_WALL_START &&
8198 element <= EL_DF_WOODEN_WALL_END ?
8204 int map_mm_wall_element_editor(int element)
8208 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8209 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8210 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8211 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8212 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8213 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8215 default: return element;
8219 int get_next_element(int element)
8223 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8224 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8225 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8226 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8227 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8228 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8229 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8230 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8231 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8232 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8233 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8235 default: return element;
8239 int el2img_mm(int element_mm)
8241 return el2img(map_element_MM_to_RND(element_mm));
8244 int el_act_dir2img(int element, int action, int direction)
8246 element = GFX_ELEMENT(element);
8247 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8249 // direction_graphic[][] == graphic[] for undefined direction graphics
8250 return element_info[element].direction_graphic[action][direction];
8253 static int el_act_dir2crm(int element, int action, int direction)
8255 element = GFX_ELEMENT(element);
8256 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8258 // direction_graphic[][] == graphic[] for undefined direction graphics
8259 return element_info[element].direction_crumbled[action][direction];
8262 int el_act2img(int element, int action)
8264 element = GFX_ELEMENT(element);
8266 return element_info[element].graphic[action];
8269 int el_act2crm(int element, int action)
8271 element = GFX_ELEMENT(element);
8273 return element_info[element].crumbled[action];
8276 int el_dir2img(int element, int direction)
8278 element = GFX_ELEMENT(element);
8280 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8283 int el2baseimg(int element)
8285 return element_info[element].graphic[ACTION_DEFAULT];
8288 int el2img(int element)
8290 element = GFX_ELEMENT(element);
8292 return element_info[element].graphic[ACTION_DEFAULT];
8295 int el2edimg(int element)
8297 element = GFX_ELEMENT(element);
8299 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8302 int el2preimg(int element)
8304 element = GFX_ELEMENT(element);
8306 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8309 int el2panelimg(int element)
8311 element = GFX_ELEMENT(element);
8313 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8316 int font2baseimg(int font_nr)
8318 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8321 int getBeltNrFromBeltElement(int element)
8323 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8324 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8325 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8328 int getBeltNrFromBeltActiveElement(int element)
8330 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8331 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8332 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8335 int getBeltNrFromBeltSwitchElement(int element)
8337 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8338 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8339 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8342 int getBeltDirNrFromBeltElement(int element)
8344 static int belt_base_element[4] =
8346 EL_CONVEYOR_BELT_1_LEFT,
8347 EL_CONVEYOR_BELT_2_LEFT,
8348 EL_CONVEYOR_BELT_3_LEFT,
8349 EL_CONVEYOR_BELT_4_LEFT
8352 int belt_nr = getBeltNrFromBeltElement(element);
8353 int belt_dir_nr = element - belt_base_element[belt_nr];
8355 return (belt_dir_nr % 3);
8358 int getBeltDirNrFromBeltSwitchElement(int element)
8360 static int belt_base_element[4] =
8362 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8363 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8364 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8365 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8368 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8369 int belt_dir_nr = element - belt_base_element[belt_nr];
8371 return (belt_dir_nr % 3);
8374 int getBeltDirFromBeltElement(int element)
8376 static int belt_move_dir[3] =
8383 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8385 return belt_move_dir[belt_dir_nr];
8388 int getBeltDirFromBeltSwitchElement(int element)
8390 static int belt_move_dir[3] =
8397 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8399 return belt_move_dir[belt_dir_nr];
8402 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8404 static int belt_base_element[4] =
8406 EL_CONVEYOR_BELT_1_LEFT,
8407 EL_CONVEYOR_BELT_2_LEFT,
8408 EL_CONVEYOR_BELT_3_LEFT,
8409 EL_CONVEYOR_BELT_4_LEFT
8412 return belt_base_element[belt_nr] + belt_dir_nr;
8415 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8417 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8419 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8422 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8424 static int belt_base_element[4] =
8426 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8427 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8428 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8429 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8432 return belt_base_element[belt_nr] + belt_dir_nr;
8435 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8437 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8439 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8442 boolean swapTiles_EM(boolean is_pre_emc_cave)
8444 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8447 boolean getTeamMode_EM(void)
8449 return game.team_mode || network_playing;
8452 boolean isActivePlayer_EM(int player_nr)
8454 return stored_player[player_nr].active;
8457 unsigned int InitRND(int seed)
8459 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8460 return InitEngineRandom_EM(seed);
8461 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8462 return InitEngineRandom_SP(seed);
8463 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8464 return InitEngineRandom_MM(seed);
8466 return InitEngineRandom_RND(seed);
8469 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8470 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8472 static int get_effective_element_EM(int tile, int frame_em)
8474 int element = object_mapping[tile].element_rnd;
8475 int action = object_mapping[tile].action;
8476 boolean is_backside = object_mapping[tile].is_backside;
8477 boolean action_removing = (action == ACTION_DIGGING ||
8478 action == ACTION_SNAPPING ||
8479 action == ACTION_COLLECTING);
8487 return (frame_em > 5 ? EL_EMPTY : element);
8493 else // frame_em == 7
8504 case Ydiamond_stone:
8508 case Xdrip_stretchB:
8524 case Ymagnify_blank:
8527 case Xsand_stonein_1:
8528 case Xsand_stonein_2:
8529 case Xsand_stonein_3:
8530 case Xsand_stonein_4:
8534 return (is_backside || action_removing ? EL_EMPTY : element);
8539 static boolean check_linear_animation_EM(int tile)
8543 case Xsand_stonesand_1:
8544 case Xsand_stonesand_quickout_1:
8545 case Xsand_sandstone_1:
8546 case Xsand_stonein_1:
8547 case Xsand_stoneout_1:
8575 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8576 boolean has_crumbled_graphics,
8577 int crumbled, int sync_frame)
8579 // if element can be crumbled, but certain action graphics are just empty
8580 // space (like instantly snapping sand to empty space in 1 frame), do not
8581 // treat these empty space graphics as crumbled graphics in EMC engine
8582 if (crumbled == IMG_EMPTY_SPACE)
8583 has_crumbled_graphics = FALSE;
8585 if (has_crumbled_graphics)
8587 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8588 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8589 g_crumbled->anim_delay,
8590 g_crumbled->anim_mode,
8591 g_crumbled->anim_start_frame,
8594 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8595 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8597 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8598 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8600 g_em->has_crumbled_graphics = TRUE;
8604 g_em->crumbled_bitmap = NULL;
8605 g_em->crumbled_src_x = 0;
8606 g_em->crumbled_src_y = 0;
8607 g_em->crumbled_border_size = 0;
8608 g_em->crumbled_tile_size = 0;
8610 g_em->has_crumbled_graphics = FALSE;
8615 void ResetGfxAnimation_EM(int x, int y, int tile)
8621 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8622 int tile, int frame_em, int x, int y)
8624 int action = object_mapping[tile].action;
8625 int direction = object_mapping[tile].direction;
8626 int effective_element = get_effective_element_EM(tile, frame_em);
8627 int graphic = (direction == MV_NONE ?
8628 el_act2img(effective_element, action) :
8629 el_act_dir2img(effective_element, action, direction));
8630 struct GraphicInfo *g = &graphic_info[graphic];
8632 boolean action_removing = (action == ACTION_DIGGING ||
8633 action == ACTION_SNAPPING ||
8634 action == ACTION_COLLECTING);
8635 boolean action_moving = (action == ACTION_FALLING ||
8636 action == ACTION_MOVING ||
8637 action == ACTION_PUSHING ||
8638 action == ACTION_EATING ||
8639 action == ACTION_FILLING ||
8640 action == ACTION_EMPTYING);
8641 boolean action_falling = (action == ACTION_FALLING ||
8642 action == ACTION_FILLING ||
8643 action == ACTION_EMPTYING);
8645 // special case: graphic uses "2nd movement tile" and has defined
8646 // 7 frames for movement animation (or less) => use default graphic
8647 // for last (8th) frame which ends the movement animation
8648 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8650 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8651 graphic = (direction == MV_NONE ?
8652 el_act2img(effective_element, action) :
8653 el_act_dir2img(effective_element, action, direction));
8655 g = &graphic_info[graphic];
8658 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8662 else if (action_moving)
8664 boolean is_backside = object_mapping[tile].is_backside;
8668 int direction = object_mapping[tile].direction;
8669 int move_dir = (action_falling ? MV_DOWN : direction);
8674 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8675 if (g->double_movement && frame_em == 0)
8679 if (move_dir == MV_LEFT)
8680 GfxFrame[x - 1][y] = GfxFrame[x][y];
8681 else if (move_dir == MV_RIGHT)
8682 GfxFrame[x + 1][y] = GfxFrame[x][y];
8683 else if (move_dir == MV_UP)
8684 GfxFrame[x][y - 1] = GfxFrame[x][y];
8685 else if (move_dir == MV_DOWN)
8686 GfxFrame[x][y + 1] = GfxFrame[x][y];
8693 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8694 if (tile == Xsand_stonesand_quickout_1 ||
8695 tile == Xsand_stonesand_quickout_2)
8699 if (graphic_info[graphic].anim_global_sync)
8700 sync_frame = FrameCounter;
8701 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8702 sync_frame = GfxFrame[x][y];
8704 sync_frame = 0; // playfield border (pseudo steel)
8706 SetRandomAnimationValue(x, y);
8708 int frame = getAnimationFrame(g->anim_frames,
8711 g->anim_start_frame,
8714 g_em->unique_identifier =
8715 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8718 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8719 int tile, int frame_em, int x, int y)
8721 int action = object_mapping[tile].action;
8722 int direction = object_mapping[tile].direction;
8723 boolean is_backside = object_mapping[tile].is_backside;
8724 int effective_element = get_effective_element_EM(tile, frame_em);
8725 int effective_action = action;
8726 int graphic = (direction == MV_NONE ?
8727 el_act2img(effective_element, effective_action) :
8728 el_act_dir2img(effective_element, effective_action,
8730 int crumbled = (direction == MV_NONE ?
8731 el_act2crm(effective_element, effective_action) :
8732 el_act_dir2crm(effective_element, effective_action,
8734 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8735 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8736 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8737 struct GraphicInfo *g = &graphic_info[graphic];
8740 // special case: graphic uses "2nd movement tile" and has defined
8741 // 7 frames for movement animation (or less) => use default graphic
8742 // for last (8th) frame which ends the movement animation
8743 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8745 effective_action = ACTION_DEFAULT;
8746 graphic = (direction == MV_NONE ?
8747 el_act2img(effective_element, effective_action) :
8748 el_act_dir2img(effective_element, effective_action,
8750 crumbled = (direction == MV_NONE ?
8751 el_act2crm(effective_element, effective_action) :
8752 el_act_dir2crm(effective_element, effective_action,
8755 g = &graphic_info[graphic];
8758 if (graphic_info[graphic].anim_global_sync)
8759 sync_frame = FrameCounter;
8760 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8761 sync_frame = GfxFrame[x][y];
8763 sync_frame = 0; // playfield border (pseudo steel)
8765 SetRandomAnimationValue(x, y);
8767 int frame = getAnimationFrame(g->anim_frames,
8770 g->anim_start_frame,
8773 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8774 g->double_movement && is_backside);
8776 // (updating the "crumbled" graphic definitions is probably not really needed,
8777 // as animations for crumbled graphics can't be longer than one EMC cycle)
8778 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8782 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8783 int player_nr, int anim, int frame_em)
8785 int element = player_mapping[player_nr][anim].element_rnd;
8786 int action = player_mapping[player_nr][anim].action;
8787 int direction = player_mapping[player_nr][anim].direction;
8788 int graphic = (direction == MV_NONE ?
8789 el_act2img(element, action) :
8790 el_act_dir2img(element, action, direction));
8791 struct GraphicInfo *g = &graphic_info[graphic];
8794 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8796 stored_player[player_nr].StepFrame = frame_em;
8798 sync_frame = stored_player[player_nr].Frame;
8800 int frame = getAnimationFrame(g->anim_frames,
8803 g->anim_start_frame,
8806 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8807 &g_em->src_x, &g_em->src_y, FALSE);
8810 void InitGraphicInfo_EM(void)
8814 // always start with reliable default values
8815 for (i = 0; i < GAME_TILE_MAX; i++)
8817 object_mapping[i].element_rnd = EL_UNKNOWN;
8818 object_mapping[i].is_backside = FALSE;
8819 object_mapping[i].action = ACTION_DEFAULT;
8820 object_mapping[i].direction = MV_NONE;
8823 // always start with reliable default values
8824 for (p = 0; p < MAX_PLAYERS; p++)
8826 for (i = 0; i < PLY_MAX; i++)
8828 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8829 player_mapping[p][i].action = ACTION_DEFAULT;
8830 player_mapping[p][i].direction = MV_NONE;
8834 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8836 int e = em_object_mapping_list[i].element_em;
8838 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8839 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8841 if (em_object_mapping_list[i].action != -1)
8842 object_mapping[e].action = em_object_mapping_list[i].action;
8844 if (em_object_mapping_list[i].direction != -1)
8845 object_mapping[e].direction =
8846 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8849 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8851 int a = em_player_mapping_list[i].action_em;
8852 int p = em_player_mapping_list[i].player_nr;
8854 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8856 if (em_player_mapping_list[i].action != -1)
8857 player_mapping[p][a].action = em_player_mapping_list[i].action;
8859 if (em_player_mapping_list[i].direction != -1)
8860 player_mapping[p][a].direction =
8861 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8864 for (i = 0; i < GAME_TILE_MAX; i++)
8866 int element = object_mapping[i].element_rnd;
8867 int action = object_mapping[i].action;
8868 int direction = object_mapping[i].direction;
8869 boolean is_backside = object_mapping[i].is_backside;
8870 boolean action_exploding = ((action == ACTION_EXPLODING ||
8871 action == ACTION_SMASHED_BY_ROCK ||
8872 action == ACTION_SMASHED_BY_SPRING) &&
8873 element != EL_DIAMOND);
8874 boolean action_active = (action == ACTION_ACTIVE);
8875 boolean action_other = (action == ACTION_OTHER);
8877 for (j = 0; j < 8; j++)
8879 int effective_element = get_effective_element_EM(i, j);
8880 int effective_action = (j < 7 ? action :
8881 i == Xdrip_stretch ? action :
8882 i == Xdrip_stretchB ? action :
8883 i == Ydrip_1_s ? action :
8884 i == Ydrip_1_sB ? action :
8885 i == Yball_1 ? action :
8886 i == Xball_2 ? action :
8887 i == Yball_2 ? action :
8888 i == Yball_blank ? action :
8889 i == Ykey_1_blank ? action :
8890 i == Ykey_2_blank ? action :
8891 i == Ykey_3_blank ? action :
8892 i == Ykey_4_blank ? action :
8893 i == Ykey_5_blank ? action :
8894 i == Ykey_6_blank ? action :
8895 i == Ykey_7_blank ? action :
8896 i == Ykey_8_blank ? action :
8897 i == Ylenses_blank ? action :
8898 i == Ymagnify_blank ? action :
8899 i == Ygrass_blank ? action :
8900 i == Ydirt_blank ? action :
8901 i == Xsand_stonein_1 ? action :
8902 i == Xsand_stonein_2 ? action :
8903 i == Xsand_stonein_3 ? action :
8904 i == Xsand_stonein_4 ? action :
8905 i == Xsand_stoneout_1 ? action :
8906 i == Xsand_stoneout_2 ? action :
8907 i == Xboom_android ? ACTION_EXPLODING :
8908 action_exploding ? ACTION_EXPLODING :
8909 action_active ? action :
8910 action_other ? action :
8912 int graphic = (el_act_dir2img(effective_element, effective_action,
8914 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8916 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8917 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8918 boolean has_action_graphics = (graphic != base_graphic);
8919 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8920 struct GraphicInfo *g = &graphic_info[graphic];
8921 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8924 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8925 boolean special_animation = (action != ACTION_DEFAULT &&
8926 g->anim_frames == 3 &&
8927 g->anim_delay == 2 &&
8928 g->anim_mode & ANIM_LINEAR);
8929 int sync_frame = (i == Xdrip_stretch ? 7 :
8930 i == Xdrip_stretchB ? 7 :
8931 i == Ydrip_2_s ? j + 8 :
8932 i == Ydrip_2_sB ? j + 8 :
8941 i == Xfake_acid_1 ? 0 :
8942 i == Xfake_acid_2 ? 10 :
8943 i == Xfake_acid_3 ? 20 :
8944 i == Xfake_acid_4 ? 30 :
8945 i == Xfake_acid_5 ? 40 :
8946 i == Xfake_acid_6 ? 50 :
8947 i == Xfake_acid_7 ? 60 :
8948 i == Xfake_acid_8 ? 70 :
8949 i == Xfake_acid_1_player ? 0 :
8950 i == Xfake_acid_2_player ? 10 :
8951 i == Xfake_acid_3_player ? 20 :
8952 i == Xfake_acid_4_player ? 30 :
8953 i == Xfake_acid_5_player ? 40 :
8954 i == Xfake_acid_6_player ? 50 :
8955 i == Xfake_acid_7_player ? 60 :
8956 i == Xfake_acid_8_player ? 70 :
8958 i == Yball_2 ? j + 8 :
8959 i == Yball_blank ? j + 1 :
8960 i == Ykey_1_blank ? j + 1 :
8961 i == Ykey_2_blank ? j + 1 :
8962 i == Ykey_3_blank ? j + 1 :
8963 i == Ykey_4_blank ? j + 1 :
8964 i == Ykey_5_blank ? j + 1 :
8965 i == Ykey_6_blank ? j + 1 :
8966 i == Ykey_7_blank ? j + 1 :
8967 i == Ykey_8_blank ? j + 1 :
8968 i == Ylenses_blank ? j + 1 :
8969 i == Ymagnify_blank ? j + 1 :
8970 i == Ygrass_blank ? j + 1 :
8971 i == Ydirt_blank ? j + 1 :
8972 i == Xamoeba_1 ? 0 :
8973 i == Xamoeba_2 ? 1 :
8974 i == Xamoeba_3 ? 2 :
8975 i == Xamoeba_4 ? 3 :
8976 i == Xamoeba_5 ? 0 :
8977 i == Xamoeba_6 ? 1 :
8978 i == Xamoeba_7 ? 2 :
8979 i == Xamoeba_8 ? 3 :
8980 i == Xexit_2 ? j + 8 :
8981 i == Xexit_3 ? j + 16 :
8982 i == Xdynamite_1 ? 0 :
8983 i == Xdynamite_2 ? 8 :
8984 i == Xdynamite_3 ? 16 :
8985 i == Xdynamite_4 ? 24 :
8986 i == Xsand_stonein_1 ? j + 1 :
8987 i == Xsand_stonein_2 ? j + 9 :
8988 i == Xsand_stonein_3 ? j + 17 :
8989 i == Xsand_stonein_4 ? j + 25 :
8990 i == Xsand_stoneout_1 && j == 0 ? 0 :
8991 i == Xsand_stoneout_1 && j == 1 ? 0 :
8992 i == Xsand_stoneout_1 && j == 2 ? 1 :
8993 i == Xsand_stoneout_1 && j == 3 ? 2 :
8994 i == Xsand_stoneout_1 && j == 4 ? 2 :
8995 i == Xsand_stoneout_1 && j == 5 ? 3 :
8996 i == Xsand_stoneout_1 && j == 6 ? 4 :
8997 i == Xsand_stoneout_1 && j == 7 ? 4 :
8998 i == Xsand_stoneout_2 && j == 0 ? 5 :
8999 i == Xsand_stoneout_2 && j == 1 ? 6 :
9000 i == Xsand_stoneout_2 && j == 2 ? 7 :
9001 i == Xsand_stoneout_2 && j == 3 ? 8 :
9002 i == Xsand_stoneout_2 && j == 4 ? 9 :
9003 i == Xsand_stoneout_2 && j == 5 ? 11 :
9004 i == Xsand_stoneout_2 && j == 6 ? 13 :
9005 i == Xsand_stoneout_2 && j == 7 ? 15 :
9006 i == Xboom_bug && j == 1 ? 2 :
9007 i == Xboom_bug && j == 2 ? 2 :
9008 i == Xboom_bug && j == 3 ? 4 :
9009 i == Xboom_bug && j == 4 ? 4 :
9010 i == Xboom_bug && j == 5 ? 2 :
9011 i == Xboom_bug && j == 6 ? 2 :
9012 i == Xboom_bug && j == 7 ? 0 :
9013 i == Xboom_tank && j == 1 ? 2 :
9014 i == Xboom_tank && j == 2 ? 2 :
9015 i == Xboom_tank && j == 3 ? 4 :
9016 i == Xboom_tank && j == 4 ? 4 :
9017 i == Xboom_tank && j == 5 ? 2 :
9018 i == Xboom_tank && j == 6 ? 2 :
9019 i == Xboom_tank && j == 7 ? 0 :
9020 i == Xboom_android && j == 7 ? 6 :
9021 i == Xboom_1 && j == 1 ? 2 :
9022 i == Xboom_1 && j == 2 ? 2 :
9023 i == Xboom_1 && j == 3 ? 4 :
9024 i == Xboom_1 && j == 4 ? 4 :
9025 i == Xboom_1 && j == 5 ? 6 :
9026 i == Xboom_1 && j == 6 ? 6 :
9027 i == Xboom_1 && j == 7 ? 8 :
9028 i == Xboom_2 && j == 0 ? 8 :
9029 i == Xboom_2 && j == 1 ? 8 :
9030 i == Xboom_2 && j == 2 ? 10 :
9031 i == Xboom_2 && j == 3 ? 10 :
9032 i == Xboom_2 && j == 4 ? 10 :
9033 i == Xboom_2 && j == 5 ? 12 :
9034 i == Xboom_2 && j == 6 ? 12 :
9035 i == Xboom_2 && j == 7 ? 12 :
9036 special_animation && j == 4 ? 3 :
9037 effective_action != action ? 0 :
9039 int frame = getAnimationFrame(g->anim_frames,
9042 g->anim_start_frame,
9045 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9046 g->double_movement && is_backside);
9048 g_em->bitmap = src_bitmap;
9049 g_em->src_x = src_x;
9050 g_em->src_y = src_y;
9051 g_em->src_offset_x = 0;
9052 g_em->src_offset_y = 0;
9053 g_em->dst_offset_x = 0;
9054 g_em->dst_offset_y = 0;
9055 g_em->width = TILEX;
9056 g_em->height = TILEY;
9058 g_em->preserve_background = FALSE;
9060 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9063 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9064 effective_action == ACTION_MOVING ||
9065 effective_action == ACTION_PUSHING ||
9066 effective_action == ACTION_EATING)) ||
9067 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9068 effective_action == ACTION_EMPTYING)))
9071 (effective_action == ACTION_FALLING ||
9072 effective_action == ACTION_FILLING ||
9073 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9074 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9075 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9076 int num_steps = (i == Ydrip_1_s ? 16 :
9077 i == Ydrip_1_sB ? 16 :
9078 i == Ydrip_2_s ? 16 :
9079 i == Ydrip_2_sB ? 16 :
9080 i == Xsand_stonein_1 ? 32 :
9081 i == Xsand_stonein_2 ? 32 :
9082 i == Xsand_stonein_3 ? 32 :
9083 i == Xsand_stonein_4 ? 32 :
9084 i == Xsand_stoneout_1 ? 16 :
9085 i == Xsand_stoneout_2 ? 16 : 8);
9086 int cx = ABS(dx) * (TILEX / num_steps);
9087 int cy = ABS(dy) * (TILEY / num_steps);
9088 int step_frame = (i == Ydrip_2_s ? j + 8 :
9089 i == Ydrip_2_sB ? j + 8 :
9090 i == Xsand_stonein_2 ? j + 8 :
9091 i == Xsand_stonein_3 ? j + 16 :
9092 i == Xsand_stonein_4 ? j + 24 :
9093 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9094 int step = (is_backside ? step_frame : num_steps - step_frame);
9096 if (is_backside) // tile where movement starts
9098 if (dx < 0 || dy < 0)
9100 g_em->src_offset_x = cx * step;
9101 g_em->src_offset_y = cy * step;
9105 g_em->dst_offset_x = cx * step;
9106 g_em->dst_offset_y = cy * step;
9109 else // tile where movement ends
9111 if (dx < 0 || dy < 0)
9113 g_em->dst_offset_x = cx * step;
9114 g_em->dst_offset_y = cy * step;
9118 g_em->src_offset_x = cx * step;
9119 g_em->src_offset_y = cy * step;
9123 g_em->width = TILEX - cx * step;
9124 g_em->height = TILEY - cy * step;
9127 // create unique graphic identifier to decide if tile must be redrawn
9128 /* bit 31 - 16 (16 bit): EM style graphic
9129 bit 15 - 12 ( 4 bit): EM style frame
9130 bit 11 - 6 ( 6 bit): graphic width
9131 bit 5 - 0 ( 6 bit): graphic height */
9132 g_em->unique_identifier =
9133 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9137 for (i = 0; i < GAME_TILE_MAX; i++)
9139 for (j = 0; j < 8; j++)
9141 int element = object_mapping[i].element_rnd;
9142 int action = object_mapping[i].action;
9143 int direction = object_mapping[i].direction;
9144 boolean is_backside = object_mapping[i].is_backside;
9145 int graphic_action = el_act_dir2img(element, action, direction);
9146 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9148 if ((action == ACTION_SMASHED_BY_ROCK ||
9149 action == ACTION_SMASHED_BY_SPRING ||
9150 action == ACTION_EATING) &&
9151 graphic_action == graphic_default)
9153 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9154 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9155 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9156 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9159 // no separate animation for "smashed by rock" -- use rock instead
9160 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9161 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9163 g_em->bitmap = g_xx->bitmap;
9164 g_em->src_x = g_xx->src_x;
9165 g_em->src_y = g_xx->src_y;
9166 g_em->src_offset_x = g_xx->src_offset_x;
9167 g_em->src_offset_y = g_xx->src_offset_y;
9168 g_em->dst_offset_x = g_xx->dst_offset_x;
9169 g_em->dst_offset_y = g_xx->dst_offset_y;
9170 g_em->width = g_xx->width;
9171 g_em->height = g_xx->height;
9172 g_em->unique_identifier = g_xx->unique_identifier;
9175 g_em->preserve_background = TRUE;
9180 for (p = 0; p < MAX_PLAYERS; p++)
9182 for (i = 0; i < PLY_MAX; i++)
9184 int element = player_mapping[p][i].element_rnd;
9185 int action = player_mapping[p][i].action;
9186 int direction = player_mapping[p][i].direction;
9188 for (j = 0; j < 8; j++)
9190 int effective_element = element;
9191 int effective_action = action;
9192 int graphic = (direction == MV_NONE ?
9193 el_act2img(effective_element, effective_action) :
9194 el_act_dir2img(effective_element, effective_action,
9196 struct GraphicInfo *g = &graphic_info[graphic];
9197 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9201 int frame = getAnimationFrame(g->anim_frames,
9204 g->anim_start_frame,
9207 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9209 g_em->bitmap = src_bitmap;
9210 g_em->src_x = src_x;
9211 g_em->src_y = src_y;
9212 g_em->src_offset_x = 0;
9213 g_em->src_offset_y = 0;
9214 g_em->dst_offset_x = 0;
9215 g_em->dst_offset_y = 0;
9216 g_em->width = TILEX;
9217 g_em->height = TILEY;
9223 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9224 boolean any_player_moving,
9225 boolean any_player_snapping,
9226 boolean any_player_dropping)
9228 if (frame == 7 && !any_player_dropping)
9230 if (!local_player->was_waiting)
9232 if (!CheckSaveEngineSnapshotToList())
9235 local_player->was_waiting = TRUE;
9238 else if (any_player_moving || any_player_snapping || any_player_dropping)
9240 local_player->was_waiting = FALSE;
9244 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9245 boolean murphy_is_dropping)
9247 if (murphy_is_waiting)
9249 if (!local_player->was_waiting)
9251 if (!CheckSaveEngineSnapshotToList())
9254 local_player->was_waiting = TRUE;
9259 local_player->was_waiting = FALSE;
9263 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9264 boolean button_released)
9266 if (button_released)
9268 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9269 CheckSaveEngineSnapshotToList();
9271 else if (element_clicked)
9273 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9274 CheckSaveEngineSnapshotToList();
9276 game.snapshot.changed_action = TRUE;
9280 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9281 boolean any_player_moving,
9282 boolean any_player_snapping,
9283 boolean any_player_dropping)
9285 if (tape.single_step && tape.recording && !tape.pausing)
9286 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9287 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9289 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9290 any_player_snapping, any_player_dropping);
9292 return tape.pausing;
9295 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9296 boolean murphy_is_dropping)
9298 boolean murphy_starts_dropping = FALSE;
9301 for (i = 0; i < MAX_PLAYERS; i++)
9302 if (stored_player[i].force_dropping)
9303 murphy_starts_dropping = TRUE;
9305 if (tape.single_step && tape.recording && !tape.pausing)
9306 if (murphy_is_waiting && !murphy_starts_dropping)
9307 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9309 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9312 void CheckSingleStepMode_MM(boolean element_clicked,
9313 boolean button_released)
9315 if (tape.single_step && tape.recording && !tape.pausing)
9316 if (button_released)
9317 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9319 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9322 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9323 int graphic, int sync_frame, int x, int y)
9325 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9327 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9330 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9332 return (IS_NEXT_FRAME(sync_frame, graphic));
9335 int getGraphicInfo_Delay(int graphic)
9337 return graphic_info[graphic].anim_delay;
9340 void PlayMenuSoundExt(int sound)
9342 if (sound == SND_UNDEFINED)
9345 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9346 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9349 if (IS_LOOP_SOUND(sound))
9350 PlaySoundLoop(sound);
9355 void PlayMenuSound(void)
9357 PlayMenuSoundExt(menu.sound[game_status]);
9360 void PlayMenuSoundStereo(int sound, int stereo_position)
9362 if (sound == SND_UNDEFINED)
9365 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9366 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9369 if (IS_LOOP_SOUND(sound))
9370 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9372 PlaySoundStereo(sound, stereo_position);
9375 void PlayMenuSoundIfLoopExt(int sound)
9377 if (sound == SND_UNDEFINED)
9380 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9381 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9384 if (IS_LOOP_SOUND(sound))
9385 PlaySoundLoop(sound);
9388 void PlayMenuSoundIfLoop(void)
9390 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9393 void PlayMenuMusicExt(int music)
9395 if (music == MUS_UNDEFINED)
9398 if (!setup.sound_music)
9401 if (IS_LOOP_MUSIC(music))
9402 PlayMusicLoop(music);
9407 void PlayMenuMusic(void)
9409 char *curr_music = getCurrentlyPlayingMusicFilename();
9410 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9412 if (!strEqual(curr_music, next_music))
9413 PlayMenuMusicExt(menu.music[game_status]);
9416 void PlayMenuSoundsAndMusic(void)
9422 static void FadeMenuSounds(void)
9427 static void FadeMenuMusic(void)
9429 char *curr_music = getCurrentlyPlayingMusicFilename();
9430 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9432 if (!strEqual(curr_music, next_music))
9436 void FadeMenuSoundsAndMusic(void)
9442 void PlaySoundActivating(void)
9445 PlaySound(SND_MENU_ITEM_ACTIVATING);
9449 void PlaySoundSelecting(void)
9452 PlaySound(SND_MENU_ITEM_SELECTING);
9456 void ToggleFullscreenIfNeeded(void)
9458 // if setup and video fullscreen state are already matching, nothing do do
9459 if (setup.fullscreen == video.fullscreen_enabled ||
9460 !video.fullscreen_available)
9463 SDLSetWindowFullscreen(setup.fullscreen);
9465 // set setup value according to successfully changed fullscreen mode
9466 setup.fullscreen = video.fullscreen_enabled;
9469 void ChangeWindowScalingIfNeeded(void)
9471 // if setup and video window scaling are already matching, nothing do do
9472 if (setup.window_scaling_percent == video.window_scaling_percent ||
9473 video.fullscreen_enabled)
9476 SDLSetWindowScaling(setup.window_scaling_percent);
9478 // set setup value according to successfully changed window scaling
9479 setup.window_scaling_percent = video.window_scaling_percent;
9482 void ChangeVsyncModeIfNeeded(void)
9484 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9485 int video_vsync_mode = video.vsync_mode;
9487 // if setup and video vsync mode are already matching, nothing do do
9488 if (setup_vsync_mode == video_vsync_mode)
9491 // if renderer is using OpenGL, vsync mode can directly be changed
9492 SDLSetScreenVsyncMode(setup.vsync_mode);
9494 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9495 if (video.vsync_mode == video_vsync_mode)
9497 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9499 // save backbuffer content which gets lost when re-creating screen
9500 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9502 // force re-creating screen and renderer to set new vsync mode
9503 video.fullscreen_enabled = !setup.fullscreen;
9505 // when creating new renderer, destroy textures linked to old renderer
9506 FreeAllImageTextures(); // needs old renderer to free the textures
9508 // re-create screen and renderer (including change of vsync mode)
9509 ChangeVideoModeIfNeeded(setup.fullscreen);
9511 // set setup value according to successfully changed fullscreen mode
9512 setup.fullscreen = video.fullscreen_enabled;
9514 // restore backbuffer content from temporary backbuffer backup bitmap
9515 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9516 FreeBitmap(tmp_backbuffer);
9518 // update visible window/screen
9519 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9521 // when changing vsync mode, re-create textures for new renderer
9522 InitImageTextures();
9525 // set setup value according to successfully changed vsync mode
9526 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9529 static void JoinRectangles(int *x, int *y, int *width, int *height,
9530 int x2, int y2, int width2, int height2)
9532 // do not join with "off-screen" rectangle
9533 if (x2 == -1 || y2 == -1)
9538 *width = MAX(*width, width2);
9539 *height = MAX(*height, height2);
9542 void SetAnimStatus(int anim_status_new)
9544 if (anim_status_new == GAME_MODE_MAIN)
9545 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9546 else if (anim_status_new == GAME_MODE_NAMES)
9547 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9548 else if (anim_status_new == GAME_MODE_SCORES)
9549 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9551 global.anim_status_next = anim_status_new;
9553 // directly set screen modes that are entered without fading
9554 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9555 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9556 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9557 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9558 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9559 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9560 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9561 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9562 global.anim_status = global.anim_status_next;
9565 void SetGameStatus(int game_status_new)
9567 if (game_status_new != game_status)
9568 game_status_last_screen = game_status;
9570 game_status = game_status_new;
9572 SetAnimStatus(game_status_new);
9575 void SetFontStatus(int game_status_new)
9577 static int last_game_status = -1;
9579 if (game_status_new != -1)
9581 // set game status for font use after storing last game status
9582 last_game_status = game_status;
9583 game_status = game_status_new;
9587 // reset game status after font use from last stored game status
9588 game_status = last_game_status;
9592 void ResetFontStatus(void)
9597 void SetLevelSetInfo(char *identifier, int level_nr)
9599 setString(&levelset.identifier, identifier);
9601 levelset.level_nr = level_nr;
9604 boolean CheckIfAllViewportsHaveChanged(void)
9606 // if game status has not changed, viewports have not changed either
9607 if (game_status == game_status_last)
9610 // check if all viewports have changed with current game status
9612 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9613 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9614 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9615 int new_real_sx = vp_playfield->x;
9616 int new_real_sy = vp_playfield->y;
9617 int new_full_sxsize = vp_playfield->width;
9618 int new_full_sysize = vp_playfield->height;
9619 int new_dx = vp_door_1->x;
9620 int new_dy = vp_door_1->y;
9621 int new_dxsize = vp_door_1->width;
9622 int new_dysize = vp_door_1->height;
9623 int new_vx = vp_door_2->x;
9624 int new_vy = vp_door_2->y;
9625 int new_vxsize = vp_door_2->width;
9626 int new_vysize = vp_door_2->height;
9628 boolean playfield_viewport_has_changed =
9629 (new_real_sx != REAL_SX ||
9630 new_real_sy != REAL_SY ||
9631 new_full_sxsize != FULL_SXSIZE ||
9632 new_full_sysize != FULL_SYSIZE);
9634 boolean door_1_viewport_has_changed =
9637 new_dxsize != DXSIZE ||
9638 new_dysize != DYSIZE);
9640 boolean door_2_viewport_has_changed =
9643 new_vxsize != VXSIZE ||
9644 new_vysize != VYSIZE ||
9645 game_status_last == GAME_MODE_EDITOR);
9647 return (playfield_viewport_has_changed &&
9648 door_1_viewport_has_changed &&
9649 door_2_viewport_has_changed);
9652 boolean CheckFadeAll(void)
9654 return (CheckIfGlobalBorderHasChanged() ||
9655 CheckIfAllViewportsHaveChanged());
9658 void ChangeViewportPropertiesIfNeeded(void)
9660 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9661 FALSE : setup.small_game_graphics);
9662 int gfx_game_mode = game_status;
9663 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9665 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9666 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9667 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9668 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9669 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9670 int new_win_xsize = vp_window->width;
9671 int new_win_ysize = vp_window->height;
9672 int border_left = vp_playfield->border_left;
9673 int border_right = vp_playfield->border_right;
9674 int border_top = vp_playfield->border_top;
9675 int border_bottom = vp_playfield->border_bottom;
9676 int new_sx = vp_playfield->x + border_left;
9677 int new_sy = vp_playfield->y + border_top;
9678 int new_sxsize = vp_playfield->width - border_left - border_right;
9679 int new_sysize = vp_playfield->height - border_top - border_bottom;
9680 int new_real_sx = vp_playfield->x;
9681 int new_real_sy = vp_playfield->y;
9682 int new_full_sxsize = vp_playfield->width;
9683 int new_full_sysize = vp_playfield->height;
9684 int new_dx = vp_door_1->x;
9685 int new_dy = vp_door_1->y;
9686 int new_dxsize = vp_door_1->width;
9687 int new_dysize = vp_door_1->height;
9688 int new_vx = vp_door_2->x;
9689 int new_vy = vp_door_2->y;
9690 int new_vxsize = vp_door_2->width;
9691 int new_vysize = vp_door_2->height;
9692 int new_ex = vp_door_3->x;
9693 int new_ey = vp_door_3->y;
9694 int new_exsize = vp_door_3->width;
9695 int new_eysize = vp_door_3->height;
9696 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9697 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9698 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9699 int new_scr_fieldx = new_sxsize / tilesize;
9700 int new_scr_fieldy = new_sysize / tilesize;
9701 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9702 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9703 boolean init_gfx_buffers = FALSE;
9704 boolean init_video_buffer = FALSE;
9705 boolean init_gadgets_and_anims = FALSE;
9706 boolean init_em_graphics = FALSE;
9708 if (new_win_xsize != WIN_XSIZE ||
9709 new_win_ysize != WIN_YSIZE)
9711 WIN_XSIZE = new_win_xsize;
9712 WIN_YSIZE = new_win_ysize;
9714 init_video_buffer = TRUE;
9715 init_gfx_buffers = TRUE;
9716 init_gadgets_and_anims = TRUE;
9718 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9721 if (new_scr_fieldx != SCR_FIELDX ||
9722 new_scr_fieldy != SCR_FIELDY)
9724 // this always toggles between MAIN and GAME when using small tile size
9726 SCR_FIELDX = new_scr_fieldx;
9727 SCR_FIELDY = new_scr_fieldy;
9729 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9740 new_sxsize != SXSIZE ||
9741 new_sysize != SYSIZE ||
9742 new_dxsize != DXSIZE ||
9743 new_dysize != DYSIZE ||
9744 new_vxsize != VXSIZE ||
9745 new_vysize != VYSIZE ||
9746 new_exsize != EXSIZE ||
9747 new_eysize != EYSIZE ||
9748 new_real_sx != REAL_SX ||
9749 new_real_sy != REAL_SY ||
9750 new_full_sxsize != FULL_SXSIZE ||
9751 new_full_sysize != FULL_SYSIZE ||
9752 new_tilesize_var != TILESIZE_VAR
9755 // ------------------------------------------------------------------------
9756 // determine next fading area for changed viewport definitions
9757 // ------------------------------------------------------------------------
9759 // start with current playfield area (default fading area)
9762 FADE_SXSIZE = FULL_SXSIZE;
9763 FADE_SYSIZE = FULL_SYSIZE;
9765 // add new playfield area if position or size has changed
9766 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9767 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9769 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9770 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9773 // add current and new door 1 area if position or size has changed
9774 if (new_dx != DX || new_dy != DY ||
9775 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9777 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9778 DX, DY, DXSIZE, DYSIZE);
9779 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9780 new_dx, new_dy, new_dxsize, new_dysize);
9783 // add current and new door 2 area if position or size has changed
9784 if (new_vx != VX || new_vy != VY ||
9785 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9787 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9788 VX, VY, VXSIZE, VYSIZE);
9789 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9790 new_vx, new_vy, new_vxsize, new_vysize);
9793 // ------------------------------------------------------------------------
9794 // handle changed tile size
9795 // ------------------------------------------------------------------------
9797 if (new_tilesize_var != TILESIZE_VAR)
9799 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9801 // changing tile size invalidates scroll values of engine snapshots
9802 FreeEngineSnapshotSingle();
9804 // changing tile size requires update of graphic mapping for EM engine
9805 init_em_graphics = TRUE;
9816 SXSIZE = new_sxsize;
9817 SYSIZE = new_sysize;
9818 DXSIZE = new_dxsize;
9819 DYSIZE = new_dysize;
9820 VXSIZE = new_vxsize;
9821 VYSIZE = new_vysize;
9822 EXSIZE = new_exsize;
9823 EYSIZE = new_eysize;
9824 REAL_SX = new_real_sx;
9825 REAL_SY = new_real_sy;
9826 FULL_SXSIZE = new_full_sxsize;
9827 FULL_SYSIZE = new_full_sysize;
9828 TILESIZE_VAR = new_tilesize_var;
9830 init_gfx_buffers = TRUE;
9831 init_gadgets_and_anims = TRUE;
9833 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9834 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9837 if (init_gfx_buffers)
9839 // Debug("tools:viewport", "init_gfx_buffers");
9841 SCR_FIELDX = new_scr_fieldx_buffers;
9842 SCR_FIELDY = new_scr_fieldy_buffers;
9846 SCR_FIELDX = new_scr_fieldx;
9847 SCR_FIELDY = new_scr_fieldy;
9849 SetDrawDeactivationMask(REDRAW_NONE);
9850 SetDrawBackgroundMask(REDRAW_FIELD);
9853 if (init_video_buffer)
9855 // Debug("tools:viewport", "init_video_buffer");
9857 FreeAllImageTextures(); // needs old renderer to free the textures
9859 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9860 InitImageTextures();
9863 if (init_gadgets_and_anims)
9865 // Debug("tools:viewport", "init_gadgets_and_anims");
9868 InitGlobalAnimations();
9871 if (init_em_graphics)
9873 InitGraphicInfo_EM();