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);
2538 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2540 int dir = MovDir[lx][ly];
2541 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2542 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2544 if (IN_SCR_FIELD(newx, newy))
2545 DrawScreenElement(newx, newy, EL_EMPTY);
2549 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2550 else if (cut_mode == NO_CUTTING)
2551 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2554 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2556 if (cut_mode == CUT_BELOW &&
2557 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2558 DrawLevelElement(lx, ly + 1, element);
2561 if (content == EL_ACID)
2563 int dir = MovDir[lx][ly];
2564 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2565 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2567 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2569 // prevent target field from being drawn again (but without masking)
2570 // (this would happen if target field is scanned after moving element)
2571 Stop[newlx][newly] = TRUE;
2574 else if (IS_BLOCKED(lx, ly))
2579 boolean cut_mode = NO_CUTTING;
2580 int element_old, content_old;
2582 Blocked2Moving(lx, ly, &oldx, &oldy);
2585 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2586 MovDir[oldx][oldy] == MV_RIGHT);
2588 element_old = Tile[oldx][oldy];
2589 content_old = Store[oldx][oldy];
2591 if (element_old == EL_QUICKSAND_EMPTYING ||
2592 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2593 element_old == EL_MAGIC_WALL_EMPTYING ||
2594 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2595 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2596 element_old == EL_AMOEBA_DROPPING)
2597 cut_mode = CUT_ABOVE;
2599 DrawScreenElement(x, y, EL_EMPTY);
2602 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2604 else if (cut_mode == NO_CUTTING)
2605 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2608 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2611 else if (IS_DRAWABLE(element))
2612 DrawScreenElement(x, y, element);
2614 DrawScreenElement(x, y, EL_EMPTY);
2617 void DrawLevelField(int x, int y)
2619 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2620 DrawScreenField(SCREENX(x), SCREENY(y));
2621 else if (IS_MOVING(x, y))
2625 Moving2Blocked(x, y, &newx, &newy);
2626 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2627 DrawScreenField(SCREENX(newx), SCREENY(newy));
2629 else if (IS_BLOCKED(x, y))
2633 Blocked2Moving(x, y, &oldx, &oldy);
2634 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2635 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2639 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2640 int (*el2img_function)(int), boolean masked,
2641 int element_bits_draw)
2643 int element_base = map_mm_wall_element(element);
2644 int element_bits = (IS_DF_WALL(element) ?
2645 element - EL_DF_WALL_START :
2646 IS_MM_WALL(element) ?
2647 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2648 int graphic = el2img_function(element_base);
2649 int tilesize_draw = tilesize / 2;
2654 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2656 for (i = 0; i < 4; i++)
2658 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2659 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2661 if (!(element_bits_draw & (1 << i)))
2664 if (element_bits & (1 << i))
2667 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2668 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2670 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2671 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2676 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2677 tilesize_draw, tilesize_draw);
2682 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2683 boolean masked, int element_bits_draw)
2685 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2686 element, tilesize, el2edimg, masked, element_bits_draw);
2689 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2690 int (*el2img_function)(int))
2692 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2696 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2699 if (IS_MM_WALL(element))
2701 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2702 element, tilesize, el2edimg, masked, 0x000f);
2706 int graphic = el2edimg(element);
2709 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2711 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2715 void DrawSizedElement(int x, int y, int element, int tilesize)
2717 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2720 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2722 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2725 void DrawMiniElement(int x, int y, int element)
2729 graphic = el2edimg(element);
2730 DrawMiniGraphic(x, y, graphic);
2733 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2736 int x = sx + scroll_x, y = sy + scroll_y;
2738 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2739 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2740 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2741 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2743 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2746 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2748 int x = sx + scroll_x, y = sy + scroll_y;
2750 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2751 DrawMiniElement(sx, sy, EL_EMPTY);
2752 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2753 DrawMiniElement(sx, sy, Tile[x][y]);
2755 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2758 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2759 int x, int y, int xsize, int ysize,
2760 int tile_width, int tile_height)
2764 int dst_x = startx + x * tile_width;
2765 int dst_y = starty + y * tile_height;
2766 int width = graphic_info[graphic].width;
2767 int height = graphic_info[graphic].height;
2768 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2769 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2770 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2771 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2772 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2773 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2774 boolean draw_masked = graphic_info[graphic].draw_masked;
2776 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2778 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2780 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2784 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2785 inner_sx + (x - 1) * tile_width % inner_width);
2786 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2787 inner_sy + (y - 1) * tile_height % inner_height);
2790 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2793 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2797 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2798 int x, int y, int xsize, int ysize,
2801 int font_width = getFontWidth(font_nr);
2802 int font_height = getFontHeight(font_nr);
2804 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2805 font_width, font_height);
2808 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2810 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2811 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2812 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2813 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2814 boolean no_delay = (tape.warp_forward);
2815 unsigned int anim_delay = 0;
2816 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2817 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2818 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2819 int font_width = getFontWidth(font_nr);
2820 int font_height = getFontHeight(font_nr);
2821 int max_xsize = level.envelope[envelope_nr].xsize;
2822 int max_ysize = level.envelope[envelope_nr].ysize;
2823 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2824 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2825 int xend = max_xsize;
2826 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2827 int xstep = (xstart < xend ? 1 : 0);
2828 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2830 int end = MAX(xend - xstart, yend - ystart);
2833 for (i = start; i <= end; i++)
2835 int last_frame = end; // last frame of this "for" loop
2836 int x = xstart + i * xstep;
2837 int y = ystart + i * ystep;
2838 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2839 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2840 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2841 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2844 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2846 BlitScreenToBitmap(backbuffer);
2848 SetDrawtoField(DRAW_TO_BACKBUFFER);
2850 for (yy = 0; yy < ysize; yy++)
2851 for (xx = 0; xx < xsize; xx++)
2852 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2854 DrawTextBuffer(sx + font_width, sy + font_height,
2855 level.envelope[envelope_nr].text, font_nr, max_xsize,
2856 xsize - 2, ysize - 2, 0, mask_mode,
2857 level.envelope[envelope_nr].autowrap,
2858 level.envelope[envelope_nr].centered, FALSE);
2860 redraw_mask |= REDRAW_FIELD;
2863 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2866 ClearAutoRepeatKeyEvents();
2869 void ShowEnvelope(int envelope_nr)
2871 int element = EL_ENVELOPE_1 + envelope_nr;
2872 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2873 int sound_opening = element_info[element].sound[ACTION_OPENING];
2874 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2875 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2876 boolean no_delay = (tape.warp_forward);
2877 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2878 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2879 int anim_mode = graphic_info[graphic].anim_mode;
2880 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2881 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2882 boolean overlay_enabled = GetOverlayEnabled();
2884 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2886 SetOverlayEnabled(FALSE);
2889 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2891 if (anim_mode == ANIM_DEFAULT)
2892 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2894 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2897 Delay_WithScreenUpdates(wait_delay_value);
2899 WaitForEventToContinue();
2902 SetOverlayEnabled(overlay_enabled);
2904 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2906 if (anim_mode != ANIM_NONE)
2907 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2909 if (anim_mode == ANIM_DEFAULT)
2910 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2912 game.envelope_active = FALSE;
2914 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2916 redraw_mask |= REDRAW_FIELD;
2920 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2921 int xsize, int ysize)
2923 if (!global.use_envelope_request ||
2924 request.sort_priority <= 0)
2927 if (request.bitmap == NULL ||
2928 xsize > request.xsize ||
2929 ysize > request.ysize)
2931 if (request.bitmap != NULL)
2932 FreeBitmap(request.bitmap);
2934 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2936 SDL_Surface *surface = request.bitmap->surface;
2938 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2939 Fail("SDLGetNativeSurface() failed");
2942 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2944 SDLFreeBitmapTextures(request.bitmap);
2945 SDLCreateBitmapTextures(request.bitmap);
2947 // set envelope request run-time values
2950 request.xsize = xsize;
2951 request.ysize = ysize;
2954 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2956 if (global.use_envelope_request &&
2957 game.request_active_or_moving &&
2958 request.sort_priority > 0 &&
2959 drawing_target == DRAW_TO_SCREEN &&
2960 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2962 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2963 request.sx, request.sy);
2967 static void setRequestBasePosition(int *x, int *y)
2969 int sx_base, sy_base;
2971 if (request.x != -1)
2972 sx_base = request.x;
2973 else if (request.align == ALIGN_LEFT)
2975 else if (request.align == ALIGN_RIGHT)
2976 sx_base = SX + SXSIZE;
2978 sx_base = SX + SXSIZE / 2;
2980 if (request.y != -1)
2981 sy_base = request.y;
2982 else if (request.valign == VALIGN_TOP)
2984 else if (request.valign == VALIGN_BOTTOM)
2985 sy_base = SY + SYSIZE;
2987 sy_base = SY + SYSIZE / 2;
2993 static void setRequestPositionExt(int *x, int *y, int width, int height,
2994 boolean add_border_size)
2996 int border_size = request.border_size;
2997 int sx_base, sy_base;
3000 setRequestBasePosition(&sx_base, &sy_base);
3002 if (request.align == ALIGN_LEFT)
3004 else if (request.align == ALIGN_RIGHT)
3005 sx = sx_base - width;
3007 sx = sx_base - width / 2;
3009 if (request.valign == VALIGN_TOP)
3011 else if (request.valign == VALIGN_BOTTOM)
3012 sy = sy_base - height;
3014 sy = sy_base - height / 2;
3016 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3017 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3019 if (add_border_size)
3029 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3031 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3034 static void DrawEnvelopeRequest(char *text)
3036 char *text_final = text;
3037 char *text_door_style = NULL;
3038 int graphic = IMG_BACKGROUND_REQUEST;
3039 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3040 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3041 int font_nr = FONT_REQUEST;
3042 int font_width = getFontWidth(font_nr);
3043 int font_height = getFontHeight(font_nr);
3044 int border_size = request.border_size;
3045 int line_spacing = request.line_spacing;
3046 int line_height = font_height + line_spacing;
3047 int max_text_width = request.width - 2 * border_size;
3048 int max_text_height = request.height - 2 * border_size;
3049 int line_length = max_text_width / font_width;
3050 int max_lines = max_text_height / line_height;
3051 int text_width = line_length * font_width;
3052 int width = request.width;
3053 int height = request.height;
3054 int tile_size = MAX(request.step_offset, 1);
3055 int x_steps = width / tile_size;
3056 int y_steps = height / tile_size;
3057 int sx_offset = border_size;
3058 int sy_offset = border_size;
3062 if (request.centered)
3063 sx_offset = (request.width - text_width) / 2;
3065 if (request.wrap_single_words && !request.autowrap)
3067 char *src_text_ptr, *dst_text_ptr;
3069 text_door_style = checked_malloc(2 * strlen(text) + 1);
3071 src_text_ptr = text;
3072 dst_text_ptr = text_door_style;
3074 while (*src_text_ptr)
3076 if (*src_text_ptr == ' ' ||
3077 *src_text_ptr == '?' ||
3078 *src_text_ptr == '!')
3079 *dst_text_ptr++ = '\n';
3081 if (*src_text_ptr != ' ')
3082 *dst_text_ptr++ = *src_text_ptr;
3087 *dst_text_ptr = '\0';
3089 text_final = text_door_style;
3092 setRequestPosition(&sx, &sy, FALSE);
3094 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3096 for (y = 0; y < y_steps; y++)
3097 for (x = 0; x < x_steps; x++)
3098 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3099 x, y, x_steps, y_steps,
3100 tile_size, tile_size);
3102 // force DOOR font inside door area
3103 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3105 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3106 line_length, -1, max_lines, line_spacing, mask_mode,
3107 request.autowrap, request.centered, FALSE);
3111 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3112 RedrawGadget(tool_gadget[i]);
3114 // store readily prepared envelope request for later use when animating
3115 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3117 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3119 if (text_door_style)
3120 free(text_door_style);
3123 static void AnimateEnvelopeRequest(int anim_mode, int action)
3125 int graphic = IMG_BACKGROUND_REQUEST;
3126 boolean draw_masked = graphic_info[graphic].draw_masked;
3127 int delay_value_normal = request.step_delay;
3128 int delay_value_fast = delay_value_normal / 2;
3129 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3130 boolean no_delay = (tape.warp_forward);
3131 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3132 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3133 unsigned int anim_delay = 0;
3135 int tile_size = MAX(request.step_offset, 1);
3136 int max_xsize = request.width / tile_size;
3137 int max_ysize = request.height / tile_size;
3138 int max_xsize_inner = max_xsize - 2;
3139 int max_ysize_inner = max_ysize - 2;
3141 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3142 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3143 int xend = max_xsize_inner;
3144 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3145 int xstep = (xstart < xend ? 1 : 0);
3146 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3148 int end = MAX(xend - xstart, yend - ystart);
3151 if (setup.quick_doors)
3158 for (i = start; i <= end; i++)
3160 int last_frame = end; // last frame of this "for" loop
3161 int x = xstart + i * xstep;
3162 int y = ystart + i * ystep;
3163 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3164 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3165 int xsize_size_left = (xsize - 1) * tile_size;
3166 int ysize_size_top = (ysize - 1) * tile_size;
3167 int max_xsize_pos = (max_xsize - 1) * tile_size;
3168 int max_ysize_pos = (max_ysize - 1) * tile_size;
3169 int width = xsize * tile_size;
3170 int height = ysize * tile_size;
3175 setRequestPosition(&src_x, &src_y, FALSE);
3176 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3178 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3180 for (yy = 0; yy < 2; yy++)
3182 for (xx = 0; xx < 2; xx++)
3184 int src_xx = src_x + xx * max_xsize_pos;
3185 int src_yy = src_y + yy * max_ysize_pos;
3186 int dst_xx = dst_x + xx * xsize_size_left;
3187 int dst_yy = dst_y + yy * ysize_size_top;
3188 int xx_size = (xx ? tile_size : xsize_size_left);
3189 int yy_size = (yy ? tile_size : ysize_size_top);
3192 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3193 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3195 BlitBitmap(bitmap_db_store_2, backbuffer,
3196 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3200 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3202 redraw_mask |= REDRAW_FIELD;
3206 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3209 ClearAutoRepeatKeyEvents();
3212 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3214 int graphic = IMG_BACKGROUND_REQUEST;
3215 int sound_opening = SND_REQUEST_OPENING;
3216 int sound_closing = SND_REQUEST_CLOSING;
3217 int anim_mode_1 = request.anim_mode; // (higher priority)
3218 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3219 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3220 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3221 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3223 if (game_status == GAME_MODE_PLAYING)
3224 BlitScreenToBitmap(backbuffer);
3226 SetDrawtoField(DRAW_TO_BACKBUFFER);
3228 // SetDrawBackgroundMask(REDRAW_NONE);
3230 if (action == ACTION_OPENING)
3232 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3234 if (req_state & REQ_ASK)
3236 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3237 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3238 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3239 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3241 else if (req_state & REQ_CONFIRM)
3243 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3244 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3246 else if (req_state & REQ_PLAYER)
3248 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3249 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3250 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3251 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3254 DrawEnvelopeRequest(text);
3257 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3259 if (action == ACTION_OPENING)
3261 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3263 if (anim_mode == ANIM_DEFAULT)
3264 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3266 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3270 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3272 if (anim_mode != ANIM_NONE)
3273 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3275 if (anim_mode == ANIM_DEFAULT)
3276 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3279 game.envelope_active = FALSE;
3281 if (action == ACTION_CLOSING)
3282 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3284 // SetDrawBackgroundMask(last_draw_background_mask);
3286 redraw_mask |= REDRAW_FIELD;
3290 if (action == ACTION_CLOSING &&
3291 game_status == GAME_MODE_PLAYING &&
3292 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3293 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3296 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3298 if (IS_MM_WALL(element))
3300 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3306 int graphic = el2preimg(element);
3308 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3309 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3314 void DrawLevel(int draw_background_mask)
3318 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3319 SetDrawBackgroundMask(draw_background_mask);
3323 for (x = BX1; x <= BX2; x++)
3324 for (y = BY1; y <= BY2; y++)
3325 DrawScreenField(x, y);
3327 redraw_mask |= REDRAW_FIELD;
3330 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3335 for (x = 0; x < size_x; x++)
3336 for (y = 0; y < size_y; y++)
3337 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3339 redraw_mask |= REDRAW_FIELD;
3342 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3346 for (x = 0; x < size_x; x++)
3347 for (y = 0; y < size_y; y++)
3348 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3350 redraw_mask |= REDRAW_FIELD;
3353 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3355 boolean show_level_border = (BorderElement != EL_EMPTY);
3356 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3357 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3358 int tile_size = preview.tile_size;
3359 int preview_width = preview.xsize * tile_size;
3360 int preview_height = preview.ysize * tile_size;
3361 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3362 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3363 int real_preview_width = real_preview_xsize * tile_size;
3364 int real_preview_height = real_preview_ysize * tile_size;
3365 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3366 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3369 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3372 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3374 dst_x += (preview_width - real_preview_width) / 2;
3375 dst_y += (preview_height - real_preview_height) / 2;
3377 for (x = 0; x < real_preview_xsize; x++)
3379 for (y = 0; y < real_preview_ysize; y++)
3381 int lx = from_x + x + (show_level_border ? -1 : 0);
3382 int ly = from_y + y + (show_level_border ? -1 : 0);
3383 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3384 getBorderElement(lx, ly));
3386 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3387 element, tile_size);
3391 redraw_mask |= REDRAW_FIELD;
3394 #define MICROLABEL_EMPTY 0
3395 #define MICROLABEL_LEVEL_NAME 1
3396 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3397 #define MICROLABEL_LEVEL_AUTHOR 3
3398 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3399 #define MICROLABEL_IMPORTED_FROM 5
3400 #define MICROLABEL_IMPORTED_BY_HEAD 6
3401 #define MICROLABEL_IMPORTED_BY 7
3403 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3405 int max_text_width = SXSIZE;
3406 int font_width = getFontWidth(font_nr);
3408 if (pos->align == ALIGN_CENTER)
3409 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3410 else if (pos->align == ALIGN_RIGHT)
3411 max_text_width = pos->x;
3413 max_text_width = SXSIZE - pos->x;
3415 return max_text_width / font_width;
3418 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3420 char label_text[MAX_OUTPUT_LINESIZE + 1];
3421 int max_len_label_text;
3422 int font_nr = pos->font;
3425 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3428 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3429 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3430 mode == MICROLABEL_IMPORTED_BY_HEAD)
3431 font_nr = pos->font_alt;
3433 max_len_label_text = getMaxTextLength(pos, font_nr);
3435 if (pos->size != -1)
3436 max_len_label_text = pos->size;
3438 for (i = 0; i < max_len_label_text; i++)
3439 label_text[i] = ' ';
3440 label_text[max_len_label_text] = '\0';
3442 if (strlen(label_text) > 0)
3443 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3446 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3447 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3448 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3449 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3450 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3451 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3452 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3453 max_len_label_text);
3454 label_text[max_len_label_text] = '\0';
3456 if (strlen(label_text) > 0)
3457 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3459 redraw_mask |= REDRAW_FIELD;
3462 static void DrawPreviewLevelLabel(int mode)
3464 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3467 static void DrawPreviewLevelInfo(int mode)
3469 if (mode == MICROLABEL_LEVEL_NAME)
3470 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3471 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3472 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3475 static void DrawPreviewLevelExt(boolean restart)
3477 static unsigned int scroll_delay = 0;
3478 static unsigned int label_delay = 0;
3479 static int from_x, from_y, scroll_direction;
3480 static int label_state, label_counter;
3481 unsigned int scroll_delay_value = preview.step_delay;
3482 boolean show_level_border = (BorderElement != EL_EMPTY);
3483 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3484 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3491 if (preview.anim_mode == ANIM_CENTERED)
3493 if (level_xsize > preview.xsize)
3494 from_x = (level_xsize - preview.xsize) / 2;
3495 if (level_ysize > preview.ysize)
3496 from_y = (level_ysize - preview.ysize) / 2;
3499 from_x += preview.xoffset;
3500 from_y += preview.yoffset;
3502 scroll_direction = MV_RIGHT;
3506 DrawPreviewLevelPlayfield(from_x, from_y);
3507 DrawPreviewLevelLabel(label_state);
3509 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3510 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3512 // initialize delay counters
3513 DelayReached(&scroll_delay, 0);
3514 DelayReached(&label_delay, 0);
3516 if (leveldir_current->name)
3518 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3519 char label_text[MAX_OUTPUT_LINESIZE + 1];
3520 int font_nr = pos->font;
3521 int max_len_label_text = getMaxTextLength(pos, font_nr);
3523 if (pos->size != -1)
3524 max_len_label_text = pos->size;
3526 strncpy(label_text, leveldir_current->name, max_len_label_text);
3527 label_text[max_len_label_text] = '\0';
3529 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3530 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3536 // scroll preview level, if needed
3537 if (preview.anim_mode != ANIM_NONE &&
3538 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3539 DelayReached(&scroll_delay, scroll_delay_value))
3541 switch (scroll_direction)
3546 from_x -= preview.step_offset;
3547 from_x = (from_x < 0 ? 0 : from_x);
3550 scroll_direction = MV_UP;
3554 if (from_x < level_xsize - preview.xsize)
3556 from_x += preview.step_offset;
3557 from_x = (from_x > level_xsize - preview.xsize ?
3558 level_xsize - preview.xsize : from_x);
3561 scroll_direction = MV_DOWN;
3567 from_y -= preview.step_offset;
3568 from_y = (from_y < 0 ? 0 : from_y);
3571 scroll_direction = MV_RIGHT;
3575 if (from_y < level_ysize - preview.ysize)
3577 from_y += preview.step_offset;
3578 from_y = (from_y > level_ysize - preview.ysize ?
3579 level_ysize - preview.ysize : from_y);
3582 scroll_direction = MV_LEFT;
3589 DrawPreviewLevelPlayfield(from_x, from_y);
3592 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3593 // redraw micro level label, if needed
3594 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3595 !strEqual(level.author, ANONYMOUS_NAME) &&
3596 !strEqual(level.author, leveldir_current->name) &&
3597 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3599 int max_label_counter = 23;
3601 if (leveldir_current->imported_from != NULL &&
3602 strlen(leveldir_current->imported_from) > 0)
3603 max_label_counter += 14;
3604 if (leveldir_current->imported_by != NULL &&
3605 strlen(leveldir_current->imported_by) > 0)
3606 max_label_counter += 14;
3608 label_counter = (label_counter + 1) % max_label_counter;
3609 label_state = (label_counter >= 0 && label_counter <= 7 ?
3610 MICROLABEL_LEVEL_NAME :
3611 label_counter >= 9 && label_counter <= 12 ?
3612 MICROLABEL_LEVEL_AUTHOR_HEAD :
3613 label_counter >= 14 && label_counter <= 21 ?
3614 MICROLABEL_LEVEL_AUTHOR :
3615 label_counter >= 23 && label_counter <= 26 ?
3616 MICROLABEL_IMPORTED_FROM_HEAD :
3617 label_counter >= 28 && label_counter <= 35 ?
3618 MICROLABEL_IMPORTED_FROM :
3619 label_counter >= 37 && label_counter <= 40 ?
3620 MICROLABEL_IMPORTED_BY_HEAD :
3621 label_counter >= 42 && label_counter <= 49 ?
3622 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3624 if (leveldir_current->imported_from == NULL &&
3625 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3626 label_state == MICROLABEL_IMPORTED_FROM))
3627 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3628 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3630 DrawPreviewLevelLabel(label_state);
3634 void DrawPreviewPlayers(void)
3636 if (game_status != GAME_MODE_MAIN)
3639 // do not draw preview players if level preview redefined, but players aren't
3640 if (preview.redefined && !menu.main.preview_players.redefined)
3643 boolean player_found[MAX_PLAYERS];
3644 int num_players = 0;
3647 for (i = 0; i < MAX_PLAYERS; i++)
3648 player_found[i] = FALSE;
3650 // check which players can be found in the level (simple approach)
3651 for (x = 0; x < lev_fieldx; x++)
3653 for (y = 0; y < lev_fieldy; y++)
3655 int element = level.field[x][y];
3657 if (ELEM_IS_PLAYER(element))
3659 int player_nr = GET_PLAYER_NR(element);
3661 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3663 if (!player_found[player_nr])
3666 player_found[player_nr] = TRUE;
3671 struct TextPosInfo *pos = &menu.main.preview_players;
3672 int tile_size = pos->tile_size;
3673 int border_size = pos->border_size;
3674 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3675 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3676 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3677 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3678 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3679 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3680 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3681 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3682 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3683 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3684 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3685 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3687 // clear area in which the players will be drawn
3688 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3689 max_players_width, max_players_height);
3691 if (!network.enabled && !setup.team_mode)
3694 // only draw players if level is suited for team mode
3695 if (num_players < 2)
3698 // draw all players that were found in the level
3699 for (i = 0; i < MAX_PLAYERS; i++)
3701 if (player_found[i])
3703 int graphic = el2img(EL_PLAYER_1 + i);
3705 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3707 xpos += player_xoffset;
3708 ypos += player_yoffset;
3713 void DrawPreviewLevelInitial(void)
3715 DrawPreviewLevelExt(TRUE);
3716 DrawPreviewPlayers();
3719 void DrawPreviewLevelAnimation(void)
3721 DrawPreviewLevelExt(FALSE);
3724 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3725 int border_size, int font_nr)
3727 int graphic = el2img(EL_PLAYER_1 + player_nr);
3728 int font_height = getFontHeight(font_nr);
3729 int player_height = MAX(tile_size, font_height);
3730 int xoffset_text = tile_size + border_size;
3731 int yoffset_text = (player_height - font_height) / 2;
3732 int yoffset_graphic = (player_height - tile_size) / 2;
3733 char *player_name = getNetworkPlayerName(player_nr + 1);
3735 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3737 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3740 static void DrawNetworkPlayersExt(boolean force)
3742 if (game_status != GAME_MODE_MAIN)
3745 if (!network.connected && !force)
3748 // do not draw network players if level preview redefined, but players aren't
3749 if (preview.redefined && !menu.main.network_players.redefined)
3752 int num_players = 0;
3755 for (i = 0; i < MAX_PLAYERS; i++)
3756 if (stored_player[i].connected_network)
3759 struct TextPosInfo *pos = &menu.main.network_players;
3760 int tile_size = pos->tile_size;
3761 int border_size = pos->border_size;
3762 int xoffset_text = tile_size + border_size;
3763 int font_nr = pos->font;
3764 int font_width = getFontWidth(font_nr);
3765 int font_height = getFontHeight(font_nr);
3766 int player_height = MAX(tile_size, font_height);
3767 int player_yoffset = player_height + border_size;
3768 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3769 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3770 int all_players_height = num_players * player_yoffset - border_size;
3771 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3772 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3773 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3775 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3776 max_players_width, max_players_height);
3778 // first draw local network player ...
3779 for (i = 0; i < MAX_PLAYERS; i++)
3781 if (stored_player[i].connected_network &&
3782 stored_player[i].connected_locally)
3784 char *player_name = getNetworkPlayerName(i + 1);
3785 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3786 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3788 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3790 ypos += player_yoffset;
3794 // ... then draw all other network players
3795 for (i = 0; i < MAX_PLAYERS; i++)
3797 if (stored_player[i].connected_network &&
3798 !stored_player[i].connected_locally)
3800 char *player_name = getNetworkPlayerName(i + 1);
3801 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3802 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3804 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3806 ypos += player_yoffset;
3811 void DrawNetworkPlayers(void)
3813 DrawNetworkPlayersExt(FALSE);
3816 void ClearNetworkPlayers(void)
3818 DrawNetworkPlayersExt(TRUE);
3821 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3822 int graphic, int sync_frame,
3825 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3827 if (mask_mode == USE_MASKING)
3828 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3830 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3833 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3834 int graphic, int sync_frame, int mask_mode)
3836 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3838 if (mask_mode == USE_MASKING)
3839 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3841 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3844 static void DrawGraphicAnimation(int x, int y, int graphic)
3846 int lx = LEVELX(x), ly = LEVELY(y);
3847 int mask_mode = NO_MASKING;
3849 if (!IN_SCR_FIELD(x, y))
3852 if (game.use_masked_elements)
3854 if (Tile[lx][ly] != EL_EMPTY)
3856 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3858 mask_mode = USE_MASKING;
3862 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3863 graphic, GfxFrame[lx][ly], mask_mode);
3865 MarkTileDirty(x, y);
3868 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3870 int lx = LEVELX(x), ly = LEVELY(y);
3871 int mask_mode = NO_MASKING;
3873 if (!IN_SCR_FIELD(x, y))
3876 if (game.use_masked_elements)
3878 if (Tile[lx][ly] != EL_EMPTY)
3880 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3882 mask_mode = USE_MASKING;
3886 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3887 graphic, GfxFrame[lx][ly], mask_mode);
3889 MarkTileDirty(x, y);
3892 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3894 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3897 void DrawLevelElementAnimation(int x, int y, int element)
3899 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3901 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3904 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3906 int sx = SCREENX(x), sy = SCREENY(y);
3908 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3911 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3914 DrawGraphicAnimation(sx, sy, graphic);
3917 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3918 DrawLevelFieldCrumbled(x, y);
3920 if (GFX_CRUMBLED(Tile[x][y]))
3921 DrawLevelFieldCrumbled(x, y);
3925 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3927 int sx = SCREENX(x), sy = SCREENY(y);
3930 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3933 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3935 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3938 DrawGraphicAnimation(sx, sy, graphic);
3940 if (GFX_CRUMBLED(element))
3941 DrawLevelFieldCrumbled(x, y);
3944 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3946 if (player->use_murphy)
3948 // this works only because currently only one player can be "murphy" ...
3949 static int last_horizontal_dir = MV_LEFT;
3950 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3952 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3953 last_horizontal_dir = move_dir;
3955 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3957 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3959 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3965 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3968 static boolean equalGraphics(int graphic1, int graphic2)
3970 struct GraphicInfo *g1 = &graphic_info[graphic1];
3971 struct GraphicInfo *g2 = &graphic_info[graphic2];
3973 return (g1->bitmap == g2->bitmap &&
3974 g1->src_x == g2->src_x &&
3975 g1->src_y == g2->src_y &&
3976 g1->anim_frames == g2->anim_frames &&
3977 g1->anim_delay == g2->anim_delay &&
3978 g1->anim_mode == g2->anim_mode);
3981 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3985 DRAW_PLAYER_STAGE_INIT = 0,
3986 DRAW_PLAYER_STAGE_LAST_FIELD,
3987 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3988 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3989 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3990 DRAW_PLAYER_STAGE_PLAYER,
3992 DRAW_PLAYER_STAGE_PLAYER,
3993 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3995 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3996 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3998 NUM_DRAW_PLAYER_STAGES
4001 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4003 static int static_last_player_graphic[MAX_PLAYERS];
4004 static int static_last_player_frame[MAX_PLAYERS];
4005 static boolean static_player_is_opaque[MAX_PLAYERS];
4006 static boolean draw_player[MAX_PLAYERS];
4007 int pnr = player->index_nr;
4009 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4011 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4012 static_last_player_frame[pnr] = player->Frame;
4013 static_player_is_opaque[pnr] = FALSE;
4015 draw_player[pnr] = TRUE;
4018 if (!draw_player[pnr])
4022 if (!IN_LEV_FIELD(player->jx, player->jy))
4024 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4025 Debug("draw:DrawPlayerExt", "This should never happen!");
4027 draw_player[pnr] = FALSE;
4033 int last_player_graphic = static_last_player_graphic[pnr];
4034 int last_player_frame = static_last_player_frame[pnr];
4035 boolean player_is_opaque = static_player_is_opaque[pnr];
4037 int jx = player->jx;
4038 int jy = player->jy;
4039 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4040 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4041 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4042 int last_jx = (player->is_moving ? jx - dx : jx);
4043 int last_jy = (player->is_moving ? jy - dy : jy);
4044 int next_jx = jx + dx;
4045 int next_jy = jy + dy;
4046 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4047 int sx = SCREENX(jx);
4048 int sy = SCREENY(jy);
4049 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4050 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4051 int element = Tile[jx][jy];
4052 int last_element = Tile[last_jx][last_jy];
4053 int action = (player->is_pushing ? ACTION_PUSHING :
4054 player->is_digging ? ACTION_DIGGING :
4055 player->is_collecting ? ACTION_COLLECTING :
4056 player->is_moving ? ACTION_MOVING :
4057 player->is_snapping ? ACTION_SNAPPING :
4058 player->is_dropping ? ACTION_DROPPING :
4059 player->is_waiting ? player->action_waiting :
4062 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4064 // ------------------------------------------------------------------------
4065 // initialize drawing the player
4066 // ------------------------------------------------------------------------
4068 draw_player[pnr] = FALSE;
4070 // GfxElement[][] is set to the element the player is digging or collecting;
4071 // remove also for off-screen player if the player is not moving anymore
4072 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4073 GfxElement[jx][jy] = EL_UNDEFINED;
4075 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4078 if (element == EL_EXPLOSION)
4081 InitPlayerGfxAnimation(player, action, move_dir);
4083 draw_player[pnr] = TRUE;
4085 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4087 // ------------------------------------------------------------------------
4088 // draw things in the field the player is leaving, if needed
4089 // ------------------------------------------------------------------------
4091 if (!IN_SCR_FIELD(sx, sy))
4092 draw_player[pnr] = FALSE;
4094 if (!player->is_moving)
4097 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4099 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4101 if (last_element == EL_DYNAMITE_ACTIVE ||
4102 last_element == EL_EM_DYNAMITE_ACTIVE ||
4103 last_element == EL_SP_DISK_RED_ACTIVE)
4104 DrawDynamite(last_jx, last_jy);
4106 DrawLevelFieldThruMask(last_jx, last_jy);
4108 else if (last_element == EL_DYNAMITE_ACTIVE ||
4109 last_element == EL_EM_DYNAMITE_ACTIVE ||
4110 last_element == EL_SP_DISK_RED_ACTIVE)
4111 DrawDynamite(last_jx, last_jy);
4113 DrawLevelField(last_jx, last_jy);
4115 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4116 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4118 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4120 // ------------------------------------------------------------------------
4121 // draw things behind the player, if needed
4122 // ------------------------------------------------------------------------
4126 DrawLevelElement(jx, jy, Back[jx][jy]);
4131 if (IS_ACTIVE_BOMB(element))
4133 DrawLevelElement(jx, jy, EL_EMPTY);
4138 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4140 int old_element = GfxElement[jx][jy];
4141 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4142 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4144 if (GFX_CRUMBLED(old_element))
4145 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4147 DrawScreenGraphic(sx, sy, old_graphic, frame);
4149 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4150 static_player_is_opaque[pnr] = TRUE;
4154 GfxElement[jx][jy] = EL_UNDEFINED;
4156 // make sure that pushed elements are drawn with correct frame rate
4157 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4159 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4160 GfxFrame[jx][jy] = player->StepFrame;
4162 DrawLevelField(jx, jy);
4165 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4167 // ------------------------------------------------------------------------
4168 // draw things the player is pushing, if needed
4169 // ------------------------------------------------------------------------
4171 if (!player->is_pushing || !player->is_moving)
4174 int gfx_frame = GfxFrame[jx][jy];
4176 if (!IS_MOVING(jx, jy)) // push movement already finished
4178 element = Tile[next_jx][next_jy];
4179 gfx_frame = GfxFrame[next_jx][next_jy];
4182 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4183 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4184 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4186 // draw background element under pushed element (like the Sokoban field)
4187 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4189 // this allows transparent pushing animation over non-black background
4192 DrawLevelElement(jx, jy, Back[jx][jy]);
4194 DrawLevelElement(jx, jy, EL_EMPTY);
4196 if (Back[next_jx][next_jy])
4197 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4199 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4201 else if (Back[next_jx][next_jy])
4202 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4204 int px = SCREENX(jx), py = SCREENY(jy);
4205 int pxx = (TILEX - ABS(sxx)) * dx;
4206 int pyy = (TILEY - ABS(syy)) * dy;
4209 // do not draw (EM style) pushing animation when pushing is finished
4210 // (two-tile animations usually do not contain start and end frame)
4211 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4212 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4214 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4216 // masked drawing is needed for EMC style (double) movement graphics
4217 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4218 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4221 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4223 // ------------------------------------------------------------------------
4224 // draw player himself
4225 // ------------------------------------------------------------------------
4227 int graphic = getPlayerGraphic(player, move_dir);
4229 // in the case of changed player action or direction, prevent the current
4230 // animation frame from being restarted for identical animations
4231 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4232 player->Frame = last_player_frame;
4234 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4236 if (player_is_opaque)
4237 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4239 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4241 if (SHIELD_ON(player))
4243 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4244 IMG_SHIELD_NORMAL_ACTIVE);
4245 frame = getGraphicAnimationFrame(graphic, -1);
4247 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4250 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4252 // ------------------------------------------------------------------------
4253 // draw things in front of player (active dynamite or dynabombs)
4254 // ------------------------------------------------------------------------
4256 if (IS_ACTIVE_BOMB(element))
4258 int graphic = el2img(element);
4259 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4261 if (game.emulation == EMU_SUPAPLEX)
4262 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4264 DrawGraphicThruMask(sx, sy, graphic, frame);
4267 if (player_is_moving && last_element == EL_EXPLOSION)
4269 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4270 GfxElement[last_jx][last_jy] : EL_EMPTY);
4271 int graphic = el_act2img(element, ACTION_EXPLODING);
4272 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4273 int phase = ExplodePhase[last_jx][last_jy] - 1;
4274 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4277 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4280 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4282 // ------------------------------------------------------------------------
4283 // draw elements the player is just walking/passing through/under
4284 // ------------------------------------------------------------------------
4286 if (player_is_moving)
4288 // handle the field the player is leaving ...
4289 if (IS_ACCESSIBLE_INSIDE(last_element))
4290 DrawLevelField(last_jx, last_jy);
4291 else if (IS_ACCESSIBLE_UNDER(last_element))
4292 DrawLevelFieldThruMask(last_jx, last_jy);
4295 // do not redraw accessible elements if the player is just pushing them
4296 if (!player_is_moving || !player->is_pushing)
4298 // ... and the field the player is entering
4299 if (IS_ACCESSIBLE_INSIDE(element))
4300 DrawLevelField(jx, jy);
4301 else if (IS_ACCESSIBLE_UNDER(element))
4302 DrawLevelFieldThruMask(jx, jy);
4305 MarkTileDirty(sx, sy);
4309 void DrawPlayer(struct PlayerInfo *player)
4313 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4314 DrawPlayerExt(player, i);
4317 void DrawAllPlayers(void)
4321 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4322 for (j = 0; j < MAX_PLAYERS; j++)
4323 if (stored_player[j].active)
4324 DrawPlayerExt(&stored_player[j], i);
4327 void DrawPlayerField(int x, int y)
4329 if (!IS_PLAYER(x, y))
4332 DrawPlayer(PLAYERINFO(x, y));
4335 // ----------------------------------------------------------------------------
4337 void WaitForEventToContinue(void)
4339 boolean first_wait = TRUE;
4340 boolean still_wait = TRUE;
4342 if (program.headless)
4345 // simulate releasing mouse button over last gadget, if still pressed
4347 HandleGadgets(-1, -1, 0);
4349 button_status = MB_RELEASED;
4352 ClearPlayerAction();
4358 if (NextValidEvent(&event))
4362 case EVENT_BUTTONPRESS:
4363 case EVENT_FINGERPRESS:
4367 case EVENT_BUTTONRELEASE:
4368 case EVENT_FINGERRELEASE:
4369 still_wait = first_wait;
4372 case EVENT_KEYPRESS:
4373 case SDL_CONTROLLERBUTTONDOWN:
4374 case SDL_JOYBUTTONDOWN:
4379 HandleOtherEvents(&event);
4383 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4388 if (!PendingEvent())
4393 #define MAX_REQUEST_LINES 13
4394 #define MAX_REQUEST_LINE_FONT1_LEN 7
4395 #define MAX_REQUEST_LINE_FONT2_LEN 10
4397 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4399 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4401 int draw_buffer_last = GetDrawtoField();
4402 int width = request.width;
4403 int height = request.height;
4407 // when showing request dialog after game ended, deactivate game panel
4408 if (game_just_ended)
4409 game.panel.active = FALSE;
4411 game.request_active = TRUE;
4413 setRequestPosition(&sx, &sy, FALSE);
4415 button_status = MB_RELEASED;
4417 request_gadget_id = -1;
4422 boolean event_handled = FALSE;
4424 if (game_just_ended)
4426 SetDrawtoField(draw_buffer_game);
4428 HandleGameActions();
4430 SetDrawtoField(DRAW_TO_BACKBUFFER);
4432 if (global.use_envelope_request)
4434 // copy current state of request area to middle of playfield area
4435 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4443 while (NextValidEvent(&event))
4445 event_handled = TRUE;
4449 case EVENT_BUTTONPRESS:
4450 case EVENT_BUTTONRELEASE:
4451 case EVENT_MOTIONNOTIFY:
4455 if (event.type == EVENT_MOTIONNOTIFY)
4460 motion_status = TRUE;
4461 mx = ((MotionEvent *) &event)->x;
4462 my = ((MotionEvent *) &event)->y;
4466 motion_status = FALSE;
4467 mx = ((ButtonEvent *) &event)->x;
4468 my = ((ButtonEvent *) &event)->y;
4469 if (event.type == EVENT_BUTTONPRESS)
4470 button_status = ((ButtonEvent *) &event)->button;
4472 button_status = MB_RELEASED;
4475 // this sets 'request_gadget_id'
4476 HandleGadgets(mx, my, button_status);
4478 switch (request_gadget_id)
4480 case TOOL_CTRL_ID_YES:
4481 case TOOL_CTRL_ID_TOUCH_YES:
4484 case TOOL_CTRL_ID_NO:
4485 case TOOL_CTRL_ID_TOUCH_NO:
4488 case TOOL_CTRL_ID_CONFIRM:
4489 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4490 result = TRUE | FALSE;
4493 case TOOL_CTRL_ID_PLAYER_1:
4496 case TOOL_CTRL_ID_PLAYER_2:
4499 case TOOL_CTRL_ID_PLAYER_3:
4502 case TOOL_CTRL_ID_PLAYER_4:
4507 // only check clickable animations if no request gadget clicked
4508 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4515 case SDL_WINDOWEVENT:
4516 HandleWindowEvent((WindowEvent *) &event);
4519 case SDL_APP_WILLENTERBACKGROUND:
4520 case SDL_APP_DIDENTERBACKGROUND:
4521 case SDL_APP_WILLENTERFOREGROUND:
4522 case SDL_APP_DIDENTERFOREGROUND:
4523 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4526 case EVENT_KEYPRESS:
4528 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4533 if (req_state & REQ_CONFIRM)
4542 #if defined(KSYM_Rewind)
4543 case KSYM_Rewind: // for Amazon Fire TV remote
4552 #if defined(KSYM_FastForward)
4553 case KSYM_FastForward: // for Amazon Fire TV remote
4559 HandleKeysDebug(key, KEY_PRESSED);
4563 if (req_state & REQ_PLAYER)
4565 int old_player_nr = setup.network_player_nr;
4568 result = old_player_nr + 1;
4573 result = old_player_nr + 1;
4604 case EVENT_FINGERRELEASE:
4605 case EVENT_KEYRELEASE:
4606 ClearPlayerAction();
4609 case SDL_CONTROLLERBUTTONDOWN:
4610 switch (event.cbutton.button)
4612 case SDL_CONTROLLER_BUTTON_A:
4613 case SDL_CONTROLLER_BUTTON_X:
4614 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4615 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4619 case SDL_CONTROLLER_BUTTON_B:
4620 case SDL_CONTROLLER_BUTTON_Y:
4621 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4622 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4623 case SDL_CONTROLLER_BUTTON_BACK:
4628 if (req_state & REQ_PLAYER)
4630 int old_player_nr = setup.network_player_nr;
4633 result = old_player_nr + 1;
4635 switch (event.cbutton.button)
4637 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4638 case SDL_CONTROLLER_BUTTON_Y:
4642 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4643 case SDL_CONTROLLER_BUTTON_B:
4647 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4648 case SDL_CONTROLLER_BUTTON_A:
4652 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4653 case SDL_CONTROLLER_BUTTON_X:
4664 case SDL_CONTROLLERBUTTONUP:
4665 HandleJoystickEvent(&event);
4666 ClearPlayerAction();
4670 HandleOtherEvents(&event);
4675 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4677 int joy = AnyJoystick();
4679 if (joy & JOY_BUTTON_1)
4681 else if (joy & JOY_BUTTON_2)
4684 else if (AnyJoystick())
4686 int joy = AnyJoystick();
4688 if (req_state & REQ_PLAYER)
4692 else if (joy & JOY_RIGHT)
4694 else if (joy & JOY_DOWN)
4696 else if (joy & JOY_LEFT)
4703 if (game_just_ended)
4705 if (global.use_envelope_request)
4707 // copy back current state of pressed buttons inside request area
4708 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4712 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4718 SetDrawtoField(draw_buffer_last);
4720 game.request_active = FALSE;
4725 static boolean RequestDoor(char *text, unsigned int req_state)
4727 int draw_buffer_last = GetDrawtoField();
4728 unsigned int old_door_state;
4729 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4730 int font_nr = FONT_TEXT_2;
4735 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4737 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4738 font_nr = FONT_TEXT_1;
4741 if (game_status == GAME_MODE_PLAYING)
4742 BlitScreenToBitmap(backbuffer);
4744 // disable deactivated drawing when quick-loading level tape recording
4745 if (tape.playing && tape.deactivate_display)
4746 TapeDeactivateDisplayOff(TRUE);
4748 SetMouseCursor(CURSOR_DEFAULT);
4750 // pause network game while waiting for request to answer
4751 if (network.enabled &&
4752 game_status == GAME_MODE_PLAYING &&
4753 !game.all_players_gone &&
4754 req_state & REQUEST_WAIT_FOR_INPUT)
4755 SendToServer_PausePlaying();
4757 old_door_state = GetDoorState();
4759 // simulate releasing mouse button over last gadget, if still pressed
4761 HandleGadgets(-1, -1, 0);
4765 // draw released gadget before proceeding
4768 if (old_door_state & DOOR_OPEN_1)
4770 CloseDoor(DOOR_CLOSE_1);
4772 // save old door content
4773 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4774 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4777 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4778 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4780 // clear door drawing field
4781 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4783 // force DOOR font inside door area
4784 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4786 // write text for request
4787 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4789 char text_line[max_request_line_len + 1];
4795 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4797 tc = *(text_ptr + tx);
4798 // if (!tc || tc == ' ')
4799 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4803 if ((tc == '?' || tc == '!') && tl == 0)
4813 strncpy(text_line, text_ptr, tl);
4816 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4817 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4818 text_line, font_nr);
4820 text_ptr += tl + (tc == ' ' ? 1 : 0);
4821 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4826 if (req_state & REQ_ASK)
4828 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4829 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4830 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4831 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4833 else if (req_state & REQ_CONFIRM)
4835 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4836 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4838 else if (req_state & REQ_PLAYER)
4840 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4841 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4842 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4843 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4846 // copy request gadgets to door backbuffer
4847 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4849 OpenDoor(DOOR_OPEN_1);
4851 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4853 if (game_status == GAME_MODE_PLAYING)
4855 SetPanelBackground();
4856 SetDrawBackgroundMask(REDRAW_DOOR_1);
4860 SetDrawBackgroundMask(REDRAW_FIELD);
4866 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4868 // ---------- handle request buttons ----------
4869 result = RequestHandleEvents(req_state, draw_buffer_last);
4873 if (!(req_state & REQ_STAY_OPEN))
4875 CloseDoor(DOOR_CLOSE_1);
4877 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4878 (req_state & REQ_REOPEN))
4879 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4884 if (game_status == GAME_MODE_PLAYING)
4886 SetPanelBackground();
4887 SetDrawBackgroundMask(REDRAW_DOOR_1);
4891 SetDrawBackgroundMask(REDRAW_FIELD);
4894 // continue network game after request
4895 if (network.enabled &&
4896 game_status == GAME_MODE_PLAYING &&
4897 !game.all_players_gone &&
4898 req_state & REQUEST_WAIT_FOR_INPUT)
4899 SendToServer_ContinuePlaying();
4901 // restore deactivated drawing when quick-loading level tape recording
4902 if (tape.playing && tape.deactivate_display)
4903 TapeDeactivateDisplayOn();
4908 static boolean RequestEnvelope(char *text, unsigned int req_state)
4910 int draw_buffer_last = GetDrawtoField();
4913 if (game_status == GAME_MODE_PLAYING)
4914 BlitScreenToBitmap(backbuffer);
4916 // disable deactivated drawing when quick-loading level tape recording
4917 if (tape.playing && tape.deactivate_display)
4918 TapeDeactivateDisplayOff(TRUE);
4920 SetMouseCursor(CURSOR_DEFAULT);
4922 // pause network game while waiting for request to answer
4923 if (network.enabled &&
4924 game_status == GAME_MODE_PLAYING &&
4925 !game.all_players_gone &&
4926 req_state & REQUEST_WAIT_FOR_INPUT)
4927 SendToServer_PausePlaying();
4929 // simulate releasing mouse button over last gadget, if still pressed
4931 HandleGadgets(-1, -1, 0);
4935 // (replace with setting corresponding request background)
4936 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4937 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4939 // clear door drawing field
4940 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4942 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4944 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4946 if (game_status == GAME_MODE_PLAYING)
4948 SetPanelBackground();
4949 SetDrawBackgroundMask(REDRAW_DOOR_1);
4953 SetDrawBackgroundMask(REDRAW_FIELD);
4959 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4961 // ---------- handle request buttons ----------
4962 result = RequestHandleEvents(req_state, draw_buffer_last);
4966 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4970 if (game_status == GAME_MODE_PLAYING)
4972 SetPanelBackground();
4973 SetDrawBackgroundMask(REDRAW_DOOR_1);
4977 SetDrawBackgroundMask(REDRAW_FIELD);
4980 // continue network game after request
4981 if (network.enabled &&
4982 game_status == GAME_MODE_PLAYING &&
4983 !game.all_players_gone &&
4984 req_state & REQUEST_WAIT_FOR_INPUT)
4985 SendToServer_ContinuePlaying();
4987 // restore deactivated drawing when quick-loading level tape recording
4988 if (tape.playing && tape.deactivate_display)
4989 TapeDeactivateDisplayOn();
4994 boolean Request(char *text, unsigned int req_state)
4996 boolean overlay_enabled = GetOverlayEnabled();
4999 game.request_active_or_moving = TRUE;
5001 SetOverlayEnabled(FALSE);
5003 if (global.use_envelope_request)
5004 result = RequestEnvelope(text, req_state);
5006 result = RequestDoor(text, req_state);
5008 SetOverlayEnabled(overlay_enabled);
5010 game.request_active_or_moving = FALSE;
5015 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5017 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5018 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5021 if (dpo1->sort_priority != dpo2->sort_priority)
5022 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5024 compare_result = dpo1->nr - dpo2->nr;
5026 return compare_result;
5029 void InitGraphicCompatibilityInfo_Doors(void)
5035 struct DoorInfo *door;
5039 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5040 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5042 { -1, -1, -1, NULL }
5044 struct Rect door_rect_list[] =
5046 { DX, DY, DXSIZE, DYSIZE },
5047 { VX, VY, VXSIZE, VYSIZE }
5051 for (i = 0; doors[i].door_token != -1; i++)
5053 int door_token = doors[i].door_token;
5054 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5055 int part_1 = doors[i].part_1;
5056 int part_8 = doors[i].part_8;
5057 int part_2 = part_1 + 1;
5058 int part_3 = part_1 + 2;
5059 struct DoorInfo *door = doors[i].door;
5060 struct Rect *door_rect = &door_rect_list[door_index];
5061 boolean door_gfx_redefined = FALSE;
5063 // check if any door part graphic definitions have been redefined
5065 for (j = 0; door_part_controls[j].door_token != -1; j++)
5067 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5068 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5070 if (dpc->door_token == door_token && fi->redefined)
5071 door_gfx_redefined = TRUE;
5074 // check for old-style door graphic/animation modifications
5076 if (!door_gfx_redefined)
5078 if (door->anim_mode & ANIM_STATIC_PANEL)
5080 door->panel.step_xoffset = 0;
5081 door->panel.step_yoffset = 0;
5084 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5086 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5087 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5088 int num_door_steps, num_panel_steps;
5090 // remove door part graphics other than the two default wings
5092 for (j = 0; door_part_controls[j].door_token != -1; j++)
5094 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5095 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5097 if (dpc->graphic >= part_3 &&
5098 dpc->graphic <= part_8)
5102 // set graphics and screen positions of the default wings
5104 g_part_1->width = door_rect->width;
5105 g_part_1->height = door_rect->height;
5106 g_part_2->width = door_rect->width;
5107 g_part_2->height = door_rect->height;
5108 g_part_2->src_x = door_rect->width;
5109 g_part_2->src_y = g_part_1->src_y;
5111 door->part_2.x = door->part_1.x;
5112 door->part_2.y = door->part_1.y;
5114 if (door->width != -1)
5116 g_part_1->width = door->width;
5117 g_part_2->width = door->width;
5119 // special treatment for graphics and screen position of right wing
5120 g_part_2->src_x += door_rect->width - door->width;
5121 door->part_2.x += door_rect->width - door->width;
5124 if (door->height != -1)
5126 g_part_1->height = door->height;
5127 g_part_2->height = door->height;
5129 // special treatment for graphics and screen position of bottom wing
5130 g_part_2->src_y += door_rect->height - door->height;
5131 door->part_2.y += door_rect->height - door->height;
5134 // set animation delays for the default wings and panels
5136 door->part_1.step_delay = door->step_delay;
5137 door->part_2.step_delay = door->step_delay;
5138 door->panel.step_delay = door->step_delay;
5140 // set animation draw order for the default wings
5142 door->part_1.sort_priority = 2; // draw left wing over ...
5143 door->part_2.sort_priority = 1; // ... right wing
5145 // set animation draw offset for the default wings
5147 if (door->anim_mode & ANIM_HORIZONTAL)
5149 door->part_1.step_xoffset = door->step_offset;
5150 door->part_1.step_yoffset = 0;
5151 door->part_2.step_xoffset = door->step_offset * -1;
5152 door->part_2.step_yoffset = 0;
5154 num_door_steps = g_part_1->width / door->step_offset;
5156 else // ANIM_VERTICAL
5158 door->part_1.step_xoffset = 0;
5159 door->part_1.step_yoffset = door->step_offset;
5160 door->part_2.step_xoffset = 0;
5161 door->part_2.step_yoffset = door->step_offset * -1;
5163 num_door_steps = g_part_1->height / door->step_offset;
5166 // set animation draw offset for the default panels
5168 if (door->step_offset > 1)
5170 num_panel_steps = 2 * door_rect->height / door->step_offset;
5171 door->panel.start_step = num_panel_steps - num_door_steps;
5172 door->panel.start_step_closing = door->panel.start_step;
5176 num_panel_steps = door_rect->height / door->step_offset;
5177 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5178 door->panel.start_step_closing = door->panel.start_step;
5179 door->panel.step_delay *= 2;
5186 void InitDoors(void)
5190 for (i = 0; door_part_controls[i].door_token != -1; i++)
5192 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5193 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5195 // initialize "start_step_opening" and "start_step_closing", if needed
5196 if (dpc->pos->start_step_opening == 0 &&
5197 dpc->pos->start_step_closing == 0)
5199 // dpc->pos->start_step_opening = dpc->pos->start_step;
5200 dpc->pos->start_step_closing = dpc->pos->start_step;
5203 // fill structure for door part draw order (sorted below)
5205 dpo->sort_priority = dpc->pos->sort_priority;
5208 // sort door part controls according to sort_priority and graphic number
5209 qsort(door_part_order, MAX_DOOR_PARTS,
5210 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5213 unsigned int OpenDoor(unsigned int door_state)
5215 if (door_state & DOOR_COPY_BACK)
5217 if (door_state & DOOR_OPEN_1)
5218 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5219 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5221 if (door_state & DOOR_OPEN_2)
5222 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5223 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5225 door_state &= ~DOOR_COPY_BACK;
5228 return MoveDoor(door_state);
5231 unsigned int CloseDoor(unsigned int door_state)
5233 unsigned int old_door_state = GetDoorState();
5235 if (!(door_state & DOOR_NO_COPY_BACK))
5237 if (old_door_state & DOOR_OPEN_1)
5238 BlitBitmap(backbuffer, bitmap_db_door_1,
5239 DX, DY, DXSIZE, DYSIZE, 0, 0);
5241 if (old_door_state & DOOR_OPEN_2)
5242 BlitBitmap(backbuffer, bitmap_db_door_2,
5243 VX, VY, VXSIZE, VYSIZE, 0, 0);
5245 door_state &= ~DOOR_NO_COPY_BACK;
5248 return MoveDoor(door_state);
5251 unsigned int GetDoorState(void)
5253 return MoveDoor(DOOR_GET_STATE);
5256 unsigned int SetDoorState(unsigned int door_state)
5258 return MoveDoor(door_state | DOOR_SET_STATE);
5261 static int euclid(int a, int b)
5263 return (b ? euclid(b, a % b) : a);
5266 unsigned int MoveDoor(unsigned int door_state)
5268 struct Rect door_rect_list[] =
5270 { DX, DY, DXSIZE, DYSIZE },
5271 { VX, VY, VXSIZE, VYSIZE }
5273 static int door1 = DOOR_CLOSE_1;
5274 static int door2 = DOOR_CLOSE_2;
5275 unsigned int door_delay = 0;
5276 unsigned int door_delay_value;
5279 if (door_state == DOOR_GET_STATE)
5280 return (door1 | door2);
5282 if (door_state & DOOR_SET_STATE)
5284 if (door_state & DOOR_ACTION_1)
5285 door1 = door_state & DOOR_ACTION_1;
5286 if (door_state & DOOR_ACTION_2)
5287 door2 = door_state & DOOR_ACTION_2;
5289 return (door1 | door2);
5292 if (!(door_state & DOOR_FORCE_REDRAW))
5294 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5295 door_state &= ~DOOR_OPEN_1;
5296 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5297 door_state &= ~DOOR_CLOSE_1;
5298 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5299 door_state &= ~DOOR_OPEN_2;
5300 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5301 door_state &= ~DOOR_CLOSE_2;
5304 if (global.autoplay_leveldir)
5306 door_state |= DOOR_NO_DELAY;
5307 door_state &= ~DOOR_CLOSE_ALL;
5310 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5311 door_state |= DOOR_NO_DELAY;
5313 if (door_state & DOOR_ACTION)
5315 boolean door_panel_drawn[NUM_DOORS];
5316 boolean panel_has_doors[NUM_DOORS];
5317 boolean door_part_skip[MAX_DOOR_PARTS];
5318 boolean door_part_done[MAX_DOOR_PARTS];
5319 boolean door_part_done_all;
5320 int num_steps[MAX_DOOR_PARTS];
5321 int max_move_delay = 0; // delay for complete animations of all doors
5322 int max_step_delay = 0; // delay (ms) between two animation frames
5323 int num_move_steps = 0; // number of animation steps for all doors
5324 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5325 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5326 int current_move_delay = 0;
5330 for (i = 0; i < NUM_DOORS; i++)
5331 panel_has_doors[i] = FALSE;
5333 for (i = 0; i < MAX_DOOR_PARTS; i++)
5335 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5336 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5337 int door_token = dpc->door_token;
5339 door_part_done[i] = FALSE;
5340 door_part_skip[i] = (!(door_state & door_token) ||
5344 for (i = 0; i < MAX_DOOR_PARTS; i++)
5346 int nr = door_part_order[i].nr;
5347 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5348 struct DoorPartPosInfo *pos = dpc->pos;
5349 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5350 int door_token = dpc->door_token;
5351 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5352 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5353 int step_xoffset = ABS(pos->step_xoffset);
5354 int step_yoffset = ABS(pos->step_yoffset);
5355 int step_delay = pos->step_delay;
5356 int current_door_state = door_state & door_token;
5357 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5358 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5359 boolean part_opening = (is_panel ? door_closing : door_opening);
5360 int start_step = (part_opening ? pos->start_step_opening :
5361 pos->start_step_closing);
5362 float move_xsize = (step_xoffset ? g->width : 0);
5363 float move_ysize = (step_yoffset ? g->height : 0);
5364 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5365 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5366 int move_steps = (move_xsteps && move_ysteps ?
5367 MIN(move_xsteps, move_ysteps) :
5368 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5369 int move_delay = move_steps * step_delay;
5371 if (door_part_skip[nr])
5374 max_move_delay = MAX(max_move_delay, move_delay);
5375 max_step_delay = (max_step_delay == 0 ? step_delay :
5376 euclid(max_step_delay, step_delay));
5377 num_steps[nr] = move_steps;
5381 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5383 panel_has_doors[door_index] = TRUE;
5387 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5389 num_move_steps = max_move_delay / max_step_delay;
5390 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5392 door_delay_value = max_step_delay;
5394 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5396 start = num_move_steps - 1;
5400 // opening door sound has priority over simultaneously closing door
5401 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5403 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5405 if (door_state & DOOR_OPEN_1)
5406 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5407 if (door_state & DOOR_OPEN_2)
5408 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5410 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5412 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5414 if (door_state & DOOR_CLOSE_1)
5415 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5416 if (door_state & DOOR_CLOSE_2)
5417 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5421 for (k = start; k < num_move_steps; k++)
5423 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5425 door_part_done_all = TRUE;
5427 for (i = 0; i < NUM_DOORS; i++)
5428 door_panel_drawn[i] = FALSE;
5430 for (i = 0; i < MAX_DOOR_PARTS; i++)
5432 int nr = door_part_order[i].nr;
5433 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5434 struct DoorPartPosInfo *pos = dpc->pos;
5435 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5436 int door_token = dpc->door_token;
5437 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5438 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5439 boolean is_panel_and_door_has_closed = FALSE;
5440 struct Rect *door_rect = &door_rect_list[door_index];
5441 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5443 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5444 int current_door_state = door_state & door_token;
5445 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5446 boolean door_closing = !door_opening;
5447 boolean part_opening = (is_panel ? door_closing : door_opening);
5448 boolean part_closing = !part_opening;
5449 int start_step = (part_opening ? pos->start_step_opening :
5450 pos->start_step_closing);
5451 int step_delay = pos->step_delay;
5452 int step_factor = step_delay / max_step_delay;
5453 int k1 = (step_factor ? k / step_factor + 1 : k);
5454 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5455 int kk = MAX(0, k2);
5458 int src_x, src_y, src_xx, src_yy;
5459 int dst_x, dst_y, dst_xx, dst_yy;
5462 if (door_part_skip[nr])
5465 if (!(door_state & door_token))
5473 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5474 int kk_door = MAX(0, k2_door);
5475 int sync_frame = kk_door * door_delay_value;
5476 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5478 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5479 &g_src_x, &g_src_y);
5484 if (!door_panel_drawn[door_index])
5486 ClearRectangle(drawto, door_rect->x, door_rect->y,
5487 door_rect->width, door_rect->height);
5489 door_panel_drawn[door_index] = TRUE;
5492 // draw opening or closing door parts
5494 if (pos->step_xoffset < 0) // door part on right side
5497 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5500 if (dst_xx + width > door_rect->width)
5501 width = door_rect->width - dst_xx;
5503 else // door part on left side
5506 dst_xx = pos->x - kk * pos->step_xoffset;
5510 src_xx = ABS(dst_xx);
5514 width = g->width - src_xx;
5516 if (width > door_rect->width)
5517 width = door_rect->width;
5519 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5522 if (pos->step_yoffset < 0) // door part on bottom side
5525 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5528 if (dst_yy + height > door_rect->height)
5529 height = door_rect->height - dst_yy;
5531 else // door part on top side
5534 dst_yy = pos->y - kk * pos->step_yoffset;
5538 src_yy = ABS(dst_yy);
5542 height = g->height - src_yy;
5545 src_x = g_src_x + src_xx;
5546 src_y = g_src_y + src_yy;
5548 dst_x = door_rect->x + dst_xx;
5549 dst_y = door_rect->y + dst_yy;
5551 is_panel_and_door_has_closed =
5554 panel_has_doors[door_index] &&
5555 k >= num_move_steps_doors_only - 1);
5557 if (width >= 0 && width <= g->width &&
5558 height >= 0 && height <= g->height &&
5559 !is_panel_and_door_has_closed)
5561 if (is_panel || !pos->draw_masked)
5562 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5565 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5569 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5571 if ((part_opening && (width < 0 || height < 0)) ||
5572 (part_closing && (width >= g->width && height >= g->height)))
5573 door_part_done[nr] = TRUE;
5575 // continue door part animations, but not panel after door has closed
5576 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5577 door_part_done_all = FALSE;
5580 if (!(door_state & DOOR_NO_DELAY))
5584 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5586 current_move_delay += max_step_delay;
5588 // prevent OS (Windows) from complaining about program not responding
5592 if (door_part_done_all)
5596 if (!(door_state & DOOR_NO_DELAY))
5598 // wait for specified door action post delay
5599 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5600 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5601 else if (door_state & DOOR_ACTION_1)
5602 door_delay_value = door_1.post_delay;
5603 else if (door_state & DOOR_ACTION_2)
5604 door_delay_value = door_2.post_delay;
5606 while (!DelayReached(&door_delay, door_delay_value))
5611 if (door_state & DOOR_ACTION_1)
5612 door1 = door_state & DOOR_ACTION_1;
5613 if (door_state & DOOR_ACTION_2)
5614 door2 = door_state & DOOR_ACTION_2;
5616 // draw masked border over door area
5617 DrawMaskedBorder(REDRAW_DOOR_1);
5618 DrawMaskedBorder(REDRAW_DOOR_2);
5620 ClearAutoRepeatKeyEvents();
5622 return (door1 | door2);
5625 static boolean useSpecialEditorDoor(void)
5627 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5628 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5630 // do not draw special editor door if editor border defined or redefined
5631 if (graphic_info[graphic].bitmap != NULL || redefined)
5634 // do not draw special editor door if global border defined to be empty
5635 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5638 // do not draw special editor door if viewport definitions do not match
5642 EY + EYSIZE != VY + VYSIZE)
5648 void DrawSpecialEditorDoor(void)
5650 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5651 int top_border_width = gfx1->width;
5652 int top_border_height = gfx1->height;
5653 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5654 int ex = EX - outer_border;
5655 int ey = EY - outer_border;
5656 int vy = VY - outer_border;
5657 int exsize = EXSIZE + 2 * outer_border;
5659 if (!useSpecialEditorDoor())
5662 // draw bigger level editor toolbox window
5663 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5664 top_border_width, top_border_height, ex, ey - top_border_height);
5665 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5666 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5668 redraw_mask |= REDRAW_ALL;
5671 void UndrawSpecialEditorDoor(void)
5673 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5674 int top_border_width = gfx1->width;
5675 int top_border_height = gfx1->height;
5676 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5677 int ex = EX - outer_border;
5678 int ey = EY - outer_border;
5679 int ey_top = ey - top_border_height;
5680 int exsize = EXSIZE + 2 * outer_border;
5681 int eysize = EYSIZE + 2 * outer_border;
5683 if (!useSpecialEditorDoor())
5686 // draw normal tape recorder window
5687 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5689 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5690 ex, ey_top, top_border_width, top_border_height,
5692 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5693 ex, ey, exsize, eysize, ex, ey);
5697 // if screen background is set to "[NONE]", clear editor toolbox window
5698 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5699 ClearRectangle(drawto, ex, ey, exsize, eysize);
5702 redraw_mask |= REDRAW_ALL;
5706 // ---------- new tool button stuff -------------------------------------------
5711 struct TextPosInfo *pos;
5713 boolean is_touch_button;
5715 } toolbutton_info[NUM_TOOL_BUTTONS] =
5718 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5719 TOOL_CTRL_ID_YES, FALSE, "yes"
5722 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5723 TOOL_CTRL_ID_NO, FALSE, "no"
5726 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5727 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5730 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5731 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5734 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5735 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5738 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5739 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5742 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5743 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5746 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5747 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5750 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5751 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5754 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5755 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5759 void CreateToolButtons(void)
5763 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5765 int graphic = toolbutton_info[i].graphic;
5766 struct GraphicInfo *gfx = &graphic_info[graphic];
5767 struct TextPosInfo *pos = toolbutton_info[i].pos;
5768 struct GadgetInfo *gi;
5769 Bitmap *deco_bitmap = None;
5770 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5771 unsigned int event_mask = GD_EVENT_RELEASED;
5772 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5773 int base_x = (is_touch_button ? 0 : DX);
5774 int base_y = (is_touch_button ? 0 : DY);
5775 int gd_x = gfx->src_x;
5776 int gd_y = gfx->src_y;
5777 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5778 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5783 if (global.use_envelope_request && !is_touch_button)
5785 setRequestPosition(&base_x, &base_y, TRUE);
5787 // check if request buttons are outside of envelope and fix, if needed
5788 if (x < 0 || x + gfx->width > request.width ||
5789 y < 0 || y + gfx->height > request.height)
5791 if (id == TOOL_CTRL_ID_YES)
5794 y = request.height - 2 * request.border_size - gfx->height;
5796 else if (id == TOOL_CTRL_ID_NO)
5798 x = request.width - 2 * request.border_size - gfx->width;
5799 y = request.height - 2 * request.border_size - gfx->height;
5801 else if (id == TOOL_CTRL_ID_CONFIRM)
5803 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5804 y = request.height - 2 * request.border_size - gfx->height;
5806 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5808 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5810 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5811 y = request.height - 2 * request.border_size - gfx->height * 2;
5813 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5814 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5819 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5821 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5823 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5824 pos->size, &deco_bitmap, &deco_x, &deco_y);
5825 deco_xpos = (gfx->width - pos->size) / 2;
5826 deco_ypos = (gfx->height - pos->size) / 2;
5829 gi = CreateGadget(GDI_CUSTOM_ID, id,
5830 GDI_IMAGE_ID, graphic,
5831 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5834 GDI_WIDTH, gfx->width,
5835 GDI_HEIGHT, gfx->height,
5836 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5837 GDI_STATE, GD_BUTTON_UNPRESSED,
5838 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5839 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5840 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5841 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5842 GDI_DECORATION_SIZE, pos->size, pos->size,
5843 GDI_DECORATION_SHIFTING, 1, 1,
5844 GDI_DIRECT_DRAW, FALSE,
5845 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5846 GDI_EVENT_MASK, event_mask,
5847 GDI_CALLBACK_ACTION, HandleToolButtons,
5851 Fail("cannot create gadget");
5853 tool_gadget[id] = gi;
5857 void FreeToolButtons(void)
5861 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5862 FreeGadget(tool_gadget[i]);
5865 static void UnmapToolButtons(void)
5869 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5870 UnmapGadget(tool_gadget[i]);
5873 static void HandleToolButtons(struct GadgetInfo *gi)
5875 request_gadget_id = gi->custom_id;
5878 static struct Mapping_EM_to_RND_object
5881 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5882 boolean is_backside; // backside of moving element
5888 em_object_mapping_list[GAME_TILE_MAX + 1] =
5891 Zborder, FALSE, FALSE,
5895 Zplayer, FALSE, FALSE,
5904 Ztank, FALSE, FALSE,
5908 Zeater, FALSE, FALSE,
5912 Zdynamite, FALSE, FALSE,
5916 Zboom, FALSE, FALSE,
5921 Xchain, FALSE, FALSE,
5922 EL_DEFAULT, ACTION_EXPLODING, -1
5925 Xboom_bug, FALSE, FALSE,
5926 EL_BUG, ACTION_EXPLODING, -1
5929 Xboom_tank, FALSE, FALSE,
5930 EL_SPACESHIP, ACTION_EXPLODING, -1
5933 Xboom_android, FALSE, FALSE,
5934 EL_EMC_ANDROID, ACTION_OTHER, -1
5937 Xboom_1, FALSE, FALSE,
5938 EL_DEFAULT, ACTION_EXPLODING, -1
5941 Xboom_2, FALSE, FALSE,
5942 EL_DEFAULT, ACTION_EXPLODING, -1
5946 Xblank, TRUE, FALSE,
5951 Xsplash_e, FALSE, FALSE,
5952 EL_ACID_SPLASH_RIGHT, -1, -1
5955 Xsplash_w, FALSE, FALSE,
5956 EL_ACID_SPLASH_LEFT, -1, -1
5960 Xplant, TRUE, FALSE,
5961 EL_EMC_PLANT, -1, -1
5964 Yplant, FALSE, FALSE,
5965 EL_EMC_PLANT, -1, -1
5969 Xacid_1, TRUE, FALSE,
5973 Xacid_2, FALSE, FALSE,
5977 Xacid_3, FALSE, FALSE,
5981 Xacid_4, FALSE, FALSE,
5985 Xacid_5, FALSE, FALSE,
5989 Xacid_6, FALSE, FALSE,
5993 Xacid_7, FALSE, FALSE,
5997 Xacid_8, FALSE, FALSE,
6002 Xfake_acid_1, TRUE, FALSE,
6003 EL_EMC_FAKE_ACID, -1, -1
6006 Xfake_acid_2, FALSE, FALSE,
6007 EL_EMC_FAKE_ACID, -1, -1
6010 Xfake_acid_3, FALSE, FALSE,
6011 EL_EMC_FAKE_ACID, -1, -1
6014 Xfake_acid_4, FALSE, FALSE,
6015 EL_EMC_FAKE_ACID, -1, -1
6018 Xfake_acid_5, FALSE, FALSE,
6019 EL_EMC_FAKE_ACID, -1, -1
6022 Xfake_acid_6, FALSE, FALSE,
6023 EL_EMC_FAKE_ACID, -1, -1
6026 Xfake_acid_7, FALSE, FALSE,
6027 EL_EMC_FAKE_ACID, -1, -1
6030 Xfake_acid_8, FALSE, FALSE,
6031 EL_EMC_FAKE_ACID, -1, -1
6035 Xfake_acid_1_player, FALSE, FALSE,
6036 EL_EMC_FAKE_ACID, -1, -1
6039 Xfake_acid_2_player, FALSE, FALSE,
6040 EL_EMC_FAKE_ACID, -1, -1
6043 Xfake_acid_3_player, FALSE, FALSE,
6044 EL_EMC_FAKE_ACID, -1, -1
6047 Xfake_acid_4_player, FALSE, FALSE,
6048 EL_EMC_FAKE_ACID, -1, -1
6051 Xfake_acid_5_player, FALSE, FALSE,
6052 EL_EMC_FAKE_ACID, -1, -1
6055 Xfake_acid_6_player, FALSE, FALSE,
6056 EL_EMC_FAKE_ACID, -1, -1
6059 Xfake_acid_7_player, FALSE, FALSE,
6060 EL_EMC_FAKE_ACID, -1, -1
6063 Xfake_acid_8_player, FALSE, FALSE,
6064 EL_EMC_FAKE_ACID, -1, -1
6068 Xgrass, TRUE, FALSE,
6069 EL_EMC_GRASS, -1, -1
6072 Ygrass_nB, FALSE, FALSE,
6073 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6076 Ygrass_eB, FALSE, FALSE,
6077 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6080 Ygrass_sB, FALSE, FALSE,
6081 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6084 Ygrass_wB, FALSE, FALSE,
6085 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6093 Ydirt_nB, FALSE, FALSE,
6094 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6097 Ydirt_eB, FALSE, FALSE,
6098 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6101 Ydirt_sB, FALSE, FALSE,
6102 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6105 Ydirt_wB, FALSE, FALSE,
6106 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6110 Xandroid, TRUE, FALSE,
6111 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6114 Xandroid_1_n, FALSE, FALSE,
6115 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6118 Xandroid_2_n, FALSE, FALSE,
6119 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6122 Xandroid_1_e, FALSE, FALSE,
6123 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6126 Xandroid_2_e, FALSE, FALSE,
6127 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6130 Xandroid_1_w, FALSE, FALSE,
6131 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6134 Xandroid_2_w, FALSE, FALSE,
6135 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6138 Xandroid_1_s, FALSE, FALSE,
6139 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6142 Xandroid_2_s, FALSE, FALSE,
6143 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6146 Yandroid_n, FALSE, FALSE,
6147 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6150 Yandroid_nB, FALSE, TRUE,
6151 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6154 Yandroid_ne, FALSE, FALSE,
6155 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6158 Yandroid_neB, FALSE, TRUE,
6159 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6162 Yandroid_e, FALSE, FALSE,
6163 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6166 Yandroid_eB, FALSE, TRUE,
6167 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6170 Yandroid_se, FALSE, FALSE,
6171 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6174 Yandroid_seB, FALSE, TRUE,
6175 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6178 Yandroid_s, FALSE, FALSE,
6179 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6182 Yandroid_sB, FALSE, TRUE,
6183 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6186 Yandroid_sw, FALSE, FALSE,
6187 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6190 Yandroid_swB, FALSE, TRUE,
6191 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6194 Yandroid_w, FALSE, FALSE,
6195 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6198 Yandroid_wB, FALSE, TRUE,
6199 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6202 Yandroid_nw, FALSE, FALSE,
6203 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6206 Yandroid_nwB, FALSE, TRUE,
6207 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6211 Xeater_n, TRUE, FALSE,
6212 EL_YAMYAM_UP, -1, -1
6215 Xeater_e, TRUE, FALSE,
6216 EL_YAMYAM_RIGHT, -1, -1
6219 Xeater_w, TRUE, FALSE,
6220 EL_YAMYAM_LEFT, -1, -1
6223 Xeater_s, TRUE, FALSE,
6224 EL_YAMYAM_DOWN, -1, -1
6227 Yeater_n, FALSE, FALSE,
6228 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6231 Yeater_nB, FALSE, TRUE,
6232 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6235 Yeater_e, FALSE, FALSE,
6236 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6239 Yeater_eB, FALSE, TRUE,
6240 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6243 Yeater_s, FALSE, FALSE,
6244 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6247 Yeater_sB, FALSE, TRUE,
6248 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6251 Yeater_w, FALSE, FALSE,
6252 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6255 Yeater_wB, FALSE, TRUE,
6256 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6259 Yeater_stone, FALSE, FALSE,
6260 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6263 Yeater_spring, FALSE, FALSE,
6264 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6268 Xalien, TRUE, FALSE,
6272 Xalien_pause, FALSE, FALSE,
6276 Yalien_n, FALSE, FALSE,
6277 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6280 Yalien_nB, FALSE, TRUE,
6281 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6284 Yalien_e, FALSE, FALSE,
6285 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6288 Yalien_eB, FALSE, TRUE,
6289 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6292 Yalien_s, FALSE, FALSE,
6293 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6296 Yalien_sB, FALSE, TRUE,
6297 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6300 Yalien_w, FALSE, FALSE,
6301 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6304 Yalien_wB, FALSE, TRUE,
6305 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6308 Yalien_stone, FALSE, FALSE,
6309 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6312 Yalien_spring, FALSE, FALSE,
6313 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6317 Xbug_1_n, TRUE, FALSE,
6321 Xbug_1_e, TRUE, FALSE,
6322 EL_BUG_RIGHT, -1, -1
6325 Xbug_1_s, TRUE, FALSE,
6329 Xbug_1_w, TRUE, FALSE,
6333 Xbug_2_n, FALSE, FALSE,
6337 Xbug_2_e, FALSE, FALSE,
6338 EL_BUG_RIGHT, -1, -1
6341 Xbug_2_s, FALSE, FALSE,
6345 Xbug_2_w, FALSE, FALSE,
6349 Ybug_n, FALSE, FALSE,
6350 EL_BUG, ACTION_MOVING, MV_BIT_UP
6353 Ybug_nB, FALSE, TRUE,
6354 EL_BUG, ACTION_MOVING, MV_BIT_UP
6357 Ybug_e, FALSE, FALSE,
6358 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6361 Ybug_eB, FALSE, TRUE,
6362 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6365 Ybug_s, FALSE, FALSE,
6366 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6369 Ybug_sB, FALSE, TRUE,
6370 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6373 Ybug_w, FALSE, FALSE,
6374 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6377 Ybug_wB, FALSE, TRUE,
6378 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6381 Ybug_w_n, FALSE, FALSE,
6382 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6385 Ybug_n_e, FALSE, FALSE,
6386 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6389 Ybug_e_s, FALSE, FALSE,
6390 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6393 Ybug_s_w, FALSE, FALSE,
6394 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6397 Ybug_e_n, FALSE, FALSE,
6398 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6401 Ybug_s_e, FALSE, FALSE,
6402 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6405 Ybug_w_s, FALSE, FALSE,
6406 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6409 Ybug_n_w, FALSE, FALSE,
6410 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6413 Ybug_stone, FALSE, FALSE,
6414 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6417 Ybug_spring, FALSE, FALSE,
6418 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6422 Xtank_1_n, TRUE, FALSE,
6423 EL_SPACESHIP_UP, -1, -1
6426 Xtank_1_e, TRUE, FALSE,
6427 EL_SPACESHIP_RIGHT, -1, -1
6430 Xtank_1_s, TRUE, FALSE,
6431 EL_SPACESHIP_DOWN, -1, -1
6434 Xtank_1_w, TRUE, FALSE,
6435 EL_SPACESHIP_LEFT, -1, -1
6438 Xtank_2_n, FALSE, FALSE,
6439 EL_SPACESHIP_UP, -1, -1
6442 Xtank_2_e, FALSE, FALSE,
6443 EL_SPACESHIP_RIGHT, -1, -1
6446 Xtank_2_s, FALSE, FALSE,
6447 EL_SPACESHIP_DOWN, -1, -1
6450 Xtank_2_w, FALSE, FALSE,
6451 EL_SPACESHIP_LEFT, -1, -1
6454 Ytank_n, FALSE, FALSE,
6455 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6458 Ytank_nB, FALSE, TRUE,
6459 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6462 Ytank_e, FALSE, FALSE,
6463 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6466 Ytank_eB, FALSE, TRUE,
6467 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6470 Ytank_s, FALSE, FALSE,
6471 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6474 Ytank_sB, FALSE, TRUE,
6475 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6478 Ytank_w, FALSE, FALSE,
6479 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6482 Ytank_wB, FALSE, TRUE,
6483 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6486 Ytank_w_n, FALSE, FALSE,
6487 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6490 Ytank_n_e, FALSE, FALSE,
6491 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6494 Ytank_e_s, FALSE, FALSE,
6495 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6498 Ytank_s_w, FALSE, FALSE,
6499 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6502 Ytank_e_n, FALSE, FALSE,
6503 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6506 Ytank_s_e, FALSE, FALSE,
6507 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6510 Ytank_w_s, FALSE, FALSE,
6511 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6514 Ytank_n_w, FALSE, FALSE,
6515 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6518 Ytank_stone, FALSE, FALSE,
6519 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6522 Ytank_spring, FALSE, FALSE,
6523 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6527 Xemerald, TRUE, FALSE,
6531 Xemerald_pause, FALSE, FALSE,
6535 Xemerald_fall, FALSE, FALSE,
6539 Xemerald_shine, FALSE, FALSE,
6540 EL_EMERALD, ACTION_TWINKLING, -1
6543 Yemerald_s, FALSE, FALSE,
6544 EL_EMERALD, ACTION_FALLING, -1
6547 Yemerald_sB, FALSE, TRUE,
6548 EL_EMERALD, ACTION_FALLING, -1
6551 Yemerald_e, FALSE, FALSE,
6552 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6555 Yemerald_eB, FALSE, TRUE,
6556 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6559 Yemerald_w, FALSE, FALSE,
6560 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6563 Yemerald_wB, FALSE, TRUE,
6564 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6567 Yemerald_blank, FALSE, FALSE,
6568 EL_EMERALD, ACTION_COLLECTING, -1
6572 Xdiamond, TRUE, FALSE,
6576 Xdiamond_pause, FALSE, FALSE,
6580 Xdiamond_fall, FALSE, FALSE,
6584 Xdiamond_shine, FALSE, FALSE,
6585 EL_DIAMOND, ACTION_TWINKLING, -1
6588 Ydiamond_s, FALSE, FALSE,
6589 EL_DIAMOND, ACTION_FALLING, -1
6592 Ydiamond_sB, FALSE, TRUE,
6593 EL_DIAMOND, ACTION_FALLING, -1
6596 Ydiamond_e, FALSE, FALSE,
6597 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6600 Ydiamond_eB, FALSE, TRUE,
6601 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6604 Ydiamond_w, FALSE, FALSE,
6605 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6608 Ydiamond_wB, FALSE, TRUE,
6609 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6612 Ydiamond_blank, FALSE, FALSE,
6613 EL_DIAMOND, ACTION_COLLECTING, -1
6616 Ydiamond_stone, FALSE, FALSE,
6617 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6621 Xstone, TRUE, FALSE,
6625 Xstone_pause, FALSE, FALSE,
6629 Xstone_fall, FALSE, FALSE,
6633 Ystone_s, FALSE, FALSE,
6634 EL_ROCK, ACTION_FALLING, -1
6637 Ystone_sB, FALSE, TRUE,
6638 EL_ROCK, ACTION_FALLING, -1
6641 Ystone_e, FALSE, FALSE,
6642 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6645 Ystone_eB, FALSE, TRUE,
6646 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6649 Ystone_w, FALSE, FALSE,
6650 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6653 Ystone_wB, FALSE, TRUE,
6654 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6662 Xbomb_pause, FALSE, FALSE,
6666 Xbomb_fall, FALSE, FALSE,
6670 Ybomb_s, FALSE, FALSE,
6671 EL_BOMB, ACTION_FALLING, -1
6674 Ybomb_sB, FALSE, TRUE,
6675 EL_BOMB, ACTION_FALLING, -1
6678 Ybomb_e, FALSE, FALSE,
6679 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6682 Ybomb_eB, FALSE, TRUE,
6683 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6686 Ybomb_w, FALSE, FALSE,
6687 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6690 Ybomb_wB, FALSE, TRUE,
6691 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6694 Ybomb_blank, FALSE, FALSE,
6695 EL_BOMB, ACTION_ACTIVATING, -1
6703 Xnut_pause, FALSE, FALSE,
6707 Xnut_fall, FALSE, FALSE,
6711 Ynut_s, FALSE, FALSE,
6712 EL_NUT, ACTION_FALLING, -1
6715 Ynut_sB, FALSE, TRUE,
6716 EL_NUT, ACTION_FALLING, -1
6719 Ynut_e, FALSE, FALSE,
6720 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6723 Ynut_eB, FALSE, TRUE,
6724 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6727 Ynut_w, FALSE, FALSE,
6728 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6731 Ynut_wB, FALSE, TRUE,
6732 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6735 Ynut_stone, FALSE, FALSE,
6736 EL_NUT, ACTION_BREAKING, -1
6740 Xspring, TRUE, FALSE,
6744 Xspring_pause, FALSE, FALSE,
6748 Xspring_e, TRUE, FALSE,
6749 EL_SPRING_RIGHT, -1, -1
6752 Xspring_w, TRUE, FALSE,
6753 EL_SPRING_LEFT, -1, -1
6756 Xspring_fall, FALSE, FALSE,
6760 Yspring_s, FALSE, FALSE,
6761 EL_SPRING, ACTION_FALLING, -1
6764 Yspring_sB, FALSE, TRUE,
6765 EL_SPRING, ACTION_FALLING, -1
6768 Yspring_e, FALSE, FALSE,
6769 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6772 Yspring_eB, FALSE, TRUE,
6773 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6776 Yspring_w, FALSE, FALSE,
6777 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6780 Yspring_wB, FALSE, TRUE,
6781 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6784 Yspring_alien_e, FALSE, FALSE,
6785 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6788 Yspring_alien_eB, FALSE, TRUE,
6789 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6792 Yspring_alien_w, FALSE, FALSE,
6793 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6796 Yspring_alien_wB, FALSE, TRUE,
6797 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6801 Xpush_emerald_e, FALSE, FALSE,
6802 EL_EMERALD, -1, MV_BIT_RIGHT
6805 Xpush_emerald_w, FALSE, FALSE,
6806 EL_EMERALD, -1, MV_BIT_LEFT
6809 Xpush_diamond_e, FALSE, FALSE,
6810 EL_DIAMOND, -1, MV_BIT_RIGHT
6813 Xpush_diamond_w, FALSE, FALSE,
6814 EL_DIAMOND, -1, MV_BIT_LEFT
6817 Xpush_stone_e, FALSE, FALSE,
6818 EL_ROCK, -1, MV_BIT_RIGHT
6821 Xpush_stone_w, FALSE, FALSE,
6822 EL_ROCK, -1, MV_BIT_LEFT
6825 Xpush_bomb_e, FALSE, FALSE,
6826 EL_BOMB, -1, MV_BIT_RIGHT
6829 Xpush_bomb_w, FALSE, FALSE,
6830 EL_BOMB, -1, MV_BIT_LEFT
6833 Xpush_nut_e, FALSE, FALSE,
6834 EL_NUT, -1, MV_BIT_RIGHT
6837 Xpush_nut_w, FALSE, FALSE,
6838 EL_NUT, -1, MV_BIT_LEFT
6841 Xpush_spring_e, FALSE, FALSE,
6842 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6845 Xpush_spring_w, FALSE, FALSE,
6846 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6850 Xdynamite, TRUE, FALSE,
6851 EL_EM_DYNAMITE, -1, -1
6854 Ydynamite_blank, FALSE, FALSE,
6855 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6858 Xdynamite_1, TRUE, FALSE,
6859 EL_EM_DYNAMITE_ACTIVE, -1, -1
6862 Xdynamite_2, FALSE, FALSE,
6863 EL_EM_DYNAMITE_ACTIVE, -1, -1
6866 Xdynamite_3, FALSE, FALSE,
6867 EL_EM_DYNAMITE_ACTIVE, -1, -1
6870 Xdynamite_4, FALSE, FALSE,
6871 EL_EM_DYNAMITE_ACTIVE, -1, -1
6875 Xkey_1, TRUE, FALSE,
6879 Xkey_2, TRUE, FALSE,
6883 Xkey_3, TRUE, FALSE,
6887 Xkey_4, TRUE, FALSE,
6891 Xkey_5, TRUE, FALSE,
6892 EL_EMC_KEY_5, -1, -1
6895 Xkey_6, TRUE, FALSE,
6896 EL_EMC_KEY_6, -1, -1
6899 Xkey_7, TRUE, FALSE,
6900 EL_EMC_KEY_7, -1, -1
6903 Xkey_8, TRUE, FALSE,
6904 EL_EMC_KEY_8, -1, -1
6908 Xdoor_1, TRUE, FALSE,
6909 EL_EM_GATE_1, -1, -1
6912 Xdoor_2, TRUE, FALSE,
6913 EL_EM_GATE_2, -1, -1
6916 Xdoor_3, TRUE, FALSE,
6917 EL_EM_GATE_3, -1, -1
6920 Xdoor_4, TRUE, FALSE,
6921 EL_EM_GATE_4, -1, -1
6924 Xdoor_5, TRUE, FALSE,
6925 EL_EMC_GATE_5, -1, -1
6928 Xdoor_6, TRUE, FALSE,
6929 EL_EMC_GATE_6, -1, -1
6932 Xdoor_7, TRUE, FALSE,
6933 EL_EMC_GATE_7, -1, -1
6936 Xdoor_8, TRUE, FALSE,
6937 EL_EMC_GATE_8, -1, -1
6941 Xfake_door_1, TRUE, FALSE,
6942 EL_EM_GATE_1_GRAY, -1, -1
6945 Xfake_door_2, TRUE, FALSE,
6946 EL_EM_GATE_2_GRAY, -1, -1
6949 Xfake_door_3, TRUE, FALSE,
6950 EL_EM_GATE_3_GRAY, -1, -1
6953 Xfake_door_4, TRUE, FALSE,
6954 EL_EM_GATE_4_GRAY, -1, -1
6957 Xfake_door_5, TRUE, FALSE,
6958 EL_EMC_GATE_5_GRAY, -1, -1
6961 Xfake_door_6, TRUE, FALSE,
6962 EL_EMC_GATE_6_GRAY, -1, -1
6965 Xfake_door_7, TRUE, FALSE,
6966 EL_EMC_GATE_7_GRAY, -1, -1
6969 Xfake_door_8, TRUE, FALSE,
6970 EL_EMC_GATE_8_GRAY, -1, -1
6974 Xballoon, TRUE, FALSE,
6978 Yballoon_n, FALSE, FALSE,
6979 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6982 Yballoon_nB, FALSE, TRUE,
6983 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6986 Yballoon_e, FALSE, FALSE,
6987 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6990 Yballoon_eB, FALSE, TRUE,
6991 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6994 Yballoon_s, FALSE, FALSE,
6995 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6998 Yballoon_sB, FALSE, TRUE,
6999 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7002 Yballoon_w, FALSE, FALSE,
7003 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7006 Yballoon_wB, FALSE, TRUE,
7007 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7011 Xball_1, TRUE, FALSE,
7012 EL_EMC_MAGIC_BALL, -1, -1
7015 Yball_1, FALSE, FALSE,
7016 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7019 Xball_2, FALSE, FALSE,
7020 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7023 Yball_2, FALSE, FALSE,
7024 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7027 Yball_blank, FALSE, FALSE,
7028 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7032 Xamoeba_1, TRUE, FALSE,
7033 EL_AMOEBA_DRY, ACTION_OTHER, -1
7036 Xamoeba_2, FALSE, FALSE,
7037 EL_AMOEBA_DRY, ACTION_OTHER, -1
7040 Xamoeba_3, FALSE, FALSE,
7041 EL_AMOEBA_DRY, ACTION_OTHER, -1
7044 Xamoeba_4, FALSE, FALSE,
7045 EL_AMOEBA_DRY, ACTION_OTHER, -1
7048 Xamoeba_5, TRUE, FALSE,
7049 EL_AMOEBA_WET, ACTION_OTHER, -1
7052 Xamoeba_6, FALSE, FALSE,
7053 EL_AMOEBA_WET, ACTION_OTHER, -1
7056 Xamoeba_7, FALSE, FALSE,
7057 EL_AMOEBA_WET, ACTION_OTHER, -1
7060 Xamoeba_8, FALSE, FALSE,
7061 EL_AMOEBA_WET, ACTION_OTHER, -1
7066 EL_AMOEBA_DROP, ACTION_GROWING, -1
7069 Xdrip_fall, FALSE, FALSE,
7070 EL_AMOEBA_DROP, -1, -1
7073 Xdrip_stretch, FALSE, FALSE,
7074 EL_AMOEBA_DROP, ACTION_FALLING, -1
7077 Xdrip_stretchB, FALSE, TRUE,
7078 EL_AMOEBA_DROP, ACTION_FALLING, -1
7081 Ydrip_1_s, FALSE, FALSE,
7082 EL_AMOEBA_DROP, ACTION_FALLING, -1
7085 Ydrip_1_sB, FALSE, TRUE,
7086 EL_AMOEBA_DROP, ACTION_FALLING, -1
7089 Ydrip_2_s, FALSE, FALSE,
7090 EL_AMOEBA_DROP, ACTION_FALLING, -1
7093 Ydrip_2_sB, FALSE, TRUE,
7094 EL_AMOEBA_DROP, ACTION_FALLING, -1
7098 Xwonderwall, TRUE, FALSE,
7099 EL_MAGIC_WALL, -1, -1
7102 Ywonderwall, FALSE, FALSE,
7103 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7107 Xwheel, TRUE, FALSE,
7108 EL_ROBOT_WHEEL, -1, -1
7111 Ywheel, FALSE, FALSE,
7112 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7116 Xswitch, TRUE, FALSE,
7117 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7120 Yswitch, FALSE, FALSE,
7121 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7125 Xbumper, TRUE, FALSE,
7126 EL_EMC_SPRING_BUMPER, -1, -1
7129 Ybumper, FALSE, FALSE,
7130 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7134 Xacid_nw, TRUE, FALSE,
7135 EL_ACID_POOL_TOPLEFT, -1, -1
7138 Xacid_ne, TRUE, FALSE,
7139 EL_ACID_POOL_TOPRIGHT, -1, -1
7142 Xacid_sw, TRUE, FALSE,
7143 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7146 Xacid_s, TRUE, FALSE,
7147 EL_ACID_POOL_BOTTOM, -1, -1
7150 Xacid_se, TRUE, FALSE,
7151 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7155 Xfake_blank, TRUE, FALSE,
7156 EL_INVISIBLE_WALL, -1, -1
7159 Yfake_blank, FALSE, FALSE,
7160 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7164 Xfake_grass, TRUE, FALSE,
7165 EL_EMC_FAKE_GRASS, -1, -1
7168 Yfake_grass, FALSE, FALSE,
7169 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7173 Xfake_amoeba, TRUE, FALSE,
7174 EL_EMC_DRIPPER, -1, -1
7177 Yfake_amoeba, FALSE, FALSE,
7178 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7182 Xlenses, TRUE, FALSE,
7183 EL_EMC_LENSES, -1, -1
7187 Xmagnify, TRUE, FALSE,
7188 EL_EMC_MAGNIFIER, -1, -1
7193 EL_QUICKSAND_EMPTY, -1, -1
7196 Xsand_stone, TRUE, FALSE,
7197 EL_QUICKSAND_FULL, -1, -1
7200 Xsand_stonein_1, FALSE, TRUE,
7201 EL_ROCK, ACTION_FILLING, -1
7204 Xsand_stonein_2, FALSE, TRUE,
7205 EL_ROCK, ACTION_FILLING, -1
7208 Xsand_stonein_3, FALSE, TRUE,
7209 EL_ROCK, ACTION_FILLING, -1
7212 Xsand_stonein_4, FALSE, TRUE,
7213 EL_ROCK, ACTION_FILLING, -1
7216 Xsand_sandstone_1, FALSE, FALSE,
7217 EL_QUICKSAND_FILLING, -1, -1
7220 Xsand_sandstone_2, FALSE, FALSE,
7221 EL_QUICKSAND_FILLING, -1, -1
7224 Xsand_sandstone_3, FALSE, FALSE,
7225 EL_QUICKSAND_FILLING, -1, -1
7228 Xsand_sandstone_4, FALSE, FALSE,
7229 EL_QUICKSAND_FILLING, -1, -1
7232 Xsand_stonesand_1, FALSE, FALSE,
7233 EL_QUICKSAND_EMPTYING, -1, -1
7236 Xsand_stonesand_2, FALSE, FALSE,
7237 EL_QUICKSAND_EMPTYING, -1, -1
7240 Xsand_stonesand_3, FALSE, FALSE,
7241 EL_QUICKSAND_EMPTYING, -1, -1
7244 Xsand_stonesand_4, FALSE, FALSE,
7245 EL_QUICKSAND_EMPTYING, -1, -1
7248 Xsand_stoneout_1, FALSE, FALSE,
7249 EL_ROCK, ACTION_EMPTYING, -1
7252 Xsand_stoneout_2, FALSE, FALSE,
7253 EL_ROCK, ACTION_EMPTYING, -1
7256 Xsand_stonesand_quickout_1, FALSE, FALSE,
7257 EL_QUICKSAND_EMPTYING, -1, -1
7260 Xsand_stonesand_quickout_2, FALSE, FALSE,
7261 EL_QUICKSAND_EMPTYING, -1, -1
7265 Xslide_ns, TRUE, FALSE,
7266 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7269 Yslide_ns_blank, FALSE, FALSE,
7270 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7273 Xslide_ew, TRUE, FALSE,
7274 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7277 Yslide_ew_blank, FALSE, FALSE,
7278 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7282 Xwind_n, TRUE, FALSE,
7283 EL_BALLOON_SWITCH_UP, -1, -1
7286 Xwind_e, TRUE, FALSE,
7287 EL_BALLOON_SWITCH_RIGHT, -1, -1
7290 Xwind_s, TRUE, FALSE,
7291 EL_BALLOON_SWITCH_DOWN, -1, -1
7294 Xwind_w, TRUE, FALSE,
7295 EL_BALLOON_SWITCH_LEFT, -1, -1
7298 Xwind_any, TRUE, FALSE,
7299 EL_BALLOON_SWITCH_ANY, -1, -1
7302 Xwind_stop, TRUE, FALSE,
7303 EL_BALLOON_SWITCH_NONE, -1, -1
7308 EL_EM_EXIT_CLOSED, -1, -1
7311 Xexit_1, TRUE, FALSE,
7312 EL_EM_EXIT_OPEN, -1, -1
7315 Xexit_2, FALSE, FALSE,
7316 EL_EM_EXIT_OPEN, -1, -1
7319 Xexit_3, FALSE, FALSE,
7320 EL_EM_EXIT_OPEN, -1, -1
7324 Xpause, FALSE, FALSE,
7329 Xwall_1, TRUE, FALSE,
7333 Xwall_2, TRUE, FALSE,
7334 EL_EMC_WALL_14, -1, -1
7337 Xwall_3, TRUE, FALSE,
7338 EL_EMC_WALL_15, -1, -1
7341 Xwall_4, TRUE, FALSE,
7342 EL_EMC_WALL_16, -1, -1
7346 Xroundwall_1, TRUE, FALSE,
7347 EL_WALL_SLIPPERY, -1, -1
7350 Xroundwall_2, TRUE, FALSE,
7351 EL_EMC_WALL_SLIPPERY_2, -1, -1
7354 Xroundwall_3, TRUE, FALSE,
7355 EL_EMC_WALL_SLIPPERY_3, -1, -1
7358 Xroundwall_4, TRUE, FALSE,
7359 EL_EMC_WALL_SLIPPERY_4, -1, -1
7363 Xsteel_1, TRUE, FALSE,
7364 EL_STEELWALL, -1, -1
7367 Xsteel_2, TRUE, FALSE,
7368 EL_EMC_STEELWALL_2, -1, -1
7371 Xsteel_3, TRUE, FALSE,
7372 EL_EMC_STEELWALL_3, -1, -1
7375 Xsteel_4, TRUE, FALSE,
7376 EL_EMC_STEELWALL_4, -1, -1
7380 Xdecor_1, TRUE, FALSE,
7381 EL_EMC_WALL_8, -1, -1
7384 Xdecor_2, TRUE, FALSE,
7385 EL_EMC_WALL_6, -1, -1
7388 Xdecor_3, TRUE, FALSE,
7389 EL_EMC_WALL_4, -1, -1
7392 Xdecor_4, TRUE, FALSE,
7393 EL_EMC_WALL_7, -1, -1
7396 Xdecor_5, TRUE, FALSE,
7397 EL_EMC_WALL_5, -1, -1
7400 Xdecor_6, TRUE, FALSE,
7401 EL_EMC_WALL_9, -1, -1
7404 Xdecor_7, TRUE, FALSE,
7405 EL_EMC_WALL_10, -1, -1
7408 Xdecor_8, TRUE, FALSE,
7409 EL_EMC_WALL_1, -1, -1
7412 Xdecor_9, TRUE, FALSE,
7413 EL_EMC_WALL_2, -1, -1
7416 Xdecor_10, TRUE, FALSE,
7417 EL_EMC_WALL_3, -1, -1
7420 Xdecor_11, TRUE, FALSE,
7421 EL_EMC_WALL_11, -1, -1
7424 Xdecor_12, TRUE, FALSE,
7425 EL_EMC_WALL_12, -1, -1
7429 Xalpha_0, TRUE, FALSE,
7430 EL_CHAR('0'), -1, -1
7433 Xalpha_1, TRUE, FALSE,
7434 EL_CHAR('1'), -1, -1
7437 Xalpha_2, TRUE, FALSE,
7438 EL_CHAR('2'), -1, -1
7441 Xalpha_3, TRUE, FALSE,
7442 EL_CHAR('3'), -1, -1
7445 Xalpha_4, TRUE, FALSE,
7446 EL_CHAR('4'), -1, -1
7449 Xalpha_5, TRUE, FALSE,
7450 EL_CHAR('5'), -1, -1
7453 Xalpha_6, TRUE, FALSE,
7454 EL_CHAR('6'), -1, -1
7457 Xalpha_7, TRUE, FALSE,
7458 EL_CHAR('7'), -1, -1
7461 Xalpha_8, TRUE, FALSE,
7462 EL_CHAR('8'), -1, -1
7465 Xalpha_9, TRUE, FALSE,
7466 EL_CHAR('9'), -1, -1
7469 Xalpha_excla, TRUE, FALSE,
7470 EL_CHAR('!'), -1, -1
7473 Xalpha_apost, TRUE, FALSE,
7474 EL_CHAR('\''), -1, -1
7477 Xalpha_comma, TRUE, FALSE,
7478 EL_CHAR(','), -1, -1
7481 Xalpha_minus, TRUE, FALSE,
7482 EL_CHAR('-'), -1, -1
7485 Xalpha_perio, TRUE, FALSE,
7486 EL_CHAR('.'), -1, -1
7489 Xalpha_colon, TRUE, FALSE,
7490 EL_CHAR(':'), -1, -1
7493 Xalpha_quest, TRUE, FALSE,
7494 EL_CHAR('?'), -1, -1
7497 Xalpha_a, TRUE, FALSE,
7498 EL_CHAR('A'), -1, -1
7501 Xalpha_b, TRUE, FALSE,
7502 EL_CHAR('B'), -1, -1
7505 Xalpha_c, TRUE, FALSE,
7506 EL_CHAR('C'), -1, -1
7509 Xalpha_d, TRUE, FALSE,
7510 EL_CHAR('D'), -1, -1
7513 Xalpha_e, TRUE, FALSE,
7514 EL_CHAR('E'), -1, -1
7517 Xalpha_f, TRUE, FALSE,
7518 EL_CHAR('F'), -1, -1
7521 Xalpha_g, TRUE, FALSE,
7522 EL_CHAR('G'), -1, -1
7525 Xalpha_h, TRUE, FALSE,
7526 EL_CHAR('H'), -1, -1
7529 Xalpha_i, TRUE, FALSE,
7530 EL_CHAR('I'), -1, -1
7533 Xalpha_j, TRUE, FALSE,
7534 EL_CHAR('J'), -1, -1
7537 Xalpha_k, TRUE, FALSE,
7538 EL_CHAR('K'), -1, -1
7541 Xalpha_l, TRUE, FALSE,
7542 EL_CHAR('L'), -1, -1
7545 Xalpha_m, TRUE, FALSE,
7546 EL_CHAR('M'), -1, -1
7549 Xalpha_n, TRUE, FALSE,
7550 EL_CHAR('N'), -1, -1
7553 Xalpha_o, TRUE, FALSE,
7554 EL_CHAR('O'), -1, -1
7557 Xalpha_p, TRUE, FALSE,
7558 EL_CHAR('P'), -1, -1
7561 Xalpha_q, TRUE, FALSE,
7562 EL_CHAR('Q'), -1, -1
7565 Xalpha_r, TRUE, FALSE,
7566 EL_CHAR('R'), -1, -1
7569 Xalpha_s, TRUE, FALSE,
7570 EL_CHAR('S'), -1, -1
7573 Xalpha_t, TRUE, FALSE,
7574 EL_CHAR('T'), -1, -1
7577 Xalpha_u, TRUE, FALSE,
7578 EL_CHAR('U'), -1, -1
7581 Xalpha_v, TRUE, FALSE,
7582 EL_CHAR('V'), -1, -1
7585 Xalpha_w, TRUE, FALSE,
7586 EL_CHAR('W'), -1, -1
7589 Xalpha_x, TRUE, FALSE,
7590 EL_CHAR('X'), -1, -1
7593 Xalpha_y, TRUE, FALSE,
7594 EL_CHAR('Y'), -1, -1
7597 Xalpha_z, TRUE, FALSE,
7598 EL_CHAR('Z'), -1, -1
7601 Xalpha_arrow_e, TRUE, FALSE,
7602 EL_CHAR('>'), -1, -1
7605 Xalpha_arrow_w, TRUE, FALSE,
7606 EL_CHAR('<'), -1, -1
7609 Xalpha_copyr, TRUE, FALSE,
7610 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7614 Ykey_1_blank, FALSE, FALSE,
7615 EL_EM_KEY_1, ACTION_COLLECTING, -1
7618 Ykey_2_blank, FALSE, FALSE,
7619 EL_EM_KEY_2, ACTION_COLLECTING, -1
7622 Ykey_3_blank, FALSE, FALSE,
7623 EL_EM_KEY_3, ACTION_COLLECTING, -1
7626 Ykey_4_blank, FALSE, FALSE,
7627 EL_EM_KEY_4, ACTION_COLLECTING, -1
7630 Ykey_5_blank, FALSE, FALSE,
7631 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7634 Ykey_6_blank, FALSE, FALSE,
7635 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7638 Ykey_7_blank, FALSE, FALSE,
7639 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7642 Ykey_8_blank, FALSE, FALSE,
7643 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7646 Ylenses_blank, FALSE, FALSE,
7647 EL_EMC_LENSES, ACTION_COLLECTING, -1
7650 Ymagnify_blank, FALSE, FALSE,
7651 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7654 Ygrass_blank, FALSE, FALSE,
7655 EL_EMC_GRASS, ACTION_SNAPPING, -1
7658 Ydirt_blank, FALSE, FALSE,
7659 EL_SAND, ACTION_SNAPPING, -1
7668 static struct Mapping_EM_to_RND_player
7677 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7681 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7685 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7689 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7693 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7697 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7701 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7705 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7709 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7713 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7717 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7721 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7725 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7729 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7733 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7737 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7741 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7745 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7749 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7753 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7757 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7761 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7765 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7769 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7773 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7777 EL_PLAYER_1, ACTION_DEFAULT, -1,
7781 EL_PLAYER_2, ACTION_DEFAULT, -1,
7785 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7789 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7793 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7797 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7801 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7805 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7809 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7813 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7817 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7821 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7825 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7829 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7833 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7837 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7841 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7845 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7849 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7853 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7857 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7861 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7865 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7869 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7873 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7877 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7881 EL_PLAYER_3, ACTION_DEFAULT, -1,
7885 EL_PLAYER_4, ACTION_DEFAULT, -1,
7894 int map_element_RND_to_EM_cave(int element_rnd)
7896 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7897 static boolean mapping_initialized = FALSE;
7899 if (!mapping_initialized)
7903 // return "Xalpha_quest" for all undefined elements in mapping array
7904 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7905 mapping_RND_to_EM[i] = Xalpha_quest;
7907 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7908 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7909 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7910 em_object_mapping_list[i].element_em;
7912 mapping_initialized = TRUE;
7915 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7917 Warn("invalid RND level element %d", element_rnd);
7922 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7925 int map_element_EM_to_RND_cave(int element_em_cave)
7927 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7928 static boolean mapping_initialized = FALSE;
7930 if (!mapping_initialized)
7934 // return "EL_UNKNOWN" for all undefined elements in mapping array
7935 for (i = 0; i < GAME_TILE_MAX; i++)
7936 mapping_EM_to_RND[i] = EL_UNKNOWN;
7938 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7939 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7940 em_object_mapping_list[i].element_rnd;
7942 mapping_initialized = TRUE;
7945 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7947 Warn("invalid EM cave element %d", element_em_cave);
7952 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7955 int map_element_EM_to_RND_game(int element_em_game)
7957 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7958 static boolean mapping_initialized = FALSE;
7960 if (!mapping_initialized)
7964 // return "EL_UNKNOWN" for all undefined elements in mapping array
7965 for (i = 0; i < GAME_TILE_MAX; i++)
7966 mapping_EM_to_RND[i] = EL_UNKNOWN;
7968 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7969 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7970 em_object_mapping_list[i].element_rnd;
7972 mapping_initialized = TRUE;
7975 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7977 Warn("invalid EM game element %d", element_em_game);
7982 return mapping_EM_to_RND[element_em_game];
7985 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7987 struct LevelInfo_EM *level_em = level->native_em_level;
7988 struct CAVE *cav = level_em->cav;
7991 for (i = 0; i < GAME_TILE_MAX; i++)
7992 cav->android_array[i] = Cblank;
7994 for (i = 0; i < level->num_android_clone_elements; i++)
7996 int element_rnd = level->android_clone_element[i];
7997 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7999 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8000 if (em_object_mapping_list[j].element_rnd == element_rnd)
8001 cav->android_array[em_object_mapping_list[j].element_em] =
8006 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8008 struct LevelInfo_EM *level_em = level->native_em_level;
8009 struct CAVE *cav = level_em->cav;
8012 level->num_android_clone_elements = 0;
8014 for (i = 0; i < GAME_TILE_MAX; i++)
8016 int element_em_cave = cav->android_array[i];
8018 boolean element_found = FALSE;
8020 if (element_em_cave == Cblank)
8023 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8025 for (j = 0; j < level->num_android_clone_elements; j++)
8026 if (level->android_clone_element[j] == element_rnd)
8027 element_found = TRUE;
8031 level->android_clone_element[level->num_android_clone_elements++] =
8034 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8039 if (level->num_android_clone_elements == 0)
8041 level->num_android_clone_elements = 1;
8042 level->android_clone_element[0] = EL_EMPTY;
8046 int map_direction_RND_to_EM(int direction)
8048 return (direction == MV_UP ? 0 :
8049 direction == MV_RIGHT ? 1 :
8050 direction == MV_DOWN ? 2 :
8051 direction == MV_LEFT ? 3 :
8055 int map_direction_EM_to_RND(int direction)
8057 return (direction == 0 ? MV_UP :
8058 direction == 1 ? MV_RIGHT :
8059 direction == 2 ? MV_DOWN :
8060 direction == 3 ? MV_LEFT :
8064 int map_element_RND_to_SP(int element_rnd)
8066 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8068 if (element_rnd >= EL_SP_START &&
8069 element_rnd <= EL_SP_END)
8070 element_sp = element_rnd - EL_SP_START;
8071 else if (element_rnd == EL_EMPTY_SPACE)
8073 else if (element_rnd == EL_INVISIBLE_WALL)
8079 int map_element_SP_to_RND(int element_sp)
8081 int element_rnd = EL_UNKNOWN;
8083 if (element_sp >= 0x00 &&
8085 element_rnd = EL_SP_START + element_sp;
8086 else if (element_sp == 0x28)
8087 element_rnd = EL_INVISIBLE_WALL;
8092 int map_action_SP_to_RND(int action_sp)
8096 case actActive: return ACTION_ACTIVE;
8097 case actImpact: return ACTION_IMPACT;
8098 case actExploding: return ACTION_EXPLODING;
8099 case actDigging: return ACTION_DIGGING;
8100 case actSnapping: return ACTION_SNAPPING;
8101 case actCollecting: return ACTION_COLLECTING;
8102 case actPassing: return ACTION_PASSING;
8103 case actPushing: return ACTION_PUSHING;
8104 case actDropping: return ACTION_DROPPING;
8106 default: return ACTION_DEFAULT;
8110 int map_element_RND_to_MM(int element_rnd)
8112 return (element_rnd >= EL_MM_START_1 &&
8113 element_rnd <= EL_MM_END_1 ?
8114 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8116 element_rnd >= EL_MM_START_2 &&
8117 element_rnd <= EL_MM_END_2 ?
8118 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8120 element_rnd >= EL_CHAR_START &&
8121 element_rnd <= EL_CHAR_END ?
8122 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8124 element_rnd >= EL_MM_RUNTIME_START &&
8125 element_rnd <= EL_MM_RUNTIME_END ?
8126 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8128 element_rnd >= EL_MM_DUMMY_START &&
8129 element_rnd <= EL_MM_DUMMY_END ?
8130 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8132 EL_MM_EMPTY_NATIVE);
8135 int map_element_MM_to_RND(int element_mm)
8137 return (element_mm == EL_MM_EMPTY_NATIVE ||
8138 element_mm == EL_DF_EMPTY_NATIVE ?
8141 element_mm >= EL_MM_START_1_NATIVE &&
8142 element_mm <= EL_MM_END_1_NATIVE ?
8143 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8145 element_mm >= EL_MM_START_2_NATIVE &&
8146 element_mm <= EL_MM_END_2_NATIVE ?
8147 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8149 element_mm >= EL_MM_CHAR_START_NATIVE &&
8150 element_mm <= EL_MM_CHAR_END_NATIVE ?
8151 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8153 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8154 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8155 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8157 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8158 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8159 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8164 int map_action_MM_to_RND(int action_mm)
8166 // all MM actions are defined to exactly match their RND counterparts
8170 int map_sound_MM_to_RND(int sound_mm)
8174 case SND_MM_GAME_LEVELTIME_CHARGING:
8175 return SND_GAME_LEVELTIME_CHARGING;
8177 case SND_MM_GAME_HEALTH_CHARGING:
8178 return SND_GAME_HEALTH_CHARGING;
8181 return SND_UNDEFINED;
8185 int map_mm_wall_element(int element)
8187 return (element >= EL_MM_STEEL_WALL_START &&
8188 element <= EL_MM_STEEL_WALL_END ?
8191 element >= EL_MM_WOODEN_WALL_START &&
8192 element <= EL_MM_WOODEN_WALL_END ?
8195 element >= EL_MM_ICE_WALL_START &&
8196 element <= EL_MM_ICE_WALL_END ?
8199 element >= EL_MM_AMOEBA_WALL_START &&
8200 element <= EL_MM_AMOEBA_WALL_END ?
8203 element >= EL_DF_STEEL_WALL_START &&
8204 element <= EL_DF_STEEL_WALL_END ?
8207 element >= EL_DF_WOODEN_WALL_START &&
8208 element <= EL_DF_WOODEN_WALL_END ?
8214 int map_mm_wall_element_editor(int element)
8218 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8219 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8220 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8221 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8222 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8223 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8225 default: return element;
8229 int get_next_element(int element)
8233 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8234 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8235 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8236 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8237 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8238 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8239 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8240 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8241 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8242 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8243 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8245 default: return element;
8249 int el2img_mm(int element_mm)
8251 return el2img(map_element_MM_to_RND(element_mm));
8254 int el_act_dir2img(int element, int action, int direction)
8256 element = GFX_ELEMENT(element);
8257 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8259 // direction_graphic[][] == graphic[] for undefined direction graphics
8260 return element_info[element].direction_graphic[action][direction];
8263 static int el_act_dir2crm(int element, int action, int direction)
8265 element = GFX_ELEMENT(element);
8266 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8268 // direction_graphic[][] == graphic[] for undefined direction graphics
8269 return element_info[element].direction_crumbled[action][direction];
8272 int el_act2img(int element, int action)
8274 element = GFX_ELEMENT(element);
8276 return element_info[element].graphic[action];
8279 int el_act2crm(int element, int action)
8281 element = GFX_ELEMENT(element);
8283 return element_info[element].crumbled[action];
8286 int el_dir2img(int element, int direction)
8288 element = GFX_ELEMENT(element);
8290 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8293 int el2baseimg(int element)
8295 return element_info[element].graphic[ACTION_DEFAULT];
8298 int el2img(int element)
8300 element = GFX_ELEMENT(element);
8302 return element_info[element].graphic[ACTION_DEFAULT];
8305 int el2edimg(int element)
8307 element = GFX_ELEMENT(element);
8309 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8312 int el2preimg(int element)
8314 element = GFX_ELEMENT(element);
8316 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8319 int el2panelimg(int element)
8321 element = GFX_ELEMENT(element);
8323 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8326 int font2baseimg(int font_nr)
8328 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8331 int getBeltNrFromBeltElement(int element)
8333 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8334 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8335 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8338 int getBeltNrFromBeltActiveElement(int element)
8340 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8341 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8342 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8345 int getBeltNrFromBeltSwitchElement(int element)
8347 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8348 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8349 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8352 int getBeltDirNrFromBeltElement(int element)
8354 static int belt_base_element[4] =
8356 EL_CONVEYOR_BELT_1_LEFT,
8357 EL_CONVEYOR_BELT_2_LEFT,
8358 EL_CONVEYOR_BELT_3_LEFT,
8359 EL_CONVEYOR_BELT_4_LEFT
8362 int belt_nr = getBeltNrFromBeltElement(element);
8363 int belt_dir_nr = element - belt_base_element[belt_nr];
8365 return (belt_dir_nr % 3);
8368 int getBeltDirNrFromBeltSwitchElement(int element)
8370 static int belt_base_element[4] =
8372 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8373 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8374 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8375 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8378 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8379 int belt_dir_nr = element - belt_base_element[belt_nr];
8381 return (belt_dir_nr % 3);
8384 int getBeltDirFromBeltElement(int element)
8386 static int belt_move_dir[3] =
8393 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8395 return belt_move_dir[belt_dir_nr];
8398 int getBeltDirFromBeltSwitchElement(int element)
8400 static int belt_move_dir[3] =
8407 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8409 return belt_move_dir[belt_dir_nr];
8412 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8414 static int belt_base_element[4] =
8416 EL_CONVEYOR_BELT_1_LEFT,
8417 EL_CONVEYOR_BELT_2_LEFT,
8418 EL_CONVEYOR_BELT_3_LEFT,
8419 EL_CONVEYOR_BELT_4_LEFT
8422 return belt_base_element[belt_nr] + belt_dir_nr;
8425 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8427 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8429 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8432 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8434 static int belt_base_element[4] =
8436 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8437 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8438 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8439 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8442 return belt_base_element[belt_nr] + belt_dir_nr;
8445 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8447 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8449 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8452 boolean swapTiles_EM(boolean is_pre_emc_cave)
8454 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8457 boolean getTeamMode_EM(void)
8459 return game.team_mode || network_playing;
8462 boolean isActivePlayer_EM(int player_nr)
8464 return stored_player[player_nr].active;
8467 unsigned int InitRND(int seed)
8469 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8470 return InitEngineRandom_EM(seed);
8471 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8472 return InitEngineRandom_SP(seed);
8473 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8474 return InitEngineRandom_MM(seed);
8476 return InitEngineRandom_RND(seed);
8479 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8480 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8482 static int get_effective_element_EM(int tile, int frame_em)
8484 int element = object_mapping[tile].element_rnd;
8485 int action = object_mapping[tile].action;
8486 boolean is_backside = object_mapping[tile].is_backside;
8487 boolean action_removing = (action == ACTION_DIGGING ||
8488 action == ACTION_SNAPPING ||
8489 action == ACTION_COLLECTING);
8497 return (frame_em > 5 ? EL_EMPTY : element);
8503 else // frame_em == 7
8514 case Ydiamond_stone:
8518 case Xdrip_stretchB:
8534 case Ymagnify_blank:
8537 case Xsand_stonein_1:
8538 case Xsand_stonein_2:
8539 case Xsand_stonein_3:
8540 case Xsand_stonein_4:
8544 return (is_backside || action_removing ? EL_EMPTY : element);
8549 static boolean check_linear_animation_EM(int tile)
8553 case Xsand_stonesand_1:
8554 case Xsand_stonesand_quickout_1:
8555 case Xsand_sandstone_1:
8556 case Xsand_stonein_1:
8557 case Xsand_stoneout_1:
8585 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8586 boolean has_crumbled_graphics,
8587 int crumbled, int sync_frame)
8589 // if element can be crumbled, but certain action graphics are just empty
8590 // space (like instantly snapping sand to empty space in 1 frame), do not
8591 // treat these empty space graphics as crumbled graphics in EMC engine
8592 if (crumbled == IMG_EMPTY_SPACE)
8593 has_crumbled_graphics = FALSE;
8595 if (has_crumbled_graphics)
8597 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8598 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8599 g_crumbled->anim_delay,
8600 g_crumbled->anim_mode,
8601 g_crumbled->anim_start_frame,
8604 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8605 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8607 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8608 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8610 g_em->has_crumbled_graphics = TRUE;
8614 g_em->crumbled_bitmap = NULL;
8615 g_em->crumbled_src_x = 0;
8616 g_em->crumbled_src_y = 0;
8617 g_em->crumbled_border_size = 0;
8618 g_em->crumbled_tile_size = 0;
8620 g_em->has_crumbled_graphics = FALSE;
8625 void ResetGfxAnimation_EM(int x, int y, int tile)
8631 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8632 int tile, int frame_em, int x, int y)
8634 int action = object_mapping[tile].action;
8635 int direction = object_mapping[tile].direction;
8636 int effective_element = get_effective_element_EM(tile, frame_em);
8637 int graphic = (direction == MV_NONE ?
8638 el_act2img(effective_element, action) :
8639 el_act_dir2img(effective_element, action, direction));
8640 struct GraphicInfo *g = &graphic_info[graphic];
8642 boolean action_removing = (action == ACTION_DIGGING ||
8643 action == ACTION_SNAPPING ||
8644 action == ACTION_COLLECTING);
8645 boolean action_moving = (action == ACTION_FALLING ||
8646 action == ACTION_MOVING ||
8647 action == ACTION_PUSHING ||
8648 action == ACTION_EATING ||
8649 action == ACTION_FILLING ||
8650 action == ACTION_EMPTYING);
8651 boolean action_falling = (action == ACTION_FALLING ||
8652 action == ACTION_FILLING ||
8653 action == ACTION_EMPTYING);
8655 // special case: graphic uses "2nd movement tile" and has defined
8656 // 7 frames for movement animation (or less) => use default graphic
8657 // for last (8th) frame which ends the movement animation
8658 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8660 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8661 graphic = (direction == MV_NONE ?
8662 el_act2img(effective_element, action) :
8663 el_act_dir2img(effective_element, action, direction));
8665 g = &graphic_info[graphic];
8668 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8672 else if (action_moving)
8674 boolean is_backside = object_mapping[tile].is_backside;
8678 int direction = object_mapping[tile].direction;
8679 int move_dir = (action_falling ? MV_DOWN : direction);
8684 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8685 if (g->double_movement && frame_em == 0)
8689 if (move_dir == MV_LEFT)
8690 GfxFrame[x - 1][y] = GfxFrame[x][y];
8691 else if (move_dir == MV_RIGHT)
8692 GfxFrame[x + 1][y] = GfxFrame[x][y];
8693 else if (move_dir == MV_UP)
8694 GfxFrame[x][y - 1] = GfxFrame[x][y];
8695 else if (move_dir == MV_DOWN)
8696 GfxFrame[x][y + 1] = GfxFrame[x][y];
8703 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8704 if (tile == Xsand_stonesand_quickout_1 ||
8705 tile == Xsand_stonesand_quickout_2)
8709 if (graphic_info[graphic].anim_global_sync)
8710 sync_frame = FrameCounter;
8711 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8712 sync_frame = GfxFrame[x][y];
8714 sync_frame = 0; // playfield border (pseudo steel)
8716 SetRandomAnimationValue(x, y);
8718 int frame = getAnimationFrame(g->anim_frames,
8721 g->anim_start_frame,
8724 g_em->unique_identifier =
8725 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8728 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8729 int tile, int frame_em, int x, int y)
8731 int action = object_mapping[tile].action;
8732 int direction = object_mapping[tile].direction;
8733 boolean is_backside = object_mapping[tile].is_backside;
8734 int effective_element = get_effective_element_EM(tile, frame_em);
8735 int effective_action = action;
8736 int graphic = (direction == MV_NONE ?
8737 el_act2img(effective_element, effective_action) :
8738 el_act_dir2img(effective_element, effective_action,
8740 int crumbled = (direction == MV_NONE ?
8741 el_act2crm(effective_element, effective_action) :
8742 el_act_dir2crm(effective_element, effective_action,
8744 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8745 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8746 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8747 struct GraphicInfo *g = &graphic_info[graphic];
8750 // special case: graphic uses "2nd movement tile" and has defined
8751 // 7 frames for movement animation (or less) => use default graphic
8752 // for last (8th) frame which ends the movement animation
8753 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8755 effective_action = ACTION_DEFAULT;
8756 graphic = (direction == MV_NONE ?
8757 el_act2img(effective_element, effective_action) :
8758 el_act_dir2img(effective_element, effective_action,
8760 crumbled = (direction == MV_NONE ?
8761 el_act2crm(effective_element, effective_action) :
8762 el_act_dir2crm(effective_element, effective_action,
8765 g = &graphic_info[graphic];
8768 if (graphic_info[graphic].anim_global_sync)
8769 sync_frame = FrameCounter;
8770 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8771 sync_frame = GfxFrame[x][y];
8773 sync_frame = 0; // playfield border (pseudo steel)
8775 SetRandomAnimationValue(x, y);
8777 int frame = getAnimationFrame(g->anim_frames,
8780 g->anim_start_frame,
8783 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8784 g->double_movement && is_backside);
8786 // (updating the "crumbled" graphic definitions is probably not really needed,
8787 // as animations for crumbled graphics can't be longer than one EMC cycle)
8788 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8792 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8793 int player_nr, int anim, int frame_em)
8795 int element = player_mapping[player_nr][anim].element_rnd;
8796 int action = player_mapping[player_nr][anim].action;
8797 int direction = player_mapping[player_nr][anim].direction;
8798 int graphic = (direction == MV_NONE ?
8799 el_act2img(element, action) :
8800 el_act_dir2img(element, action, direction));
8801 struct GraphicInfo *g = &graphic_info[graphic];
8804 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8806 stored_player[player_nr].StepFrame = frame_em;
8808 sync_frame = stored_player[player_nr].Frame;
8810 int frame = getAnimationFrame(g->anim_frames,
8813 g->anim_start_frame,
8816 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8817 &g_em->src_x, &g_em->src_y, FALSE);
8820 void InitGraphicInfo_EM(void)
8824 // always start with reliable default values
8825 for (i = 0; i < GAME_TILE_MAX; i++)
8827 object_mapping[i].element_rnd = EL_UNKNOWN;
8828 object_mapping[i].is_backside = FALSE;
8829 object_mapping[i].action = ACTION_DEFAULT;
8830 object_mapping[i].direction = MV_NONE;
8833 // always start with reliable default values
8834 for (p = 0; p < MAX_PLAYERS; p++)
8836 for (i = 0; i < PLY_MAX; i++)
8838 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8839 player_mapping[p][i].action = ACTION_DEFAULT;
8840 player_mapping[p][i].direction = MV_NONE;
8844 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8846 int e = em_object_mapping_list[i].element_em;
8848 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8849 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8851 if (em_object_mapping_list[i].action != -1)
8852 object_mapping[e].action = em_object_mapping_list[i].action;
8854 if (em_object_mapping_list[i].direction != -1)
8855 object_mapping[e].direction =
8856 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8859 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8861 int a = em_player_mapping_list[i].action_em;
8862 int p = em_player_mapping_list[i].player_nr;
8864 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8866 if (em_player_mapping_list[i].action != -1)
8867 player_mapping[p][a].action = em_player_mapping_list[i].action;
8869 if (em_player_mapping_list[i].direction != -1)
8870 player_mapping[p][a].direction =
8871 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8874 for (i = 0; i < GAME_TILE_MAX; i++)
8876 int element = object_mapping[i].element_rnd;
8877 int action = object_mapping[i].action;
8878 int direction = object_mapping[i].direction;
8879 boolean is_backside = object_mapping[i].is_backside;
8880 boolean action_exploding = ((action == ACTION_EXPLODING ||
8881 action == ACTION_SMASHED_BY_ROCK ||
8882 action == ACTION_SMASHED_BY_SPRING) &&
8883 element != EL_DIAMOND);
8884 boolean action_active = (action == ACTION_ACTIVE);
8885 boolean action_other = (action == ACTION_OTHER);
8887 for (j = 0; j < 8; j++)
8889 int effective_element = get_effective_element_EM(i, j);
8890 int effective_action = (j < 7 ? action :
8891 i == Xdrip_stretch ? action :
8892 i == Xdrip_stretchB ? action :
8893 i == Ydrip_1_s ? action :
8894 i == Ydrip_1_sB ? action :
8895 i == Yball_1 ? action :
8896 i == Xball_2 ? action :
8897 i == Yball_2 ? action :
8898 i == Yball_blank ? action :
8899 i == Ykey_1_blank ? action :
8900 i == Ykey_2_blank ? action :
8901 i == Ykey_3_blank ? action :
8902 i == Ykey_4_blank ? action :
8903 i == Ykey_5_blank ? action :
8904 i == Ykey_6_blank ? action :
8905 i == Ykey_7_blank ? action :
8906 i == Ykey_8_blank ? action :
8907 i == Ylenses_blank ? action :
8908 i == Ymagnify_blank ? action :
8909 i == Ygrass_blank ? action :
8910 i == Ydirt_blank ? action :
8911 i == Xsand_stonein_1 ? action :
8912 i == Xsand_stonein_2 ? action :
8913 i == Xsand_stonein_3 ? action :
8914 i == Xsand_stonein_4 ? action :
8915 i == Xsand_stoneout_1 ? action :
8916 i == Xsand_stoneout_2 ? action :
8917 i == Xboom_android ? ACTION_EXPLODING :
8918 action_exploding ? ACTION_EXPLODING :
8919 action_active ? action :
8920 action_other ? action :
8922 int graphic = (el_act_dir2img(effective_element, effective_action,
8924 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8926 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8927 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8928 boolean has_action_graphics = (graphic != base_graphic);
8929 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8930 struct GraphicInfo *g = &graphic_info[graphic];
8931 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8934 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8935 boolean special_animation = (action != ACTION_DEFAULT &&
8936 g->anim_frames == 3 &&
8937 g->anim_delay == 2 &&
8938 g->anim_mode & ANIM_LINEAR);
8939 int sync_frame = (i == Xdrip_stretch ? 7 :
8940 i == Xdrip_stretchB ? 7 :
8941 i == Ydrip_2_s ? j + 8 :
8942 i == Ydrip_2_sB ? j + 8 :
8951 i == Xfake_acid_1 ? 0 :
8952 i == Xfake_acid_2 ? 10 :
8953 i == Xfake_acid_3 ? 20 :
8954 i == Xfake_acid_4 ? 30 :
8955 i == Xfake_acid_5 ? 40 :
8956 i == Xfake_acid_6 ? 50 :
8957 i == Xfake_acid_7 ? 60 :
8958 i == Xfake_acid_8 ? 70 :
8959 i == Xfake_acid_1_player ? 0 :
8960 i == Xfake_acid_2_player ? 10 :
8961 i == Xfake_acid_3_player ? 20 :
8962 i == Xfake_acid_4_player ? 30 :
8963 i == Xfake_acid_5_player ? 40 :
8964 i == Xfake_acid_6_player ? 50 :
8965 i == Xfake_acid_7_player ? 60 :
8966 i == Xfake_acid_8_player ? 70 :
8968 i == Yball_2 ? j + 8 :
8969 i == Yball_blank ? j + 1 :
8970 i == Ykey_1_blank ? j + 1 :
8971 i == Ykey_2_blank ? j + 1 :
8972 i == Ykey_3_blank ? j + 1 :
8973 i == Ykey_4_blank ? j + 1 :
8974 i == Ykey_5_blank ? j + 1 :
8975 i == Ykey_6_blank ? j + 1 :
8976 i == Ykey_7_blank ? j + 1 :
8977 i == Ykey_8_blank ? j + 1 :
8978 i == Ylenses_blank ? j + 1 :
8979 i == Ymagnify_blank ? j + 1 :
8980 i == Ygrass_blank ? j + 1 :
8981 i == Ydirt_blank ? j + 1 :
8982 i == Xamoeba_1 ? 0 :
8983 i == Xamoeba_2 ? 1 :
8984 i == Xamoeba_3 ? 2 :
8985 i == Xamoeba_4 ? 3 :
8986 i == Xamoeba_5 ? 0 :
8987 i == Xamoeba_6 ? 1 :
8988 i == Xamoeba_7 ? 2 :
8989 i == Xamoeba_8 ? 3 :
8990 i == Xexit_2 ? j + 8 :
8991 i == Xexit_3 ? j + 16 :
8992 i == Xdynamite_1 ? 0 :
8993 i == Xdynamite_2 ? 8 :
8994 i == Xdynamite_3 ? 16 :
8995 i == Xdynamite_4 ? 24 :
8996 i == Xsand_stonein_1 ? j + 1 :
8997 i == Xsand_stonein_2 ? j + 9 :
8998 i == Xsand_stonein_3 ? j + 17 :
8999 i == Xsand_stonein_4 ? j + 25 :
9000 i == Xsand_stoneout_1 && j == 0 ? 0 :
9001 i == Xsand_stoneout_1 && j == 1 ? 0 :
9002 i == Xsand_stoneout_1 && j == 2 ? 1 :
9003 i == Xsand_stoneout_1 && j == 3 ? 2 :
9004 i == Xsand_stoneout_1 && j == 4 ? 2 :
9005 i == Xsand_stoneout_1 && j == 5 ? 3 :
9006 i == Xsand_stoneout_1 && j == 6 ? 4 :
9007 i == Xsand_stoneout_1 && j == 7 ? 4 :
9008 i == Xsand_stoneout_2 && j == 0 ? 5 :
9009 i == Xsand_stoneout_2 && j == 1 ? 6 :
9010 i == Xsand_stoneout_2 && j == 2 ? 7 :
9011 i == Xsand_stoneout_2 && j == 3 ? 8 :
9012 i == Xsand_stoneout_2 && j == 4 ? 9 :
9013 i == Xsand_stoneout_2 && j == 5 ? 11 :
9014 i == Xsand_stoneout_2 && j == 6 ? 13 :
9015 i == Xsand_stoneout_2 && j == 7 ? 15 :
9016 i == Xboom_bug && j == 1 ? 2 :
9017 i == Xboom_bug && j == 2 ? 2 :
9018 i == Xboom_bug && j == 3 ? 4 :
9019 i == Xboom_bug && j == 4 ? 4 :
9020 i == Xboom_bug && j == 5 ? 2 :
9021 i == Xboom_bug && j == 6 ? 2 :
9022 i == Xboom_bug && j == 7 ? 0 :
9023 i == Xboom_tank && j == 1 ? 2 :
9024 i == Xboom_tank && j == 2 ? 2 :
9025 i == Xboom_tank && j == 3 ? 4 :
9026 i == Xboom_tank && j == 4 ? 4 :
9027 i == Xboom_tank && j == 5 ? 2 :
9028 i == Xboom_tank && j == 6 ? 2 :
9029 i == Xboom_tank && j == 7 ? 0 :
9030 i == Xboom_android && j == 7 ? 6 :
9031 i == Xboom_1 && j == 1 ? 2 :
9032 i == Xboom_1 && j == 2 ? 2 :
9033 i == Xboom_1 && j == 3 ? 4 :
9034 i == Xboom_1 && j == 4 ? 4 :
9035 i == Xboom_1 && j == 5 ? 6 :
9036 i == Xboom_1 && j == 6 ? 6 :
9037 i == Xboom_1 && j == 7 ? 8 :
9038 i == Xboom_2 && j == 0 ? 8 :
9039 i == Xboom_2 && j == 1 ? 8 :
9040 i == Xboom_2 && j == 2 ? 10 :
9041 i == Xboom_2 && j == 3 ? 10 :
9042 i == Xboom_2 && j == 4 ? 10 :
9043 i == Xboom_2 && j == 5 ? 12 :
9044 i == Xboom_2 && j == 6 ? 12 :
9045 i == Xboom_2 && j == 7 ? 12 :
9046 special_animation && j == 4 ? 3 :
9047 effective_action != action ? 0 :
9049 int frame = getAnimationFrame(g->anim_frames,
9052 g->anim_start_frame,
9055 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9056 g->double_movement && is_backside);
9058 g_em->bitmap = src_bitmap;
9059 g_em->src_x = src_x;
9060 g_em->src_y = src_y;
9061 g_em->src_offset_x = 0;
9062 g_em->src_offset_y = 0;
9063 g_em->dst_offset_x = 0;
9064 g_em->dst_offset_y = 0;
9065 g_em->width = TILEX;
9066 g_em->height = TILEY;
9068 g_em->preserve_background = FALSE;
9070 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9073 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9074 effective_action == ACTION_MOVING ||
9075 effective_action == ACTION_PUSHING ||
9076 effective_action == ACTION_EATING)) ||
9077 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9078 effective_action == ACTION_EMPTYING)))
9081 (effective_action == ACTION_FALLING ||
9082 effective_action == ACTION_FILLING ||
9083 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9084 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9085 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9086 int num_steps = (i == Ydrip_1_s ? 16 :
9087 i == Ydrip_1_sB ? 16 :
9088 i == Ydrip_2_s ? 16 :
9089 i == Ydrip_2_sB ? 16 :
9090 i == Xsand_stonein_1 ? 32 :
9091 i == Xsand_stonein_2 ? 32 :
9092 i == Xsand_stonein_3 ? 32 :
9093 i == Xsand_stonein_4 ? 32 :
9094 i == Xsand_stoneout_1 ? 16 :
9095 i == Xsand_stoneout_2 ? 16 : 8);
9096 int cx = ABS(dx) * (TILEX / num_steps);
9097 int cy = ABS(dy) * (TILEY / num_steps);
9098 int step_frame = (i == Ydrip_2_s ? j + 8 :
9099 i == Ydrip_2_sB ? j + 8 :
9100 i == Xsand_stonein_2 ? j + 8 :
9101 i == Xsand_stonein_3 ? j + 16 :
9102 i == Xsand_stonein_4 ? j + 24 :
9103 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9104 int step = (is_backside ? step_frame : num_steps - step_frame);
9106 if (is_backside) // tile where movement starts
9108 if (dx < 0 || dy < 0)
9110 g_em->src_offset_x = cx * step;
9111 g_em->src_offset_y = cy * step;
9115 g_em->dst_offset_x = cx * step;
9116 g_em->dst_offset_y = cy * step;
9119 else // tile where movement ends
9121 if (dx < 0 || dy < 0)
9123 g_em->dst_offset_x = cx * step;
9124 g_em->dst_offset_y = cy * step;
9128 g_em->src_offset_x = cx * step;
9129 g_em->src_offset_y = cy * step;
9133 g_em->width = TILEX - cx * step;
9134 g_em->height = TILEY - cy * step;
9137 // create unique graphic identifier to decide if tile must be redrawn
9138 /* bit 31 - 16 (16 bit): EM style graphic
9139 bit 15 - 12 ( 4 bit): EM style frame
9140 bit 11 - 6 ( 6 bit): graphic width
9141 bit 5 - 0 ( 6 bit): graphic height */
9142 g_em->unique_identifier =
9143 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9147 for (i = 0; i < GAME_TILE_MAX; i++)
9149 for (j = 0; j < 8; j++)
9151 int element = object_mapping[i].element_rnd;
9152 int action = object_mapping[i].action;
9153 int direction = object_mapping[i].direction;
9154 boolean is_backside = object_mapping[i].is_backside;
9155 int graphic_action = el_act_dir2img(element, action, direction);
9156 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9158 if ((action == ACTION_SMASHED_BY_ROCK ||
9159 action == ACTION_SMASHED_BY_SPRING ||
9160 action == ACTION_EATING) &&
9161 graphic_action == graphic_default)
9163 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9164 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9165 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9166 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9169 // no separate animation for "smashed by rock" -- use rock instead
9170 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9171 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9173 g_em->bitmap = g_xx->bitmap;
9174 g_em->src_x = g_xx->src_x;
9175 g_em->src_y = g_xx->src_y;
9176 g_em->src_offset_x = g_xx->src_offset_x;
9177 g_em->src_offset_y = g_xx->src_offset_y;
9178 g_em->dst_offset_x = g_xx->dst_offset_x;
9179 g_em->dst_offset_y = g_xx->dst_offset_y;
9180 g_em->width = g_xx->width;
9181 g_em->height = g_xx->height;
9182 g_em->unique_identifier = g_xx->unique_identifier;
9185 g_em->preserve_background = TRUE;
9190 for (p = 0; p < MAX_PLAYERS; p++)
9192 for (i = 0; i < PLY_MAX; i++)
9194 int element = player_mapping[p][i].element_rnd;
9195 int action = player_mapping[p][i].action;
9196 int direction = player_mapping[p][i].direction;
9198 for (j = 0; j < 8; j++)
9200 int effective_element = element;
9201 int effective_action = action;
9202 int graphic = (direction == MV_NONE ?
9203 el_act2img(effective_element, effective_action) :
9204 el_act_dir2img(effective_element, effective_action,
9206 struct GraphicInfo *g = &graphic_info[graphic];
9207 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9211 int frame = getAnimationFrame(g->anim_frames,
9214 g->anim_start_frame,
9217 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9219 g_em->bitmap = src_bitmap;
9220 g_em->src_x = src_x;
9221 g_em->src_y = src_y;
9222 g_em->src_offset_x = 0;
9223 g_em->src_offset_y = 0;
9224 g_em->dst_offset_x = 0;
9225 g_em->dst_offset_y = 0;
9226 g_em->width = TILEX;
9227 g_em->height = TILEY;
9233 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9234 boolean any_player_moving,
9235 boolean any_player_snapping,
9236 boolean any_player_dropping)
9238 if (frame == 7 && !any_player_dropping)
9240 if (!local_player->was_waiting)
9242 if (!CheckSaveEngineSnapshotToList())
9245 local_player->was_waiting = TRUE;
9248 else if (any_player_moving || any_player_snapping || any_player_dropping)
9250 local_player->was_waiting = FALSE;
9254 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9255 boolean murphy_is_dropping)
9257 if (murphy_is_waiting)
9259 if (!local_player->was_waiting)
9261 if (!CheckSaveEngineSnapshotToList())
9264 local_player->was_waiting = TRUE;
9269 local_player->was_waiting = FALSE;
9273 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9274 boolean button_released)
9276 if (button_released)
9278 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9279 CheckSaveEngineSnapshotToList();
9281 else if (element_clicked)
9283 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9284 CheckSaveEngineSnapshotToList();
9286 game.snapshot.changed_action = TRUE;
9290 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9291 boolean any_player_moving,
9292 boolean any_player_snapping,
9293 boolean any_player_dropping)
9295 if (tape.single_step && tape.recording && !tape.pausing)
9296 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9297 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9299 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9300 any_player_snapping, any_player_dropping);
9302 return tape.pausing;
9305 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9306 boolean murphy_is_dropping)
9308 boolean murphy_starts_dropping = FALSE;
9311 for (i = 0; i < MAX_PLAYERS; i++)
9312 if (stored_player[i].force_dropping)
9313 murphy_starts_dropping = TRUE;
9315 if (tape.single_step && tape.recording && !tape.pausing)
9316 if (murphy_is_waiting && !murphy_starts_dropping)
9317 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9319 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9322 void CheckSingleStepMode_MM(boolean element_clicked,
9323 boolean button_released)
9325 if (tape.single_step && tape.recording && !tape.pausing)
9326 if (button_released)
9327 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9329 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9332 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9333 int graphic, int sync_frame, int x, int y)
9335 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9337 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9340 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9342 return (IS_NEXT_FRAME(sync_frame, graphic));
9345 int getGraphicInfo_Delay(int graphic)
9347 return graphic_info[graphic].anim_delay;
9350 void PlayMenuSoundExt(int sound)
9352 if (sound == SND_UNDEFINED)
9355 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9356 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9359 if (IS_LOOP_SOUND(sound))
9360 PlaySoundLoop(sound);
9365 void PlayMenuSound(void)
9367 PlayMenuSoundExt(menu.sound[game_status]);
9370 void PlayMenuSoundStereo(int sound, int stereo_position)
9372 if (sound == SND_UNDEFINED)
9375 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9376 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9379 if (IS_LOOP_SOUND(sound))
9380 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9382 PlaySoundStereo(sound, stereo_position);
9385 void PlayMenuSoundIfLoopExt(int sound)
9387 if (sound == SND_UNDEFINED)
9390 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9391 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9394 if (IS_LOOP_SOUND(sound))
9395 PlaySoundLoop(sound);
9398 void PlayMenuSoundIfLoop(void)
9400 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9403 void PlayMenuMusicExt(int music)
9405 if (music == MUS_UNDEFINED)
9408 if (!setup.sound_music)
9411 if (IS_LOOP_MUSIC(music))
9412 PlayMusicLoop(music);
9417 void PlayMenuMusic(void)
9419 char *curr_music = getCurrentlyPlayingMusicFilename();
9420 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9422 if (!strEqual(curr_music, next_music))
9423 PlayMenuMusicExt(menu.music[game_status]);
9426 void PlayMenuSoundsAndMusic(void)
9432 static void FadeMenuSounds(void)
9437 static void FadeMenuMusic(void)
9439 char *curr_music = getCurrentlyPlayingMusicFilename();
9440 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9442 if (!strEqual(curr_music, next_music))
9446 void FadeMenuSoundsAndMusic(void)
9452 void PlaySoundActivating(void)
9455 PlaySound(SND_MENU_ITEM_ACTIVATING);
9459 void PlaySoundSelecting(void)
9462 PlaySound(SND_MENU_ITEM_SELECTING);
9466 void ToggleFullscreenIfNeeded(void)
9468 // if setup and video fullscreen state are already matching, nothing do do
9469 if (setup.fullscreen == video.fullscreen_enabled ||
9470 !video.fullscreen_available)
9473 SDLSetWindowFullscreen(setup.fullscreen);
9475 // set setup value according to successfully changed fullscreen mode
9476 setup.fullscreen = video.fullscreen_enabled;
9479 void ChangeWindowScalingIfNeeded(void)
9481 // if setup and video window scaling are already matching, nothing do do
9482 if (setup.window_scaling_percent == video.window_scaling_percent ||
9483 video.fullscreen_enabled)
9486 SDLSetWindowScaling(setup.window_scaling_percent);
9488 // set setup value according to successfully changed window scaling
9489 setup.window_scaling_percent = video.window_scaling_percent;
9492 void ChangeVsyncModeIfNeeded(void)
9494 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9495 int video_vsync_mode = video.vsync_mode;
9497 // if setup and video vsync mode are already matching, nothing do do
9498 if (setup_vsync_mode == video_vsync_mode)
9501 // if renderer is using OpenGL, vsync mode can directly be changed
9502 SDLSetScreenVsyncMode(setup.vsync_mode);
9504 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9505 if (video.vsync_mode == video_vsync_mode)
9507 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9509 // save backbuffer content which gets lost when re-creating screen
9510 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9512 // force re-creating screen and renderer to set new vsync mode
9513 video.fullscreen_enabled = !setup.fullscreen;
9515 // when creating new renderer, destroy textures linked to old renderer
9516 FreeAllImageTextures(); // needs old renderer to free the textures
9518 // re-create screen and renderer (including change of vsync mode)
9519 ChangeVideoModeIfNeeded(setup.fullscreen);
9521 // set setup value according to successfully changed fullscreen mode
9522 setup.fullscreen = video.fullscreen_enabled;
9524 // restore backbuffer content from temporary backbuffer backup bitmap
9525 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9526 FreeBitmap(tmp_backbuffer);
9528 // update visible window/screen
9529 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9531 // when changing vsync mode, re-create textures for new renderer
9532 InitImageTextures();
9535 // set setup value according to successfully changed vsync mode
9536 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9539 static void JoinRectangles(int *x, int *y, int *width, int *height,
9540 int x2, int y2, int width2, int height2)
9542 // do not join with "off-screen" rectangle
9543 if (x2 == -1 || y2 == -1)
9548 *width = MAX(*width, width2);
9549 *height = MAX(*height, height2);
9552 void SetAnimStatus(int anim_status_new)
9554 if (anim_status_new == GAME_MODE_MAIN)
9555 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9556 else if (anim_status_new == GAME_MODE_NAMES)
9557 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9558 else if (anim_status_new == GAME_MODE_SCORES)
9559 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9561 global.anim_status_next = anim_status_new;
9563 // directly set screen modes that are entered without fading
9564 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9565 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9566 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9567 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9568 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9569 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9570 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9571 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9572 global.anim_status = global.anim_status_next;
9575 void SetGameStatus(int game_status_new)
9577 if (game_status_new != game_status)
9578 game_status_last_screen = game_status;
9580 game_status = game_status_new;
9582 SetAnimStatus(game_status_new);
9585 void SetFontStatus(int game_status_new)
9587 static int last_game_status = -1;
9589 if (game_status_new != -1)
9591 // set game status for font use after storing last game status
9592 last_game_status = game_status;
9593 game_status = game_status_new;
9597 // reset game status after font use from last stored game status
9598 game_status = last_game_status;
9602 void ResetFontStatus(void)
9607 void SetLevelSetInfo(char *identifier, int level_nr)
9609 setString(&levelset.identifier, identifier);
9611 levelset.level_nr = level_nr;
9614 boolean CheckIfAllViewportsHaveChanged(void)
9616 // if game status has not changed, viewports have not changed either
9617 if (game_status == game_status_last)
9620 // check if all viewports have changed with current game status
9622 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9623 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9624 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9625 int new_real_sx = vp_playfield->x;
9626 int new_real_sy = vp_playfield->y;
9627 int new_full_sxsize = vp_playfield->width;
9628 int new_full_sysize = vp_playfield->height;
9629 int new_dx = vp_door_1->x;
9630 int new_dy = vp_door_1->y;
9631 int new_dxsize = vp_door_1->width;
9632 int new_dysize = vp_door_1->height;
9633 int new_vx = vp_door_2->x;
9634 int new_vy = vp_door_2->y;
9635 int new_vxsize = vp_door_2->width;
9636 int new_vysize = vp_door_2->height;
9638 boolean playfield_viewport_has_changed =
9639 (new_real_sx != REAL_SX ||
9640 new_real_sy != REAL_SY ||
9641 new_full_sxsize != FULL_SXSIZE ||
9642 new_full_sysize != FULL_SYSIZE);
9644 boolean door_1_viewport_has_changed =
9647 new_dxsize != DXSIZE ||
9648 new_dysize != DYSIZE);
9650 boolean door_2_viewport_has_changed =
9653 new_vxsize != VXSIZE ||
9654 new_vysize != VYSIZE ||
9655 game_status_last == GAME_MODE_EDITOR);
9657 return (playfield_viewport_has_changed &&
9658 door_1_viewport_has_changed &&
9659 door_2_viewport_has_changed);
9662 boolean CheckFadeAll(void)
9664 return (CheckIfGlobalBorderHasChanged() ||
9665 CheckIfAllViewportsHaveChanged());
9668 void ChangeViewportPropertiesIfNeeded(void)
9670 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9671 FALSE : setup.small_game_graphics);
9672 int gfx_game_mode = game_status;
9673 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9675 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9676 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9677 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9678 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9679 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9680 int new_win_xsize = vp_window->width;
9681 int new_win_ysize = vp_window->height;
9682 int border_left = vp_playfield->border_left;
9683 int border_right = vp_playfield->border_right;
9684 int border_top = vp_playfield->border_top;
9685 int border_bottom = vp_playfield->border_bottom;
9686 int new_sx = vp_playfield->x + border_left;
9687 int new_sy = vp_playfield->y + border_top;
9688 int new_sxsize = vp_playfield->width - border_left - border_right;
9689 int new_sysize = vp_playfield->height - border_top - border_bottom;
9690 int new_real_sx = vp_playfield->x;
9691 int new_real_sy = vp_playfield->y;
9692 int new_full_sxsize = vp_playfield->width;
9693 int new_full_sysize = vp_playfield->height;
9694 int new_dx = vp_door_1->x;
9695 int new_dy = vp_door_1->y;
9696 int new_dxsize = vp_door_1->width;
9697 int new_dysize = vp_door_1->height;
9698 int new_vx = vp_door_2->x;
9699 int new_vy = vp_door_2->y;
9700 int new_vxsize = vp_door_2->width;
9701 int new_vysize = vp_door_2->height;
9702 int new_ex = vp_door_3->x;
9703 int new_ey = vp_door_3->y;
9704 int new_exsize = vp_door_3->width;
9705 int new_eysize = vp_door_3->height;
9706 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9707 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9708 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9709 int new_scr_fieldx = new_sxsize / tilesize;
9710 int new_scr_fieldy = new_sysize / tilesize;
9711 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9712 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9713 boolean init_gfx_buffers = FALSE;
9714 boolean init_video_buffer = FALSE;
9715 boolean init_gadgets_and_anims = FALSE;
9716 boolean init_em_graphics = FALSE;
9718 if (new_win_xsize != WIN_XSIZE ||
9719 new_win_ysize != WIN_YSIZE)
9721 WIN_XSIZE = new_win_xsize;
9722 WIN_YSIZE = new_win_ysize;
9724 init_video_buffer = TRUE;
9725 init_gfx_buffers = TRUE;
9726 init_gadgets_and_anims = TRUE;
9728 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9731 if (new_scr_fieldx != SCR_FIELDX ||
9732 new_scr_fieldy != SCR_FIELDY)
9734 // this always toggles between MAIN and GAME when using small tile size
9736 SCR_FIELDX = new_scr_fieldx;
9737 SCR_FIELDY = new_scr_fieldy;
9739 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9750 new_sxsize != SXSIZE ||
9751 new_sysize != SYSIZE ||
9752 new_dxsize != DXSIZE ||
9753 new_dysize != DYSIZE ||
9754 new_vxsize != VXSIZE ||
9755 new_vysize != VYSIZE ||
9756 new_exsize != EXSIZE ||
9757 new_eysize != EYSIZE ||
9758 new_real_sx != REAL_SX ||
9759 new_real_sy != REAL_SY ||
9760 new_full_sxsize != FULL_SXSIZE ||
9761 new_full_sysize != FULL_SYSIZE ||
9762 new_tilesize_var != TILESIZE_VAR
9765 // ------------------------------------------------------------------------
9766 // determine next fading area for changed viewport definitions
9767 // ------------------------------------------------------------------------
9769 // start with current playfield area (default fading area)
9772 FADE_SXSIZE = FULL_SXSIZE;
9773 FADE_SYSIZE = FULL_SYSIZE;
9775 // add new playfield area if position or size has changed
9776 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9777 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9779 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9780 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9783 // add current and new door 1 area if position or size has changed
9784 if (new_dx != DX || new_dy != DY ||
9785 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9787 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9788 DX, DY, DXSIZE, DYSIZE);
9789 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9790 new_dx, new_dy, new_dxsize, new_dysize);
9793 // add current and new door 2 area if position or size has changed
9794 if (new_vx != VX || new_vy != VY ||
9795 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9797 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9798 VX, VY, VXSIZE, VYSIZE);
9799 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9800 new_vx, new_vy, new_vxsize, new_vysize);
9803 // ------------------------------------------------------------------------
9804 // handle changed tile size
9805 // ------------------------------------------------------------------------
9807 if (new_tilesize_var != TILESIZE_VAR)
9809 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9811 // changing tile size invalidates scroll values of engine snapshots
9812 FreeEngineSnapshotSingle();
9814 // changing tile size requires update of graphic mapping for EM engine
9815 init_em_graphics = TRUE;
9826 SXSIZE = new_sxsize;
9827 SYSIZE = new_sysize;
9828 DXSIZE = new_dxsize;
9829 DYSIZE = new_dysize;
9830 VXSIZE = new_vxsize;
9831 VYSIZE = new_vysize;
9832 EXSIZE = new_exsize;
9833 EYSIZE = new_eysize;
9834 REAL_SX = new_real_sx;
9835 REAL_SY = new_real_sy;
9836 FULL_SXSIZE = new_full_sxsize;
9837 FULL_SYSIZE = new_full_sysize;
9838 TILESIZE_VAR = new_tilesize_var;
9840 init_gfx_buffers = TRUE;
9841 init_gadgets_and_anims = TRUE;
9843 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9844 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9847 if (init_gfx_buffers)
9849 // Debug("tools:viewport", "init_gfx_buffers");
9851 SCR_FIELDX = new_scr_fieldx_buffers;
9852 SCR_FIELDY = new_scr_fieldy_buffers;
9856 SCR_FIELDX = new_scr_fieldx;
9857 SCR_FIELDY = new_scr_fieldy;
9859 SetDrawDeactivationMask(REDRAW_NONE);
9860 SetDrawBackgroundMask(REDRAW_FIELD);
9863 if (init_video_buffer)
9865 // Debug("tools:viewport", "init_video_buffer");
9867 FreeAllImageTextures(); // needs old renderer to free the textures
9869 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9870 InitImageTextures();
9873 if (init_gadgets_and_anims)
9875 // Debug("tools:viewport", "init_gadgets_and_anims");
9878 InitGlobalAnimations();
9881 if (init_em_graphics)
9883 InitGraphicInfo_EM();