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 setRequestBasePosition(int *x, int *y)
2815 int sx_base, sy_base;
2817 if (request.x != -1)
2818 sx_base = request.x;
2819 else if (request.align == ALIGN_LEFT)
2821 else if (request.align == ALIGN_RIGHT)
2822 sx_base = SX + SXSIZE;
2824 sx_base = SX + SXSIZE / 2;
2826 if (request.y != -1)
2827 sy_base = request.y;
2828 else if (request.valign == VALIGN_TOP)
2830 else if (request.valign == VALIGN_BOTTOM)
2831 sy_base = SY + SYSIZE;
2833 sy_base = SY + SYSIZE / 2;
2839 static void setRequestPositionExt(int *x, int *y, int width, int height,
2840 boolean add_border_size)
2842 int border_size = request.border_size;
2843 int sx_base, sy_base;
2846 setRequestBasePosition(&sx_base, &sy_base);
2848 if (request.align == ALIGN_LEFT)
2850 else if (request.align == ALIGN_RIGHT)
2851 sx = sx_base - width;
2853 sx = sx_base - width / 2;
2855 if (request.valign == VALIGN_TOP)
2857 else if (request.valign == VALIGN_BOTTOM)
2858 sy = sy_base - height;
2860 sy = sy_base - height / 2;
2862 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2863 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2865 if (add_border_size)
2875 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2877 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2880 static void DrawEnvelopeRequest(char *text)
2882 char *text_final = text;
2883 char *text_door_style = NULL;
2884 int graphic = IMG_BACKGROUND_REQUEST;
2885 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2886 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2887 int font_nr = FONT_REQUEST;
2888 int font_width = getFontWidth(font_nr);
2889 int font_height = getFontHeight(font_nr);
2890 int border_size = request.border_size;
2891 int line_spacing = request.line_spacing;
2892 int line_height = font_height + line_spacing;
2893 int max_text_width = request.width - 2 * border_size;
2894 int max_text_height = request.height - 2 * border_size;
2895 int line_length = max_text_width / font_width;
2896 int max_lines = max_text_height / line_height;
2897 int text_width = line_length * font_width;
2898 int width = request.width;
2899 int height = request.height;
2900 int tile_size = MAX(request.step_offset, 1);
2901 int x_steps = width / tile_size;
2902 int y_steps = height / tile_size;
2903 int sx_offset = border_size;
2904 int sy_offset = border_size;
2908 if (request.centered)
2909 sx_offset = (request.width - text_width) / 2;
2911 if (request.wrap_single_words && !request.autowrap)
2913 char *src_text_ptr, *dst_text_ptr;
2915 text_door_style = checked_malloc(2 * strlen(text) + 1);
2917 src_text_ptr = text;
2918 dst_text_ptr = text_door_style;
2920 while (*src_text_ptr)
2922 if (*src_text_ptr == ' ' ||
2923 *src_text_ptr == '?' ||
2924 *src_text_ptr == '!')
2925 *dst_text_ptr++ = '\n';
2927 if (*src_text_ptr != ' ')
2928 *dst_text_ptr++ = *src_text_ptr;
2933 *dst_text_ptr = '\0';
2935 text_final = text_door_style;
2938 setRequestPosition(&sx, &sy, FALSE);
2940 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2942 for (y = 0; y < y_steps; y++)
2943 for (x = 0; x < x_steps; x++)
2944 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2945 x, y, x_steps, y_steps,
2946 tile_size, tile_size);
2948 // force DOOR font inside door area
2949 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2951 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2952 line_length, -1, max_lines, line_spacing, mask_mode,
2953 request.autowrap, request.centered, FALSE);
2957 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2958 RedrawGadget(tool_gadget[i]);
2960 // store readily prepared envelope request for later use when animating
2961 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2963 if (text_door_style)
2964 free(text_door_style);
2967 static void AnimateEnvelopeRequest(int anim_mode, int action)
2969 int graphic = IMG_BACKGROUND_REQUEST;
2970 boolean draw_masked = graphic_info[graphic].draw_masked;
2971 int delay_value_normal = request.step_delay;
2972 int delay_value_fast = delay_value_normal / 2;
2973 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2974 boolean no_delay = (tape.warp_forward);
2975 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2976 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2977 unsigned int anim_delay = 0;
2979 int tile_size = MAX(request.step_offset, 1);
2980 int max_xsize = request.width / tile_size;
2981 int max_ysize = request.height / tile_size;
2982 int max_xsize_inner = max_xsize - 2;
2983 int max_ysize_inner = max_ysize - 2;
2985 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2986 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2987 int xend = max_xsize_inner;
2988 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2989 int xstep = (xstart < xend ? 1 : 0);
2990 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2992 int end = MAX(xend - xstart, yend - ystart);
2995 if (setup.quick_doors)
3002 for (i = start; i <= end; i++)
3004 int last_frame = end; // last frame of this "for" loop
3005 int x = xstart + i * xstep;
3006 int y = ystart + i * ystep;
3007 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3008 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3009 int xsize_size_left = (xsize - 1) * tile_size;
3010 int ysize_size_top = (ysize - 1) * tile_size;
3011 int max_xsize_pos = (max_xsize - 1) * tile_size;
3012 int max_ysize_pos = (max_ysize - 1) * tile_size;
3013 int width = xsize * tile_size;
3014 int height = ysize * tile_size;
3019 setRequestPosition(&src_x, &src_y, FALSE);
3020 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3022 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3024 for (yy = 0; yy < 2; yy++)
3026 for (xx = 0; xx < 2; xx++)
3028 int src_xx = src_x + xx * max_xsize_pos;
3029 int src_yy = src_y + yy * max_ysize_pos;
3030 int dst_xx = dst_x + xx * xsize_size_left;
3031 int dst_yy = dst_y + yy * ysize_size_top;
3032 int xx_size = (xx ? tile_size : xsize_size_left);
3033 int yy_size = (yy ? tile_size : ysize_size_top);
3036 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3037 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3039 BlitBitmap(bitmap_db_store_2, backbuffer,
3040 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3044 redraw_mask |= REDRAW_FIELD;
3048 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3051 ClearAutoRepeatKeyEvents();
3054 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3056 int graphic = IMG_BACKGROUND_REQUEST;
3057 int sound_opening = SND_REQUEST_OPENING;
3058 int sound_closing = SND_REQUEST_CLOSING;
3059 int anim_mode_1 = request.anim_mode; // (higher priority)
3060 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3061 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3062 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3063 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3065 if (game_status == GAME_MODE_PLAYING)
3066 BlitScreenToBitmap(backbuffer);
3068 SetDrawtoField(DRAW_TO_BACKBUFFER);
3070 // SetDrawBackgroundMask(REDRAW_NONE);
3072 if (action == ACTION_OPENING)
3074 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3076 if (req_state & REQ_ASK)
3078 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3079 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3080 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3081 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3083 else if (req_state & REQ_CONFIRM)
3085 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3086 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3088 else if (req_state & REQ_PLAYER)
3090 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3091 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3092 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3093 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3096 DrawEnvelopeRequest(text);
3099 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3101 if (action == ACTION_OPENING)
3103 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3105 if (anim_mode == ANIM_DEFAULT)
3106 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3108 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3112 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3114 if (anim_mode != ANIM_NONE)
3115 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3117 if (anim_mode == ANIM_DEFAULT)
3118 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3121 game.envelope_active = FALSE;
3123 if (action == ACTION_CLOSING)
3124 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3126 // SetDrawBackgroundMask(last_draw_background_mask);
3128 redraw_mask |= REDRAW_FIELD;
3132 if (action == ACTION_CLOSING &&
3133 game_status == GAME_MODE_PLAYING &&
3134 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3135 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3138 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3140 if (IS_MM_WALL(element))
3142 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3148 int graphic = el2preimg(element);
3150 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3151 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3156 void DrawLevel(int draw_background_mask)
3160 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3161 SetDrawBackgroundMask(draw_background_mask);
3165 for (x = BX1; x <= BX2; x++)
3166 for (y = BY1; y <= BY2; y++)
3167 DrawScreenField(x, y);
3169 redraw_mask |= REDRAW_FIELD;
3172 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3177 for (x = 0; x < size_x; x++)
3178 for (y = 0; y < size_y; y++)
3179 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3181 redraw_mask |= REDRAW_FIELD;
3184 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3188 for (x = 0; x < size_x; x++)
3189 for (y = 0; y < size_y; y++)
3190 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3192 redraw_mask |= REDRAW_FIELD;
3195 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3197 boolean show_level_border = (BorderElement != EL_EMPTY);
3198 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3199 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3200 int tile_size = preview.tile_size;
3201 int preview_width = preview.xsize * tile_size;
3202 int preview_height = preview.ysize * tile_size;
3203 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3204 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3205 int real_preview_width = real_preview_xsize * tile_size;
3206 int real_preview_height = real_preview_ysize * tile_size;
3207 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3208 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3211 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3214 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3216 dst_x += (preview_width - real_preview_width) / 2;
3217 dst_y += (preview_height - real_preview_height) / 2;
3219 for (x = 0; x < real_preview_xsize; x++)
3221 for (y = 0; y < real_preview_ysize; y++)
3223 int lx = from_x + x + (show_level_border ? -1 : 0);
3224 int ly = from_y + y + (show_level_border ? -1 : 0);
3225 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3226 getBorderElement(lx, ly));
3228 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3229 element, tile_size);
3233 redraw_mask |= REDRAW_FIELD;
3236 #define MICROLABEL_EMPTY 0
3237 #define MICROLABEL_LEVEL_NAME 1
3238 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3239 #define MICROLABEL_LEVEL_AUTHOR 3
3240 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3241 #define MICROLABEL_IMPORTED_FROM 5
3242 #define MICROLABEL_IMPORTED_BY_HEAD 6
3243 #define MICROLABEL_IMPORTED_BY 7
3245 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3247 int max_text_width = SXSIZE;
3248 int font_width = getFontWidth(font_nr);
3250 if (pos->align == ALIGN_CENTER)
3251 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3252 else if (pos->align == ALIGN_RIGHT)
3253 max_text_width = pos->x;
3255 max_text_width = SXSIZE - pos->x;
3257 return max_text_width / font_width;
3260 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3262 char label_text[MAX_OUTPUT_LINESIZE + 1];
3263 int max_len_label_text;
3264 int font_nr = pos->font;
3267 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3270 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3271 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3272 mode == MICROLABEL_IMPORTED_BY_HEAD)
3273 font_nr = pos->font_alt;
3275 max_len_label_text = getMaxTextLength(pos, font_nr);
3277 if (pos->size != -1)
3278 max_len_label_text = pos->size;
3280 for (i = 0; i < max_len_label_text; i++)
3281 label_text[i] = ' ';
3282 label_text[max_len_label_text] = '\0';
3284 if (strlen(label_text) > 0)
3285 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3288 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3289 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3290 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3291 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3292 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3293 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3294 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3295 max_len_label_text);
3296 label_text[max_len_label_text] = '\0';
3298 if (strlen(label_text) > 0)
3299 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3301 redraw_mask |= REDRAW_FIELD;
3304 static void DrawPreviewLevelLabel(int mode)
3306 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3309 static void DrawPreviewLevelInfo(int mode)
3311 if (mode == MICROLABEL_LEVEL_NAME)
3312 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3313 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3314 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3317 static void DrawPreviewLevelExt(boolean restart)
3319 static unsigned int scroll_delay = 0;
3320 static unsigned int label_delay = 0;
3321 static int from_x, from_y, scroll_direction;
3322 static int label_state, label_counter;
3323 unsigned int scroll_delay_value = preview.step_delay;
3324 boolean show_level_border = (BorderElement != EL_EMPTY);
3325 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3326 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3333 if (preview.anim_mode == ANIM_CENTERED)
3335 if (level_xsize > preview.xsize)
3336 from_x = (level_xsize - preview.xsize) / 2;
3337 if (level_ysize > preview.ysize)
3338 from_y = (level_ysize - preview.ysize) / 2;
3341 from_x += preview.xoffset;
3342 from_y += preview.yoffset;
3344 scroll_direction = MV_RIGHT;
3348 DrawPreviewLevelPlayfield(from_x, from_y);
3349 DrawPreviewLevelLabel(label_state);
3351 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3352 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3354 // initialize delay counters
3355 DelayReached(&scroll_delay, 0);
3356 DelayReached(&label_delay, 0);
3358 if (leveldir_current->name)
3360 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3361 char label_text[MAX_OUTPUT_LINESIZE + 1];
3362 int font_nr = pos->font;
3363 int max_len_label_text = getMaxTextLength(pos, font_nr);
3365 if (pos->size != -1)
3366 max_len_label_text = pos->size;
3368 strncpy(label_text, leveldir_current->name, max_len_label_text);
3369 label_text[max_len_label_text] = '\0';
3371 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3372 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3378 // scroll preview level, if needed
3379 if (preview.anim_mode != ANIM_NONE &&
3380 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3381 DelayReached(&scroll_delay, scroll_delay_value))
3383 switch (scroll_direction)
3388 from_x -= preview.step_offset;
3389 from_x = (from_x < 0 ? 0 : from_x);
3392 scroll_direction = MV_UP;
3396 if (from_x < level_xsize - preview.xsize)
3398 from_x += preview.step_offset;
3399 from_x = (from_x > level_xsize - preview.xsize ?
3400 level_xsize - preview.xsize : from_x);
3403 scroll_direction = MV_DOWN;
3409 from_y -= preview.step_offset;
3410 from_y = (from_y < 0 ? 0 : from_y);
3413 scroll_direction = MV_RIGHT;
3417 if (from_y < level_ysize - preview.ysize)
3419 from_y += preview.step_offset;
3420 from_y = (from_y > level_ysize - preview.ysize ?
3421 level_ysize - preview.ysize : from_y);
3424 scroll_direction = MV_LEFT;
3431 DrawPreviewLevelPlayfield(from_x, from_y);
3434 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3435 // redraw micro level label, if needed
3436 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3437 !strEqual(level.author, ANONYMOUS_NAME) &&
3438 !strEqual(level.author, leveldir_current->name) &&
3439 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3441 int max_label_counter = 23;
3443 if (leveldir_current->imported_from != NULL &&
3444 strlen(leveldir_current->imported_from) > 0)
3445 max_label_counter += 14;
3446 if (leveldir_current->imported_by != NULL &&
3447 strlen(leveldir_current->imported_by) > 0)
3448 max_label_counter += 14;
3450 label_counter = (label_counter + 1) % max_label_counter;
3451 label_state = (label_counter >= 0 && label_counter <= 7 ?
3452 MICROLABEL_LEVEL_NAME :
3453 label_counter >= 9 && label_counter <= 12 ?
3454 MICROLABEL_LEVEL_AUTHOR_HEAD :
3455 label_counter >= 14 && label_counter <= 21 ?
3456 MICROLABEL_LEVEL_AUTHOR :
3457 label_counter >= 23 && label_counter <= 26 ?
3458 MICROLABEL_IMPORTED_FROM_HEAD :
3459 label_counter >= 28 && label_counter <= 35 ?
3460 MICROLABEL_IMPORTED_FROM :
3461 label_counter >= 37 && label_counter <= 40 ?
3462 MICROLABEL_IMPORTED_BY_HEAD :
3463 label_counter >= 42 && label_counter <= 49 ?
3464 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3466 if (leveldir_current->imported_from == NULL &&
3467 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3468 label_state == MICROLABEL_IMPORTED_FROM))
3469 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3470 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3472 DrawPreviewLevelLabel(label_state);
3476 void DrawPreviewPlayers(void)
3478 if (game_status != GAME_MODE_MAIN)
3481 // do not draw preview players if level preview redefined, but players aren't
3482 if (preview.redefined && !menu.main.preview_players.redefined)
3485 boolean player_found[MAX_PLAYERS];
3486 int num_players = 0;
3489 for (i = 0; i < MAX_PLAYERS; i++)
3490 player_found[i] = FALSE;
3492 // check which players can be found in the level (simple approach)
3493 for (x = 0; x < lev_fieldx; x++)
3495 for (y = 0; y < lev_fieldy; y++)
3497 int element = level.field[x][y];
3499 if (ELEM_IS_PLAYER(element))
3501 int player_nr = GET_PLAYER_NR(element);
3503 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3505 if (!player_found[player_nr])
3508 player_found[player_nr] = TRUE;
3513 struct TextPosInfo *pos = &menu.main.preview_players;
3514 int tile_size = pos->tile_size;
3515 int border_size = pos->border_size;
3516 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3517 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3518 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3519 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3520 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3521 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3522 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3523 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3524 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3525 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3526 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3527 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3529 // clear area in which the players will be drawn
3530 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3531 max_players_width, max_players_height);
3533 if (!network.enabled && !setup.team_mode)
3536 // only draw players if level is suited for team mode
3537 if (num_players < 2)
3540 // draw all players that were found in the level
3541 for (i = 0; i < MAX_PLAYERS; i++)
3543 if (player_found[i])
3545 int graphic = el2img(EL_PLAYER_1 + i);
3547 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3549 xpos += player_xoffset;
3550 ypos += player_yoffset;
3555 void DrawPreviewLevelInitial(void)
3557 DrawPreviewLevelExt(TRUE);
3558 DrawPreviewPlayers();
3561 void DrawPreviewLevelAnimation(void)
3563 DrawPreviewLevelExt(FALSE);
3566 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3567 int border_size, int font_nr)
3569 int graphic = el2img(EL_PLAYER_1 + player_nr);
3570 int font_height = getFontHeight(font_nr);
3571 int player_height = MAX(tile_size, font_height);
3572 int xoffset_text = tile_size + border_size;
3573 int yoffset_text = (player_height - font_height) / 2;
3574 int yoffset_graphic = (player_height - tile_size) / 2;
3575 char *player_name = getNetworkPlayerName(player_nr + 1);
3577 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3579 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3582 static void DrawNetworkPlayersExt(boolean force)
3584 if (game_status != GAME_MODE_MAIN)
3587 if (!network.connected && !force)
3590 // do not draw network players if level preview redefined, but players aren't
3591 if (preview.redefined && !menu.main.network_players.redefined)
3594 int num_players = 0;
3597 for (i = 0; i < MAX_PLAYERS; i++)
3598 if (stored_player[i].connected_network)
3601 struct TextPosInfo *pos = &menu.main.network_players;
3602 int tile_size = pos->tile_size;
3603 int border_size = pos->border_size;
3604 int xoffset_text = tile_size + border_size;
3605 int font_nr = pos->font;
3606 int font_width = getFontWidth(font_nr);
3607 int font_height = getFontHeight(font_nr);
3608 int player_height = MAX(tile_size, font_height);
3609 int player_yoffset = player_height + border_size;
3610 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3611 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3612 int all_players_height = num_players * player_yoffset - border_size;
3613 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3614 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3615 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3617 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3618 max_players_width, max_players_height);
3620 // first draw local network player ...
3621 for (i = 0; i < MAX_PLAYERS; i++)
3623 if (stored_player[i].connected_network &&
3624 stored_player[i].connected_locally)
3626 char *player_name = getNetworkPlayerName(i + 1);
3627 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3628 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3630 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3632 ypos += player_yoffset;
3636 // ... then draw all other network players
3637 for (i = 0; i < MAX_PLAYERS; i++)
3639 if (stored_player[i].connected_network &&
3640 !stored_player[i].connected_locally)
3642 char *player_name = getNetworkPlayerName(i + 1);
3643 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3644 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3646 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3648 ypos += player_yoffset;
3653 void DrawNetworkPlayers(void)
3655 DrawNetworkPlayersExt(FALSE);
3658 void ClearNetworkPlayers(void)
3660 DrawNetworkPlayersExt(TRUE);
3663 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3664 int graphic, int sync_frame,
3667 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3669 if (mask_mode == USE_MASKING)
3670 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3672 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3675 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3676 int graphic, int sync_frame, int mask_mode)
3678 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3680 if (mask_mode == USE_MASKING)
3681 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3683 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3686 static void DrawGraphicAnimation(int x, int y, int graphic)
3688 int lx = LEVELX(x), ly = LEVELY(y);
3690 if (!IN_SCR_FIELD(x, y))
3693 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3694 graphic, GfxFrame[lx][ly], NO_MASKING);
3696 MarkTileDirty(x, y);
3699 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3701 int lx = LEVELX(x), ly = LEVELY(y);
3703 if (!IN_SCR_FIELD(x, y))
3706 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3707 graphic, GfxFrame[lx][ly], NO_MASKING);
3708 MarkTileDirty(x, y);
3711 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3713 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3716 void DrawLevelElementAnimation(int x, int y, int element)
3718 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3720 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3723 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3725 int sx = SCREENX(x), sy = SCREENY(y);
3727 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3730 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3733 DrawGraphicAnimation(sx, sy, graphic);
3736 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3737 DrawLevelFieldCrumbled(x, y);
3739 if (GFX_CRUMBLED(Tile[x][y]))
3740 DrawLevelFieldCrumbled(x, y);
3744 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3746 int sx = SCREENX(x), sy = SCREENY(y);
3749 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3752 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3754 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3757 DrawGraphicAnimation(sx, sy, graphic);
3759 if (GFX_CRUMBLED(element))
3760 DrawLevelFieldCrumbled(x, y);
3763 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3765 if (player->use_murphy)
3767 // this works only because currently only one player can be "murphy" ...
3768 static int last_horizontal_dir = MV_LEFT;
3769 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3771 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3772 last_horizontal_dir = move_dir;
3774 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3776 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3778 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3784 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3787 static boolean equalGraphics(int graphic1, int graphic2)
3789 struct GraphicInfo *g1 = &graphic_info[graphic1];
3790 struct GraphicInfo *g2 = &graphic_info[graphic2];
3792 return (g1->bitmap == g2->bitmap &&
3793 g1->src_x == g2->src_x &&
3794 g1->src_y == g2->src_y &&
3795 g1->anim_frames == g2->anim_frames &&
3796 g1->anim_delay == g2->anim_delay &&
3797 g1->anim_mode == g2->anim_mode);
3800 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3804 DRAW_PLAYER_STAGE_INIT = 0,
3805 DRAW_PLAYER_STAGE_LAST_FIELD,
3806 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3807 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3808 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3809 DRAW_PLAYER_STAGE_PLAYER,
3811 DRAW_PLAYER_STAGE_PLAYER,
3812 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3814 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3815 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3817 NUM_DRAW_PLAYER_STAGES
3820 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3822 static int static_last_player_graphic[MAX_PLAYERS];
3823 static int static_last_player_frame[MAX_PLAYERS];
3824 static boolean static_player_is_opaque[MAX_PLAYERS];
3825 static boolean draw_player[MAX_PLAYERS];
3826 int pnr = player->index_nr;
3828 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3830 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3831 static_last_player_frame[pnr] = player->Frame;
3832 static_player_is_opaque[pnr] = FALSE;
3834 draw_player[pnr] = TRUE;
3837 if (!draw_player[pnr])
3841 if (!IN_LEV_FIELD(player->jx, player->jy))
3843 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3844 Debug("draw:DrawPlayerExt", "This should never happen!");
3846 draw_player[pnr] = FALSE;
3852 int last_player_graphic = static_last_player_graphic[pnr];
3853 int last_player_frame = static_last_player_frame[pnr];
3854 boolean player_is_opaque = static_player_is_opaque[pnr];
3856 int jx = player->jx;
3857 int jy = player->jy;
3858 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3859 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3860 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3861 int last_jx = (player->is_moving ? jx - dx : jx);
3862 int last_jy = (player->is_moving ? jy - dy : jy);
3863 int next_jx = jx + dx;
3864 int next_jy = jy + dy;
3865 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3866 int sx = SCREENX(jx);
3867 int sy = SCREENY(jy);
3868 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3869 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3870 int element = Tile[jx][jy];
3871 int last_element = Tile[last_jx][last_jy];
3872 int action = (player->is_pushing ? ACTION_PUSHING :
3873 player->is_digging ? ACTION_DIGGING :
3874 player->is_collecting ? ACTION_COLLECTING :
3875 player->is_moving ? ACTION_MOVING :
3876 player->is_snapping ? ACTION_SNAPPING :
3877 player->is_dropping ? ACTION_DROPPING :
3878 player->is_waiting ? player->action_waiting :
3881 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3883 // ------------------------------------------------------------------------
3884 // initialize drawing the player
3885 // ------------------------------------------------------------------------
3887 draw_player[pnr] = FALSE;
3889 // GfxElement[][] is set to the element the player is digging or collecting;
3890 // remove also for off-screen player if the player is not moving anymore
3891 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3892 GfxElement[jx][jy] = EL_UNDEFINED;
3894 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3897 if (element == EL_EXPLOSION)
3900 InitPlayerGfxAnimation(player, action, move_dir);
3902 draw_player[pnr] = TRUE;
3904 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3906 // ------------------------------------------------------------------------
3907 // draw things in the field the player is leaving, if needed
3908 // ------------------------------------------------------------------------
3910 if (!IN_SCR_FIELD(sx, sy))
3911 draw_player[pnr] = FALSE;
3913 if (!player->is_moving)
3916 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3918 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3920 if (last_element == EL_DYNAMITE_ACTIVE ||
3921 last_element == EL_EM_DYNAMITE_ACTIVE ||
3922 last_element == EL_SP_DISK_RED_ACTIVE)
3923 DrawDynamite(last_jx, last_jy);
3925 DrawLevelFieldThruMask(last_jx, last_jy);
3927 else if (last_element == EL_DYNAMITE_ACTIVE ||
3928 last_element == EL_EM_DYNAMITE_ACTIVE ||
3929 last_element == EL_SP_DISK_RED_ACTIVE)
3930 DrawDynamite(last_jx, last_jy);
3932 DrawLevelField(last_jx, last_jy);
3934 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3935 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3937 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3939 // ------------------------------------------------------------------------
3940 // draw things behind the player, if needed
3941 // ------------------------------------------------------------------------
3945 DrawLevelElement(jx, jy, Back[jx][jy]);
3950 if (IS_ACTIVE_BOMB(element))
3952 DrawLevelElement(jx, jy, EL_EMPTY);
3957 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3959 int old_element = GfxElement[jx][jy];
3960 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3961 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3963 if (GFX_CRUMBLED(old_element))
3964 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3966 DrawGraphic(sx, sy, old_graphic, frame);
3968 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3969 static_player_is_opaque[pnr] = TRUE;
3973 GfxElement[jx][jy] = EL_UNDEFINED;
3975 // make sure that pushed elements are drawn with correct frame rate
3976 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3978 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3979 GfxFrame[jx][jy] = player->StepFrame;
3981 DrawLevelField(jx, jy);
3984 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
3986 // ------------------------------------------------------------------------
3987 // draw things the player is pushing, if needed
3988 // ------------------------------------------------------------------------
3990 if (!player->is_pushing || !player->is_moving)
3993 int gfx_frame = GfxFrame[jx][jy];
3995 if (!IS_MOVING(jx, jy)) // push movement already finished
3997 element = Tile[next_jx][next_jy];
3998 gfx_frame = GfxFrame[next_jx][next_jy];
4001 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4002 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4003 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4005 // draw background element under pushed element (like the Sokoban field)
4006 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4008 // this allows transparent pushing animation over non-black background
4011 DrawLevelElement(jx, jy, Back[jx][jy]);
4013 DrawLevelElement(jx, jy, EL_EMPTY);
4015 if (Back[next_jx][next_jy])
4016 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4018 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4020 else if (Back[next_jx][next_jy])
4021 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4023 int px = SCREENX(jx), py = SCREENY(jy);
4024 int pxx = (TILEX - ABS(sxx)) * dx;
4025 int pyy = (TILEY - ABS(syy)) * dy;
4028 // do not draw (EM style) pushing animation when pushing is finished
4029 // (two-tile animations usually do not contain start and end frame)
4030 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4031 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4033 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4035 // masked drawing is needed for EMC style (double) movement graphics
4036 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4037 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4040 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4042 // ------------------------------------------------------------------------
4043 // draw player himself
4044 // ------------------------------------------------------------------------
4046 int graphic = getPlayerGraphic(player, move_dir);
4048 // in the case of changed player action or direction, prevent the current
4049 // animation frame from being restarted for identical animations
4050 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4051 player->Frame = last_player_frame;
4053 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4055 if (player_is_opaque)
4056 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4058 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4060 if (SHIELD_ON(player))
4062 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4063 IMG_SHIELD_NORMAL_ACTIVE);
4064 frame = getGraphicAnimationFrame(graphic, -1);
4066 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4069 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4071 // ------------------------------------------------------------------------
4072 // draw things in front of player (active dynamite or dynabombs)
4073 // ------------------------------------------------------------------------
4075 if (IS_ACTIVE_BOMB(element))
4077 int graphic = el2img(element);
4078 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4080 if (game.emulation == EMU_SUPAPLEX)
4081 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4083 DrawGraphicThruMask(sx, sy, graphic, frame);
4086 if (player_is_moving && last_element == EL_EXPLOSION)
4088 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4089 GfxElement[last_jx][last_jy] : EL_EMPTY);
4090 int graphic = el_act2img(element, ACTION_EXPLODING);
4091 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4092 int phase = ExplodePhase[last_jx][last_jy] - 1;
4093 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4096 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4099 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4101 // ------------------------------------------------------------------------
4102 // draw elements the player is just walking/passing through/under
4103 // ------------------------------------------------------------------------
4105 if (player_is_moving)
4107 // handle the field the player is leaving ...
4108 if (IS_ACCESSIBLE_INSIDE(last_element))
4109 DrawLevelField(last_jx, last_jy);
4110 else if (IS_ACCESSIBLE_UNDER(last_element))
4111 DrawLevelFieldThruMask(last_jx, last_jy);
4114 // do not redraw accessible elements if the player is just pushing them
4115 if (!player_is_moving || !player->is_pushing)
4117 // ... and the field the player is entering
4118 if (IS_ACCESSIBLE_INSIDE(element))
4119 DrawLevelField(jx, jy);
4120 else if (IS_ACCESSIBLE_UNDER(element))
4121 DrawLevelFieldThruMask(jx, jy);
4124 MarkTileDirty(sx, sy);
4128 void DrawPlayer(struct PlayerInfo *player)
4132 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4133 DrawPlayerExt(player, i);
4136 void DrawAllPlayers(void)
4140 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4141 for (j = 0; j < MAX_PLAYERS; j++)
4142 if (stored_player[j].active)
4143 DrawPlayerExt(&stored_player[j], i);
4146 void DrawPlayerField(int x, int y)
4148 if (!IS_PLAYER(x, y))
4151 DrawPlayer(PLAYERINFO(x, y));
4154 // ----------------------------------------------------------------------------
4156 void WaitForEventToContinue(void)
4158 boolean first_wait = TRUE;
4159 boolean still_wait = TRUE;
4161 if (program.headless)
4164 // simulate releasing mouse button over last gadget, if still pressed
4166 HandleGadgets(-1, -1, 0);
4168 button_status = MB_RELEASED;
4171 ClearPlayerAction();
4177 if (NextValidEvent(&event))
4181 case EVENT_BUTTONPRESS:
4182 case EVENT_FINGERPRESS:
4186 case EVENT_BUTTONRELEASE:
4187 case EVENT_FINGERRELEASE:
4188 still_wait = first_wait;
4191 case EVENT_KEYPRESS:
4192 case SDL_CONTROLLERBUTTONDOWN:
4193 case SDL_JOYBUTTONDOWN:
4198 HandleOtherEvents(&event);
4202 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4207 if (!PendingEvent())
4212 #define MAX_REQUEST_LINES 13
4213 #define MAX_REQUEST_LINE_FONT1_LEN 7
4214 #define MAX_REQUEST_LINE_FONT2_LEN 10
4216 static int RequestHandleEvents(unsigned int req_state)
4218 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4220 int draw_buffer_last = GetDrawtoField();
4221 int width = request.width;
4222 int height = request.height;
4226 // when showing request dialog after game ended, deactivate game panel
4227 if (game_just_ended)
4228 game.panel.active = FALSE;
4230 game.request_active = TRUE;
4232 setRequestPosition(&sx, &sy, FALSE);
4234 button_status = MB_RELEASED;
4236 request_gadget_id = -1;
4241 if (game_just_ended)
4243 SetDrawtoField(draw_buffer_last);
4245 HandleGameActions();
4247 SetDrawtoField(DRAW_TO_BACKBUFFER);
4249 if (global.use_envelope_request)
4251 // copy current state of request area to middle of playfield area
4252 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4260 while (NextValidEvent(&event))
4264 case EVENT_BUTTONPRESS:
4265 case EVENT_BUTTONRELEASE:
4266 case EVENT_MOTIONNOTIFY:
4270 if (event.type == EVENT_MOTIONNOTIFY)
4275 motion_status = TRUE;
4276 mx = ((MotionEvent *) &event)->x;
4277 my = ((MotionEvent *) &event)->y;
4281 motion_status = FALSE;
4282 mx = ((ButtonEvent *) &event)->x;
4283 my = ((ButtonEvent *) &event)->y;
4284 if (event.type == EVENT_BUTTONPRESS)
4285 button_status = ((ButtonEvent *) &event)->button;
4287 button_status = MB_RELEASED;
4290 // this sets 'request_gadget_id'
4291 HandleGadgets(mx, my, button_status);
4293 switch (request_gadget_id)
4295 case TOOL_CTRL_ID_YES:
4296 case TOOL_CTRL_ID_TOUCH_YES:
4299 case TOOL_CTRL_ID_NO:
4300 case TOOL_CTRL_ID_TOUCH_NO:
4303 case TOOL_CTRL_ID_CONFIRM:
4304 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4305 result = TRUE | FALSE;
4308 case TOOL_CTRL_ID_PLAYER_1:
4311 case TOOL_CTRL_ID_PLAYER_2:
4314 case TOOL_CTRL_ID_PLAYER_3:
4317 case TOOL_CTRL_ID_PLAYER_4:
4322 // only check clickable animations if no request gadget clicked
4323 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4330 case SDL_WINDOWEVENT:
4331 HandleWindowEvent((WindowEvent *) &event);
4334 case SDL_APP_WILLENTERBACKGROUND:
4335 case SDL_APP_DIDENTERBACKGROUND:
4336 case SDL_APP_WILLENTERFOREGROUND:
4337 case SDL_APP_DIDENTERFOREGROUND:
4338 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4341 case EVENT_KEYPRESS:
4343 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4348 if (req_state & REQ_CONFIRM)
4357 #if defined(KSYM_Rewind)
4358 case KSYM_Rewind: // for Amazon Fire TV remote
4367 #if defined(KSYM_FastForward)
4368 case KSYM_FastForward: // for Amazon Fire TV remote
4374 HandleKeysDebug(key, KEY_PRESSED);
4378 if (req_state & REQ_PLAYER)
4380 int old_player_nr = setup.network_player_nr;
4383 result = old_player_nr + 1;
4388 result = old_player_nr + 1;
4419 case EVENT_FINGERRELEASE:
4420 case EVENT_KEYRELEASE:
4421 ClearPlayerAction();
4424 case SDL_CONTROLLERBUTTONDOWN:
4425 switch (event.cbutton.button)
4427 case SDL_CONTROLLER_BUTTON_A:
4428 case SDL_CONTROLLER_BUTTON_X:
4429 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4430 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4434 case SDL_CONTROLLER_BUTTON_B:
4435 case SDL_CONTROLLER_BUTTON_Y:
4436 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4437 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4438 case SDL_CONTROLLER_BUTTON_BACK:
4443 if (req_state & REQ_PLAYER)
4445 int old_player_nr = setup.network_player_nr;
4448 result = old_player_nr + 1;
4450 switch (event.cbutton.button)
4452 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4453 case SDL_CONTROLLER_BUTTON_Y:
4457 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4458 case SDL_CONTROLLER_BUTTON_B:
4462 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4463 case SDL_CONTROLLER_BUTTON_A:
4467 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4468 case SDL_CONTROLLER_BUTTON_X:
4479 case SDL_CONTROLLERBUTTONUP:
4480 HandleJoystickEvent(&event);
4481 ClearPlayerAction();
4485 HandleOtherEvents(&event);
4490 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4492 int joy = AnyJoystick();
4494 if (joy & JOY_BUTTON_1)
4496 else if (joy & JOY_BUTTON_2)
4499 else if (AnyJoystick())
4501 int joy = AnyJoystick();
4503 if (req_state & REQ_PLAYER)
4507 else if (joy & JOY_RIGHT)
4509 else if (joy & JOY_DOWN)
4511 else if (joy & JOY_LEFT)
4516 if (game_just_ended)
4518 if (global.use_envelope_request)
4520 // copy back current state of pressed buttons inside request area
4521 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4528 SetDrawtoField(draw_buffer_last);
4530 game.request_active = FALSE;
4535 static boolean RequestDoor(char *text, unsigned int req_state)
4537 unsigned int old_door_state;
4538 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4539 int font_nr = FONT_TEXT_2;
4544 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4546 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4547 font_nr = FONT_TEXT_1;
4550 if (game_status == GAME_MODE_PLAYING)
4551 BlitScreenToBitmap(backbuffer);
4553 // disable deactivated drawing when quick-loading level tape recording
4554 if (tape.playing && tape.deactivate_display)
4555 TapeDeactivateDisplayOff(TRUE);
4557 SetMouseCursor(CURSOR_DEFAULT);
4559 // pause network game while waiting for request to answer
4560 if (network.enabled &&
4561 game_status == GAME_MODE_PLAYING &&
4562 !game.all_players_gone &&
4563 req_state & REQUEST_WAIT_FOR_INPUT)
4564 SendToServer_PausePlaying();
4566 old_door_state = GetDoorState();
4568 // simulate releasing mouse button over last gadget, if still pressed
4570 HandleGadgets(-1, -1, 0);
4574 // draw released gadget before proceeding
4577 if (old_door_state & DOOR_OPEN_1)
4579 CloseDoor(DOOR_CLOSE_1);
4581 // save old door content
4582 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4583 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4586 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4587 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4589 // clear door drawing field
4590 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4592 // force DOOR font inside door area
4593 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4595 // write text for request
4596 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4598 char text_line[max_request_line_len + 1];
4604 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4606 tc = *(text_ptr + tx);
4607 // if (!tc || tc == ' ')
4608 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4612 if ((tc == '?' || tc == '!') && tl == 0)
4622 strncpy(text_line, text_ptr, tl);
4625 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4626 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4627 text_line, font_nr);
4629 text_ptr += tl + (tc == ' ' ? 1 : 0);
4630 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4635 if (req_state & REQ_ASK)
4637 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4638 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4639 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4640 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4642 else if (req_state & REQ_CONFIRM)
4644 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4645 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4647 else if (req_state & REQ_PLAYER)
4649 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4650 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4651 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4652 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4655 // copy request gadgets to door backbuffer
4656 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4658 OpenDoor(DOOR_OPEN_1);
4660 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4662 if (game_status == GAME_MODE_PLAYING)
4664 SetPanelBackground();
4665 SetDrawBackgroundMask(REDRAW_DOOR_1);
4669 SetDrawBackgroundMask(REDRAW_FIELD);
4675 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4677 // ---------- handle request buttons ----------
4678 result = RequestHandleEvents(req_state);
4682 if (!(req_state & REQ_STAY_OPEN))
4684 CloseDoor(DOOR_CLOSE_1);
4686 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4687 (req_state & REQ_REOPEN))
4688 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4693 if (game_status == GAME_MODE_PLAYING)
4695 SetPanelBackground();
4696 SetDrawBackgroundMask(REDRAW_DOOR_1);
4700 SetDrawBackgroundMask(REDRAW_FIELD);
4703 // continue network game after request
4704 if (network.enabled &&
4705 game_status == GAME_MODE_PLAYING &&
4706 !game.all_players_gone &&
4707 req_state & REQUEST_WAIT_FOR_INPUT)
4708 SendToServer_ContinuePlaying();
4710 // restore deactivated drawing when quick-loading level tape recording
4711 if (tape.playing && tape.deactivate_display)
4712 TapeDeactivateDisplayOn();
4717 static boolean RequestEnvelope(char *text, unsigned int req_state)
4721 if (game_status == GAME_MODE_PLAYING)
4722 BlitScreenToBitmap(backbuffer);
4724 // disable deactivated drawing when quick-loading level tape recording
4725 if (tape.playing && tape.deactivate_display)
4726 TapeDeactivateDisplayOff(TRUE);
4728 SetMouseCursor(CURSOR_DEFAULT);
4730 // pause network game while waiting for request to answer
4731 if (network.enabled &&
4732 game_status == GAME_MODE_PLAYING &&
4733 !game.all_players_gone &&
4734 req_state & REQUEST_WAIT_FOR_INPUT)
4735 SendToServer_PausePlaying();
4737 // simulate releasing mouse button over last gadget, if still pressed
4739 HandleGadgets(-1, -1, 0);
4743 // (replace with setting corresponding request background)
4744 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4745 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4747 // clear door drawing field
4748 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4750 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4752 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4754 if (game_status == GAME_MODE_PLAYING)
4756 SetPanelBackground();
4757 SetDrawBackgroundMask(REDRAW_DOOR_1);
4761 SetDrawBackgroundMask(REDRAW_FIELD);
4767 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4769 // ---------- handle request buttons ----------
4770 result = RequestHandleEvents(req_state);
4774 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4778 if (game_status == GAME_MODE_PLAYING)
4780 SetPanelBackground();
4781 SetDrawBackgroundMask(REDRAW_DOOR_1);
4785 SetDrawBackgroundMask(REDRAW_FIELD);
4788 // continue network game after request
4789 if (network.enabled &&
4790 game_status == GAME_MODE_PLAYING &&
4791 !game.all_players_gone &&
4792 req_state & REQUEST_WAIT_FOR_INPUT)
4793 SendToServer_ContinuePlaying();
4795 // restore deactivated drawing when quick-loading level tape recording
4796 if (tape.playing && tape.deactivate_display)
4797 TapeDeactivateDisplayOn();
4802 boolean Request(char *text, unsigned int req_state)
4804 boolean overlay_enabled = GetOverlayEnabled();
4807 game.request_active_or_moving = TRUE;
4809 SetOverlayEnabled(FALSE);
4811 if (global.use_envelope_request)
4812 result = RequestEnvelope(text, req_state);
4814 result = RequestDoor(text, req_state);
4816 SetOverlayEnabled(overlay_enabled);
4818 game.request_active_or_moving = FALSE;
4823 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4825 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4826 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4829 if (dpo1->sort_priority != dpo2->sort_priority)
4830 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4832 compare_result = dpo1->nr - dpo2->nr;
4834 return compare_result;
4837 void InitGraphicCompatibilityInfo_Doors(void)
4843 struct DoorInfo *door;
4847 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4848 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4850 { -1, -1, -1, NULL }
4852 struct Rect door_rect_list[] =
4854 { DX, DY, DXSIZE, DYSIZE },
4855 { VX, VY, VXSIZE, VYSIZE }
4859 for (i = 0; doors[i].door_token != -1; i++)
4861 int door_token = doors[i].door_token;
4862 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4863 int part_1 = doors[i].part_1;
4864 int part_8 = doors[i].part_8;
4865 int part_2 = part_1 + 1;
4866 int part_3 = part_1 + 2;
4867 struct DoorInfo *door = doors[i].door;
4868 struct Rect *door_rect = &door_rect_list[door_index];
4869 boolean door_gfx_redefined = FALSE;
4871 // check if any door part graphic definitions have been redefined
4873 for (j = 0; door_part_controls[j].door_token != -1; j++)
4875 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4876 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4878 if (dpc->door_token == door_token && fi->redefined)
4879 door_gfx_redefined = TRUE;
4882 // check for old-style door graphic/animation modifications
4884 if (!door_gfx_redefined)
4886 if (door->anim_mode & ANIM_STATIC_PANEL)
4888 door->panel.step_xoffset = 0;
4889 door->panel.step_yoffset = 0;
4892 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4894 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4895 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4896 int num_door_steps, num_panel_steps;
4898 // remove door part graphics other than the two default wings
4900 for (j = 0; door_part_controls[j].door_token != -1; j++)
4902 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4903 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4905 if (dpc->graphic >= part_3 &&
4906 dpc->graphic <= part_8)
4910 // set graphics and screen positions of the default wings
4912 g_part_1->width = door_rect->width;
4913 g_part_1->height = door_rect->height;
4914 g_part_2->width = door_rect->width;
4915 g_part_2->height = door_rect->height;
4916 g_part_2->src_x = door_rect->width;
4917 g_part_2->src_y = g_part_1->src_y;
4919 door->part_2.x = door->part_1.x;
4920 door->part_2.y = door->part_1.y;
4922 if (door->width != -1)
4924 g_part_1->width = door->width;
4925 g_part_2->width = door->width;
4927 // special treatment for graphics and screen position of right wing
4928 g_part_2->src_x += door_rect->width - door->width;
4929 door->part_2.x += door_rect->width - door->width;
4932 if (door->height != -1)
4934 g_part_1->height = door->height;
4935 g_part_2->height = door->height;
4937 // special treatment for graphics and screen position of bottom wing
4938 g_part_2->src_y += door_rect->height - door->height;
4939 door->part_2.y += door_rect->height - door->height;
4942 // set animation delays for the default wings and panels
4944 door->part_1.step_delay = door->step_delay;
4945 door->part_2.step_delay = door->step_delay;
4946 door->panel.step_delay = door->step_delay;
4948 // set animation draw order for the default wings
4950 door->part_1.sort_priority = 2; // draw left wing over ...
4951 door->part_2.sort_priority = 1; // ... right wing
4953 // set animation draw offset for the default wings
4955 if (door->anim_mode & ANIM_HORIZONTAL)
4957 door->part_1.step_xoffset = door->step_offset;
4958 door->part_1.step_yoffset = 0;
4959 door->part_2.step_xoffset = door->step_offset * -1;
4960 door->part_2.step_yoffset = 0;
4962 num_door_steps = g_part_1->width / door->step_offset;
4964 else // ANIM_VERTICAL
4966 door->part_1.step_xoffset = 0;
4967 door->part_1.step_yoffset = door->step_offset;
4968 door->part_2.step_xoffset = 0;
4969 door->part_2.step_yoffset = door->step_offset * -1;
4971 num_door_steps = g_part_1->height / door->step_offset;
4974 // set animation draw offset for the default panels
4976 if (door->step_offset > 1)
4978 num_panel_steps = 2 * door_rect->height / door->step_offset;
4979 door->panel.start_step = num_panel_steps - num_door_steps;
4980 door->panel.start_step_closing = door->panel.start_step;
4984 num_panel_steps = door_rect->height / door->step_offset;
4985 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4986 door->panel.start_step_closing = door->panel.start_step;
4987 door->panel.step_delay *= 2;
4994 void InitDoors(void)
4998 for (i = 0; door_part_controls[i].door_token != -1; i++)
5000 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5001 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5003 // initialize "start_step_opening" and "start_step_closing", if needed
5004 if (dpc->pos->start_step_opening == 0 &&
5005 dpc->pos->start_step_closing == 0)
5007 // dpc->pos->start_step_opening = dpc->pos->start_step;
5008 dpc->pos->start_step_closing = dpc->pos->start_step;
5011 // fill structure for door part draw order (sorted below)
5013 dpo->sort_priority = dpc->pos->sort_priority;
5016 // sort door part controls according to sort_priority and graphic number
5017 qsort(door_part_order, MAX_DOOR_PARTS,
5018 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5021 unsigned int OpenDoor(unsigned int door_state)
5023 if (door_state & DOOR_COPY_BACK)
5025 if (door_state & DOOR_OPEN_1)
5026 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5027 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5029 if (door_state & DOOR_OPEN_2)
5030 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5031 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5033 door_state &= ~DOOR_COPY_BACK;
5036 return MoveDoor(door_state);
5039 unsigned int CloseDoor(unsigned int door_state)
5041 unsigned int old_door_state = GetDoorState();
5043 if (!(door_state & DOOR_NO_COPY_BACK))
5045 if (old_door_state & DOOR_OPEN_1)
5046 BlitBitmap(backbuffer, bitmap_db_door_1,
5047 DX, DY, DXSIZE, DYSIZE, 0, 0);
5049 if (old_door_state & DOOR_OPEN_2)
5050 BlitBitmap(backbuffer, bitmap_db_door_2,
5051 VX, VY, VXSIZE, VYSIZE, 0, 0);
5053 door_state &= ~DOOR_NO_COPY_BACK;
5056 return MoveDoor(door_state);
5059 unsigned int GetDoorState(void)
5061 return MoveDoor(DOOR_GET_STATE);
5064 unsigned int SetDoorState(unsigned int door_state)
5066 return MoveDoor(door_state | DOOR_SET_STATE);
5069 static int euclid(int a, int b)
5071 return (b ? euclid(b, a % b) : a);
5074 unsigned int MoveDoor(unsigned int door_state)
5076 struct Rect door_rect_list[] =
5078 { DX, DY, DXSIZE, DYSIZE },
5079 { VX, VY, VXSIZE, VYSIZE }
5081 static int door1 = DOOR_CLOSE_1;
5082 static int door2 = DOOR_CLOSE_2;
5083 unsigned int door_delay = 0;
5084 unsigned int door_delay_value;
5087 if (door_state == DOOR_GET_STATE)
5088 return (door1 | door2);
5090 if (door_state & DOOR_SET_STATE)
5092 if (door_state & DOOR_ACTION_1)
5093 door1 = door_state & DOOR_ACTION_1;
5094 if (door_state & DOOR_ACTION_2)
5095 door2 = door_state & DOOR_ACTION_2;
5097 return (door1 | door2);
5100 if (!(door_state & DOOR_FORCE_REDRAW))
5102 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5103 door_state &= ~DOOR_OPEN_1;
5104 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5105 door_state &= ~DOOR_CLOSE_1;
5106 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5107 door_state &= ~DOOR_OPEN_2;
5108 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5109 door_state &= ~DOOR_CLOSE_2;
5112 if (global.autoplay_leveldir)
5114 door_state |= DOOR_NO_DELAY;
5115 door_state &= ~DOOR_CLOSE_ALL;
5118 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5119 door_state |= DOOR_NO_DELAY;
5121 if (door_state & DOOR_ACTION)
5123 boolean door_panel_drawn[NUM_DOORS];
5124 boolean panel_has_doors[NUM_DOORS];
5125 boolean door_part_skip[MAX_DOOR_PARTS];
5126 boolean door_part_done[MAX_DOOR_PARTS];
5127 boolean door_part_done_all;
5128 int num_steps[MAX_DOOR_PARTS];
5129 int max_move_delay = 0; // delay for complete animations of all doors
5130 int max_step_delay = 0; // delay (ms) between two animation frames
5131 int num_move_steps = 0; // number of animation steps for all doors
5132 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5133 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5134 int current_move_delay = 0;
5138 for (i = 0; i < NUM_DOORS; i++)
5139 panel_has_doors[i] = FALSE;
5141 for (i = 0; i < MAX_DOOR_PARTS; i++)
5143 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5144 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5145 int door_token = dpc->door_token;
5147 door_part_done[i] = FALSE;
5148 door_part_skip[i] = (!(door_state & door_token) ||
5152 for (i = 0; i < MAX_DOOR_PARTS; i++)
5154 int nr = door_part_order[i].nr;
5155 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5156 struct DoorPartPosInfo *pos = dpc->pos;
5157 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5158 int door_token = dpc->door_token;
5159 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5160 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5161 int step_xoffset = ABS(pos->step_xoffset);
5162 int step_yoffset = ABS(pos->step_yoffset);
5163 int step_delay = pos->step_delay;
5164 int current_door_state = door_state & door_token;
5165 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5166 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5167 boolean part_opening = (is_panel ? door_closing : door_opening);
5168 int start_step = (part_opening ? pos->start_step_opening :
5169 pos->start_step_closing);
5170 float move_xsize = (step_xoffset ? g->width : 0);
5171 float move_ysize = (step_yoffset ? g->height : 0);
5172 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5173 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5174 int move_steps = (move_xsteps && move_ysteps ?
5175 MIN(move_xsteps, move_ysteps) :
5176 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5177 int move_delay = move_steps * step_delay;
5179 if (door_part_skip[nr])
5182 max_move_delay = MAX(max_move_delay, move_delay);
5183 max_step_delay = (max_step_delay == 0 ? step_delay :
5184 euclid(max_step_delay, step_delay));
5185 num_steps[nr] = move_steps;
5189 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5191 panel_has_doors[door_index] = TRUE;
5195 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5197 num_move_steps = max_move_delay / max_step_delay;
5198 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5200 door_delay_value = max_step_delay;
5202 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5204 start = num_move_steps - 1;
5208 // opening door sound has priority over simultaneously closing door
5209 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5211 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5213 if (door_state & DOOR_OPEN_1)
5214 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5215 if (door_state & DOOR_OPEN_2)
5216 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5218 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5220 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5222 if (door_state & DOOR_CLOSE_1)
5223 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5224 if (door_state & DOOR_CLOSE_2)
5225 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5229 for (k = start; k < num_move_steps; k++)
5231 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5233 door_part_done_all = TRUE;
5235 for (i = 0; i < NUM_DOORS; i++)
5236 door_panel_drawn[i] = FALSE;
5238 for (i = 0; i < MAX_DOOR_PARTS; i++)
5240 int nr = door_part_order[i].nr;
5241 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5242 struct DoorPartPosInfo *pos = dpc->pos;
5243 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5244 int door_token = dpc->door_token;
5245 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5246 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5247 boolean is_panel_and_door_has_closed = FALSE;
5248 struct Rect *door_rect = &door_rect_list[door_index];
5249 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5251 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5252 int current_door_state = door_state & door_token;
5253 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5254 boolean door_closing = !door_opening;
5255 boolean part_opening = (is_panel ? door_closing : door_opening);
5256 boolean part_closing = !part_opening;
5257 int start_step = (part_opening ? pos->start_step_opening :
5258 pos->start_step_closing);
5259 int step_delay = pos->step_delay;
5260 int step_factor = step_delay / max_step_delay;
5261 int k1 = (step_factor ? k / step_factor + 1 : k);
5262 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5263 int kk = MAX(0, k2);
5266 int src_x, src_y, src_xx, src_yy;
5267 int dst_x, dst_y, dst_xx, dst_yy;
5270 if (door_part_skip[nr])
5273 if (!(door_state & door_token))
5281 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5282 int kk_door = MAX(0, k2_door);
5283 int sync_frame = kk_door * door_delay_value;
5284 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5286 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5287 &g_src_x, &g_src_y);
5292 if (!door_panel_drawn[door_index])
5294 ClearRectangle(drawto, door_rect->x, door_rect->y,
5295 door_rect->width, door_rect->height);
5297 door_panel_drawn[door_index] = TRUE;
5300 // draw opening or closing door parts
5302 if (pos->step_xoffset < 0) // door part on right side
5305 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5308 if (dst_xx + width > door_rect->width)
5309 width = door_rect->width - dst_xx;
5311 else // door part on left side
5314 dst_xx = pos->x - kk * pos->step_xoffset;
5318 src_xx = ABS(dst_xx);
5322 width = g->width - src_xx;
5324 if (width > door_rect->width)
5325 width = door_rect->width;
5327 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5330 if (pos->step_yoffset < 0) // door part on bottom side
5333 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5336 if (dst_yy + height > door_rect->height)
5337 height = door_rect->height - dst_yy;
5339 else // door part on top side
5342 dst_yy = pos->y - kk * pos->step_yoffset;
5346 src_yy = ABS(dst_yy);
5350 height = g->height - src_yy;
5353 src_x = g_src_x + src_xx;
5354 src_y = g_src_y + src_yy;
5356 dst_x = door_rect->x + dst_xx;
5357 dst_y = door_rect->y + dst_yy;
5359 is_panel_and_door_has_closed =
5362 panel_has_doors[door_index] &&
5363 k >= num_move_steps_doors_only - 1);
5365 if (width >= 0 && width <= g->width &&
5366 height >= 0 && height <= g->height &&
5367 !is_panel_and_door_has_closed)
5369 if (is_panel || !pos->draw_masked)
5370 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5373 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5377 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5379 if ((part_opening && (width < 0 || height < 0)) ||
5380 (part_closing && (width >= g->width && height >= g->height)))
5381 door_part_done[nr] = TRUE;
5383 // continue door part animations, but not panel after door has closed
5384 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5385 door_part_done_all = FALSE;
5388 if (!(door_state & DOOR_NO_DELAY))
5392 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5394 current_move_delay += max_step_delay;
5396 // prevent OS (Windows) from complaining about program not responding
5400 if (door_part_done_all)
5404 if (!(door_state & DOOR_NO_DELAY))
5406 // wait for specified door action post delay
5407 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5408 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5409 else if (door_state & DOOR_ACTION_1)
5410 door_delay_value = door_1.post_delay;
5411 else if (door_state & DOOR_ACTION_2)
5412 door_delay_value = door_2.post_delay;
5414 while (!DelayReached(&door_delay, door_delay_value))
5419 if (door_state & DOOR_ACTION_1)
5420 door1 = door_state & DOOR_ACTION_1;
5421 if (door_state & DOOR_ACTION_2)
5422 door2 = door_state & DOOR_ACTION_2;
5424 // draw masked border over door area
5425 DrawMaskedBorder(REDRAW_DOOR_1);
5426 DrawMaskedBorder(REDRAW_DOOR_2);
5428 ClearAutoRepeatKeyEvents();
5430 return (door1 | door2);
5433 static boolean useSpecialEditorDoor(void)
5435 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5436 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5438 // do not draw special editor door if editor border defined or redefined
5439 if (graphic_info[graphic].bitmap != NULL || redefined)
5442 // do not draw special editor door if global border defined to be empty
5443 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5446 // do not draw special editor door if viewport definitions do not match
5450 EY + EYSIZE != VY + VYSIZE)
5456 void DrawSpecialEditorDoor(void)
5458 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5459 int top_border_width = gfx1->width;
5460 int top_border_height = gfx1->height;
5461 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5462 int ex = EX - outer_border;
5463 int ey = EY - outer_border;
5464 int vy = VY - outer_border;
5465 int exsize = EXSIZE + 2 * outer_border;
5467 if (!useSpecialEditorDoor())
5470 // draw bigger level editor toolbox window
5471 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5472 top_border_width, top_border_height, ex, ey - top_border_height);
5473 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5474 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5476 redraw_mask |= REDRAW_ALL;
5479 void UndrawSpecialEditorDoor(void)
5481 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5482 int top_border_width = gfx1->width;
5483 int top_border_height = gfx1->height;
5484 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5485 int ex = EX - outer_border;
5486 int ey = EY - outer_border;
5487 int ey_top = ey - top_border_height;
5488 int exsize = EXSIZE + 2 * outer_border;
5489 int eysize = EYSIZE + 2 * outer_border;
5491 if (!useSpecialEditorDoor())
5494 // draw normal tape recorder window
5495 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5497 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5498 ex, ey_top, top_border_width, top_border_height,
5500 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5501 ex, ey, exsize, eysize, ex, ey);
5505 // if screen background is set to "[NONE]", clear editor toolbox window
5506 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5507 ClearRectangle(drawto, ex, ey, exsize, eysize);
5510 redraw_mask |= REDRAW_ALL;
5514 // ---------- new tool button stuff -------------------------------------------
5519 struct TextPosInfo *pos;
5521 boolean is_touch_button;
5523 } toolbutton_info[NUM_TOOL_BUTTONS] =
5526 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5527 TOOL_CTRL_ID_YES, FALSE, "yes"
5530 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5531 TOOL_CTRL_ID_NO, FALSE, "no"
5534 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5535 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5538 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5539 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5542 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5543 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5546 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5547 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5550 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5551 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5554 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5555 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5558 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5559 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5562 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5563 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5567 void CreateToolButtons(void)
5571 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5573 int graphic = toolbutton_info[i].graphic;
5574 struct GraphicInfo *gfx = &graphic_info[graphic];
5575 struct TextPosInfo *pos = toolbutton_info[i].pos;
5576 struct GadgetInfo *gi;
5577 Bitmap *deco_bitmap = None;
5578 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5579 unsigned int event_mask = GD_EVENT_RELEASED;
5580 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5581 int base_x = (is_touch_button ? 0 : DX);
5582 int base_y = (is_touch_button ? 0 : DY);
5583 int gd_x = gfx->src_x;
5584 int gd_y = gfx->src_y;
5585 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5586 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5591 if (global.use_envelope_request && !is_touch_button)
5593 setRequestPosition(&base_x, &base_y, TRUE);
5595 // check if request buttons are outside of envelope and fix, if needed
5596 if (x < 0 || x + gfx->width > request.width ||
5597 y < 0 || y + gfx->height > request.height)
5599 if (id == TOOL_CTRL_ID_YES)
5602 y = request.height - 2 * request.border_size - gfx->height;
5604 else if (id == TOOL_CTRL_ID_NO)
5606 x = request.width - 2 * request.border_size - gfx->width;
5607 y = request.height - 2 * request.border_size - gfx->height;
5609 else if (id == TOOL_CTRL_ID_CONFIRM)
5611 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5612 y = request.height - 2 * request.border_size - gfx->height;
5614 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5616 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5618 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5619 y = request.height - 2 * request.border_size - gfx->height * 2;
5621 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5622 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5627 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5629 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5631 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5632 pos->size, &deco_bitmap, &deco_x, &deco_y);
5633 deco_xpos = (gfx->width - pos->size) / 2;
5634 deco_ypos = (gfx->height - pos->size) / 2;
5637 gi = CreateGadget(GDI_CUSTOM_ID, id,
5638 GDI_IMAGE_ID, graphic,
5639 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5642 GDI_WIDTH, gfx->width,
5643 GDI_HEIGHT, gfx->height,
5644 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5645 GDI_STATE, GD_BUTTON_UNPRESSED,
5646 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5647 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5648 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5649 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5650 GDI_DECORATION_SIZE, pos->size, pos->size,
5651 GDI_DECORATION_SHIFTING, 1, 1,
5652 GDI_DIRECT_DRAW, FALSE,
5653 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5654 GDI_EVENT_MASK, event_mask,
5655 GDI_CALLBACK_ACTION, HandleToolButtons,
5659 Fail("cannot create gadget");
5661 tool_gadget[id] = gi;
5665 void FreeToolButtons(void)
5669 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5670 FreeGadget(tool_gadget[i]);
5673 static void UnmapToolButtons(void)
5677 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5678 UnmapGadget(tool_gadget[i]);
5681 static void HandleToolButtons(struct GadgetInfo *gi)
5683 request_gadget_id = gi->custom_id;
5686 static struct Mapping_EM_to_RND_object
5689 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5690 boolean is_backside; // backside of moving element
5696 em_object_mapping_list[GAME_TILE_MAX + 1] =
5699 Zborder, FALSE, FALSE,
5703 Zplayer, FALSE, FALSE,
5712 Ztank, FALSE, FALSE,
5716 Zeater, FALSE, FALSE,
5720 Zdynamite, FALSE, FALSE,
5724 Zboom, FALSE, FALSE,
5729 Xchain, FALSE, FALSE,
5730 EL_DEFAULT, ACTION_EXPLODING, -1
5733 Xboom_bug, FALSE, FALSE,
5734 EL_BUG, ACTION_EXPLODING, -1
5737 Xboom_tank, FALSE, FALSE,
5738 EL_SPACESHIP, ACTION_EXPLODING, -1
5741 Xboom_android, FALSE, FALSE,
5742 EL_EMC_ANDROID, ACTION_OTHER, -1
5745 Xboom_1, FALSE, FALSE,
5746 EL_DEFAULT, ACTION_EXPLODING, -1
5749 Xboom_2, FALSE, FALSE,
5750 EL_DEFAULT, ACTION_EXPLODING, -1
5754 Xblank, TRUE, FALSE,
5759 Xsplash_e, FALSE, FALSE,
5760 EL_ACID_SPLASH_RIGHT, -1, -1
5763 Xsplash_w, FALSE, FALSE,
5764 EL_ACID_SPLASH_LEFT, -1, -1
5768 Xplant, TRUE, FALSE,
5769 EL_EMC_PLANT, -1, -1
5772 Yplant, FALSE, FALSE,
5773 EL_EMC_PLANT, -1, -1
5777 Xacid_1, TRUE, FALSE,
5781 Xacid_2, FALSE, FALSE,
5785 Xacid_3, FALSE, FALSE,
5789 Xacid_4, FALSE, FALSE,
5793 Xacid_5, FALSE, FALSE,
5797 Xacid_6, FALSE, FALSE,
5801 Xacid_7, FALSE, FALSE,
5805 Xacid_8, FALSE, FALSE,
5810 Xfake_acid_1, TRUE, FALSE,
5811 EL_EMC_FAKE_ACID, -1, -1
5814 Xfake_acid_2, FALSE, FALSE,
5815 EL_EMC_FAKE_ACID, -1, -1
5818 Xfake_acid_3, FALSE, FALSE,
5819 EL_EMC_FAKE_ACID, -1, -1
5822 Xfake_acid_4, FALSE, FALSE,
5823 EL_EMC_FAKE_ACID, -1, -1
5826 Xfake_acid_5, FALSE, FALSE,
5827 EL_EMC_FAKE_ACID, -1, -1
5830 Xfake_acid_6, FALSE, FALSE,
5831 EL_EMC_FAKE_ACID, -1, -1
5834 Xfake_acid_7, FALSE, FALSE,
5835 EL_EMC_FAKE_ACID, -1, -1
5838 Xfake_acid_8, FALSE, FALSE,
5839 EL_EMC_FAKE_ACID, -1, -1
5843 Xfake_acid_1_player, FALSE, FALSE,
5844 EL_EMC_FAKE_ACID, -1, -1
5847 Xfake_acid_2_player, FALSE, FALSE,
5848 EL_EMC_FAKE_ACID, -1, -1
5851 Xfake_acid_3_player, FALSE, FALSE,
5852 EL_EMC_FAKE_ACID, -1, -1
5855 Xfake_acid_4_player, FALSE, FALSE,
5856 EL_EMC_FAKE_ACID, -1, -1
5859 Xfake_acid_5_player, FALSE, FALSE,
5860 EL_EMC_FAKE_ACID, -1, -1
5863 Xfake_acid_6_player, FALSE, FALSE,
5864 EL_EMC_FAKE_ACID, -1, -1
5867 Xfake_acid_7_player, FALSE, FALSE,
5868 EL_EMC_FAKE_ACID, -1, -1
5871 Xfake_acid_8_player, FALSE, FALSE,
5872 EL_EMC_FAKE_ACID, -1, -1
5876 Xgrass, TRUE, FALSE,
5877 EL_EMC_GRASS, -1, -1
5880 Ygrass_nB, FALSE, FALSE,
5881 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5884 Ygrass_eB, FALSE, FALSE,
5885 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5888 Ygrass_sB, FALSE, FALSE,
5889 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5892 Ygrass_wB, FALSE, FALSE,
5893 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5901 Ydirt_nB, FALSE, FALSE,
5902 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5905 Ydirt_eB, FALSE, FALSE,
5906 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5909 Ydirt_sB, FALSE, FALSE,
5910 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5913 Ydirt_wB, FALSE, FALSE,
5914 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5918 Xandroid, TRUE, FALSE,
5919 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5922 Xandroid_1_n, FALSE, FALSE,
5923 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5926 Xandroid_2_n, FALSE, FALSE,
5927 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5930 Xandroid_1_e, FALSE, FALSE,
5931 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5934 Xandroid_2_e, FALSE, FALSE,
5935 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5938 Xandroid_1_w, FALSE, FALSE,
5939 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5942 Xandroid_2_w, FALSE, FALSE,
5943 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5946 Xandroid_1_s, FALSE, FALSE,
5947 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5950 Xandroid_2_s, FALSE, FALSE,
5951 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5954 Yandroid_n, FALSE, FALSE,
5955 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5958 Yandroid_nB, FALSE, TRUE,
5959 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5962 Yandroid_ne, FALSE, FALSE,
5963 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5966 Yandroid_neB, FALSE, TRUE,
5967 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5970 Yandroid_e, FALSE, FALSE,
5971 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5974 Yandroid_eB, FALSE, TRUE,
5975 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5978 Yandroid_se, FALSE, FALSE,
5979 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5982 Yandroid_seB, FALSE, TRUE,
5983 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5986 Yandroid_s, FALSE, FALSE,
5987 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5990 Yandroid_sB, FALSE, TRUE,
5991 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5994 Yandroid_sw, FALSE, FALSE,
5995 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5998 Yandroid_swB, FALSE, TRUE,
5999 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6002 Yandroid_w, FALSE, FALSE,
6003 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6006 Yandroid_wB, FALSE, TRUE,
6007 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6010 Yandroid_nw, FALSE, FALSE,
6011 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6014 Yandroid_nwB, FALSE, TRUE,
6015 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6019 Xeater_n, TRUE, FALSE,
6020 EL_YAMYAM_UP, -1, -1
6023 Xeater_e, TRUE, FALSE,
6024 EL_YAMYAM_RIGHT, -1, -1
6027 Xeater_w, TRUE, FALSE,
6028 EL_YAMYAM_LEFT, -1, -1
6031 Xeater_s, TRUE, FALSE,
6032 EL_YAMYAM_DOWN, -1, -1
6035 Yeater_n, FALSE, FALSE,
6036 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6039 Yeater_nB, FALSE, TRUE,
6040 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6043 Yeater_e, FALSE, FALSE,
6044 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6047 Yeater_eB, FALSE, TRUE,
6048 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6051 Yeater_s, FALSE, FALSE,
6052 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6055 Yeater_sB, FALSE, TRUE,
6056 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6059 Yeater_w, FALSE, FALSE,
6060 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6063 Yeater_wB, FALSE, TRUE,
6064 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6067 Yeater_stone, FALSE, FALSE,
6068 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6071 Yeater_spring, FALSE, FALSE,
6072 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6076 Xalien, TRUE, FALSE,
6080 Xalien_pause, FALSE, FALSE,
6084 Yalien_n, FALSE, FALSE,
6085 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6088 Yalien_nB, FALSE, TRUE,
6089 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6092 Yalien_e, FALSE, FALSE,
6093 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6096 Yalien_eB, FALSE, TRUE,
6097 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6100 Yalien_s, FALSE, FALSE,
6101 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6104 Yalien_sB, FALSE, TRUE,
6105 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6108 Yalien_w, FALSE, FALSE,
6109 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6112 Yalien_wB, FALSE, TRUE,
6113 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6116 Yalien_stone, FALSE, FALSE,
6117 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6120 Yalien_spring, FALSE, FALSE,
6121 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6125 Xbug_1_n, TRUE, FALSE,
6129 Xbug_1_e, TRUE, FALSE,
6130 EL_BUG_RIGHT, -1, -1
6133 Xbug_1_s, TRUE, FALSE,
6137 Xbug_1_w, TRUE, FALSE,
6141 Xbug_2_n, FALSE, FALSE,
6145 Xbug_2_e, FALSE, FALSE,
6146 EL_BUG_RIGHT, -1, -1
6149 Xbug_2_s, FALSE, FALSE,
6153 Xbug_2_w, FALSE, FALSE,
6157 Ybug_n, FALSE, FALSE,
6158 EL_BUG, ACTION_MOVING, MV_BIT_UP
6161 Ybug_nB, FALSE, TRUE,
6162 EL_BUG, ACTION_MOVING, MV_BIT_UP
6165 Ybug_e, FALSE, FALSE,
6166 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6169 Ybug_eB, FALSE, TRUE,
6170 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6173 Ybug_s, FALSE, FALSE,
6174 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6177 Ybug_sB, FALSE, TRUE,
6178 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6181 Ybug_w, FALSE, FALSE,
6182 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6185 Ybug_wB, FALSE, TRUE,
6186 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6189 Ybug_w_n, FALSE, FALSE,
6190 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6193 Ybug_n_e, FALSE, FALSE,
6194 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6197 Ybug_e_s, FALSE, FALSE,
6198 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6201 Ybug_s_w, FALSE, FALSE,
6202 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6205 Ybug_e_n, FALSE, FALSE,
6206 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6209 Ybug_s_e, FALSE, FALSE,
6210 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6213 Ybug_w_s, FALSE, FALSE,
6214 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6217 Ybug_n_w, FALSE, FALSE,
6218 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6221 Ybug_stone, FALSE, FALSE,
6222 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6225 Ybug_spring, FALSE, FALSE,
6226 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6230 Xtank_1_n, TRUE, FALSE,
6231 EL_SPACESHIP_UP, -1, -1
6234 Xtank_1_e, TRUE, FALSE,
6235 EL_SPACESHIP_RIGHT, -1, -1
6238 Xtank_1_s, TRUE, FALSE,
6239 EL_SPACESHIP_DOWN, -1, -1
6242 Xtank_1_w, TRUE, FALSE,
6243 EL_SPACESHIP_LEFT, -1, -1
6246 Xtank_2_n, FALSE, FALSE,
6247 EL_SPACESHIP_UP, -1, -1
6250 Xtank_2_e, FALSE, FALSE,
6251 EL_SPACESHIP_RIGHT, -1, -1
6254 Xtank_2_s, FALSE, FALSE,
6255 EL_SPACESHIP_DOWN, -1, -1
6258 Xtank_2_w, FALSE, FALSE,
6259 EL_SPACESHIP_LEFT, -1, -1
6262 Ytank_n, FALSE, FALSE,
6263 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6266 Ytank_nB, FALSE, TRUE,
6267 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6270 Ytank_e, FALSE, FALSE,
6271 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6274 Ytank_eB, FALSE, TRUE,
6275 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6278 Ytank_s, FALSE, FALSE,
6279 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6282 Ytank_sB, FALSE, TRUE,
6283 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6286 Ytank_w, FALSE, FALSE,
6287 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6290 Ytank_wB, FALSE, TRUE,
6291 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6294 Ytank_w_n, FALSE, FALSE,
6295 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6298 Ytank_n_e, FALSE, FALSE,
6299 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6302 Ytank_e_s, FALSE, FALSE,
6303 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6306 Ytank_s_w, FALSE, FALSE,
6307 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6310 Ytank_e_n, FALSE, FALSE,
6311 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6314 Ytank_s_e, FALSE, FALSE,
6315 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6318 Ytank_w_s, FALSE, FALSE,
6319 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6322 Ytank_n_w, FALSE, FALSE,
6323 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6326 Ytank_stone, FALSE, FALSE,
6327 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6330 Ytank_spring, FALSE, FALSE,
6331 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6335 Xemerald, TRUE, FALSE,
6339 Xemerald_pause, FALSE, FALSE,
6343 Xemerald_fall, FALSE, FALSE,
6347 Xemerald_shine, FALSE, FALSE,
6348 EL_EMERALD, ACTION_TWINKLING, -1
6351 Yemerald_s, FALSE, FALSE,
6352 EL_EMERALD, ACTION_FALLING, -1
6355 Yemerald_sB, FALSE, TRUE,
6356 EL_EMERALD, ACTION_FALLING, -1
6359 Yemerald_e, FALSE, FALSE,
6360 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6363 Yemerald_eB, FALSE, TRUE,
6364 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6367 Yemerald_w, FALSE, FALSE,
6368 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6371 Yemerald_wB, FALSE, TRUE,
6372 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6375 Yemerald_blank, FALSE, FALSE,
6376 EL_EMERALD, ACTION_COLLECTING, -1
6380 Xdiamond, TRUE, FALSE,
6384 Xdiamond_pause, FALSE, FALSE,
6388 Xdiamond_fall, FALSE, FALSE,
6392 Xdiamond_shine, FALSE, FALSE,
6393 EL_DIAMOND, ACTION_TWINKLING, -1
6396 Ydiamond_s, FALSE, FALSE,
6397 EL_DIAMOND, ACTION_FALLING, -1
6400 Ydiamond_sB, FALSE, TRUE,
6401 EL_DIAMOND, ACTION_FALLING, -1
6404 Ydiamond_e, FALSE, FALSE,
6405 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6408 Ydiamond_eB, FALSE, TRUE,
6409 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6412 Ydiamond_w, FALSE, FALSE,
6413 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6416 Ydiamond_wB, FALSE, TRUE,
6417 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6420 Ydiamond_blank, FALSE, FALSE,
6421 EL_DIAMOND, ACTION_COLLECTING, -1
6424 Ydiamond_stone, FALSE, FALSE,
6425 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6429 Xstone, TRUE, FALSE,
6433 Xstone_pause, FALSE, FALSE,
6437 Xstone_fall, FALSE, FALSE,
6441 Ystone_s, FALSE, FALSE,
6442 EL_ROCK, ACTION_FALLING, -1
6445 Ystone_sB, FALSE, TRUE,
6446 EL_ROCK, ACTION_FALLING, -1
6449 Ystone_e, FALSE, FALSE,
6450 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6453 Ystone_eB, FALSE, TRUE,
6454 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6457 Ystone_w, FALSE, FALSE,
6458 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6461 Ystone_wB, FALSE, TRUE,
6462 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6470 Xbomb_pause, FALSE, FALSE,
6474 Xbomb_fall, FALSE, FALSE,
6478 Ybomb_s, FALSE, FALSE,
6479 EL_BOMB, ACTION_FALLING, -1
6482 Ybomb_sB, FALSE, TRUE,
6483 EL_BOMB, ACTION_FALLING, -1
6486 Ybomb_e, FALSE, FALSE,
6487 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6490 Ybomb_eB, FALSE, TRUE,
6491 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6494 Ybomb_w, FALSE, FALSE,
6495 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6498 Ybomb_wB, FALSE, TRUE,
6499 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6502 Ybomb_blank, FALSE, FALSE,
6503 EL_BOMB, ACTION_ACTIVATING, -1
6511 Xnut_pause, FALSE, FALSE,
6515 Xnut_fall, FALSE, FALSE,
6519 Ynut_s, FALSE, FALSE,
6520 EL_NUT, ACTION_FALLING, -1
6523 Ynut_sB, FALSE, TRUE,
6524 EL_NUT, ACTION_FALLING, -1
6527 Ynut_e, FALSE, FALSE,
6528 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6531 Ynut_eB, FALSE, TRUE,
6532 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6535 Ynut_w, FALSE, FALSE,
6536 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6539 Ynut_wB, FALSE, TRUE,
6540 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6543 Ynut_stone, FALSE, FALSE,
6544 EL_NUT, ACTION_BREAKING, -1
6548 Xspring, TRUE, FALSE,
6552 Xspring_pause, FALSE, FALSE,
6556 Xspring_e, TRUE, FALSE,
6557 EL_SPRING_RIGHT, -1, -1
6560 Xspring_w, TRUE, FALSE,
6561 EL_SPRING_LEFT, -1, -1
6564 Xspring_fall, FALSE, FALSE,
6568 Yspring_s, FALSE, FALSE,
6569 EL_SPRING, ACTION_FALLING, -1
6572 Yspring_sB, FALSE, TRUE,
6573 EL_SPRING, ACTION_FALLING, -1
6576 Yspring_e, FALSE, FALSE,
6577 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6580 Yspring_eB, FALSE, TRUE,
6581 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6584 Yspring_w, FALSE, FALSE,
6585 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6588 Yspring_wB, FALSE, TRUE,
6589 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6592 Yspring_alien_e, FALSE, FALSE,
6593 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6596 Yspring_alien_eB, FALSE, TRUE,
6597 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6600 Yspring_alien_w, FALSE, FALSE,
6601 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6604 Yspring_alien_wB, FALSE, TRUE,
6605 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6609 Xpush_emerald_e, FALSE, FALSE,
6610 EL_EMERALD, -1, MV_BIT_RIGHT
6613 Xpush_emerald_w, FALSE, FALSE,
6614 EL_EMERALD, -1, MV_BIT_LEFT
6617 Xpush_diamond_e, FALSE, FALSE,
6618 EL_DIAMOND, -1, MV_BIT_RIGHT
6621 Xpush_diamond_w, FALSE, FALSE,
6622 EL_DIAMOND, -1, MV_BIT_LEFT
6625 Xpush_stone_e, FALSE, FALSE,
6626 EL_ROCK, -1, MV_BIT_RIGHT
6629 Xpush_stone_w, FALSE, FALSE,
6630 EL_ROCK, -1, MV_BIT_LEFT
6633 Xpush_bomb_e, FALSE, FALSE,
6634 EL_BOMB, -1, MV_BIT_RIGHT
6637 Xpush_bomb_w, FALSE, FALSE,
6638 EL_BOMB, -1, MV_BIT_LEFT
6641 Xpush_nut_e, FALSE, FALSE,
6642 EL_NUT, -1, MV_BIT_RIGHT
6645 Xpush_nut_w, FALSE, FALSE,
6646 EL_NUT, -1, MV_BIT_LEFT
6649 Xpush_spring_e, FALSE, FALSE,
6650 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6653 Xpush_spring_w, FALSE, FALSE,
6654 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6658 Xdynamite, TRUE, FALSE,
6659 EL_EM_DYNAMITE, -1, -1
6662 Ydynamite_blank, FALSE, FALSE,
6663 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6666 Xdynamite_1, TRUE, FALSE,
6667 EL_EM_DYNAMITE_ACTIVE, -1, -1
6670 Xdynamite_2, FALSE, FALSE,
6671 EL_EM_DYNAMITE_ACTIVE, -1, -1
6674 Xdynamite_3, FALSE, FALSE,
6675 EL_EM_DYNAMITE_ACTIVE, -1, -1
6678 Xdynamite_4, FALSE, FALSE,
6679 EL_EM_DYNAMITE_ACTIVE, -1, -1
6683 Xkey_1, TRUE, FALSE,
6687 Xkey_2, TRUE, FALSE,
6691 Xkey_3, TRUE, FALSE,
6695 Xkey_4, TRUE, FALSE,
6699 Xkey_5, TRUE, FALSE,
6700 EL_EMC_KEY_5, -1, -1
6703 Xkey_6, TRUE, FALSE,
6704 EL_EMC_KEY_6, -1, -1
6707 Xkey_7, TRUE, FALSE,
6708 EL_EMC_KEY_7, -1, -1
6711 Xkey_8, TRUE, FALSE,
6712 EL_EMC_KEY_8, -1, -1
6716 Xdoor_1, TRUE, FALSE,
6717 EL_EM_GATE_1, -1, -1
6720 Xdoor_2, TRUE, FALSE,
6721 EL_EM_GATE_2, -1, -1
6724 Xdoor_3, TRUE, FALSE,
6725 EL_EM_GATE_3, -1, -1
6728 Xdoor_4, TRUE, FALSE,
6729 EL_EM_GATE_4, -1, -1
6732 Xdoor_5, TRUE, FALSE,
6733 EL_EMC_GATE_5, -1, -1
6736 Xdoor_6, TRUE, FALSE,
6737 EL_EMC_GATE_6, -1, -1
6740 Xdoor_7, TRUE, FALSE,
6741 EL_EMC_GATE_7, -1, -1
6744 Xdoor_8, TRUE, FALSE,
6745 EL_EMC_GATE_8, -1, -1
6749 Xfake_door_1, TRUE, FALSE,
6750 EL_EM_GATE_1_GRAY, -1, -1
6753 Xfake_door_2, TRUE, FALSE,
6754 EL_EM_GATE_2_GRAY, -1, -1
6757 Xfake_door_3, TRUE, FALSE,
6758 EL_EM_GATE_3_GRAY, -1, -1
6761 Xfake_door_4, TRUE, FALSE,
6762 EL_EM_GATE_4_GRAY, -1, -1
6765 Xfake_door_5, TRUE, FALSE,
6766 EL_EMC_GATE_5_GRAY, -1, -1
6769 Xfake_door_6, TRUE, FALSE,
6770 EL_EMC_GATE_6_GRAY, -1, -1
6773 Xfake_door_7, TRUE, FALSE,
6774 EL_EMC_GATE_7_GRAY, -1, -1
6777 Xfake_door_8, TRUE, FALSE,
6778 EL_EMC_GATE_8_GRAY, -1, -1
6782 Xballoon, TRUE, FALSE,
6786 Yballoon_n, FALSE, FALSE,
6787 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6790 Yballoon_nB, FALSE, TRUE,
6791 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6794 Yballoon_e, FALSE, FALSE,
6795 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6798 Yballoon_eB, FALSE, TRUE,
6799 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6802 Yballoon_s, FALSE, FALSE,
6803 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6806 Yballoon_sB, FALSE, TRUE,
6807 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6810 Yballoon_w, FALSE, FALSE,
6811 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6814 Yballoon_wB, FALSE, TRUE,
6815 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6819 Xball_1, TRUE, FALSE,
6820 EL_EMC_MAGIC_BALL, -1, -1
6823 Yball_1, FALSE, FALSE,
6824 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6827 Xball_2, FALSE, FALSE,
6828 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6831 Yball_2, FALSE, FALSE,
6832 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6835 Yball_blank, FALSE, FALSE,
6836 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6840 Xamoeba_1, TRUE, FALSE,
6841 EL_AMOEBA_DRY, ACTION_OTHER, -1
6844 Xamoeba_2, FALSE, FALSE,
6845 EL_AMOEBA_DRY, ACTION_OTHER, -1
6848 Xamoeba_3, FALSE, FALSE,
6849 EL_AMOEBA_DRY, ACTION_OTHER, -1
6852 Xamoeba_4, FALSE, FALSE,
6853 EL_AMOEBA_DRY, ACTION_OTHER, -1
6856 Xamoeba_5, TRUE, FALSE,
6857 EL_AMOEBA_WET, ACTION_OTHER, -1
6860 Xamoeba_6, FALSE, FALSE,
6861 EL_AMOEBA_WET, ACTION_OTHER, -1
6864 Xamoeba_7, FALSE, FALSE,
6865 EL_AMOEBA_WET, ACTION_OTHER, -1
6868 Xamoeba_8, FALSE, FALSE,
6869 EL_AMOEBA_WET, ACTION_OTHER, -1
6874 EL_AMOEBA_DROP, ACTION_GROWING, -1
6877 Xdrip_fall, FALSE, FALSE,
6878 EL_AMOEBA_DROP, -1, -1
6881 Xdrip_stretch, FALSE, FALSE,
6882 EL_AMOEBA_DROP, ACTION_FALLING, -1
6885 Xdrip_stretchB, FALSE, TRUE,
6886 EL_AMOEBA_DROP, ACTION_FALLING, -1
6889 Ydrip_1_s, FALSE, FALSE,
6890 EL_AMOEBA_DROP, ACTION_FALLING, -1
6893 Ydrip_1_sB, FALSE, TRUE,
6894 EL_AMOEBA_DROP, ACTION_FALLING, -1
6897 Ydrip_2_s, FALSE, FALSE,
6898 EL_AMOEBA_DROP, ACTION_FALLING, -1
6901 Ydrip_2_sB, FALSE, TRUE,
6902 EL_AMOEBA_DROP, ACTION_FALLING, -1
6906 Xwonderwall, TRUE, FALSE,
6907 EL_MAGIC_WALL, -1, -1
6910 Ywonderwall, FALSE, FALSE,
6911 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6915 Xwheel, TRUE, FALSE,
6916 EL_ROBOT_WHEEL, -1, -1
6919 Ywheel, FALSE, FALSE,
6920 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6924 Xswitch, TRUE, FALSE,
6925 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6928 Yswitch, FALSE, FALSE,
6929 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6933 Xbumper, TRUE, FALSE,
6934 EL_EMC_SPRING_BUMPER, -1, -1
6937 Ybumper, FALSE, FALSE,
6938 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6942 Xacid_nw, TRUE, FALSE,
6943 EL_ACID_POOL_TOPLEFT, -1, -1
6946 Xacid_ne, TRUE, FALSE,
6947 EL_ACID_POOL_TOPRIGHT, -1, -1
6950 Xacid_sw, TRUE, FALSE,
6951 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6954 Xacid_s, TRUE, FALSE,
6955 EL_ACID_POOL_BOTTOM, -1, -1
6958 Xacid_se, TRUE, FALSE,
6959 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6963 Xfake_blank, TRUE, FALSE,
6964 EL_INVISIBLE_WALL, -1, -1
6967 Yfake_blank, FALSE, FALSE,
6968 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6972 Xfake_grass, TRUE, FALSE,
6973 EL_EMC_FAKE_GRASS, -1, -1
6976 Yfake_grass, FALSE, FALSE,
6977 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6981 Xfake_amoeba, TRUE, FALSE,
6982 EL_EMC_DRIPPER, -1, -1
6985 Yfake_amoeba, FALSE, FALSE,
6986 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6990 Xlenses, TRUE, FALSE,
6991 EL_EMC_LENSES, -1, -1
6995 Xmagnify, TRUE, FALSE,
6996 EL_EMC_MAGNIFIER, -1, -1
7001 EL_QUICKSAND_EMPTY, -1, -1
7004 Xsand_stone, TRUE, FALSE,
7005 EL_QUICKSAND_FULL, -1, -1
7008 Xsand_stonein_1, FALSE, TRUE,
7009 EL_ROCK, ACTION_FILLING, -1
7012 Xsand_stonein_2, FALSE, TRUE,
7013 EL_ROCK, ACTION_FILLING, -1
7016 Xsand_stonein_3, FALSE, TRUE,
7017 EL_ROCK, ACTION_FILLING, -1
7020 Xsand_stonein_4, FALSE, TRUE,
7021 EL_ROCK, ACTION_FILLING, -1
7024 Xsand_sandstone_1, FALSE, FALSE,
7025 EL_QUICKSAND_FILLING, -1, -1
7028 Xsand_sandstone_2, FALSE, FALSE,
7029 EL_QUICKSAND_FILLING, -1, -1
7032 Xsand_sandstone_3, FALSE, FALSE,
7033 EL_QUICKSAND_FILLING, -1, -1
7036 Xsand_sandstone_4, FALSE, FALSE,
7037 EL_QUICKSAND_FILLING, -1, -1
7040 Xsand_stonesand_1, FALSE, FALSE,
7041 EL_QUICKSAND_EMPTYING, -1, -1
7044 Xsand_stonesand_2, FALSE, FALSE,
7045 EL_QUICKSAND_EMPTYING, -1, -1
7048 Xsand_stonesand_3, FALSE, FALSE,
7049 EL_QUICKSAND_EMPTYING, -1, -1
7052 Xsand_stonesand_4, FALSE, FALSE,
7053 EL_QUICKSAND_EMPTYING, -1, -1
7056 Xsand_stoneout_1, FALSE, FALSE,
7057 EL_ROCK, ACTION_EMPTYING, -1
7060 Xsand_stoneout_2, FALSE, FALSE,
7061 EL_ROCK, ACTION_EMPTYING, -1
7064 Xsand_stonesand_quickout_1, FALSE, FALSE,
7065 EL_QUICKSAND_EMPTYING, -1, -1
7068 Xsand_stonesand_quickout_2, FALSE, FALSE,
7069 EL_QUICKSAND_EMPTYING, -1, -1
7073 Xslide_ns, TRUE, FALSE,
7074 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7077 Yslide_ns_blank, FALSE, FALSE,
7078 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7081 Xslide_ew, TRUE, FALSE,
7082 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7085 Yslide_ew_blank, FALSE, FALSE,
7086 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7090 Xwind_n, TRUE, FALSE,
7091 EL_BALLOON_SWITCH_UP, -1, -1
7094 Xwind_e, TRUE, FALSE,
7095 EL_BALLOON_SWITCH_RIGHT, -1, -1
7098 Xwind_s, TRUE, FALSE,
7099 EL_BALLOON_SWITCH_DOWN, -1, -1
7102 Xwind_w, TRUE, FALSE,
7103 EL_BALLOON_SWITCH_LEFT, -1, -1
7106 Xwind_any, TRUE, FALSE,
7107 EL_BALLOON_SWITCH_ANY, -1, -1
7110 Xwind_stop, TRUE, FALSE,
7111 EL_BALLOON_SWITCH_NONE, -1, -1
7116 EL_EM_EXIT_CLOSED, -1, -1
7119 Xexit_1, TRUE, FALSE,
7120 EL_EM_EXIT_OPEN, -1, -1
7123 Xexit_2, FALSE, FALSE,
7124 EL_EM_EXIT_OPEN, -1, -1
7127 Xexit_3, FALSE, FALSE,
7128 EL_EM_EXIT_OPEN, -1, -1
7132 Xpause, FALSE, FALSE,
7137 Xwall_1, TRUE, FALSE,
7141 Xwall_2, TRUE, FALSE,
7142 EL_EMC_WALL_14, -1, -1
7145 Xwall_3, TRUE, FALSE,
7146 EL_EMC_WALL_15, -1, -1
7149 Xwall_4, TRUE, FALSE,
7150 EL_EMC_WALL_16, -1, -1
7154 Xroundwall_1, TRUE, FALSE,
7155 EL_WALL_SLIPPERY, -1, -1
7158 Xroundwall_2, TRUE, FALSE,
7159 EL_EMC_WALL_SLIPPERY_2, -1, -1
7162 Xroundwall_3, TRUE, FALSE,
7163 EL_EMC_WALL_SLIPPERY_3, -1, -1
7166 Xroundwall_4, TRUE, FALSE,
7167 EL_EMC_WALL_SLIPPERY_4, -1, -1
7171 Xsteel_1, TRUE, FALSE,
7172 EL_STEELWALL, -1, -1
7175 Xsteel_2, TRUE, FALSE,
7176 EL_EMC_STEELWALL_2, -1, -1
7179 Xsteel_3, TRUE, FALSE,
7180 EL_EMC_STEELWALL_3, -1, -1
7183 Xsteel_4, TRUE, FALSE,
7184 EL_EMC_STEELWALL_4, -1, -1
7188 Xdecor_1, TRUE, FALSE,
7189 EL_EMC_WALL_8, -1, -1
7192 Xdecor_2, TRUE, FALSE,
7193 EL_EMC_WALL_6, -1, -1
7196 Xdecor_3, TRUE, FALSE,
7197 EL_EMC_WALL_4, -1, -1
7200 Xdecor_4, TRUE, FALSE,
7201 EL_EMC_WALL_7, -1, -1
7204 Xdecor_5, TRUE, FALSE,
7205 EL_EMC_WALL_5, -1, -1
7208 Xdecor_6, TRUE, FALSE,
7209 EL_EMC_WALL_9, -1, -1
7212 Xdecor_7, TRUE, FALSE,
7213 EL_EMC_WALL_10, -1, -1
7216 Xdecor_8, TRUE, FALSE,
7217 EL_EMC_WALL_1, -1, -1
7220 Xdecor_9, TRUE, FALSE,
7221 EL_EMC_WALL_2, -1, -1
7224 Xdecor_10, TRUE, FALSE,
7225 EL_EMC_WALL_3, -1, -1
7228 Xdecor_11, TRUE, FALSE,
7229 EL_EMC_WALL_11, -1, -1
7232 Xdecor_12, TRUE, FALSE,
7233 EL_EMC_WALL_12, -1, -1
7237 Xalpha_0, TRUE, FALSE,
7238 EL_CHAR('0'), -1, -1
7241 Xalpha_1, TRUE, FALSE,
7242 EL_CHAR('1'), -1, -1
7245 Xalpha_2, TRUE, FALSE,
7246 EL_CHAR('2'), -1, -1
7249 Xalpha_3, TRUE, FALSE,
7250 EL_CHAR('3'), -1, -1
7253 Xalpha_4, TRUE, FALSE,
7254 EL_CHAR('4'), -1, -1
7257 Xalpha_5, TRUE, FALSE,
7258 EL_CHAR('5'), -1, -1
7261 Xalpha_6, TRUE, FALSE,
7262 EL_CHAR('6'), -1, -1
7265 Xalpha_7, TRUE, FALSE,
7266 EL_CHAR('7'), -1, -1
7269 Xalpha_8, TRUE, FALSE,
7270 EL_CHAR('8'), -1, -1
7273 Xalpha_9, TRUE, FALSE,
7274 EL_CHAR('9'), -1, -1
7277 Xalpha_excla, TRUE, FALSE,
7278 EL_CHAR('!'), -1, -1
7281 Xalpha_apost, TRUE, FALSE,
7282 EL_CHAR('\''), -1, -1
7285 Xalpha_comma, TRUE, FALSE,
7286 EL_CHAR(','), -1, -1
7289 Xalpha_minus, TRUE, FALSE,
7290 EL_CHAR('-'), -1, -1
7293 Xalpha_perio, TRUE, FALSE,
7294 EL_CHAR('.'), -1, -1
7297 Xalpha_colon, TRUE, FALSE,
7298 EL_CHAR(':'), -1, -1
7301 Xalpha_quest, TRUE, FALSE,
7302 EL_CHAR('?'), -1, -1
7305 Xalpha_a, TRUE, FALSE,
7306 EL_CHAR('A'), -1, -1
7309 Xalpha_b, TRUE, FALSE,
7310 EL_CHAR('B'), -1, -1
7313 Xalpha_c, TRUE, FALSE,
7314 EL_CHAR('C'), -1, -1
7317 Xalpha_d, TRUE, FALSE,
7318 EL_CHAR('D'), -1, -1
7321 Xalpha_e, TRUE, FALSE,
7322 EL_CHAR('E'), -1, -1
7325 Xalpha_f, TRUE, FALSE,
7326 EL_CHAR('F'), -1, -1
7329 Xalpha_g, TRUE, FALSE,
7330 EL_CHAR('G'), -1, -1
7333 Xalpha_h, TRUE, FALSE,
7334 EL_CHAR('H'), -1, -1
7337 Xalpha_i, TRUE, FALSE,
7338 EL_CHAR('I'), -1, -1
7341 Xalpha_j, TRUE, FALSE,
7342 EL_CHAR('J'), -1, -1
7345 Xalpha_k, TRUE, FALSE,
7346 EL_CHAR('K'), -1, -1
7349 Xalpha_l, TRUE, FALSE,
7350 EL_CHAR('L'), -1, -1
7353 Xalpha_m, TRUE, FALSE,
7354 EL_CHAR('M'), -1, -1
7357 Xalpha_n, TRUE, FALSE,
7358 EL_CHAR('N'), -1, -1
7361 Xalpha_o, TRUE, FALSE,
7362 EL_CHAR('O'), -1, -1
7365 Xalpha_p, TRUE, FALSE,
7366 EL_CHAR('P'), -1, -1
7369 Xalpha_q, TRUE, FALSE,
7370 EL_CHAR('Q'), -1, -1
7373 Xalpha_r, TRUE, FALSE,
7374 EL_CHAR('R'), -1, -1
7377 Xalpha_s, TRUE, FALSE,
7378 EL_CHAR('S'), -1, -1
7381 Xalpha_t, TRUE, FALSE,
7382 EL_CHAR('T'), -1, -1
7385 Xalpha_u, TRUE, FALSE,
7386 EL_CHAR('U'), -1, -1
7389 Xalpha_v, TRUE, FALSE,
7390 EL_CHAR('V'), -1, -1
7393 Xalpha_w, TRUE, FALSE,
7394 EL_CHAR('W'), -1, -1
7397 Xalpha_x, TRUE, FALSE,
7398 EL_CHAR('X'), -1, -1
7401 Xalpha_y, TRUE, FALSE,
7402 EL_CHAR('Y'), -1, -1
7405 Xalpha_z, TRUE, FALSE,
7406 EL_CHAR('Z'), -1, -1
7409 Xalpha_arrow_e, TRUE, FALSE,
7410 EL_CHAR('>'), -1, -1
7413 Xalpha_arrow_w, TRUE, FALSE,
7414 EL_CHAR('<'), -1, -1
7417 Xalpha_copyr, TRUE, FALSE,
7418 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7422 Ykey_1_blank, FALSE, FALSE,
7423 EL_EM_KEY_1, ACTION_COLLECTING, -1
7426 Ykey_2_blank, FALSE, FALSE,
7427 EL_EM_KEY_2, ACTION_COLLECTING, -1
7430 Ykey_3_blank, FALSE, FALSE,
7431 EL_EM_KEY_3, ACTION_COLLECTING, -1
7434 Ykey_4_blank, FALSE, FALSE,
7435 EL_EM_KEY_4, ACTION_COLLECTING, -1
7438 Ykey_5_blank, FALSE, FALSE,
7439 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7442 Ykey_6_blank, FALSE, FALSE,
7443 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7446 Ykey_7_blank, FALSE, FALSE,
7447 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7450 Ykey_8_blank, FALSE, FALSE,
7451 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7454 Ylenses_blank, FALSE, FALSE,
7455 EL_EMC_LENSES, ACTION_COLLECTING, -1
7458 Ymagnify_blank, FALSE, FALSE,
7459 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7462 Ygrass_blank, FALSE, FALSE,
7463 EL_EMC_GRASS, ACTION_SNAPPING, -1
7466 Ydirt_blank, FALSE, FALSE,
7467 EL_SAND, ACTION_SNAPPING, -1
7476 static struct Mapping_EM_to_RND_player
7485 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7489 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7493 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7497 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7501 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7505 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7509 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7513 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7517 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7521 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7525 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7529 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7533 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7537 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7541 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7545 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7549 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7553 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7557 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7561 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7565 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7569 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7573 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7577 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7581 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7585 EL_PLAYER_1, ACTION_DEFAULT, -1,
7589 EL_PLAYER_2, ACTION_DEFAULT, -1,
7593 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7597 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7601 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7605 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7609 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7613 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7617 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7621 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7625 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7629 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7633 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7637 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7641 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7645 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7649 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7653 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7657 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7661 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7665 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7669 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7673 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7677 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7681 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7685 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7689 EL_PLAYER_3, ACTION_DEFAULT, -1,
7693 EL_PLAYER_4, ACTION_DEFAULT, -1,
7702 int map_element_RND_to_EM_cave(int element_rnd)
7704 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7705 static boolean mapping_initialized = FALSE;
7707 if (!mapping_initialized)
7711 // return "Xalpha_quest" for all undefined elements in mapping array
7712 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7713 mapping_RND_to_EM[i] = Xalpha_quest;
7715 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7716 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7717 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7718 em_object_mapping_list[i].element_em;
7720 mapping_initialized = TRUE;
7723 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7725 Warn("invalid RND level element %d", element_rnd);
7730 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7733 int map_element_EM_to_RND_cave(int element_em_cave)
7735 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7736 static boolean mapping_initialized = FALSE;
7738 if (!mapping_initialized)
7742 // return "EL_UNKNOWN" for all undefined elements in mapping array
7743 for (i = 0; i < GAME_TILE_MAX; i++)
7744 mapping_EM_to_RND[i] = EL_UNKNOWN;
7746 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7747 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7748 em_object_mapping_list[i].element_rnd;
7750 mapping_initialized = TRUE;
7753 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7755 Warn("invalid EM cave element %d", element_em_cave);
7760 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7763 int map_element_EM_to_RND_game(int element_em_game)
7765 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7766 static boolean mapping_initialized = FALSE;
7768 if (!mapping_initialized)
7772 // return "EL_UNKNOWN" for all undefined elements in mapping array
7773 for (i = 0; i < GAME_TILE_MAX; i++)
7774 mapping_EM_to_RND[i] = EL_UNKNOWN;
7776 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7777 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7778 em_object_mapping_list[i].element_rnd;
7780 mapping_initialized = TRUE;
7783 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7785 Warn("invalid EM game element %d", element_em_game);
7790 return mapping_EM_to_RND[element_em_game];
7793 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7795 struct LevelInfo_EM *level_em = level->native_em_level;
7796 struct CAVE *cav = level_em->cav;
7799 for (i = 0; i < GAME_TILE_MAX; i++)
7800 cav->android_array[i] = Cblank;
7802 for (i = 0; i < level->num_android_clone_elements; i++)
7804 int element_rnd = level->android_clone_element[i];
7805 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7807 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7808 if (em_object_mapping_list[j].element_rnd == element_rnd)
7809 cav->android_array[em_object_mapping_list[j].element_em] =
7814 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7816 struct LevelInfo_EM *level_em = level->native_em_level;
7817 struct CAVE *cav = level_em->cav;
7820 level->num_android_clone_elements = 0;
7822 for (i = 0; i < GAME_TILE_MAX; i++)
7824 int element_em_cave = cav->android_array[i];
7826 boolean element_found = FALSE;
7828 if (element_em_cave == Cblank)
7831 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7833 for (j = 0; j < level->num_android_clone_elements; j++)
7834 if (level->android_clone_element[j] == element_rnd)
7835 element_found = TRUE;
7839 level->android_clone_element[level->num_android_clone_elements++] =
7842 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7847 if (level->num_android_clone_elements == 0)
7849 level->num_android_clone_elements = 1;
7850 level->android_clone_element[0] = EL_EMPTY;
7854 int map_direction_RND_to_EM(int direction)
7856 return (direction == MV_UP ? 0 :
7857 direction == MV_RIGHT ? 1 :
7858 direction == MV_DOWN ? 2 :
7859 direction == MV_LEFT ? 3 :
7863 int map_direction_EM_to_RND(int direction)
7865 return (direction == 0 ? MV_UP :
7866 direction == 1 ? MV_RIGHT :
7867 direction == 2 ? MV_DOWN :
7868 direction == 3 ? MV_LEFT :
7872 int map_element_RND_to_SP(int element_rnd)
7874 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7876 if (element_rnd >= EL_SP_START &&
7877 element_rnd <= EL_SP_END)
7878 element_sp = element_rnd - EL_SP_START;
7879 else if (element_rnd == EL_EMPTY_SPACE)
7881 else if (element_rnd == EL_INVISIBLE_WALL)
7887 int map_element_SP_to_RND(int element_sp)
7889 int element_rnd = EL_UNKNOWN;
7891 if (element_sp >= 0x00 &&
7893 element_rnd = EL_SP_START + element_sp;
7894 else if (element_sp == 0x28)
7895 element_rnd = EL_INVISIBLE_WALL;
7900 int map_action_SP_to_RND(int action_sp)
7904 case actActive: return ACTION_ACTIVE;
7905 case actImpact: return ACTION_IMPACT;
7906 case actExploding: return ACTION_EXPLODING;
7907 case actDigging: return ACTION_DIGGING;
7908 case actSnapping: return ACTION_SNAPPING;
7909 case actCollecting: return ACTION_COLLECTING;
7910 case actPassing: return ACTION_PASSING;
7911 case actPushing: return ACTION_PUSHING;
7912 case actDropping: return ACTION_DROPPING;
7914 default: return ACTION_DEFAULT;
7918 int map_element_RND_to_MM(int element_rnd)
7920 return (element_rnd >= EL_MM_START_1 &&
7921 element_rnd <= EL_MM_END_1 ?
7922 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7924 element_rnd >= EL_MM_START_2 &&
7925 element_rnd <= EL_MM_END_2 ?
7926 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7928 element_rnd >= EL_CHAR_START &&
7929 element_rnd <= EL_CHAR_END ?
7930 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7932 element_rnd >= EL_MM_RUNTIME_START &&
7933 element_rnd <= EL_MM_RUNTIME_END ?
7934 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7936 element_rnd >= EL_MM_DUMMY_START &&
7937 element_rnd <= EL_MM_DUMMY_END ?
7938 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7940 EL_MM_EMPTY_NATIVE);
7943 int map_element_MM_to_RND(int element_mm)
7945 return (element_mm == EL_MM_EMPTY_NATIVE ||
7946 element_mm == EL_DF_EMPTY_NATIVE ?
7949 element_mm >= EL_MM_START_1_NATIVE &&
7950 element_mm <= EL_MM_END_1_NATIVE ?
7951 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7953 element_mm >= EL_MM_START_2_NATIVE &&
7954 element_mm <= EL_MM_END_2_NATIVE ?
7955 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7957 element_mm >= EL_MM_CHAR_START_NATIVE &&
7958 element_mm <= EL_MM_CHAR_END_NATIVE ?
7959 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7961 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7962 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7963 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7965 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7966 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7967 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7972 int map_action_MM_to_RND(int action_mm)
7974 // all MM actions are defined to exactly match their RND counterparts
7978 int map_sound_MM_to_RND(int sound_mm)
7982 case SND_MM_GAME_LEVELTIME_CHARGING:
7983 return SND_GAME_LEVELTIME_CHARGING;
7985 case SND_MM_GAME_HEALTH_CHARGING:
7986 return SND_GAME_HEALTH_CHARGING;
7989 return SND_UNDEFINED;
7993 int map_mm_wall_element(int element)
7995 return (element >= EL_MM_STEEL_WALL_START &&
7996 element <= EL_MM_STEEL_WALL_END ?
7999 element >= EL_MM_WOODEN_WALL_START &&
8000 element <= EL_MM_WOODEN_WALL_END ?
8003 element >= EL_MM_ICE_WALL_START &&
8004 element <= EL_MM_ICE_WALL_END ?
8007 element >= EL_MM_AMOEBA_WALL_START &&
8008 element <= EL_MM_AMOEBA_WALL_END ?
8011 element >= EL_DF_STEEL_WALL_START &&
8012 element <= EL_DF_STEEL_WALL_END ?
8015 element >= EL_DF_WOODEN_WALL_START &&
8016 element <= EL_DF_WOODEN_WALL_END ?
8022 int map_mm_wall_element_editor(int element)
8026 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8027 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8028 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8029 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8030 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8031 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8033 default: return element;
8037 int get_next_element(int element)
8041 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8042 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8043 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8044 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8045 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8046 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8047 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8048 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8049 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8050 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8051 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8053 default: return element;
8057 int el2img_mm(int element_mm)
8059 return el2img(map_element_MM_to_RND(element_mm));
8062 int el_act_dir2img(int element, int action, int direction)
8064 element = GFX_ELEMENT(element);
8065 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8067 // direction_graphic[][] == graphic[] for undefined direction graphics
8068 return element_info[element].direction_graphic[action][direction];
8071 static int el_act_dir2crm(int element, int action, int direction)
8073 element = GFX_ELEMENT(element);
8074 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8076 // direction_graphic[][] == graphic[] for undefined direction graphics
8077 return element_info[element].direction_crumbled[action][direction];
8080 int el_act2img(int element, int action)
8082 element = GFX_ELEMENT(element);
8084 return element_info[element].graphic[action];
8087 int el_act2crm(int element, int action)
8089 element = GFX_ELEMENT(element);
8091 return element_info[element].crumbled[action];
8094 int el_dir2img(int element, int direction)
8096 element = GFX_ELEMENT(element);
8098 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8101 int el2baseimg(int element)
8103 return element_info[element].graphic[ACTION_DEFAULT];
8106 int el2img(int element)
8108 element = GFX_ELEMENT(element);
8110 return element_info[element].graphic[ACTION_DEFAULT];
8113 int el2edimg(int element)
8115 element = GFX_ELEMENT(element);
8117 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8120 int el2preimg(int element)
8122 element = GFX_ELEMENT(element);
8124 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8127 int el2panelimg(int element)
8129 element = GFX_ELEMENT(element);
8131 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8134 int font2baseimg(int font_nr)
8136 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8139 int getBeltNrFromBeltElement(int element)
8141 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8142 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8143 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8146 int getBeltNrFromBeltActiveElement(int element)
8148 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8149 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8150 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8153 int getBeltNrFromBeltSwitchElement(int element)
8155 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8156 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8157 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8160 int getBeltDirNrFromBeltElement(int element)
8162 static int belt_base_element[4] =
8164 EL_CONVEYOR_BELT_1_LEFT,
8165 EL_CONVEYOR_BELT_2_LEFT,
8166 EL_CONVEYOR_BELT_3_LEFT,
8167 EL_CONVEYOR_BELT_4_LEFT
8170 int belt_nr = getBeltNrFromBeltElement(element);
8171 int belt_dir_nr = element - belt_base_element[belt_nr];
8173 return (belt_dir_nr % 3);
8176 int getBeltDirNrFromBeltSwitchElement(int element)
8178 static int belt_base_element[4] =
8180 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8181 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8182 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8183 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8186 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8187 int belt_dir_nr = element - belt_base_element[belt_nr];
8189 return (belt_dir_nr % 3);
8192 int getBeltDirFromBeltElement(int element)
8194 static int belt_move_dir[3] =
8201 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8203 return belt_move_dir[belt_dir_nr];
8206 int getBeltDirFromBeltSwitchElement(int element)
8208 static int belt_move_dir[3] =
8215 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8217 return belt_move_dir[belt_dir_nr];
8220 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
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 return belt_base_element[belt_nr] + belt_dir_nr;
8233 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8235 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8237 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8240 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8242 static int belt_base_element[4] =
8244 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8245 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8246 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8247 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8250 return belt_base_element[belt_nr] + belt_dir_nr;
8253 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8255 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8257 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8260 boolean swapTiles_EM(boolean is_pre_emc_cave)
8262 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8265 boolean getTeamMode_EM(void)
8267 return game.team_mode || network_playing;
8270 boolean isActivePlayer_EM(int player_nr)
8272 return stored_player[player_nr].active;
8275 unsigned int InitRND(int seed)
8277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8278 return InitEngineRandom_EM(seed);
8279 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8280 return InitEngineRandom_SP(seed);
8281 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8282 return InitEngineRandom_MM(seed);
8284 return InitEngineRandom_RND(seed);
8287 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8288 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8290 static int get_effective_element_EM(int tile, int frame_em)
8292 int element = object_mapping[tile].element_rnd;
8293 int action = object_mapping[tile].action;
8294 boolean is_backside = object_mapping[tile].is_backside;
8295 boolean action_removing = (action == ACTION_DIGGING ||
8296 action == ACTION_SNAPPING ||
8297 action == ACTION_COLLECTING);
8305 return (frame_em > 5 ? EL_EMPTY : element);
8311 else // frame_em == 7
8322 case Ydiamond_stone:
8326 case Xdrip_stretchB:
8342 case Ymagnify_blank:
8345 case Xsand_stonein_1:
8346 case Xsand_stonein_2:
8347 case Xsand_stonein_3:
8348 case Xsand_stonein_4:
8352 return (is_backside || action_removing ? EL_EMPTY : element);
8357 static boolean check_linear_animation_EM(int tile)
8361 case Xsand_stonesand_1:
8362 case Xsand_stonesand_quickout_1:
8363 case Xsand_sandstone_1:
8364 case Xsand_stonein_1:
8365 case Xsand_stoneout_1:
8393 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8394 boolean has_crumbled_graphics,
8395 int crumbled, int sync_frame)
8397 // if element can be crumbled, but certain action graphics are just empty
8398 // space (like instantly snapping sand to empty space in 1 frame), do not
8399 // treat these empty space graphics as crumbled graphics in EMC engine
8400 if (crumbled == IMG_EMPTY_SPACE)
8401 has_crumbled_graphics = FALSE;
8403 if (has_crumbled_graphics)
8405 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8406 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8407 g_crumbled->anim_delay,
8408 g_crumbled->anim_mode,
8409 g_crumbled->anim_start_frame,
8412 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8413 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8415 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8416 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8418 g_em->has_crumbled_graphics = TRUE;
8422 g_em->crumbled_bitmap = NULL;
8423 g_em->crumbled_src_x = 0;
8424 g_em->crumbled_src_y = 0;
8425 g_em->crumbled_border_size = 0;
8426 g_em->crumbled_tile_size = 0;
8428 g_em->has_crumbled_graphics = FALSE;
8433 void ResetGfxAnimation_EM(int x, int y, int tile)
8439 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8440 int tile, int frame_em, int x, int y)
8442 int action = object_mapping[tile].action;
8443 int direction = object_mapping[tile].direction;
8444 int effective_element = get_effective_element_EM(tile, frame_em);
8445 int graphic = (direction == MV_NONE ?
8446 el_act2img(effective_element, action) :
8447 el_act_dir2img(effective_element, action, direction));
8448 struct GraphicInfo *g = &graphic_info[graphic];
8450 boolean action_removing = (action == ACTION_DIGGING ||
8451 action == ACTION_SNAPPING ||
8452 action == ACTION_COLLECTING);
8453 boolean action_moving = (action == ACTION_FALLING ||
8454 action == ACTION_MOVING ||
8455 action == ACTION_PUSHING ||
8456 action == ACTION_EATING ||
8457 action == ACTION_FILLING ||
8458 action == ACTION_EMPTYING);
8459 boolean action_falling = (action == ACTION_FALLING ||
8460 action == ACTION_FILLING ||
8461 action == ACTION_EMPTYING);
8463 // special case: graphic uses "2nd movement tile" and has defined
8464 // 7 frames for movement animation (or less) => use default graphic
8465 // for last (8th) frame which ends the movement animation
8466 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8468 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8469 graphic = (direction == MV_NONE ?
8470 el_act2img(effective_element, action) :
8471 el_act_dir2img(effective_element, action, direction));
8473 g = &graphic_info[graphic];
8476 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8480 else if (action_moving)
8482 boolean is_backside = object_mapping[tile].is_backside;
8486 int direction = object_mapping[tile].direction;
8487 int move_dir = (action_falling ? MV_DOWN : direction);
8492 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8493 if (g->double_movement && frame_em == 0)
8497 if (move_dir == MV_LEFT)
8498 GfxFrame[x - 1][y] = GfxFrame[x][y];
8499 else if (move_dir == MV_RIGHT)
8500 GfxFrame[x + 1][y] = GfxFrame[x][y];
8501 else if (move_dir == MV_UP)
8502 GfxFrame[x][y - 1] = GfxFrame[x][y];
8503 else if (move_dir == MV_DOWN)
8504 GfxFrame[x][y + 1] = GfxFrame[x][y];
8511 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8512 if (tile == Xsand_stonesand_quickout_1 ||
8513 tile == Xsand_stonesand_quickout_2)
8517 if (graphic_info[graphic].anim_global_sync)
8518 sync_frame = FrameCounter;
8519 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8520 sync_frame = GfxFrame[x][y];
8522 sync_frame = 0; // playfield border (pseudo steel)
8524 SetRandomAnimationValue(x, y);
8526 int frame = getAnimationFrame(g->anim_frames,
8529 g->anim_start_frame,
8532 g_em->unique_identifier =
8533 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8536 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8537 int tile, int frame_em, int x, int y)
8539 int action = object_mapping[tile].action;
8540 int direction = object_mapping[tile].direction;
8541 boolean is_backside = object_mapping[tile].is_backside;
8542 int effective_element = get_effective_element_EM(tile, frame_em);
8543 int effective_action = action;
8544 int graphic = (direction == MV_NONE ?
8545 el_act2img(effective_element, effective_action) :
8546 el_act_dir2img(effective_element, effective_action,
8548 int crumbled = (direction == MV_NONE ?
8549 el_act2crm(effective_element, effective_action) :
8550 el_act_dir2crm(effective_element, effective_action,
8552 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8553 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8554 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8555 struct GraphicInfo *g = &graphic_info[graphic];
8558 // special case: graphic uses "2nd movement tile" and has defined
8559 // 7 frames for movement animation (or less) => use default graphic
8560 // for last (8th) frame which ends the movement animation
8561 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8563 effective_action = ACTION_DEFAULT;
8564 graphic = (direction == MV_NONE ?
8565 el_act2img(effective_element, effective_action) :
8566 el_act_dir2img(effective_element, effective_action,
8568 crumbled = (direction == MV_NONE ?
8569 el_act2crm(effective_element, effective_action) :
8570 el_act_dir2crm(effective_element, effective_action,
8573 g = &graphic_info[graphic];
8576 if (graphic_info[graphic].anim_global_sync)
8577 sync_frame = FrameCounter;
8578 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8579 sync_frame = GfxFrame[x][y];
8581 sync_frame = 0; // playfield border (pseudo steel)
8583 SetRandomAnimationValue(x, y);
8585 int frame = getAnimationFrame(g->anim_frames,
8588 g->anim_start_frame,
8591 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8592 g->double_movement && is_backside);
8594 // (updating the "crumbled" graphic definitions is probably not really needed,
8595 // as animations for crumbled graphics can't be longer than one EMC cycle)
8596 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8600 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8601 int player_nr, int anim, int frame_em)
8603 int element = player_mapping[player_nr][anim].element_rnd;
8604 int action = player_mapping[player_nr][anim].action;
8605 int direction = player_mapping[player_nr][anim].direction;
8606 int graphic = (direction == MV_NONE ?
8607 el_act2img(element, action) :
8608 el_act_dir2img(element, action, direction));
8609 struct GraphicInfo *g = &graphic_info[graphic];
8612 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8614 stored_player[player_nr].StepFrame = frame_em;
8616 sync_frame = stored_player[player_nr].Frame;
8618 int frame = getAnimationFrame(g->anim_frames,
8621 g->anim_start_frame,
8624 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8625 &g_em->src_x, &g_em->src_y, FALSE);
8628 void InitGraphicInfo_EM(void)
8632 // always start with reliable default values
8633 for (i = 0; i < GAME_TILE_MAX; i++)
8635 object_mapping[i].element_rnd = EL_UNKNOWN;
8636 object_mapping[i].is_backside = FALSE;
8637 object_mapping[i].action = ACTION_DEFAULT;
8638 object_mapping[i].direction = MV_NONE;
8641 // always start with reliable default values
8642 for (p = 0; p < MAX_PLAYERS; p++)
8644 for (i = 0; i < PLY_MAX; i++)
8646 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8647 player_mapping[p][i].action = ACTION_DEFAULT;
8648 player_mapping[p][i].direction = MV_NONE;
8652 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8654 int e = em_object_mapping_list[i].element_em;
8656 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8657 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8659 if (em_object_mapping_list[i].action != -1)
8660 object_mapping[e].action = em_object_mapping_list[i].action;
8662 if (em_object_mapping_list[i].direction != -1)
8663 object_mapping[e].direction =
8664 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8667 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8669 int a = em_player_mapping_list[i].action_em;
8670 int p = em_player_mapping_list[i].player_nr;
8672 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8674 if (em_player_mapping_list[i].action != -1)
8675 player_mapping[p][a].action = em_player_mapping_list[i].action;
8677 if (em_player_mapping_list[i].direction != -1)
8678 player_mapping[p][a].direction =
8679 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8682 for (i = 0; i < GAME_TILE_MAX; i++)
8684 int element = object_mapping[i].element_rnd;
8685 int action = object_mapping[i].action;
8686 int direction = object_mapping[i].direction;
8687 boolean is_backside = object_mapping[i].is_backside;
8688 boolean action_exploding = ((action == ACTION_EXPLODING ||
8689 action == ACTION_SMASHED_BY_ROCK ||
8690 action == ACTION_SMASHED_BY_SPRING) &&
8691 element != EL_DIAMOND);
8692 boolean action_active = (action == ACTION_ACTIVE);
8693 boolean action_other = (action == ACTION_OTHER);
8695 for (j = 0; j < 8; j++)
8697 int effective_element = get_effective_element_EM(i, j);
8698 int effective_action = (j < 7 ? action :
8699 i == Xdrip_stretch ? action :
8700 i == Xdrip_stretchB ? action :
8701 i == Ydrip_1_s ? action :
8702 i == Ydrip_1_sB ? action :
8703 i == Yball_1 ? action :
8704 i == Xball_2 ? action :
8705 i == Yball_2 ? action :
8706 i == Yball_blank ? action :
8707 i == Ykey_1_blank ? action :
8708 i == Ykey_2_blank ? action :
8709 i == Ykey_3_blank ? action :
8710 i == Ykey_4_blank ? action :
8711 i == Ykey_5_blank ? action :
8712 i == Ykey_6_blank ? action :
8713 i == Ykey_7_blank ? action :
8714 i == Ykey_8_blank ? action :
8715 i == Ylenses_blank ? action :
8716 i == Ymagnify_blank ? action :
8717 i == Ygrass_blank ? action :
8718 i == Ydirt_blank ? action :
8719 i == Xsand_stonein_1 ? action :
8720 i == Xsand_stonein_2 ? action :
8721 i == Xsand_stonein_3 ? action :
8722 i == Xsand_stonein_4 ? action :
8723 i == Xsand_stoneout_1 ? action :
8724 i == Xsand_stoneout_2 ? action :
8725 i == Xboom_android ? ACTION_EXPLODING :
8726 action_exploding ? ACTION_EXPLODING :
8727 action_active ? action :
8728 action_other ? action :
8730 int graphic = (el_act_dir2img(effective_element, effective_action,
8732 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8734 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8735 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8736 boolean has_action_graphics = (graphic != base_graphic);
8737 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8738 struct GraphicInfo *g = &graphic_info[graphic];
8739 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8742 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8743 boolean special_animation = (action != ACTION_DEFAULT &&
8744 g->anim_frames == 3 &&
8745 g->anim_delay == 2 &&
8746 g->anim_mode & ANIM_LINEAR);
8747 int sync_frame = (i == Xdrip_stretch ? 7 :
8748 i == Xdrip_stretchB ? 7 :
8749 i == Ydrip_2_s ? j + 8 :
8750 i == Ydrip_2_sB ? j + 8 :
8759 i == Xfake_acid_1 ? 0 :
8760 i == Xfake_acid_2 ? 10 :
8761 i == Xfake_acid_3 ? 20 :
8762 i == Xfake_acid_4 ? 30 :
8763 i == Xfake_acid_5 ? 40 :
8764 i == Xfake_acid_6 ? 50 :
8765 i == Xfake_acid_7 ? 60 :
8766 i == Xfake_acid_8 ? 70 :
8767 i == Xfake_acid_1_player ? 0 :
8768 i == Xfake_acid_2_player ? 10 :
8769 i == Xfake_acid_3_player ? 20 :
8770 i == Xfake_acid_4_player ? 30 :
8771 i == Xfake_acid_5_player ? 40 :
8772 i == Xfake_acid_6_player ? 50 :
8773 i == Xfake_acid_7_player ? 60 :
8774 i == Xfake_acid_8_player ? 70 :
8776 i == Yball_2 ? j + 8 :
8777 i == Yball_blank ? j + 1 :
8778 i == Ykey_1_blank ? j + 1 :
8779 i == Ykey_2_blank ? j + 1 :
8780 i == Ykey_3_blank ? j + 1 :
8781 i == Ykey_4_blank ? j + 1 :
8782 i == Ykey_5_blank ? j + 1 :
8783 i == Ykey_6_blank ? j + 1 :
8784 i == Ykey_7_blank ? j + 1 :
8785 i == Ykey_8_blank ? j + 1 :
8786 i == Ylenses_blank ? j + 1 :
8787 i == Ymagnify_blank ? j + 1 :
8788 i == Ygrass_blank ? j + 1 :
8789 i == Ydirt_blank ? j + 1 :
8790 i == Xamoeba_1 ? 0 :
8791 i == Xamoeba_2 ? 1 :
8792 i == Xamoeba_3 ? 2 :
8793 i == Xamoeba_4 ? 3 :
8794 i == Xamoeba_5 ? 0 :
8795 i == Xamoeba_6 ? 1 :
8796 i == Xamoeba_7 ? 2 :
8797 i == Xamoeba_8 ? 3 :
8798 i == Xexit_2 ? j + 8 :
8799 i == Xexit_3 ? j + 16 :
8800 i == Xdynamite_1 ? 0 :
8801 i == Xdynamite_2 ? 8 :
8802 i == Xdynamite_3 ? 16 :
8803 i == Xdynamite_4 ? 24 :
8804 i == Xsand_stonein_1 ? j + 1 :
8805 i == Xsand_stonein_2 ? j + 9 :
8806 i == Xsand_stonein_3 ? j + 17 :
8807 i == Xsand_stonein_4 ? j + 25 :
8808 i == Xsand_stoneout_1 && j == 0 ? 0 :
8809 i == Xsand_stoneout_1 && j == 1 ? 0 :
8810 i == Xsand_stoneout_1 && j == 2 ? 1 :
8811 i == Xsand_stoneout_1 && j == 3 ? 2 :
8812 i == Xsand_stoneout_1 && j == 4 ? 2 :
8813 i == Xsand_stoneout_1 && j == 5 ? 3 :
8814 i == Xsand_stoneout_1 && j == 6 ? 4 :
8815 i == Xsand_stoneout_1 && j == 7 ? 4 :
8816 i == Xsand_stoneout_2 && j == 0 ? 5 :
8817 i == Xsand_stoneout_2 && j == 1 ? 6 :
8818 i == Xsand_stoneout_2 && j == 2 ? 7 :
8819 i == Xsand_stoneout_2 && j == 3 ? 8 :
8820 i == Xsand_stoneout_2 && j == 4 ? 9 :
8821 i == Xsand_stoneout_2 && j == 5 ? 11 :
8822 i == Xsand_stoneout_2 && j == 6 ? 13 :
8823 i == Xsand_stoneout_2 && j == 7 ? 15 :
8824 i == Xboom_bug && j == 1 ? 2 :
8825 i == Xboom_bug && j == 2 ? 2 :
8826 i == Xboom_bug && j == 3 ? 4 :
8827 i == Xboom_bug && j == 4 ? 4 :
8828 i == Xboom_bug && j == 5 ? 2 :
8829 i == Xboom_bug && j == 6 ? 2 :
8830 i == Xboom_bug && j == 7 ? 0 :
8831 i == Xboom_tank && j == 1 ? 2 :
8832 i == Xboom_tank && j == 2 ? 2 :
8833 i == Xboom_tank && j == 3 ? 4 :
8834 i == Xboom_tank && j == 4 ? 4 :
8835 i == Xboom_tank && j == 5 ? 2 :
8836 i == Xboom_tank && j == 6 ? 2 :
8837 i == Xboom_tank && j == 7 ? 0 :
8838 i == Xboom_android && j == 7 ? 6 :
8839 i == Xboom_1 && j == 1 ? 2 :
8840 i == Xboom_1 && j == 2 ? 2 :
8841 i == Xboom_1 && j == 3 ? 4 :
8842 i == Xboom_1 && j == 4 ? 4 :
8843 i == Xboom_1 && j == 5 ? 6 :
8844 i == Xboom_1 && j == 6 ? 6 :
8845 i == Xboom_1 && j == 7 ? 8 :
8846 i == Xboom_2 && j == 0 ? 8 :
8847 i == Xboom_2 && j == 1 ? 8 :
8848 i == Xboom_2 && j == 2 ? 10 :
8849 i == Xboom_2 && j == 3 ? 10 :
8850 i == Xboom_2 && j == 4 ? 10 :
8851 i == Xboom_2 && j == 5 ? 12 :
8852 i == Xboom_2 && j == 6 ? 12 :
8853 i == Xboom_2 && j == 7 ? 12 :
8854 special_animation && j == 4 ? 3 :
8855 effective_action != action ? 0 :
8857 int frame = getAnimationFrame(g->anim_frames,
8860 g->anim_start_frame,
8863 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8864 g->double_movement && is_backside);
8866 g_em->bitmap = src_bitmap;
8867 g_em->src_x = src_x;
8868 g_em->src_y = src_y;
8869 g_em->src_offset_x = 0;
8870 g_em->src_offset_y = 0;
8871 g_em->dst_offset_x = 0;
8872 g_em->dst_offset_y = 0;
8873 g_em->width = TILEX;
8874 g_em->height = TILEY;
8876 g_em->preserve_background = FALSE;
8878 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8881 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8882 effective_action == ACTION_MOVING ||
8883 effective_action == ACTION_PUSHING ||
8884 effective_action == ACTION_EATING)) ||
8885 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8886 effective_action == ACTION_EMPTYING)))
8889 (effective_action == ACTION_FALLING ||
8890 effective_action == ACTION_FILLING ||
8891 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8892 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8893 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8894 int num_steps = (i == Ydrip_1_s ? 16 :
8895 i == Ydrip_1_sB ? 16 :
8896 i == Ydrip_2_s ? 16 :
8897 i == Ydrip_2_sB ? 16 :
8898 i == Xsand_stonein_1 ? 32 :
8899 i == Xsand_stonein_2 ? 32 :
8900 i == Xsand_stonein_3 ? 32 :
8901 i == Xsand_stonein_4 ? 32 :
8902 i == Xsand_stoneout_1 ? 16 :
8903 i == Xsand_stoneout_2 ? 16 : 8);
8904 int cx = ABS(dx) * (TILEX / num_steps);
8905 int cy = ABS(dy) * (TILEY / num_steps);
8906 int step_frame = (i == Ydrip_2_s ? j + 8 :
8907 i == Ydrip_2_sB ? j + 8 :
8908 i == Xsand_stonein_2 ? j + 8 :
8909 i == Xsand_stonein_3 ? j + 16 :
8910 i == Xsand_stonein_4 ? j + 24 :
8911 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8912 int step = (is_backside ? step_frame : num_steps - step_frame);
8914 if (is_backside) // tile where movement starts
8916 if (dx < 0 || dy < 0)
8918 g_em->src_offset_x = cx * step;
8919 g_em->src_offset_y = cy * step;
8923 g_em->dst_offset_x = cx * step;
8924 g_em->dst_offset_y = cy * step;
8927 else // tile where movement ends
8929 if (dx < 0 || dy < 0)
8931 g_em->dst_offset_x = cx * step;
8932 g_em->dst_offset_y = cy * step;
8936 g_em->src_offset_x = cx * step;
8937 g_em->src_offset_y = cy * step;
8941 g_em->width = TILEX - cx * step;
8942 g_em->height = TILEY - cy * step;
8945 // create unique graphic identifier to decide if tile must be redrawn
8946 /* bit 31 - 16 (16 bit): EM style graphic
8947 bit 15 - 12 ( 4 bit): EM style frame
8948 bit 11 - 6 ( 6 bit): graphic width
8949 bit 5 - 0 ( 6 bit): graphic height */
8950 g_em->unique_identifier =
8951 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8955 for (i = 0; i < GAME_TILE_MAX; i++)
8957 for (j = 0; j < 8; j++)
8959 int element = object_mapping[i].element_rnd;
8960 int action = object_mapping[i].action;
8961 int direction = object_mapping[i].direction;
8962 boolean is_backside = object_mapping[i].is_backside;
8963 int graphic_action = el_act_dir2img(element, action, direction);
8964 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8966 if ((action == ACTION_SMASHED_BY_ROCK ||
8967 action == ACTION_SMASHED_BY_SPRING ||
8968 action == ACTION_EATING) &&
8969 graphic_action == graphic_default)
8971 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8972 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8973 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8974 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8977 // no separate animation for "smashed by rock" -- use rock instead
8978 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8979 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8981 g_em->bitmap = g_xx->bitmap;
8982 g_em->src_x = g_xx->src_x;
8983 g_em->src_y = g_xx->src_y;
8984 g_em->src_offset_x = g_xx->src_offset_x;
8985 g_em->src_offset_y = g_xx->src_offset_y;
8986 g_em->dst_offset_x = g_xx->dst_offset_x;
8987 g_em->dst_offset_y = g_xx->dst_offset_y;
8988 g_em->width = g_xx->width;
8989 g_em->height = g_xx->height;
8990 g_em->unique_identifier = g_xx->unique_identifier;
8993 g_em->preserve_background = TRUE;
8998 for (p = 0; p < MAX_PLAYERS; p++)
9000 for (i = 0; i < PLY_MAX; i++)
9002 int element = player_mapping[p][i].element_rnd;
9003 int action = player_mapping[p][i].action;
9004 int direction = player_mapping[p][i].direction;
9006 for (j = 0; j < 8; j++)
9008 int effective_element = element;
9009 int effective_action = action;
9010 int graphic = (direction == MV_NONE ?
9011 el_act2img(effective_element, effective_action) :
9012 el_act_dir2img(effective_element, effective_action,
9014 struct GraphicInfo *g = &graphic_info[graphic];
9015 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9019 int frame = getAnimationFrame(g->anim_frames,
9022 g->anim_start_frame,
9025 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9027 g_em->bitmap = src_bitmap;
9028 g_em->src_x = src_x;
9029 g_em->src_y = src_y;
9030 g_em->src_offset_x = 0;
9031 g_em->src_offset_y = 0;
9032 g_em->dst_offset_x = 0;
9033 g_em->dst_offset_y = 0;
9034 g_em->width = TILEX;
9035 g_em->height = TILEY;
9041 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9042 boolean any_player_moving,
9043 boolean any_player_snapping,
9044 boolean any_player_dropping)
9046 if (frame == 7 && !any_player_dropping)
9048 if (!local_player->was_waiting)
9050 if (!CheckSaveEngineSnapshotToList())
9053 local_player->was_waiting = TRUE;
9056 else if (any_player_moving || any_player_snapping || any_player_dropping)
9058 local_player->was_waiting = FALSE;
9062 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9063 boolean murphy_is_dropping)
9065 if (murphy_is_waiting)
9067 if (!local_player->was_waiting)
9069 if (!CheckSaveEngineSnapshotToList())
9072 local_player->was_waiting = TRUE;
9077 local_player->was_waiting = FALSE;
9081 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9082 boolean button_released)
9084 if (button_released)
9086 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9087 CheckSaveEngineSnapshotToList();
9089 else if (element_clicked)
9091 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9092 CheckSaveEngineSnapshotToList();
9094 game.snapshot.changed_action = TRUE;
9098 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9099 boolean any_player_moving,
9100 boolean any_player_snapping,
9101 boolean any_player_dropping)
9103 if (tape.single_step && tape.recording && !tape.pausing)
9104 if (frame == 7 && !any_player_dropping)
9105 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9107 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9108 any_player_snapping, any_player_dropping);
9110 return tape.pausing;
9113 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9114 boolean murphy_is_dropping)
9116 boolean murphy_starts_dropping = FALSE;
9119 for (i = 0; i < MAX_PLAYERS; i++)
9120 if (stored_player[i].force_dropping)
9121 murphy_starts_dropping = TRUE;
9123 if (tape.single_step && tape.recording && !tape.pausing)
9124 if (murphy_is_waiting && !murphy_starts_dropping)
9125 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9127 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9130 void CheckSingleStepMode_MM(boolean element_clicked,
9131 boolean button_released)
9133 if (tape.single_step && tape.recording && !tape.pausing)
9134 if (button_released)
9135 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9137 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9140 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9141 int graphic, int sync_frame, int x, int y)
9143 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9145 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9148 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9150 return (IS_NEXT_FRAME(sync_frame, graphic));
9153 int getGraphicInfo_Delay(int graphic)
9155 return graphic_info[graphic].anim_delay;
9158 void PlayMenuSoundExt(int sound)
9160 if (sound == SND_UNDEFINED)
9163 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9164 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9167 if (IS_LOOP_SOUND(sound))
9168 PlaySoundLoop(sound);
9173 void PlayMenuSound(void)
9175 PlayMenuSoundExt(menu.sound[game_status]);
9178 void PlayMenuSoundStereo(int sound, int stereo_position)
9180 if (sound == SND_UNDEFINED)
9183 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9184 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9187 if (IS_LOOP_SOUND(sound))
9188 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9190 PlaySoundStereo(sound, stereo_position);
9193 void PlayMenuSoundIfLoopExt(int sound)
9195 if (sound == SND_UNDEFINED)
9198 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9199 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9202 if (IS_LOOP_SOUND(sound))
9203 PlaySoundLoop(sound);
9206 void PlayMenuSoundIfLoop(void)
9208 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9211 void PlayMenuMusicExt(int music)
9213 if (music == MUS_UNDEFINED)
9216 if (!setup.sound_music)
9219 if (IS_LOOP_MUSIC(music))
9220 PlayMusicLoop(music);
9225 void PlayMenuMusic(void)
9227 char *curr_music = getCurrentlyPlayingMusicFilename();
9228 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9230 if (!strEqual(curr_music, next_music))
9231 PlayMenuMusicExt(menu.music[game_status]);
9234 void PlayMenuSoundsAndMusic(void)
9240 static void FadeMenuSounds(void)
9245 static void FadeMenuMusic(void)
9247 char *curr_music = getCurrentlyPlayingMusicFilename();
9248 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9250 if (!strEqual(curr_music, next_music))
9254 void FadeMenuSoundsAndMusic(void)
9260 void PlaySoundActivating(void)
9263 PlaySound(SND_MENU_ITEM_ACTIVATING);
9267 void PlaySoundSelecting(void)
9270 PlaySound(SND_MENU_ITEM_SELECTING);
9274 void ToggleFullscreenIfNeeded(void)
9276 // if setup and video fullscreen state are already matching, nothing do do
9277 if (setup.fullscreen == video.fullscreen_enabled ||
9278 !video.fullscreen_available)
9281 SDLSetWindowFullscreen(setup.fullscreen);
9283 // set setup value according to successfully changed fullscreen mode
9284 setup.fullscreen = video.fullscreen_enabled;
9287 void ChangeWindowScalingIfNeeded(void)
9289 // if setup and video window scaling are already matching, nothing do do
9290 if (setup.window_scaling_percent == video.window_scaling_percent ||
9291 video.fullscreen_enabled)
9294 SDLSetWindowScaling(setup.window_scaling_percent);
9296 // set setup value according to successfully changed window scaling
9297 setup.window_scaling_percent = video.window_scaling_percent;
9300 void ChangeVsyncModeIfNeeded(void)
9302 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9303 int video_vsync_mode = video.vsync_mode;
9305 // if setup and video vsync mode are already matching, nothing do do
9306 if (setup_vsync_mode == video_vsync_mode)
9309 // if renderer is using OpenGL, vsync mode can directly be changed
9310 SDLSetScreenVsyncMode(setup.vsync_mode);
9312 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9313 if (video.vsync_mode == video_vsync_mode)
9315 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9317 // save backbuffer content which gets lost when re-creating screen
9318 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9320 // force re-creating screen and renderer to set new vsync mode
9321 video.fullscreen_enabled = !setup.fullscreen;
9323 // when creating new renderer, destroy textures linked to old renderer
9324 FreeAllImageTextures(); // needs old renderer to free the textures
9326 // re-create screen and renderer (including change of vsync mode)
9327 ChangeVideoModeIfNeeded(setup.fullscreen);
9329 // set setup value according to successfully changed fullscreen mode
9330 setup.fullscreen = video.fullscreen_enabled;
9332 // restore backbuffer content from temporary backbuffer backup bitmap
9333 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9334 FreeBitmap(tmp_backbuffer);
9336 // update visible window/screen
9337 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9339 // when changing vsync mode, re-create textures for new renderer
9340 InitImageTextures();
9343 // set setup value according to successfully changed vsync mode
9344 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9347 static void JoinRectangles(int *x, int *y, int *width, int *height,
9348 int x2, int y2, int width2, int height2)
9350 // do not join with "off-screen" rectangle
9351 if (x2 == -1 || y2 == -1)
9356 *width = MAX(*width, width2);
9357 *height = MAX(*height, height2);
9360 void SetAnimStatus(int anim_status_new)
9362 if (anim_status_new == GAME_MODE_MAIN)
9363 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9364 else if (anim_status_new == GAME_MODE_NAMES)
9365 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9366 else if (anim_status_new == GAME_MODE_SCORES)
9367 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9369 global.anim_status_next = anim_status_new;
9371 // directly set screen modes that are entered without fading
9372 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9373 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9374 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9375 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9376 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9377 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9378 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9379 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9380 global.anim_status = global.anim_status_next;
9383 void SetGameStatus(int game_status_new)
9385 if (game_status_new != game_status)
9386 game_status_last_screen = game_status;
9388 game_status = game_status_new;
9390 SetAnimStatus(game_status_new);
9393 void SetFontStatus(int game_status_new)
9395 static int last_game_status = -1;
9397 if (game_status_new != -1)
9399 // set game status for font use after storing last game status
9400 last_game_status = game_status;
9401 game_status = game_status_new;
9405 // reset game status after font use from last stored game status
9406 game_status = last_game_status;
9410 void ResetFontStatus(void)
9415 void SetLevelSetInfo(char *identifier, int level_nr)
9417 setString(&levelset.identifier, identifier);
9419 levelset.level_nr = level_nr;
9422 boolean CheckIfAllViewportsHaveChanged(void)
9424 // if game status has not changed, viewports have not changed either
9425 if (game_status == game_status_last)
9428 // check if all viewports have changed with current game status
9430 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9431 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9432 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9433 int new_real_sx = vp_playfield->x;
9434 int new_real_sy = vp_playfield->y;
9435 int new_full_sxsize = vp_playfield->width;
9436 int new_full_sysize = vp_playfield->height;
9437 int new_dx = vp_door_1->x;
9438 int new_dy = vp_door_1->y;
9439 int new_dxsize = vp_door_1->width;
9440 int new_dysize = vp_door_1->height;
9441 int new_vx = vp_door_2->x;
9442 int new_vy = vp_door_2->y;
9443 int new_vxsize = vp_door_2->width;
9444 int new_vysize = vp_door_2->height;
9446 boolean playfield_viewport_has_changed =
9447 (new_real_sx != REAL_SX ||
9448 new_real_sy != REAL_SY ||
9449 new_full_sxsize != FULL_SXSIZE ||
9450 new_full_sysize != FULL_SYSIZE);
9452 boolean door_1_viewport_has_changed =
9455 new_dxsize != DXSIZE ||
9456 new_dysize != DYSIZE);
9458 boolean door_2_viewport_has_changed =
9461 new_vxsize != VXSIZE ||
9462 new_vysize != VYSIZE ||
9463 game_status_last == GAME_MODE_EDITOR);
9465 return (playfield_viewport_has_changed &&
9466 door_1_viewport_has_changed &&
9467 door_2_viewport_has_changed);
9470 boolean CheckFadeAll(void)
9472 return (CheckIfGlobalBorderHasChanged() ||
9473 CheckIfAllViewportsHaveChanged());
9476 void ChangeViewportPropertiesIfNeeded(void)
9478 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9479 FALSE : setup.small_game_graphics);
9480 int gfx_game_mode = game_status;
9481 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9483 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9484 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9485 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9486 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9487 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9488 int new_win_xsize = vp_window->width;
9489 int new_win_ysize = vp_window->height;
9490 int border_left = vp_playfield->border_left;
9491 int border_right = vp_playfield->border_right;
9492 int border_top = vp_playfield->border_top;
9493 int border_bottom = vp_playfield->border_bottom;
9494 int new_sx = vp_playfield->x + border_left;
9495 int new_sy = vp_playfield->y + border_top;
9496 int new_sxsize = vp_playfield->width - border_left - border_right;
9497 int new_sysize = vp_playfield->height - border_top - border_bottom;
9498 int new_real_sx = vp_playfield->x;
9499 int new_real_sy = vp_playfield->y;
9500 int new_full_sxsize = vp_playfield->width;
9501 int new_full_sysize = vp_playfield->height;
9502 int new_dx = vp_door_1->x;
9503 int new_dy = vp_door_1->y;
9504 int new_dxsize = vp_door_1->width;
9505 int new_dysize = vp_door_1->height;
9506 int new_vx = vp_door_2->x;
9507 int new_vy = vp_door_2->y;
9508 int new_vxsize = vp_door_2->width;
9509 int new_vysize = vp_door_2->height;
9510 int new_ex = vp_door_3->x;
9511 int new_ey = vp_door_3->y;
9512 int new_exsize = vp_door_3->width;
9513 int new_eysize = vp_door_3->height;
9514 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9515 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9516 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9517 int new_scr_fieldx = new_sxsize / tilesize;
9518 int new_scr_fieldy = new_sysize / tilesize;
9519 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9520 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9521 boolean init_gfx_buffers = FALSE;
9522 boolean init_video_buffer = FALSE;
9523 boolean init_gadgets_and_anims = FALSE;
9524 boolean init_em_graphics = FALSE;
9526 if (new_win_xsize != WIN_XSIZE ||
9527 new_win_ysize != WIN_YSIZE)
9529 WIN_XSIZE = new_win_xsize;
9530 WIN_YSIZE = new_win_ysize;
9532 init_video_buffer = TRUE;
9533 init_gfx_buffers = TRUE;
9534 init_gadgets_and_anims = TRUE;
9536 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9539 if (new_scr_fieldx != SCR_FIELDX ||
9540 new_scr_fieldy != SCR_FIELDY)
9542 // this always toggles between MAIN and GAME when using small tile size
9544 SCR_FIELDX = new_scr_fieldx;
9545 SCR_FIELDY = new_scr_fieldy;
9547 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9558 new_sxsize != SXSIZE ||
9559 new_sysize != SYSIZE ||
9560 new_dxsize != DXSIZE ||
9561 new_dysize != DYSIZE ||
9562 new_vxsize != VXSIZE ||
9563 new_vysize != VYSIZE ||
9564 new_exsize != EXSIZE ||
9565 new_eysize != EYSIZE ||
9566 new_real_sx != REAL_SX ||
9567 new_real_sy != REAL_SY ||
9568 new_full_sxsize != FULL_SXSIZE ||
9569 new_full_sysize != FULL_SYSIZE ||
9570 new_tilesize_var != TILESIZE_VAR
9573 // ------------------------------------------------------------------------
9574 // determine next fading area for changed viewport definitions
9575 // ------------------------------------------------------------------------
9577 // start with current playfield area (default fading area)
9580 FADE_SXSIZE = FULL_SXSIZE;
9581 FADE_SYSIZE = FULL_SYSIZE;
9583 // add new playfield area if position or size has changed
9584 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9585 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9587 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9588 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9591 // add current and new door 1 area if position or size has changed
9592 if (new_dx != DX || new_dy != DY ||
9593 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9595 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9596 DX, DY, DXSIZE, DYSIZE);
9597 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9598 new_dx, new_dy, new_dxsize, new_dysize);
9601 // add current and new door 2 area if position or size has changed
9602 if (new_vx != VX || new_vy != VY ||
9603 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9605 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9606 VX, VY, VXSIZE, VYSIZE);
9607 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9608 new_vx, new_vy, new_vxsize, new_vysize);
9611 // ------------------------------------------------------------------------
9612 // handle changed tile size
9613 // ------------------------------------------------------------------------
9615 if (new_tilesize_var != TILESIZE_VAR)
9617 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9619 // changing tile size invalidates scroll values of engine snapshots
9620 FreeEngineSnapshotSingle();
9622 // changing tile size requires update of graphic mapping for EM engine
9623 init_em_graphics = TRUE;
9634 SXSIZE = new_sxsize;
9635 SYSIZE = new_sysize;
9636 DXSIZE = new_dxsize;
9637 DYSIZE = new_dysize;
9638 VXSIZE = new_vxsize;
9639 VYSIZE = new_vysize;
9640 EXSIZE = new_exsize;
9641 EYSIZE = new_eysize;
9642 REAL_SX = new_real_sx;
9643 REAL_SY = new_real_sy;
9644 FULL_SXSIZE = new_full_sxsize;
9645 FULL_SYSIZE = new_full_sysize;
9646 TILESIZE_VAR = new_tilesize_var;
9648 init_gfx_buffers = TRUE;
9649 init_gadgets_and_anims = TRUE;
9651 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9652 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9655 if (init_gfx_buffers)
9657 // Debug("tools:viewport", "init_gfx_buffers");
9659 SCR_FIELDX = new_scr_fieldx_buffers;
9660 SCR_FIELDY = new_scr_fieldy_buffers;
9664 SCR_FIELDX = new_scr_fieldx;
9665 SCR_FIELDY = new_scr_fieldy;
9667 SetDrawDeactivationMask(REDRAW_NONE);
9668 SetDrawBackgroundMask(REDRAW_FIELD);
9671 if (init_video_buffer)
9673 // Debug("tools:viewport", "init_video_buffer");
9675 FreeAllImageTextures(); // needs old renderer to free the textures
9677 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9678 InitImageTextures();
9681 if (init_gadgets_and_anims)
9683 // Debug("tools:viewport", "init_gadgets_and_anims");
9686 InitGlobalAnimations();
9689 if (init_em_graphics)
9691 InitGraphicInfo_EM();