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 SetOverlayEnabled(FALSE);
4809 if (global.use_envelope_request)
4810 result = RequestEnvelope(text, req_state);
4812 result = RequestDoor(text, req_state);
4814 SetOverlayEnabled(overlay_enabled);
4819 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4821 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4822 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4825 if (dpo1->sort_priority != dpo2->sort_priority)
4826 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4828 compare_result = dpo1->nr - dpo2->nr;
4830 return compare_result;
4833 void InitGraphicCompatibilityInfo_Doors(void)
4839 struct DoorInfo *door;
4843 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4844 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4846 { -1, -1, -1, NULL }
4848 struct Rect door_rect_list[] =
4850 { DX, DY, DXSIZE, DYSIZE },
4851 { VX, VY, VXSIZE, VYSIZE }
4855 for (i = 0; doors[i].door_token != -1; i++)
4857 int door_token = doors[i].door_token;
4858 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4859 int part_1 = doors[i].part_1;
4860 int part_8 = doors[i].part_8;
4861 int part_2 = part_1 + 1;
4862 int part_3 = part_1 + 2;
4863 struct DoorInfo *door = doors[i].door;
4864 struct Rect *door_rect = &door_rect_list[door_index];
4865 boolean door_gfx_redefined = FALSE;
4867 // check if any door part graphic definitions have been redefined
4869 for (j = 0; door_part_controls[j].door_token != -1; j++)
4871 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4872 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4874 if (dpc->door_token == door_token && fi->redefined)
4875 door_gfx_redefined = TRUE;
4878 // check for old-style door graphic/animation modifications
4880 if (!door_gfx_redefined)
4882 if (door->anim_mode & ANIM_STATIC_PANEL)
4884 door->panel.step_xoffset = 0;
4885 door->panel.step_yoffset = 0;
4888 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4890 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4891 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4892 int num_door_steps, num_panel_steps;
4894 // remove door part graphics other than the two default wings
4896 for (j = 0; door_part_controls[j].door_token != -1; j++)
4898 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4899 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4901 if (dpc->graphic >= part_3 &&
4902 dpc->graphic <= part_8)
4906 // set graphics and screen positions of the default wings
4908 g_part_1->width = door_rect->width;
4909 g_part_1->height = door_rect->height;
4910 g_part_2->width = door_rect->width;
4911 g_part_2->height = door_rect->height;
4912 g_part_2->src_x = door_rect->width;
4913 g_part_2->src_y = g_part_1->src_y;
4915 door->part_2.x = door->part_1.x;
4916 door->part_2.y = door->part_1.y;
4918 if (door->width != -1)
4920 g_part_1->width = door->width;
4921 g_part_2->width = door->width;
4923 // special treatment for graphics and screen position of right wing
4924 g_part_2->src_x += door_rect->width - door->width;
4925 door->part_2.x += door_rect->width - door->width;
4928 if (door->height != -1)
4930 g_part_1->height = door->height;
4931 g_part_2->height = door->height;
4933 // special treatment for graphics and screen position of bottom wing
4934 g_part_2->src_y += door_rect->height - door->height;
4935 door->part_2.y += door_rect->height - door->height;
4938 // set animation delays for the default wings and panels
4940 door->part_1.step_delay = door->step_delay;
4941 door->part_2.step_delay = door->step_delay;
4942 door->panel.step_delay = door->step_delay;
4944 // set animation draw order for the default wings
4946 door->part_1.sort_priority = 2; // draw left wing over ...
4947 door->part_2.sort_priority = 1; // ... right wing
4949 // set animation draw offset for the default wings
4951 if (door->anim_mode & ANIM_HORIZONTAL)
4953 door->part_1.step_xoffset = door->step_offset;
4954 door->part_1.step_yoffset = 0;
4955 door->part_2.step_xoffset = door->step_offset * -1;
4956 door->part_2.step_yoffset = 0;
4958 num_door_steps = g_part_1->width / door->step_offset;
4960 else // ANIM_VERTICAL
4962 door->part_1.step_xoffset = 0;
4963 door->part_1.step_yoffset = door->step_offset;
4964 door->part_2.step_xoffset = 0;
4965 door->part_2.step_yoffset = door->step_offset * -1;
4967 num_door_steps = g_part_1->height / door->step_offset;
4970 // set animation draw offset for the default panels
4972 if (door->step_offset > 1)
4974 num_panel_steps = 2 * door_rect->height / door->step_offset;
4975 door->panel.start_step = num_panel_steps - num_door_steps;
4976 door->panel.start_step_closing = door->panel.start_step;
4980 num_panel_steps = door_rect->height / door->step_offset;
4981 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4982 door->panel.start_step_closing = door->panel.start_step;
4983 door->panel.step_delay *= 2;
4990 void InitDoors(void)
4994 for (i = 0; door_part_controls[i].door_token != -1; i++)
4996 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4997 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4999 // initialize "start_step_opening" and "start_step_closing", if needed
5000 if (dpc->pos->start_step_opening == 0 &&
5001 dpc->pos->start_step_closing == 0)
5003 // dpc->pos->start_step_opening = dpc->pos->start_step;
5004 dpc->pos->start_step_closing = dpc->pos->start_step;
5007 // fill structure for door part draw order (sorted below)
5009 dpo->sort_priority = dpc->pos->sort_priority;
5012 // sort door part controls according to sort_priority and graphic number
5013 qsort(door_part_order, MAX_DOOR_PARTS,
5014 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5017 unsigned int OpenDoor(unsigned int door_state)
5019 if (door_state & DOOR_COPY_BACK)
5021 if (door_state & DOOR_OPEN_1)
5022 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5023 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5025 if (door_state & DOOR_OPEN_2)
5026 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5027 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5029 door_state &= ~DOOR_COPY_BACK;
5032 return MoveDoor(door_state);
5035 unsigned int CloseDoor(unsigned int door_state)
5037 unsigned int old_door_state = GetDoorState();
5039 if (!(door_state & DOOR_NO_COPY_BACK))
5041 if (old_door_state & DOOR_OPEN_1)
5042 BlitBitmap(backbuffer, bitmap_db_door_1,
5043 DX, DY, DXSIZE, DYSIZE, 0, 0);
5045 if (old_door_state & DOOR_OPEN_2)
5046 BlitBitmap(backbuffer, bitmap_db_door_2,
5047 VX, VY, VXSIZE, VYSIZE, 0, 0);
5049 door_state &= ~DOOR_NO_COPY_BACK;
5052 return MoveDoor(door_state);
5055 unsigned int GetDoorState(void)
5057 return MoveDoor(DOOR_GET_STATE);
5060 unsigned int SetDoorState(unsigned int door_state)
5062 return MoveDoor(door_state | DOOR_SET_STATE);
5065 static int euclid(int a, int b)
5067 return (b ? euclid(b, a % b) : a);
5070 unsigned int MoveDoor(unsigned int door_state)
5072 struct Rect door_rect_list[] =
5074 { DX, DY, DXSIZE, DYSIZE },
5075 { VX, VY, VXSIZE, VYSIZE }
5077 static int door1 = DOOR_CLOSE_1;
5078 static int door2 = DOOR_CLOSE_2;
5079 unsigned int door_delay = 0;
5080 unsigned int door_delay_value;
5083 if (door_state == DOOR_GET_STATE)
5084 return (door1 | door2);
5086 if (door_state & DOOR_SET_STATE)
5088 if (door_state & DOOR_ACTION_1)
5089 door1 = door_state & DOOR_ACTION_1;
5090 if (door_state & DOOR_ACTION_2)
5091 door2 = door_state & DOOR_ACTION_2;
5093 return (door1 | door2);
5096 if (!(door_state & DOOR_FORCE_REDRAW))
5098 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5099 door_state &= ~DOOR_OPEN_1;
5100 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5101 door_state &= ~DOOR_CLOSE_1;
5102 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5103 door_state &= ~DOOR_OPEN_2;
5104 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5105 door_state &= ~DOOR_CLOSE_2;
5108 if (global.autoplay_leveldir)
5110 door_state |= DOOR_NO_DELAY;
5111 door_state &= ~DOOR_CLOSE_ALL;
5114 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5115 door_state |= DOOR_NO_DELAY;
5117 if (door_state & DOOR_ACTION)
5119 boolean door_panel_drawn[NUM_DOORS];
5120 boolean panel_has_doors[NUM_DOORS];
5121 boolean door_part_skip[MAX_DOOR_PARTS];
5122 boolean door_part_done[MAX_DOOR_PARTS];
5123 boolean door_part_done_all;
5124 int num_steps[MAX_DOOR_PARTS];
5125 int max_move_delay = 0; // delay for complete animations of all doors
5126 int max_step_delay = 0; // delay (ms) between two animation frames
5127 int num_move_steps = 0; // number of animation steps for all doors
5128 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5129 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5130 int current_move_delay = 0;
5134 for (i = 0; i < NUM_DOORS; i++)
5135 panel_has_doors[i] = FALSE;
5137 for (i = 0; i < MAX_DOOR_PARTS; i++)
5139 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5140 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5141 int door_token = dpc->door_token;
5143 door_part_done[i] = FALSE;
5144 door_part_skip[i] = (!(door_state & door_token) ||
5148 for (i = 0; i < MAX_DOOR_PARTS; i++)
5150 int nr = door_part_order[i].nr;
5151 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5152 struct DoorPartPosInfo *pos = dpc->pos;
5153 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5154 int door_token = dpc->door_token;
5155 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5156 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5157 int step_xoffset = ABS(pos->step_xoffset);
5158 int step_yoffset = ABS(pos->step_yoffset);
5159 int step_delay = pos->step_delay;
5160 int current_door_state = door_state & door_token;
5161 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5162 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5163 boolean part_opening = (is_panel ? door_closing : door_opening);
5164 int start_step = (part_opening ? pos->start_step_opening :
5165 pos->start_step_closing);
5166 float move_xsize = (step_xoffset ? g->width : 0);
5167 float move_ysize = (step_yoffset ? g->height : 0);
5168 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5169 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5170 int move_steps = (move_xsteps && move_ysteps ?
5171 MIN(move_xsteps, move_ysteps) :
5172 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5173 int move_delay = move_steps * step_delay;
5175 if (door_part_skip[nr])
5178 max_move_delay = MAX(max_move_delay, move_delay);
5179 max_step_delay = (max_step_delay == 0 ? step_delay :
5180 euclid(max_step_delay, step_delay));
5181 num_steps[nr] = move_steps;
5185 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5187 panel_has_doors[door_index] = TRUE;
5191 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5193 num_move_steps = max_move_delay / max_step_delay;
5194 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5196 door_delay_value = max_step_delay;
5198 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5200 start = num_move_steps - 1;
5204 // opening door sound has priority over simultaneously closing door
5205 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5207 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5209 if (door_state & DOOR_OPEN_1)
5210 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5211 if (door_state & DOOR_OPEN_2)
5212 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5214 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5216 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5218 if (door_state & DOOR_CLOSE_1)
5219 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5220 if (door_state & DOOR_CLOSE_2)
5221 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5225 for (k = start; k < num_move_steps; k++)
5227 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5229 door_part_done_all = TRUE;
5231 for (i = 0; i < NUM_DOORS; i++)
5232 door_panel_drawn[i] = FALSE;
5234 for (i = 0; i < MAX_DOOR_PARTS; i++)
5236 int nr = door_part_order[i].nr;
5237 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5238 struct DoorPartPosInfo *pos = dpc->pos;
5239 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5240 int door_token = dpc->door_token;
5241 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5242 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5243 boolean is_panel_and_door_has_closed = FALSE;
5244 struct Rect *door_rect = &door_rect_list[door_index];
5245 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5247 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5248 int current_door_state = door_state & door_token;
5249 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5250 boolean door_closing = !door_opening;
5251 boolean part_opening = (is_panel ? door_closing : door_opening);
5252 boolean part_closing = !part_opening;
5253 int start_step = (part_opening ? pos->start_step_opening :
5254 pos->start_step_closing);
5255 int step_delay = pos->step_delay;
5256 int step_factor = step_delay / max_step_delay;
5257 int k1 = (step_factor ? k / step_factor + 1 : k);
5258 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5259 int kk = MAX(0, k2);
5262 int src_x, src_y, src_xx, src_yy;
5263 int dst_x, dst_y, dst_xx, dst_yy;
5266 if (door_part_skip[nr])
5269 if (!(door_state & door_token))
5277 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5278 int kk_door = MAX(0, k2_door);
5279 int sync_frame = kk_door * door_delay_value;
5280 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5282 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5283 &g_src_x, &g_src_y);
5288 if (!door_panel_drawn[door_index])
5290 ClearRectangle(drawto, door_rect->x, door_rect->y,
5291 door_rect->width, door_rect->height);
5293 door_panel_drawn[door_index] = TRUE;
5296 // draw opening or closing door parts
5298 if (pos->step_xoffset < 0) // door part on right side
5301 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5304 if (dst_xx + width > door_rect->width)
5305 width = door_rect->width - dst_xx;
5307 else // door part on left side
5310 dst_xx = pos->x - kk * pos->step_xoffset;
5314 src_xx = ABS(dst_xx);
5318 width = g->width - src_xx;
5320 if (width > door_rect->width)
5321 width = door_rect->width;
5323 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5326 if (pos->step_yoffset < 0) // door part on bottom side
5329 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5332 if (dst_yy + height > door_rect->height)
5333 height = door_rect->height - dst_yy;
5335 else // door part on top side
5338 dst_yy = pos->y - kk * pos->step_yoffset;
5342 src_yy = ABS(dst_yy);
5346 height = g->height - src_yy;
5349 src_x = g_src_x + src_xx;
5350 src_y = g_src_y + src_yy;
5352 dst_x = door_rect->x + dst_xx;
5353 dst_y = door_rect->y + dst_yy;
5355 is_panel_and_door_has_closed =
5358 panel_has_doors[door_index] &&
5359 k >= num_move_steps_doors_only - 1);
5361 if (width >= 0 && width <= g->width &&
5362 height >= 0 && height <= g->height &&
5363 !is_panel_and_door_has_closed)
5365 if (is_panel || !pos->draw_masked)
5366 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5369 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5373 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5375 if ((part_opening && (width < 0 || height < 0)) ||
5376 (part_closing && (width >= g->width && height >= g->height)))
5377 door_part_done[nr] = TRUE;
5379 // continue door part animations, but not panel after door has closed
5380 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5381 door_part_done_all = FALSE;
5384 if (!(door_state & DOOR_NO_DELAY))
5388 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5390 current_move_delay += max_step_delay;
5392 // prevent OS (Windows) from complaining about program not responding
5396 if (door_part_done_all)
5400 if (!(door_state & DOOR_NO_DELAY))
5402 // wait for specified door action post delay
5403 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5404 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5405 else if (door_state & DOOR_ACTION_1)
5406 door_delay_value = door_1.post_delay;
5407 else if (door_state & DOOR_ACTION_2)
5408 door_delay_value = door_2.post_delay;
5410 while (!DelayReached(&door_delay, door_delay_value))
5415 if (door_state & DOOR_ACTION_1)
5416 door1 = door_state & DOOR_ACTION_1;
5417 if (door_state & DOOR_ACTION_2)
5418 door2 = door_state & DOOR_ACTION_2;
5420 // draw masked border over door area
5421 DrawMaskedBorder(REDRAW_DOOR_1);
5422 DrawMaskedBorder(REDRAW_DOOR_2);
5424 ClearAutoRepeatKeyEvents();
5426 return (door1 | door2);
5429 static boolean useSpecialEditorDoor(void)
5431 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5432 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5434 // do not draw special editor door if editor border defined or redefined
5435 if (graphic_info[graphic].bitmap != NULL || redefined)
5438 // do not draw special editor door if global border defined to be empty
5439 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5442 // do not draw special editor door if viewport definitions do not match
5446 EY + EYSIZE != VY + VYSIZE)
5452 void DrawSpecialEditorDoor(void)
5454 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5455 int top_border_width = gfx1->width;
5456 int top_border_height = gfx1->height;
5457 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5458 int ex = EX - outer_border;
5459 int ey = EY - outer_border;
5460 int vy = VY - outer_border;
5461 int exsize = EXSIZE + 2 * outer_border;
5463 if (!useSpecialEditorDoor())
5466 // draw bigger level editor toolbox window
5467 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5468 top_border_width, top_border_height, ex, ey - top_border_height);
5469 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5470 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5472 redraw_mask |= REDRAW_ALL;
5475 void UndrawSpecialEditorDoor(void)
5477 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5478 int top_border_width = gfx1->width;
5479 int top_border_height = gfx1->height;
5480 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5481 int ex = EX - outer_border;
5482 int ey = EY - outer_border;
5483 int ey_top = ey - top_border_height;
5484 int exsize = EXSIZE + 2 * outer_border;
5485 int eysize = EYSIZE + 2 * outer_border;
5487 if (!useSpecialEditorDoor())
5490 // draw normal tape recorder window
5491 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5493 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5494 ex, ey_top, top_border_width, top_border_height,
5496 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5497 ex, ey, exsize, eysize, ex, ey);
5501 // if screen background is set to "[NONE]", clear editor toolbox window
5502 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5503 ClearRectangle(drawto, ex, ey, exsize, eysize);
5506 redraw_mask |= REDRAW_ALL;
5510 // ---------- new tool button stuff -------------------------------------------
5515 struct TextPosInfo *pos;
5517 boolean is_touch_button;
5519 } toolbutton_info[NUM_TOOL_BUTTONS] =
5522 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5523 TOOL_CTRL_ID_YES, FALSE, "yes"
5526 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5527 TOOL_CTRL_ID_NO, FALSE, "no"
5530 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5531 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5534 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5535 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5538 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5539 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5542 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5543 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5546 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5547 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5550 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5551 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5554 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5555 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5558 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5559 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5563 void CreateToolButtons(void)
5567 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5569 int graphic = toolbutton_info[i].graphic;
5570 struct GraphicInfo *gfx = &graphic_info[graphic];
5571 struct TextPosInfo *pos = toolbutton_info[i].pos;
5572 struct GadgetInfo *gi;
5573 Bitmap *deco_bitmap = None;
5574 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5575 unsigned int event_mask = GD_EVENT_RELEASED;
5576 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5577 int base_x = (is_touch_button ? 0 : DX);
5578 int base_y = (is_touch_button ? 0 : DY);
5579 int gd_x = gfx->src_x;
5580 int gd_y = gfx->src_y;
5581 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5582 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5587 if (global.use_envelope_request && !is_touch_button)
5589 setRequestPosition(&base_x, &base_y, TRUE);
5591 // check if request buttons are outside of envelope and fix, if needed
5592 if (x < 0 || x + gfx->width > request.width ||
5593 y < 0 || y + gfx->height > request.height)
5595 if (id == TOOL_CTRL_ID_YES)
5598 y = request.height - 2 * request.border_size - gfx->height;
5600 else if (id == TOOL_CTRL_ID_NO)
5602 x = request.width - 2 * request.border_size - gfx->width;
5603 y = request.height - 2 * request.border_size - gfx->height;
5605 else if (id == TOOL_CTRL_ID_CONFIRM)
5607 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5608 y = request.height - 2 * request.border_size - gfx->height;
5610 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5612 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5614 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5615 y = request.height - 2 * request.border_size - gfx->height * 2;
5617 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5618 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5623 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5625 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5627 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5628 pos->size, &deco_bitmap, &deco_x, &deco_y);
5629 deco_xpos = (gfx->width - pos->size) / 2;
5630 deco_ypos = (gfx->height - pos->size) / 2;
5633 gi = CreateGadget(GDI_CUSTOM_ID, id,
5634 GDI_IMAGE_ID, graphic,
5635 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5638 GDI_WIDTH, gfx->width,
5639 GDI_HEIGHT, gfx->height,
5640 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5641 GDI_STATE, GD_BUTTON_UNPRESSED,
5642 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5643 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5644 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5645 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5646 GDI_DECORATION_SIZE, pos->size, pos->size,
5647 GDI_DECORATION_SHIFTING, 1, 1,
5648 GDI_DIRECT_DRAW, FALSE,
5649 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5650 GDI_EVENT_MASK, event_mask,
5651 GDI_CALLBACK_ACTION, HandleToolButtons,
5655 Fail("cannot create gadget");
5657 tool_gadget[id] = gi;
5661 void FreeToolButtons(void)
5665 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5666 FreeGadget(tool_gadget[i]);
5669 static void UnmapToolButtons(void)
5673 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5674 UnmapGadget(tool_gadget[i]);
5677 static void HandleToolButtons(struct GadgetInfo *gi)
5679 request_gadget_id = gi->custom_id;
5682 static struct Mapping_EM_to_RND_object
5685 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5686 boolean is_backside; // backside of moving element
5692 em_object_mapping_list[GAME_TILE_MAX + 1] =
5695 Zborder, FALSE, FALSE,
5699 Zplayer, FALSE, FALSE,
5708 Ztank, FALSE, FALSE,
5712 Zeater, FALSE, FALSE,
5716 Zdynamite, FALSE, FALSE,
5720 Zboom, FALSE, FALSE,
5725 Xchain, FALSE, FALSE,
5726 EL_DEFAULT, ACTION_EXPLODING, -1
5729 Xboom_bug, FALSE, FALSE,
5730 EL_BUG, ACTION_EXPLODING, -1
5733 Xboom_tank, FALSE, FALSE,
5734 EL_SPACESHIP, ACTION_EXPLODING, -1
5737 Xboom_android, FALSE, FALSE,
5738 EL_EMC_ANDROID, ACTION_OTHER, -1
5741 Xboom_1, FALSE, FALSE,
5742 EL_DEFAULT, ACTION_EXPLODING, -1
5745 Xboom_2, FALSE, FALSE,
5746 EL_DEFAULT, ACTION_EXPLODING, -1
5750 Xblank, TRUE, FALSE,
5755 Xsplash_e, FALSE, FALSE,
5756 EL_ACID_SPLASH_RIGHT, -1, -1
5759 Xsplash_w, FALSE, FALSE,
5760 EL_ACID_SPLASH_LEFT, -1, -1
5764 Xplant, TRUE, FALSE,
5765 EL_EMC_PLANT, -1, -1
5768 Yplant, FALSE, FALSE,
5769 EL_EMC_PLANT, -1, -1
5773 Xacid_1, TRUE, FALSE,
5777 Xacid_2, FALSE, FALSE,
5781 Xacid_3, FALSE, FALSE,
5785 Xacid_4, FALSE, FALSE,
5789 Xacid_5, FALSE, FALSE,
5793 Xacid_6, FALSE, FALSE,
5797 Xacid_7, FALSE, FALSE,
5801 Xacid_8, FALSE, FALSE,
5806 Xfake_acid_1, TRUE, FALSE,
5807 EL_EMC_FAKE_ACID, -1, -1
5810 Xfake_acid_2, FALSE, FALSE,
5811 EL_EMC_FAKE_ACID, -1, -1
5814 Xfake_acid_3, FALSE, FALSE,
5815 EL_EMC_FAKE_ACID, -1, -1
5818 Xfake_acid_4, FALSE, FALSE,
5819 EL_EMC_FAKE_ACID, -1, -1
5822 Xfake_acid_5, FALSE, FALSE,
5823 EL_EMC_FAKE_ACID, -1, -1
5826 Xfake_acid_6, FALSE, FALSE,
5827 EL_EMC_FAKE_ACID, -1, -1
5830 Xfake_acid_7, FALSE, FALSE,
5831 EL_EMC_FAKE_ACID, -1, -1
5834 Xfake_acid_8, FALSE, FALSE,
5835 EL_EMC_FAKE_ACID, -1, -1
5839 Xfake_acid_1_player, FALSE, FALSE,
5840 EL_EMC_FAKE_ACID, -1, -1
5843 Xfake_acid_2_player, FALSE, FALSE,
5844 EL_EMC_FAKE_ACID, -1, -1
5847 Xfake_acid_3_player, FALSE, FALSE,
5848 EL_EMC_FAKE_ACID, -1, -1
5851 Xfake_acid_4_player, FALSE, FALSE,
5852 EL_EMC_FAKE_ACID, -1, -1
5855 Xfake_acid_5_player, FALSE, FALSE,
5856 EL_EMC_FAKE_ACID, -1, -1
5859 Xfake_acid_6_player, FALSE, FALSE,
5860 EL_EMC_FAKE_ACID, -1, -1
5863 Xfake_acid_7_player, FALSE, FALSE,
5864 EL_EMC_FAKE_ACID, -1, -1
5867 Xfake_acid_8_player, FALSE, FALSE,
5868 EL_EMC_FAKE_ACID, -1, -1
5872 Xgrass, TRUE, FALSE,
5873 EL_EMC_GRASS, -1, -1
5876 Ygrass_nB, FALSE, FALSE,
5877 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5880 Ygrass_eB, FALSE, FALSE,
5881 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5884 Ygrass_sB, FALSE, FALSE,
5885 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5888 Ygrass_wB, FALSE, FALSE,
5889 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5897 Ydirt_nB, FALSE, FALSE,
5898 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5901 Ydirt_eB, FALSE, FALSE,
5902 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5905 Ydirt_sB, FALSE, FALSE,
5906 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5909 Ydirt_wB, FALSE, FALSE,
5910 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5914 Xandroid, TRUE, FALSE,
5915 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5918 Xandroid_1_n, FALSE, FALSE,
5919 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5922 Xandroid_2_n, FALSE, FALSE,
5923 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5926 Xandroid_1_e, FALSE, FALSE,
5927 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5930 Xandroid_2_e, FALSE, FALSE,
5931 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5934 Xandroid_1_w, FALSE, FALSE,
5935 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5938 Xandroid_2_w, FALSE, FALSE,
5939 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5942 Xandroid_1_s, FALSE, FALSE,
5943 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5946 Xandroid_2_s, FALSE, FALSE,
5947 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5950 Yandroid_n, FALSE, FALSE,
5951 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5954 Yandroid_nB, FALSE, TRUE,
5955 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5958 Yandroid_ne, FALSE, FALSE,
5959 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5962 Yandroid_neB, FALSE, TRUE,
5963 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5966 Yandroid_e, FALSE, FALSE,
5967 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5970 Yandroid_eB, FALSE, TRUE,
5971 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5974 Yandroid_se, FALSE, FALSE,
5975 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5978 Yandroid_seB, FALSE, TRUE,
5979 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5982 Yandroid_s, FALSE, FALSE,
5983 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5986 Yandroid_sB, FALSE, TRUE,
5987 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5990 Yandroid_sw, FALSE, FALSE,
5991 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5994 Yandroid_swB, FALSE, TRUE,
5995 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5998 Yandroid_w, FALSE, FALSE,
5999 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6002 Yandroid_wB, FALSE, TRUE,
6003 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6006 Yandroid_nw, FALSE, FALSE,
6007 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6010 Yandroid_nwB, FALSE, TRUE,
6011 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6015 Xeater_n, TRUE, FALSE,
6016 EL_YAMYAM_UP, -1, -1
6019 Xeater_e, TRUE, FALSE,
6020 EL_YAMYAM_RIGHT, -1, -1
6023 Xeater_w, TRUE, FALSE,
6024 EL_YAMYAM_LEFT, -1, -1
6027 Xeater_s, TRUE, FALSE,
6028 EL_YAMYAM_DOWN, -1, -1
6031 Yeater_n, FALSE, FALSE,
6032 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6035 Yeater_nB, FALSE, TRUE,
6036 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6039 Yeater_e, FALSE, FALSE,
6040 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6043 Yeater_eB, FALSE, TRUE,
6044 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6047 Yeater_s, FALSE, FALSE,
6048 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6051 Yeater_sB, FALSE, TRUE,
6052 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6055 Yeater_w, FALSE, FALSE,
6056 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6059 Yeater_wB, FALSE, TRUE,
6060 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6063 Yeater_stone, FALSE, FALSE,
6064 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6067 Yeater_spring, FALSE, FALSE,
6068 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6072 Xalien, TRUE, FALSE,
6076 Xalien_pause, FALSE, FALSE,
6080 Yalien_n, FALSE, FALSE,
6081 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6084 Yalien_nB, FALSE, TRUE,
6085 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6088 Yalien_e, FALSE, FALSE,
6089 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6092 Yalien_eB, FALSE, TRUE,
6093 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6096 Yalien_s, FALSE, FALSE,
6097 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6100 Yalien_sB, FALSE, TRUE,
6101 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6104 Yalien_w, FALSE, FALSE,
6105 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6108 Yalien_wB, FALSE, TRUE,
6109 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6112 Yalien_stone, FALSE, FALSE,
6113 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6116 Yalien_spring, FALSE, FALSE,
6117 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6121 Xbug_1_n, TRUE, FALSE,
6125 Xbug_1_e, TRUE, FALSE,
6126 EL_BUG_RIGHT, -1, -1
6129 Xbug_1_s, TRUE, FALSE,
6133 Xbug_1_w, TRUE, FALSE,
6137 Xbug_2_n, FALSE, FALSE,
6141 Xbug_2_e, FALSE, FALSE,
6142 EL_BUG_RIGHT, -1, -1
6145 Xbug_2_s, FALSE, FALSE,
6149 Xbug_2_w, FALSE, FALSE,
6153 Ybug_n, FALSE, FALSE,
6154 EL_BUG, ACTION_MOVING, MV_BIT_UP
6157 Ybug_nB, FALSE, TRUE,
6158 EL_BUG, ACTION_MOVING, MV_BIT_UP
6161 Ybug_e, FALSE, FALSE,
6162 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6165 Ybug_eB, FALSE, TRUE,
6166 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6169 Ybug_s, FALSE, FALSE,
6170 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6173 Ybug_sB, FALSE, TRUE,
6174 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6177 Ybug_w, FALSE, FALSE,
6178 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6181 Ybug_wB, FALSE, TRUE,
6182 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6185 Ybug_w_n, FALSE, FALSE,
6186 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6189 Ybug_n_e, FALSE, FALSE,
6190 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6193 Ybug_e_s, FALSE, FALSE,
6194 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6197 Ybug_s_w, FALSE, FALSE,
6198 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6201 Ybug_e_n, FALSE, FALSE,
6202 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6205 Ybug_s_e, FALSE, FALSE,
6206 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6209 Ybug_w_s, FALSE, FALSE,
6210 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6213 Ybug_n_w, FALSE, FALSE,
6214 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6217 Ybug_stone, FALSE, FALSE,
6218 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6221 Ybug_spring, FALSE, FALSE,
6222 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6226 Xtank_1_n, TRUE, FALSE,
6227 EL_SPACESHIP_UP, -1, -1
6230 Xtank_1_e, TRUE, FALSE,
6231 EL_SPACESHIP_RIGHT, -1, -1
6234 Xtank_1_s, TRUE, FALSE,
6235 EL_SPACESHIP_DOWN, -1, -1
6238 Xtank_1_w, TRUE, FALSE,
6239 EL_SPACESHIP_LEFT, -1, -1
6242 Xtank_2_n, FALSE, FALSE,
6243 EL_SPACESHIP_UP, -1, -1
6246 Xtank_2_e, FALSE, FALSE,
6247 EL_SPACESHIP_RIGHT, -1, -1
6250 Xtank_2_s, FALSE, FALSE,
6251 EL_SPACESHIP_DOWN, -1, -1
6254 Xtank_2_w, FALSE, FALSE,
6255 EL_SPACESHIP_LEFT, -1, -1
6258 Ytank_n, FALSE, FALSE,
6259 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6262 Ytank_nB, FALSE, TRUE,
6263 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6266 Ytank_e, FALSE, FALSE,
6267 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6270 Ytank_eB, FALSE, TRUE,
6271 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6274 Ytank_s, FALSE, FALSE,
6275 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6278 Ytank_sB, FALSE, TRUE,
6279 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6282 Ytank_w, FALSE, FALSE,
6283 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6286 Ytank_wB, FALSE, TRUE,
6287 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6290 Ytank_w_n, FALSE, FALSE,
6291 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6294 Ytank_n_e, FALSE, FALSE,
6295 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6298 Ytank_e_s, FALSE, FALSE,
6299 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6302 Ytank_s_w, FALSE, FALSE,
6303 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6306 Ytank_e_n, FALSE, FALSE,
6307 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6310 Ytank_s_e, FALSE, FALSE,
6311 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6314 Ytank_w_s, FALSE, FALSE,
6315 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6318 Ytank_n_w, FALSE, FALSE,
6319 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6322 Ytank_stone, FALSE, FALSE,
6323 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6326 Ytank_spring, FALSE, FALSE,
6327 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6331 Xemerald, TRUE, FALSE,
6335 Xemerald_pause, FALSE, FALSE,
6339 Xemerald_fall, FALSE, FALSE,
6343 Xemerald_shine, FALSE, FALSE,
6344 EL_EMERALD, ACTION_TWINKLING, -1
6347 Yemerald_s, FALSE, FALSE,
6348 EL_EMERALD, ACTION_FALLING, -1
6351 Yemerald_sB, FALSE, TRUE,
6352 EL_EMERALD, ACTION_FALLING, -1
6355 Yemerald_e, FALSE, FALSE,
6356 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6359 Yemerald_eB, FALSE, TRUE,
6360 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6363 Yemerald_w, FALSE, FALSE,
6364 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6367 Yemerald_wB, FALSE, TRUE,
6368 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6371 Yemerald_blank, FALSE, FALSE,
6372 EL_EMERALD, ACTION_COLLECTING, -1
6376 Xdiamond, TRUE, FALSE,
6380 Xdiamond_pause, FALSE, FALSE,
6384 Xdiamond_fall, FALSE, FALSE,
6388 Xdiamond_shine, FALSE, FALSE,
6389 EL_DIAMOND, ACTION_TWINKLING, -1
6392 Ydiamond_s, FALSE, FALSE,
6393 EL_DIAMOND, ACTION_FALLING, -1
6396 Ydiamond_sB, FALSE, TRUE,
6397 EL_DIAMOND, ACTION_FALLING, -1
6400 Ydiamond_e, FALSE, FALSE,
6401 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6404 Ydiamond_eB, FALSE, TRUE,
6405 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6408 Ydiamond_w, FALSE, FALSE,
6409 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6412 Ydiamond_wB, FALSE, TRUE,
6413 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6416 Ydiamond_blank, FALSE, FALSE,
6417 EL_DIAMOND, ACTION_COLLECTING, -1
6420 Ydiamond_stone, FALSE, FALSE,
6421 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6425 Xstone, TRUE, FALSE,
6429 Xstone_pause, FALSE, FALSE,
6433 Xstone_fall, FALSE, FALSE,
6437 Ystone_s, FALSE, FALSE,
6438 EL_ROCK, ACTION_FALLING, -1
6441 Ystone_sB, FALSE, TRUE,
6442 EL_ROCK, ACTION_FALLING, -1
6445 Ystone_e, FALSE, FALSE,
6446 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6449 Ystone_eB, FALSE, TRUE,
6450 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6453 Ystone_w, FALSE, FALSE,
6454 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6457 Ystone_wB, FALSE, TRUE,
6458 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6466 Xbomb_pause, FALSE, FALSE,
6470 Xbomb_fall, FALSE, FALSE,
6474 Ybomb_s, FALSE, FALSE,
6475 EL_BOMB, ACTION_FALLING, -1
6478 Ybomb_sB, FALSE, TRUE,
6479 EL_BOMB, ACTION_FALLING, -1
6482 Ybomb_e, FALSE, FALSE,
6483 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6486 Ybomb_eB, FALSE, TRUE,
6487 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6490 Ybomb_w, FALSE, FALSE,
6491 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6494 Ybomb_wB, FALSE, TRUE,
6495 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6498 Ybomb_blank, FALSE, FALSE,
6499 EL_BOMB, ACTION_ACTIVATING, -1
6507 Xnut_pause, FALSE, FALSE,
6511 Xnut_fall, FALSE, FALSE,
6515 Ynut_s, FALSE, FALSE,
6516 EL_NUT, ACTION_FALLING, -1
6519 Ynut_sB, FALSE, TRUE,
6520 EL_NUT, ACTION_FALLING, -1
6523 Ynut_e, FALSE, FALSE,
6524 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6527 Ynut_eB, FALSE, TRUE,
6528 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6531 Ynut_w, FALSE, FALSE,
6532 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6535 Ynut_wB, FALSE, TRUE,
6536 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6539 Ynut_stone, FALSE, FALSE,
6540 EL_NUT, ACTION_BREAKING, -1
6544 Xspring, TRUE, FALSE,
6548 Xspring_pause, FALSE, FALSE,
6552 Xspring_e, TRUE, FALSE,
6553 EL_SPRING_RIGHT, -1, -1
6556 Xspring_w, TRUE, FALSE,
6557 EL_SPRING_LEFT, -1, -1
6560 Xspring_fall, FALSE, FALSE,
6564 Yspring_s, FALSE, FALSE,
6565 EL_SPRING, ACTION_FALLING, -1
6568 Yspring_sB, FALSE, TRUE,
6569 EL_SPRING, ACTION_FALLING, -1
6572 Yspring_e, FALSE, FALSE,
6573 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6576 Yspring_eB, FALSE, TRUE,
6577 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6580 Yspring_w, FALSE, FALSE,
6581 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6584 Yspring_wB, FALSE, TRUE,
6585 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6588 Yspring_alien_e, FALSE, FALSE,
6589 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6592 Yspring_alien_eB, FALSE, TRUE,
6593 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6596 Yspring_alien_w, FALSE, FALSE,
6597 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6600 Yspring_alien_wB, FALSE, TRUE,
6601 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6605 Xpush_emerald_e, FALSE, FALSE,
6606 EL_EMERALD, -1, MV_BIT_RIGHT
6609 Xpush_emerald_w, FALSE, FALSE,
6610 EL_EMERALD, -1, MV_BIT_LEFT
6613 Xpush_diamond_e, FALSE, FALSE,
6614 EL_DIAMOND, -1, MV_BIT_RIGHT
6617 Xpush_diamond_w, FALSE, FALSE,
6618 EL_DIAMOND, -1, MV_BIT_LEFT
6621 Xpush_stone_e, FALSE, FALSE,
6622 EL_ROCK, -1, MV_BIT_RIGHT
6625 Xpush_stone_w, FALSE, FALSE,
6626 EL_ROCK, -1, MV_BIT_LEFT
6629 Xpush_bomb_e, FALSE, FALSE,
6630 EL_BOMB, -1, MV_BIT_RIGHT
6633 Xpush_bomb_w, FALSE, FALSE,
6634 EL_BOMB, -1, MV_BIT_LEFT
6637 Xpush_nut_e, FALSE, FALSE,
6638 EL_NUT, -1, MV_BIT_RIGHT
6641 Xpush_nut_w, FALSE, FALSE,
6642 EL_NUT, -1, MV_BIT_LEFT
6645 Xpush_spring_e, FALSE, FALSE,
6646 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6649 Xpush_spring_w, FALSE, FALSE,
6650 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6654 Xdynamite, TRUE, FALSE,
6655 EL_EM_DYNAMITE, -1, -1
6658 Ydynamite_blank, FALSE, FALSE,
6659 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6662 Xdynamite_1, TRUE, FALSE,
6663 EL_EM_DYNAMITE_ACTIVE, -1, -1
6666 Xdynamite_2, FALSE, FALSE,
6667 EL_EM_DYNAMITE_ACTIVE, -1, -1
6670 Xdynamite_3, FALSE, FALSE,
6671 EL_EM_DYNAMITE_ACTIVE, -1, -1
6674 Xdynamite_4, FALSE, FALSE,
6675 EL_EM_DYNAMITE_ACTIVE, -1, -1
6679 Xkey_1, TRUE, FALSE,
6683 Xkey_2, TRUE, FALSE,
6687 Xkey_3, TRUE, FALSE,
6691 Xkey_4, TRUE, FALSE,
6695 Xkey_5, TRUE, FALSE,
6696 EL_EMC_KEY_5, -1, -1
6699 Xkey_6, TRUE, FALSE,
6700 EL_EMC_KEY_6, -1, -1
6703 Xkey_7, TRUE, FALSE,
6704 EL_EMC_KEY_7, -1, -1
6707 Xkey_8, TRUE, FALSE,
6708 EL_EMC_KEY_8, -1, -1
6712 Xdoor_1, TRUE, FALSE,
6713 EL_EM_GATE_1, -1, -1
6716 Xdoor_2, TRUE, FALSE,
6717 EL_EM_GATE_2, -1, -1
6720 Xdoor_3, TRUE, FALSE,
6721 EL_EM_GATE_3, -1, -1
6724 Xdoor_4, TRUE, FALSE,
6725 EL_EM_GATE_4, -1, -1
6728 Xdoor_5, TRUE, FALSE,
6729 EL_EMC_GATE_5, -1, -1
6732 Xdoor_6, TRUE, FALSE,
6733 EL_EMC_GATE_6, -1, -1
6736 Xdoor_7, TRUE, FALSE,
6737 EL_EMC_GATE_7, -1, -1
6740 Xdoor_8, TRUE, FALSE,
6741 EL_EMC_GATE_8, -1, -1
6745 Xfake_door_1, TRUE, FALSE,
6746 EL_EM_GATE_1_GRAY, -1, -1
6749 Xfake_door_2, TRUE, FALSE,
6750 EL_EM_GATE_2_GRAY, -1, -1
6753 Xfake_door_3, TRUE, FALSE,
6754 EL_EM_GATE_3_GRAY, -1, -1
6757 Xfake_door_4, TRUE, FALSE,
6758 EL_EM_GATE_4_GRAY, -1, -1
6761 Xfake_door_5, TRUE, FALSE,
6762 EL_EMC_GATE_5_GRAY, -1, -1
6765 Xfake_door_6, TRUE, FALSE,
6766 EL_EMC_GATE_6_GRAY, -1, -1
6769 Xfake_door_7, TRUE, FALSE,
6770 EL_EMC_GATE_7_GRAY, -1, -1
6773 Xfake_door_8, TRUE, FALSE,
6774 EL_EMC_GATE_8_GRAY, -1, -1
6778 Xballoon, TRUE, FALSE,
6782 Yballoon_n, FALSE, FALSE,
6783 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6786 Yballoon_nB, FALSE, TRUE,
6787 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6790 Yballoon_e, FALSE, FALSE,
6791 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6794 Yballoon_eB, FALSE, TRUE,
6795 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6798 Yballoon_s, FALSE, FALSE,
6799 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6802 Yballoon_sB, FALSE, TRUE,
6803 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6806 Yballoon_w, FALSE, FALSE,
6807 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6810 Yballoon_wB, FALSE, TRUE,
6811 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6815 Xball_1, TRUE, FALSE,
6816 EL_EMC_MAGIC_BALL, -1, -1
6819 Yball_1, FALSE, FALSE,
6820 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6823 Xball_2, FALSE, FALSE,
6824 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6827 Yball_2, FALSE, FALSE,
6828 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6831 Yball_blank, FALSE, FALSE,
6832 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6836 Xamoeba_1, TRUE, FALSE,
6837 EL_AMOEBA_DRY, ACTION_OTHER, -1
6840 Xamoeba_2, FALSE, FALSE,
6841 EL_AMOEBA_DRY, ACTION_OTHER, -1
6844 Xamoeba_3, FALSE, FALSE,
6845 EL_AMOEBA_DRY, ACTION_OTHER, -1
6848 Xamoeba_4, FALSE, FALSE,
6849 EL_AMOEBA_DRY, ACTION_OTHER, -1
6852 Xamoeba_5, TRUE, FALSE,
6853 EL_AMOEBA_WET, ACTION_OTHER, -1
6856 Xamoeba_6, FALSE, FALSE,
6857 EL_AMOEBA_WET, ACTION_OTHER, -1
6860 Xamoeba_7, FALSE, FALSE,
6861 EL_AMOEBA_WET, ACTION_OTHER, -1
6864 Xamoeba_8, FALSE, FALSE,
6865 EL_AMOEBA_WET, ACTION_OTHER, -1
6870 EL_AMOEBA_DROP, ACTION_GROWING, -1
6873 Xdrip_fall, FALSE, FALSE,
6874 EL_AMOEBA_DROP, -1, -1
6877 Xdrip_stretch, FALSE, FALSE,
6878 EL_AMOEBA_DROP, ACTION_FALLING, -1
6881 Xdrip_stretchB, FALSE, TRUE,
6882 EL_AMOEBA_DROP, ACTION_FALLING, -1
6885 Ydrip_1_s, FALSE, FALSE,
6886 EL_AMOEBA_DROP, ACTION_FALLING, -1
6889 Ydrip_1_sB, FALSE, TRUE,
6890 EL_AMOEBA_DROP, ACTION_FALLING, -1
6893 Ydrip_2_s, FALSE, FALSE,
6894 EL_AMOEBA_DROP, ACTION_FALLING, -1
6897 Ydrip_2_sB, FALSE, TRUE,
6898 EL_AMOEBA_DROP, ACTION_FALLING, -1
6902 Xwonderwall, TRUE, FALSE,
6903 EL_MAGIC_WALL, -1, -1
6906 Ywonderwall, FALSE, FALSE,
6907 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6911 Xwheel, TRUE, FALSE,
6912 EL_ROBOT_WHEEL, -1, -1
6915 Ywheel, FALSE, FALSE,
6916 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6920 Xswitch, TRUE, FALSE,
6921 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6924 Yswitch, FALSE, FALSE,
6925 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6929 Xbumper, TRUE, FALSE,
6930 EL_EMC_SPRING_BUMPER, -1, -1
6933 Ybumper, FALSE, FALSE,
6934 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6938 Xacid_nw, TRUE, FALSE,
6939 EL_ACID_POOL_TOPLEFT, -1, -1
6942 Xacid_ne, TRUE, FALSE,
6943 EL_ACID_POOL_TOPRIGHT, -1, -1
6946 Xacid_sw, TRUE, FALSE,
6947 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6950 Xacid_s, TRUE, FALSE,
6951 EL_ACID_POOL_BOTTOM, -1, -1
6954 Xacid_se, TRUE, FALSE,
6955 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6959 Xfake_blank, TRUE, FALSE,
6960 EL_INVISIBLE_WALL, -1, -1
6963 Yfake_blank, FALSE, FALSE,
6964 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6968 Xfake_grass, TRUE, FALSE,
6969 EL_EMC_FAKE_GRASS, -1, -1
6972 Yfake_grass, FALSE, FALSE,
6973 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6977 Xfake_amoeba, TRUE, FALSE,
6978 EL_EMC_DRIPPER, -1, -1
6981 Yfake_amoeba, FALSE, FALSE,
6982 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6986 Xlenses, TRUE, FALSE,
6987 EL_EMC_LENSES, -1, -1
6991 Xmagnify, TRUE, FALSE,
6992 EL_EMC_MAGNIFIER, -1, -1
6997 EL_QUICKSAND_EMPTY, -1, -1
7000 Xsand_stone, TRUE, FALSE,
7001 EL_QUICKSAND_FULL, -1, -1
7004 Xsand_stonein_1, FALSE, TRUE,
7005 EL_ROCK, ACTION_FILLING, -1
7008 Xsand_stonein_2, FALSE, TRUE,
7009 EL_ROCK, ACTION_FILLING, -1
7012 Xsand_stonein_3, FALSE, TRUE,
7013 EL_ROCK, ACTION_FILLING, -1
7016 Xsand_stonein_4, FALSE, TRUE,
7017 EL_ROCK, ACTION_FILLING, -1
7020 Xsand_sandstone_1, FALSE, FALSE,
7021 EL_QUICKSAND_FILLING, -1, -1
7024 Xsand_sandstone_2, FALSE, FALSE,
7025 EL_QUICKSAND_FILLING, -1, -1
7028 Xsand_sandstone_3, FALSE, FALSE,
7029 EL_QUICKSAND_FILLING, -1, -1
7032 Xsand_sandstone_4, FALSE, FALSE,
7033 EL_QUICKSAND_FILLING, -1, -1
7036 Xsand_stonesand_1, FALSE, FALSE,
7037 EL_QUICKSAND_EMPTYING, -1, -1
7040 Xsand_stonesand_2, FALSE, FALSE,
7041 EL_QUICKSAND_EMPTYING, -1, -1
7044 Xsand_stonesand_3, FALSE, FALSE,
7045 EL_QUICKSAND_EMPTYING, -1, -1
7048 Xsand_stonesand_4, FALSE, FALSE,
7049 EL_QUICKSAND_EMPTYING, -1, -1
7052 Xsand_stoneout_1, FALSE, FALSE,
7053 EL_ROCK, ACTION_EMPTYING, -1
7056 Xsand_stoneout_2, FALSE, FALSE,
7057 EL_ROCK, ACTION_EMPTYING, -1
7060 Xsand_stonesand_quickout_1, FALSE, FALSE,
7061 EL_QUICKSAND_EMPTYING, -1, -1
7064 Xsand_stonesand_quickout_2, FALSE, FALSE,
7065 EL_QUICKSAND_EMPTYING, -1, -1
7069 Xslide_ns, TRUE, FALSE,
7070 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7073 Yslide_ns_blank, FALSE, FALSE,
7074 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7077 Xslide_ew, TRUE, FALSE,
7078 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7081 Yslide_ew_blank, FALSE, FALSE,
7082 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7086 Xwind_n, TRUE, FALSE,
7087 EL_BALLOON_SWITCH_UP, -1, -1
7090 Xwind_e, TRUE, FALSE,
7091 EL_BALLOON_SWITCH_RIGHT, -1, -1
7094 Xwind_s, TRUE, FALSE,
7095 EL_BALLOON_SWITCH_DOWN, -1, -1
7098 Xwind_w, TRUE, FALSE,
7099 EL_BALLOON_SWITCH_LEFT, -1, -1
7102 Xwind_any, TRUE, FALSE,
7103 EL_BALLOON_SWITCH_ANY, -1, -1
7106 Xwind_stop, TRUE, FALSE,
7107 EL_BALLOON_SWITCH_NONE, -1, -1
7112 EL_EM_EXIT_CLOSED, -1, -1
7115 Xexit_1, TRUE, FALSE,
7116 EL_EM_EXIT_OPEN, -1, -1
7119 Xexit_2, FALSE, FALSE,
7120 EL_EM_EXIT_OPEN, -1, -1
7123 Xexit_3, FALSE, FALSE,
7124 EL_EM_EXIT_OPEN, -1, -1
7128 Xpause, FALSE, FALSE,
7133 Xwall_1, TRUE, FALSE,
7137 Xwall_2, TRUE, FALSE,
7138 EL_EMC_WALL_14, -1, -1
7141 Xwall_3, TRUE, FALSE,
7142 EL_EMC_WALL_15, -1, -1
7145 Xwall_4, TRUE, FALSE,
7146 EL_EMC_WALL_16, -1, -1
7150 Xroundwall_1, TRUE, FALSE,
7151 EL_WALL_SLIPPERY, -1, -1
7154 Xroundwall_2, TRUE, FALSE,
7155 EL_EMC_WALL_SLIPPERY_2, -1, -1
7158 Xroundwall_3, TRUE, FALSE,
7159 EL_EMC_WALL_SLIPPERY_3, -1, -1
7162 Xroundwall_4, TRUE, FALSE,
7163 EL_EMC_WALL_SLIPPERY_4, -1, -1
7167 Xsteel_1, TRUE, FALSE,
7168 EL_STEELWALL, -1, -1
7171 Xsteel_2, TRUE, FALSE,
7172 EL_EMC_STEELWALL_2, -1, -1
7175 Xsteel_3, TRUE, FALSE,
7176 EL_EMC_STEELWALL_3, -1, -1
7179 Xsteel_4, TRUE, FALSE,
7180 EL_EMC_STEELWALL_4, -1, -1
7184 Xdecor_1, TRUE, FALSE,
7185 EL_EMC_WALL_8, -1, -1
7188 Xdecor_2, TRUE, FALSE,
7189 EL_EMC_WALL_6, -1, -1
7192 Xdecor_3, TRUE, FALSE,
7193 EL_EMC_WALL_4, -1, -1
7196 Xdecor_4, TRUE, FALSE,
7197 EL_EMC_WALL_7, -1, -1
7200 Xdecor_5, TRUE, FALSE,
7201 EL_EMC_WALL_5, -1, -1
7204 Xdecor_6, TRUE, FALSE,
7205 EL_EMC_WALL_9, -1, -1
7208 Xdecor_7, TRUE, FALSE,
7209 EL_EMC_WALL_10, -1, -1
7212 Xdecor_8, TRUE, FALSE,
7213 EL_EMC_WALL_1, -1, -1
7216 Xdecor_9, TRUE, FALSE,
7217 EL_EMC_WALL_2, -1, -1
7220 Xdecor_10, TRUE, FALSE,
7221 EL_EMC_WALL_3, -1, -1
7224 Xdecor_11, TRUE, FALSE,
7225 EL_EMC_WALL_11, -1, -1
7228 Xdecor_12, TRUE, FALSE,
7229 EL_EMC_WALL_12, -1, -1
7233 Xalpha_0, TRUE, FALSE,
7234 EL_CHAR('0'), -1, -1
7237 Xalpha_1, TRUE, FALSE,
7238 EL_CHAR('1'), -1, -1
7241 Xalpha_2, TRUE, FALSE,
7242 EL_CHAR('2'), -1, -1
7245 Xalpha_3, TRUE, FALSE,
7246 EL_CHAR('3'), -1, -1
7249 Xalpha_4, TRUE, FALSE,
7250 EL_CHAR('4'), -1, -1
7253 Xalpha_5, TRUE, FALSE,
7254 EL_CHAR('5'), -1, -1
7257 Xalpha_6, TRUE, FALSE,
7258 EL_CHAR('6'), -1, -1
7261 Xalpha_7, TRUE, FALSE,
7262 EL_CHAR('7'), -1, -1
7265 Xalpha_8, TRUE, FALSE,
7266 EL_CHAR('8'), -1, -1
7269 Xalpha_9, TRUE, FALSE,
7270 EL_CHAR('9'), -1, -1
7273 Xalpha_excla, TRUE, FALSE,
7274 EL_CHAR('!'), -1, -1
7277 Xalpha_apost, TRUE, FALSE,
7278 EL_CHAR('\''), -1, -1
7281 Xalpha_comma, TRUE, FALSE,
7282 EL_CHAR(','), -1, -1
7285 Xalpha_minus, TRUE, FALSE,
7286 EL_CHAR('-'), -1, -1
7289 Xalpha_perio, TRUE, FALSE,
7290 EL_CHAR('.'), -1, -1
7293 Xalpha_colon, TRUE, FALSE,
7294 EL_CHAR(':'), -1, -1
7297 Xalpha_quest, TRUE, FALSE,
7298 EL_CHAR('?'), -1, -1
7301 Xalpha_a, TRUE, FALSE,
7302 EL_CHAR('A'), -1, -1
7305 Xalpha_b, TRUE, FALSE,
7306 EL_CHAR('B'), -1, -1
7309 Xalpha_c, TRUE, FALSE,
7310 EL_CHAR('C'), -1, -1
7313 Xalpha_d, TRUE, FALSE,
7314 EL_CHAR('D'), -1, -1
7317 Xalpha_e, TRUE, FALSE,
7318 EL_CHAR('E'), -1, -1
7321 Xalpha_f, TRUE, FALSE,
7322 EL_CHAR('F'), -1, -1
7325 Xalpha_g, TRUE, FALSE,
7326 EL_CHAR('G'), -1, -1
7329 Xalpha_h, TRUE, FALSE,
7330 EL_CHAR('H'), -1, -1
7333 Xalpha_i, TRUE, FALSE,
7334 EL_CHAR('I'), -1, -1
7337 Xalpha_j, TRUE, FALSE,
7338 EL_CHAR('J'), -1, -1
7341 Xalpha_k, TRUE, FALSE,
7342 EL_CHAR('K'), -1, -1
7345 Xalpha_l, TRUE, FALSE,
7346 EL_CHAR('L'), -1, -1
7349 Xalpha_m, TRUE, FALSE,
7350 EL_CHAR('M'), -1, -1
7353 Xalpha_n, TRUE, FALSE,
7354 EL_CHAR('N'), -1, -1
7357 Xalpha_o, TRUE, FALSE,
7358 EL_CHAR('O'), -1, -1
7361 Xalpha_p, TRUE, FALSE,
7362 EL_CHAR('P'), -1, -1
7365 Xalpha_q, TRUE, FALSE,
7366 EL_CHAR('Q'), -1, -1
7369 Xalpha_r, TRUE, FALSE,
7370 EL_CHAR('R'), -1, -1
7373 Xalpha_s, TRUE, FALSE,
7374 EL_CHAR('S'), -1, -1
7377 Xalpha_t, TRUE, FALSE,
7378 EL_CHAR('T'), -1, -1
7381 Xalpha_u, TRUE, FALSE,
7382 EL_CHAR('U'), -1, -1
7385 Xalpha_v, TRUE, FALSE,
7386 EL_CHAR('V'), -1, -1
7389 Xalpha_w, TRUE, FALSE,
7390 EL_CHAR('W'), -1, -1
7393 Xalpha_x, TRUE, FALSE,
7394 EL_CHAR('X'), -1, -1
7397 Xalpha_y, TRUE, FALSE,
7398 EL_CHAR('Y'), -1, -1
7401 Xalpha_z, TRUE, FALSE,
7402 EL_CHAR('Z'), -1, -1
7405 Xalpha_arrow_e, TRUE, FALSE,
7406 EL_CHAR('>'), -1, -1
7409 Xalpha_arrow_w, TRUE, FALSE,
7410 EL_CHAR('<'), -1, -1
7413 Xalpha_copyr, TRUE, FALSE,
7414 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7418 Ykey_1_blank, FALSE, FALSE,
7419 EL_EM_KEY_1, ACTION_COLLECTING, -1
7422 Ykey_2_blank, FALSE, FALSE,
7423 EL_EM_KEY_2, ACTION_COLLECTING, -1
7426 Ykey_3_blank, FALSE, FALSE,
7427 EL_EM_KEY_3, ACTION_COLLECTING, -1
7430 Ykey_4_blank, FALSE, FALSE,
7431 EL_EM_KEY_4, ACTION_COLLECTING, -1
7434 Ykey_5_blank, FALSE, FALSE,
7435 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7438 Ykey_6_blank, FALSE, FALSE,
7439 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7442 Ykey_7_blank, FALSE, FALSE,
7443 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7446 Ykey_8_blank, FALSE, FALSE,
7447 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7450 Ylenses_blank, FALSE, FALSE,
7451 EL_EMC_LENSES, ACTION_COLLECTING, -1
7454 Ymagnify_blank, FALSE, FALSE,
7455 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7458 Ygrass_blank, FALSE, FALSE,
7459 EL_EMC_GRASS, ACTION_SNAPPING, -1
7462 Ydirt_blank, FALSE, FALSE,
7463 EL_SAND, ACTION_SNAPPING, -1
7472 static struct Mapping_EM_to_RND_player
7481 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7485 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7489 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7493 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7497 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7501 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7505 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7509 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7513 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7517 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7521 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7525 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7529 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7533 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7537 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7541 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7545 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7549 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7553 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7557 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7561 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7565 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7569 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7573 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7577 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7581 EL_PLAYER_1, ACTION_DEFAULT, -1,
7585 EL_PLAYER_2, ACTION_DEFAULT, -1,
7589 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7593 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7597 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7601 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7605 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7609 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7613 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7617 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7621 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7625 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7629 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7633 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7637 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7641 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7645 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7649 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7653 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7657 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7661 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7665 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7669 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7673 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7677 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7681 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7685 EL_PLAYER_3, ACTION_DEFAULT, -1,
7689 EL_PLAYER_4, ACTION_DEFAULT, -1,
7698 int map_element_RND_to_EM_cave(int element_rnd)
7700 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7701 static boolean mapping_initialized = FALSE;
7703 if (!mapping_initialized)
7707 // return "Xalpha_quest" for all undefined elements in mapping array
7708 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7709 mapping_RND_to_EM[i] = Xalpha_quest;
7711 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7712 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7713 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7714 em_object_mapping_list[i].element_em;
7716 mapping_initialized = TRUE;
7719 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7721 Warn("invalid RND level element %d", element_rnd);
7726 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7729 int map_element_EM_to_RND_cave(int element_em_cave)
7731 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7732 static boolean mapping_initialized = FALSE;
7734 if (!mapping_initialized)
7738 // return "EL_UNKNOWN" for all undefined elements in mapping array
7739 for (i = 0; i < GAME_TILE_MAX; i++)
7740 mapping_EM_to_RND[i] = EL_UNKNOWN;
7742 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7743 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7744 em_object_mapping_list[i].element_rnd;
7746 mapping_initialized = TRUE;
7749 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7751 Warn("invalid EM cave element %d", element_em_cave);
7756 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7759 int map_element_EM_to_RND_game(int element_em_game)
7761 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7762 static boolean mapping_initialized = FALSE;
7764 if (!mapping_initialized)
7768 // return "EL_UNKNOWN" for all undefined elements in mapping array
7769 for (i = 0; i < GAME_TILE_MAX; i++)
7770 mapping_EM_to_RND[i] = EL_UNKNOWN;
7772 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7773 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7774 em_object_mapping_list[i].element_rnd;
7776 mapping_initialized = TRUE;
7779 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7781 Warn("invalid EM game element %d", element_em_game);
7786 return mapping_EM_to_RND[element_em_game];
7789 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7791 struct LevelInfo_EM *level_em = level->native_em_level;
7792 struct CAVE *cav = level_em->cav;
7795 for (i = 0; i < GAME_TILE_MAX; i++)
7796 cav->android_array[i] = Cblank;
7798 for (i = 0; i < level->num_android_clone_elements; i++)
7800 int element_rnd = level->android_clone_element[i];
7801 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7803 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7804 if (em_object_mapping_list[j].element_rnd == element_rnd)
7805 cav->android_array[em_object_mapping_list[j].element_em] =
7810 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7812 struct LevelInfo_EM *level_em = level->native_em_level;
7813 struct CAVE *cav = level_em->cav;
7816 level->num_android_clone_elements = 0;
7818 for (i = 0; i < GAME_TILE_MAX; i++)
7820 int element_em_cave = cav->android_array[i];
7822 boolean element_found = FALSE;
7824 if (element_em_cave == Cblank)
7827 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7829 for (j = 0; j < level->num_android_clone_elements; j++)
7830 if (level->android_clone_element[j] == element_rnd)
7831 element_found = TRUE;
7835 level->android_clone_element[level->num_android_clone_elements++] =
7838 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7843 if (level->num_android_clone_elements == 0)
7845 level->num_android_clone_elements = 1;
7846 level->android_clone_element[0] = EL_EMPTY;
7850 int map_direction_RND_to_EM(int direction)
7852 return (direction == MV_UP ? 0 :
7853 direction == MV_RIGHT ? 1 :
7854 direction == MV_DOWN ? 2 :
7855 direction == MV_LEFT ? 3 :
7859 int map_direction_EM_to_RND(int direction)
7861 return (direction == 0 ? MV_UP :
7862 direction == 1 ? MV_RIGHT :
7863 direction == 2 ? MV_DOWN :
7864 direction == 3 ? MV_LEFT :
7868 int map_element_RND_to_SP(int element_rnd)
7870 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7872 if (element_rnd >= EL_SP_START &&
7873 element_rnd <= EL_SP_END)
7874 element_sp = element_rnd - EL_SP_START;
7875 else if (element_rnd == EL_EMPTY_SPACE)
7877 else if (element_rnd == EL_INVISIBLE_WALL)
7883 int map_element_SP_to_RND(int element_sp)
7885 int element_rnd = EL_UNKNOWN;
7887 if (element_sp >= 0x00 &&
7889 element_rnd = EL_SP_START + element_sp;
7890 else if (element_sp == 0x28)
7891 element_rnd = EL_INVISIBLE_WALL;
7896 int map_action_SP_to_RND(int action_sp)
7900 case actActive: return ACTION_ACTIVE;
7901 case actImpact: return ACTION_IMPACT;
7902 case actExploding: return ACTION_EXPLODING;
7903 case actDigging: return ACTION_DIGGING;
7904 case actSnapping: return ACTION_SNAPPING;
7905 case actCollecting: return ACTION_COLLECTING;
7906 case actPassing: return ACTION_PASSING;
7907 case actPushing: return ACTION_PUSHING;
7908 case actDropping: return ACTION_DROPPING;
7910 default: return ACTION_DEFAULT;
7914 int map_element_RND_to_MM(int element_rnd)
7916 return (element_rnd >= EL_MM_START_1 &&
7917 element_rnd <= EL_MM_END_1 ?
7918 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7920 element_rnd >= EL_MM_START_2 &&
7921 element_rnd <= EL_MM_END_2 ?
7922 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7924 element_rnd >= EL_CHAR_START &&
7925 element_rnd <= EL_CHAR_END ?
7926 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7928 element_rnd >= EL_MM_RUNTIME_START &&
7929 element_rnd <= EL_MM_RUNTIME_END ?
7930 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7932 element_rnd >= EL_MM_DUMMY_START &&
7933 element_rnd <= EL_MM_DUMMY_END ?
7934 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7936 EL_MM_EMPTY_NATIVE);
7939 int map_element_MM_to_RND(int element_mm)
7941 return (element_mm == EL_MM_EMPTY_NATIVE ||
7942 element_mm == EL_DF_EMPTY_NATIVE ?
7945 element_mm >= EL_MM_START_1_NATIVE &&
7946 element_mm <= EL_MM_END_1_NATIVE ?
7947 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7949 element_mm >= EL_MM_START_2_NATIVE &&
7950 element_mm <= EL_MM_END_2_NATIVE ?
7951 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7953 element_mm >= EL_MM_CHAR_START_NATIVE &&
7954 element_mm <= EL_MM_CHAR_END_NATIVE ?
7955 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7957 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7958 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7959 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7961 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7962 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7963 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7968 int map_action_MM_to_RND(int action_mm)
7970 // all MM actions are defined to exactly match their RND counterparts
7974 int map_sound_MM_to_RND(int sound_mm)
7978 case SND_MM_GAME_LEVELTIME_CHARGING:
7979 return SND_GAME_LEVELTIME_CHARGING;
7981 case SND_MM_GAME_HEALTH_CHARGING:
7982 return SND_GAME_HEALTH_CHARGING;
7985 return SND_UNDEFINED;
7989 int map_mm_wall_element(int element)
7991 return (element >= EL_MM_STEEL_WALL_START &&
7992 element <= EL_MM_STEEL_WALL_END ?
7995 element >= EL_MM_WOODEN_WALL_START &&
7996 element <= EL_MM_WOODEN_WALL_END ?
7999 element >= EL_MM_ICE_WALL_START &&
8000 element <= EL_MM_ICE_WALL_END ?
8003 element >= EL_MM_AMOEBA_WALL_START &&
8004 element <= EL_MM_AMOEBA_WALL_END ?
8007 element >= EL_DF_STEEL_WALL_START &&
8008 element <= EL_DF_STEEL_WALL_END ?
8011 element >= EL_DF_WOODEN_WALL_START &&
8012 element <= EL_DF_WOODEN_WALL_END ?
8018 int map_mm_wall_element_editor(int element)
8022 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8023 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8024 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8025 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8026 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8027 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8029 default: return element;
8033 int get_next_element(int element)
8037 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8038 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8039 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8040 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8041 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8042 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8043 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8044 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8045 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8046 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8047 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8049 default: return element;
8053 int el2img_mm(int element_mm)
8055 return el2img(map_element_MM_to_RND(element_mm));
8058 int el_act_dir2img(int element, int action, int direction)
8060 element = GFX_ELEMENT(element);
8061 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8063 // direction_graphic[][] == graphic[] for undefined direction graphics
8064 return element_info[element].direction_graphic[action][direction];
8067 static int el_act_dir2crm(int element, int action, int direction)
8069 element = GFX_ELEMENT(element);
8070 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8072 // direction_graphic[][] == graphic[] for undefined direction graphics
8073 return element_info[element].direction_crumbled[action][direction];
8076 int el_act2img(int element, int action)
8078 element = GFX_ELEMENT(element);
8080 return element_info[element].graphic[action];
8083 int el_act2crm(int element, int action)
8085 element = GFX_ELEMENT(element);
8087 return element_info[element].crumbled[action];
8090 int el_dir2img(int element, int direction)
8092 element = GFX_ELEMENT(element);
8094 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8097 int el2baseimg(int element)
8099 return element_info[element].graphic[ACTION_DEFAULT];
8102 int el2img(int element)
8104 element = GFX_ELEMENT(element);
8106 return element_info[element].graphic[ACTION_DEFAULT];
8109 int el2edimg(int element)
8111 element = GFX_ELEMENT(element);
8113 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8116 int el2preimg(int element)
8118 element = GFX_ELEMENT(element);
8120 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8123 int el2panelimg(int element)
8125 element = GFX_ELEMENT(element);
8127 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8130 int font2baseimg(int font_nr)
8132 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8135 int getBeltNrFromBeltElement(int element)
8137 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8138 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8139 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8142 int getBeltNrFromBeltActiveElement(int element)
8144 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8145 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8146 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8149 int getBeltNrFromBeltSwitchElement(int element)
8151 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8152 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8153 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8156 int getBeltDirNrFromBeltElement(int element)
8158 static int belt_base_element[4] =
8160 EL_CONVEYOR_BELT_1_LEFT,
8161 EL_CONVEYOR_BELT_2_LEFT,
8162 EL_CONVEYOR_BELT_3_LEFT,
8163 EL_CONVEYOR_BELT_4_LEFT
8166 int belt_nr = getBeltNrFromBeltElement(element);
8167 int belt_dir_nr = element - belt_base_element[belt_nr];
8169 return (belt_dir_nr % 3);
8172 int getBeltDirNrFromBeltSwitchElement(int element)
8174 static int belt_base_element[4] =
8176 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8177 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8178 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8179 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8182 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8183 int belt_dir_nr = element - belt_base_element[belt_nr];
8185 return (belt_dir_nr % 3);
8188 int getBeltDirFromBeltElement(int element)
8190 static int belt_move_dir[3] =
8197 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8199 return belt_move_dir[belt_dir_nr];
8202 int getBeltDirFromBeltSwitchElement(int element)
8204 static int belt_move_dir[3] =
8211 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8213 return belt_move_dir[belt_dir_nr];
8216 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8218 static int belt_base_element[4] =
8220 EL_CONVEYOR_BELT_1_LEFT,
8221 EL_CONVEYOR_BELT_2_LEFT,
8222 EL_CONVEYOR_BELT_3_LEFT,
8223 EL_CONVEYOR_BELT_4_LEFT
8226 return belt_base_element[belt_nr] + belt_dir_nr;
8229 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8231 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8233 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8236 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8238 static int belt_base_element[4] =
8240 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8241 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8242 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8243 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8246 return belt_base_element[belt_nr] + belt_dir_nr;
8249 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8251 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8253 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8256 boolean swapTiles_EM(boolean is_pre_emc_cave)
8258 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8261 boolean getTeamMode_EM(void)
8263 return game.team_mode || network_playing;
8266 boolean isActivePlayer_EM(int player_nr)
8268 return stored_player[player_nr].active;
8271 unsigned int InitRND(int seed)
8273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8274 return InitEngineRandom_EM(seed);
8275 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8276 return InitEngineRandom_SP(seed);
8277 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8278 return InitEngineRandom_MM(seed);
8280 return InitEngineRandom_RND(seed);
8283 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8284 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8286 static int get_effective_element_EM(int tile, int frame_em)
8288 int element = object_mapping[tile].element_rnd;
8289 int action = object_mapping[tile].action;
8290 boolean is_backside = object_mapping[tile].is_backside;
8291 boolean action_removing = (action == ACTION_DIGGING ||
8292 action == ACTION_SNAPPING ||
8293 action == ACTION_COLLECTING);
8301 return (frame_em > 5 ? EL_EMPTY : element);
8307 else // frame_em == 7
8318 case Ydiamond_stone:
8322 case Xdrip_stretchB:
8338 case Ymagnify_blank:
8341 case Xsand_stonein_1:
8342 case Xsand_stonein_2:
8343 case Xsand_stonein_3:
8344 case Xsand_stonein_4:
8348 return (is_backside || action_removing ? EL_EMPTY : element);
8353 static boolean check_linear_animation_EM(int tile)
8357 case Xsand_stonesand_1:
8358 case Xsand_stonesand_quickout_1:
8359 case Xsand_sandstone_1:
8360 case Xsand_stonein_1:
8361 case Xsand_stoneout_1:
8389 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8390 boolean has_crumbled_graphics,
8391 int crumbled, int sync_frame)
8393 // if element can be crumbled, but certain action graphics are just empty
8394 // space (like instantly snapping sand to empty space in 1 frame), do not
8395 // treat these empty space graphics as crumbled graphics in EMC engine
8396 if (crumbled == IMG_EMPTY_SPACE)
8397 has_crumbled_graphics = FALSE;
8399 if (has_crumbled_graphics)
8401 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8402 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8403 g_crumbled->anim_delay,
8404 g_crumbled->anim_mode,
8405 g_crumbled->anim_start_frame,
8408 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8409 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8411 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8412 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8414 g_em->has_crumbled_graphics = TRUE;
8418 g_em->crumbled_bitmap = NULL;
8419 g_em->crumbled_src_x = 0;
8420 g_em->crumbled_src_y = 0;
8421 g_em->crumbled_border_size = 0;
8422 g_em->crumbled_tile_size = 0;
8424 g_em->has_crumbled_graphics = FALSE;
8429 void ResetGfxAnimation_EM(int x, int y, int tile)
8435 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8436 int tile, int frame_em, int x, int y)
8438 int action = object_mapping[tile].action;
8439 int direction = object_mapping[tile].direction;
8440 int effective_element = get_effective_element_EM(tile, frame_em);
8441 int graphic = (direction == MV_NONE ?
8442 el_act2img(effective_element, action) :
8443 el_act_dir2img(effective_element, action, direction));
8444 struct GraphicInfo *g = &graphic_info[graphic];
8446 boolean action_removing = (action == ACTION_DIGGING ||
8447 action == ACTION_SNAPPING ||
8448 action == ACTION_COLLECTING);
8449 boolean action_moving = (action == ACTION_FALLING ||
8450 action == ACTION_MOVING ||
8451 action == ACTION_PUSHING ||
8452 action == ACTION_EATING ||
8453 action == ACTION_FILLING ||
8454 action == ACTION_EMPTYING);
8455 boolean action_falling = (action == ACTION_FALLING ||
8456 action == ACTION_FILLING ||
8457 action == ACTION_EMPTYING);
8459 // special case: graphic uses "2nd movement tile" and has defined
8460 // 7 frames for movement animation (or less) => use default graphic
8461 // for last (8th) frame which ends the movement animation
8462 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8464 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8465 graphic = (direction == MV_NONE ?
8466 el_act2img(effective_element, action) :
8467 el_act_dir2img(effective_element, action, direction));
8469 g = &graphic_info[graphic];
8472 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8476 else if (action_moving)
8478 boolean is_backside = object_mapping[tile].is_backside;
8482 int direction = object_mapping[tile].direction;
8483 int move_dir = (action_falling ? MV_DOWN : direction);
8488 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8489 if (g->double_movement && frame_em == 0)
8493 if (move_dir == MV_LEFT)
8494 GfxFrame[x - 1][y] = GfxFrame[x][y];
8495 else if (move_dir == MV_RIGHT)
8496 GfxFrame[x + 1][y] = GfxFrame[x][y];
8497 else if (move_dir == MV_UP)
8498 GfxFrame[x][y - 1] = GfxFrame[x][y];
8499 else if (move_dir == MV_DOWN)
8500 GfxFrame[x][y + 1] = GfxFrame[x][y];
8507 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8508 if (tile == Xsand_stonesand_quickout_1 ||
8509 tile == Xsand_stonesand_quickout_2)
8513 if (graphic_info[graphic].anim_global_sync)
8514 sync_frame = FrameCounter;
8515 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8516 sync_frame = GfxFrame[x][y];
8518 sync_frame = 0; // playfield border (pseudo steel)
8520 SetRandomAnimationValue(x, y);
8522 int frame = getAnimationFrame(g->anim_frames,
8525 g->anim_start_frame,
8528 g_em->unique_identifier =
8529 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8532 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8533 int tile, int frame_em, int x, int y)
8535 int action = object_mapping[tile].action;
8536 int direction = object_mapping[tile].direction;
8537 boolean is_backside = object_mapping[tile].is_backside;
8538 int effective_element = get_effective_element_EM(tile, frame_em);
8539 int effective_action = action;
8540 int graphic = (direction == MV_NONE ?
8541 el_act2img(effective_element, effective_action) :
8542 el_act_dir2img(effective_element, effective_action,
8544 int crumbled = (direction == MV_NONE ?
8545 el_act2crm(effective_element, effective_action) :
8546 el_act_dir2crm(effective_element, effective_action,
8548 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8549 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8550 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8551 struct GraphicInfo *g = &graphic_info[graphic];
8554 // special case: graphic uses "2nd movement tile" and has defined
8555 // 7 frames for movement animation (or less) => use default graphic
8556 // for last (8th) frame which ends the movement animation
8557 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8559 effective_action = ACTION_DEFAULT;
8560 graphic = (direction == MV_NONE ?
8561 el_act2img(effective_element, effective_action) :
8562 el_act_dir2img(effective_element, effective_action,
8564 crumbled = (direction == MV_NONE ?
8565 el_act2crm(effective_element, effective_action) :
8566 el_act_dir2crm(effective_element, effective_action,
8569 g = &graphic_info[graphic];
8572 if (graphic_info[graphic].anim_global_sync)
8573 sync_frame = FrameCounter;
8574 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8575 sync_frame = GfxFrame[x][y];
8577 sync_frame = 0; // playfield border (pseudo steel)
8579 SetRandomAnimationValue(x, y);
8581 int frame = getAnimationFrame(g->anim_frames,
8584 g->anim_start_frame,
8587 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8588 g->double_movement && is_backside);
8590 // (updating the "crumbled" graphic definitions is probably not really needed,
8591 // as animations for crumbled graphics can't be longer than one EMC cycle)
8592 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8596 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8597 int player_nr, int anim, int frame_em)
8599 int element = player_mapping[player_nr][anim].element_rnd;
8600 int action = player_mapping[player_nr][anim].action;
8601 int direction = player_mapping[player_nr][anim].direction;
8602 int graphic = (direction == MV_NONE ?
8603 el_act2img(element, action) :
8604 el_act_dir2img(element, action, direction));
8605 struct GraphicInfo *g = &graphic_info[graphic];
8608 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8610 stored_player[player_nr].StepFrame = frame_em;
8612 sync_frame = stored_player[player_nr].Frame;
8614 int frame = getAnimationFrame(g->anim_frames,
8617 g->anim_start_frame,
8620 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8621 &g_em->src_x, &g_em->src_y, FALSE);
8624 void InitGraphicInfo_EM(void)
8628 // always start with reliable default values
8629 for (i = 0; i < GAME_TILE_MAX; i++)
8631 object_mapping[i].element_rnd = EL_UNKNOWN;
8632 object_mapping[i].is_backside = FALSE;
8633 object_mapping[i].action = ACTION_DEFAULT;
8634 object_mapping[i].direction = MV_NONE;
8637 // always start with reliable default values
8638 for (p = 0; p < MAX_PLAYERS; p++)
8640 for (i = 0; i < PLY_MAX; i++)
8642 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8643 player_mapping[p][i].action = ACTION_DEFAULT;
8644 player_mapping[p][i].direction = MV_NONE;
8648 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8650 int e = em_object_mapping_list[i].element_em;
8652 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8653 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8655 if (em_object_mapping_list[i].action != -1)
8656 object_mapping[e].action = em_object_mapping_list[i].action;
8658 if (em_object_mapping_list[i].direction != -1)
8659 object_mapping[e].direction =
8660 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8663 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8665 int a = em_player_mapping_list[i].action_em;
8666 int p = em_player_mapping_list[i].player_nr;
8668 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8670 if (em_player_mapping_list[i].action != -1)
8671 player_mapping[p][a].action = em_player_mapping_list[i].action;
8673 if (em_player_mapping_list[i].direction != -1)
8674 player_mapping[p][a].direction =
8675 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8678 for (i = 0; i < GAME_TILE_MAX; i++)
8680 int element = object_mapping[i].element_rnd;
8681 int action = object_mapping[i].action;
8682 int direction = object_mapping[i].direction;
8683 boolean is_backside = object_mapping[i].is_backside;
8684 boolean action_exploding = ((action == ACTION_EXPLODING ||
8685 action == ACTION_SMASHED_BY_ROCK ||
8686 action == ACTION_SMASHED_BY_SPRING) &&
8687 element != EL_DIAMOND);
8688 boolean action_active = (action == ACTION_ACTIVE);
8689 boolean action_other = (action == ACTION_OTHER);
8691 for (j = 0; j < 8; j++)
8693 int effective_element = get_effective_element_EM(i, j);
8694 int effective_action = (j < 7 ? action :
8695 i == Xdrip_stretch ? action :
8696 i == Xdrip_stretchB ? action :
8697 i == Ydrip_1_s ? action :
8698 i == Ydrip_1_sB ? action :
8699 i == Yball_1 ? action :
8700 i == Xball_2 ? action :
8701 i == Yball_2 ? action :
8702 i == Yball_blank ? action :
8703 i == Ykey_1_blank ? action :
8704 i == Ykey_2_blank ? action :
8705 i == Ykey_3_blank ? action :
8706 i == Ykey_4_blank ? action :
8707 i == Ykey_5_blank ? action :
8708 i == Ykey_6_blank ? action :
8709 i == Ykey_7_blank ? action :
8710 i == Ykey_8_blank ? action :
8711 i == Ylenses_blank ? action :
8712 i == Ymagnify_blank ? action :
8713 i == Ygrass_blank ? action :
8714 i == Ydirt_blank ? action :
8715 i == Xsand_stonein_1 ? action :
8716 i == Xsand_stonein_2 ? action :
8717 i == Xsand_stonein_3 ? action :
8718 i == Xsand_stonein_4 ? action :
8719 i == Xsand_stoneout_1 ? action :
8720 i == Xsand_stoneout_2 ? action :
8721 i == Xboom_android ? ACTION_EXPLODING :
8722 action_exploding ? ACTION_EXPLODING :
8723 action_active ? action :
8724 action_other ? action :
8726 int graphic = (el_act_dir2img(effective_element, effective_action,
8728 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8730 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8731 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8732 boolean has_action_graphics = (graphic != base_graphic);
8733 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8734 struct GraphicInfo *g = &graphic_info[graphic];
8735 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8738 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8739 boolean special_animation = (action != ACTION_DEFAULT &&
8740 g->anim_frames == 3 &&
8741 g->anim_delay == 2 &&
8742 g->anim_mode & ANIM_LINEAR);
8743 int sync_frame = (i == Xdrip_stretch ? 7 :
8744 i == Xdrip_stretchB ? 7 :
8745 i == Ydrip_2_s ? j + 8 :
8746 i == Ydrip_2_sB ? j + 8 :
8755 i == Xfake_acid_1 ? 0 :
8756 i == Xfake_acid_2 ? 10 :
8757 i == Xfake_acid_3 ? 20 :
8758 i == Xfake_acid_4 ? 30 :
8759 i == Xfake_acid_5 ? 40 :
8760 i == Xfake_acid_6 ? 50 :
8761 i == Xfake_acid_7 ? 60 :
8762 i == Xfake_acid_8 ? 70 :
8763 i == Xfake_acid_1_player ? 0 :
8764 i == Xfake_acid_2_player ? 10 :
8765 i == Xfake_acid_3_player ? 20 :
8766 i == Xfake_acid_4_player ? 30 :
8767 i == Xfake_acid_5_player ? 40 :
8768 i == Xfake_acid_6_player ? 50 :
8769 i == Xfake_acid_7_player ? 60 :
8770 i == Xfake_acid_8_player ? 70 :
8772 i == Yball_2 ? j + 8 :
8773 i == Yball_blank ? j + 1 :
8774 i == Ykey_1_blank ? j + 1 :
8775 i == Ykey_2_blank ? j + 1 :
8776 i == Ykey_3_blank ? j + 1 :
8777 i == Ykey_4_blank ? j + 1 :
8778 i == Ykey_5_blank ? j + 1 :
8779 i == Ykey_6_blank ? j + 1 :
8780 i == Ykey_7_blank ? j + 1 :
8781 i == Ykey_8_blank ? j + 1 :
8782 i == Ylenses_blank ? j + 1 :
8783 i == Ymagnify_blank ? j + 1 :
8784 i == Ygrass_blank ? j + 1 :
8785 i == Ydirt_blank ? j + 1 :
8786 i == Xamoeba_1 ? 0 :
8787 i == Xamoeba_2 ? 1 :
8788 i == Xamoeba_3 ? 2 :
8789 i == Xamoeba_4 ? 3 :
8790 i == Xamoeba_5 ? 0 :
8791 i == Xamoeba_6 ? 1 :
8792 i == Xamoeba_7 ? 2 :
8793 i == Xamoeba_8 ? 3 :
8794 i == Xexit_2 ? j + 8 :
8795 i == Xexit_3 ? j + 16 :
8796 i == Xdynamite_1 ? 0 :
8797 i == Xdynamite_2 ? 8 :
8798 i == Xdynamite_3 ? 16 :
8799 i == Xdynamite_4 ? 24 :
8800 i == Xsand_stonein_1 ? j + 1 :
8801 i == Xsand_stonein_2 ? j + 9 :
8802 i == Xsand_stonein_3 ? j + 17 :
8803 i == Xsand_stonein_4 ? j + 25 :
8804 i == Xsand_stoneout_1 && j == 0 ? 0 :
8805 i == Xsand_stoneout_1 && j == 1 ? 0 :
8806 i == Xsand_stoneout_1 && j == 2 ? 1 :
8807 i == Xsand_stoneout_1 && j == 3 ? 2 :
8808 i == Xsand_stoneout_1 && j == 4 ? 2 :
8809 i == Xsand_stoneout_1 && j == 5 ? 3 :
8810 i == Xsand_stoneout_1 && j == 6 ? 4 :
8811 i == Xsand_stoneout_1 && j == 7 ? 4 :
8812 i == Xsand_stoneout_2 && j == 0 ? 5 :
8813 i == Xsand_stoneout_2 && j == 1 ? 6 :
8814 i == Xsand_stoneout_2 && j == 2 ? 7 :
8815 i == Xsand_stoneout_2 && j == 3 ? 8 :
8816 i == Xsand_stoneout_2 && j == 4 ? 9 :
8817 i == Xsand_stoneout_2 && j == 5 ? 11 :
8818 i == Xsand_stoneout_2 && j == 6 ? 13 :
8819 i == Xsand_stoneout_2 && j == 7 ? 15 :
8820 i == Xboom_bug && j == 1 ? 2 :
8821 i == Xboom_bug && j == 2 ? 2 :
8822 i == Xboom_bug && j == 3 ? 4 :
8823 i == Xboom_bug && j == 4 ? 4 :
8824 i == Xboom_bug && j == 5 ? 2 :
8825 i == Xboom_bug && j == 6 ? 2 :
8826 i == Xboom_bug && j == 7 ? 0 :
8827 i == Xboom_tank && j == 1 ? 2 :
8828 i == Xboom_tank && j == 2 ? 2 :
8829 i == Xboom_tank && j == 3 ? 4 :
8830 i == Xboom_tank && j == 4 ? 4 :
8831 i == Xboom_tank && j == 5 ? 2 :
8832 i == Xboom_tank && j == 6 ? 2 :
8833 i == Xboom_tank && j == 7 ? 0 :
8834 i == Xboom_android && j == 7 ? 6 :
8835 i == Xboom_1 && j == 1 ? 2 :
8836 i == Xboom_1 && j == 2 ? 2 :
8837 i == Xboom_1 && j == 3 ? 4 :
8838 i == Xboom_1 && j == 4 ? 4 :
8839 i == Xboom_1 && j == 5 ? 6 :
8840 i == Xboom_1 && j == 6 ? 6 :
8841 i == Xboom_1 && j == 7 ? 8 :
8842 i == Xboom_2 && j == 0 ? 8 :
8843 i == Xboom_2 && j == 1 ? 8 :
8844 i == Xboom_2 && j == 2 ? 10 :
8845 i == Xboom_2 && j == 3 ? 10 :
8846 i == Xboom_2 && j == 4 ? 10 :
8847 i == Xboom_2 && j == 5 ? 12 :
8848 i == Xboom_2 && j == 6 ? 12 :
8849 i == Xboom_2 && j == 7 ? 12 :
8850 special_animation && j == 4 ? 3 :
8851 effective_action != action ? 0 :
8853 int frame = getAnimationFrame(g->anim_frames,
8856 g->anim_start_frame,
8859 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8860 g->double_movement && is_backside);
8862 g_em->bitmap = src_bitmap;
8863 g_em->src_x = src_x;
8864 g_em->src_y = src_y;
8865 g_em->src_offset_x = 0;
8866 g_em->src_offset_y = 0;
8867 g_em->dst_offset_x = 0;
8868 g_em->dst_offset_y = 0;
8869 g_em->width = TILEX;
8870 g_em->height = TILEY;
8872 g_em->preserve_background = FALSE;
8874 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8877 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8878 effective_action == ACTION_MOVING ||
8879 effective_action == ACTION_PUSHING ||
8880 effective_action == ACTION_EATING)) ||
8881 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8882 effective_action == ACTION_EMPTYING)))
8885 (effective_action == ACTION_FALLING ||
8886 effective_action == ACTION_FILLING ||
8887 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8888 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8889 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8890 int num_steps = (i == Ydrip_1_s ? 16 :
8891 i == Ydrip_1_sB ? 16 :
8892 i == Ydrip_2_s ? 16 :
8893 i == Ydrip_2_sB ? 16 :
8894 i == Xsand_stonein_1 ? 32 :
8895 i == Xsand_stonein_2 ? 32 :
8896 i == Xsand_stonein_3 ? 32 :
8897 i == Xsand_stonein_4 ? 32 :
8898 i == Xsand_stoneout_1 ? 16 :
8899 i == Xsand_stoneout_2 ? 16 : 8);
8900 int cx = ABS(dx) * (TILEX / num_steps);
8901 int cy = ABS(dy) * (TILEY / num_steps);
8902 int step_frame = (i == Ydrip_2_s ? j + 8 :
8903 i == Ydrip_2_sB ? j + 8 :
8904 i == Xsand_stonein_2 ? j + 8 :
8905 i == Xsand_stonein_3 ? j + 16 :
8906 i == Xsand_stonein_4 ? j + 24 :
8907 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8908 int step = (is_backside ? step_frame : num_steps - step_frame);
8910 if (is_backside) // tile where movement starts
8912 if (dx < 0 || dy < 0)
8914 g_em->src_offset_x = cx * step;
8915 g_em->src_offset_y = cy * step;
8919 g_em->dst_offset_x = cx * step;
8920 g_em->dst_offset_y = cy * step;
8923 else // tile where movement ends
8925 if (dx < 0 || dy < 0)
8927 g_em->dst_offset_x = cx * step;
8928 g_em->dst_offset_y = cy * step;
8932 g_em->src_offset_x = cx * step;
8933 g_em->src_offset_y = cy * step;
8937 g_em->width = TILEX - cx * step;
8938 g_em->height = TILEY - cy * step;
8941 // create unique graphic identifier to decide if tile must be redrawn
8942 /* bit 31 - 16 (16 bit): EM style graphic
8943 bit 15 - 12 ( 4 bit): EM style frame
8944 bit 11 - 6 ( 6 bit): graphic width
8945 bit 5 - 0 ( 6 bit): graphic height */
8946 g_em->unique_identifier =
8947 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8951 for (i = 0; i < GAME_TILE_MAX; i++)
8953 for (j = 0; j < 8; j++)
8955 int element = object_mapping[i].element_rnd;
8956 int action = object_mapping[i].action;
8957 int direction = object_mapping[i].direction;
8958 boolean is_backside = object_mapping[i].is_backside;
8959 int graphic_action = el_act_dir2img(element, action, direction);
8960 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8962 if ((action == ACTION_SMASHED_BY_ROCK ||
8963 action == ACTION_SMASHED_BY_SPRING ||
8964 action == ACTION_EATING) &&
8965 graphic_action == graphic_default)
8967 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8968 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8969 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8970 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8973 // no separate animation for "smashed by rock" -- use rock instead
8974 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8975 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8977 g_em->bitmap = g_xx->bitmap;
8978 g_em->src_x = g_xx->src_x;
8979 g_em->src_y = g_xx->src_y;
8980 g_em->src_offset_x = g_xx->src_offset_x;
8981 g_em->src_offset_y = g_xx->src_offset_y;
8982 g_em->dst_offset_x = g_xx->dst_offset_x;
8983 g_em->dst_offset_y = g_xx->dst_offset_y;
8984 g_em->width = g_xx->width;
8985 g_em->height = g_xx->height;
8986 g_em->unique_identifier = g_xx->unique_identifier;
8989 g_em->preserve_background = TRUE;
8994 for (p = 0; p < MAX_PLAYERS; p++)
8996 for (i = 0; i < PLY_MAX; i++)
8998 int element = player_mapping[p][i].element_rnd;
8999 int action = player_mapping[p][i].action;
9000 int direction = player_mapping[p][i].direction;
9002 for (j = 0; j < 8; j++)
9004 int effective_element = element;
9005 int effective_action = action;
9006 int graphic = (direction == MV_NONE ?
9007 el_act2img(effective_element, effective_action) :
9008 el_act_dir2img(effective_element, effective_action,
9010 struct GraphicInfo *g = &graphic_info[graphic];
9011 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9015 int frame = getAnimationFrame(g->anim_frames,
9018 g->anim_start_frame,
9021 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9023 g_em->bitmap = src_bitmap;
9024 g_em->src_x = src_x;
9025 g_em->src_y = src_y;
9026 g_em->src_offset_x = 0;
9027 g_em->src_offset_y = 0;
9028 g_em->dst_offset_x = 0;
9029 g_em->dst_offset_y = 0;
9030 g_em->width = TILEX;
9031 g_em->height = TILEY;
9037 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9038 boolean any_player_moving,
9039 boolean any_player_snapping,
9040 boolean any_player_dropping)
9042 if (frame == 7 && !any_player_dropping)
9044 if (!local_player->was_waiting)
9046 if (!CheckSaveEngineSnapshotToList())
9049 local_player->was_waiting = TRUE;
9052 else if (any_player_moving || any_player_snapping || any_player_dropping)
9054 local_player->was_waiting = FALSE;
9058 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9059 boolean murphy_is_dropping)
9061 if (murphy_is_waiting)
9063 if (!local_player->was_waiting)
9065 if (!CheckSaveEngineSnapshotToList())
9068 local_player->was_waiting = TRUE;
9073 local_player->was_waiting = FALSE;
9077 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9078 boolean button_released)
9080 if (button_released)
9082 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9083 CheckSaveEngineSnapshotToList();
9085 else if (element_clicked)
9087 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9088 CheckSaveEngineSnapshotToList();
9090 game.snapshot.changed_action = TRUE;
9094 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9095 boolean any_player_moving,
9096 boolean any_player_snapping,
9097 boolean any_player_dropping)
9099 if (tape.single_step && tape.recording && !tape.pausing)
9100 if (frame == 7 && !any_player_dropping)
9101 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9103 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9104 any_player_snapping, any_player_dropping);
9106 return tape.pausing;
9109 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9110 boolean murphy_is_dropping)
9112 boolean murphy_starts_dropping = FALSE;
9115 for (i = 0; i < MAX_PLAYERS; i++)
9116 if (stored_player[i].force_dropping)
9117 murphy_starts_dropping = TRUE;
9119 if (tape.single_step && tape.recording && !tape.pausing)
9120 if (murphy_is_waiting && !murphy_starts_dropping)
9121 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9123 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9126 void CheckSingleStepMode_MM(boolean element_clicked,
9127 boolean button_released)
9129 if (tape.single_step && tape.recording && !tape.pausing)
9130 if (button_released)
9131 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9133 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9136 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9137 int graphic, int sync_frame, int x, int y)
9139 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9141 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9144 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9146 return (IS_NEXT_FRAME(sync_frame, graphic));
9149 int getGraphicInfo_Delay(int graphic)
9151 return graphic_info[graphic].anim_delay;
9154 void PlayMenuSoundExt(int sound)
9156 if (sound == SND_UNDEFINED)
9159 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9160 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9163 if (IS_LOOP_SOUND(sound))
9164 PlaySoundLoop(sound);
9169 void PlayMenuSound(void)
9171 PlayMenuSoundExt(menu.sound[game_status]);
9174 void PlayMenuSoundStereo(int sound, int stereo_position)
9176 if (sound == SND_UNDEFINED)
9179 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9180 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9183 if (IS_LOOP_SOUND(sound))
9184 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9186 PlaySoundStereo(sound, stereo_position);
9189 void PlayMenuSoundIfLoopExt(int sound)
9191 if (sound == SND_UNDEFINED)
9194 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9195 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9198 if (IS_LOOP_SOUND(sound))
9199 PlaySoundLoop(sound);
9202 void PlayMenuSoundIfLoop(void)
9204 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9207 void PlayMenuMusicExt(int music)
9209 if (music == MUS_UNDEFINED)
9212 if (!setup.sound_music)
9215 if (IS_LOOP_MUSIC(music))
9216 PlayMusicLoop(music);
9221 void PlayMenuMusic(void)
9223 char *curr_music = getCurrentlyPlayingMusicFilename();
9224 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9226 if (!strEqual(curr_music, next_music))
9227 PlayMenuMusicExt(menu.music[game_status]);
9230 void PlayMenuSoundsAndMusic(void)
9236 static void FadeMenuSounds(void)
9241 static void FadeMenuMusic(void)
9243 char *curr_music = getCurrentlyPlayingMusicFilename();
9244 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9246 if (!strEqual(curr_music, next_music))
9250 void FadeMenuSoundsAndMusic(void)
9256 void PlaySoundActivating(void)
9259 PlaySound(SND_MENU_ITEM_ACTIVATING);
9263 void PlaySoundSelecting(void)
9266 PlaySound(SND_MENU_ITEM_SELECTING);
9270 void ToggleFullscreenIfNeeded(void)
9272 // if setup and video fullscreen state are already matching, nothing do do
9273 if (setup.fullscreen == video.fullscreen_enabled ||
9274 !video.fullscreen_available)
9277 SDLSetWindowFullscreen(setup.fullscreen);
9279 // set setup value according to successfully changed fullscreen mode
9280 setup.fullscreen = video.fullscreen_enabled;
9283 void ChangeWindowScalingIfNeeded(void)
9285 // if setup and video window scaling are already matching, nothing do do
9286 if (setup.window_scaling_percent == video.window_scaling_percent ||
9287 video.fullscreen_enabled)
9290 SDLSetWindowScaling(setup.window_scaling_percent);
9292 // set setup value according to successfully changed window scaling
9293 setup.window_scaling_percent = video.window_scaling_percent;
9296 void ChangeVsyncModeIfNeeded(void)
9298 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9299 int video_vsync_mode = video.vsync_mode;
9301 // if setup and video vsync mode are already matching, nothing do do
9302 if (setup_vsync_mode == video_vsync_mode)
9305 // if renderer is using OpenGL, vsync mode can directly be changed
9306 SDLSetScreenVsyncMode(setup.vsync_mode);
9308 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9309 if (video.vsync_mode == video_vsync_mode)
9311 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9313 // save backbuffer content which gets lost when re-creating screen
9314 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9316 // force re-creating screen and renderer to set new vsync mode
9317 video.fullscreen_enabled = !setup.fullscreen;
9319 // when creating new renderer, destroy textures linked to old renderer
9320 FreeAllImageTextures(); // needs old renderer to free the textures
9322 // re-create screen and renderer (including change of vsync mode)
9323 ChangeVideoModeIfNeeded(setup.fullscreen);
9325 // set setup value according to successfully changed fullscreen mode
9326 setup.fullscreen = video.fullscreen_enabled;
9328 // restore backbuffer content from temporary backbuffer backup bitmap
9329 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9330 FreeBitmap(tmp_backbuffer);
9332 // update visible window/screen
9333 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9335 // when changing vsync mode, re-create textures for new renderer
9336 InitImageTextures();
9339 // set setup value according to successfully changed vsync mode
9340 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9343 static void JoinRectangles(int *x, int *y, int *width, int *height,
9344 int x2, int y2, int width2, int height2)
9346 // do not join with "off-screen" rectangle
9347 if (x2 == -1 || y2 == -1)
9352 *width = MAX(*width, width2);
9353 *height = MAX(*height, height2);
9356 void SetAnimStatus(int anim_status_new)
9358 if (anim_status_new == GAME_MODE_MAIN)
9359 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9360 else if (anim_status_new == GAME_MODE_NAMES)
9361 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9362 else if (anim_status_new == GAME_MODE_SCORES)
9363 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9365 global.anim_status_next = anim_status_new;
9367 // directly set screen modes that are entered without fading
9368 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9369 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9370 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9371 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9372 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9373 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9374 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9375 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9376 global.anim_status = global.anim_status_next;
9379 void SetGameStatus(int game_status_new)
9381 if (game_status_new != game_status)
9382 game_status_last_screen = game_status;
9384 game_status = game_status_new;
9386 SetAnimStatus(game_status_new);
9389 void SetFontStatus(int game_status_new)
9391 static int last_game_status = -1;
9393 if (game_status_new != -1)
9395 // set game status for font use after storing last game status
9396 last_game_status = game_status;
9397 game_status = game_status_new;
9401 // reset game status after font use from last stored game status
9402 game_status = last_game_status;
9406 void ResetFontStatus(void)
9411 void SetLevelSetInfo(char *identifier, int level_nr)
9413 setString(&levelset.identifier, identifier);
9415 levelset.level_nr = level_nr;
9418 boolean CheckIfAllViewportsHaveChanged(void)
9420 // if game status has not changed, viewports have not changed either
9421 if (game_status == game_status_last)
9424 // check if all viewports have changed with current game status
9426 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9427 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9428 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9429 int new_real_sx = vp_playfield->x;
9430 int new_real_sy = vp_playfield->y;
9431 int new_full_sxsize = vp_playfield->width;
9432 int new_full_sysize = vp_playfield->height;
9433 int new_dx = vp_door_1->x;
9434 int new_dy = vp_door_1->y;
9435 int new_dxsize = vp_door_1->width;
9436 int new_dysize = vp_door_1->height;
9437 int new_vx = vp_door_2->x;
9438 int new_vy = vp_door_2->y;
9439 int new_vxsize = vp_door_2->width;
9440 int new_vysize = vp_door_2->height;
9442 boolean playfield_viewport_has_changed =
9443 (new_real_sx != REAL_SX ||
9444 new_real_sy != REAL_SY ||
9445 new_full_sxsize != FULL_SXSIZE ||
9446 new_full_sysize != FULL_SYSIZE);
9448 boolean door_1_viewport_has_changed =
9451 new_dxsize != DXSIZE ||
9452 new_dysize != DYSIZE);
9454 boolean door_2_viewport_has_changed =
9457 new_vxsize != VXSIZE ||
9458 new_vysize != VYSIZE ||
9459 game_status_last == GAME_MODE_EDITOR);
9461 return (playfield_viewport_has_changed &&
9462 door_1_viewport_has_changed &&
9463 door_2_viewport_has_changed);
9466 boolean CheckFadeAll(void)
9468 return (CheckIfGlobalBorderHasChanged() ||
9469 CheckIfAllViewportsHaveChanged());
9472 void ChangeViewportPropertiesIfNeeded(void)
9474 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9475 FALSE : setup.small_game_graphics);
9476 int gfx_game_mode = game_status;
9477 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9479 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9480 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9481 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9482 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9483 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9484 int new_win_xsize = vp_window->width;
9485 int new_win_ysize = vp_window->height;
9486 int border_left = vp_playfield->border_left;
9487 int border_right = vp_playfield->border_right;
9488 int border_top = vp_playfield->border_top;
9489 int border_bottom = vp_playfield->border_bottom;
9490 int new_sx = vp_playfield->x + border_left;
9491 int new_sy = vp_playfield->y + border_top;
9492 int new_sxsize = vp_playfield->width - border_left - border_right;
9493 int new_sysize = vp_playfield->height - border_top - border_bottom;
9494 int new_real_sx = vp_playfield->x;
9495 int new_real_sy = vp_playfield->y;
9496 int new_full_sxsize = vp_playfield->width;
9497 int new_full_sysize = vp_playfield->height;
9498 int new_dx = vp_door_1->x;
9499 int new_dy = vp_door_1->y;
9500 int new_dxsize = vp_door_1->width;
9501 int new_dysize = vp_door_1->height;
9502 int new_vx = vp_door_2->x;
9503 int new_vy = vp_door_2->y;
9504 int new_vxsize = vp_door_2->width;
9505 int new_vysize = vp_door_2->height;
9506 int new_ex = vp_door_3->x;
9507 int new_ey = vp_door_3->y;
9508 int new_exsize = vp_door_3->width;
9509 int new_eysize = vp_door_3->height;
9510 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9511 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9512 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9513 int new_scr_fieldx = new_sxsize / tilesize;
9514 int new_scr_fieldy = new_sysize / tilesize;
9515 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9516 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9517 boolean init_gfx_buffers = FALSE;
9518 boolean init_video_buffer = FALSE;
9519 boolean init_gadgets_and_anims = FALSE;
9520 boolean init_em_graphics = FALSE;
9522 if (new_win_xsize != WIN_XSIZE ||
9523 new_win_ysize != WIN_YSIZE)
9525 WIN_XSIZE = new_win_xsize;
9526 WIN_YSIZE = new_win_ysize;
9528 init_video_buffer = TRUE;
9529 init_gfx_buffers = TRUE;
9530 init_gadgets_and_anims = TRUE;
9532 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9535 if (new_scr_fieldx != SCR_FIELDX ||
9536 new_scr_fieldy != SCR_FIELDY)
9538 // this always toggles between MAIN and GAME when using small tile size
9540 SCR_FIELDX = new_scr_fieldx;
9541 SCR_FIELDY = new_scr_fieldy;
9543 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9554 new_sxsize != SXSIZE ||
9555 new_sysize != SYSIZE ||
9556 new_dxsize != DXSIZE ||
9557 new_dysize != DYSIZE ||
9558 new_vxsize != VXSIZE ||
9559 new_vysize != VYSIZE ||
9560 new_exsize != EXSIZE ||
9561 new_eysize != EYSIZE ||
9562 new_real_sx != REAL_SX ||
9563 new_real_sy != REAL_SY ||
9564 new_full_sxsize != FULL_SXSIZE ||
9565 new_full_sysize != FULL_SYSIZE ||
9566 new_tilesize_var != TILESIZE_VAR
9569 // ------------------------------------------------------------------------
9570 // determine next fading area for changed viewport definitions
9571 // ------------------------------------------------------------------------
9573 // start with current playfield area (default fading area)
9576 FADE_SXSIZE = FULL_SXSIZE;
9577 FADE_SYSIZE = FULL_SYSIZE;
9579 // add new playfield area if position or size has changed
9580 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9581 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9583 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9584 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9587 // add current and new door 1 area if position or size has changed
9588 if (new_dx != DX || new_dy != DY ||
9589 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9591 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9592 DX, DY, DXSIZE, DYSIZE);
9593 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9594 new_dx, new_dy, new_dxsize, new_dysize);
9597 // add current and new door 2 area if position or size has changed
9598 if (new_vx != VX || new_vy != VY ||
9599 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9601 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9602 VX, VY, VXSIZE, VYSIZE);
9603 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9604 new_vx, new_vy, new_vxsize, new_vysize);
9607 // ------------------------------------------------------------------------
9608 // handle changed tile size
9609 // ------------------------------------------------------------------------
9611 if (new_tilesize_var != TILESIZE_VAR)
9613 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9615 // changing tile size invalidates scroll values of engine snapshots
9616 FreeEngineSnapshotSingle();
9618 // changing tile size requires update of graphic mapping for EM engine
9619 init_em_graphics = TRUE;
9630 SXSIZE = new_sxsize;
9631 SYSIZE = new_sysize;
9632 DXSIZE = new_dxsize;
9633 DYSIZE = new_dysize;
9634 VXSIZE = new_vxsize;
9635 VYSIZE = new_vysize;
9636 EXSIZE = new_exsize;
9637 EYSIZE = new_eysize;
9638 REAL_SX = new_real_sx;
9639 REAL_SY = new_real_sy;
9640 FULL_SXSIZE = new_full_sxsize;
9641 FULL_SYSIZE = new_full_sysize;
9642 TILESIZE_VAR = new_tilesize_var;
9644 init_gfx_buffers = TRUE;
9645 init_gadgets_and_anims = TRUE;
9647 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9648 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9651 if (init_gfx_buffers)
9653 // Debug("tools:viewport", "init_gfx_buffers");
9655 SCR_FIELDX = new_scr_fieldx_buffers;
9656 SCR_FIELDY = new_scr_fieldy_buffers;
9660 SCR_FIELDX = new_scr_fieldx;
9661 SCR_FIELDY = new_scr_fieldy;
9663 SetDrawDeactivationMask(REDRAW_NONE);
9664 SetDrawBackgroundMask(REDRAW_FIELD);
9667 if (init_video_buffer)
9669 // Debug("tools:viewport", "init_video_buffer");
9671 FreeAllImageTextures(); // needs old renderer to free the textures
9673 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9674 InitImageTextures();
9677 if (init_gadgets_and_anims)
9679 // Debug("tools:viewport", "init_gadgets_and_anims");
9682 InitGlobalAnimations();
9685 if (init_em_graphics)
9687 InitGraphicInfo_EM();