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 void DumpTile(int x, int y)
401 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
404 if (!IN_LEV_FIELD(x, y))
406 Info("(not in level field)");
412 token_name = element_info[Tile[x][y]].token_name;
414 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
415 Info("Back: %s", print_if_not_empty(Back[x][y]));
416 Info("Store: %s", print_if_not_empty(Store[x][y]));
417 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
418 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
419 Info("MovPos: %d", MovPos[x][y]);
420 Info("MovDir: %d", MovDir[x][y]);
421 Info("MovDelay: %d", MovDelay[x][y]);
422 Info("ChangeDelay: %d", ChangeDelay[x][y]);
423 Info("CustomValue: %d", CustomValue[x][y]);
424 Info("GfxElement: %d", GfxElement[x][y]);
425 Info("GfxAction: %d", GfxAction[x][y]);
426 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
427 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
431 void DumpTileFromScreen(int sx, int sy)
433 int lx = getLevelFromScreenX(sx);
434 int ly = getLevelFromScreenY(sy);
439 void SetDrawtoField(int mode)
441 if (mode == DRAW_TO_FIELDBUFFER)
447 BX2 = SCR_FIELDX + 1;
448 BY2 = SCR_FIELDY + 1;
450 drawto_field = fieldbuffer;
452 else // DRAW_TO_BACKBUFFER
458 BX2 = SCR_FIELDX - 1;
459 BY2 = SCR_FIELDY - 1;
461 drawto_field = backbuffer;
465 int GetDrawtoField(void)
467 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
470 static void RedrawPlayfield_RND(void)
472 if (game.envelope_active)
475 DrawLevel(REDRAW_ALL);
479 void RedrawPlayfield(void)
481 if (game_status != GAME_MODE_PLAYING)
484 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
485 RedrawPlayfield_EM(TRUE);
486 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
487 RedrawPlayfield_SP(TRUE);
488 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
489 RedrawPlayfield_MM();
490 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
491 RedrawPlayfield_RND();
493 BlitScreenToBitmap(backbuffer);
495 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
499 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
502 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
503 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
505 if (x == -1 && y == -1)
508 if (draw_target == DRAW_TO_SCREEN)
509 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
511 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
514 static void DrawMaskedBorderExt_FIELD(int draw_target)
516 if (global.border_status >= GAME_MODE_MAIN &&
517 global.border_status <= GAME_MODE_PLAYING &&
518 border.draw_masked[global.border_status])
519 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
523 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
525 // when drawing to backbuffer, never draw border over open doors
526 if (draw_target == DRAW_TO_BACKBUFFER &&
527 (GetDoorState() & DOOR_OPEN_1))
530 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
531 (global.border_status != GAME_MODE_EDITOR ||
532 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
533 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
536 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
538 // when drawing to backbuffer, never draw border over open doors
539 if (draw_target == DRAW_TO_BACKBUFFER &&
540 (GetDoorState() & DOOR_OPEN_2))
543 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
544 global.border_status != GAME_MODE_EDITOR)
545 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
550 // currently not available
553 static void DrawMaskedBorderExt_ALL(int draw_target)
555 DrawMaskedBorderExt_FIELD(draw_target);
556 DrawMaskedBorderExt_DOOR_1(draw_target);
557 DrawMaskedBorderExt_DOOR_2(draw_target);
558 DrawMaskedBorderExt_DOOR_3(draw_target);
561 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
563 // never draw masked screen borders on borderless screens
564 if (global.border_status == GAME_MODE_LOADING ||
565 global.border_status == GAME_MODE_TITLE)
568 if (redraw_mask & REDRAW_ALL)
569 DrawMaskedBorderExt_ALL(draw_target);
572 if (redraw_mask & REDRAW_FIELD)
573 DrawMaskedBorderExt_FIELD(draw_target);
574 if (redraw_mask & REDRAW_DOOR_1)
575 DrawMaskedBorderExt_DOOR_1(draw_target);
576 if (redraw_mask & REDRAW_DOOR_2)
577 DrawMaskedBorderExt_DOOR_2(draw_target);
578 if (redraw_mask & REDRAW_DOOR_3)
579 DrawMaskedBorderExt_DOOR_3(draw_target);
583 void DrawMaskedBorder_FIELD(void)
585 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
588 void DrawMaskedBorder(int redraw_mask)
590 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
593 void DrawMaskedBorderToTarget(int draw_target)
595 if (draw_target == DRAW_TO_BACKBUFFER ||
596 draw_target == DRAW_TO_SCREEN)
598 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
602 int last_border_status = global.border_status;
604 if (draw_target == DRAW_TO_FADE_SOURCE)
606 global.border_status = gfx.fade_border_source_status;
607 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
609 else if (draw_target == DRAW_TO_FADE_TARGET)
611 global.border_status = gfx.fade_border_target_status;
612 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
615 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
617 global.border_status = last_border_status;
618 gfx.masked_border_bitmap_ptr = backbuffer;
622 void DrawTileCursor(int draw_target)
624 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
627 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
629 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
632 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
634 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
635 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
637 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
640 void BlitScreenToBitmap(Bitmap *target_bitmap)
642 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
643 BlitScreenToBitmap_EM(target_bitmap);
644 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
645 BlitScreenToBitmap_SP(target_bitmap);
646 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
647 BlitScreenToBitmap_MM(target_bitmap);
648 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
649 BlitScreenToBitmap_RND(target_bitmap);
651 redraw_mask |= REDRAW_FIELD;
654 static void DrawFramesPerSecond(void)
657 int font_nr = FONT_TEXT_2;
658 int font_width = getFontWidth(font_nr);
659 int draw_deactivation_mask = GetDrawDeactivationMask();
660 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
662 // draw FPS with leading space (needed if field buffer deactivated)
663 sprintf(text, " %04.1f fps", global.frames_per_second);
665 // override draw deactivation mask (required for invisible warp mode)
666 SetDrawDeactivationMask(REDRAW_NONE);
668 // draw opaque FPS if field buffer deactivated, else draw masked FPS
669 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
670 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
672 // set draw deactivation mask to previous value
673 SetDrawDeactivationMask(draw_deactivation_mask);
675 // force full-screen redraw in this frame
676 redraw_mask = REDRAW_ALL;
680 static void PrintFrameTimeDebugging(void)
682 static unsigned int last_counter = 0;
683 unsigned int counter = Counter();
684 int diff_1 = counter - last_counter;
685 int diff_2 = diff_1 - GAME_FRAME_DELAY;
687 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
688 char diff_bar[2 * diff_2_max + 5];
692 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
694 for (i = 0; i < diff_2_max; i++)
695 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
696 i >= diff_2_max - diff_2_cut ? '-' : ' ');
698 diff_bar[pos++] = '|';
700 for (i = 0; i < diff_2_max; i++)
701 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
703 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
705 diff_bar[pos++] = '\0';
707 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
710 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
713 last_counter = counter;
717 static int unifiedRedrawMask(int mask)
719 if (mask & REDRAW_ALL)
722 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
728 static boolean equalRedrawMasks(int mask_1, int mask_2)
730 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
733 void BackToFront(void)
735 static int last_redraw_mask = REDRAW_NONE;
737 // force screen redraw in every frame to continue drawing global animations
738 // (but always use the last redraw mask to prevent unwanted side effects)
739 if (redraw_mask == REDRAW_NONE)
740 redraw_mask = last_redraw_mask;
742 last_redraw_mask = redraw_mask;
745 // masked border now drawn immediately when blitting backbuffer to window
747 // draw masked border to all viewports, if defined
748 DrawMaskedBorder(redraw_mask);
751 // draw frames per second (only if debug mode is enabled)
752 if (redraw_mask & REDRAW_FPS)
753 DrawFramesPerSecond();
755 // remove playfield redraw before potentially merging with doors redraw
756 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
757 redraw_mask &= ~REDRAW_FIELD;
759 // redraw complete window if both playfield and (some) doors need redraw
760 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
761 redraw_mask = REDRAW_ALL;
763 /* although redrawing the whole window would be fine for normal gameplay,
764 being able to only redraw the playfield is required for deactivating
765 certain drawing areas (mainly playfield) to work, which is needed for
766 warp-forward to be fast enough (by skipping redraw of most frames) */
768 if (redraw_mask & REDRAW_ALL)
770 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
772 else if (redraw_mask & REDRAW_FIELD)
774 BlitBitmap(backbuffer, window,
775 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
777 else if (redraw_mask & REDRAW_DOORS)
779 // merge door areas to prevent calling screen redraw more than once
785 if (redraw_mask & REDRAW_DOOR_1)
789 x2 = MAX(x2, DX + DXSIZE);
790 y2 = MAX(y2, DY + DYSIZE);
793 if (redraw_mask & REDRAW_DOOR_2)
797 x2 = MAX(x2, VX + VXSIZE);
798 y2 = MAX(y2, VY + VYSIZE);
801 if (redraw_mask & REDRAW_DOOR_3)
805 x2 = MAX(x2, EX + EXSIZE);
806 y2 = MAX(y2, EY + EYSIZE);
809 // make sure that at least one pixel is blitted, and inside the screen
810 // (else nothing is blitted, causing the animations not to be updated)
811 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
812 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
813 x2 = MIN(MAX(1, x2), WIN_XSIZE);
814 y2 = MIN(MAX(1, y2), WIN_YSIZE);
816 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
819 redraw_mask = REDRAW_NONE;
822 PrintFrameTimeDebugging();
826 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
828 unsigned int frame_delay_value_old = GetVideoFrameDelay();
830 SetVideoFrameDelay(frame_delay_value);
834 SetVideoFrameDelay(frame_delay_value_old);
837 static int fade_type_skip = FADE_TYPE_NONE;
839 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
841 void (*draw_border_function)(void) = NULL;
842 int x, y, width, height;
843 int fade_delay, post_delay;
845 if (fade_type == FADE_TYPE_FADE_OUT)
847 if (fade_type_skip != FADE_TYPE_NONE)
849 // skip all fade operations until specified fade operation
850 if (fade_type & fade_type_skip)
851 fade_type_skip = FADE_TYPE_NONE;
856 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
860 redraw_mask |= fade_mask;
862 if (fade_type == FADE_TYPE_SKIP)
864 fade_type_skip = fade_mode;
869 fade_delay = fading.fade_delay;
870 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
872 if (fade_type_skip != FADE_TYPE_NONE)
874 // skip all fade operations until specified fade operation
875 if (fade_type & fade_type_skip)
876 fade_type_skip = FADE_TYPE_NONE;
881 if (global.autoplay_leveldir)
886 if (fade_mask == REDRAW_FIELD)
891 height = FADE_SYSIZE;
893 if (border.draw_masked_when_fading)
894 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
896 DrawMaskedBorder_FIELD(); // draw once
906 // when switching screens without fading, set fade delay to zero
907 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
910 // do not display black frame when fading out without fade delay
911 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
914 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
915 draw_border_function);
917 redraw_mask &= ~fade_mask;
919 ClearAutoRepeatKeyEvents();
922 static void SetScreenStates_BeforeFadingIn(void)
924 // temporarily set screen mode for animations to screen after fading in
925 global.anim_status = global.anim_status_next;
927 // store backbuffer with all animations that will be started after fading in
928 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
930 // set screen mode for animations back to fading
931 global.anim_status = GAME_MODE_PSEUDO_FADING;
934 static void SetScreenStates_AfterFadingIn(void)
936 // store new source screen (to use correct masked border for fading)
937 gfx.fade_border_source_status = global.border_status;
939 global.anim_status = global.anim_status_next;
942 static void SetScreenStates_BeforeFadingOut(void)
944 // store new target screen (to use correct masked border for fading)
945 gfx.fade_border_target_status = game_status;
947 // set screen mode for animations to fading
948 global.anim_status = GAME_MODE_PSEUDO_FADING;
950 // store backbuffer with all animations that will be stopped for fading out
951 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
954 static void SetScreenStates_AfterFadingOut(void)
956 global.border_status = game_status;
959 void FadeIn(int fade_mask)
961 SetScreenStates_BeforeFadingIn();
964 DrawMaskedBorder(REDRAW_ALL);
967 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
968 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
970 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
974 FADE_SXSIZE = FULL_SXSIZE;
975 FADE_SYSIZE = FULL_SYSIZE;
977 // activate virtual buttons depending on upcoming game status
978 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
979 game_status == GAME_MODE_PLAYING && !tape.playing)
980 SetOverlayActive(TRUE);
982 SetScreenStates_AfterFadingIn();
984 // force update of global animation status in case of rapid screen changes
985 redraw_mask = REDRAW_ALL;
989 void FadeOut(int fade_mask)
991 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
992 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
993 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
996 SetScreenStates_BeforeFadingOut();
998 SetTileCursorActive(FALSE);
999 SetOverlayActive(FALSE);
1002 DrawMaskedBorder(REDRAW_ALL);
1005 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1006 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1008 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1010 SetScreenStates_AfterFadingOut();
1013 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1015 static struct TitleFadingInfo fading_leave_stored;
1018 fading_leave_stored = fading_leave;
1020 fading = fading_leave_stored;
1023 void FadeSetEnterMenu(void)
1025 fading = menu.enter_menu;
1027 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1030 void FadeSetLeaveMenu(void)
1032 fading = menu.leave_menu;
1034 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1037 void FadeSetEnterScreen(void)
1039 fading = menu.enter_screen[game_status];
1041 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1044 void FadeSetNextScreen(void)
1046 fading = menu.next_screen[game_status];
1048 // (do not overwrite fade mode set by FadeSetEnterScreen)
1049 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1052 void FadeSetLeaveScreen(void)
1054 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1057 void FadeSetFromType(int type)
1059 if (type & TYPE_ENTER_SCREEN)
1060 FadeSetEnterScreen();
1061 else if (type & TYPE_ENTER)
1063 else if (type & TYPE_LEAVE)
1067 void FadeSetDisabled(void)
1069 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1071 fading = fading_none;
1074 void FadeSkipNextFadeIn(void)
1076 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1079 void FadeSkipNextFadeOut(void)
1081 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1084 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1086 if (graphic == IMG_UNDEFINED)
1089 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1091 return (graphic_info[graphic].bitmap != NULL || redefined ?
1092 graphic_info[graphic].bitmap :
1093 graphic_info[default_graphic].bitmap);
1096 static Bitmap *getBackgroundBitmap(int graphic)
1098 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1101 static Bitmap *getGlobalBorderBitmap(int graphic)
1103 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1106 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1109 (status == GAME_MODE_MAIN ||
1110 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1111 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1112 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1113 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1116 return getGlobalBorderBitmap(graphic);
1119 void SetWindowBackgroundImageIfDefined(int graphic)
1121 if (graphic_info[graphic].bitmap)
1122 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1125 void SetMainBackgroundImageIfDefined(int graphic)
1127 if (graphic_info[graphic].bitmap)
1128 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1131 void SetDoorBackgroundImageIfDefined(int graphic)
1133 if (graphic_info[graphic].bitmap)
1134 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1137 void SetWindowBackgroundImage(int graphic)
1139 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1142 void SetMainBackgroundImage(int graphic)
1144 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1147 void SetDoorBackgroundImage(int graphic)
1149 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetPanelBackground(void)
1154 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1156 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1157 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1159 SetDoorBackgroundBitmap(bitmap_db_panel);
1162 void DrawBackground(int x, int y, int width, int height)
1164 // "drawto" might still point to playfield buffer here (hall of fame)
1165 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1167 if (IN_GFX_FIELD_FULL(x, y))
1168 redraw_mask |= REDRAW_FIELD;
1169 else if (IN_GFX_DOOR_1(x, y))
1170 redraw_mask |= REDRAW_DOOR_1;
1171 else if (IN_GFX_DOOR_2(x, y))
1172 redraw_mask |= REDRAW_DOOR_2;
1173 else if (IN_GFX_DOOR_3(x, y))
1174 redraw_mask |= REDRAW_DOOR_3;
1177 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1179 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1181 if (font->bitmap == NULL)
1184 DrawBackground(x, y, width, height);
1187 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1189 struct GraphicInfo *g = &graphic_info[graphic];
1191 if (g->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 static int game_status_last = -1;
1198 static Bitmap *global_border_bitmap_last = NULL;
1199 static Bitmap *global_border_bitmap = NULL;
1200 static int real_sx_last = -1, real_sy_last = -1;
1201 static int full_sxsize_last = -1, full_sysize_last = -1;
1202 static int dx_last = -1, dy_last = -1;
1203 static int dxsize_last = -1, dysize_last = -1;
1204 static int vx_last = -1, vy_last = -1;
1205 static int vxsize_last = -1, vysize_last = -1;
1206 static int ex_last = -1, ey_last = -1;
1207 static int exsize_last = -1, eysize_last = -1;
1209 boolean CheckIfGlobalBorderHasChanged(void)
1211 // if game status has not changed, global border has not changed either
1212 if (game_status == game_status_last)
1215 // determine and store new global border bitmap for current game status
1216 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1218 return (global_border_bitmap_last != global_border_bitmap);
1221 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1223 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1224 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1226 // if game status has not changed, nothing has to be redrawn
1227 if (game_status == game_status_last)
1230 // redraw if last screen was title screen
1231 if (game_status_last == GAME_MODE_TITLE)
1234 // redraw if global screen border has changed
1235 if (CheckIfGlobalBorderHasChanged())
1238 // redraw if position or size of playfield area has changed
1239 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1240 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1243 // redraw if position or size of door area has changed
1244 if (dx_last != DX || dy_last != DY ||
1245 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1248 // redraw if position or size of tape area has changed
1249 if (vx_last != VX || vy_last != VY ||
1250 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1253 // redraw if position or size of editor area has changed
1254 if (ex_last != EX || ey_last != EY ||
1255 exsize_last != EXSIZE || eysize_last != EYSIZE)
1262 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1265 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1267 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1270 void RedrawGlobalBorder(void)
1272 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1274 RedrawGlobalBorderFromBitmap(bitmap);
1276 redraw_mask = REDRAW_ALL;
1279 static void RedrawGlobalBorderIfNeeded(void)
1281 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1282 if (game_status == game_status_last)
1286 // copy current draw buffer to later copy back areas that have not changed
1287 if (game_status_last != GAME_MODE_TITLE)
1288 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1290 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1291 if (CheckIfGlobalBorderRedrawIsNeeded())
1293 // determine and store new global border bitmap for current game status
1294 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1297 // redraw global screen border (or clear, if defined to be empty)
1298 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1300 if (game_status == GAME_MODE_EDITOR)
1301 DrawSpecialEditorDoor();
1303 // copy previous playfield and door areas, if they are defined on both
1304 // previous and current screen and if they still have the same size
1306 if (real_sx_last != -1 && real_sy_last != -1 &&
1307 REAL_SX != -1 && REAL_SY != -1 &&
1308 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1309 BlitBitmap(bitmap_db_store_1, backbuffer,
1310 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1313 if (dx_last != -1 && dy_last != -1 &&
1314 DX != -1 && DY != -1 &&
1315 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1316 BlitBitmap(bitmap_db_store_1, backbuffer,
1317 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1319 if (game_status != GAME_MODE_EDITOR)
1321 if (vx_last != -1 && vy_last != -1 &&
1322 VX != -1 && VY != -1 &&
1323 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1324 BlitBitmap(bitmap_db_store_1, backbuffer,
1325 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1329 if (ex_last != -1 && ey_last != -1 &&
1330 EX != -1 && EY != -1 &&
1331 exsize_last == EXSIZE && eysize_last == EYSIZE)
1332 BlitBitmap(bitmap_db_store_1, backbuffer,
1333 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1336 redraw_mask = REDRAW_ALL;
1339 game_status_last = game_status;
1341 global_border_bitmap_last = global_border_bitmap;
1343 real_sx_last = REAL_SX;
1344 real_sy_last = REAL_SY;
1345 full_sxsize_last = FULL_SXSIZE;
1346 full_sysize_last = FULL_SYSIZE;
1349 dxsize_last = DXSIZE;
1350 dysize_last = DYSIZE;
1353 vxsize_last = VXSIZE;
1354 vysize_last = VYSIZE;
1357 exsize_last = EXSIZE;
1358 eysize_last = EYSIZE;
1361 void ClearField(void)
1363 RedrawGlobalBorderIfNeeded();
1365 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1366 // (when entering hall of fame after playing)
1367 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1369 // !!! maybe this should be done before clearing the background !!!
1370 if (game_status == GAME_MODE_PLAYING)
1372 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1373 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1377 SetDrawtoField(DRAW_TO_BACKBUFFER);
1381 void MarkTileDirty(int x, int y)
1383 redraw_mask |= REDRAW_FIELD;
1386 void SetBorderElement(void)
1390 BorderElement = EL_EMPTY;
1392 // only the R'n'D game engine may use an additional steelwall border
1393 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1396 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1398 for (x = 0; x < lev_fieldx; x++)
1400 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1401 BorderElement = EL_STEELWALL;
1403 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1409 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1410 int max_array_fieldx, int max_array_fieldy,
1411 short field[max_array_fieldx][max_array_fieldy],
1412 int max_fieldx, int max_fieldy)
1416 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1417 static int safety = 0;
1419 // check if starting field still has the desired content
1420 if (field[from_x][from_y] == fill_element)
1425 if (safety > max_fieldx * max_fieldy)
1426 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1428 old_element = field[from_x][from_y];
1429 field[from_x][from_y] = fill_element;
1431 for (i = 0; i < 4; i++)
1433 x = from_x + check[i][0];
1434 y = from_y + check[i][1];
1436 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1437 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1438 field, max_fieldx, max_fieldy);
1444 void FloodFillLevel(int from_x, int from_y, int fill_element,
1445 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1446 int max_fieldx, int max_fieldy)
1448 FloodFillLevelExt(from_x, from_y, fill_element,
1449 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1450 max_fieldx, max_fieldy);
1453 void SetRandomAnimationValue(int x, int y)
1455 gfx.anim_random_frame = GfxRandom[x][y];
1458 int getGraphicAnimationFrame(int graphic, int sync_frame)
1460 // animation synchronized with global frame counter, not move position
1461 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1462 sync_frame = FrameCounter;
1464 return getAnimationFrame(graphic_info[graphic].anim_frames,
1465 graphic_info[graphic].anim_delay,
1466 graphic_info[graphic].anim_mode,
1467 graphic_info[graphic].anim_start_frame,
1471 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1473 struct GraphicInfo *g = &graphic_info[graphic];
1474 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1476 if (tilesize == gfx.standard_tile_size)
1477 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1478 else if (tilesize == game.tile_size)
1479 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1481 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1484 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1485 boolean get_backside)
1487 struct GraphicInfo *g = &graphic_info[graphic];
1488 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1489 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1491 if (g->offset_y == 0) // frames are ordered horizontally
1493 int max_width = g->anim_frames_per_line * g->width;
1494 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1496 *x = pos % max_width;
1497 *y = src_y % g->height + pos / max_width * g->height;
1499 else if (g->offset_x == 0) // frames are ordered vertically
1501 int max_height = g->anim_frames_per_line * g->height;
1502 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1504 *x = src_x % g->width + pos / max_height * g->width;
1505 *y = pos % max_height;
1507 else // frames are ordered diagonally
1509 *x = src_x + frame * g->offset_x;
1510 *y = src_y + frame * g->offset_y;
1514 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1515 Bitmap **bitmap, int *x, int *y,
1516 boolean get_backside)
1518 struct GraphicInfo *g = &graphic_info[graphic];
1520 // if no graphics defined at all, use fallback graphics
1521 if (g->bitmaps == NULL)
1522 *g = graphic_info[IMG_CHAR_EXCLAM];
1524 // if no in-game graphics defined, always use standard graphic size
1525 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1526 tilesize = TILESIZE;
1528 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1529 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1531 *x = *x * tilesize / g->tile_size;
1532 *y = *y * tilesize / g->tile_size;
1535 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1536 Bitmap **bitmap, int *x, int *y)
1538 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1541 void getFixedGraphicSource(int graphic, int frame,
1542 Bitmap **bitmap, int *x, int *y)
1544 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1547 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1549 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1552 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1553 int *x, int *y, boolean get_backside)
1555 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1559 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1561 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1564 void DrawGraphic(int x, int y, int graphic, int frame)
1567 if (!IN_SCR_FIELD(x, y))
1569 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1570 Debug("draw:DrawGraphic", "This should never happen!");
1576 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1579 MarkTileDirty(x, y);
1582 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1585 if (!IN_SCR_FIELD(x, y))
1587 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1589 Debug("draw:DrawFixedGraphic", "This should never happen!");
1595 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1597 MarkTileDirty(x, y);
1600 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1606 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1608 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1611 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1617 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1618 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1621 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1624 if (!IN_SCR_FIELD(x, y))
1626 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1628 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1634 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1637 MarkTileDirty(x, y);
1640 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1643 if (!IN_SCR_FIELD(x, y))
1645 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1647 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1653 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1655 MarkTileDirty(x, y);
1658 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1664 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1666 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1670 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1671 int graphic, int frame)
1676 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1678 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1682 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1684 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1686 MarkTileDirty(x / tilesize, y / tilesize);
1689 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1692 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1693 graphic, frame, tilesize);
1694 MarkTileDirty(x / tilesize, y / tilesize);
1697 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1703 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1704 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1707 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1708 int frame, int tilesize)
1713 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1714 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1717 void DrawMiniGraphic(int x, int y, int graphic)
1719 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1720 MarkTileDirty(x / 2, y / 2);
1723 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1728 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1729 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1732 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1733 int graphic, int frame,
1734 int cut_mode, int mask_mode)
1739 int width = TILEX, height = TILEY;
1742 if (dx || dy) // shifted graphic
1744 if (x < BX1) // object enters playfield from the left
1751 else if (x > BX2) // object enters playfield from the right
1757 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1763 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1765 else if (dx) // general horizontal movement
1766 MarkTileDirty(x + SIGN(dx), y);
1768 if (y < BY1) // object enters playfield from the top
1770 if (cut_mode == CUT_BELOW) // object completely above top border
1778 else if (y > BY2) // object enters playfield from the bottom
1784 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1790 else if (dy > 0 && cut_mode == CUT_ABOVE)
1792 if (y == BY2) // object completely above bottom border
1798 MarkTileDirty(x, y + 1);
1799 } // object leaves playfield to the bottom
1800 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1802 else if (dy) // general vertical movement
1803 MarkTileDirty(x, y + SIGN(dy));
1807 if (!IN_SCR_FIELD(x, y))
1809 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1811 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1817 width = width * TILESIZE_VAR / TILESIZE;
1818 height = height * TILESIZE_VAR / TILESIZE;
1819 cx = cx * TILESIZE_VAR / TILESIZE;
1820 cy = cy * TILESIZE_VAR / TILESIZE;
1821 dx = dx * TILESIZE_VAR / TILESIZE;
1822 dy = dy * TILESIZE_VAR / TILESIZE;
1824 if (width > 0 && height > 0)
1826 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1831 dst_x = FX + x * TILEX_VAR + dx;
1832 dst_y = FY + y * TILEY_VAR + dy;
1834 if (mask_mode == USE_MASKING)
1835 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1838 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1841 MarkTileDirty(x, y);
1845 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1846 int graphic, int frame,
1847 int cut_mode, int mask_mode)
1852 int width = TILEX_VAR, height = TILEY_VAR;
1855 int x2 = x + SIGN(dx);
1856 int y2 = y + SIGN(dy);
1858 // movement with two-tile animations must be sync'ed with movement position,
1859 // not with current GfxFrame (which can be higher when using slow movement)
1860 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1861 int anim_frames = graphic_info[graphic].anim_frames;
1863 // (we also need anim_delay here for movement animations with less frames)
1864 int anim_delay = graphic_info[graphic].anim_delay;
1865 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1867 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1868 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1870 // re-calculate animation frame for two-tile movement animation
1871 frame = getGraphicAnimationFrame(graphic, sync_frame);
1873 // check if movement start graphic inside screen area and should be drawn
1874 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1876 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1878 dst_x = FX + x1 * TILEX_VAR;
1879 dst_y = FY + y1 * TILEY_VAR;
1881 if (mask_mode == USE_MASKING)
1882 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1885 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1888 MarkTileDirty(x1, y1);
1891 // check if movement end graphic inside screen area and should be drawn
1892 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1894 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1896 dst_x = FX + x2 * TILEX_VAR;
1897 dst_y = FY + y2 * TILEY_VAR;
1899 if (mask_mode == USE_MASKING)
1900 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1903 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1906 MarkTileDirty(x2, y2);
1910 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1911 int graphic, int frame,
1912 int cut_mode, int mask_mode)
1916 DrawGraphic(x, y, graphic, frame);
1921 if (graphic_info[graphic].double_movement) // EM style movement images
1922 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1924 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1927 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1928 int graphic, int frame, int cut_mode)
1930 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1933 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1934 int cut_mode, int mask_mode)
1936 int lx = LEVELX(x), ly = LEVELY(y);
1940 if (IN_LEV_FIELD(lx, ly))
1942 SetRandomAnimationValue(lx, ly);
1944 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1945 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1947 // do not use double (EM style) movement graphic when not moving
1948 if (graphic_info[graphic].double_movement && !dx && !dy)
1950 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1951 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1954 else // border element
1956 graphic = el2img(element);
1957 frame = getGraphicAnimationFrame(graphic, -1);
1960 if (element == EL_EXPANDABLE_WALL)
1962 boolean left_stopped = FALSE, right_stopped = FALSE;
1964 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1965 left_stopped = TRUE;
1966 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
1967 right_stopped = TRUE;
1969 if (left_stopped && right_stopped)
1971 else if (left_stopped)
1973 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1974 frame = graphic_info[graphic].anim_frames - 1;
1976 else if (right_stopped)
1978 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1979 frame = graphic_info[graphic].anim_frames - 1;
1984 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1985 else if (mask_mode == USE_MASKING)
1986 DrawGraphicThruMask(x, y, graphic, frame);
1988 DrawGraphic(x, y, graphic, frame);
1991 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1992 int cut_mode, int mask_mode)
1994 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1995 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1996 cut_mode, mask_mode);
1999 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2002 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2005 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2008 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2011 void DrawLevelElementThruMask(int x, int y, int element)
2013 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2016 void DrawLevelFieldThruMask(int x, int y)
2018 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2021 // !!! implementation of quicksand is totally broken !!!
2022 #define IS_CRUMBLED_TILE(x, y, e) \
2023 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2024 !IS_MOVING(x, y) || \
2025 (e) == EL_QUICKSAND_EMPTYING || \
2026 (e) == EL_QUICKSAND_FAST_EMPTYING))
2028 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2033 int width, height, cx, cy;
2034 int sx = SCREENX(x), sy = SCREENY(y);
2035 int crumbled_border_size = graphic_info[graphic].border_size;
2036 int crumbled_tile_size = graphic_info[graphic].tile_size;
2037 int crumbled_border_size_var =
2038 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2041 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2043 for (i = 1; i < 4; i++)
2045 int dxx = (i & 1 ? dx : 0);
2046 int dyy = (i & 2 ? dy : 0);
2049 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2052 // check if neighbour field is of same crumble type
2053 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2054 graphic_info[graphic].class ==
2055 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2057 // return if check prevents inner corner
2058 if (same == (dxx == dx && dyy == dy))
2062 // if we reach this point, we have an inner corner
2064 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2066 width = crumbled_border_size_var;
2067 height = crumbled_border_size_var;
2068 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2069 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2071 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2072 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2075 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2080 int width, height, bx, by, cx, cy;
2081 int sx = SCREENX(x), sy = SCREENY(y);
2082 int crumbled_border_size = graphic_info[graphic].border_size;
2083 int crumbled_tile_size = graphic_info[graphic].tile_size;
2084 int crumbled_border_size_var =
2085 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2086 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2089 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2091 // draw simple, sloppy, non-corner-accurate crumbled border
2093 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2094 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2095 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2096 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2098 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2099 FX + sx * TILEX_VAR + cx,
2100 FY + sy * TILEY_VAR + cy);
2102 // (remaining middle border part must be at least as big as corner part)
2103 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2104 crumbled_border_size_var >= TILESIZE_VAR / 3)
2107 // correct corners of crumbled border, if needed
2109 for (i = -1; i <= 1; i += 2)
2111 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2112 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2113 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2116 // check if neighbour field is of same crumble type
2117 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2118 graphic_info[graphic].class ==
2119 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2121 // no crumbled corner, but continued crumbled border
2123 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2124 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2125 int b1 = (i == 1 ? crumbled_border_size_var :
2126 TILESIZE_VAR - 2 * crumbled_border_size_var);
2128 width = crumbled_border_size_var;
2129 height = crumbled_border_size_var;
2131 if (dir == 1 || dir == 2)
2146 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2148 FX + sx * TILEX_VAR + cx,
2149 FY + sy * TILEY_VAR + cy);
2154 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2156 int sx = SCREENX(x), sy = SCREENY(y);
2159 static int xy[4][2] =
2167 if (!IN_LEV_FIELD(x, y))
2170 element = TILE_GFX_ELEMENT(x, y);
2172 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2174 if (!IN_SCR_FIELD(sx, sy))
2177 // crumble field borders towards direct neighbour fields
2178 for (i = 0; i < 4; i++)
2180 int xx = x + xy[i][0];
2181 int yy = y + xy[i][1];
2183 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2186 // check if neighbour field is of same crumble type
2187 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2188 graphic_info[graphic].class ==
2189 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2192 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2195 // crumble inner field corners towards corner neighbour fields
2196 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2197 graphic_info[graphic].anim_frames == 2)
2199 for (i = 0; i < 4; i++)
2201 int dx = (i & 1 ? +1 : -1);
2202 int dy = (i & 2 ? +1 : -1);
2204 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2208 MarkTileDirty(sx, sy);
2210 else // center field is not crumbled -- crumble neighbour fields
2212 // crumble field borders of direct neighbour fields
2213 for (i = 0; i < 4; i++)
2215 int xx = x + xy[i][0];
2216 int yy = y + xy[i][1];
2217 int sxx = sx + xy[i][0];
2218 int syy = sy + xy[i][1];
2220 if (!IN_LEV_FIELD(xx, yy) ||
2221 !IN_SCR_FIELD(sxx, syy))
2224 // do not crumble fields that are being digged or snapped
2225 if (Tile[xx][yy] == EL_EMPTY ||
2226 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2229 element = TILE_GFX_ELEMENT(xx, yy);
2231 if (!IS_CRUMBLED_TILE(xx, yy, element))
2234 graphic = el_act2crm(element, ACTION_DEFAULT);
2236 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2238 MarkTileDirty(sxx, syy);
2241 // crumble inner field corners of corner neighbour fields
2242 for (i = 0; i < 4; i++)
2244 int dx = (i & 1 ? +1 : -1);
2245 int dy = (i & 2 ? +1 : -1);
2251 if (!IN_LEV_FIELD(xx, yy) ||
2252 !IN_SCR_FIELD(sxx, syy))
2255 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2258 element = TILE_GFX_ELEMENT(xx, yy);
2260 if (!IS_CRUMBLED_TILE(xx, yy, element))
2263 graphic = el_act2crm(element, ACTION_DEFAULT);
2265 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2266 graphic_info[graphic].anim_frames == 2)
2267 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2269 MarkTileDirty(sxx, syy);
2274 void DrawLevelFieldCrumbled(int x, int y)
2278 if (!IN_LEV_FIELD(x, y))
2281 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2282 GfxElement[x][y] != EL_UNDEFINED &&
2283 GFX_CRUMBLED(GfxElement[x][y]))
2285 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2290 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2292 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2295 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2298 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2299 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2300 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2301 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2302 int sx = SCREENX(x), sy = SCREENY(y);
2304 DrawGraphic(sx, sy, graphic1, frame1);
2305 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2308 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2310 int sx = SCREENX(x), sy = SCREENY(y);
2311 static int xy[4][2] =
2320 // crumble direct neighbour fields (required for field borders)
2321 for (i = 0; i < 4; i++)
2323 int xx = x + xy[i][0];
2324 int yy = y + xy[i][1];
2325 int sxx = sx + xy[i][0];
2326 int syy = sy + xy[i][1];
2328 if (!IN_LEV_FIELD(xx, yy) ||
2329 !IN_SCR_FIELD(sxx, syy) ||
2330 !GFX_CRUMBLED(Tile[xx][yy]) ||
2334 DrawLevelField(xx, yy);
2337 // crumble corner neighbour fields (required for inner field corners)
2338 for (i = 0; i < 4; i++)
2340 int dx = (i & 1 ? +1 : -1);
2341 int dy = (i & 2 ? +1 : -1);
2347 if (!IN_LEV_FIELD(xx, yy) ||
2348 !IN_SCR_FIELD(sxx, syy) ||
2349 !GFX_CRUMBLED(Tile[xx][yy]) ||
2353 int element = TILE_GFX_ELEMENT(xx, yy);
2354 int graphic = el_act2crm(element, ACTION_DEFAULT);
2356 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2357 graphic_info[graphic].anim_frames == 2)
2358 DrawLevelField(xx, yy);
2362 static int getBorderElement(int x, int y)
2366 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2367 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2368 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2369 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2370 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2371 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2372 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2374 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2375 int steel_position = (x == -1 && y == -1 ? 0 :
2376 x == lev_fieldx && y == -1 ? 1 :
2377 x == -1 && y == lev_fieldy ? 2 :
2378 x == lev_fieldx && y == lev_fieldy ? 3 :
2379 x == -1 || x == lev_fieldx ? 4 :
2380 y == -1 || y == lev_fieldy ? 5 : 6);
2382 return border[steel_position][steel_type];
2385 void DrawScreenElement(int x, int y, int element)
2387 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2388 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2391 void DrawLevelElement(int x, int y, int element)
2393 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2394 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2397 void DrawScreenField(int x, int y)
2399 int lx = LEVELX(x), ly = LEVELY(y);
2400 int element, content;
2402 if (!IN_LEV_FIELD(lx, ly))
2404 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2407 element = getBorderElement(lx, ly);
2409 DrawScreenElement(x, y, element);
2414 element = Tile[lx][ly];
2415 content = Store[lx][ly];
2417 if (IS_MOVING(lx, ly))
2419 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2420 boolean cut_mode = NO_CUTTING;
2422 if (element == EL_QUICKSAND_EMPTYING ||
2423 element == EL_QUICKSAND_FAST_EMPTYING ||
2424 element == EL_MAGIC_WALL_EMPTYING ||
2425 element == EL_BD_MAGIC_WALL_EMPTYING ||
2426 element == EL_DC_MAGIC_WALL_EMPTYING ||
2427 element == EL_AMOEBA_DROPPING)
2428 cut_mode = CUT_ABOVE;
2429 else if (element == EL_QUICKSAND_FILLING ||
2430 element == EL_QUICKSAND_FAST_FILLING ||
2431 element == EL_MAGIC_WALL_FILLING ||
2432 element == EL_BD_MAGIC_WALL_FILLING ||
2433 element == EL_DC_MAGIC_WALL_FILLING)
2434 cut_mode = CUT_BELOW;
2436 if (cut_mode == CUT_ABOVE)
2437 DrawScreenElement(x, y, element);
2439 DrawScreenElement(x, y, EL_EMPTY);
2442 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2443 else if (cut_mode == NO_CUTTING)
2444 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2447 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2449 if (cut_mode == CUT_BELOW &&
2450 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2451 DrawLevelElement(lx, ly + 1, element);
2454 if (content == EL_ACID)
2456 int dir = MovDir[lx][ly];
2457 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2458 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2460 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2462 // prevent target field from being drawn again (but without masking)
2463 // (this would happen if target field is scanned after moving element)
2464 Stop[newlx][newly] = TRUE;
2467 else if (IS_BLOCKED(lx, ly))
2472 boolean cut_mode = NO_CUTTING;
2473 int element_old, content_old;
2475 Blocked2Moving(lx, ly, &oldx, &oldy);
2478 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2479 MovDir[oldx][oldy] == MV_RIGHT);
2481 element_old = Tile[oldx][oldy];
2482 content_old = Store[oldx][oldy];
2484 if (element_old == EL_QUICKSAND_EMPTYING ||
2485 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2486 element_old == EL_MAGIC_WALL_EMPTYING ||
2487 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2488 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2489 element_old == EL_AMOEBA_DROPPING)
2490 cut_mode = CUT_ABOVE;
2492 DrawScreenElement(x, y, EL_EMPTY);
2495 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2497 else if (cut_mode == NO_CUTTING)
2498 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2501 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2504 else if (IS_DRAWABLE(element))
2505 DrawScreenElement(x, y, element);
2507 DrawScreenElement(x, y, EL_EMPTY);
2510 void DrawLevelField(int x, int y)
2512 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2513 DrawScreenField(SCREENX(x), SCREENY(y));
2514 else if (IS_MOVING(x, y))
2518 Moving2Blocked(x, y, &newx, &newy);
2519 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2520 DrawScreenField(SCREENX(newx), SCREENY(newy));
2522 else if (IS_BLOCKED(x, y))
2526 Blocked2Moving(x, y, &oldx, &oldy);
2527 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2528 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2532 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2533 int (*el2img_function)(int), boolean masked,
2534 int element_bits_draw)
2536 int element_base = map_mm_wall_element(element);
2537 int element_bits = (IS_DF_WALL(element) ?
2538 element - EL_DF_WALL_START :
2539 IS_MM_WALL(element) ?
2540 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2541 int graphic = el2img_function(element_base);
2542 int tilesize_draw = tilesize / 2;
2547 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2549 for (i = 0; i < 4; i++)
2551 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2552 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2554 if (!(element_bits_draw & (1 << i)))
2557 if (element_bits & (1 << i))
2560 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2561 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2563 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2564 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2569 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2570 tilesize_draw, tilesize_draw);
2575 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2576 boolean masked, int element_bits_draw)
2578 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2579 element, tilesize, el2edimg, masked, element_bits_draw);
2582 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2583 int (*el2img_function)(int))
2585 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2589 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2592 if (IS_MM_WALL(element))
2594 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2595 element, tilesize, el2edimg, masked, 0x000f);
2599 int graphic = el2edimg(element);
2602 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2604 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2608 void DrawSizedElement(int x, int y, int element, int tilesize)
2610 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2613 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2615 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2618 void DrawMiniElement(int x, int y, int element)
2622 graphic = el2edimg(element);
2623 DrawMiniGraphic(x, y, graphic);
2626 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2629 int x = sx + scroll_x, y = sy + scroll_y;
2631 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2632 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2633 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2634 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2636 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2639 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2641 int x = sx + scroll_x, y = sy + scroll_y;
2643 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2644 DrawMiniElement(sx, sy, EL_EMPTY);
2645 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2646 DrawMiniElement(sx, sy, Tile[x][y]);
2648 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2651 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2652 int x, int y, int xsize, int ysize,
2653 int tile_width, int tile_height)
2657 int dst_x = startx + x * tile_width;
2658 int dst_y = starty + y * tile_height;
2659 int width = graphic_info[graphic].width;
2660 int height = graphic_info[graphic].height;
2661 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2662 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2663 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2664 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2665 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2666 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2667 boolean draw_masked = graphic_info[graphic].draw_masked;
2669 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2671 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2673 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2677 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2678 inner_sx + (x - 1) * tile_width % inner_width);
2679 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2680 inner_sy + (y - 1) * tile_height % inner_height);
2683 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2686 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2690 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2691 int x, int y, int xsize, int ysize,
2694 int font_width = getFontWidth(font_nr);
2695 int font_height = getFontHeight(font_nr);
2697 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2698 font_width, font_height);
2701 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2703 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2704 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2705 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2706 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2707 boolean no_delay = (tape.warp_forward);
2708 unsigned int anim_delay = 0;
2709 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2710 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2711 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2712 int font_width = getFontWidth(font_nr);
2713 int font_height = getFontHeight(font_nr);
2714 int max_xsize = level.envelope[envelope_nr].xsize;
2715 int max_ysize = level.envelope[envelope_nr].ysize;
2716 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2717 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2718 int xend = max_xsize;
2719 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2720 int xstep = (xstart < xend ? 1 : 0);
2721 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2723 int end = MAX(xend - xstart, yend - ystart);
2726 for (i = start; i <= end; i++)
2728 int last_frame = end; // last frame of this "for" loop
2729 int x = xstart + i * xstep;
2730 int y = ystart + i * ystep;
2731 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2732 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2733 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2734 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2737 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2739 BlitScreenToBitmap(backbuffer);
2741 SetDrawtoField(DRAW_TO_BACKBUFFER);
2743 for (yy = 0; yy < ysize; yy++)
2744 for (xx = 0; xx < xsize; xx++)
2745 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2747 DrawTextBuffer(sx + font_width, sy + font_height,
2748 level.envelope[envelope_nr].text, font_nr, max_xsize,
2749 xsize - 2, ysize - 2, 0, mask_mode,
2750 level.envelope[envelope_nr].autowrap,
2751 level.envelope[envelope_nr].centered, FALSE);
2753 redraw_mask |= REDRAW_FIELD;
2756 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2759 ClearAutoRepeatKeyEvents();
2762 void ShowEnvelope(int envelope_nr)
2764 int element = EL_ENVELOPE_1 + envelope_nr;
2765 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2766 int sound_opening = element_info[element].sound[ACTION_OPENING];
2767 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2768 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2769 boolean no_delay = (tape.warp_forward);
2770 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2771 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2772 int anim_mode = graphic_info[graphic].anim_mode;
2773 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2774 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2775 boolean overlay_enabled = GetOverlayEnabled();
2777 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2779 SetOverlayEnabled(FALSE);
2782 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2784 if (anim_mode == ANIM_DEFAULT)
2785 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2787 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2790 Delay_WithScreenUpdates(wait_delay_value);
2792 WaitForEventToContinue();
2795 SetOverlayEnabled(overlay_enabled);
2797 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2799 if (anim_mode != ANIM_NONE)
2800 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2802 if (anim_mode == ANIM_DEFAULT)
2803 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2805 game.envelope_active = FALSE;
2807 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2809 redraw_mask |= REDRAW_FIELD;
2813 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2814 int xsize, int ysize)
2816 if (!global.use_envelope_request ||
2817 request.sort_priority <= 0)
2820 if (request.bitmap == NULL ||
2821 xsize > request.xsize ||
2822 ysize > request.ysize)
2824 if (request.bitmap != NULL)
2825 FreeBitmap(request.bitmap);
2827 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2829 SDL_Surface *surface = request.bitmap->surface;
2831 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2832 Fail("SDLGetNativeSurface() failed");
2835 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2837 SDLFreeBitmapTextures(request.bitmap);
2838 SDLCreateBitmapTextures(request.bitmap);
2840 // set envelope request run-time values
2843 request.xsize = xsize;
2844 request.ysize = ysize;
2847 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2849 if (global.use_envelope_request &&
2850 game.request_active_or_moving &&
2851 request.sort_priority > 0 &&
2852 drawing_target == DRAW_TO_SCREEN &&
2853 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2855 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2856 request.sx, request.sy);
2860 static void setRequestBasePosition(int *x, int *y)
2862 int sx_base, sy_base;
2864 if (request.x != -1)
2865 sx_base = request.x;
2866 else if (request.align == ALIGN_LEFT)
2868 else if (request.align == ALIGN_RIGHT)
2869 sx_base = SX + SXSIZE;
2871 sx_base = SX + SXSIZE / 2;
2873 if (request.y != -1)
2874 sy_base = request.y;
2875 else if (request.valign == VALIGN_TOP)
2877 else if (request.valign == VALIGN_BOTTOM)
2878 sy_base = SY + SYSIZE;
2880 sy_base = SY + SYSIZE / 2;
2886 static void setRequestPositionExt(int *x, int *y, int width, int height,
2887 boolean add_border_size)
2889 int border_size = request.border_size;
2890 int sx_base, sy_base;
2893 setRequestBasePosition(&sx_base, &sy_base);
2895 if (request.align == ALIGN_LEFT)
2897 else if (request.align == ALIGN_RIGHT)
2898 sx = sx_base - width;
2900 sx = sx_base - width / 2;
2902 if (request.valign == VALIGN_TOP)
2904 else if (request.valign == VALIGN_BOTTOM)
2905 sy = sy_base - height;
2907 sy = sy_base - height / 2;
2909 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2910 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2912 if (add_border_size)
2922 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2924 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2927 static void DrawEnvelopeRequest(char *text)
2929 char *text_final = text;
2930 char *text_door_style = NULL;
2931 int graphic = IMG_BACKGROUND_REQUEST;
2932 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2933 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2934 int font_nr = FONT_REQUEST;
2935 int font_width = getFontWidth(font_nr);
2936 int font_height = getFontHeight(font_nr);
2937 int border_size = request.border_size;
2938 int line_spacing = request.line_spacing;
2939 int line_height = font_height + line_spacing;
2940 int max_text_width = request.width - 2 * border_size;
2941 int max_text_height = request.height - 2 * border_size;
2942 int line_length = max_text_width / font_width;
2943 int max_lines = max_text_height / line_height;
2944 int text_width = line_length * font_width;
2945 int width = request.width;
2946 int height = request.height;
2947 int tile_size = MAX(request.step_offset, 1);
2948 int x_steps = width / tile_size;
2949 int y_steps = height / tile_size;
2950 int sx_offset = border_size;
2951 int sy_offset = border_size;
2955 if (request.centered)
2956 sx_offset = (request.width - text_width) / 2;
2958 if (request.wrap_single_words && !request.autowrap)
2960 char *src_text_ptr, *dst_text_ptr;
2962 text_door_style = checked_malloc(2 * strlen(text) + 1);
2964 src_text_ptr = text;
2965 dst_text_ptr = text_door_style;
2967 while (*src_text_ptr)
2969 if (*src_text_ptr == ' ' ||
2970 *src_text_ptr == '?' ||
2971 *src_text_ptr == '!')
2972 *dst_text_ptr++ = '\n';
2974 if (*src_text_ptr != ' ')
2975 *dst_text_ptr++ = *src_text_ptr;
2980 *dst_text_ptr = '\0';
2982 text_final = text_door_style;
2985 setRequestPosition(&sx, &sy, FALSE);
2987 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2989 for (y = 0; y < y_steps; y++)
2990 for (x = 0; x < x_steps; x++)
2991 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2992 x, y, x_steps, y_steps,
2993 tile_size, tile_size);
2995 // force DOOR font inside door area
2996 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2998 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2999 line_length, -1, max_lines, line_spacing, mask_mode,
3000 request.autowrap, request.centered, FALSE);
3004 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3005 RedrawGadget(tool_gadget[i]);
3007 // store readily prepared envelope request for later use when animating
3008 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3010 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3012 if (text_door_style)
3013 free(text_door_style);
3016 static void AnimateEnvelopeRequest(int anim_mode, int action)
3018 int graphic = IMG_BACKGROUND_REQUEST;
3019 boolean draw_masked = graphic_info[graphic].draw_masked;
3020 int delay_value_normal = request.step_delay;
3021 int delay_value_fast = delay_value_normal / 2;
3022 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3023 boolean no_delay = (tape.warp_forward);
3024 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3025 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3026 unsigned int anim_delay = 0;
3028 int tile_size = MAX(request.step_offset, 1);
3029 int max_xsize = request.width / tile_size;
3030 int max_ysize = request.height / tile_size;
3031 int max_xsize_inner = max_xsize - 2;
3032 int max_ysize_inner = max_ysize - 2;
3034 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3035 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3036 int xend = max_xsize_inner;
3037 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3038 int xstep = (xstart < xend ? 1 : 0);
3039 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3041 int end = MAX(xend - xstart, yend - ystart);
3044 if (setup.quick_doors)
3051 for (i = start; i <= end; i++)
3053 int last_frame = end; // last frame of this "for" loop
3054 int x = xstart + i * xstep;
3055 int y = ystart + i * ystep;
3056 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3057 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3058 int xsize_size_left = (xsize - 1) * tile_size;
3059 int ysize_size_top = (ysize - 1) * tile_size;
3060 int max_xsize_pos = (max_xsize - 1) * tile_size;
3061 int max_ysize_pos = (max_ysize - 1) * tile_size;
3062 int width = xsize * tile_size;
3063 int height = ysize * tile_size;
3068 setRequestPosition(&src_x, &src_y, FALSE);
3069 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3071 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3073 for (yy = 0; yy < 2; yy++)
3075 for (xx = 0; xx < 2; xx++)
3077 int src_xx = src_x + xx * max_xsize_pos;
3078 int src_yy = src_y + yy * max_ysize_pos;
3079 int dst_xx = dst_x + xx * xsize_size_left;
3080 int dst_yy = dst_y + yy * ysize_size_top;
3081 int xx_size = (xx ? tile_size : xsize_size_left);
3082 int yy_size = (yy ? tile_size : ysize_size_top);
3085 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3086 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3088 BlitBitmap(bitmap_db_store_2, backbuffer,
3089 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3093 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3095 redraw_mask |= REDRAW_FIELD;
3099 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3102 ClearAutoRepeatKeyEvents();
3105 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3107 int graphic = IMG_BACKGROUND_REQUEST;
3108 int sound_opening = SND_REQUEST_OPENING;
3109 int sound_closing = SND_REQUEST_CLOSING;
3110 int anim_mode_1 = request.anim_mode; // (higher priority)
3111 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3112 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3113 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3114 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3116 if (game_status == GAME_MODE_PLAYING)
3117 BlitScreenToBitmap(backbuffer);
3119 SetDrawtoField(DRAW_TO_BACKBUFFER);
3121 // SetDrawBackgroundMask(REDRAW_NONE);
3123 if (action == ACTION_OPENING)
3125 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3127 if (req_state & REQ_ASK)
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3132 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3134 else if (req_state & REQ_CONFIRM)
3136 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3139 else if (req_state & REQ_PLAYER)
3141 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3142 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3143 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3144 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3147 DrawEnvelopeRequest(text);
3150 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3152 if (action == ACTION_OPENING)
3154 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3156 if (anim_mode == ANIM_DEFAULT)
3157 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3159 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3163 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3165 if (anim_mode != ANIM_NONE)
3166 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3168 if (anim_mode == ANIM_DEFAULT)
3169 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3172 game.envelope_active = FALSE;
3174 if (action == ACTION_CLOSING)
3175 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3177 // SetDrawBackgroundMask(last_draw_background_mask);
3179 redraw_mask |= REDRAW_FIELD;
3183 if (action == ACTION_CLOSING &&
3184 game_status == GAME_MODE_PLAYING &&
3185 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3186 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3189 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3191 if (IS_MM_WALL(element))
3193 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3199 int graphic = el2preimg(element);
3201 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3202 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3207 void DrawLevel(int draw_background_mask)
3211 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3212 SetDrawBackgroundMask(draw_background_mask);
3216 for (x = BX1; x <= BX2; x++)
3217 for (y = BY1; y <= BY2; y++)
3218 DrawScreenField(x, y);
3220 redraw_mask |= REDRAW_FIELD;
3223 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3228 for (x = 0; x < size_x; x++)
3229 for (y = 0; y < size_y; y++)
3230 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3232 redraw_mask |= REDRAW_FIELD;
3235 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3239 for (x = 0; x < size_x; x++)
3240 for (y = 0; y < size_y; y++)
3241 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3243 redraw_mask |= REDRAW_FIELD;
3246 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3248 boolean show_level_border = (BorderElement != EL_EMPTY);
3249 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3250 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3251 int tile_size = preview.tile_size;
3252 int preview_width = preview.xsize * tile_size;
3253 int preview_height = preview.ysize * tile_size;
3254 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3255 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3256 int real_preview_width = real_preview_xsize * tile_size;
3257 int real_preview_height = real_preview_ysize * tile_size;
3258 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3259 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3262 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3265 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3267 dst_x += (preview_width - real_preview_width) / 2;
3268 dst_y += (preview_height - real_preview_height) / 2;
3270 for (x = 0; x < real_preview_xsize; x++)
3272 for (y = 0; y < real_preview_ysize; y++)
3274 int lx = from_x + x + (show_level_border ? -1 : 0);
3275 int ly = from_y + y + (show_level_border ? -1 : 0);
3276 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3277 getBorderElement(lx, ly));
3279 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3280 element, tile_size);
3284 redraw_mask |= REDRAW_FIELD;
3287 #define MICROLABEL_EMPTY 0
3288 #define MICROLABEL_LEVEL_NAME 1
3289 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3290 #define MICROLABEL_LEVEL_AUTHOR 3
3291 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3292 #define MICROLABEL_IMPORTED_FROM 5
3293 #define MICROLABEL_IMPORTED_BY_HEAD 6
3294 #define MICROLABEL_IMPORTED_BY 7
3296 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3298 int max_text_width = SXSIZE;
3299 int font_width = getFontWidth(font_nr);
3301 if (pos->align == ALIGN_CENTER)
3302 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3303 else if (pos->align == ALIGN_RIGHT)
3304 max_text_width = pos->x;
3306 max_text_width = SXSIZE - pos->x;
3308 return max_text_width / font_width;
3311 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3313 char label_text[MAX_OUTPUT_LINESIZE + 1];
3314 int max_len_label_text;
3315 int font_nr = pos->font;
3318 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3321 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3322 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3323 mode == MICROLABEL_IMPORTED_BY_HEAD)
3324 font_nr = pos->font_alt;
3326 max_len_label_text = getMaxTextLength(pos, font_nr);
3328 if (pos->size != -1)
3329 max_len_label_text = pos->size;
3331 for (i = 0; i < max_len_label_text; i++)
3332 label_text[i] = ' ';
3333 label_text[max_len_label_text] = '\0';
3335 if (strlen(label_text) > 0)
3336 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3339 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3340 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3341 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3342 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3343 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3344 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3345 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3346 max_len_label_text);
3347 label_text[max_len_label_text] = '\0';
3349 if (strlen(label_text) > 0)
3350 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3352 redraw_mask |= REDRAW_FIELD;
3355 static void DrawPreviewLevelLabel(int mode)
3357 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3360 static void DrawPreviewLevelInfo(int mode)
3362 if (mode == MICROLABEL_LEVEL_NAME)
3363 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3364 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3365 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3368 static void DrawPreviewLevelExt(boolean restart)
3370 static unsigned int scroll_delay = 0;
3371 static unsigned int label_delay = 0;
3372 static int from_x, from_y, scroll_direction;
3373 static int label_state, label_counter;
3374 unsigned int scroll_delay_value = preview.step_delay;
3375 boolean show_level_border = (BorderElement != EL_EMPTY);
3376 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3377 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3384 if (preview.anim_mode == ANIM_CENTERED)
3386 if (level_xsize > preview.xsize)
3387 from_x = (level_xsize - preview.xsize) / 2;
3388 if (level_ysize > preview.ysize)
3389 from_y = (level_ysize - preview.ysize) / 2;
3392 from_x += preview.xoffset;
3393 from_y += preview.yoffset;
3395 scroll_direction = MV_RIGHT;
3399 DrawPreviewLevelPlayfield(from_x, from_y);
3400 DrawPreviewLevelLabel(label_state);
3402 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3403 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3405 // initialize delay counters
3406 DelayReached(&scroll_delay, 0);
3407 DelayReached(&label_delay, 0);
3409 if (leveldir_current->name)
3411 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3412 char label_text[MAX_OUTPUT_LINESIZE + 1];
3413 int font_nr = pos->font;
3414 int max_len_label_text = getMaxTextLength(pos, font_nr);
3416 if (pos->size != -1)
3417 max_len_label_text = pos->size;
3419 strncpy(label_text, leveldir_current->name, max_len_label_text);
3420 label_text[max_len_label_text] = '\0';
3422 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3423 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3429 // scroll preview level, if needed
3430 if (preview.anim_mode != ANIM_NONE &&
3431 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3432 DelayReached(&scroll_delay, scroll_delay_value))
3434 switch (scroll_direction)
3439 from_x -= preview.step_offset;
3440 from_x = (from_x < 0 ? 0 : from_x);
3443 scroll_direction = MV_UP;
3447 if (from_x < level_xsize - preview.xsize)
3449 from_x += preview.step_offset;
3450 from_x = (from_x > level_xsize - preview.xsize ?
3451 level_xsize - preview.xsize : from_x);
3454 scroll_direction = MV_DOWN;
3460 from_y -= preview.step_offset;
3461 from_y = (from_y < 0 ? 0 : from_y);
3464 scroll_direction = MV_RIGHT;
3468 if (from_y < level_ysize - preview.ysize)
3470 from_y += preview.step_offset;
3471 from_y = (from_y > level_ysize - preview.ysize ?
3472 level_ysize - preview.ysize : from_y);
3475 scroll_direction = MV_LEFT;
3482 DrawPreviewLevelPlayfield(from_x, from_y);
3485 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3486 // redraw micro level label, if needed
3487 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3488 !strEqual(level.author, ANONYMOUS_NAME) &&
3489 !strEqual(level.author, leveldir_current->name) &&
3490 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3492 int max_label_counter = 23;
3494 if (leveldir_current->imported_from != NULL &&
3495 strlen(leveldir_current->imported_from) > 0)
3496 max_label_counter += 14;
3497 if (leveldir_current->imported_by != NULL &&
3498 strlen(leveldir_current->imported_by) > 0)
3499 max_label_counter += 14;
3501 label_counter = (label_counter + 1) % max_label_counter;
3502 label_state = (label_counter >= 0 && label_counter <= 7 ?
3503 MICROLABEL_LEVEL_NAME :
3504 label_counter >= 9 && label_counter <= 12 ?
3505 MICROLABEL_LEVEL_AUTHOR_HEAD :
3506 label_counter >= 14 && label_counter <= 21 ?
3507 MICROLABEL_LEVEL_AUTHOR :
3508 label_counter >= 23 && label_counter <= 26 ?
3509 MICROLABEL_IMPORTED_FROM_HEAD :
3510 label_counter >= 28 && label_counter <= 35 ?
3511 MICROLABEL_IMPORTED_FROM :
3512 label_counter >= 37 && label_counter <= 40 ?
3513 MICROLABEL_IMPORTED_BY_HEAD :
3514 label_counter >= 42 && label_counter <= 49 ?
3515 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3517 if (leveldir_current->imported_from == NULL &&
3518 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3519 label_state == MICROLABEL_IMPORTED_FROM))
3520 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3521 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3523 DrawPreviewLevelLabel(label_state);
3527 void DrawPreviewPlayers(void)
3529 if (game_status != GAME_MODE_MAIN)
3532 // do not draw preview players if level preview redefined, but players aren't
3533 if (preview.redefined && !menu.main.preview_players.redefined)
3536 boolean player_found[MAX_PLAYERS];
3537 int num_players = 0;
3540 for (i = 0; i < MAX_PLAYERS; i++)
3541 player_found[i] = FALSE;
3543 // check which players can be found in the level (simple approach)
3544 for (x = 0; x < lev_fieldx; x++)
3546 for (y = 0; y < lev_fieldy; y++)
3548 int element = level.field[x][y];
3550 if (ELEM_IS_PLAYER(element))
3552 int player_nr = GET_PLAYER_NR(element);
3554 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3556 if (!player_found[player_nr])
3559 player_found[player_nr] = TRUE;
3564 struct TextPosInfo *pos = &menu.main.preview_players;
3565 int tile_size = pos->tile_size;
3566 int border_size = pos->border_size;
3567 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3568 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3569 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3570 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3571 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3572 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3573 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3574 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3575 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3576 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3577 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3578 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3580 // clear area in which the players will be drawn
3581 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3582 max_players_width, max_players_height);
3584 if (!network.enabled && !setup.team_mode)
3587 // only draw players if level is suited for team mode
3588 if (num_players < 2)
3591 // draw all players that were found in the level
3592 for (i = 0; i < MAX_PLAYERS; i++)
3594 if (player_found[i])
3596 int graphic = el2img(EL_PLAYER_1 + i);
3598 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3600 xpos += player_xoffset;
3601 ypos += player_yoffset;
3606 void DrawPreviewLevelInitial(void)
3608 DrawPreviewLevelExt(TRUE);
3609 DrawPreviewPlayers();
3612 void DrawPreviewLevelAnimation(void)
3614 DrawPreviewLevelExt(FALSE);
3617 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3618 int border_size, int font_nr)
3620 int graphic = el2img(EL_PLAYER_1 + player_nr);
3621 int font_height = getFontHeight(font_nr);
3622 int player_height = MAX(tile_size, font_height);
3623 int xoffset_text = tile_size + border_size;
3624 int yoffset_text = (player_height - font_height) / 2;
3625 int yoffset_graphic = (player_height - tile_size) / 2;
3626 char *player_name = getNetworkPlayerName(player_nr + 1);
3628 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3630 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3633 static void DrawNetworkPlayersExt(boolean force)
3635 if (game_status != GAME_MODE_MAIN)
3638 if (!network.connected && !force)
3641 // do not draw network players if level preview redefined, but players aren't
3642 if (preview.redefined && !menu.main.network_players.redefined)
3645 int num_players = 0;
3648 for (i = 0; i < MAX_PLAYERS; i++)
3649 if (stored_player[i].connected_network)
3652 struct TextPosInfo *pos = &menu.main.network_players;
3653 int tile_size = pos->tile_size;
3654 int border_size = pos->border_size;
3655 int xoffset_text = tile_size + border_size;
3656 int font_nr = pos->font;
3657 int font_width = getFontWidth(font_nr);
3658 int font_height = getFontHeight(font_nr);
3659 int player_height = MAX(tile_size, font_height);
3660 int player_yoffset = player_height + border_size;
3661 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3662 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3663 int all_players_height = num_players * player_yoffset - border_size;
3664 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3665 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3666 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3668 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3669 max_players_width, max_players_height);
3671 // first draw local network player ...
3672 for (i = 0; i < MAX_PLAYERS; i++)
3674 if (stored_player[i].connected_network &&
3675 stored_player[i].connected_locally)
3677 char *player_name = getNetworkPlayerName(i + 1);
3678 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3679 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3681 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3683 ypos += player_yoffset;
3687 // ... then draw all other network players
3688 for (i = 0; i < MAX_PLAYERS; i++)
3690 if (stored_player[i].connected_network &&
3691 !stored_player[i].connected_locally)
3693 char *player_name = getNetworkPlayerName(i + 1);
3694 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3695 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3697 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3699 ypos += player_yoffset;
3704 void DrawNetworkPlayers(void)
3706 DrawNetworkPlayersExt(FALSE);
3709 void ClearNetworkPlayers(void)
3711 DrawNetworkPlayersExt(TRUE);
3714 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3715 int graphic, int sync_frame,
3718 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3720 if (mask_mode == USE_MASKING)
3721 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3723 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3726 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3727 int graphic, int sync_frame, int mask_mode)
3729 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3731 if (mask_mode == USE_MASKING)
3732 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3734 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3737 static void DrawGraphicAnimation(int x, int y, int graphic)
3739 int lx = LEVELX(x), ly = LEVELY(y);
3741 if (!IN_SCR_FIELD(x, y))
3744 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3745 graphic, GfxFrame[lx][ly], NO_MASKING);
3747 MarkTileDirty(x, y);
3750 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3752 int lx = LEVELX(x), ly = LEVELY(y);
3754 if (!IN_SCR_FIELD(x, y))
3757 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3758 graphic, GfxFrame[lx][ly], NO_MASKING);
3759 MarkTileDirty(x, y);
3762 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3764 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3767 void DrawLevelElementAnimation(int x, int y, int element)
3769 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3771 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3774 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3776 int sx = SCREENX(x), sy = SCREENY(y);
3778 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3781 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3784 DrawGraphicAnimation(sx, sy, graphic);
3787 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3788 DrawLevelFieldCrumbled(x, y);
3790 if (GFX_CRUMBLED(Tile[x][y]))
3791 DrawLevelFieldCrumbled(x, y);
3795 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3797 int sx = SCREENX(x), sy = SCREENY(y);
3800 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3803 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3805 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3808 DrawGraphicAnimation(sx, sy, graphic);
3810 if (GFX_CRUMBLED(element))
3811 DrawLevelFieldCrumbled(x, y);
3814 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3816 if (player->use_murphy)
3818 // this works only because currently only one player can be "murphy" ...
3819 static int last_horizontal_dir = MV_LEFT;
3820 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3822 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3823 last_horizontal_dir = move_dir;
3825 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3827 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3829 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3835 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3838 static boolean equalGraphics(int graphic1, int graphic2)
3840 struct GraphicInfo *g1 = &graphic_info[graphic1];
3841 struct GraphicInfo *g2 = &graphic_info[graphic2];
3843 return (g1->bitmap == g2->bitmap &&
3844 g1->src_x == g2->src_x &&
3845 g1->src_y == g2->src_y &&
3846 g1->anim_frames == g2->anim_frames &&
3847 g1->anim_delay == g2->anim_delay &&
3848 g1->anim_mode == g2->anim_mode);
3851 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3855 DRAW_PLAYER_STAGE_INIT = 0,
3856 DRAW_PLAYER_STAGE_LAST_FIELD,
3857 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3858 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3859 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3860 DRAW_PLAYER_STAGE_PLAYER,
3862 DRAW_PLAYER_STAGE_PLAYER,
3863 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3865 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3866 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3868 NUM_DRAW_PLAYER_STAGES
3871 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3873 static int static_last_player_graphic[MAX_PLAYERS];
3874 static int static_last_player_frame[MAX_PLAYERS];
3875 static boolean static_player_is_opaque[MAX_PLAYERS];
3876 static boolean draw_player[MAX_PLAYERS];
3877 int pnr = player->index_nr;
3879 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3881 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3882 static_last_player_frame[pnr] = player->Frame;
3883 static_player_is_opaque[pnr] = FALSE;
3885 draw_player[pnr] = TRUE;
3888 if (!draw_player[pnr])
3892 if (!IN_LEV_FIELD(player->jx, player->jy))
3894 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3895 Debug("draw:DrawPlayerExt", "This should never happen!");
3897 draw_player[pnr] = FALSE;
3903 int last_player_graphic = static_last_player_graphic[pnr];
3904 int last_player_frame = static_last_player_frame[pnr];
3905 boolean player_is_opaque = static_player_is_opaque[pnr];
3907 int jx = player->jx;
3908 int jy = player->jy;
3909 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3910 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3911 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3912 int last_jx = (player->is_moving ? jx - dx : jx);
3913 int last_jy = (player->is_moving ? jy - dy : jy);
3914 int next_jx = jx + dx;
3915 int next_jy = jy + dy;
3916 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3917 int sx = SCREENX(jx);
3918 int sy = SCREENY(jy);
3919 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3920 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3921 int element = Tile[jx][jy];
3922 int last_element = Tile[last_jx][last_jy];
3923 int action = (player->is_pushing ? ACTION_PUSHING :
3924 player->is_digging ? ACTION_DIGGING :
3925 player->is_collecting ? ACTION_COLLECTING :
3926 player->is_moving ? ACTION_MOVING :
3927 player->is_snapping ? ACTION_SNAPPING :
3928 player->is_dropping ? ACTION_DROPPING :
3929 player->is_waiting ? player->action_waiting :
3932 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3934 // ------------------------------------------------------------------------
3935 // initialize drawing the player
3936 // ------------------------------------------------------------------------
3938 draw_player[pnr] = FALSE;
3940 // GfxElement[][] is set to the element the player is digging or collecting;
3941 // remove also for off-screen player if the player is not moving anymore
3942 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3943 GfxElement[jx][jy] = EL_UNDEFINED;
3945 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3948 if (element == EL_EXPLOSION)
3951 InitPlayerGfxAnimation(player, action, move_dir);
3953 draw_player[pnr] = TRUE;
3955 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3957 // ------------------------------------------------------------------------
3958 // draw things in the field the player is leaving, if needed
3959 // ------------------------------------------------------------------------
3961 if (!IN_SCR_FIELD(sx, sy))
3962 draw_player[pnr] = FALSE;
3964 if (!player->is_moving)
3967 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3969 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3971 if (last_element == EL_DYNAMITE_ACTIVE ||
3972 last_element == EL_EM_DYNAMITE_ACTIVE ||
3973 last_element == EL_SP_DISK_RED_ACTIVE)
3974 DrawDynamite(last_jx, last_jy);
3976 DrawLevelFieldThruMask(last_jx, last_jy);
3978 else if (last_element == EL_DYNAMITE_ACTIVE ||
3979 last_element == EL_EM_DYNAMITE_ACTIVE ||
3980 last_element == EL_SP_DISK_RED_ACTIVE)
3981 DrawDynamite(last_jx, last_jy);
3983 DrawLevelField(last_jx, last_jy);
3985 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3986 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3988 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3990 // ------------------------------------------------------------------------
3991 // draw things behind the player, if needed
3992 // ------------------------------------------------------------------------
3996 DrawLevelElement(jx, jy, Back[jx][jy]);
4001 if (IS_ACTIVE_BOMB(element))
4003 DrawLevelElement(jx, jy, EL_EMPTY);
4008 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4010 int old_element = GfxElement[jx][jy];
4011 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4012 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4014 if (GFX_CRUMBLED(old_element))
4015 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4017 DrawGraphic(sx, sy, old_graphic, frame);
4019 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4020 static_player_is_opaque[pnr] = TRUE;
4024 GfxElement[jx][jy] = EL_UNDEFINED;
4026 // make sure that pushed elements are drawn with correct frame rate
4027 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4029 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4030 GfxFrame[jx][jy] = player->StepFrame;
4032 DrawLevelField(jx, jy);
4035 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4037 // ------------------------------------------------------------------------
4038 // draw things the player is pushing, if needed
4039 // ------------------------------------------------------------------------
4041 if (!player->is_pushing || !player->is_moving)
4044 int gfx_frame = GfxFrame[jx][jy];
4046 if (!IS_MOVING(jx, jy)) // push movement already finished
4048 element = Tile[next_jx][next_jy];
4049 gfx_frame = GfxFrame[next_jx][next_jy];
4052 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4053 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4054 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4056 // draw background element under pushed element (like the Sokoban field)
4057 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4059 // this allows transparent pushing animation over non-black background
4062 DrawLevelElement(jx, jy, Back[jx][jy]);
4064 DrawLevelElement(jx, jy, EL_EMPTY);
4066 if (Back[next_jx][next_jy])
4067 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4069 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4071 else if (Back[next_jx][next_jy])
4072 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4074 int px = SCREENX(jx), py = SCREENY(jy);
4075 int pxx = (TILEX - ABS(sxx)) * dx;
4076 int pyy = (TILEY - ABS(syy)) * dy;
4079 // do not draw (EM style) pushing animation when pushing is finished
4080 // (two-tile animations usually do not contain start and end frame)
4081 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4082 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4084 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4086 // masked drawing is needed for EMC style (double) movement graphics
4087 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4088 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4091 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4093 // ------------------------------------------------------------------------
4094 // draw player himself
4095 // ------------------------------------------------------------------------
4097 int graphic = getPlayerGraphic(player, move_dir);
4099 // in the case of changed player action or direction, prevent the current
4100 // animation frame from being restarted for identical animations
4101 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4102 player->Frame = last_player_frame;
4104 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4106 if (player_is_opaque)
4107 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4109 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4111 if (SHIELD_ON(player))
4113 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4114 IMG_SHIELD_NORMAL_ACTIVE);
4115 frame = getGraphicAnimationFrame(graphic, -1);
4117 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4120 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4122 // ------------------------------------------------------------------------
4123 // draw things in front of player (active dynamite or dynabombs)
4124 // ------------------------------------------------------------------------
4126 if (IS_ACTIVE_BOMB(element))
4128 int graphic = el2img(element);
4129 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4131 if (game.emulation == EMU_SUPAPLEX)
4132 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4134 DrawGraphicThruMask(sx, sy, graphic, frame);
4137 if (player_is_moving && last_element == EL_EXPLOSION)
4139 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4140 GfxElement[last_jx][last_jy] : EL_EMPTY);
4141 int graphic = el_act2img(element, ACTION_EXPLODING);
4142 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4143 int phase = ExplodePhase[last_jx][last_jy] - 1;
4144 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4147 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4150 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4152 // ------------------------------------------------------------------------
4153 // draw elements the player is just walking/passing through/under
4154 // ------------------------------------------------------------------------
4156 if (player_is_moving)
4158 // handle the field the player is leaving ...
4159 if (IS_ACCESSIBLE_INSIDE(last_element))
4160 DrawLevelField(last_jx, last_jy);
4161 else if (IS_ACCESSIBLE_UNDER(last_element))
4162 DrawLevelFieldThruMask(last_jx, last_jy);
4165 // do not redraw accessible elements if the player is just pushing them
4166 if (!player_is_moving || !player->is_pushing)
4168 // ... and the field the player is entering
4169 if (IS_ACCESSIBLE_INSIDE(element))
4170 DrawLevelField(jx, jy);
4171 else if (IS_ACCESSIBLE_UNDER(element))
4172 DrawLevelFieldThruMask(jx, jy);
4175 MarkTileDirty(sx, sy);
4179 void DrawPlayer(struct PlayerInfo *player)
4183 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4184 DrawPlayerExt(player, i);
4187 void DrawAllPlayers(void)
4191 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4192 for (j = 0; j < MAX_PLAYERS; j++)
4193 if (stored_player[j].active)
4194 DrawPlayerExt(&stored_player[j], i);
4197 void DrawPlayerField(int x, int y)
4199 if (!IS_PLAYER(x, y))
4202 DrawPlayer(PLAYERINFO(x, y));
4205 // ----------------------------------------------------------------------------
4207 void WaitForEventToContinue(void)
4209 boolean first_wait = TRUE;
4210 boolean still_wait = TRUE;
4212 if (program.headless)
4215 // simulate releasing mouse button over last gadget, if still pressed
4217 HandleGadgets(-1, -1, 0);
4219 button_status = MB_RELEASED;
4222 ClearPlayerAction();
4228 if (NextValidEvent(&event))
4232 case EVENT_BUTTONPRESS:
4233 case EVENT_FINGERPRESS:
4237 case EVENT_BUTTONRELEASE:
4238 case EVENT_FINGERRELEASE:
4239 still_wait = first_wait;
4242 case EVENT_KEYPRESS:
4243 case SDL_CONTROLLERBUTTONDOWN:
4244 case SDL_JOYBUTTONDOWN:
4249 HandleOtherEvents(&event);
4253 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4258 if (!PendingEvent())
4263 #define MAX_REQUEST_LINES 13
4264 #define MAX_REQUEST_LINE_FONT1_LEN 7
4265 #define MAX_REQUEST_LINE_FONT2_LEN 10
4267 static int RequestHandleEvents(unsigned int req_state)
4269 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4271 int draw_buffer_last = GetDrawtoField();
4272 int width = request.width;
4273 int height = request.height;
4277 // when showing request dialog after game ended, deactivate game panel
4278 if (game_just_ended)
4279 game.panel.active = FALSE;
4281 game.request_active = TRUE;
4283 setRequestPosition(&sx, &sy, FALSE);
4285 button_status = MB_RELEASED;
4287 request_gadget_id = -1;
4292 boolean event_handled = FALSE;
4294 if (game_just_ended)
4296 SetDrawtoField(draw_buffer_last);
4298 HandleGameActions();
4300 SetDrawtoField(DRAW_TO_BACKBUFFER);
4302 if (global.use_envelope_request)
4304 // copy current state of request area to middle of playfield area
4305 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4313 while (NextValidEvent(&event))
4315 event_handled = TRUE;
4319 case EVENT_BUTTONPRESS:
4320 case EVENT_BUTTONRELEASE:
4321 case EVENT_MOTIONNOTIFY:
4325 if (event.type == EVENT_MOTIONNOTIFY)
4330 motion_status = TRUE;
4331 mx = ((MotionEvent *) &event)->x;
4332 my = ((MotionEvent *) &event)->y;
4336 motion_status = FALSE;
4337 mx = ((ButtonEvent *) &event)->x;
4338 my = ((ButtonEvent *) &event)->y;
4339 if (event.type == EVENT_BUTTONPRESS)
4340 button_status = ((ButtonEvent *) &event)->button;
4342 button_status = MB_RELEASED;
4345 // this sets 'request_gadget_id'
4346 HandleGadgets(mx, my, button_status);
4348 switch (request_gadget_id)
4350 case TOOL_CTRL_ID_YES:
4351 case TOOL_CTRL_ID_TOUCH_YES:
4354 case TOOL_CTRL_ID_NO:
4355 case TOOL_CTRL_ID_TOUCH_NO:
4358 case TOOL_CTRL_ID_CONFIRM:
4359 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4360 result = TRUE | FALSE;
4363 case TOOL_CTRL_ID_PLAYER_1:
4366 case TOOL_CTRL_ID_PLAYER_2:
4369 case TOOL_CTRL_ID_PLAYER_3:
4372 case TOOL_CTRL_ID_PLAYER_4:
4377 // only check clickable animations if no request gadget clicked
4378 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4385 case SDL_WINDOWEVENT:
4386 HandleWindowEvent((WindowEvent *) &event);
4389 case SDL_APP_WILLENTERBACKGROUND:
4390 case SDL_APP_DIDENTERBACKGROUND:
4391 case SDL_APP_WILLENTERFOREGROUND:
4392 case SDL_APP_DIDENTERFOREGROUND:
4393 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4396 case EVENT_KEYPRESS:
4398 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4403 if (req_state & REQ_CONFIRM)
4412 #if defined(KSYM_Rewind)
4413 case KSYM_Rewind: // for Amazon Fire TV remote
4422 #if defined(KSYM_FastForward)
4423 case KSYM_FastForward: // for Amazon Fire TV remote
4429 HandleKeysDebug(key, KEY_PRESSED);
4433 if (req_state & REQ_PLAYER)
4435 int old_player_nr = setup.network_player_nr;
4438 result = old_player_nr + 1;
4443 result = old_player_nr + 1;
4474 case EVENT_FINGERRELEASE:
4475 case EVENT_KEYRELEASE:
4476 ClearPlayerAction();
4479 case SDL_CONTROLLERBUTTONDOWN:
4480 switch (event.cbutton.button)
4482 case SDL_CONTROLLER_BUTTON_A:
4483 case SDL_CONTROLLER_BUTTON_X:
4484 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4485 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4489 case SDL_CONTROLLER_BUTTON_B:
4490 case SDL_CONTROLLER_BUTTON_Y:
4491 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4492 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4493 case SDL_CONTROLLER_BUTTON_BACK:
4498 if (req_state & REQ_PLAYER)
4500 int old_player_nr = setup.network_player_nr;
4503 result = old_player_nr + 1;
4505 switch (event.cbutton.button)
4507 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4508 case SDL_CONTROLLER_BUTTON_Y:
4512 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4513 case SDL_CONTROLLER_BUTTON_B:
4517 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4518 case SDL_CONTROLLER_BUTTON_A:
4522 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4523 case SDL_CONTROLLER_BUTTON_X:
4534 case SDL_CONTROLLERBUTTONUP:
4535 HandleJoystickEvent(&event);
4536 ClearPlayerAction();
4540 HandleOtherEvents(&event);
4545 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4547 int joy = AnyJoystick();
4549 if (joy & JOY_BUTTON_1)
4551 else if (joy & JOY_BUTTON_2)
4554 else if (AnyJoystick())
4556 int joy = AnyJoystick();
4558 if (req_state & REQ_PLAYER)
4562 else if (joy & JOY_RIGHT)
4564 else if (joy & JOY_DOWN)
4566 else if (joy & JOY_LEFT)
4573 if (game_just_ended)
4575 if (global.use_envelope_request)
4577 // copy back current state of pressed buttons inside request area
4578 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4582 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4588 SetDrawtoField(draw_buffer_last);
4590 game.request_active = FALSE;
4595 static boolean RequestDoor(char *text, unsigned int req_state)
4597 unsigned int old_door_state;
4598 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4599 int font_nr = FONT_TEXT_2;
4604 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4606 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4607 font_nr = FONT_TEXT_1;
4610 if (game_status == GAME_MODE_PLAYING)
4611 BlitScreenToBitmap(backbuffer);
4613 // disable deactivated drawing when quick-loading level tape recording
4614 if (tape.playing && tape.deactivate_display)
4615 TapeDeactivateDisplayOff(TRUE);
4617 SetMouseCursor(CURSOR_DEFAULT);
4619 // pause network game while waiting for request to answer
4620 if (network.enabled &&
4621 game_status == GAME_MODE_PLAYING &&
4622 !game.all_players_gone &&
4623 req_state & REQUEST_WAIT_FOR_INPUT)
4624 SendToServer_PausePlaying();
4626 old_door_state = GetDoorState();
4628 // simulate releasing mouse button over last gadget, if still pressed
4630 HandleGadgets(-1, -1, 0);
4634 // draw released gadget before proceeding
4637 if (old_door_state & DOOR_OPEN_1)
4639 CloseDoor(DOOR_CLOSE_1);
4641 // save old door content
4642 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4643 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4646 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4647 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4649 // clear door drawing field
4650 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4652 // force DOOR font inside door area
4653 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4655 // write text for request
4656 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4658 char text_line[max_request_line_len + 1];
4664 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4666 tc = *(text_ptr + tx);
4667 // if (!tc || tc == ' ')
4668 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4672 if ((tc == '?' || tc == '!') && tl == 0)
4682 strncpy(text_line, text_ptr, tl);
4685 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4686 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4687 text_line, font_nr);
4689 text_ptr += tl + (tc == ' ' ? 1 : 0);
4690 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4695 if (req_state & REQ_ASK)
4697 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4698 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4699 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4700 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4702 else if (req_state & REQ_CONFIRM)
4704 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4705 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4707 else if (req_state & REQ_PLAYER)
4709 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4710 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4711 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4712 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4715 // copy request gadgets to door backbuffer
4716 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4718 OpenDoor(DOOR_OPEN_1);
4720 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4722 if (game_status == GAME_MODE_PLAYING)
4724 SetPanelBackground();
4725 SetDrawBackgroundMask(REDRAW_DOOR_1);
4729 SetDrawBackgroundMask(REDRAW_FIELD);
4735 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4737 // ---------- handle request buttons ----------
4738 result = RequestHandleEvents(req_state);
4742 if (!(req_state & REQ_STAY_OPEN))
4744 CloseDoor(DOOR_CLOSE_1);
4746 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4747 (req_state & REQ_REOPEN))
4748 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4753 if (game_status == GAME_MODE_PLAYING)
4755 SetPanelBackground();
4756 SetDrawBackgroundMask(REDRAW_DOOR_1);
4760 SetDrawBackgroundMask(REDRAW_FIELD);
4763 // continue network game after request
4764 if (network.enabled &&
4765 game_status == GAME_MODE_PLAYING &&
4766 !game.all_players_gone &&
4767 req_state & REQUEST_WAIT_FOR_INPUT)
4768 SendToServer_ContinuePlaying();
4770 // restore deactivated drawing when quick-loading level tape recording
4771 if (tape.playing && tape.deactivate_display)
4772 TapeDeactivateDisplayOn();
4777 static boolean RequestEnvelope(char *text, unsigned int req_state)
4781 if (game_status == GAME_MODE_PLAYING)
4782 BlitScreenToBitmap(backbuffer);
4784 // disable deactivated drawing when quick-loading level tape recording
4785 if (tape.playing && tape.deactivate_display)
4786 TapeDeactivateDisplayOff(TRUE);
4788 SetMouseCursor(CURSOR_DEFAULT);
4790 // pause network game while waiting for request to answer
4791 if (network.enabled &&
4792 game_status == GAME_MODE_PLAYING &&
4793 !game.all_players_gone &&
4794 req_state & REQUEST_WAIT_FOR_INPUT)
4795 SendToServer_PausePlaying();
4797 // simulate releasing mouse button over last gadget, if still pressed
4799 HandleGadgets(-1, -1, 0);
4803 // (replace with setting corresponding request background)
4804 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4805 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4807 // clear door drawing field
4808 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4810 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4812 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4814 if (game_status == GAME_MODE_PLAYING)
4816 SetPanelBackground();
4817 SetDrawBackgroundMask(REDRAW_DOOR_1);
4821 SetDrawBackgroundMask(REDRAW_FIELD);
4827 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4829 // ---------- handle request buttons ----------
4830 result = RequestHandleEvents(req_state);
4834 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4838 if (game_status == GAME_MODE_PLAYING)
4840 SetPanelBackground();
4841 SetDrawBackgroundMask(REDRAW_DOOR_1);
4845 SetDrawBackgroundMask(REDRAW_FIELD);
4848 // continue network game after request
4849 if (network.enabled &&
4850 game_status == GAME_MODE_PLAYING &&
4851 !game.all_players_gone &&
4852 req_state & REQUEST_WAIT_FOR_INPUT)
4853 SendToServer_ContinuePlaying();
4855 // restore deactivated drawing when quick-loading level tape recording
4856 if (tape.playing && tape.deactivate_display)
4857 TapeDeactivateDisplayOn();
4862 boolean Request(char *text, unsigned int req_state)
4864 boolean overlay_enabled = GetOverlayEnabled();
4867 game.request_active_or_moving = TRUE;
4869 SetOverlayEnabled(FALSE);
4871 if (global.use_envelope_request)
4872 result = RequestEnvelope(text, req_state);
4874 result = RequestDoor(text, req_state);
4876 SetOverlayEnabled(overlay_enabled);
4878 game.request_active_or_moving = FALSE;
4883 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4885 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4886 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4889 if (dpo1->sort_priority != dpo2->sort_priority)
4890 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4892 compare_result = dpo1->nr - dpo2->nr;
4894 return compare_result;
4897 void InitGraphicCompatibilityInfo_Doors(void)
4903 struct DoorInfo *door;
4907 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4908 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4910 { -1, -1, -1, NULL }
4912 struct Rect door_rect_list[] =
4914 { DX, DY, DXSIZE, DYSIZE },
4915 { VX, VY, VXSIZE, VYSIZE }
4919 for (i = 0; doors[i].door_token != -1; i++)
4921 int door_token = doors[i].door_token;
4922 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4923 int part_1 = doors[i].part_1;
4924 int part_8 = doors[i].part_8;
4925 int part_2 = part_1 + 1;
4926 int part_3 = part_1 + 2;
4927 struct DoorInfo *door = doors[i].door;
4928 struct Rect *door_rect = &door_rect_list[door_index];
4929 boolean door_gfx_redefined = FALSE;
4931 // check if any door part graphic definitions have been redefined
4933 for (j = 0; door_part_controls[j].door_token != -1; j++)
4935 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4936 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4938 if (dpc->door_token == door_token && fi->redefined)
4939 door_gfx_redefined = TRUE;
4942 // check for old-style door graphic/animation modifications
4944 if (!door_gfx_redefined)
4946 if (door->anim_mode & ANIM_STATIC_PANEL)
4948 door->panel.step_xoffset = 0;
4949 door->panel.step_yoffset = 0;
4952 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4954 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4955 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4956 int num_door_steps, num_panel_steps;
4958 // remove door part graphics other than the two default wings
4960 for (j = 0; door_part_controls[j].door_token != -1; j++)
4962 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4963 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4965 if (dpc->graphic >= part_3 &&
4966 dpc->graphic <= part_8)
4970 // set graphics and screen positions of the default wings
4972 g_part_1->width = door_rect->width;
4973 g_part_1->height = door_rect->height;
4974 g_part_2->width = door_rect->width;
4975 g_part_2->height = door_rect->height;
4976 g_part_2->src_x = door_rect->width;
4977 g_part_2->src_y = g_part_1->src_y;
4979 door->part_2.x = door->part_1.x;
4980 door->part_2.y = door->part_1.y;
4982 if (door->width != -1)
4984 g_part_1->width = door->width;
4985 g_part_2->width = door->width;
4987 // special treatment for graphics and screen position of right wing
4988 g_part_2->src_x += door_rect->width - door->width;
4989 door->part_2.x += door_rect->width - door->width;
4992 if (door->height != -1)
4994 g_part_1->height = door->height;
4995 g_part_2->height = door->height;
4997 // special treatment for graphics and screen position of bottom wing
4998 g_part_2->src_y += door_rect->height - door->height;
4999 door->part_2.y += door_rect->height - door->height;
5002 // set animation delays for the default wings and panels
5004 door->part_1.step_delay = door->step_delay;
5005 door->part_2.step_delay = door->step_delay;
5006 door->panel.step_delay = door->step_delay;
5008 // set animation draw order for the default wings
5010 door->part_1.sort_priority = 2; // draw left wing over ...
5011 door->part_2.sort_priority = 1; // ... right wing
5013 // set animation draw offset for the default wings
5015 if (door->anim_mode & ANIM_HORIZONTAL)
5017 door->part_1.step_xoffset = door->step_offset;
5018 door->part_1.step_yoffset = 0;
5019 door->part_2.step_xoffset = door->step_offset * -1;
5020 door->part_2.step_yoffset = 0;
5022 num_door_steps = g_part_1->width / door->step_offset;
5024 else // ANIM_VERTICAL
5026 door->part_1.step_xoffset = 0;
5027 door->part_1.step_yoffset = door->step_offset;
5028 door->part_2.step_xoffset = 0;
5029 door->part_2.step_yoffset = door->step_offset * -1;
5031 num_door_steps = g_part_1->height / door->step_offset;
5034 // set animation draw offset for the default panels
5036 if (door->step_offset > 1)
5038 num_panel_steps = 2 * door_rect->height / door->step_offset;
5039 door->panel.start_step = num_panel_steps - num_door_steps;
5040 door->panel.start_step_closing = door->panel.start_step;
5044 num_panel_steps = door_rect->height / door->step_offset;
5045 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5046 door->panel.start_step_closing = door->panel.start_step;
5047 door->panel.step_delay *= 2;
5054 void InitDoors(void)
5058 for (i = 0; door_part_controls[i].door_token != -1; i++)
5060 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5061 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5063 // initialize "start_step_opening" and "start_step_closing", if needed
5064 if (dpc->pos->start_step_opening == 0 &&
5065 dpc->pos->start_step_closing == 0)
5067 // dpc->pos->start_step_opening = dpc->pos->start_step;
5068 dpc->pos->start_step_closing = dpc->pos->start_step;
5071 // fill structure for door part draw order (sorted below)
5073 dpo->sort_priority = dpc->pos->sort_priority;
5076 // sort door part controls according to sort_priority and graphic number
5077 qsort(door_part_order, MAX_DOOR_PARTS,
5078 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5081 unsigned int OpenDoor(unsigned int door_state)
5083 if (door_state & DOOR_COPY_BACK)
5085 if (door_state & DOOR_OPEN_1)
5086 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5087 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5089 if (door_state & DOOR_OPEN_2)
5090 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5091 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5093 door_state &= ~DOOR_COPY_BACK;
5096 return MoveDoor(door_state);
5099 unsigned int CloseDoor(unsigned int door_state)
5101 unsigned int old_door_state = GetDoorState();
5103 if (!(door_state & DOOR_NO_COPY_BACK))
5105 if (old_door_state & DOOR_OPEN_1)
5106 BlitBitmap(backbuffer, bitmap_db_door_1,
5107 DX, DY, DXSIZE, DYSIZE, 0, 0);
5109 if (old_door_state & DOOR_OPEN_2)
5110 BlitBitmap(backbuffer, bitmap_db_door_2,
5111 VX, VY, VXSIZE, VYSIZE, 0, 0);
5113 door_state &= ~DOOR_NO_COPY_BACK;
5116 return MoveDoor(door_state);
5119 unsigned int GetDoorState(void)
5121 return MoveDoor(DOOR_GET_STATE);
5124 unsigned int SetDoorState(unsigned int door_state)
5126 return MoveDoor(door_state | DOOR_SET_STATE);
5129 static int euclid(int a, int b)
5131 return (b ? euclid(b, a % b) : a);
5134 unsigned int MoveDoor(unsigned int door_state)
5136 struct Rect door_rect_list[] =
5138 { DX, DY, DXSIZE, DYSIZE },
5139 { VX, VY, VXSIZE, VYSIZE }
5141 static int door1 = DOOR_CLOSE_1;
5142 static int door2 = DOOR_CLOSE_2;
5143 unsigned int door_delay = 0;
5144 unsigned int door_delay_value;
5147 if (door_state == DOOR_GET_STATE)
5148 return (door1 | door2);
5150 if (door_state & DOOR_SET_STATE)
5152 if (door_state & DOOR_ACTION_1)
5153 door1 = door_state & DOOR_ACTION_1;
5154 if (door_state & DOOR_ACTION_2)
5155 door2 = door_state & DOOR_ACTION_2;
5157 return (door1 | door2);
5160 if (!(door_state & DOOR_FORCE_REDRAW))
5162 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5163 door_state &= ~DOOR_OPEN_1;
5164 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5165 door_state &= ~DOOR_CLOSE_1;
5166 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5167 door_state &= ~DOOR_OPEN_2;
5168 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5169 door_state &= ~DOOR_CLOSE_2;
5172 if (global.autoplay_leveldir)
5174 door_state |= DOOR_NO_DELAY;
5175 door_state &= ~DOOR_CLOSE_ALL;
5178 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5179 door_state |= DOOR_NO_DELAY;
5181 if (door_state & DOOR_ACTION)
5183 boolean door_panel_drawn[NUM_DOORS];
5184 boolean panel_has_doors[NUM_DOORS];
5185 boolean door_part_skip[MAX_DOOR_PARTS];
5186 boolean door_part_done[MAX_DOOR_PARTS];
5187 boolean door_part_done_all;
5188 int num_steps[MAX_DOOR_PARTS];
5189 int max_move_delay = 0; // delay for complete animations of all doors
5190 int max_step_delay = 0; // delay (ms) between two animation frames
5191 int num_move_steps = 0; // number of animation steps for all doors
5192 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5193 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5194 int current_move_delay = 0;
5198 for (i = 0; i < NUM_DOORS; i++)
5199 panel_has_doors[i] = FALSE;
5201 for (i = 0; i < MAX_DOOR_PARTS; i++)
5203 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5204 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5205 int door_token = dpc->door_token;
5207 door_part_done[i] = FALSE;
5208 door_part_skip[i] = (!(door_state & door_token) ||
5212 for (i = 0; i < MAX_DOOR_PARTS; i++)
5214 int nr = door_part_order[i].nr;
5215 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5216 struct DoorPartPosInfo *pos = dpc->pos;
5217 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5218 int door_token = dpc->door_token;
5219 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5220 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5221 int step_xoffset = ABS(pos->step_xoffset);
5222 int step_yoffset = ABS(pos->step_yoffset);
5223 int step_delay = pos->step_delay;
5224 int current_door_state = door_state & door_token;
5225 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5226 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5227 boolean part_opening = (is_panel ? door_closing : door_opening);
5228 int start_step = (part_opening ? pos->start_step_opening :
5229 pos->start_step_closing);
5230 float move_xsize = (step_xoffset ? g->width : 0);
5231 float move_ysize = (step_yoffset ? g->height : 0);
5232 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5233 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5234 int move_steps = (move_xsteps && move_ysteps ?
5235 MIN(move_xsteps, move_ysteps) :
5236 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5237 int move_delay = move_steps * step_delay;
5239 if (door_part_skip[nr])
5242 max_move_delay = MAX(max_move_delay, move_delay);
5243 max_step_delay = (max_step_delay == 0 ? step_delay :
5244 euclid(max_step_delay, step_delay));
5245 num_steps[nr] = move_steps;
5249 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5251 panel_has_doors[door_index] = TRUE;
5255 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5257 num_move_steps = max_move_delay / max_step_delay;
5258 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5260 door_delay_value = max_step_delay;
5262 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5264 start = num_move_steps - 1;
5268 // opening door sound has priority over simultaneously closing door
5269 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5271 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5273 if (door_state & DOOR_OPEN_1)
5274 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5275 if (door_state & DOOR_OPEN_2)
5276 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5278 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5280 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5282 if (door_state & DOOR_CLOSE_1)
5283 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5284 if (door_state & DOOR_CLOSE_2)
5285 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5289 for (k = start; k < num_move_steps; k++)
5291 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5293 door_part_done_all = TRUE;
5295 for (i = 0; i < NUM_DOORS; i++)
5296 door_panel_drawn[i] = FALSE;
5298 for (i = 0; i < MAX_DOOR_PARTS; i++)
5300 int nr = door_part_order[i].nr;
5301 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5302 struct DoorPartPosInfo *pos = dpc->pos;
5303 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5304 int door_token = dpc->door_token;
5305 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5306 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5307 boolean is_panel_and_door_has_closed = FALSE;
5308 struct Rect *door_rect = &door_rect_list[door_index];
5309 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5311 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5312 int current_door_state = door_state & door_token;
5313 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5314 boolean door_closing = !door_opening;
5315 boolean part_opening = (is_panel ? door_closing : door_opening);
5316 boolean part_closing = !part_opening;
5317 int start_step = (part_opening ? pos->start_step_opening :
5318 pos->start_step_closing);
5319 int step_delay = pos->step_delay;
5320 int step_factor = step_delay / max_step_delay;
5321 int k1 = (step_factor ? k / step_factor + 1 : k);
5322 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5323 int kk = MAX(0, k2);
5326 int src_x, src_y, src_xx, src_yy;
5327 int dst_x, dst_y, dst_xx, dst_yy;
5330 if (door_part_skip[nr])
5333 if (!(door_state & door_token))
5341 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5342 int kk_door = MAX(0, k2_door);
5343 int sync_frame = kk_door * door_delay_value;
5344 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5346 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5347 &g_src_x, &g_src_y);
5352 if (!door_panel_drawn[door_index])
5354 ClearRectangle(drawto, door_rect->x, door_rect->y,
5355 door_rect->width, door_rect->height);
5357 door_panel_drawn[door_index] = TRUE;
5360 // draw opening or closing door parts
5362 if (pos->step_xoffset < 0) // door part on right side
5365 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5368 if (dst_xx + width > door_rect->width)
5369 width = door_rect->width - dst_xx;
5371 else // door part on left side
5374 dst_xx = pos->x - kk * pos->step_xoffset;
5378 src_xx = ABS(dst_xx);
5382 width = g->width - src_xx;
5384 if (width > door_rect->width)
5385 width = door_rect->width;
5387 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5390 if (pos->step_yoffset < 0) // door part on bottom side
5393 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5396 if (dst_yy + height > door_rect->height)
5397 height = door_rect->height - dst_yy;
5399 else // door part on top side
5402 dst_yy = pos->y - kk * pos->step_yoffset;
5406 src_yy = ABS(dst_yy);
5410 height = g->height - src_yy;
5413 src_x = g_src_x + src_xx;
5414 src_y = g_src_y + src_yy;
5416 dst_x = door_rect->x + dst_xx;
5417 dst_y = door_rect->y + dst_yy;
5419 is_panel_and_door_has_closed =
5422 panel_has_doors[door_index] &&
5423 k >= num_move_steps_doors_only - 1);
5425 if (width >= 0 && width <= g->width &&
5426 height >= 0 && height <= g->height &&
5427 !is_panel_and_door_has_closed)
5429 if (is_panel || !pos->draw_masked)
5430 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5433 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5437 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5439 if ((part_opening && (width < 0 || height < 0)) ||
5440 (part_closing && (width >= g->width && height >= g->height)))
5441 door_part_done[nr] = TRUE;
5443 // continue door part animations, but not panel after door has closed
5444 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5445 door_part_done_all = FALSE;
5448 if (!(door_state & DOOR_NO_DELAY))
5452 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5454 current_move_delay += max_step_delay;
5456 // prevent OS (Windows) from complaining about program not responding
5460 if (door_part_done_all)
5464 if (!(door_state & DOOR_NO_DELAY))
5466 // wait for specified door action post delay
5467 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5468 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5469 else if (door_state & DOOR_ACTION_1)
5470 door_delay_value = door_1.post_delay;
5471 else if (door_state & DOOR_ACTION_2)
5472 door_delay_value = door_2.post_delay;
5474 while (!DelayReached(&door_delay, door_delay_value))
5479 if (door_state & DOOR_ACTION_1)
5480 door1 = door_state & DOOR_ACTION_1;
5481 if (door_state & DOOR_ACTION_2)
5482 door2 = door_state & DOOR_ACTION_2;
5484 // draw masked border over door area
5485 DrawMaskedBorder(REDRAW_DOOR_1);
5486 DrawMaskedBorder(REDRAW_DOOR_2);
5488 ClearAutoRepeatKeyEvents();
5490 return (door1 | door2);
5493 static boolean useSpecialEditorDoor(void)
5495 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5496 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5498 // do not draw special editor door if editor border defined or redefined
5499 if (graphic_info[graphic].bitmap != NULL || redefined)
5502 // do not draw special editor door if global border defined to be empty
5503 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5506 // do not draw special editor door if viewport definitions do not match
5510 EY + EYSIZE != VY + VYSIZE)
5516 void DrawSpecialEditorDoor(void)
5518 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5519 int top_border_width = gfx1->width;
5520 int top_border_height = gfx1->height;
5521 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5522 int ex = EX - outer_border;
5523 int ey = EY - outer_border;
5524 int vy = VY - outer_border;
5525 int exsize = EXSIZE + 2 * outer_border;
5527 if (!useSpecialEditorDoor())
5530 // draw bigger level editor toolbox window
5531 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5532 top_border_width, top_border_height, ex, ey - top_border_height);
5533 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5534 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5536 redraw_mask |= REDRAW_ALL;
5539 void UndrawSpecialEditorDoor(void)
5541 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5542 int top_border_width = gfx1->width;
5543 int top_border_height = gfx1->height;
5544 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5545 int ex = EX - outer_border;
5546 int ey = EY - outer_border;
5547 int ey_top = ey - top_border_height;
5548 int exsize = EXSIZE + 2 * outer_border;
5549 int eysize = EYSIZE + 2 * outer_border;
5551 if (!useSpecialEditorDoor())
5554 // draw normal tape recorder window
5555 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5557 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5558 ex, ey_top, top_border_width, top_border_height,
5560 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5561 ex, ey, exsize, eysize, ex, ey);
5565 // if screen background is set to "[NONE]", clear editor toolbox window
5566 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5567 ClearRectangle(drawto, ex, ey, exsize, eysize);
5570 redraw_mask |= REDRAW_ALL;
5574 // ---------- new tool button stuff -------------------------------------------
5579 struct TextPosInfo *pos;
5581 boolean is_touch_button;
5583 } toolbutton_info[NUM_TOOL_BUTTONS] =
5586 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5587 TOOL_CTRL_ID_YES, FALSE, "yes"
5590 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5591 TOOL_CTRL_ID_NO, FALSE, "no"
5594 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5595 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5598 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5599 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5602 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5603 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5606 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5607 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5610 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5611 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5614 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5615 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5618 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5619 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5622 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5623 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5627 void CreateToolButtons(void)
5631 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5633 int graphic = toolbutton_info[i].graphic;
5634 struct GraphicInfo *gfx = &graphic_info[graphic];
5635 struct TextPosInfo *pos = toolbutton_info[i].pos;
5636 struct GadgetInfo *gi;
5637 Bitmap *deco_bitmap = None;
5638 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5639 unsigned int event_mask = GD_EVENT_RELEASED;
5640 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5641 int base_x = (is_touch_button ? 0 : DX);
5642 int base_y = (is_touch_button ? 0 : DY);
5643 int gd_x = gfx->src_x;
5644 int gd_y = gfx->src_y;
5645 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5646 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5651 if (global.use_envelope_request && !is_touch_button)
5653 setRequestPosition(&base_x, &base_y, TRUE);
5655 // check if request buttons are outside of envelope and fix, if needed
5656 if (x < 0 || x + gfx->width > request.width ||
5657 y < 0 || y + gfx->height > request.height)
5659 if (id == TOOL_CTRL_ID_YES)
5662 y = request.height - 2 * request.border_size - gfx->height;
5664 else if (id == TOOL_CTRL_ID_NO)
5666 x = request.width - 2 * request.border_size - gfx->width;
5667 y = request.height - 2 * request.border_size - gfx->height;
5669 else if (id == TOOL_CTRL_ID_CONFIRM)
5671 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5672 y = request.height - 2 * request.border_size - gfx->height;
5674 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5676 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5678 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5679 y = request.height - 2 * request.border_size - gfx->height * 2;
5681 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5682 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5687 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5689 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5691 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5692 pos->size, &deco_bitmap, &deco_x, &deco_y);
5693 deco_xpos = (gfx->width - pos->size) / 2;
5694 deco_ypos = (gfx->height - pos->size) / 2;
5697 gi = CreateGadget(GDI_CUSTOM_ID, id,
5698 GDI_IMAGE_ID, graphic,
5699 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5702 GDI_WIDTH, gfx->width,
5703 GDI_HEIGHT, gfx->height,
5704 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5705 GDI_STATE, GD_BUTTON_UNPRESSED,
5706 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5707 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5708 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5709 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5710 GDI_DECORATION_SIZE, pos->size, pos->size,
5711 GDI_DECORATION_SHIFTING, 1, 1,
5712 GDI_DIRECT_DRAW, FALSE,
5713 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5714 GDI_EVENT_MASK, event_mask,
5715 GDI_CALLBACK_ACTION, HandleToolButtons,
5719 Fail("cannot create gadget");
5721 tool_gadget[id] = gi;
5725 void FreeToolButtons(void)
5729 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5730 FreeGadget(tool_gadget[i]);
5733 static void UnmapToolButtons(void)
5737 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5738 UnmapGadget(tool_gadget[i]);
5741 static void HandleToolButtons(struct GadgetInfo *gi)
5743 request_gadget_id = gi->custom_id;
5746 static struct Mapping_EM_to_RND_object
5749 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5750 boolean is_backside; // backside of moving element
5756 em_object_mapping_list[GAME_TILE_MAX + 1] =
5759 Zborder, FALSE, FALSE,
5763 Zplayer, FALSE, FALSE,
5772 Ztank, FALSE, FALSE,
5776 Zeater, FALSE, FALSE,
5780 Zdynamite, FALSE, FALSE,
5784 Zboom, FALSE, FALSE,
5789 Xchain, FALSE, FALSE,
5790 EL_DEFAULT, ACTION_EXPLODING, -1
5793 Xboom_bug, FALSE, FALSE,
5794 EL_BUG, ACTION_EXPLODING, -1
5797 Xboom_tank, FALSE, FALSE,
5798 EL_SPACESHIP, ACTION_EXPLODING, -1
5801 Xboom_android, FALSE, FALSE,
5802 EL_EMC_ANDROID, ACTION_OTHER, -1
5805 Xboom_1, FALSE, FALSE,
5806 EL_DEFAULT, ACTION_EXPLODING, -1
5809 Xboom_2, FALSE, FALSE,
5810 EL_DEFAULT, ACTION_EXPLODING, -1
5814 Xblank, TRUE, FALSE,
5819 Xsplash_e, FALSE, FALSE,
5820 EL_ACID_SPLASH_RIGHT, -1, -1
5823 Xsplash_w, FALSE, FALSE,
5824 EL_ACID_SPLASH_LEFT, -1, -1
5828 Xplant, TRUE, FALSE,
5829 EL_EMC_PLANT, -1, -1
5832 Yplant, FALSE, FALSE,
5833 EL_EMC_PLANT, -1, -1
5837 Xacid_1, TRUE, FALSE,
5841 Xacid_2, FALSE, FALSE,
5845 Xacid_3, FALSE, FALSE,
5849 Xacid_4, FALSE, FALSE,
5853 Xacid_5, FALSE, FALSE,
5857 Xacid_6, FALSE, FALSE,
5861 Xacid_7, FALSE, FALSE,
5865 Xacid_8, FALSE, FALSE,
5870 Xfake_acid_1, TRUE, FALSE,
5871 EL_EMC_FAKE_ACID, -1, -1
5874 Xfake_acid_2, FALSE, FALSE,
5875 EL_EMC_FAKE_ACID, -1, -1
5878 Xfake_acid_3, FALSE, FALSE,
5879 EL_EMC_FAKE_ACID, -1, -1
5882 Xfake_acid_4, FALSE, FALSE,
5883 EL_EMC_FAKE_ACID, -1, -1
5886 Xfake_acid_5, FALSE, FALSE,
5887 EL_EMC_FAKE_ACID, -1, -1
5890 Xfake_acid_6, FALSE, FALSE,
5891 EL_EMC_FAKE_ACID, -1, -1
5894 Xfake_acid_7, FALSE, FALSE,
5895 EL_EMC_FAKE_ACID, -1, -1
5898 Xfake_acid_8, FALSE, FALSE,
5899 EL_EMC_FAKE_ACID, -1, -1
5903 Xfake_acid_1_player, FALSE, FALSE,
5904 EL_EMC_FAKE_ACID, -1, -1
5907 Xfake_acid_2_player, FALSE, FALSE,
5908 EL_EMC_FAKE_ACID, -1, -1
5911 Xfake_acid_3_player, FALSE, FALSE,
5912 EL_EMC_FAKE_ACID, -1, -1
5915 Xfake_acid_4_player, FALSE, FALSE,
5916 EL_EMC_FAKE_ACID, -1, -1
5919 Xfake_acid_5_player, FALSE, FALSE,
5920 EL_EMC_FAKE_ACID, -1, -1
5923 Xfake_acid_6_player, FALSE, FALSE,
5924 EL_EMC_FAKE_ACID, -1, -1
5927 Xfake_acid_7_player, FALSE, FALSE,
5928 EL_EMC_FAKE_ACID, -1, -1
5931 Xfake_acid_8_player, FALSE, FALSE,
5932 EL_EMC_FAKE_ACID, -1, -1
5936 Xgrass, TRUE, FALSE,
5937 EL_EMC_GRASS, -1, -1
5940 Ygrass_nB, FALSE, FALSE,
5941 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5944 Ygrass_eB, FALSE, FALSE,
5945 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5948 Ygrass_sB, FALSE, FALSE,
5949 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5952 Ygrass_wB, FALSE, FALSE,
5953 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5961 Ydirt_nB, FALSE, FALSE,
5962 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5965 Ydirt_eB, FALSE, FALSE,
5966 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5969 Ydirt_sB, FALSE, FALSE,
5970 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5973 Ydirt_wB, FALSE, FALSE,
5974 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5978 Xandroid, TRUE, FALSE,
5979 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5982 Xandroid_1_n, FALSE, FALSE,
5983 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5986 Xandroid_2_n, FALSE, FALSE,
5987 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5990 Xandroid_1_e, FALSE, FALSE,
5991 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5994 Xandroid_2_e, FALSE, FALSE,
5995 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5998 Xandroid_1_w, FALSE, FALSE,
5999 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6002 Xandroid_2_w, FALSE, FALSE,
6003 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6006 Xandroid_1_s, FALSE, FALSE,
6007 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6010 Xandroid_2_s, FALSE, FALSE,
6011 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6014 Yandroid_n, FALSE, FALSE,
6015 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6018 Yandroid_nB, FALSE, TRUE,
6019 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6022 Yandroid_ne, FALSE, FALSE,
6023 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6026 Yandroid_neB, FALSE, TRUE,
6027 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6030 Yandroid_e, FALSE, FALSE,
6031 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6034 Yandroid_eB, FALSE, TRUE,
6035 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6038 Yandroid_se, FALSE, FALSE,
6039 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6042 Yandroid_seB, FALSE, TRUE,
6043 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6046 Yandroid_s, FALSE, FALSE,
6047 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6050 Yandroid_sB, FALSE, TRUE,
6051 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6054 Yandroid_sw, FALSE, FALSE,
6055 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6058 Yandroid_swB, FALSE, TRUE,
6059 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6062 Yandroid_w, FALSE, FALSE,
6063 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6066 Yandroid_wB, FALSE, TRUE,
6067 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6070 Yandroid_nw, FALSE, FALSE,
6071 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6074 Yandroid_nwB, FALSE, TRUE,
6075 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6079 Xeater_n, TRUE, FALSE,
6080 EL_YAMYAM_UP, -1, -1
6083 Xeater_e, TRUE, FALSE,
6084 EL_YAMYAM_RIGHT, -1, -1
6087 Xeater_w, TRUE, FALSE,
6088 EL_YAMYAM_LEFT, -1, -1
6091 Xeater_s, TRUE, FALSE,
6092 EL_YAMYAM_DOWN, -1, -1
6095 Yeater_n, FALSE, FALSE,
6096 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6099 Yeater_nB, FALSE, TRUE,
6100 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6103 Yeater_e, FALSE, FALSE,
6104 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6107 Yeater_eB, FALSE, TRUE,
6108 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6111 Yeater_s, FALSE, FALSE,
6112 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6115 Yeater_sB, FALSE, TRUE,
6116 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6119 Yeater_w, FALSE, FALSE,
6120 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6123 Yeater_wB, FALSE, TRUE,
6124 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6127 Yeater_stone, FALSE, FALSE,
6128 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6131 Yeater_spring, FALSE, FALSE,
6132 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6136 Xalien, TRUE, FALSE,
6140 Xalien_pause, FALSE, FALSE,
6144 Yalien_n, FALSE, FALSE,
6145 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6148 Yalien_nB, FALSE, TRUE,
6149 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6152 Yalien_e, FALSE, FALSE,
6153 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6156 Yalien_eB, FALSE, TRUE,
6157 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6160 Yalien_s, FALSE, FALSE,
6161 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6164 Yalien_sB, FALSE, TRUE,
6165 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6168 Yalien_w, FALSE, FALSE,
6169 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6172 Yalien_wB, FALSE, TRUE,
6173 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6176 Yalien_stone, FALSE, FALSE,
6177 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6180 Yalien_spring, FALSE, FALSE,
6181 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6185 Xbug_1_n, TRUE, FALSE,
6189 Xbug_1_e, TRUE, FALSE,
6190 EL_BUG_RIGHT, -1, -1
6193 Xbug_1_s, TRUE, FALSE,
6197 Xbug_1_w, TRUE, FALSE,
6201 Xbug_2_n, FALSE, FALSE,
6205 Xbug_2_e, FALSE, FALSE,
6206 EL_BUG_RIGHT, -1, -1
6209 Xbug_2_s, FALSE, FALSE,
6213 Xbug_2_w, FALSE, FALSE,
6217 Ybug_n, FALSE, FALSE,
6218 EL_BUG, ACTION_MOVING, MV_BIT_UP
6221 Ybug_nB, FALSE, TRUE,
6222 EL_BUG, ACTION_MOVING, MV_BIT_UP
6225 Ybug_e, FALSE, FALSE,
6226 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6229 Ybug_eB, FALSE, TRUE,
6230 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6233 Ybug_s, FALSE, FALSE,
6234 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6237 Ybug_sB, FALSE, TRUE,
6238 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6241 Ybug_w, FALSE, FALSE,
6242 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6245 Ybug_wB, FALSE, TRUE,
6246 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6249 Ybug_w_n, FALSE, FALSE,
6250 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6253 Ybug_n_e, FALSE, FALSE,
6254 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6257 Ybug_e_s, FALSE, FALSE,
6258 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6261 Ybug_s_w, FALSE, FALSE,
6262 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6265 Ybug_e_n, FALSE, FALSE,
6266 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6269 Ybug_s_e, FALSE, FALSE,
6270 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6273 Ybug_w_s, FALSE, FALSE,
6274 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6277 Ybug_n_w, FALSE, FALSE,
6278 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6281 Ybug_stone, FALSE, FALSE,
6282 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6285 Ybug_spring, FALSE, FALSE,
6286 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6290 Xtank_1_n, TRUE, FALSE,
6291 EL_SPACESHIP_UP, -1, -1
6294 Xtank_1_e, TRUE, FALSE,
6295 EL_SPACESHIP_RIGHT, -1, -1
6298 Xtank_1_s, TRUE, FALSE,
6299 EL_SPACESHIP_DOWN, -1, -1
6302 Xtank_1_w, TRUE, FALSE,
6303 EL_SPACESHIP_LEFT, -1, -1
6306 Xtank_2_n, FALSE, FALSE,
6307 EL_SPACESHIP_UP, -1, -1
6310 Xtank_2_e, FALSE, FALSE,
6311 EL_SPACESHIP_RIGHT, -1, -1
6314 Xtank_2_s, FALSE, FALSE,
6315 EL_SPACESHIP_DOWN, -1, -1
6318 Xtank_2_w, FALSE, FALSE,
6319 EL_SPACESHIP_LEFT, -1, -1
6322 Ytank_n, FALSE, FALSE,
6323 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6326 Ytank_nB, FALSE, TRUE,
6327 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6330 Ytank_e, FALSE, FALSE,
6331 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6334 Ytank_eB, FALSE, TRUE,
6335 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6338 Ytank_s, FALSE, FALSE,
6339 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6342 Ytank_sB, FALSE, TRUE,
6343 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6346 Ytank_w, FALSE, FALSE,
6347 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6350 Ytank_wB, FALSE, TRUE,
6351 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6354 Ytank_w_n, FALSE, FALSE,
6355 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6358 Ytank_n_e, FALSE, FALSE,
6359 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6362 Ytank_e_s, FALSE, FALSE,
6363 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6366 Ytank_s_w, FALSE, FALSE,
6367 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6370 Ytank_e_n, FALSE, FALSE,
6371 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6374 Ytank_s_e, FALSE, FALSE,
6375 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6378 Ytank_w_s, FALSE, FALSE,
6379 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6382 Ytank_n_w, FALSE, FALSE,
6383 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6386 Ytank_stone, FALSE, FALSE,
6387 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6390 Ytank_spring, FALSE, FALSE,
6391 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6395 Xemerald, TRUE, FALSE,
6399 Xemerald_pause, FALSE, FALSE,
6403 Xemerald_fall, FALSE, FALSE,
6407 Xemerald_shine, FALSE, FALSE,
6408 EL_EMERALD, ACTION_TWINKLING, -1
6411 Yemerald_s, FALSE, FALSE,
6412 EL_EMERALD, ACTION_FALLING, -1
6415 Yemerald_sB, FALSE, TRUE,
6416 EL_EMERALD, ACTION_FALLING, -1
6419 Yemerald_e, FALSE, FALSE,
6420 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6423 Yemerald_eB, FALSE, TRUE,
6424 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6427 Yemerald_w, FALSE, FALSE,
6428 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6431 Yemerald_wB, FALSE, TRUE,
6432 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6435 Yemerald_blank, FALSE, FALSE,
6436 EL_EMERALD, ACTION_COLLECTING, -1
6440 Xdiamond, TRUE, FALSE,
6444 Xdiamond_pause, FALSE, FALSE,
6448 Xdiamond_fall, FALSE, FALSE,
6452 Xdiamond_shine, FALSE, FALSE,
6453 EL_DIAMOND, ACTION_TWINKLING, -1
6456 Ydiamond_s, FALSE, FALSE,
6457 EL_DIAMOND, ACTION_FALLING, -1
6460 Ydiamond_sB, FALSE, TRUE,
6461 EL_DIAMOND, ACTION_FALLING, -1
6464 Ydiamond_e, FALSE, FALSE,
6465 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6468 Ydiamond_eB, FALSE, TRUE,
6469 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6472 Ydiamond_w, FALSE, FALSE,
6473 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6476 Ydiamond_wB, FALSE, TRUE,
6477 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6480 Ydiamond_blank, FALSE, FALSE,
6481 EL_DIAMOND, ACTION_COLLECTING, -1
6484 Ydiamond_stone, FALSE, FALSE,
6485 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6489 Xstone, TRUE, FALSE,
6493 Xstone_pause, FALSE, FALSE,
6497 Xstone_fall, FALSE, FALSE,
6501 Ystone_s, FALSE, FALSE,
6502 EL_ROCK, ACTION_FALLING, -1
6505 Ystone_sB, FALSE, TRUE,
6506 EL_ROCK, ACTION_FALLING, -1
6509 Ystone_e, FALSE, FALSE,
6510 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6513 Ystone_eB, FALSE, TRUE,
6514 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6517 Ystone_w, FALSE, FALSE,
6518 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6521 Ystone_wB, FALSE, TRUE,
6522 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6530 Xbomb_pause, FALSE, FALSE,
6534 Xbomb_fall, FALSE, FALSE,
6538 Ybomb_s, FALSE, FALSE,
6539 EL_BOMB, ACTION_FALLING, -1
6542 Ybomb_sB, FALSE, TRUE,
6543 EL_BOMB, ACTION_FALLING, -1
6546 Ybomb_e, FALSE, FALSE,
6547 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6550 Ybomb_eB, FALSE, TRUE,
6551 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6554 Ybomb_w, FALSE, FALSE,
6555 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6558 Ybomb_wB, FALSE, TRUE,
6559 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6562 Ybomb_blank, FALSE, FALSE,
6563 EL_BOMB, ACTION_ACTIVATING, -1
6571 Xnut_pause, FALSE, FALSE,
6575 Xnut_fall, FALSE, FALSE,
6579 Ynut_s, FALSE, FALSE,
6580 EL_NUT, ACTION_FALLING, -1
6583 Ynut_sB, FALSE, TRUE,
6584 EL_NUT, ACTION_FALLING, -1
6587 Ynut_e, FALSE, FALSE,
6588 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6591 Ynut_eB, FALSE, TRUE,
6592 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6595 Ynut_w, FALSE, FALSE,
6596 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6599 Ynut_wB, FALSE, TRUE,
6600 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6603 Ynut_stone, FALSE, FALSE,
6604 EL_NUT, ACTION_BREAKING, -1
6608 Xspring, TRUE, FALSE,
6612 Xspring_pause, FALSE, FALSE,
6616 Xspring_e, TRUE, FALSE,
6617 EL_SPRING_RIGHT, -1, -1
6620 Xspring_w, TRUE, FALSE,
6621 EL_SPRING_LEFT, -1, -1
6624 Xspring_fall, FALSE, FALSE,
6628 Yspring_s, FALSE, FALSE,
6629 EL_SPRING, ACTION_FALLING, -1
6632 Yspring_sB, FALSE, TRUE,
6633 EL_SPRING, ACTION_FALLING, -1
6636 Yspring_e, FALSE, FALSE,
6637 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6640 Yspring_eB, FALSE, TRUE,
6641 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6644 Yspring_w, FALSE, FALSE,
6645 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6648 Yspring_wB, FALSE, TRUE,
6649 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6652 Yspring_alien_e, FALSE, FALSE,
6653 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6656 Yspring_alien_eB, FALSE, TRUE,
6657 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6660 Yspring_alien_w, FALSE, FALSE,
6661 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6664 Yspring_alien_wB, FALSE, TRUE,
6665 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6669 Xpush_emerald_e, FALSE, FALSE,
6670 EL_EMERALD, -1, MV_BIT_RIGHT
6673 Xpush_emerald_w, FALSE, FALSE,
6674 EL_EMERALD, -1, MV_BIT_LEFT
6677 Xpush_diamond_e, FALSE, FALSE,
6678 EL_DIAMOND, -1, MV_BIT_RIGHT
6681 Xpush_diamond_w, FALSE, FALSE,
6682 EL_DIAMOND, -1, MV_BIT_LEFT
6685 Xpush_stone_e, FALSE, FALSE,
6686 EL_ROCK, -1, MV_BIT_RIGHT
6689 Xpush_stone_w, FALSE, FALSE,
6690 EL_ROCK, -1, MV_BIT_LEFT
6693 Xpush_bomb_e, FALSE, FALSE,
6694 EL_BOMB, -1, MV_BIT_RIGHT
6697 Xpush_bomb_w, FALSE, FALSE,
6698 EL_BOMB, -1, MV_BIT_LEFT
6701 Xpush_nut_e, FALSE, FALSE,
6702 EL_NUT, -1, MV_BIT_RIGHT
6705 Xpush_nut_w, FALSE, FALSE,
6706 EL_NUT, -1, MV_BIT_LEFT
6709 Xpush_spring_e, FALSE, FALSE,
6710 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6713 Xpush_spring_w, FALSE, FALSE,
6714 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6718 Xdynamite, TRUE, FALSE,
6719 EL_EM_DYNAMITE, -1, -1
6722 Ydynamite_blank, FALSE, FALSE,
6723 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6726 Xdynamite_1, TRUE, FALSE,
6727 EL_EM_DYNAMITE_ACTIVE, -1, -1
6730 Xdynamite_2, FALSE, FALSE,
6731 EL_EM_DYNAMITE_ACTIVE, -1, -1
6734 Xdynamite_3, FALSE, FALSE,
6735 EL_EM_DYNAMITE_ACTIVE, -1, -1
6738 Xdynamite_4, FALSE, FALSE,
6739 EL_EM_DYNAMITE_ACTIVE, -1, -1
6743 Xkey_1, TRUE, FALSE,
6747 Xkey_2, TRUE, FALSE,
6751 Xkey_3, TRUE, FALSE,
6755 Xkey_4, TRUE, FALSE,
6759 Xkey_5, TRUE, FALSE,
6760 EL_EMC_KEY_5, -1, -1
6763 Xkey_6, TRUE, FALSE,
6764 EL_EMC_KEY_6, -1, -1
6767 Xkey_7, TRUE, FALSE,
6768 EL_EMC_KEY_7, -1, -1
6771 Xkey_8, TRUE, FALSE,
6772 EL_EMC_KEY_8, -1, -1
6776 Xdoor_1, TRUE, FALSE,
6777 EL_EM_GATE_1, -1, -1
6780 Xdoor_2, TRUE, FALSE,
6781 EL_EM_GATE_2, -1, -1
6784 Xdoor_3, TRUE, FALSE,
6785 EL_EM_GATE_3, -1, -1
6788 Xdoor_4, TRUE, FALSE,
6789 EL_EM_GATE_4, -1, -1
6792 Xdoor_5, TRUE, FALSE,
6793 EL_EMC_GATE_5, -1, -1
6796 Xdoor_6, TRUE, FALSE,
6797 EL_EMC_GATE_6, -1, -1
6800 Xdoor_7, TRUE, FALSE,
6801 EL_EMC_GATE_7, -1, -1
6804 Xdoor_8, TRUE, FALSE,
6805 EL_EMC_GATE_8, -1, -1
6809 Xfake_door_1, TRUE, FALSE,
6810 EL_EM_GATE_1_GRAY, -1, -1
6813 Xfake_door_2, TRUE, FALSE,
6814 EL_EM_GATE_2_GRAY, -1, -1
6817 Xfake_door_3, TRUE, FALSE,
6818 EL_EM_GATE_3_GRAY, -1, -1
6821 Xfake_door_4, TRUE, FALSE,
6822 EL_EM_GATE_4_GRAY, -1, -1
6825 Xfake_door_5, TRUE, FALSE,
6826 EL_EMC_GATE_5_GRAY, -1, -1
6829 Xfake_door_6, TRUE, FALSE,
6830 EL_EMC_GATE_6_GRAY, -1, -1
6833 Xfake_door_7, TRUE, FALSE,
6834 EL_EMC_GATE_7_GRAY, -1, -1
6837 Xfake_door_8, TRUE, FALSE,
6838 EL_EMC_GATE_8_GRAY, -1, -1
6842 Xballoon, TRUE, FALSE,
6846 Yballoon_n, FALSE, FALSE,
6847 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6850 Yballoon_nB, FALSE, TRUE,
6851 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6854 Yballoon_e, FALSE, FALSE,
6855 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6858 Yballoon_eB, FALSE, TRUE,
6859 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6862 Yballoon_s, FALSE, FALSE,
6863 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6866 Yballoon_sB, FALSE, TRUE,
6867 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6870 Yballoon_w, FALSE, FALSE,
6871 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6874 Yballoon_wB, FALSE, TRUE,
6875 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6879 Xball_1, TRUE, FALSE,
6880 EL_EMC_MAGIC_BALL, -1, -1
6883 Yball_1, FALSE, FALSE,
6884 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6887 Xball_2, FALSE, FALSE,
6888 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6891 Yball_2, FALSE, FALSE,
6892 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6895 Yball_blank, FALSE, FALSE,
6896 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6900 Xamoeba_1, TRUE, FALSE,
6901 EL_AMOEBA_DRY, ACTION_OTHER, -1
6904 Xamoeba_2, FALSE, FALSE,
6905 EL_AMOEBA_DRY, ACTION_OTHER, -1
6908 Xamoeba_3, FALSE, FALSE,
6909 EL_AMOEBA_DRY, ACTION_OTHER, -1
6912 Xamoeba_4, FALSE, FALSE,
6913 EL_AMOEBA_DRY, ACTION_OTHER, -1
6916 Xamoeba_5, TRUE, FALSE,
6917 EL_AMOEBA_WET, ACTION_OTHER, -1
6920 Xamoeba_6, FALSE, FALSE,
6921 EL_AMOEBA_WET, ACTION_OTHER, -1
6924 Xamoeba_7, FALSE, FALSE,
6925 EL_AMOEBA_WET, ACTION_OTHER, -1
6928 Xamoeba_8, FALSE, FALSE,
6929 EL_AMOEBA_WET, ACTION_OTHER, -1
6934 EL_AMOEBA_DROP, ACTION_GROWING, -1
6937 Xdrip_fall, FALSE, FALSE,
6938 EL_AMOEBA_DROP, -1, -1
6941 Xdrip_stretch, FALSE, FALSE,
6942 EL_AMOEBA_DROP, ACTION_FALLING, -1
6945 Xdrip_stretchB, FALSE, TRUE,
6946 EL_AMOEBA_DROP, ACTION_FALLING, -1
6949 Ydrip_1_s, FALSE, FALSE,
6950 EL_AMOEBA_DROP, ACTION_FALLING, -1
6953 Ydrip_1_sB, FALSE, TRUE,
6954 EL_AMOEBA_DROP, ACTION_FALLING, -1
6957 Ydrip_2_s, FALSE, FALSE,
6958 EL_AMOEBA_DROP, ACTION_FALLING, -1
6961 Ydrip_2_sB, FALSE, TRUE,
6962 EL_AMOEBA_DROP, ACTION_FALLING, -1
6966 Xwonderwall, TRUE, FALSE,
6967 EL_MAGIC_WALL, -1, -1
6970 Ywonderwall, FALSE, FALSE,
6971 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6975 Xwheel, TRUE, FALSE,
6976 EL_ROBOT_WHEEL, -1, -1
6979 Ywheel, FALSE, FALSE,
6980 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6984 Xswitch, TRUE, FALSE,
6985 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6988 Yswitch, FALSE, FALSE,
6989 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6993 Xbumper, TRUE, FALSE,
6994 EL_EMC_SPRING_BUMPER, -1, -1
6997 Ybumper, FALSE, FALSE,
6998 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7002 Xacid_nw, TRUE, FALSE,
7003 EL_ACID_POOL_TOPLEFT, -1, -1
7006 Xacid_ne, TRUE, FALSE,
7007 EL_ACID_POOL_TOPRIGHT, -1, -1
7010 Xacid_sw, TRUE, FALSE,
7011 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7014 Xacid_s, TRUE, FALSE,
7015 EL_ACID_POOL_BOTTOM, -1, -1
7018 Xacid_se, TRUE, FALSE,
7019 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7023 Xfake_blank, TRUE, FALSE,
7024 EL_INVISIBLE_WALL, -1, -1
7027 Yfake_blank, FALSE, FALSE,
7028 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7032 Xfake_grass, TRUE, FALSE,
7033 EL_EMC_FAKE_GRASS, -1, -1
7036 Yfake_grass, FALSE, FALSE,
7037 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7041 Xfake_amoeba, TRUE, FALSE,
7042 EL_EMC_DRIPPER, -1, -1
7045 Yfake_amoeba, FALSE, FALSE,
7046 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7050 Xlenses, TRUE, FALSE,
7051 EL_EMC_LENSES, -1, -1
7055 Xmagnify, TRUE, FALSE,
7056 EL_EMC_MAGNIFIER, -1, -1
7061 EL_QUICKSAND_EMPTY, -1, -1
7064 Xsand_stone, TRUE, FALSE,
7065 EL_QUICKSAND_FULL, -1, -1
7068 Xsand_stonein_1, FALSE, TRUE,
7069 EL_ROCK, ACTION_FILLING, -1
7072 Xsand_stonein_2, FALSE, TRUE,
7073 EL_ROCK, ACTION_FILLING, -1
7076 Xsand_stonein_3, FALSE, TRUE,
7077 EL_ROCK, ACTION_FILLING, -1
7080 Xsand_stonein_4, FALSE, TRUE,
7081 EL_ROCK, ACTION_FILLING, -1
7084 Xsand_sandstone_1, FALSE, FALSE,
7085 EL_QUICKSAND_FILLING, -1, -1
7088 Xsand_sandstone_2, FALSE, FALSE,
7089 EL_QUICKSAND_FILLING, -1, -1
7092 Xsand_sandstone_3, FALSE, FALSE,
7093 EL_QUICKSAND_FILLING, -1, -1
7096 Xsand_sandstone_4, FALSE, FALSE,
7097 EL_QUICKSAND_FILLING, -1, -1
7100 Xsand_stonesand_1, FALSE, FALSE,
7101 EL_QUICKSAND_EMPTYING, -1, -1
7104 Xsand_stonesand_2, FALSE, FALSE,
7105 EL_QUICKSAND_EMPTYING, -1, -1
7108 Xsand_stonesand_3, FALSE, FALSE,
7109 EL_QUICKSAND_EMPTYING, -1, -1
7112 Xsand_stonesand_4, FALSE, FALSE,
7113 EL_QUICKSAND_EMPTYING, -1, -1
7116 Xsand_stoneout_1, FALSE, FALSE,
7117 EL_ROCK, ACTION_EMPTYING, -1
7120 Xsand_stoneout_2, FALSE, FALSE,
7121 EL_ROCK, ACTION_EMPTYING, -1
7124 Xsand_stonesand_quickout_1, FALSE, FALSE,
7125 EL_QUICKSAND_EMPTYING, -1, -1
7128 Xsand_stonesand_quickout_2, FALSE, FALSE,
7129 EL_QUICKSAND_EMPTYING, -1, -1
7133 Xslide_ns, TRUE, FALSE,
7134 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7137 Yslide_ns_blank, FALSE, FALSE,
7138 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7141 Xslide_ew, TRUE, FALSE,
7142 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7145 Yslide_ew_blank, FALSE, FALSE,
7146 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7150 Xwind_n, TRUE, FALSE,
7151 EL_BALLOON_SWITCH_UP, -1, -1
7154 Xwind_e, TRUE, FALSE,
7155 EL_BALLOON_SWITCH_RIGHT, -1, -1
7158 Xwind_s, TRUE, FALSE,
7159 EL_BALLOON_SWITCH_DOWN, -1, -1
7162 Xwind_w, TRUE, FALSE,
7163 EL_BALLOON_SWITCH_LEFT, -1, -1
7166 Xwind_any, TRUE, FALSE,
7167 EL_BALLOON_SWITCH_ANY, -1, -1
7170 Xwind_stop, TRUE, FALSE,
7171 EL_BALLOON_SWITCH_NONE, -1, -1
7176 EL_EM_EXIT_CLOSED, -1, -1
7179 Xexit_1, TRUE, FALSE,
7180 EL_EM_EXIT_OPEN, -1, -1
7183 Xexit_2, FALSE, FALSE,
7184 EL_EM_EXIT_OPEN, -1, -1
7187 Xexit_3, FALSE, FALSE,
7188 EL_EM_EXIT_OPEN, -1, -1
7192 Xpause, FALSE, FALSE,
7197 Xwall_1, TRUE, FALSE,
7201 Xwall_2, TRUE, FALSE,
7202 EL_EMC_WALL_14, -1, -1
7205 Xwall_3, TRUE, FALSE,
7206 EL_EMC_WALL_15, -1, -1
7209 Xwall_4, TRUE, FALSE,
7210 EL_EMC_WALL_16, -1, -1
7214 Xroundwall_1, TRUE, FALSE,
7215 EL_WALL_SLIPPERY, -1, -1
7218 Xroundwall_2, TRUE, FALSE,
7219 EL_EMC_WALL_SLIPPERY_2, -1, -1
7222 Xroundwall_3, TRUE, FALSE,
7223 EL_EMC_WALL_SLIPPERY_3, -1, -1
7226 Xroundwall_4, TRUE, FALSE,
7227 EL_EMC_WALL_SLIPPERY_4, -1, -1
7231 Xsteel_1, TRUE, FALSE,
7232 EL_STEELWALL, -1, -1
7235 Xsteel_2, TRUE, FALSE,
7236 EL_EMC_STEELWALL_2, -1, -1
7239 Xsteel_3, TRUE, FALSE,
7240 EL_EMC_STEELWALL_3, -1, -1
7243 Xsteel_4, TRUE, FALSE,
7244 EL_EMC_STEELWALL_4, -1, -1
7248 Xdecor_1, TRUE, FALSE,
7249 EL_EMC_WALL_8, -1, -1
7252 Xdecor_2, TRUE, FALSE,
7253 EL_EMC_WALL_6, -1, -1
7256 Xdecor_3, TRUE, FALSE,
7257 EL_EMC_WALL_4, -1, -1
7260 Xdecor_4, TRUE, FALSE,
7261 EL_EMC_WALL_7, -1, -1
7264 Xdecor_5, TRUE, FALSE,
7265 EL_EMC_WALL_5, -1, -1
7268 Xdecor_6, TRUE, FALSE,
7269 EL_EMC_WALL_9, -1, -1
7272 Xdecor_7, TRUE, FALSE,
7273 EL_EMC_WALL_10, -1, -1
7276 Xdecor_8, TRUE, FALSE,
7277 EL_EMC_WALL_1, -1, -1
7280 Xdecor_9, TRUE, FALSE,
7281 EL_EMC_WALL_2, -1, -1
7284 Xdecor_10, TRUE, FALSE,
7285 EL_EMC_WALL_3, -1, -1
7288 Xdecor_11, TRUE, FALSE,
7289 EL_EMC_WALL_11, -1, -1
7292 Xdecor_12, TRUE, FALSE,
7293 EL_EMC_WALL_12, -1, -1
7297 Xalpha_0, TRUE, FALSE,
7298 EL_CHAR('0'), -1, -1
7301 Xalpha_1, TRUE, FALSE,
7302 EL_CHAR('1'), -1, -1
7305 Xalpha_2, TRUE, FALSE,
7306 EL_CHAR('2'), -1, -1
7309 Xalpha_3, TRUE, FALSE,
7310 EL_CHAR('3'), -1, -1
7313 Xalpha_4, TRUE, FALSE,
7314 EL_CHAR('4'), -1, -1
7317 Xalpha_5, TRUE, FALSE,
7318 EL_CHAR('5'), -1, -1
7321 Xalpha_6, TRUE, FALSE,
7322 EL_CHAR('6'), -1, -1
7325 Xalpha_7, TRUE, FALSE,
7326 EL_CHAR('7'), -1, -1
7329 Xalpha_8, TRUE, FALSE,
7330 EL_CHAR('8'), -1, -1
7333 Xalpha_9, TRUE, FALSE,
7334 EL_CHAR('9'), -1, -1
7337 Xalpha_excla, TRUE, FALSE,
7338 EL_CHAR('!'), -1, -1
7341 Xalpha_apost, TRUE, FALSE,
7342 EL_CHAR('\''), -1, -1
7345 Xalpha_comma, TRUE, FALSE,
7346 EL_CHAR(','), -1, -1
7349 Xalpha_minus, TRUE, FALSE,
7350 EL_CHAR('-'), -1, -1
7353 Xalpha_perio, TRUE, FALSE,
7354 EL_CHAR('.'), -1, -1
7357 Xalpha_colon, TRUE, FALSE,
7358 EL_CHAR(':'), -1, -1
7361 Xalpha_quest, TRUE, FALSE,
7362 EL_CHAR('?'), -1, -1
7365 Xalpha_a, TRUE, FALSE,
7366 EL_CHAR('A'), -1, -1
7369 Xalpha_b, TRUE, FALSE,
7370 EL_CHAR('B'), -1, -1
7373 Xalpha_c, TRUE, FALSE,
7374 EL_CHAR('C'), -1, -1
7377 Xalpha_d, TRUE, FALSE,
7378 EL_CHAR('D'), -1, -1
7381 Xalpha_e, TRUE, FALSE,
7382 EL_CHAR('E'), -1, -1
7385 Xalpha_f, TRUE, FALSE,
7386 EL_CHAR('F'), -1, -1
7389 Xalpha_g, TRUE, FALSE,
7390 EL_CHAR('G'), -1, -1
7393 Xalpha_h, TRUE, FALSE,
7394 EL_CHAR('H'), -1, -1
7397 Xalpha_i, TRUE, FALSE,
7398 EL_CHAR('I'), -1, -1
7401 Xalpha_j, TRUE, FALSE,
7402 EL_CHAR('J'), -1, -1
7405 Xalpha_k, TRUE, FALSE,
7406 EL_CHAR('K'), -1, -1
7409 Xalpha_l, TRUE, FALSE,
7410 EL_CHAR('L'), -1, -1
7413 Xalpha_m, TRUE, FALSE,
7414 EL_CHAR('M'), -1, -1
7417 Xalpha_n, TRUE, FALSE,
7418 EL_CHAR('N'), -1, -1
7421 Xalpha_o, TRUE, FALSE,
7422 EL_CHAR('O'), -1, -1
7425 Xalpha_p, TRUE, FALSE,
7426 EL_CHAR('P'), -1, -1
7429 Xalpha_q, TRUE, FALSE,
7430 EL_CHAR('Q'), -1, -1
7433 Xalpha_r, TRUE, FALSE,
7434 EL_CHAR('R'), -1, -1
7437 Xalpha_s, TRUE, FALSE,
7438 EL_CHAR('S'), -1, -1
7441 Xalpha_t, TRUE, FALSE,
7442 EL_CHAR('T'), -1, -1
7445 Xalpha_u, TRUE, FALSE,
7446 EL_CHAR('U'), -1, -1
7449 Xalpha_v, TRUE, FALSE,
7450 EL_CHAR('V'), -1, -1
7453 Xalpha_w, TRUE, FALSE,
7454 EL_CHAR('W'), -1, -1
7457 Xalpha_x, TRUE, FALSE,
7458 EL_CHAR('X'), -1, -1
7461 Xalpha_y, TRUE, FALSE,
7462 EL_CHAR('Y'), -1, -1
7465 Xalpha_z, TRUE, FALSE,
7466 EL_CHAR('Z'), -1, -1
7469 Xalpha_arrow_e, TRUE, FALSE,
7470 EL_CHAR('>'), -1, -1
7473 Xalpha_arrow_w, TRUE, FALSE,
7474 EL_CHAR('<'), -1, -1
7477 Xalpha_copyr, TRUE, FALSE,
7478 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7482 Ykey_1_blank, FALSE, FALSE,
7483 EL_EM_KEY_1, ACTION_COLLECTING, -1
7486 Ykey_2_blank, FALSE, FALSE,
7487 EL_EM_KEY_2, ACTION_COLLECTING, -1
7490 Ykey_3_blank, FALSE, FALSE,
7491 EL_EM_KEY_3, ACTION_COLLECTING, -1
7494 Ykey_4_blank, FALSE, FALSE,
7495 EL_EM_KEY_4, ACTION_COLLECTING, -1
7498 Ykey_5_blank, FALSE, FALSE,
7499 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7502 Ykey_6_blank, FALSE, FALSE,
7503 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7506 Ykey_7_blank, FALSE, FALSE,
7507 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7510 Ykey_8_blank, FALSE, FALSE,
7511 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7514 Ylenses_blank, FALSE, FALSE,
7515 EL_EMC_LENSES, ACTION_COLLECTING, -1
7518 Ymagnify_blank, FALSE, FALSE,
7519 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7522 Ygrass_blank, FALSE, FALSE,
7523 EL_EMC_GRASS, ACTION_SNAPPING, -1
7526 Ydirt_blank, FALSE, FALSE,
7527 EL_SAND, ACTION_SNAPPING, -1
7536 static struct Mapping_EM_to_RND_player
7545 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7549 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7553 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7557 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7561 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7565 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7569 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7573 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7577 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7581 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7585 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7589 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7593 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7597 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7601 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7605 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7609 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7613 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7617 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7621 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7625 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7629 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7633 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7637 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7641 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7645 EL_PLAYER_1, ACTION_DEFAULT, -1,
7649 EL_PLAYER_2, ACTION_DEFAULT, -1,
7653 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7657 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7661 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7665 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7669 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7673 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7677 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7681 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7685 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7689 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7693 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7697 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7701 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7705 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7709 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7713 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7717 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7721 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7725 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7729 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7733 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7737 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7741 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7745 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7749 EL_PLAYER_3, ACTION_DEFAULT, -1,
7753 EL_PLAYER_4, ACTION_DEFAULT, -1,
7762 int map_element_RND_to_EM_cave(int element_rnd)
7764 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7765 static boolean mapping_initialized = FALSE;
7767 if (!mapping_initialized)
7771 // return "Xalpha_quest" for all undefined elements in mapping array
7772 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7773 mapping_RND_to_EM[i] = Xalpha_quest;
7775 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7776 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7777 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7778 em_object_mapping_list[i].element_em;
7780 mapping_initialized = TRUE;
7783 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7785 Warn("invalid RND level element %d", element_rnd);
7790 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7793 int map_element_EM_to_RND_cave(int element_em_cave)
7795 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7796 static boolean mapping_initialized = FALSE;
7798 if (!mapping_initialized)
7802 // return "EL_UNKNOWN" for all undefined elements in mapping array
7803 for (i = 0; i < GAME_TILE_MAX; i++)
7804 mapping_EM_to_RND[i] = EL_UNKNOWN;
7806 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7807 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7808 em_object_mapping_list[i].element_rnd;
7810 mapping_initialized = TRUE;
7813 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7815 Warn("invalid EM cave element %d", element_em_cave);
7820 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7823 int map_element_EM_to_RND_game(int element_em_game)
7825 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7826 static boolean mapping_initialized = FALSE;
7828 if (!mapping_initialized)
7832 // return "EL_UNKNOWN" for all undefined elements in mapping array
7833 for (i = 0; i < GAME_TILE_MAX; i++)
7834 mapping_EM_to_RND[i] = EL_UNKNOWN;
7836 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7837 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7838 em_object_mapping_list[i].element_rnd;
7840 mapping_initialized = TRUE;
7843 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7845 Warn("invalid EM game element %d", element_em_game);
7850 return mapping_EM_to_RND[element_em_game];
7853 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7855 struct LevelInfo_EM *level_em = level->native_em_level;
7856 struct CAVE *cav = level_em->cav;
7859 for (i = 0; i < GAME_TILE_MAX; i++)
7860 cav->android_array[i] = Cblank;
7862 for (i = 0; i < level->num_android_clone_elements; i++)
7864 int element_rnd = level->android_clone_element[i];
7865 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7867 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7868 if (em_object_mapping_list[j].element_rnd == element_rnd)
7869 cav->android_array[em_object_mapping_list[j].element_em] =
7874 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7876 struct LevelInfo_EM *level_em = level->native_em_level;
7877 struct CAVE *cav = level_em->cav;
7880 level->num_android_clone_elements = 0;
7882 for (i = 0; i < GAME_TILE_MAX; i++)
7884 int element_em_cave = cav->android_array[i];
7886 boolean element_found = FALSE;
7888 if (element_em_cave == Cblank)
7891 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7893 for (j = 0; j < level->num_android_clone_elements; j++)
7894 if (level->android_clone_element[j] == element_rnd)
7895 element_found = TRUE;
7899 level->android_clone_element[level->num_android_clone_elements++] =
7902 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7907 if (level->num_android_clone_elements == 0)
7909 level->num_android_clone_elements = 1;
7910 level->android_clone_element[0] = EL_EMPTY;
7914 int map_direction_RND_to_EM(int direction)
7916 return (direction == MV_UP ? 0 :
7917 direction == MV_RIGHT ? 1 :
7918 direction == MV_DOWN ? 2 :
7919 direction == MV_LEFT ? 3 :
7923 int map_direction_EM_to_RND(int direction)
7925 return (direction == 0 ? MV_UP :
7926 direction == 1 ? MV_RIGHT :
7927 direction == 2 ? MV_DOWN :
7928 direction == 3 ? MV_LEFT :
7932 int map_element_RND_to_SP(int element_rnd)
7934 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7936 if (element_rnd >= EL_SP_START &&
7937 element_rnd <= EL_SP_END)
7938 element_sp = element_rnd - EL_SP_START;
7939 else if (element_rnd == EL_EMPTY_SPACE)
7941 else if (element_rnd == EL_INVISIBLE_WALL)
7947 int map_element_SP_to_RND(int element_sp)
7949 int element_rnd = EL_UNKNOWN;
7951 if (element_sp >= 0x00 &&
7953 element_rnd = EL_SP_START + element_sp;
7954 else if (element_sp == 0x28)
7955 element_rnd = EL_INVISIBLE_WALL;
7960 int map_action_SP_to_RND(int action_sp)
7964 case actActive: return ACTION_ACTIVE;
7965 case actImpact: return ACTION_IMPACT;
7966 case actExploding: return ACTION_EXPLODING;
7967 case actDigging: return ACTION_DIGGING;
7968 case actSnapping: return ACTION_SNAPPING;
7969 case actCollecting: return ACTION_COLLECTING;
7970 case actPassing: return ACTION_PASSING;
7971 case actPushing: return ACTION_PUSHING;
7972 case actDropping: return ACTION_DROPPING;
7974 default: return ACTION_DEFAULT;
7978 int map_element_RND_to_MM(int element_rnd)
7980 return (element_rnd >= EL_MM_START_1 &&
7981 element_rnd <= EL_MM_END_1 ?
7982 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7984 element_rnd >= EL_MM_START_2 &&
7985 element_rnd <= EL_MM_END_2 ?
7986 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7988 element_rnd >= EL_CHAR_START &&
7989 element_rnd <= EL_CHAR_END ?
7990 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7992 element_rnd >= EL_MM_RUNTIME_START &&
7993 element_rnd <= EL_MM_RUNTIME_END ?
7994 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7996 element_rnd >= EL_MM_DUMMY_START &&
7997 element_rnd <= EL_MM_DUMMY_END ?
7998 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8000 EL_MM_EMPTY_NATIVE);
8003 int map_element_MM_to_RND(int element_mm)
8005 return (element_mm == EL_MM_EMPTY_NATIVE ||
8006 element_mm == EL_DF_EMPTY_NATIVE ?
8009 element_mm >= EL_MM_START_1_NATIVE &&
8010 element_mm <= EL_MM_END_1_NATIVE ?
8011 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8013 element_mm >= EL_MM_START_2_NATIVE &&
8014 element_mm <= EL_MM_END_2_NATIVE ?
8015 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8017 element_mm >= EL_MM_CHAR_START_NATIVE &&
8018 element_mm <= EL_MM_CHAR_END_NATIVE ?
8019 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8021 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8022 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8023 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8025 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8026 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8027 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8032 int map_action_MM_to_RND(int action_mm)
8034 // all MM actions are defined to exactly match their RND counterparts
8038 int map_sound_MM_to_RND(int sound_mm)
8042 case SND_MM_GAME_LEVELTIME_CHARGING:
8043 return SND_GAME_LEVELTIME_CHARGING;
8045 case SND_MM_GAME_HEALTH_CHARGING:
8046 return SND_GAME_HEALTH_CHARGING;
8049 return SND_UNDEFINED;
8053 int map_mm_wall_element(int element)
8055 return (element >= EL_MM_STEEL_WALL_START &&
8056 element <= EL_MM_STEEL_WALL_END ?
8059 element >= EL_MM_WOODEN_WALL_START &&
8060 element <= EL_MM_WOODEN_WALL_END ?
8063 element >= EL_MM_ICE_WALL_START &&
8064 element <= EL_MM_ICE_WALL_END ?
8067 element >= EL_MM_AMOEBA_WALL_START &&
8068 element <= EL_MM_AMOEBA_WALL_END ?
8071 element >= EL_DF_STEEL_WALL_START &&
8072 element <= EL_DF_STEEL_WALL_END ?
8075 element >= EL_DF_WOODEN_WALL_START &&
8076 element <= EL_DF_WOODEN_WALL_END ?
8082 int map_mm_wall_element_editor(int element)
8086 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8087 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8088 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8089 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8090 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8091 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8093 default: return element;
8097 int get_next_element(int element)
8101 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8102 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8103 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8104 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8105 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8106 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8107 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8108 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8109 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8110 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8111 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8113 default: return element;
8117 int el2img_mm(int element_mm)
8119 return el2img(map_element_MM_to_RND(element_mm));
8122 int el_act_dir2img(int element, int action, int direction)
8124 element = GFX_ELEMENT(element);
8125 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8127 // direction_graphic[][] == graphic[] for undefined direction graphics
8128 return element_info[element].direction_graphic[action][direction];
8131 static int el_act_dir2crm(int element, int action, int direction)
8133 element = GFX_ELEMENT(element);
8134 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8136 // direction_graphic[][] == graphic[] for undefined direction graphics
8137 return element_info[element].direction_crumbled[action][direction];
8140 int el_act2img(int element, int action)
8142 element = GFX_ELEMENT(element);
8144 return element_info[element].graphic[action];
8147 int el_act2crm(int element, int action)
8149 element = GFX_ELEMENT(element);
8151 return element_info[element].crumbled[action];
8154 int el_dir2img(int element, int direction)
8156 element = GFX_ELEMENT(element);
8158 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8161 int el2baseimg(int element)
8163 return element_info[element].graphic[ACTION_DEFAULT];
8166 int el2img(int element)
8168 element = GFX_ELEMENT(element);
8170 return element_info[element].graphic[ACTION_DEFAULT];
8173 int el2edimg(int element)
8175 element = GFX_ELEMENT(element);
8177 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8180 int el2preimg(int element)
8182 element = GFX_ELEMENT(element);
8184 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8187 int el2panelimg(int element)
8189 element = GFX_ELEMENT(element);
8191 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8194 int font2baseimg(int font_nr)
8196 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8199 int getBeltNrFromBeltElement(int element)
8201 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8202 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8203 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8206 int getBeltNrFromBeltActiveElement(int element)
8208 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8209 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8210 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8213 int getBeltNrFromBeltSwitchElement(int element)
8215 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8216 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8217 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8220 int getBeltDirNrFromBeltElement(int element)
8222 static int belt_base_element[4] =
8224 EL_CONVEYOR_BELT_1_LEFT,
8225 EL_CONVEYOR_BELT_2_LEFT,
8226 EL_CONVEYOR_BELT_3_LEFT,
8227 EL_CONVEYOR_BELT_4_LEFT
8230 int belt_nr = getBeltNrFromBeltElement(element);
8231 int belt_dir_nr = element - belt_base_element[belt_nr];
8233 return (belt_dir_nr % 3);
8236 int getBeltDirNrFromBeltSwitchElement(int element)
8238 static int belt_base_element[4] =
8240 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8241 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8242 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8243 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8246 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8247 int belt_dir_nr = element - belt_base_element[belt_nr];
8249 return (belt_dir_nr % 3);
8252 int getBeltDirFromBeltElement(int element)
8254 static int belt_move_dir[3] =
8261 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8263 return belt_move_dir[belt_dir_nr];
8266 int getBeltDirFromBeltSwitchElement(int element)
8268 static int belt_move_dir[3] =
8275 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8277 return belt_move_dir[belt_dir_nr];
8280 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8282 static int belt_base_element[4] =
8284 EL_CONVEYOR_BELT_1_LEFT,
8285 EL_CONVEYOR_BELT_2_LEFT,
8286 EL_CONVEYOR_BELT_3_LEFT,
8287 EL_CONVEYOR_BELT_4_LEFT
8290 return belt_base_element[belt_nr] + belt_dir_nr;
8293 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8295 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8297 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8300 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8302 static int belt_base_element[4] =
8304 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8305 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8306 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8307 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8310 return belt_base_element[belt_nr] + belt_dir_nr;
8313 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8315 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8317 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8320 boolean swapTiles_EM(boolean is_pre_emc_cave)
8322 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8325 boolean getTeamMode_EM(void)
8327 return game.team_mode || network_playing;
8330 boolean isActivePlayer_EM(int player_nr)
8332 return stored_player[player_nr].active;
8335 unsigned int InitRND(int seed)
8337 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8338 return InitEngineRandom_EM(seed);
8339 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8340 return InitEngineRandom_SP(seed);
8341 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8342 return InitEngineRandom_MM(seed);
8344 return InitEngineRandom_RND(seed);
8347 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8348 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8350 static int get_effective_element_EM(int tile, int frame_em)
8352 int element = object_mapping[tile].element_rnd;
8353 int action = object_mapping[tile].action;
8354 boolean is_backside = object_mapping[tile].is_backside;
8355 boolean action_removing = (action == ACTION_DIGGING ||
8356 action == ACTION_SNAPPING ||
8357 action == ACTION_COLLECTING);
8365 return (frame_em > 5 ? EL_EMPTY : element);
8371 else // frame_em == 7
8382 case Ydiamond_stone:
8386 case Xdrip_stretchB:
8402 case Ymagnify_blank:
8405 case Xsand_stonein_1:
8406 case Xsand_stonein_2:
8407 case Xsand_stonein_3:
8408 case Xsand_stonein_4:
8412 return (is_backside || action_removing ? EL_EMPTY : element);
8417 static boolean check_linear_animation_EM(int tile)
8421 case Xsand_stonesand_1:
8422 case Xsand_stonesand_quickout_1:
8423 case Xsand_sandstone_1:
8424 case Xsand_stonein_1:
8425 case Xsand_stoneout_1:
8453 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8454 boolean has_crumbled_graphics,
8455 int crumbled, int sync_frame)
8457 // if element can be crumbled, but certain action graphics are just empty
8458 // space (like instantly snapping sand to empty space in 1 frame), do not
8459 // treat these empty space graphics as crumbled graphics in EMC engine
8460 if (crumbled == IMG_EMPTY_SPACE)
8461 has_crumbled_graphics = FALSE;
8463 if (has_crumbled_graphics)
8465 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8466 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8467 g_crumbled->anim_delay,
8468 g_crumbled->anim_mode,
8469 g_crumbled->anim_start_frame,
8472 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8473 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8475 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8476 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8478 g_em->has_crumbled_graphics = TRUE;
8482 g_em->crumbled_bitmap = NULL;
8483 g_em->crumbled_src_x = 0;
8484 g_em->crumbled_src_y = 0;
8485 g_em->crumbled_border_size = 0;
8486 g_em->crumbled_tile_size = 0;
8488 g_em->has_crumbled_graphics = FALSE;
8493 void ResetGfxAnimation_EM(int x, int y, int tile)
8499 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8500 int tile, int frame_em, int x, int y)
8502 int action = object_mapping[tile].action;
8503 int direction = object_mapping[tile].direction;
8504 int effective_element = get_effective_element_EM(tile, frame_em);
8505 int graphic = (direction == MV_NONE ?
8506 el_act2img(effective_element, action) :
8507 el_act_dir2img(effective_element, action, direction));
8508 struct GraphicInfo *g = &graphic_info[graphic];
8510 boolean action_removing = (action == ACTION_DIGGING ||
8511 action == ACTION_SNAPPING ||
8512 action == ACTION_COLLECTING);
8513 boolean action_moving = (action == ACTION_FALLING ||
8514 action == ACTION_MOVING ||
8515 action == ACTION_PUSHING ||
8516 action == ACTION_EATING ||
8517 action == ACTION_FILLING ||
8518 action == ACTION_EMPTYING);
8519 boolean action_falling = (action == ACTION_FALLING ||
8520 action == ACTION_FILLING ||
8521 action == ACTION_EMPTYING);
8523 // special case: graphic uses "2nd movement tile" and has defined
8524 // 7 frames for movement animation (or less) => use default graphic
8525 // for last (8th) frame which ends the movement animation
8526 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8528 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8529 graphic = (direction == MV_NONE ?
8530 el_act2img(effective_element, action) :
8531 el_act_dir2img(effective_element, action, direction));
8533 g = &graphic_info[graphic];
8536 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8540 else if (action_moving)
8542 boolean is_backside = object_mapping[tile].is_backside;
8546 int direction = object_mapping[tile].direction;
8547 int move_dir = (action_falling ? MV_DOWN : direction);
8552 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8553 if (g->double_movement && frame_em == 0)
8557 if (move_dir == MV_LEFT)
8558 GfxFrame[x - 1][y] = GfxFrame[x][y];
8559 else if (move_dir == MV_RIGHT)
8560 GfxFrame[x + 1][y] = GfxFrame[x][y];
8561 else if (move_dir == MV_UP)
8562 GfxFrame[x][y - 1] = GfxFrame[x][y];
8563 else if (move_dir == MV_DOWN)
8564 GfxFrame[x][y + 1] = GfxFrame[x][y];
8571 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8572 if (tile == Xsand_stonesand_quickout_1 ||
8573 tile == Xsand_stonesand_quickout_2)
8577 if (graphic_info[graphic].anim_global_sync)
8578 sync_frame = FrameCounter;
8579 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8580 sync_frame = GfxFrame[x][y];
8582 sync_frame = 0; // playfield border (pseudo steel)
8584 SetRandomAnimationValue(x, y);
8586 int frame = getAnimationFrame(g->anim_frames,
8589 g->anim_start_frame,
8592 g_em->unique_identifier =
8593 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8596 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8597 int tile, int frame_em, int x, int y)
8599 int action = object_mapping[tile].action;
8600 int direction = object_mapping[tile].direction;
8601 boolean is_backside = object_mapping[tile].is_backside;
8602 int effective_element = get_effective_element_EM(tile, frame_em);
8603 int effective_action = action;
8604 int graphic = (direction == MV_NONE ?
8605 el_act2img(effective_element, effective_action) :
8606 el_act_dir2img(effective_element, effective_action,
8608 int crumbled = (direction == MV_NONE ?
8609 el_act2crm(effective_element, effective_action) :
8610 el_act_dir2crm(effective_element, effective_action,
8612 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8613 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8614 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8615 struct GraphicInfo *g = &graphic_info[graphic];
8618 // special case: graphic uses "2nd movement tile" and has defined
8619 // 7 frames for movement animation (or less) => use default graphic
8620 // for last (8th) frame which ends the movement animation
8621 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8623 effective_action = ACTION_DEFAULT;
8624 graphic = (direction == MV_NONE ?
8625 el_act2img(effective_element, effective_action) :
8626 el_act_dir2img(effective_element, effective_action,
8628 crumbled = (direction == MV_NONE ?
8629 el_act2crm(effective_element, effective_action) :
8630 el_act_dir2crm(effective_element, effective_action,
8633 g = &graphic_info[graphic];
8636 if (graphic_info[graphic].anim_global_sync)
8637 sync_frame = FrameCounter;
8638 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8639 sync_frame = GfxFrame[x][y];
8641 sync_frame = 0; // playfield border (pseudo steel)
8643 SetRandomAnimationValue(x, y);
8645 int frame = getAnimationFrame(g->anim_frames,
8648 g->anim_start_frame,
8651 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8652 g->double_movement && is_backside);
8654 // (updating the "crumbled" graphic definitions is probably not really needed,
8655 // as animations for crumbled graphics can't be longer than one EMC cycle)
8656 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8660 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8661 int player_nr, int anim, int frame_em)
8663 int element = player_mapping[player_nr][anim].element_rnd;
8664 int action = player_mapping[player_nr][anim].action;
8665 int direction = player_mapping[player_nr][anim].direction;
8666 int graphic = (direction == MV_NONE ?
8667 el_act2img(element, action) :
8668 el_act_dir2img(element, action, direction));
8669 struct GraphicInfo *g = &graphic_info[graphic];
8672 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8674 stored_player[player_nr].StepFrame = frame_em;
8676 sync_frame = stored_player[player_nr].Frame;
8678 int frame = getAnimationFrame(g->anim_frames,
8681 g->anim_start_frame,
8684 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8685 &g_em->src_x, &g_em->src_y, FALSE);
8688 void InitGraphicInfo_EM(void)
8692 // always start with reliable default values
8693 for (i = 0; i < GAME_TILE_MAX; i++)
8695 object_mapping[i].element_rnd = EL_UNKNOWN;
8696 object_mapping[i].is_backside = FALSE;
8697 object_mapping[i].action = ACTION_DEFAULT;
8698 object_mapping[i].direction = MV_NONE;
8701 // always start with reliable default values
8702 for (p = 0; p < MAX_PLAYERS; p++)
8704 for (i = 0; i < PLY_MAX; i++)
8706 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8707 player_mapping[p][i].action = ACTION_DEFAULT;
8708 player_mapping[p][i].direction = MV_NONE;
8712 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8714 int e = em_object_mapping_list[i].element_em;
8716 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8717 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8719 if (em_object_mapping_list[i].action != -1)
8720 object_mapping[e].action = em_object_mapping_list[i].action;
8722 if (em_object_mapping_list[i].direction != -1)
8723 object_mapping[e].direction =
8724 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8727 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8729 int a = em_player_mapping_list[i].action_em;
8730 int p = em_player_mapping_list[i].player_nr;
8732 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8734 if (em_player_mapping_list[i].action != -1)
8735 player_mapping[p][a].action = em_player_mapping_list[i].action;
8737 if (em_player_mapping_list[i].direction != -1)
8738 player_mapping[p][a].direction =
8739 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8742 for (i = 0; i < GAME_TILE_MAX; i++)
8744 int element = object_mapping[i].element_rnd;
8745 int action = object_mapping[i].action;
8746 int direction = object_mapping[i].direction;
8747 boolean is_backside = object_mapping[i].is_backside;
8748 boolean action_exploding = ((action == ACTION_EXPLODING ||
8749 action == ACTION_SMASHED_BY_ROCK ||
8750 action == ACTION_SMASHED_BY_SPRING) &&
8751 element != EL_DIAMOND);
8752 boolean action_active = (action == ACTION_ACTIVE);
8753 boolean action_other = (action == ACTION_OTHER);
8755 for (j = 0; j < 8; j++)
8757 int effective_element = get_effective_element_EM(i, j);
8758 int effective_action = (j < 7 ? action :
8759 i == Xdrip_stretch ? action :
8760 i == Xdrip_stretchB ? action :
8761 i == Ydrip_1_s ? action :
8762 i == Ydrip_1_sB ? action :
8763 i == Yball_1 ? action :
8764 i == Xball_2 ? action :
8765 i == Yball_2 ? action :
8766 i == Yball_blank ? action :
8767 i == Ykey_1_blank ? action :
8768 i == Ykey_2_blank ? action :
8769 i == Ykey_3_blank ? action :
8770 i == Ykey_4_blank ? action :
8771 i == Ykey_5_blank ? action :
8772 i == Ykey_6_blank ? action :
8773 i == Ykey_7_blank ? action :
8774 i == Ykey_8_blank ? action :
8775 i == Ylenses_blank ? action :
8776 i == Ymagnify_blank ? action :
8777 i == Ygrass_blank ? action :
8778 i == Ydirt_blank ? action :
8779 i == Xsand_stonein_1 ? action :
8780 i == Xsand_stonein_2 ? action :
8781 i == Xsand_stonein_3 ? action :
8782 i == Xsand_stonein_4 ? action :
8783 i == Xsand_stoneout_1 ? action :
8784 i == Xsand_stoneout_2 ? action :
8785 i == Xboom_android ? ACTION_EXPLODING :
8786 action_exploding ? ACTION_EXPLODING :
8787 action_active ? action :
8788 action_other ? action :
8790 int graphic = (el_act_dir2img(effective_element, effective_action,
8792 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8794 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8795 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8796 boolean has_action_graphics = (graphic != base_graphic);
8797 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8798 struct GraphicInfo *g = &graphic_info[graphic];
8799 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8802 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8803 boolean special_animation = (action != ACTION_DEFAULT &&
8804 g->anim_frames == 3 &&
8805 g->anim_delay == 2 &&
8806 g->anim_mode & ANIM_LINEAR);
8807 int sync_frame = (i == Xdrip_stretch ? 7 :
8808 i == Xdrip_stretchB ? 7 :
8809 i == Ydrip_2_s ? j + 8 :
8810 i == Ydrip_2_sB ? j + 8 :
8819 i == Xfake_acid_1 ? 0 :
8820 i == Xfake_acid_2 ? 10 :
8821 i == Xfake_acid_3 ? 20 :
8822 i == Xfake_acid_4 ? 30 :
8823 i == Xfake_acid_5 ? 40 :
8824 i == Xfake_acid_6 ? 50 :
8825 i == Xfake_acid_7 ? 60 :
8826 i == Xfake_acid_8 ? 70 :
8827 i == Xfake_acid_1_player ? 0 :
8828 i == Xfake_acid_2_player ? 10 :
8829 i == Xfake_acid_3_player ? 20 :
8830 i == Xfake_acid_4_player ? 30 :
8831 i == Xfake_acid_5_player ? 40 :
8832 i == Xfake_acid_6_player ? 50 :
8833 i == Xfake_acid_7_player ? 60 :
8834 i == Xfake_acid_8_player ? 70 :
8836 i == Yball_2 ? j + 8 :
8837 i == Yball_blank ? j + 1 :
8838 i == Ykey_1_blank ? j + 1 :
8839 i == Ykey_2_blank ? j + 1 :
8840 i == Ykey_3_blank ? j + 1 :
8841 i == Ykey_4_blank ? j + 1 :
8842 i == Ykey_5_blank ? j + 1 :
8843 i == Ykey_6_blank ? j + 1 :
8844 i == Ykey_7_blank ? j + 1 :
8845 i == Ykey_8_blank ? j + 1 :
8846 i == Ylenses_blank ? j + 1 :
8847 i == Ymagnify_blank ? j + 1 :
8848 i == Ygrass_blank ? j + 1 :
8849 i == Ydirt_blank ? j + 1 :
8850 i == Xamoeba_1 ? 0 :
8851 i == Xamoeba_2 ? 1 :
8852 i == Xamoeba_3 ? 2 :
8853 i == Xamoeba_4 ? 3 :
8854 i == Xamoeba_5 ? 0 :
8855 i == Xamoeba_6 ? 1 :
8856 i == Xamoeba_7 ? 2 :
8857 i == Xamoeba_8 ? 3 :
8858 i == Xexit_2 ? j + 8 :
8859 i == Xexit_3 ? j + 16 :
8860 i == Xdynamite_1 ? 0 :
8861 i == Xdynamite_2 ? 8 :
8862 i == Xdynamite_3 ? 16 :
8863 i == Xdynamite_4 ? 24 :
8864 i == Xsand_stonein_1 ? j + 1 :
8865 i == Xsand_stonein_2 ? j + 9 :
8866 i == Xsand_stonein_3 ? j + 17 :
8867 i == Xsand_stonein_4 ? j + 25 :
8868 i == Xsand_stoneout_1 && j == 0 ? 0 :
8869 i == Xsand_stoneout_1 && j == 1 ? 0 :
8870 i == Xsand_stoneout_1 && j == 2 ? 1 :
8871 i == Xsand_stoneout_1 && j == 3 ? 2 :
8872 i == Xsand_stoneout_1 && j == 4 ? 2 :
8873 i == Xsand_stoneout_1 && j == 5 ? 3 :
8874 i == Xsand_stoneout_1 && j == 6 ? 4 :
8875 i == Xsand_stoneout_1 && j == 7 ? 4 :
8876 i == Xsand_stoneout_2 && j == 0 ? 5 :
8877 i == Xsand_stoneout_2 && j == 1 ? 6 :
8878 i == Xsand_stoneout_2 && j == 2 ? 7 :
8879 i == Xsand_stoneout_2 && j == 3 ? 8 :
8880 i == Xsand_stoneout_2 && j == 4 ? 9 :
8881 i == Xsand_stoneout_2 && j == 5 ? 11 :
8882 i == Xsand_stoneout_2 && j == 6 ? 13 :
8883 i == Xsand_stoneout_2 && j == 7 ? 15 :
8884 i == Xboom_bug && j == 1 ? 2 :
8885 i == Xboom_bug && j == 2 ? 2 :
8886 i == Xboom_bug && j == 3 ? 4 :
8887 i == Xboom_bug && j == 4 ? 4 :
8888 i == Xboom_bug && j == 5 ? 2 :
8889 i == Xboom_bug && j == 6 ? 2 :
8890 i == Xboom_bug && j == 7 ? 0 :
8891 i == Xboom_tank && j == 1 ? 2 :
8892 i == Xboom_tank && j == 2 ? 2 :
8893 i == Xboom_tank && j == 3 ? 4 :
8894 i == Xboom_tank && j == 4 ? 4 :
8895 i == Xboom_tank && j == 5 ? 2 :
8896 i == Xboom_tank && j == 6 ? 2 :
8897 i == Xboom_tank && j == 7 ? 0 :
8898 i == Xboom_android && j == 7 ? 6 :
8899 i == Xboom_1 && j == 1 ? 2 :
8900 i == Xboom_1 && j == 2 ? 2 :
8901 i == Xboom_1 && j == 3 ? 4 :
8902 i == Xboom_1 && j == 4 ? 4 :
8903 i == Xboom_1 && j == 5 ? 6 :
8904 i == Xboom_1 && j == 6 ? 6 :
8905 i == Xboom_1 && j == 7 ? 8 :
8906 i == Xboom_2 && j == 0 ? 8 :
8907 i == Xboom_2 && j == 1 ? 8 :
8908 i == Xboom_2 && j == 2 ? 10 :
8909 i == Xboom_2 && j == 3 ? 10 :
8910 i == Xboom_2 && j == 4 ? 10 :
8911 i == Xboom_2 && j == 5 ? 12 :
8912 i == Xboom_2 && j == 6 ? 12 :
8913 i == Xboom_2 && j == 7 ? 12 :
8914 special_animation && j == 4 ? 3 :
8915 effective_action != action ? 0 :
8917 int frame = getAnimationFrame(g->anim_frames,
8920 g->anim_start_frame,
8923 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8924 g->double_movement && is_backside);
8926 g_em->bitmap = src_bitmap;
8927 g_em->src_x = src_x;
8928 g_em->src_y = src_y;
8929 g_em->src_offset_x = 0;
8930 g_em->src_offset_y = 0;
8931 g_em->dst_offset_x = 0;
8932 g_em->dst_offset_y = 0;
8933 g_em->width = TILEX;
8934 g_em->height = TILEY;
8936 g_em->preserve_background = FALSE;
8938 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8941 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8942 effective_action == ACTION_MOVING ||
8943 effective_action == ACTION_PUSHING ||
8944 effective_action == ACTION_EATING)) ||
8945 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8946 effective_action == ACTION_EMPTYING)))
8949 (effective_action == ACTION_FALLING ||
8950 effective_action == ACTION_FILLING ||
8951 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8952 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8953 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8954 int num_steps = (i == Ydrip_1_s ? 16 :
8955 i == Ydrip_1_sB ? 16 :
8956 i == Ydrip_2_s ? 16 :
8957 i == Ydrip_2_sB ? 16 :
8958 i == Xsand_stonein_1 ? 32 :
8959 i == Xsand_stonein_2 ? 32 :
8960 i == Xsand_stonein_3 ? 32 :
8961 i == Xsand_stonein_4 ? 32 :
8962 i == Xsand_stoneout_1 ? 16 :
8963 i == Xsand_stoneout_2 ? 16 : 8);
8964 int cx = ABS(dx) * (TILEX / num_steps);
8965 int cy = ABS(dy) * (TILEY / num_steps);
8966 int step_frame = (i == Ydrip_2_s ? j + 8 :
8967 i == Ydrip_2_sB ? j + 8 :
8968 i == Xsand_stonein_2 ? j + 8 :
8969 i == Xsand_stonein_3 ? j + 16 :
8970 i == Xsand_stonein_4 ? j + 24 :
8971 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8972 int step = (is_backside ? step_frame : num_steps - step_frame);
8974 if (is_backside) // tile where movement starts
8976 if (dx < 0 || dy < 0)
8978 g_em->src_offset_x = cx * step;
8979 g_em->src_offset_y = cy * step;
8983 g_em->dst_offset_x = cx * step;
8984 g_em->dst_offset_y = cy * step;
8987 else // tile where movement ends
8989 if (dx < 0 || dy < 0)
8991 g_em->dst_offset_x = cx * step;
8992 g_em->dst_offset_y = cy * step;
8996 g_em->src_offset_x = cx * step;
8997 g_em->src_offset_y = cy * step;
9001 g_em->width = TILEX - cx * step;
9002 g_em->height = TILEY - cy * step;
9005 // create unique graphic identifier to decide if tile must be redrawn
9006 /* bit 31 - 16 (16 bit): EM style graphic
9007 bit 15 - 12 ( 4 bit): EM style frame
9008 bit 11 - 6 ( 6 bit): graphic width
9009 bit 5 - 0 ( 6 bit): graphic height */
9010 g_em->unique_identifier =
9011 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9015 for (i = 0; i < GAME_TILE_MAX; i++)
9017 for (j = 0; j < 8; j++)
9019 int element = object_mapping[i].element_rnd;
9020 int action = object_mapping[i].action;
9021 int direction = object_mapping[i].direction;
9022 boolean is_backside = object_mapping[i].is_backside;
9023 int graphic_action = el_act_dir2img(element, action, direction);
9024 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9026 if ((action == ACTION_SMASHED_BY_ROCK ||
9027 action == ACTION_SMASHED_BY_SPRING ||
9028 action == ACTION_EATING) &&
9029 graphic_action == graphic_default)
9031 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9032 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9033 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9034 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9037 // no separate animation for "smashed by rock" -- use rock instead
9038 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9039 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9041 g_em->bitmap = g_xx->bitmap;
9042 g_em->src_x = g_xx->src_x;
9043 g_em->src_y = g_xx->src_y;
9044 g_em->src_offset_x = g_xx->src_offset_x;
9045 g_em->src_offset_y = g_xx->src_offset_y;
9046 g_em->dst_offset_x = g_xx->dst_offset_x;
9047 g_em->dst_offset_y = g_xx->dst_offset_y;
9048 g_em->width = g_xx->width;
9049 g_em->height = g_xx->height;
9050 g_em->unique_identifier = g_xx->unique_identifier;
9053 g_em->preserve_background = TRUE;
9058 for (p = 0; p < MAX_PLAYERS; p++)
9060 for (i = 0; i < PLY_MAX; i++)
9062 int element = player_mapping[p][i].element_rnd;
9063 int action = player_mapping[p][i].action;
9064 int direction = player_mapping[p][i].direction;
9066 for (j = 0; j < 8; j++)
9068 int effective_element = element;
9069 int effective_action = action;
9070 int graphic = (direction == MV_NONE ?
9071 el_act2img(effective_element, effective_action) :
9072 el_act_dir2img(effective_element, effective_action,
9074 struct GraphicInfo *g = &graphic_info[graphic];
9075 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9079 int frame = getAnimationFrame(g->anim_frames,
9082 g->anim_start_frame,
9085 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9087 g_em->bitmap = src_bitmap;
9088 g_em->src_x = src_x;
9089 g_em->src_y = src_y;
9090 g_em->src_offset_x = 0;
9091 g_em->src_offset_y = 0;
9092 g_em->dst_offset_x = 0;
9093 g_em->dst_offset_y = 0;
9094 g_em->width = TILEX;
9095 g_em->height = TILEY;
9101 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9102 boolean any_player_moving,
9103 boolean any_player_snapping,
9104 boolean any_player_dropping)
9106 if (frame == 7 && !any_player_dropping)
9108 if (!local_player->was_waiting)
9110 if (!CheckSaveEngineSnapshotToList())
9113 local_player->was_waiting = TRUE;
9116 else if (any_player_moving || any_player_snapping || any_player_dropping)
9118 local_player->was_waiting = FALSE;
9122 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9123 boolean murphy_is_dropping)
9125 if (murphy_is_waiting)
9127 if (!local_player->was_waiting)
9129 if (!CheckSaveEngineSnapshotToList())
9132 local_player->was_waiting = TRUE;
9137 local_player->was_waiting = FALSE;
9141 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9142 boolean button_released)
9144 if (button_released)
9146 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9147 CheckSaveEngineSnapshotToList();
9149 else if (element_clicked)
9151 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9152 CheckSaveEngineSnapshotToList();
9154 game.snapshot.changed_action = TRUE;
9158 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9159 boolean any_player_moving,
9160 boolean any_player_snapping,
9161 boolean any_player_dropping)
9163 if (tape.single_step && tape.recording && !tape.pausing)
9164 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9165 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9167 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9168 any_player_snapping, any_player_dropping);
9170 return tape.pausing;
9173 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9174 boolean murphy_is_dropping)
9176 boolean murphy_starts_dropping = FALSE;
9179 for (i = 0; i < MAX_PLAYERS; i++)
9180 if (stored_player[i].force_dropping)
9181 murphy_starts_dropping = TRUE;
9183 if (tape.single_step && tape.recording && !tape.pausing)
9184 if (murphy_is_waiting && !murphy_starts_dropping)
9185 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9187 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9190 void CheckSingleStepMode_MM(boolean element_clicked,
9191 boolean button_released)
9193 if (tape.single_step && tape.recording && !tape.pausing)
9194 if (button_released)
9195 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9197 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9200 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9201 int graphic, int sync_frame, int x, int y)
9203 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9205 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9208 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9210 return (IS_NEXT_FRAME(sync_frame, graphic));
9213 int getGraphicInfo_Delay(int graphic)
9215 return graphic_info[graphic].anim_delay;
9218 void PlayMenuSoundExt(int sound)
9220 if (sound == SND_UNDEFINED)
9223 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9224 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9227 if (IS_LOOP_SOUND(sound))
9228 PlaySoundLoop(sound);
9233 void PlayMenuSound(void)
9235 PlayMenuSoundExt(menu.sound[game_status]);
9238 void PlayMenuSoundStereo(int sound, int stereo_position)
9240 if (sound == SND_UNDEFINED)
9243 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9244 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9247 if (IS_LOOP_SOUND(sound))
9248 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9250 PlaySoundStereo(sound, stereo_position);
9253 void PlayMenuSoundIfLoopExt(int sound)
9255 if (sound == SND_UNDEFINED)
9258 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9259 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9262 if (IS_LOOP_SOUND(sound))
9263 PlaySoundLoop(sound);
9266 void PlayMenuSoundIfLoop(void)
9268 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9271 void PlayMenuMusicExt(int music)
9273 if (music == MUS_UNDEFINED)
9276 if (!setup.sound_music)
9279 if (IS_LOOP_MUSIC(music))
9280 PlayMusicLoop(music);
9285 void PlayMenuMusic(void)
9287 char *curr_music = getCurrentlyPlayingMusicFilename();
9288 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9290 if (!strEqual(curr_music, next_music))
9291 PlayMenuMusicExt(menu.music[game_status]);
9294 void PlayMenuSoundsAndMusic(void)
9300 static void FadeMenuSounds(void)
9305 static void FadeMenuMusic(void)
9307 char *curr_music = getCurrentlyPlayingMusicFilename();
9308 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9310 if (!strEqual(curr_music, next_music))
9314 void FadeMenuSoundsAndMusic(void)
9320 void PlaySoundActivating(void)
9323 PlaySound(SND_MENU_ITEM_ACTIVATING);
9327 void PlaySoundSelecting(void)
9330 PlaySound(SND_MENU_ITEM_SELECTING);
9334 void ToggleFullscreenIfNeeded(void)
9336 // if setup and video fullscreen state are already matching, nothing do do
9337 if (setup.fullscreen == video.fullscreen_enabled ||
9338 !video.fullscreen_available)
9341 SDLSetWindowFullscreen(setup.fullscreen);
9343 // set setup value according to successfully changed fullscreen mode
9344 setup.fullscreen = video.fullscreen_enabled;
9347 void ChangeWindowScalingIfNeeded(void)
9349 // if setup and video window scaling are already matching, nothing do do
9350 if (setup.window_scaling_percent == video.window_scaling_percent ||
9351 video.fullscreen_enabled)
9354 SDLSetWindowScaling(setup.window_scaling_percent);
9356 // set setup value according to successfully changed window scaling
9357 setup.window_scaling_percent = video.window_scaling_percent;
9360 void ChangeVsyncModeIfNeeded(void)
9362 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9363 int video_vsync_mode = video.vsync_mode;
9365 // if setup and video vsync mode are already matching, nothing do do
9366 if (setup_vsync_mode == video_vsync_mode)
9369 // if renderer is using OpenGL, vsync mode can directly be changed
9370 SDLSetScreenVsyncMode(setup.vsync_mode);
9372 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9373 if (video.vsync_mode == video_vsync_mode)
9375 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9377 // save backbuffer content which gets lost when re-creating screen
9378 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9380 // force re-creating screen and renderer to set new vsync mode
9381 video.fullscreen_enabled = !setup.fullscreen;
9383 // when creating new renderer, destroy textures linked to old renderer
9384 FreeAllImageTextures(); // needs old renderer to free the textures
9386 // re-create screen and renderer (including change of vsync mode)
9387 ChangeVideoModeIfNeeded(setup.fullscreen);
9389 // set setup value according to successfully changed fullscreen mode
9390 setup.fullscreen = video.fullscreen_enabled;
9392 // restore backbuffer content from temporary backbuffer backup bitmap
9393 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9394 FreeBitmap(tmp_backbuffer);
9396 // update visible window/screen
9397 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9399 // when changing vsync mode, re-create textures for new renderer
9400 InitImageTextures();
9403 // set setup value according to successfully changed vsync mode
9404 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9407 static void JoinRectangles(int *x, int *y, int *width, int *height,
9408 int x2, int y2, int width2, int height2)
9410 // do not join with "off-screen" rectangle
9411 if (x2 == -1 || y2 == -1)
9416 *width = MAX(*width, width2);
9417 *height = MAX(*height, height2);
9420 void SetAnimStatus(int anim_status_new)
9422 if (anim_status_new == GAME_MODE_MAIN)
9423 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9424 else if (anim_status_new == GAME_MODE_NAMES)
9425 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9426 else if (anim_status_new == GAME_MODE_SCORES)
9427 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9429 global.anim_status_next = anim_status_new;
9431 // directly set screen modes that are entered without fading
9432 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9433 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9434 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9435 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9436 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9437 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9438 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9439 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9440 global.anim_status = global.anim_status_next;
9443 void SetGameStatus(int game_status_new)
9445 if (game_status_new != game_status)
9446 game_status_last_screen = game_status;
9448 game_status = game_status_new;
9450 SetAnimStatus(game_status_new);
9453 void SetFontStatus(int game_status_new)
9455 static int last_game_status = -1;
9457 if (game_status_new != -1)
9459 // set game status for font use after storing last game status
9460 last_game_status = game_status;
9461 game_status = game_status_new;
9465 // reset game status after font use from last stored game status
9466 game_status = last_game_status;
9470 void ResetFontStatus(void)
9475 void SetLevelSetInfo(char *identifier, int level_nr)
9477 setString(&levelset.identifier, identifier);
9479 levelset.level_nr = level_nr;
9482 boolean CheckIfAllViewportsHaveChanged(void)
9484 // if game status has not changed, viewports have not changed either
9485 if (game_status == game_status_last)
9488 // check if all viewports have changed with current game status
9490 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9491 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9492 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9493 int new_real_sx = vp_playfield->x;
9494 int new_real_sy = vp_playfield->y;
9495 int new_full_sxsize = vp_playfield->width;
9496 int new_full_sysize = vp_playfield->height;
9497 int new_dx = vp_door_1->x;
9498 int new_dy = vp_door_1->y;
9499 int new_dxsize = vp_door_1->width;
9500 int new_dysize = vp_door_1->height;
9501 int new_vx = vp_door_2->x;
9502 int new_vy = vp_door_2->y;
9503 int new_vxsize = vp_door_2->width;
9504 int new_vysize = vp_door_2->height;
9506 boolean playfield_viewport_has_changed =
9507 (new_real_sx != REAL_SX ||
9508 new_real_sy != REAL_SY ||
9509 new_full_sxsize != FULL_SXSIZE ||
9510 new_full_sysize != FULL_SYSIZE);
9512 boolean door_1_viewport_has_changed =
9515 new_dxsize != DXSIZE ||
9516 new_dysize != DYSIZE);
9518 boolean door_2_viewport_has_changed =
9521 new_vxsize != VXSIZE ||
9522 new_vysize != VYSIZE ||
9523 game_status_last == GAME_MODE_EDITOR);
9525 return (playfield_viewport_has_changed &&
9526 door_1_viewport_has_changed &&
9527 door_2_viewport_has_changed);
9530 boolean CheckFadeAll(void)
9532 return (CheckIfGlobalBorderHasChanged() ||
9533 CheckIfAllViewportsHaveChanged());
9536 void ChangeViewportPropertiesIfNeeded(void)
9538 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9539 FALSE : setup.small_game_graphics);
9540 int gfx_game_mode = game_status;
9541 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9543 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9544 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9545 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9546 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9547 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9548 int new_win_xsize = vp_window->width;
9549 int new_win_ysize = vp_window->height;
9550 int border_left = vp_playfield->border_left;
9551 int border_right = vp_playfield->border_right;
9552 int border_top = vp_playfield->border_top;
9553 int border_bottom = vp_playfield->border_bottom;
9554 int new_sx = vp_playfield->x + border_left;
9555 int new_sy = vp_playfield->y + border_top;
9556 int new_sxsize = vp_playfield->width - border_left - border_right;
9557 int new_sysize = vp_playfield->height - border_top - border_bottom;
9558 int new_real_sx = vp_playfield->x;
9559 int new_real_sy = vp_playfield->y;
9560 int new_full_sxsize = vp_playfield->width;
9561 int new_full_sysize = vp_playfield->height;
9562 int new_dx = vp_door_1->x;
9563 int new_dy = vp_door_1->y;
9564 int new_dxsize = vp_door_1->width;
9565 int new_dysize = vp_door_1->height;
9566 int new_vx = vp_door_2->x;
9567 int new_vy = vp_door_2->y;
9568 int new_vxsize = vp_door_2->width;
9569 int new_vysize = vp_door_2->height;
9570 int new_ex = vp_door_3->x;
9571 int new_ey = vp_door_3->y;
9572 int new_exsize = vp_door_3->width;
9573 int new_eysize = vp_door_3->height;
9574 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9575 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9576 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9577 int new_scr_fieldx = new_sxsize / tilesize;
9578 int new_scr_fieldy = new_sysize / tilesize;
9579 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9580 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9581 boolean init_gfx_buffers = FALSE;
9582 boolean init_video_buffer = FALSE;
9583 boolean init_gadgets_and_anims = FALSE;
9584 boolean init_em_graphics = FALSE;
9586 if (new_win_xsize != WIN_XSIZE ||
9587 new_win_ysize != WIN_YSIZE)
9589 WIN_XSIZE = new_win_xsize;
9590 WIN_YSIZE = new_win_ysize;
9592 init_video_buffer = TRUE;
9593 init_gfx_buffers = TRUE;
9594 init_gadgets_and_anims = TRUE;
9596 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9599 if (new_scr_fieldx != SCR_FIELDX ||
9600 new_scr_fieldy != SCR_FIELDY)
9602 // this always toggles between MAIN and GAME when using small tile size
9604 SCR_FIELDX = new_scr_fieldx;
9605 SCR_FIELDY = new_scr_fieldy;
9607 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9618 new_sxsize != SXSIZE ||
9619 new_sysize != SYSIZE ||
9620 new_dxsize != DXSIZE ||
9621 new_dysize != DYSIZE ||
9622 new_vxsize != VXSIZE ||
9623 new_vysize != VYSIZE ||
9624 new_exsize != EXSIZE ||
9625 new_eysize != EYSIZE ||
9626 new_real_sx != REAL_SX ||
9627 new_real_sy != REAL_SY ||
9628 new_full_sxsize != FULL_SXSIZE ||
9629 new_full_sysize != FULL_SYSIZE ||
9630 new_tilesize_var != TILESIZE_VAR
9633 // ------------------------------------------------------------------------
9634 // determine next fading area for changed viewport definitions
9635 // ------------------------------------------------------------------------
9637 // start with current playfield area (default fading area)
9640 FADE_SXSIZE = FULL_SXSIZE;
9641 FADE_SYSIZE = FULL_SYSIZE;
9643 // add new playfield area if position or size has changed
9644 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9645 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9647 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9648 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9651 // add current and new door 1 area if position or size has changed
9652 if (new_dx != DX || new_dy != DY ||
9653 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9655 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9656 DX, DY, DXSIZE, DYSIZE);
9657 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9658 new_dx, new_dy, new_dxsize, new_dysize);
9661 // add current and new door 2 area if position or size has changed
9662 if (new_vx != VX || new_vy != VY ||
9663 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9665 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9666 VX, VY, VXSIZE, VYSIZE);
9667 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9668 new_vx, new_vy, new_vxsize, new_vysize);
9671 // ------------------------------------------------------------------------
9672 // handle changed tile size
9673 // ------------------------------------------------------------------------
9675 if (new_tilesize_var != TILESIZE_VAR)
9677 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9679 // changing tile size invalidates scroll values of engine snapshots
9680 FreeEngineSnapshotSingle();
9682 // changing tile size requires update of graphic mapping for EM engine
9683 init_em_graphics = TRUE;
9694 SXSIZE = new_sxsize;
9695 SYSIZE = new_sysize;
9696 DXSIZE = new_dxsize;
9697 DYSIZE = new_dysize;
9698 VXSIZE = new_vxsize;
9699 VYSIZE = new_vysize;
9700 EXSIZE = new_exsize;
9701 EYSIZE = new_eysize;
9702 REAL_SX = new_real_sx;
9703 REAL_SY = new_real_sy;
9704 FULL_SXSIZE = new_full_sxsize;
9705 FULL_SYSIZE = new_full_sysize;
9706 TILESIZE_VAR = new_tilesize_var;
9708 init_gfx_buffers = TRUE;
9709 init_gadgets_and_anims = TRUE;
9711 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9712 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9715 if (init_gfx_buffers)
9717 // Debug("tools:viewport", "init_gfx_buffers");
9719 SCR_FIELDX = new_scr_fieldx_buffers;
9720 SCR_FIELDY = new_scr_fieldy_buffers;
9724 SCR_FIELDX = new_scr_fieldx;
9725 SCR_FIELDY = new_scr_fieldy;
9727 SetDrawDeactivationMask(REDRAW_NONE);
9728 SetDrawBackgroundMask(REDRAW_FIELD);
9731 if (init_video_buffer)
9733 // Debug("tools:viewport", "init_video_buffer");
9735 FreeAllImageTextures(); // needs old renderer to free the textures
9737 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9738 InitImageTextures();
9741 if (init_gadgets_and_anims)
9743 // Debug("tools:viewport", "init_gadgets_and_anims");
9746 InitGlobalAnimations();
9749 if (init_em_graphics)
9751 InitGraphicInfo_EM();