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 boolean event_handled = FALSE;
4243 if (game_just_ended)
4245 SetDrawtoField(draw_buffer_last);
4247 HandleGameActions();
4249 SetDrawtoField(DRAW_TO_BACKBUFFER);
4251 if (global.use_envelope_request)
4253 // copy current state of request area to middle of playfield area
4254 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4262 while (NextValidEvent(&event))
4264 event_handled = TRUE;
4268 case EVENT_BUTTONPRESS:
4269 case EVENT_BUTTONRELEASE:
4270 case EVENT_MOTIONNOTIFY:
4274 if (event.type == EVENT_MOTIONNOTIFY)
4279 motion_status = TRUE;
4280 mx = ((MotionEvent *) &event)->x;
4281 my = ((MotionEvent *) &event)->y;
4285 motion_status = FALSE;
4286 mx = ((ButtonEvent *) &event)->x;
4287 my = ((ButtonEvent *) &event)->y;
4288 if (event.type == EVENT_BUTTONPRESS)
4289 button_status = ((ButtonEvent *) &event)->button;
4291 button_status = MB_RELEASED;
4294 // this sets 'request_gadget_id'
4295 HandleGadgets(mx, my, button_status);
4297 switch (request_gadget_id)
4299 case TOOL_CTRL_ID_YES:
4300 case TOOL_CTRL_ID_TOUCH_YES:
4303 case TOOL_CTRL_ID_NO:
4304 case TOOL_CTRL_ID_TOUCH_NO:
4307 case TOOL_CTRL_ID_CONFIRM:
4308 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4309 result = TRUE | FALSE;
4312 case TOOL_CTRL_ID_PLAYER_1:
4315 case TOOL_CTRL_ID_PLAYER_2:
4318 case TOOL_CTRL_ID_PLAYER_3:
4321 case TOOL_CTRL_ID_PLAYER_4:
4326 // only check clickable animations if no request gadget clicked
4327 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4334 case SDL_WINDOWEVENT:
4335 HandleWindowEvent((WindowEvent *) &event);
4338 case SDL_APP_WILLENTERBACKGROUND:
4339 case SDL_APP_DIDENTERBACKGROUND:
4340 case SDL_APP_WILLENTERFOREGROUND:
4341 case SDL_APP_DIDENTERFOREGROUND:
4342 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4345 case EVENT_KEYPRESS:
4347 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4352 if (req_state & REQ_CONFIRM)
4361 #if defined(KSYM_Rewind)
4362 case KSYM_Rewind: // for Amazon Fire TV remote
4371 #if defined(KSYM_FastForward)
4372 case KSYM_FastForward: // for Amazon Fire TV remote
4378 HandleKeysDebug(key, KEY_PRESSED);
4382 if (req_state & REQ_PLAYER)
4384 int old_player_nr = setup.network_player_nr;
4387 result = old_player_nr + 1;
4392 result = old_player_nr + 1;
4423 case EVENT_FINGERRELEASE:
4424 case EVENT_KEYRELEASE:
4425 ClearPlayerAction();
4428 case SDL_CONTROLLERBUTTONDOWN:
4429 switch (event.cbutton.button)
4431 case SDL_CONTROLLER_BUTTON_A:
4432 case SDL_CONTROLLER_BUTTON_X:
4433 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4434 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4438 case SDL_CONTROLLER_BUTTON_B:
4439 case SDL_CONTROLLER_BUTTON_Y:
4440 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4441 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4442 case SDL_CONTROLLER_BUTTON_BACK:
4447 if (req_state & REQ_PLAYER)
4449 int old_player_nr = setup.network_player_nr;
4452 result = old_player_nr + 1;
4454 switch (event.cbutton.button)
4456 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4457 case SDL_CONTROLLER_BUTTON_Y:
4461 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4462 case SDL_CONTROLLER_BUTTON_B:
4466 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4467 case SDL_CONTROLLER_BUTTON_A:
4471 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4472 case SDL_CONTROLLER_BUTTON_X:
4483 case SDL_CONTROLLERBUTTONUP:
4484 HandleJoystickEvent(&event);
4485 ClearPlayerAction();
4489 HandleOtherEvents(&event);
4494 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4496 int joy = AnyJoystick();
4498 if (joy & JOY_BUTTON_1)
4500 else if (joy & JOY_BUTTON_2)
4503 else if (AnyJoystick())
4505 int joy = AnyJoystick();
4507 if (req_state & REQ_PLAYER)
4511 else if (joy & JOY_RIGHT)
4513 else if (joy & JOY_DOWN)
4515 else if (joy & JOY_LEFT)
4522 if (game_just_ended)
4524 if (global.use_envelope_request)
4526 // copy back current state of pressed buttons inside request area
4527 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4535 SetDrawtoField(draw_buffer_last);
4537 game.request_active = FALSE;
4542 static boolean RequestDoor(char *text, unsigned int req_state)
4544 unsigned int old_door_state;
4545 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4546 int font_nr = FONT_TEXT_2;
4551 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4553 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4554 font_nr = FONT_TEXT_1;
4557 if (game_status == GAME_MODE_PLAYING)
4558 BlitScreenToBitmap(backbuffer);
4560 // disable deactivated drawing when quick-loading level tape recording
4561 if (tape.playing && tape.deactivate_display)
4562 TapeDeactivateDisplayOff(TRUE);
4564 SetMouseCursor(CURSOR_DEFAULT);
4566 // pause network game while waiting for request to answer
4567 if (network.enabled &&
4568 game_status == GAME_MODE_PLAYING &&
4569 !game.all_players_gone &&
4570 req_state & REQUEST_WAIT_FOR_INPUT)
4571 SendToServer_PausePlaying();
4573 old_door_state = GetDoorState();
4575 // simulate releasing mouse button over last gadget, if still pressed
4577 HandleGadgets(-1, -1, 0);
4581 // draw released gadget before proceeding
4584 if (old_door_state & DOOR_OPEN_1)
4586 CloseDoor(DOOR_CLOSE_1);
4588 // save old door content
4589 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4590 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4593 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4594 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4596 // clear door drawing field
4597 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4599 // force DOOR font inside door area
4600 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4602 // write text for request
4603 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4605 char text_line[max_request_line_len + 1];
4611 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4613 tc = *(text_ptr + tx);
4614 // if (!tc || tc == ' ')
4615 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4619 if ((tc == '?' || tc == '!') && tl == 0)
4629 strncpy(text_line, text_ptr, tl);
4632 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4633 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4634 text_line, font_nr);
4636 text_ptr += tl + (tc == ' ' ? 1 : 0);
4637 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4642 if (req_state & REQ_ASK)
4644 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4645 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4646 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4647 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4649 else if (req_state & REQ_CONFIRM)
4651 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4652 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4654 else if (req_state & REQ_PLAYER)
4656 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4657 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4658 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4659 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4662 // copy request gadgets to door backbuffer
4663 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4665 OpenDoor(DOOR_OPEN_1);
4667 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4669 if (game_status == GAME_MODE_PLAYING)
4671 SetPanelBackground();
4672 SetDrawBackgroundMask(REDRAW_DOOR_1);
4676 SetDrawBackgroundMask(REDRAW_FIELD);
4682 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4684 // ---------- handle request buttons ----------
4685 result = RequestHandleEvents(req_state);
4689 if (!(req_state & REQ_STAY_OPEN))
4691 CloseDoor(DOOR_CLOSE_1);
4693 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4694 (req_state & REQ_REOPEN))
4695 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4700 if (game_status == GAME_MODE_PLAYING)
4702 SetPanelBackground();
4703 SetDrawBackgroundMask(REDRAW_DOOR_1);
4707 SetDrawBackgroundMask(REDRAW_FIELD);
4710 // continue network game after request
4711 if (network.enabled &&
4712 game_status == GAME_MODE_PLAYING &&
4713 !game.all_players_gone &&
4714 req_state & REQUEST_WAIT_FOR_INPUT)
4715 SendToServer_ContinuePlaying();
4717 // restore deactivated drawing when quick-loading level tape recording
4718 if (tape.playing && tape.deactivate_display)
4719 TapeDeactivateDisplayOn();
4724 static boolean RequestEnvelope(char *text, unsigned int req_state)
4728 if (game_status == GAME_MODE_PLAYING)
4729 BlitScreenToBitmap(backbuffer);
4731 // disable deactivated drawing when quick-loading level tape recording
4732 if (tape.playing && tape.deactivate_display)
4733 TapeDeactivateDisplayOff(TRUE);
4735 SetMouseCursor(CURSOR_DEFAULT);
4737 // pause network game while waiting for request to answer
4738 if (network.enabled &&
4739 game_status == GAME_MODE_PLAYING &&
4740 !game.all_players_gone &&
4741 req_state & REQUEST_WAIT_FOR_INPUT)
4742 SendToServer_PausePlaying();
4744 // simulate releasing mouse button over last gadget, if still pressed
4746 HandleGadgets(-1, -1, 0);
4750 // (replace with setting corresponding request background)
4751 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4752 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4754 // clear door drawing field
4755 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4757 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4759 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4761 if (game_status == GAME_MODE_PLAYING)
4763 SetPanelBackground();
4764 SetDrawBackgroundMask(REDRAW_DOOR_1);
4768 SetDrawBackgroundMask(REDRAW_FIELD);
4774 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4776 // ---------- handle request buttons ----------
4777 result = RequestHandleEvents(req_state);
4781 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4785 if (game_status == GAME_MODE_PLAYING)
4787 SetPanelBackground();
4788 SetDrawBackgroundMask(REDRAW_DOOR_1);
4792 SetDrawBackgroundMask(REDRAW_FIELD);
4795 // continue network game after request
4796 if (network.enabled &&
4797 game_status == GAME_MODE_PLAYING &&
4798 !game.all_players_gone &&
4799 req_state & REQUEST_WAIT_FOR_INPUT)
4800 SendToServer_ContinuePlaying();
4802 // restore deactivated drawing when quick-loading level tape recording
4803 if (tape.playing && tape.deactivate_display)
4804 TapeDeactivateDisplayOn();
4809 boolean Request(char *text, unsigned int req_state)
4811 boolean overlay_enabled = GetOverlayEnabled();
4814 game.request_active_or_moving = TRUE;
4816 SetOverlayEnabled(FALSE);
4818 if (global.use_envelope_request)
4819 result = RequestEnvelope(text, req_state);
4821 result = RequestDoor(text, req_state);
4823 SetOverlayEnabled(overlay_enabled);
4825 game.request_active_or_moving = FALSE;
4830 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4832 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4833 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4836 if (dpo1->sort_priority != dpo2->sort_priority)
4837 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4839 compare_result = dpo1->nr - dpo2->nr;
4841 return compare_result;
4844 void InitGraphicCompatibilityInfo_Doors(void)
4850 struct DoorInfo *door;
4854 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4855 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4857 { -1, -1, -1, NULL }
4859 struct Rect door_rect_list[] =
4861 { DX, DY, DXSIZE, DYSIZE },
4862 { VX, VY, VXSIZE, VYSIZE }
4866 for (i = 0; doors[i].door_token != -1; i++)
4868 int door_token = doors[i].door_token;
4869 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4870 int part_1 = doors[i].part_1;
4871 int part_8 = doors[i].part_8;
4872 int part_2 = part_1 + 1;
4873 int part_3 = part_1 + 2;
4874 struct DoorInfo *door = doors[i].door;
4875 struct Rect *door_rect = &door_rect_list[door_index];
4876 boolean door_gfx_redefined = FALSE;
4878 // check if any door part graphic definitions have been redefined
4880 for (j = 0; door_part_controls[j].door_token != -1; j++)
4882 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4883 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4885 if (dpc->door_token == door_token && fi->redefined)
4886 door_gfx_redefined = TRUE;
4889 // check for old-style door graphic/animation modifications
4891 if (!door_gfx_redefined)
4893 if (door->anim_mode & ANIM_STATIC_PANEL)
4895 door->panel.step_xoffset = 0;
4896 door->panel.step_yoffset = 0;
4899 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4901 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4902 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4903 int num_door_steps, num_panel_steps;
4905 // remove door part graphics other than the two default wings
4907 for (j = 0; door_part_controls[j].door_token != -1; j++)
4909 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4910 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4912 if (dpc->graphic >= part_3 &&
4913 dpc->graphic <= part_8)
4917 // set graphics and screen positions of the default wings
4919 g_part_1->width = door_rect->width;
4920 g_part_1->height = door_rect->height;
4921 g_part_2->width = door_rect->width;
4922 g_part_2->height = door_rect->height;
4923 g_part_2->src_x = door_rect->width;
4924 g_part_2->src_y = g_part_1->src_y;
4926 door->part_2.x = door->part_1.x;
4927 door->part_2.y = door->part_1.y;
4929 if (door->width != -1)
4931 g_part_1->width = door->width;
4932 g_part_2->width = door->width;
4934 // special treatment for graphics and screen position of right wing
4935 g_part_2->src_x += door_rect->width - door->width;
4936 door->part_2.x += door_rect->width - door->width;
4939 if (door->height != -1)
4941 g_part_1->height = door->height;
4942 g_part_2->height = door->height;
4944 // special treatment for graphics and screen position of bottom wing
4945 g_part_2->src_y += door_rect->height - door->height;
4946 door->part_2.y += door_rect->height - door->height;
4949 // set animation delays for the default wings and panels
4951 door->part_1.step_delay = door->step_delay;
4952 door->part_2.step_delay = door->step_delay;
4953 door->panel.step_delay = door->step_delay;
4955 // set animation draw order for the default wings
4957 door->part_1.sort_priority = 2; // draw left wing over ...
4958 door->part_2.sort_priority = 1; // ... right wing
4960 // set animation draw offset for the default wings
4962 if (door->anim_mode & ANIM_HORIZONTAL)
4964 door->part_1.step_xoffset = door->step_offset;
4965 door->part_1.step_yoffset = 0;
4966 door->part_2.step_xoffset = door->step_offset * -1;
4967 door->part_2.step_yoffset = 0;
4969 num_door_steps = g_part_1->width / door->step_offset;
4971 else // ANIM_VERTICAL
4973 door->part_1.step_xoffset = 0;
4974 door->part_1.step_yoffset = door->step_offset;
4975 door->part_2.step_xoffset = 0;
4976 door->part_2.step_yoffset = door->step_offset * -1;
4978 num_door_steps = g_part_1->height / door->step_offset;
4981 // set animation draw offset for the default panels
4983 if (door->step_offset > 1)
4985 num_panel_steps = 2 * door_rect->height / door->step_offset;
4986 door->panel.start_step = num_panel_steps - num_door_steps;
4987 door->panel.start_step_closing = door->panel.start_step;
4991 num_panel_steps = door_rect->height / door->step_offset;
4992 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4993 door->panel.start_step_closing = door->panel.start_step;
4994 door->panel.step_delay *= 2;
5001 void InitDoors(void)
5005 for (i = 0; door_part_controls[i].door_token != -1; i++)
5007 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5008 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5010 // initialize "start_step_opening" and "start_step_closing", if needed
5011 if (dpc->pos->start_step_opening == 0 &&
5012 dpc->pos->start_step_closing == 0)
5014 // dpc->pos->start_step_opening = dpc->pos->start_step;
5015 dpc->pos->start_step_closing = dpc->pos->start_step;
5018 // fill structure for door part draw order (sorted below)
5020 dpo->sort_priority = dpc->pos->sort_priority;
5023 // sort door part controls according to sort_priority and graphic number
5024 qsort(door_part_order, MAX_DOOR_PARTS,
5025 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5028 unsigned int OpenDoor(unsigned int door_state)
5030 if (door_state & DOOR_COPY_BACK)
5032 if (door_state & DOOR_OPEN_1)
5033 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5034 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5036 if (door_state & DOOR_OPEN_2)
5037 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5038 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5040 door_state &= ~DOOR_COPY_BACK;
5043 return MoveDoor(door_state);
5046 unsigned int CloseDoor(unsigned int door_state)
5048 unsigned int old_door_state = GetDoorState();
5050 if (!(door_state & DOOR_NO_COPY_BACK))
5052 if (old_door_state & DOOR_OPEN_1)
5053 BlitBitmap(backbuffer, bitmap_db_door_1,
5054 DX, DY, DXSIZE, DYSIZE, 0, 0);
5056 if (old_door_state & DOOR_OPEN_2)
5057 BlitBitmap(backbuffer, bitmap_db_door_2,
5058 VX, VY, VXSIZE, VYSIZE, 0, 0);
5060 door_state &= ~DOOR_NO_COPY_BACK;
5063 return MoveDoor(door_state);
5066 unsigned int GetDoorState(void)
5068 return MoveDoor(DOOR_GET_STATE);
5071 unsigned int SetDoorState(unsigned int door_state)
5073 return MoveDoor(door_state | DOOR_SET_STATE);
5076 static int euclid(int a, int b)
5078 return (b ? euclid(b, a % b) : a);
5081 unsigned int MoveDoor(unsigned int door_state)
5083 struct Rect door_rect_list[] =
5085 { DX, DY, DXSIZE, DYSIZE },
5086 { VX, VY, VXSIZE, VYSIZE }
5088 static int door1 = DOOR_CLOSE_1;
5089 static int door2 = DOOR_CLOSE_2;
5090 unsigned int door_delay = 0;
5091 unsigned int door_delay_value;
5094 if (door_state == DOOR_GET_STATE)
5095 return (door1 | door2);
5097 if (door_state & DOOR_SET_STATE)
5099 if (door_state & DOOR_ACTION_1)
5100 door1 = door_state & DOOR_ACTION_1;
5101 if (door_state & DOOR_ACTION_2)
5102 door2 = door_state & DOOR_ACTION_2;
5104 return (door1 | door2);
5107 if (!(door_state & DOOR_FORCE_REDRAW))
5109 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5110 door_state &= ~DOOR_OPEN_1;
5111 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5112 door_state &= ~DOOR_CLOSE_1;
5113 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5114 door_state &= ~DOOR_OPEN_2;
5115 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5116 door_state &= ~DOOR_CLOSE_2;
5119 if (global.autoplay_leveldir)
5121 door_state |= DOOR_NO_DELAY;
5122 door_state &= ~DOOR_CLOSE_ALL;
5125 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5126 door_state |= DOOR_NO_DELAY;
5128 if (door_state & DOOR_ACTION)
5130 boolean door_panel_drawn[NUM_DOORS];
5131 boolean panel_has_doors[NUM_DOORS];
5132 boolean door_part_skip[MAX_DOOR_PARTS];
5133 boolean door_part_done[MAX_DOOR_PARTS];
5134 boolean door_part_done_all;
5135 int num_steps[MAX_DOOR_PARTS];
5136 int max_move_delay = 0; // delay for complete animations of all doors
5137 int max_step_delay = 0; // delay (ms) between two animation frames
5138 int num_move_steps = 0; // number of animation steps for all doors
5139 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5140 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5141 int current_move_delay = 0;
5145 for (i = 0; i < NUM_DOORS; i++)
5146 panel_has_doors[i] = FALSE;
5148 for (i = 0; i < MAX_DOOR_PARTS; i++)
5150 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5151 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5152 int door_token = dpc->door_token;
5154 door_part_done[i] = FALSE;
5155 door_part_skip[i] = (!(door_state & door_token) ||
5159 for (i = 0; i < MAX_DOOR_PARTS; i++)
5161 int nr = door_part_order[i].nr;
5162 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5163 struct DoorPartPosInfo *pos = dpc->pos;
5164 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5165 int door_token = dpc->door_token;
5166 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5167 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5168 int step_xoffset = ABS(pos->step_xoffset);
5169 int step_yoffset = ABS(pos->step_yoffset);
5170 int step_delay = pos->step_delay;
5171 int current_door_state = door_state & door_token;
5172 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5173 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5174 boolean part_opening = (is_panel ? door_closing : door_opening);
5175 int start_step = (part_opening ? pos->start_step_opening :
5176 pos->start_step_closing);
5177 float move_xsize = (step_xoffset ? g->width : 0);
5178 float move_ysize = (step_yoffset ? g->height : 0);
5179 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5180 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5181 int move_steps = (move_xsteps && move_ysteps ?
5182 MIN(move_xsteps, move_ysteps) :
5183 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5184 int move_delay = move_steps * step_delay;
5186 if (door_part_skip[nr])
5189 max_move_delay = MAX(max_move_delay, move_delay);
5190 max_step_delay = (max_step_delay == 0 ? step_delay :
5191 euclid(max_step_delay, step_delay));
5192 num_steps[nr] = move_steps;
5196 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5198 panel_has_doors[door_index] = TRUE;
5202 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5204 num_move_steps = max_move_delay / max_step_delay;
5205 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5207 door_delay_value = max_step_delay;
5209 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5211 start = num_move_steps - 1;
5215 // opening door sound has priority over simultaneously closing door
5216 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5218 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5220 if (door_state & DOOR_OPEN_1)
5221 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5222 if (door_state & DOOR_OPEN_2)
5223 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5225 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5227 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5229 if (door_state & DOOR_CLOSE_1)
5230 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5231 if (door_state & DOOR_CLOSE_2)
5232 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5236 for (k = start; k < num_move_steps; k++)
5238 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5240 door_part_done_all = TRUE;
5242 for (i = 0; i < NUM_DOORS; i++)
5243 door_panel_drawn[i] = FALSE;
5245 for (i = 0; i < MAX_DOOR_PARTS; i++)
5247 int nr = door_part_order[i].nr;
5248 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5249 struct DoorPartPosInfo *pos = dpc->pos;
5250 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5251 int door_token = dpc->door_token;
5252 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5253 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5254 boolean is_panel_and_door_has_closed = FALSE;
5255 struct Rect *door_rect = &door_rect_list[door_index];
5256 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5258 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5259 int current_door_state = door_state & door_token;
5260 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5261 boolean door_closing = !door_opening;
5262 boolean part_opening = (is_panel ? door_closing : door_opening);
5263 boolean part_closing = !part_opening;
5264 int start_step = (part_opening ? pos->start_step_opening :
5265 pos->start_step_closing);
5266 int step_delay = pos->step_delay;
5267 int step_factor = step_delay / max_step_delay;
5268 int k1 = (step_factor ? k / step_factor + 1 : k);
5269 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5270 int kk = MAX(0, k2);
5273 int src_x, src_y, src_xx, src_yy;
5274 int dst_x, dst_y, dst_xx, dst_yy;
5277 if (door_part_skip[nr])
5280 if (!(door_state & door_token))
5288 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5289 int kk_door = MAX(0, k2_door);
5290 int sync_frame = kk_door * door_delay_value;
5291 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5293 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5294 &g_src_x, &g_src_y);
5299 if (!door_panel_drawn[door_index])
5301 ClearRectangle(drawto, door_rect->x, door_rect->y,
5302 door_rect->width, door_rect->height);
5304 door_panel_drawn[door_index] = TRUE;
5307 // draw opening or closing door parts
5309 if (pos->step_xoffset < 0) // door part on right side
5312 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5315 if (dst_xx + width > door_rect->width)
5316 width = door_rect->width - dst_xx;
5318 else // door part on left side
5321 dst_xx = pos->x - kk * pos->step_xoffset;
5325 src_xx = ABS(dst_xx);
5329 width = g->width - src_xx;
5331 if (width > door_rect->width)
5332 width = door_rect->width;
5334 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5337 if (pos->step_yoffset < 0) // door part on bottom side
5340 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5343 if (dst_yy + height > door_rect->height)
5344 height = door_rect->height - dst_yy;
5346 else // door part on top side
5349 dst_yy = pos->y - kk * pos->step_yoffset;
5353 src_yy = ABS(dst_yy);
5357 height = g->height - src_yy;
5360 src_x = g_src_x + src_xx;
5361 src_y = g_src_y + src_yy;
5363 dst_x = door_rect->x + dst_xx;
5364 dst_y = door_rect->y + dst_yy;
5366 is_panel_and_door_has_closed =
5369 panel_has_doors[door_index] &&
5370 k >= num_move_steps_doors_only - 1);
5372 if (width >= 0 && width <= g->width &&
5373 height >= 0 && height <= g->height &&
5374 !is_panel_and_door_has_closed)
5376 if (is_panel || !pos->draw_masked)
5377 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5380 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5384 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5386 if ((part_opening && (width < 0 || height < 0)) ||
5387 (part_closing && (width >= g->width && height >= g->height)))
5388 door_part_done[nr] = TRUE;
5390 // continue door part animations, but not panel after door has closed
5391 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5392 door_part_done_all = FALSE;
5395 if (!(door_state & DOOR_NO_DELAY))
5399 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5401 current_move_delay += max_step_delay;
5403 // prevent OS (Windows) from complaining about program not responding
5407 if (door_part_done_all)
5411 if (!(door_state & DOOR_NO_DELAY))
5413 // wait for specified door action post delay
5414 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5415 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5416 else if (door_state & DOOR_ACTION_1)
5417 door_delay_value = door_1.post_delay;
5418 else if (door_state & DOOR_ACTION_2)
5419 door_delay_value = door_2.post_delay;
5421 while (!DelayReached(&door_delay, door_delay_value))
5426 if (door_state & DOOR_ACTION_1)
5427 door1 = door_state & DOOR_ACTION_1;
5428 if (door_state & DOOR_ACTION_2)
5429 door2 = door_state & DOOR_ACTION_2;
5431 // draw masked border over door area
5432 DrawMaskedBorder(REDRAW_DOOR_1);
5433 DrawMaskedBorder(REDRAW_DOOR_2);
5435 ClearAutoRepeatKeyEvents();
5437 return (door1 | door2);
5440 static boolean useSpecialEditorDoor(void)
5442 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5443 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5445 // do not draw special editor door if editor border defined or redefined
5446 if (graphic_info[graphic].bitmap != NULL || redefined)
5449 // do not draw special editor door if global border defined to be empty
5450 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5453 // do not draw special editor door if viewport definitions do not match
5457 EY + EYSIZE != VY + VYSIZE)
5463 void DrawSpecialEditorDoor(void)
5465 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5466 int top_border_width = gfx1->width;
5467 int top_border_height = gfx1->height;
5468 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5469 int ex = EX - outer_border;
5470 int ey = EY - outer_border;
5471 int vy = VY - outer_border;
5472 int exsize = EXSIZE + 2 * outer_border;
5474 if (!useSpecialEditorDoor())
5477 // draw bigger level editor toolbox window
5478 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5479 top_border_width, top_border_height, ex, ey - top_border_height);
5480 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5481 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5483 redraw_mask |= REDRAW_ALL;
5486 void UndrawSpecialEditorDoor(void)
5488 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5489 int top_border_width = gfx1->width;
5490 int top_border_height = gfx1->height;
5491 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5492 int ex = EX - outer_border;
5493 int ey = EY - outer_border;
5494 int ey_top = ey - top_border_height;
5495 int exsize = EXSIZE + 2 * outer_border;
5496 int eysize = EYSIZE + 2 * outer_border;
5498 if (!useSpecialEditorDoor())
5501 // draw normal tape recorder window
5502 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5504 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5505 ex, ey_top, top_border_width, top_border_height,
5507 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5508 ex, ey, exsize, eysize, ex, ey);
5512 // if screen background is set to "[NONE]", clear editor toolbox window
5513 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5514 ClearRectangle(drawto, ex, ey, exsize, eysize);
5517 redraw_mask |= REDRAW_ALL;
5521 // ---------- new tool button stuff -------------------------------------------
5526 struct TextPosInfo *pos;
5528 boolean is_touch_button;
5530 } toolbutton_info[NUM_TOOL_BUTTONS] =
5533 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5534 TOOL_CTRL_ID_YES, FALSE, "yes"
5537 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5538 TOOL_CTRL_ID_NO, FALSE, "no"
5541 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5542 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5545 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5546 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5549 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5550 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5553 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5554 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5557 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5558 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5561 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5562 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5565 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5566 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5569 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5570 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5574 void CreateToolButtons(void)
5578 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5580 int graphic = toolbutton_info[i].graphic;
5581 struct GraphicInfo *gfx = &graphic_info[graphic];
5582 struct TextPosInfo *pos = toolbutton_info[i].pos;
5583 struct GadgetInfo *gi;
5584 Bitmap *deco_bitmap = None;
5585 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5586 unsigned int event_mask = GD_EVENT_RELEASED;
5587 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5588 int base_x = (is_touch_button ? 0 : DX);
5589 int base_y = (is_touch_button ? 0 : DY);
5590 int gd_x = gfx->src_x;
5591 int gd_y = gfx->src_y;
5592 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5593 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5598 if (global.use_envelope_request && !is_touch_button)
5600 setRequestPosition(&base_x, &base_y, TRUE);
5602 // check if request buttons are outside of envelope and fix, if needed
5603 if (x < 0 || x + gfx->width > request.width ||
5604 y < 0 || y + gfx->height > request.height)
5606 if (id == TOOL_CTRL_ID_YES)
5609 y = request.height - 2 * request.border_size - gfx->height;
5611 else if (id == TOOL_CTRL_ID_NO)
5613 x = request.width - 2 * request.border_size - gfx->width;
5614 y = request.height - 2 * request.border_size - gfx->height;
5616 else if (id == TOOL_CTRL_ID_CONFIRM)
5618 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5619 y = request.height - 2 * request.border_size - gfx->height;
5621 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5623 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5625 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5626 y = request.height - 2 * request.border_size - gfx->height * 2;
5628 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5629 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5634 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5636 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5638 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5639 pos->size, &deco_bitmap, &deco_x, &deco_y);
5640 deco_xpos = (gfx->width - pos->size) / 2;
5641 deco_ypos = (gfx->height - pos->size) / 2;
5644 gi = CreateGadget(GDI_CUSTOM_ID, id,
5645 GDI_IMAGE_ID, graphic,
5646 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5649 GDI_WIDTH, gfx->width,
5650 GDI_HEIGHT, gfx->height,
5651 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5652 GDI_STATE, GD_BUTTON_UNPRESSED,
5653 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5654 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5655 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5656 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5657 GDI_DECORATION_SIZE, pos->size, pos->size,
5658 GDI_DECORATION_SHIFTING, 1, 1,
5659 GDI_DIRECT_DRAW, FALSE,
5660 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5661 GDI_EVENT_MASK, event_mask,
5662 GDI_CALLBACK_ACTION, HandleToolButtons,
5666 Fail("cannot create gadget");
5668 tool_gadget[id] = gi;
5672 void FreeToolButtons(void)
5676 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5677 FreeGadget(tool_gadget[i]);
5680 static void UnmapToolButtons(void)
5684 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5685 UnmapGadget(tool_gadget[i]);
5688 static void HandleToolButtons(struct GadgetInfo *gi)
5690 request_gadget_id = gi->custom_id;
5693 static struct Mapping_EM_to_RND_object
5696 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5697 boolean is_backside; // backside of moving element
5703 em_object_mapping_list[GAME_TILE_MAX + 1] =
5706 Zborder, FALSE, FALSE,
5710 Zplayer, FALSE, FALSE,
5719 Ztank, FALSE, FALSE,
5723 Zeater, FALSE, FALSE,
5727 Zdynamite, FALSE, FALSE,
5731 Zboom, FALSE, FALSE,
5736 Xchain, FALSE, FALSE,
5737 EL_DEFAULT, ACTION_EXPLODING, -1
5740 Xboom_bug, FALSE, FALSE,
5741 EL_BUG, ACTION_EXPLODING, -1
5744 Xboom_tank, FALSE, FALSE,
5745 EL_SPACESHIP, ACTION_EXPLODING, -1
5748 Xboom_android, FALSE, FALSE,
5749 EL_EMC_ANDROID, ACTION_OTHER, -1
5752 Xboom_1, FALSE, FALSE,
5753 EL_DEFAULT, ACTION_EXPLODING, -1
5756 Xboom_2, FALSE, FALSE,
5757 EL_DEFAULT, ACTION_EXPLODING, -1
5761 Xblank, TRUE, FALSE,
5766 Xsplash_e, FALSE, FALSE,
5767 EL_ACID_SPLASH_RIGHT, -1, -1
5770 Xsplash_w, FALSE, FALSE,
5771 EL_ACID_SPLASH_LEFT, -1, -1
5775 Xplant, TRUE, FALSE,
5776 EL_EMC_PLANT, -1, -1
5779 Yplant, FALSE, FALSE,
5780 EL_EMC_PLANT, -1, -1
5784 Xacid_1, TRUE, FALSE,
5788 Xacid_2, FALSE, FALSE,
5792 Xacid_3, FALSE, FALSE,
5796 Xacid_4, FALSE, FALSE,
5800 Xacid_5, FALSE, FALSE,
5804 Xacid_6, FALSE, FALSE,
5808 Xacid_7, FALSE, FALSE,
5812 Xacid_8, FALSE, FALSE,
5817 Xfake_acid_1, TRUE, FALSE,
5818 EL_EMC_FAKE_ACID, -1, -1
5821 Xfake_acid_2, FALSE, FALSE,
5822 EL_EMC_FAKE_ACID, -1, -1
5825 Xfake_acid_3, FALSE, FALSE,
5826 EL_EMC_FAKE_ACID, -1, -1
5829 Xfake_acid_4, FALSE, FALSE,
5830 EL_EMC_FAKE_ACID, -1, -1
5833 Xfake_acid_5, FALSE, FALSE,
5834 EL_EMC_FAKE_ACID, -1, -1
5837 Xfake_acid_6, FALSE, FALSE,
5838 EL_EMC_FAKE_ACID, -1, -1
5841 Xfake_acid_7, FALSE, FALSE,
5842 EL_EMC_FAKE_ACID, -1, -1
5845 Xfake_acid_8, FALSE, FALSE,
5846 EL_EMC_FAKE_ACID, -1, -1
5850 Xfake_acid_1_player, FALSE, FALSE,
5851 EL_EMC_FAKE_ACID, -1, -1
5854 Xfake_acid_2_player, FALSE, FALSE,
5855 EL_EMC_FAKE_ACID, -1, -1
5858 Xfake_acid_3_player, FALSE, FALSE,
5859 EL_EMC_FAKE_ACID, -1, -1
5862 Xfake_acid_4_player, FALSE, FALSE,
5863 EL_EMC_FAKE_ACID, -1, -1
5866 Xfake_acid_5_player, FALSE, FALSE,
5867 EL_EMC_FAKE_ACID, -1, -1
5870 Xfake_acid_6_player, FALSE, FALSE,
5871 EL_EMC_FAKE_ACID, -1, -1
5874 Xfake_acid_7_player, FALSE, FALSE,
5875 EL_EMC_FAKE_ACID, -1, -1
5878 Xfake_acid_8_player, FALSE, FALSE,
5879 EL_EMC_FAKE_ACID, -1, -1
5883 Xgrass, TRUE, FALSE,
5884 EL_EMC_GRASS, -1, -1
5887 Ygrass_nB, FALSE, FALSE,
5888 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5891 Ygrass_eB, FALSE, FALSE,
5892 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5895 Ygrass_sB, FALSE, FALSE,
5896 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5899 Ygrass_wB, FALSE, FALSE,
5900 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5908 Ydirt_nB, FALSE, FALSE,
5909 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5912 Ydirt_eB, FALSE, FALSE,
5913 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5916 Ydirt_sB, FALSE, FALSE,
5917 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5920 Ydirt_wB, FALSE, FALSE,
5921 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5925 Xandroid, TRUE, FALSE,
5926 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5929 Xandroid_1_n, FALSE, FALSE,
5930 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5933 Xandroid_2_n, FALSE, FALSE,
5934 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5937 Xandroid_1_e, FALSE, FALSE,
5938 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5941 Xandroid_2_e, FALSE, FALSE,
5942 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5945 Xandroid_1_w, FALSE, FALSE,
5946 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5949 Xandroid_2_w, FALSE, FALSE,
5950 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5953 Xandroid_1_s, FALSE, FALSE,
5954 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5957 Xandroid_2_s, FALSE, FALSE,
5958 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5961 Yandroid_n, FALSE, FALSE,
5962 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5965 Yandroid_nB, FALSE, TRUE,
5966 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5969 Yandroid_ne, FALSE, FALSE,
5970 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5973 Yandroid_neB, FALSE, TRUE,
5974 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5977 Yandroid_e, FALSE, FALSE,
5978 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5981 Yandroid_eB, FALSE, TRUE,
5982 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5985 Yandroid_se, FALSE, FALSE,
5986 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5989 Yandroid_seB, FALSE, TRUE,
5990 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5993 Yandroid_s, FALSE, FALSE,
5994 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5997 Yandroid_sB, FALSE, TRUE,
5998 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6001 Yandroid_sw, FALSE, FALSE,
6002 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6005 Yandroid_swB, FALSE, TRUE,
6006 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6009 Yandroid_w, FALSE, FALSE,
6010 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6013 Yandroid_wB, FALSE, TRUE,
6014 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6017 Yandroid_nw, FALSE, FALSE,
6018 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6021 Yandroid_nwB, FALSE, TRUE,
6022 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6026 Xeater_n, TRUE, FALSE,
6027 EL_YAMYAM_UP, -1, -1
6030 Xeater_e, TRUE, FALSE,
6031 EL_YAMYAM_RIGHT, -1, -1
6034 Xeater_w, TRUE, FALSE,
6035 EL_YAMYAM_LEFT, -1, -1
6038 Xeater_s, TRUE, FALSE,
6039 EL_YAMYAM_DOWN, -1, -1
6042 Yeater_n, FALSE, FALSE,
6043 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6046 Yeater_nB, FALSE, TRUE,
6047 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6050 Yeater_e, FALSE, FALSE,
6051 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6054 Yeater_eB, FALSE, TRUE,
6055 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6058 Yeater_s, FALSE, FALSE,
6059 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6062 Yeater_sB, FALSE, TRUE,
6063 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6066 Yeater_w, FALSE, FALSE,
6067 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6070 Yeater_wB, FALSE, TRUE,
6071 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6074 Yeater_stone, FALSE, FALSE,
6075 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6078 Yeater_spring, FALSE, FALSE,
6079 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6083 Xalien, TRUE, FALSE,
6087 Xalien_pause, FALSE, FALSE,
6091 Yalien_n, FALSE, FALSE,
6092 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6095 Yalien_nB, FALSE, TRUE,
6096 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6099 Yalien_e, FALSE, FALSE,
6100 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6103 Yalien_eB, FALSE, TRUE,
6104 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6107 Yalien_s, FALSE, FALSE,
6108 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6111 Yalien_sB, FALSE, TRUE,
6112 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6115 Yalien_w, FALSE, FALSE,
6116 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6119 Yalien_wB, FALSE, TRUE,
6120 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6123 Yalien_stone, FALSE, FALSE,
6124 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6127 Yalien_spring, FALSE, FALSE,
6128 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6132 Xbug_1_n, TRUE, FALSE,
6136 Xbug_1_e, TRUE, FALSE,
6137 EL_BUG_RIGHT, -1, -1
6140 Xbug_1_s, TRUE, FALSE,
6144 Xbug_1_w, TRUE, FALSE,
6148 Xbug_2_n, FALSE, FALSE,
6152 Xbug_2_e, FALSE, FALSE,
6153 EL_BUG_RIGHT, -1, -1
6156 Xbug_2_s, FALSE, FALSE,
6160 Xbug_2_w, FALSE, FALSE,
6164 Ybug_n, FALSE, FALSE,
6165 EL_BUG, ACTION_MOVING, MV_BIT_UP
6168 Ybug_nB, FALSE, TRUE,
6169 EL_BUG, ACTION_MOVING, MV_BIT_UP
6172 Ybug_e, FALSE, FALSE,
6173 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6176 Ybug_eB, FALSE, TRUE,
6177 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6180 Ybug_s, FALSE, FALSE,
6181 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6184 Ybug_sB, FALSE, TRUE,
6185 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6188 Ybug_w, FALSE, FALSE,
6189 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6192 Ybug_wB, FALSE, TRUE,
6193 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6196 Ybug_w_n, FALSE, FALSE,
6197 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6200 Ybug_n_e, FALSE, FALSE,
6201 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6204 Ybug_e_s, FALSE, FALSE,
6205 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6208 Ybug_s_w, FALSE, FALSE,
6209 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6212 Ybug_e_n, FALSE, FALSE,
6213 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6216 Ybug_s_e, FALSE, FALSE,
6217 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6220 Ybug_w_s, FALSE, FALSE,
6221 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6224 Ybug_n_w, FALSE, FALSE,
6225 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6228 Ybug_stone, FALSE, FALSE,
6229 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6232 Ybug_spring, FALSE, FALSE,
6233 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6237 Xtank_1_n, TRUE, FALSE,
6238 EL_SPACESHIP_UP, -1, -1
6241 Xtank_1_e, TRUE, FALSE,
6242 EL_SPACESHIP_RIGHT, -1, -1
6245 Xtank_1_s, TRUE, FALSE,
6246 EL_SPACESHIP_DOWN, -1, -1
6249 Xtank_1_w, TRUE, FALSE,
6250 EL_SPACESHIP_LEFT, -1, -1
6253 Xtank_2_n, FALSE, FALSE,
6254 EL_SPACESHIP_UP, -1, -1
6257 Xtank_2_e, FALSE, FALSE,
6258 EL_SPACESHIP_RIGHT, -1, -1
6261 Xtank_2_s, FALSE, FALSE,
6262 EL_SPACESHIP_DOWN, -1, -1
6265 Xtank_2_w, FALSE, FALSE,
6266 EL_SPACESHIP_LEFT, -1, -1
6269 Ytank_n, FALSE, FALSE,
6270 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6273 Ytank_nB, FALSE, TRUE,
6274 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6277 Ytank_e, FALSE, FALSE,
6278 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6281 Ytank_eB, FALSE, TRUE,
6282 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6285 Ytank_s, FALSE, FALSE,
6286 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6289 Ytank_sB, FALSE, TRUE,
6290 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6293 Ytank_w, FALSE, FALSE,
6294 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6297 Ytank_wB, FALSE, TRUE,
6298 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6301 Ytank_w_n, FALSE, FALSE,
6302 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6305 Ytank_n_e, FALSE, FALSE,
6306 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6309 Ytank_e_s, FALSE, FALSE,
6310 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6313 Ytank_s_w, FALSE, FALSE,
6314 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6317 Ytank_e_n, FALSE, FALSE,
6318 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6321 Ytank_s_e, FALSE, FALSE,
6322 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6325 Ytank_w_s, FALSE, FALSE,
6326 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6329 Ytank_n_w, FALSE, FALSE,
6330 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6333 Ytank_stone, FALSE, FALSE,
6334 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6337 Ytank_spring, FALSE, FALSE,
6338 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6342 Xemerald, TRUE, FALSE,
6346 Xemerald_pause, FALSE, FALSE,
6350 Xemerald_fall, FALSE, FALSE,
6354 Xemerald_shine, FALSE, FALSE,
6355 EL_EMERALD, ACTION_TWINKLING, -1
6358 Yemerald_s, FALSE, FALSE,
6359 EL_EMERALD, ACTION_FALLING, -1
6362 Yemerald_sB, FALSE, TRUE,
6363 EL_EMERALD, ACTION_FALLING, -1
6366 Yemerald_e, FALSE, FALSE,
6367 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6370 Yemerald_eB, FALSE, TRUE,
6371 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6374 Yemerald_w, FALSE, FALSE,
6375 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6378 Yemerald_wB, FALSE, TRUE,
6379 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6382 Yemerald_blank, FALSE, FALSE,
6383 EL_EMERALD, ACTION_COLLECTING, -1
6387 Xdiamond, TRUE, FALSE,
6391 Xdiamond_pause, FALSE, FALSE,
6395 Xdiamond_fall, FALSE, FALSE,
6399 Xdiamond_shine, FALSE, FALSE,
6400 EL_DIAMOND, ACTION_TWINKLING, -1
6403 Ydiamond_s, FALSE, FALSE,
6404 EL_DIAMOND, ACTION_FALLING, -1
6407 Ydiamond_sB, FALSE, TRUE,
6408 EL_DIAMOND, ACTION_FALLING, -1
6411 Ydiamond_e, FALSE, FALSE,
6412 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6415 Ydiamond_eB, FALSE, TRUE,
6416 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6419 Ydiamond_w, FALSE, FALSE,
6420 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6423 Ydiamond_wB, FALSE, TRUE,
6424 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6427 Ydiamond_blank, FALSE, FALSE,
6428 EL_DIAMOND, ACTION_COLLECTING, -1
6431 Ydiamond_stone, FALSE, FALSE,
6432 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6436 Xstone, TRUE, FALSE,
6440 Xstone_pause, FALSE, FALSE,
6444 Xstone_fall, FALSE, FALSE,
6448 Ystone_s, FALSE, FALSE,
6449 EL_ROCK, ACTION_FALLING, -1
6452 Ystone_sB, FALSE, TRUE,
6453 EL_ROCK, ACTION_FALLING, -1
6456 Ystone_e, FALSE, FALSE,
6457 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6460 Ystone_eB, FALSE, TRUE,
6461 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6464 Ystone_w, FALSE, FALSE,
6465 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6468 Ystone_wB, FALSE, TRUE,
6469 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6477 Xbomb_pause, FALSE, FALSE,
6481 Xbomb_fall, FALSE, FALSE,
6485 Ybomb_s, FALSE, FALSE,
6486 EL_BOMB, ACTION_FALLING, -1
6489 Ybomb_sB, FALSE, TRUE,
6490 EL_BOMB, ACTION_FALLING, -1
6493 Ybomb_e, FALSE, FALSE,
6494 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6497 Ybomb_eB, FALSE, TRUE,
6498 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6501 Ybomb_w, FALSE, FALSE,
6502 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6505 Ybomb_wB, FALSE, TRUE,
6506 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6509 Ybomb_blank, FALSE, FALSE,
6510 EL_BOMB, ACTION_ACTIVATING, -1
6518 Xnut_pause, FALSE, FALSE,
6522 Xnut_fall, FALSE, FALSE,
6526 Ynut_s, FALSE, FALSE,
6527 EL_NUT, ACTION_FALLING, -1
6530 Ynut_sB, FALSE, TRUE,
6531 EL_NUT, ACTION_FALLING, -1
6534 Ynut_e, FALSE, FALSE,
6535 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6538 Ynut_eB, FALSE, TRUE,
6539 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6542 Ynut_w, FALSE, FALSE,
6543 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6546 Ynut_wB, FALSE, TRUE,
6547 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6550 Ynut_stone, FALSE, FALSE,
6551 EL_NUT, ACTION_BREAKING, -1
6555 Xspring, TRUE, FALSE,
6559 Xspring_pause, FALSE, FALSE,
6563 Xspring_e, TRUE, FALSE,
6564 EL_SPRING_RIGHT, -1, -1
6567 Xspring_w, TRUE, FALSE,
6568 EL_SPRING_LEFT, -1, -1
6571 Xspring_fall, FALSE, FALSE,
6575 Yspring_s, FALSE, FALSE,
6576 EL_SPRING, ACTION_FALLING, -1
6579 Yspring_sB, FALSE, TRUE,
6580 EL_SPRING, ACTION_FALLING, -1
6583 Yspring_e, FALSE, FALSE,
6584 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6587 Yspring_eB, FALSE, TRUE,
6588 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6591 Yspring_w, FALSE, FALSE,
6592 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6595 Yspring_wB, FALSE, TRUE,
6596 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6599 Yspring_alien_e, FALSE, FALSE,
6600 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6603 Yspring_alien_eB, FALSE, TRUE,
6604 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6607 Yspring_alien_w, FALSE, FALSE,
6608 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6611 Yspring_alien_wB, FALSE, TRUE,
6612 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6616 Xpush_emerald_e, FALSE, FALSE,
6617 EL_EMERALD, -1, MV_BIT_RIGHT
6620 Xpush_emerald_w, FALSE, FALSE,
6621 EL_EMERALD, -1, MV_BIT_LEFT
6624 Xpush_diamond_e, FALSE, FALSE,
6625 EL_DIAMOND, -1, MV_BIT_RIGHT
6628 Xpush_diamond_w, FALSE, FALSE,
6629 EL_DIAMOND, -1, MV_BIT_LEFT
6632 Xpush_stone_e, FALSE, FALSE,
6633 EL_ROCK, -1, MV_BIT_RIGHT
6636 Xpush_stone_w, FALSE, FALSE,
6637 EL_ROCK, -1, MV_BIT_LEFT
6640 Xpush_bomb_e, FALSE, FALSE,
6641 EL_BOMB, -1, MV_BIT_RIGHT
6644 Xpush_bomb_w, FALSE, FALSE,
6645 EL_BOMB, -1, MV_BIT_LEFT
6648 Xpush_nut_e, FALSE, FALSE,
6649 EL_NUT, -1, MV_BIT_RIGHT
6652 Xpush_nut_w, FALSE, FALSE,
6653 EL_NUT, -1, MV_BIT_LEFT
6656 Xpush_spring_e, FALSE, FALSE,
6657 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6660 Xpush_spring_w, FALSE, FALSE,
6661 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6665 Xdynamite, TRUE, FALSE,
6666 EL_EM_DYNAMITE, -1, -1
6669 Ydynamite_blank, FALSE, FALSE,
6670 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6673 Xdynamite_1, TRUE, FALSE,
6674 EL_EM_DYNAMITE_ACTIVE, -1, -1
6677 Xdynamite_2, FALSE, FALSE,
6678 EL_EM_DYNAMITE_ACTIVE, -1, -1
6681 Xdynamite_3, FALSE, FALSE,
6682 EL_EM_DYNAMITE_ACTIVE, -1, -1
6685 Xdynamite_4, FALSE, FALSE,
6686 EL_EM_DYNAMITE_ACTIVE, -1, -1
6690 Xkey_1, TRUE, FALSE,
6694 Xkey_2, TRUE, FALSE,
6698 Xkey_3, TRUE, FALSE,
6702 Xkey_4, TRUE, FALSE,
6706 Xkey_5, TRUE, FALSE,
6707 EL_EMC_KEY_5, -1, -1
6710 Xkey_6, TRUE, FALSE,
6711 EL_EMC_KEY_6, -1, -1
6714 Xkey_7, TRUE, FALSE,
6715 EL_EMC_KEY_7, -1, -1
6718 Xkey_8, TRUE, FALSE,
6719 EL_EMC_KEY_8, -1, -1
6723 Xdoor_1, TRUE, FALSE,
6724 EL_EM_GATE_1, -1, -1
6727 Xdoor_2, TRUE, FALSE,
6728 EL_EM_GATE_2, -1, -1
6731 Xdoor_3, TRUE, FALSE,
6732 EL_EM_GATE_3, -1, -1
6735 Xdoor_4, TRUE, FALSE,
6736 EL_EM_GATE_4, -1, -1
6739 Xdoor_5, TRUE, FALSE,
6740 EL_EMC_GATE_5, -1, -1
6743 Xdoor_6, TRUE, FALSE,
6744 EL_EMC_GATE_6, -1, -1
6747 Xdoor_7, TRUE, FALSE,
6748 EL_EMC_GATE_7, -1, -1
6751 Xdoor_8, TRUE, FALSE,
6752 EL_EMC_GATE_8, -1, -1
6756 Xfake_door_1, TRUE, FALSE,
6757 EL_EM_GATE_1_GRAY, -1, -1
6760 Xfake_door_2, TRUE, FALSE,
6761 EL_EM_GATE_2_GRAY, -1, -1
6764 Xfake_door_3, TRUE, FALSE,
6765 EL_EM_GATE_3_GRAY, -1, -1
6768 Xfake_door_4, TRUE, FALSE,
6769 EL_EM_GATE_4_GRAY, -1, -1
6772 Xfake_door_5, TRUE, FALSE,
6773 EL_EMC_GATE_5_GRAY, -1, -1
6776 Xfake_door_6, TRUE, FALSE,
6777 EL_EMC_GATE_6_GRAY, -1, -1
6780 Xfake_door_7, TRUE, FALSE,
6781 EL_EMC_GATE_7_GRAY, -1, -1
6784 Xfake_door_8, TRUE, FALSE,
6785 EL_EMC_GATE_8_GRAY, -1, -1
6789 Xballoon, TRUE, FALSE,
6793 Yballoon_n, FALSE, FALSE,
6794 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6797 Yballoon_nB, FALSE, TRUE,
6798 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6801 Yballoon_e, FALSE, FALSE,
6802 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6805 Yballoon_eB, FALSE, TRUE,
6806 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6809 Yballoon_s, FALSE, FALSE,
6810 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6813 Yballoon_sB, FALSE, TRUE,
6814 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6817 Yballoon_w, FALSE, FALSE,
6818 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6821 Yballoon_wB, FALSE, TRUE,
6822 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6826 Xball_1, TRUE, FALSE,
6827 EL_EMC_MAGIC_BALL, -1, -1
6830 Yball_1, FALSE, FALSE,
6831 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6834 Xball_2, FALSE, FALSE,
6835 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6838 Yball_2, FALSE, FALSE,
6839 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6842 Yball_blank, FALSE, FALSE,
6843 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6847 Xamoeba_1, TRUE, FALSE,
6848 EL_AMOEBA_DRY, ACTION_OTHER, -1
6851 Xamoeba_2, FALSE, FALSE,
6852 EL_AMOEBA_DRY, ACTION_OTHER, -1
6855 Xamoeba_3, FALSE, FALSE,
6856 EL_AMOEBA_DRY, ACTION_OTHER, -1
6859 Xamoeba_4, FALSE, FALSE,
6860 EL_AMOEBA_DRY, ACTION_OTHER, -1
6863 Xamoeba_5, TRUE, FALSE,
6864 EL_AMOEBA_WET, ACTION_OTHER, -1
6867 Xamoeba_6, FALSE, FALSE,
6868 EL_AMOEBA_WET, ACTION_OTHER, -1
6871 Xamoeba_7, FALSE, FALSE,
6872 EL_AMOEBA_WET, ACTION_OTHER, -1
6875 Xamoeba_8, FALSE, FALSE,
6876 EL_AMOEBA_WET, ACTION_OTHER, -1
6881 EL_AMOEBA_DROP, ACTION_GROWING, -1
6884 Xdrip_fall, FALSE, FALSE,
6885 EL_AMOEBA_DROP, -1, -1
6888 Xdrip_stretch, FALSE, FALSE,
6889 EL_AMOEBA_DROP, ACTION_FALLING, -1
6892 Xdrip_stretchB, FALSE, TRUE,
6893 EL_AMOEBA_DROP, ACTION_FALLING, -1
6896 Ydrip_1_s, FALSE, FALSE,
6897 EL_AMOEBA_DROP, ACTION_FALLING, -1
6900 Ydrip_1_sB, FALSE, TRUE,
6901 EL_AMOEBA_DROP, ACTION_FALLING, -1
6904 Ydrip_2_s, FALSE, FALSE,
6905 EL_AMOEBA_DROP, ACTION_FALLING, -1
6908 Ydrip_2_sB, FALSE, TRUE,
6909 EL_AMOEBA_DROP, ACTION_FALLING, -1
6913 Xwonderwall, TRUE, FALSE,
6914 EL_MAGIC_WALL, -1, -1
6917 Ywonderwall, FALSE, FALSE,
6918 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6922 Xwheel, TRUE, FALSE,
6923 EL_ROBOT_WHEEL, -1, -1
6926 Ywheel, FALSE, FALSE,
6927 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6931 Xswitch, TRUE, FALSE,
6932 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6935 Yswitch, FALSE, FALSE,
6936 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6940 Xbumper, TRUE, FALSE,
6941 EL_EMC_SPRING_BUMPER, -1, -1
6944 Ybumper, FALSE, FALSE,
6945 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6949 Xacid_nw, TRUE, FALSE,
6950 EL_ACID_POOL_TOPLEFT, -1, -1
6953 Xacid_ne, TRUE, FALSE,
6954 EL_ACID_POOL_TOPRIGHT, -1, -1
6957 Xacid_sw, TRUE, FALSE,
6958 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6961 Xacid_s, TRUE, FALSE,
6962 EL_ACID_POOL_BOTTOM, -1, -1
6965 Xacid_se, TRUE, FALSE,
6966 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6970 Xfake_blank, TRUE, FALSE,
6971 EL_INVISIBLE_WALL, -1, -1
6974 Yfake_blank, FALSE, FALSE,
6975 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6979 Xfake_grass, TRUE, FALSE,
6980 EL_EMC_FAKE_GRASS, -1, -1
6983 Yfake_grass, FALSE, FALSE,
6984 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6988 Xfake_amoeba, TRUE, FALSE,
6989 EL_EMC_DRIPPER, -1, -1
6992 Yfake_amoeba, FALSE, FALSE,
6993 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6997 Xlenses, TRUE, FALSE,
6998 EL_EMC_LENSES, -1, -1
7002 Xmagnify, TRUE, FALSE,
7003 EL_EMC_MAGNIFIER, -1, -1
7008 EL_QUICKSAND_EMPTY, -1, -1
7011 Xsand_stone, TRUE, FALSE,
7012 EL_QUICKSAND_FULL, -1, -1
7015 Xsand_stonein_1, FALSE, TRUE,
7016 EL_ROCK, ACTION_FILLING, -1
7019 Xsand_stonein_2, FALSE, TRUE,
7020 EL_ROCK, ACTION_FILLING, -1
7023 Xsand_stonein_3, FALSE, TRUE,
7024 EL_ROCK, ACTION_FILLING, -1
7027 Xsand_stonein_4, FALSE, TRUE,
7028 EL_ROCK, ACTION_FILLING, -1
7031 Xsand_sandstone_1, FALSE, FALSE,
7032 EL_QUICKSAND_FILLING, -1, -1
7035 Xsand_sandstone_2, FALSE, FALSE,
7036 EL_QUICKSAND_FILLING, -1, -1
7039 Xsand_sandstone_3, FALSE, FALSE,
7040 EL_QUICKSAND_FILLING, -1, -1
7043 Xsand_sandstone_4, FALSE, FALSE,
7044 EL_QUICKSAND_FILLING, -1, -1
7047 Xsand_stonesand_1, FALSE, FALSE,
7048 EL_QUICKSAND_EMPTYING, -1, -1
7051 Xsand_stonesand_2, FALSE, FALSE,
7052 EL_QUICKSAND_EMPTYING, -1, -1
7055 Xsand_stonesand_3, FALSE, FALSE,
7056 EL_QUICKSAND_EMPTYING, -1, -1
7059 Xsand_stonesand_4, FALSE, FALSE,
7060 EL_QUICKSAND_EMPTYING, -1, -1
7063 Xsand_stoneout_1, FALSE, FALSE,
7064 EL_ROCK, ACTION_EMPTYING, -1
7067 Xsand_stoneout_2, FALSE, FALSE,
7068 EL_ROCK, ACTION_EMPTYING, -1
7071 Xsand_stonesand_quickout_1, FALSE, FALSE,
7072 EL_QUICKSAND_EMPTYING, -1, -1
7075 Xsand_stonesand_quickout_2, FALSE, FALSE,
7076 EL_QUICKSAND_EMPTYING, -1, -1
7080 Xslide_ns, TRUE, FALSE,
7081 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7084 Yslide_ns_blank, FALSE, FALSE,
7085 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7088 Xslide_ew, TRUE, FALSE,
7089 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7092 Yslide_ew_blank, FALSE, FALSE,
7093 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7097 Xwind_n, TRUE, FALSE,
7098 EL_BALLOON_SWITCH_UP, -1, -1
7101 Xwind_e, TRUE, FALSE,
7102 EL_BALLOON_SWITCH_RIGHT, -1, -1
7105 Xwind_s, TRUE, FALSE,
7106 EL_BALLOON_SWITCH_DOWN, -1, -1
7109 Xwind_w, TRUE, FALSE,
7110 EL_BALLOON_SWITCH_LEFT, -1, -1
7113 Xwind_any, TRUE, FALSE,
7114 EL_BALLOON_SWITCH_ANY, -1, -1
7117 Xwind_stop, TRUE, FALSE,
7118 EL_BALLOON_SWITCH_NONE, -1, -1
7123 EL_EM_EXIT_CLOSED, -1, -1
7126 Xexit_1, TRUE, FALSE,
7127 EL_EM_EXIT_OPEN, -1, -1
7130 Xexit_2, FALSE, FALSE,
7131 EL_EM_EXIT_OPEN, -1, -1
7134 Xexit_3, FALSE, FALSE,
7135 EL_EM_EXIT_OPEN, -1, -1
7139 Xpause, FALSE, FALSE,
7144 Xwall_1, TRUE, FALSE,
7148 Xwall_2, TRUE, FALSE,
7149 EL_EMC_WALL_14, -1, -1
7152 Xwall_3, TRUE, FALSE,
7153 EL_EMC_WALL_15, -1, -1
7156 Xwall_4, TRUE, FALSE,
7157 EL_EMC_WALL_16, -1, -1
7161 Xroundwall_1, TRUE, FALSE,
7162 EL_WALL_SLIPPERY, -1, -1
7165 Xroundwall_2, TRUE, FALSE,
7166 EL_EMC_WALL_SLIPPERY_2, -1, -1
7169 Xroundwall_3, TRUE, FALSE,
7170 EL_EMC_WALL_SLIPPERY_3, -1, -1
7173 Xroundwall_4, TRUE, FALSE,
7174 EL_EMC_WALL_SLIPPERY_4, -1, -1
7178 Xsteel_1, TRUE, FALSE,
7179 EL_STEELWALL, -1, -1
7182 Xsteel_2, TRUE, FALSE,
7183 EL_EMC_STEELWALL_2, -1, -1
7186 Xsteel_3, TRUE, FALSE,
7187 EL_EMC_STEELWALL_3, -1, -1
7190 Xsteel_4, TRUE, FALSE,
7191 EL_EMC_STEELWALL_4, -1, -1
7195 Xdecor_1, TRUE, FALSE,
7196 EL_EMC_WALL_8, -1, -1
7199 Xdecor_2, TRUE, FALSE,
7200 EL_EMC_WALL_6, -1, -1
7203 Xdecor_3, TRUE, FALSE,
7204 EL_EMC_WALL_4, -1, -1
7207 Xdecor_4, TRUE, FALSE,
7208 EL_EMC_WALL_7, -1, -1
7211 Xdecor_5, TRUE, FALSE,
7212 EL_EMC_WALL_5, -1, -1
7215 Xdecor_6, TRUE, FALSE,
7216 EL_EMC_WALL_9, -1, -1
7219 Xdecor_7, TRUE, FALSE,
7220 EL_EMC_WALL_10, -1, -1
7223 Xdecor_8, TRUE, FALSE,
7224 EL_EMC_WALL_1, -1, -1
7227 Xdecor_9, TRUE, FALSE,
7228 EL_EMC_WALL_2, -1, -1
7231 Xdecor_10, TRUE, FALSE,
7232 EL_EMC_WALL_3, -1, -1
7235 Xdecor_11, TRUE, FALSE,
7236 EL_EMC_WALL_11, -1, -1
7239 Xdecor_12, TRUE, FALSE,
7240 EL_EMC_WALL_12, -1, -1
7244 Xalpha_0, TRUE, FALSE,
7245 EL_CHAR('0'), -1, -1
7248 Xalpha_1, TRUE, FALSE,
7249 EL_CHAR('1'), -1, -1
7252 Xalpha_2, TRUE, FALSE,
7253 EL_CHAR('2'), -1, -1
7256 Xalpha_3, TRUE, FALSE,
7257 EL_CHAR('3'), -1, -1
7260 Xalpha_4, TRUE, FALSE,
7261 EL_CHAR('4'), -1, -1
7264 Xalpha_5, TRUE, FALSE,
7265 EL_CHAR('5'), -1, -1
7268 Xalpha_6, TRUE, FALSE,
7269 EL_CHAR('6'), -1, -1
7272 Xalpha_7, TRUE, FALSE,
7273 EL_CHAR('7'), -1, -1
7276 Xalpha_8, TRUE, FALSE,
7277 EL_CHAR('8'), -1, -1
7280 Xalpha_9, TRUE, FALSE,
7281 EL_CHAR('9'), -1, -1
7284 Xalpha_excla, TRUE, FALSE,
7285 EL_CHAR('!'), -1, -1
7288 Xalpha_apost, TRUE, FALSE,
7289 EL_CHAR('\''), -1, -1
7292 Xalpha_comma, TRUE, FALSE,
7293 EL_CHAR(','), -1, -1
7296 Xalpha_minus, TRUE, FALSE,
7297 EL_CHAR('-'), -1, -1
7300 Xalpha_perio, TRUE, FALSE,
7301 EL_CHAR('.'), -1, -1
7304 Xalpha_colon, TRUE, FALSE,
7305 EL_CHAR(':'), -1, -1
7308 Xalpha_quest, TRUE, FALSE,
7309 EL_CHAR('?'), -1, -1
7312 Xalpha_a, TRUE, FALSE,
7313 EL_CHAR('A'), -1, -1
7316 Xalpha_b, TRUE, FALSE,
7317 EL_CHAR('B'), -1, -1
7320 Xalpha_c, TRUE, FALSE,
7321 EL_CHAR('C'), -1, -1
7324 Xalpha_d, TRUE, FALSE,
7325 EL_CHAR('D'), -1, -1
7328 Xalpha_e, TRUE, FALSE,
7329 EL_CHAR('E'), -1, -1
7332 Xalpha_f, TRUE, FALSE,
7333 EL_CHAR('F'), -1, -1
7336 Xalpha_g, TRUE, FALSE,
7337 EL_CHAR('G'), -1, -1
7340 Xalpha_h, TRUE, FALSE,
7341 EL_CHAR('H'), -1, -1
7344 Xalpha_i, TRUE, FALSE,
7345 EL_CHAR('I'), -1, -1
7348 Xalpha_j, TRUE, FALSE,
7349 EL_CHAR('J'), -1, -1
7352 Xalpha_k, TRUE, FALSE,
7353 EL_CHAR('K'), -1, -1
7356 Xalpha_l, TRUE, FALSE,
7357 EL_CHAR('L'), -1, -1
7360 Xalpha_m, TRUE, FALSE,
7361 EL_CHAR('M'), -1, -1
7364 Xalpha_n, TRUE, FALSE,
7365 EL_CHAR('N'), -1, -1
7368 Xalpha_o, TRUE, FALSE,
7369 EL_CHAR('O'), -1, -1
7372 Xalpha_p, TRUE, FALSE,
7373 EL_CHAR('P'), -1, -1
7376 Xalpha_q, TRUE, FALSE,
7377 EL_CHAR('Q'), -1, -1
7380 Xalpha_r, TRUE, FALSE,
7381 EL_CHAR('R'), -1, -1
7384 Xalpha_s, TRUE, FALSE,
7385 EL_CHAR('S'), -1, -1
7388 Xalpha_t, TRUE, FALSE,
7389 EL_CHAR('T'), -1, -1
7392 Xalpha_u, TRUE, FALSE,
7393 EL_CHAR('U'), -1, -1
7396 Xalpha_v, TRUE, FALSE,
7397 EL_CHAR('V'), -1, -1
7400 Xalpha_w, TRUE, FALSE,
7401 EL_CHAR('W'), -1, -1
7404 Xalpha_x, TRUE, FALSE,
7405 EL_CHAR('X'), -1, -1
7408 Xalpha_y, TRUE, FALSE,
7409 EL_CHAR('Y'), -1, -1
7412 Xalpha_z, TRUE, FALSE,
7413 EL_CHAR('Z'), -1, -1
7416 Xalpha_arrow_e, TRUE, FALSE,
7417 EL_CHAR('>'), -1, -1
7420 Xalpha_arrow_w, TRUE, FALSE,
7421 EL_CHAR('<'), -1, -1
7424 Xalpha_copyr, TRUE, FALSE,
7425 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7429 Ykey_1_blank, FALSE, FALSE,
7430 EL_EM_KEY_1, ACTION_COLLECTING, -1
7433 Ykey_2_blank, FALSE, FALSE,
7434 EL_EM_KEY_2, ACTION_COLLECTING, -1
7437 Ykey_3_blank, FALSE, FALSE,
7438 EL_EM_KEY_3, ACTION_COLLECTING, -1
7441 Ykey_4_blank, FALSE, FALSE,
7442 EL_EM_KEY_4, ACTION_COLLECTING, -1
7445 Ykey_5_blank, FALSE, FALSE,
7446 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7449 Ykey_6_blank, FALSE, FALSE,
7450 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7453 Ykey_7_blank, FALSE, FALSE,
7454 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7457 Ykey_8_blank, FALSE, FALSE,
7458 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7461 Ylenses_blank, FALSE, FALSE,
7462 EL_EMC_LENSES, ACTION_COLLECTING, -1
7465 Ymagnify_blank, FALSE, FALSE,
7466 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7469 Ygrass_blank, FALSE, FALSE,
7470 EL_EMC_GRASS, ACTION_SNAPPING, -1
7473 Ydirt_blank, FALSE, FALSE,
7474 EL_SAND, ACTION_SNAPPING, -1
7483 static struct Mapping_EM_to_RND_player
7492 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7496 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7500 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7504 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7508 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7512 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7516 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7520 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7524 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7528 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7532 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7536 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7540 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7544 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7548 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7552 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7556 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7560 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7564 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7568 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7572 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7576 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7580 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7584 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7588 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7592 EL_PLAYER_1, ACTION_DEFAULT, -1,
7596 EL_PLAYER_2, ACTION_DEFAULT, -1,
7600 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7604 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7608 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7612 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7616 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7620 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7624 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7628 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7632 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7636 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7640 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7644 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7648 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7652 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7656 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7660 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7664 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7668 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7672 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7676 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7680 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7684 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7688 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7692 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7696 EL_PLAYER_3, ACTION_DEFAULT, -1,
7700 EL_PLAYER_4, ACTION_DEFAULT, -1,
7709 int map_element_RND_to_EM_cave(int element_rnd)
7711 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7712 static boolean mapping_initialized = FALSE;
7714 if (!mapping_initialized)
7718 // return "Xalpha_quest" for all undefined elements in mapping array
7719 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7720 mapping_RND_to_EM[i] = Xalpha_quest;
7722 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7723 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7724 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7725 em_object_mapping_list[i].element_em;
7727 mapping_initialized = TRUE;
7730 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7732 Warn("invalid RND level element %d", element_rnd);
7737 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7740 int map_element_EM_to_RND_cave(int element_em_cave)
7742 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7743 static boolean mapping_initialized = FALSE;
7745 if (!mapping_initialized)
7749 // return "EL_UNKNOWN" for all undefined elements in mapping array
7750 for (i = 0; i < GAME_TILE_MAX; i++)
7751 mapping_EM_to_RND[i] = EL_UNKNOWN;
7753 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7754 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7755 em_object_mapping_list[i].element_rnd;
7757 mapping_initialized = TRUE;
7760 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7762 Warn("invalid EM cave element %d", element_em_cave);
7767 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7770 int map_element_EM_to_RND_game(int element_em_game)
7772 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7773 static boolean mapping_initialized = FALSE;
7775 if (!mapping_initialized)
7779 // return "EL_UNKNOWN" for all undefined elements in mapping array
7780 for (i = 0; i < GAME_TILE_MAX; i++)
7781 mapping_EM_to_RND[i] = EL_UNKNOWN;
7783 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7784 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7785 em_object_mapping_list[i].element_rnd;
7787 mapping_initialized = TRUE;
7790 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7792 Warn("invalid EM game element %d", element_em_game);
7797 return mapping_EM_to_RND[element_em_game];
7800 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7802 struct LevelInfo_EM *level_em = level->native_em_level;
7803 struct CAVE *cav = level_em->cav;
7806 for (i = 0; i < GAME_TILE_MAX; i++)
7807 cav->android_array[i] = Cblank;
7809 for (i = 0; i < level->num_android_clone_elements; i++)
7811 int element_rnd = level->android_clone_element[i];
7812 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7814 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7815 if (em_object_mapping_list[j].element_rnd == element_rnd)
7816 cav->android_array[em_object_mapping_list[j].element_em] =
7821 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7823 struct LevelInfo_EM *level_em = level->native_em_level;
7824 struct CAVE *cav = level_em->cav;
7827 level->num_android_clone_elements = 0;
7829 for (i = 0; i < GAME_TILE_MAX; i++)
7831 int element_em_cave = cav->android_array[i];
7833 boolean element_found = FALSE;
7835 if (element_em_cave == Cblank)
7838 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7840 for (j = 0; j < level->num_android_clone_elements; j++)
7841 if (level->android_clone_element[j] == element_rnd)
7842 element_found = TRUE;
7846 level->android_clone_element[level->num_android_clone_elements++] =
7849 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7854 if (level->num_android_clone_elements == 0)
7856 level->num_android_clone_elements = 1;
7857 level->android_clone_element[0] = EL_EMPTY;
7861 int map_direction_RND_to_EM(int direction)
7863 return (direction == MV_UP ? 0 :
7864 direction == MV_RIGHT ? 1 :
7865 direction == MV_DOWN ? 2 :
7866 direction == MV_LEFT ? 3 :
7870 int map_direction_EM_to_RND(int direction)
7872 return (direction == 0 ? MV_UP :
7873 direction == 1 ? MV_RIGHT :
7874 direction == 2 ? MV_DOWN :
7875 direction == 3 ? MV_LEFT :
7879 int map_element_RND_to_SP(int element_rnd)
7881 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7883 if (element_rnd >= EL_SP_START &&
7884 element_rnd <= EL_SP_END)
7885 element_sp = element_rnd - EL_SP_START;
7886 else if (element_rnd == EL_EMPTY_SPACE)
7888 else if (element_rnd == EL_INVISIBLE_WALL)
7894 int map_element_SP_to_RND(int element_sp)
7896 int element_rnd = EL_UNKNOWN;
7898 if (element_sp >= 0x00 &&
7900 element_rnd = EL_SP_START + element_sp;
7901 else if (element_sp == 0x28)
7902 element_rnd = EL_INVISIBLE_WALL;
7907 int map_action_SP_to_RND(int action_sp)
7911 case actActive: return ACTION_ACTIVE;
7912 case actImpact: return ACTION_IMPACT;
7913 case actExploding: return ACTION_EXPLODING;
7914 case actDigging: return ACTION_DIGGING;
7915 case actSnapping: return ACTION_SNAPPING;
7916 case actCollecting: return ACTION_COLLECTING;
7917 case actPassing: return ACTION_PASSING;
7918 case actPushing: return ACTION_PUSHING;
7919 case actDropping: return ACTION_DROPPING;
7921 default: return ACTION_DEFAULT;
7925 int map_element_RND_to_MM(int element_rnd)
7927 return (element_rnd >= EL_MM_START_1 &&
7928 element_rnd <= EL_MM_END_1 ?
7929 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7931 element_rnd >= EL_MM_START_2 &&
7932 element_rnd <= EL_MM_END_2 ?
7933 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7935 element_rnd >= EL_CHAR_START &&
7936 element_rnd <= EL_CHAR_END ?
7937 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7939 element_rnd >= EL_MM_RUNTIME_START &&
7940 element_rnd <= EL_MM_RUNTIME_END ?
7941 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7943 element_rnd >= EL_MM_DUMMY_START &&
7944 element_rnd <= EL_MM_DUMMY_END ?
7945 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7947 EL_MM_EMPTY_NATIVE);
7950 int map_element_MM_to_RND(int element_mm)
7952 return (element_mm == EL_MM_EMPTY_NATIVE ||
7953 element_mm == EL_DF_EMPTY_NATIVE ?
7956 element_mm >= EL_MM_START_1_NATIVE &&
7957 element_mm <= EL_MM_END_1_NATIVE ?
7958 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7960 element_mm >= EL_MM_START_2_NATIVE &&
7961 element_mm <= EL_MM_END_2_NATIVE ?
7962 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7964 element_mm >= EL_MM_CHAR_START_NATIVE &&
7965 element_mm <= EL_MM_CHAR_END_NATIVE ?
7966 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7968 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7969 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7970 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7972 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7973 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7974 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7979 int map_action_MM_to_RND(int action_mm)
7981 // all MM actions are defined to exactly match their RND counterparts
7985 int map_sound_MM_to_RND(int sound_mm)
7989 case SND_MM_GAME_LEVELTIME_CHARGING:
7990 return SND_GAME_LEVELTIME_CHARGING;
7992 case SND_MM_GAME_HEALTH_CHARGING:
7993 return SND_GAME_HEALTH_CHARGING;
7996 return SND_UNDEFINED;
8000 int map_mm_wall_element(int element)
8002 return (element >= EL_MM_STEEL_WALL_START &&
8003 element <= EL_MM_STEEL_WALL_END ?
8006 element >= EL_MM_WOODEN_WALL_START &&
8007 element <= EL_MM_WOODEN_WALL_END ?
8010 element >= EL_MM_ICE_WALL_START &&
8011 element <= EL_MM_ICE_WALL_END ?
8014 element >= EL_MM_AMOEBA_WALL_START &&
8015 element <= EL_MM_AMOEBA_WALL_END ?
8018 element >= EL_DF_STEEL_WALL_START &&
8019 element <= EL_DF_STEEL_WALL_END ?
8022 element >= EL_DF_WOODEN_WALL_START &&
8023 element <= EL_DF_WOODEN_WALL_END ?
8029 int map_mm_wall_element_editor(int element)
8033 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8034 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8035 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8036 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8037 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8038 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8040 default: return element;
8044 int get_next_element(int element)
8048 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8049 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8050 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8051 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8052 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8053 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8054 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8055 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8056 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8057 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8058 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8060 default: return element;
8064 int el2img_mm(int element_mm)
8066 return el2img(map_element_MM_to_RND(element_mm));
8069 int el_act_dir2img(int element, int action, int direction)
8071 element = GFX_ELEMENT(element);
8072 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8074 // direction_graphic[][] == graphic[] for undefined direction graphics
8075 return element_info[element].direction_graphic[action][direction];
8078 static int el_act_dir2crm(int element, int action, int direction)
8080 element = GFX_ELEMENT(element);
8081 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8083 // direction_graphic[][] == graphic[] for undefined direction graphics
8084 return element_info[element].direction_crumbled[action][direction];
8087 int el_act2img(int element, int action)
8089 element = GFX_ELEMENT(element);
8091 return element_info[element].graphic[action];
8094 int el_act2crm(int element, int action)
8096 element = GFX_ELEMENT(element);
8098 return element_info[element].crumbled[action];
8101 int el_dir2img(int element, int direction)
8103 element = GFX_ELEMENT(element);
8105 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8108 int el2baseimg(int element)
8110 return element_info[element].graphic[ACTION_DEFAULT];
8113 int el2img(int element)
8115 element = GFX_ELEMENT(element);
8117 return element_info[element].graphic[ACTION_DEFAULT];
8120 int el2edimg(int element)
8122 element = GFX_ELEMENT(element);
8124 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8127 int el2preimg(int element)
8129 element = GFX_ELEMENT(element);
8131 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8134 int el2panelimg(int element)
8136 element = GFX_ELEMENT(element);
8138 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8141 int font2baseimg(int font_nr)
8143 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8146 int getBeltNrFromBeltElement(int element)
8148 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8149 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8150 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8153 int getBeltNrFromBeltActiveElement(int element)
8155 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8156 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8157 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8160 int getBeltNrFromBeltSwitchElement(int element)
8162 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8163 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8164 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8167 int getBeltDirNrFromBeltElement(int element)
8169 static int belt_base_element[4] =
8171 EL_CONVEYOR_BELT_1_LEFT,
8172 EL_CONVEYOR_BELT_2_LEFT,
8173 EL_CONVEYOR_BELT_3_LEFT,
8174 EL_CONVEYOR_BELT_4_LEFT
8177 int belt_nr = getBeltNrFromBeltElement(element);
8178 int belt_dir_nr = element - belt_base_element[belt_nr];
8180 return (belt_dir_nr % 3);
8183 int getBeltDirNrFromBeltSwitchElement(int element)
8185 static int belt_base_element[4] =
8187 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8188 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8189 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8190 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8193 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8194 int belt_dir_nr = element - belt_base_element[belt_nr];
8196 return (belt_dir_nr % 3);
8199 int getBeltDirFromBeltElement(int element)
8201 static int belt_move_dir[3] =
8208 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8210 return belt_move_dir[belt_dir_nr];
8213 int getBeltDirFromBeltSwitchElement(int element)
8215 static int belt_move_dir[3] =
8222 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8224 return belt_move_dir[belt_dir_nr];
8227 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8229 static int belt_base_element[4] =
8231 EL_CONVEYOR_BELT_1_LEFT,
8232 EL_CONVEYOR_BELT_2_LEFT,
8233 EL_CONVEYOR_BELT_3_LEFT,
8234 EL_CONVEYOR_BELT_4_LEFT
8237 return belt_base_element[belt_nr] + belt_dir_nr;
8240 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8242 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8244 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8247 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8249 static int belt_base_element[4] =
8251 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8252 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8253 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8254 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8257 return belt_base_element[belt_nr] + belt_dir_nr;
8260 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8262 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8264 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8267 boolean swapTiles_EM(boolean is_pre_emc_cave)
8269 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8272 boolean getTeamMode_EM(void)
8274 return game.team_mode || network_playing;
8277 boolean isActivePlayer_EM(int player_nr)
8279 return stored_player[player_nr].active;
8282 unsigned int InitRND(int seed)
8284 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8285 return InitEngineRandom_EM(seed);
8286 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8287 return InitEngineRandom_SP(seed);
8288 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8289 return InitEngineRandom_MM(seed);
8291 return InitEngineRandom_RND(seed);
8294 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8295 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8297 static int get_effective_element_EM(int tile, int frame_em)
8299 int element = object_mapping[tile].element_rnd;
8300 int action = object_mapping[tile].action;
8301 boolean is_backside = object_mapping[tile].is_backside;
8302 boolean action_removing = (action == ACTION_DIGGING ||
8303 action == ACTION_SNAPPING ||
8304 action == ACTION_COLLECTING);
8312 return (frame_em > 5 ? EL_EMPTY : element);
8318 else // frame_em == 7
8329 case Ydiamond_stone:
8333 case Xdrip_stretchB:
8349 case Ymagnify_blank:
8352 case Xsand_stonein_1:
8353 case Xsand_stonein_2:
8354 case Xsand_stonein_3:
8355 case Xsand_stonein_4:
8359 return (is_backside || action_removing ? EL_EMPTY : element);
8364 static boolean check_linear_animation_EM(int tile)
8368 case Xsand_stonesand_1:
8369 case Xsand_stonesand_quickout_1:
8370 case Xsand_sandstone_1:
8371 case Xsand_stonein_1:
8372 case Xsand_stoneout_1:
8400 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8401 boolean has_crumbled_graphics,
8402 int crumbled, int sync_frame)
8404 // if element can be crumbled, but certain action graphics are just empty
8405 // space (like instantly snapping sand to empty space in 1 frame), do not
8406 // treat these empty space graphics as crumbled graphics in EMC engine
8407 if (crumbled == IMG_EMPTY_SPACE)
8408 has_crumbled_graphics = FALSE;
8410 if (has_crumbled_graphics)
8412 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8413 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8414 g_crumbled->anim_delay,
8415 g_crumbled->anim_mode,
8416 g_crumbled->anim_start_frame,
8419 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8420 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8422 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8423 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8425 g_em->has_crumbled_graphics = TRUE;
8429 g_em->crumbled_bitmap = NULL;
8430 g_em->crumbled_src_x = 0;
8431 g_em->crumbled_src_y = 0;
8432 g_em->crumbled_border_size = 0;
8433 g_em->crumbled_tile_size = 0;
8435 g_em->has_crumbled_graphics = FALSE;
8440 void ResetGfxAnimation_EM(int x, int y, int tile)
8446 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8447 int tile, int frame_em, int x, int y)
8449 int action = object_mapping[tile].action;
8450 int direction = object_mapping[tile].direction;
8451 int effective_element = get_effective_element_EM(tile, frame_em);
8452 int graphic = (direction == MV_NONE ?
8453 el_act2img(effective_element, action) :
8454 el_act_dir2img(effective_element, action, direction));
8455 struct GraphicInfo *g = &graphic_info[graphic];
8457 boolean action_removing = (action == ACTION_DIGGING ||
8458 action == ACTION_SNAPPING ||
8459 action == ACTION_COLLECTING);
8460 boolean action_moving = (action == ACTION_FALLING ||
8461 action == ACTION_MOVING ||
8462 action == ACTION_PUSHING ||
8463 action == ACTION_EATING ||
8464 action == ACTION_FILLING ||
8465 action == ACTION_EMPTYING);
8466 boolean action_falling = (action == ACTION_FALLING ||
8467 action == ACTION_FILLING ||
8468 action == ACTION_EMPTYING);
8470 // special case: graphic uses "2nd movement tile" and has defined
8471 // 7 frames for movement animation (or less) => use default graphic
8472 // for last (8th) frame which ends the movement animation
8473 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8475 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8476 graphic = (direction == MV_NONE ?
8477 el_act2img(effective_element, action) :
8478 el_act_dir2img(effective_element, action, direction));
8480 g = &graphic_info[graphic];
8483 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8487 else if (action_moving)
8489 boolean is_backside = object_mapping[tile].is_backside;
8493 int direction = object_mapping[tile].direction;
8494 int move_dir = (action_falling ? MV_DOWN : direction);
8499 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8500 if (g->double_movement && frame_em == 0)
8504 if (move_dir == MV_LEFT)
8505 GfxFrame[x - 1][y] = GfxFrame[x][y];
8506 else if (move_dir == MV_RIGHT)
8507 GfxFrame[x + 1][y] = GfxFrame[x][y];
8508 else if (move_dir == MV_UP)
8509 GfxFrame[x][y - 1] = GfxFrame[x][y];
8510 else if (move_dir == MV_DOWN)
8511 GfxFrame[x][y + 1] = GfxFrame[x][y];
8518 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8519 if (tile == Xsand_stonesand_quickout_1 ||
8520 tile == Xsand_stonesand_quickout_2)
8524 if (graphic_info[graphic].anim_global_sync)
8525 sync_frame = FrameCounter;
8526 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8527 sync_frame = GfxFrame[x][y];
8529 sync_frame = 0; // playfield border (pseudo steel)
8531 SetRandomAnimationValue(x, y);
8533 int frame = getAnimationFrame(g->anim_frames,
8536 g->anim_start_frame,
8539 g_em->unique_identifier =
8540 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8543 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8544 int tile, int frame_em, int x, int y)
8546 int action = object_mapping[tile].action;
8547 int direction = object_mapping[tile].direction;
8548 boolean is_backside = object_mapping[tile].is_backside;
8549 int effective_element = get_effective_element_EM(tile, frame_em);
8550 int effective_action = action;
8551 int graphic = (direction == MV_NONE ?
8552 el_act2img(effective_element, effective_action) :
8553 el_act_dir2img(effective_element, effective_action,
8555 int crumbled = (direction == MV_NONE ?
8556 el_act2crm(effective_element, effective_action) :
8557 el_act_dir2crm(effective_element, effective_action,
8559 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8560 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8561 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8562 struct GraphicInfo *g = &graphic_info[graphic];
8565 // special case: graphic uses "2nd movement tile" and has defined
8566 // 7 frames for movement animation (or less) => use default graphic
8567 // for last (8th) frame which ends the movement animation
8568 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8570 effective_action = ACTION_DEFAULT;
8571 graphic = (direction == MV_NONE ?
8572 el_act2img(effective_element, effective_action) :
8573 el_act_dir2img(effective_element, effective_action,
8575 crumbled = (direction == MV_NONE ?
8576 el_act2crm(effective_element, effective_action) :
8577 el_act_dir2crm(effective_element, effective_action,
8580 g = &graphic_info[graphic];
8583 if (graphic_info[graphic].anim_global_sync)
8584 sync_frame = FrameCounter;
8585 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8586 sync_frame = GfxFrame[x][y];
8588 sync_frame = 0; // playfield border (pseudo steel)
8590 SetRandomAnimationValue(x, y);
8592 int frame = getAnimationFrame(g->anim_frames,
8595 g->anim_start_frame,
8598 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8599 g->double_movement && is_backside);
8601 // (updating the "crumbled" graphic definitions is probably not really needed,
8602 // as animations for crumbled graphics can't be longer than one EMC cycle)
8603 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8607 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8608 int player_nr, int anim, int frame_em)
8610 int element = player_mapping[player_nr][anim].element_rnd;
8611 int action = player_mapping[player_nr][anim].action;
8612 int direction = player_mapping[player_nr][anim].direction;
8613 int graphic = (direction == MV_NONE ?
8614 el_act2img(element, action) :
8615 el_act_dir2img(element, action, direction));
8616 struct GraphicInfo *g = &graphic_info[graphic];
8619 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8621 stored_player[player_nr].StepFrame = frame_em;
8623 sync_frame = stored_player[player_nr].Frame;
8625 int frame = getAnimationFrame(g->anim_frames,
8628 g->anim_start_frame,
8631 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8632 &g_em->src_x, &g_em->src_y, FALSE);
8635 void InitGraphicInfo_EM(void)
8639 // always start with reliable default values
8640 for (i = 0; i < GAME_TILE_MAX; i++)
8642 object_mapping[i].element_rnd = EL_UNKNOWN;
8643 object_mapping[i].is_backside = FALSE;
8644 object_mapping[i].action = ACTION_DEFAULT;
8645 object_mapping[i].direction = MV_NONE;
8648 // always start with reliable default values
8649 for (p = 0; p < MAX_PLAYERS; p++)
8651 for (i = 0; i < PLY_MAX; i++)
8653 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8654 player_mapping[p][i].action = ACTION_DEFAULT;
8655 player_mapping[p][i].direction = MV_NONE;
8659 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8661 int e = em_object_mapping_list[i].element_em;
8663 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8664 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8666 if (em_object_mapping_list[i].action != -1)
8667 object_mapping[e].action = em_object_mapping_list[i].action;
8669 if (em_object_mapping_list[i].direction != -1)
8670 object_mapping[e].direction =
8671 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8674 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8676 int a = em_player_mapping_list[i].action_em;
8677 int p = em_player_mapping_list[i].player_nr;
8679 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8681 if (em_player_mapping_list[i].action != -1)
8682 player_mapping[p][a].action = em_player_mapping_list[i].action;
8684 if (em_player_mapping_list[i].direction != -1)
8685 player_mapping[p][a].direction =
8686 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8689 for (i = 0; i < GAME_TILE_MAX; i++)
8691 int element = object_mapping[i].element_rnd;
8692 int action = object_mapping[i].action;
8693 int direction = object_mapping[i].direction;
8694 boolean is_backside = object_mapping[i].is_backside;
8695 boolean action_exploding = ((action == ACTION_EXPLODING ||
8696 action == ACTION_SMASHED_BY_ROCK ||
8697 action == ACTION_SMASHED_BY_SPRING) &&
8698 element != EL_DIAMOND);
8699 boolean action_active = (action == ACTION_ACTIVE);
8700 boolean action_other = (action == ACTION_OTHER);
8702 for (j = 0; j < 8; j++)
8704 int effective_element = get_effective_element_EM(i, j);
8705 int effective_action = (j < 7 ? action :
8706 i == Xdrip_stretch ? action :
8707 i == Xdrip_stretchB ? action :
8708 i == Ydrip_1_s ? action :
8709 i == Ydrip_1_sB ? action :
8710 i == Yball_1 ? action :
8711 i == Xball_2 ? action :
8712 i == Yball_2 ? action :
8713 i == Yball_blank ? action :
8714 i == Ykey_1_blank ? action :
8715 i == Ykey_2_blank ? action :
8716 i == Ykey_3_blank ? action :
8717 i == Ykey_4_blank ? action :
8718 i == Ykey_5_blank ? action :
8719 i == Ykey_6_blank ? action :
8720 i == Ykey_7_blank ? action :
8721 i == Ykey_8_blank ? action :
8722 i == Ylenses_blank ? action :
8723 i == Ymagnify_blank ? action :
8724 i == Ygrass_blank ? action :
8725 i == Ydirt_blank ? action :
8726 i == Xsand_stonein_1 ? action :
8727 i == Xsand_stonein_2 ? action :
8728 i == Xsand_stonein_3 ? action :
8729 i == Xsand_stonein_4 ? action :
8730 i == Xsand_stoneout_1 ? action :
8731 i == Xsand_stoneout_2 ? action :
8732 i == Xboom_android ? ACTION_EXPLODING :
8733 action_exploding ? ACTION_EXPLODING :
8734 action_active ? action :
8735 action_other ? action :
8737 int graphic = (el_act_dir2img(effective_element, effective_action,
8739 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8741 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8742 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8743 boolean has_action_graphics = (graphic != base_graphic);
8744 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8745 struct GraphicInfo *g = &graphic_info[graphic];
8746 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8749 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8750 boolean special_animation = (action != ACTION_DEFAULT &&
8751 g->anim_frames == 3 &&
8752 g->anim_delay == 2 &&
8753 g->anim_mode & ANIM_LINEAR);
8754 int sync_frame = (i == Xdrip_stretch ? 7 :
8755 i == Xdrip_stretchB ? 7 :
8756 i == Ydrip_2_s ? j + 8 :
8757 i == Ydrip_2_sB ? j + 8 :
8766 i == Xfake_acid_1 ? 0 :
8767 i == Xfake_acid_2 ? 10 :
8768 i == Xfake_acid_3 ? 20 :
8769 i == Xfake_acid_4 ? 30 :
8770 i == Xfake_acid_5 ? 40 :
8771 i == Xfake_acid_6 ? 50 :
8772 i == Xfake_acid_7 ? 60 :
8773 i == Xfake_acid_8 ? 70 :
8774 i == Xfake_acid_1_player ? 0 :
8775 i == Xfake_acid_2_player ? 10 :
8776 i == Xfake_acid_3_player ? 20 :
8777 i == Xfake_acid_4_player ? 30 :
8778 i == Xfake_acid_5_player ? 40 :
8779 i == Xfake_acid_6_player ? 50 :
8780 i == Xfake_acid_7_player ? 60 :
8781 i == Xfake_acid_8_player ? 70 :
8783 i == Yball_2 ? j + 8 :
8784 i == Yball_blank ? j + 1 :
8785 i == Ykey_1_blank ? j + 1 :
8786 i == Ykey_2_blank ? j + 1 :
8787 i == Ykey_3_blank ? j + 1 :
8788 i == Ykey_4_blank ? j + 1 :
8789 i == Ykey_5_blank ? j + 1 :
8790 i == Ykey_6_blank ? j + 1 :
8791 i == Ykey_7_blank ? j + 1 :
8792 i == Ykey_8_blank ? j + 1 :
8793 i == Ylenses_blank ? j + 1 :
8794 i == Ymagnify_blank ? j + 1 :
8795 i == Ygrass_blank ? j + 1 :
8796 i == Ydirt_blank ? j + 1 :
8797 i == Xamoeba_1 ? 0 :
8798 i == Xamoeba_2 ? 1 :
8799 i == Xamoeba_3 ? 2 :
8800 i == Xamoeba_4 ? 3 :
8801 i == Xamoeba_5 ? 0 :
8802 i == Xamoeba_6 ? 1 :
8803 i == Xamoeba_7 ? 2 :
8804 i == Xamoeba_8 ? 3 :
8805 i == Xexit_2 ? j + 8 :
8806 i == Xexit_3 ? j + 16 :
8807 i == Xdynamite_1 ? 0 :
8808 i == Xdynamite_2 ? 8 :
8809 i == Xdynamite_3 ? 16 :
8810 i == Xdynamite_4 ? 24 :
8811 i == Xsand_stonein_1 ? j + 1 :
8812 i == Xsand_stonein_2 ? j + 9 :
8813 i == Xsand_stonein_3 ? j + 17 :
8814 i == Xsand_stonein_4 ? j + 25 :
8815 i == Xsand_stoneout_1 && j == 0 ? 0 :
8816 i == Xsand_stoneout_1 && j == 1 ? 0 :
8817 i == Xsand_stoneout_1 && j == 2 ? 1 :
8818 i == Xsand_stoneout_1 && j == 3 ? 2 :
8819 i == Xsand_stoneout_1 && j == 4 ? 2 :
8820 i == Xsand_stoneout_1 && j == 5 ? 3 :
8821 i == Xsand_stoneout_1 && j == 6 ? 4 :
8822 i == Xsand_stoneout_1 && j == 7 ? 4 :
8823 i == Xsand_stoneout_2 && j == 0 ? 5 :
8824 i == Xsand_stoneout_2 && j == 1 ? 6 :
8825 i == Xsand_stoneout_2 && j == 2 ? 7 :
8826 i == Xsand_stoneout_2 && j == 3 ? 8 :
8827 i == Xsand_stoneout_2 && j == 4 ? 9 :
8828 i == Xsand_stoneout_2 && j == 5 ? 11 :
8829 i == Xsand_stoneout_2 && j == 6 ? 13 :
8830 i == Xsand_stoneout_2 && j == 7 ? 15 :
8831 i == Xboom_bug && j == 1 ? 2 :
8832 i == Xboom_bug && j == 2 ? 2 :
8833 i == Xboom_bug && j == 3 ? 4 :
8834 i == Xboom_bug && j == 4 ? 4 :
8835 i == Xboom_bug && j == 5 ? 2 :
8836 i == Xboom_bug && j == 6 ? 2 :
8837 i == Xboom_bug && j == 7 ? 0 :
8838 i == Xboom_tank && j == 1 ? 2 :
8839 i == Xboom_tank && j == 2 ? 2 :
8840 i == Xboom_tank && j == 3 ? 4 :
8841 i == Xboom_tank && j == 4 ? 4 :
8842 i == Xboom_tank && j == 5 ? 2 :
8843 i == Xboom_tank && j == 6 ? 2 :
8844 i == Xboom_tank && j == 7 ? 0 :
8845 i == Xboom_android && j == 7 ? 6 :
8846 i == Xboom_1 && j == 1 ? 2 :
8847 i == Xboom_1 && j == 2 ? 2 :
8848 i == Xboom_1 && j == 3 ? 4 :
8849 i == Xboom_1 && j == 4 ? 4 :
8850 i == Xboom_1 && j == 5 ? 6 :
8851 i == Xboom_1 && j == 6 ? 6 :
8852 i == Xboom_1 && j == 7 ? 8 :
8853 i == Xboom_2 && j == 0 ? 8 :
8854 i == Xboom_2 && j == 1 ? 8 :
8855 i == Xboom_2 && j == 2 ? 10 :
8856 i == Xboom_2 && j == 3 ? 10 :
8857 i == Xboom_2 && j == 4 ? 10 :
8858 i == Xboom_2 && j == 5 ? 12 :
8859 i == Xboom_2 && j == 6 ? 12 :
8860 i == Xboom_2 && j == 7 ? 12 :
8861 special_animation && j == 4 ? 3 :
8862 effective_action != action ? 0 :
8864 int frame = getAnimationFrame(g->anim_frames,
8867 g->anim_start_frame,
8870 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8871 g->double_movement && is_backside);
8873 g_em->bitmap = src_bitmap;
8874 g_em->src_x = src_x;
8875 g_em->src_y = src_y;
8876 g_em->src_offset_x = 0;
8877 g_em->src_offset_y = 0;
8878 g_em->dst_offset_x = 0;
8879 g_em->dst_offset_y = 0;
8880 g_em->width = TILEX;
8881 g_em->height = TILEY;
8883 g_em->preserve_background = FALSE;
8885 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8888 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8889 effective_action == ACTION_MOVING ||
8890 effective_action == ACTION_PUSHING ||
8891 effective_action == ACTION_EATING)) ||
8892 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8893 effective_action == ACTION_EMPTYING)))
8896 (effective_action == ACTION_FALLING ||
8897 effective_action == ACTION_FILLING ||
8898 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8899 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8900 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8901 int num_steps = (i == Ydrip_1_s ? 16 :
8902 i == Ydrip_1_sB ? 16 :
8903 i == Ydrip_2_s ? 16 :
8904 i == Ydrip_2_sB ? 16 :
8905 i == Xsand_stonein_1 ? 32 :
8906 i == Xsand_stonein_2 ? 32 :
8907 i == Xsand_stonein_3 ? 32 :
8908 i == Xsand_stonein_4 ? 32 :
8909 i == Xsand_stoneout_1 ? 16 :
8910 i == Xsand_stoneout_2 ? 16 : 8);
8911 int cx = ABS(dx) * (TILEX / num_steps);
8912 int cy = ABS(dy) * (TILEY / num_steps);
8913 int step_frame = (i == Ydrip_2_s ? j + 8 :
8914 i == Ydrip_2_sB ? j + 8 :
8915 i == Xsand_stonein_2 ? j + 8 :
8916 i == Xsand_stonein_3 ? j + 16 :
8917 i == Xsand_stonein_4 ? j + 24 :
8918 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8919 int step = (is_backside ? step_frame : num_steps - step_frame);
8921 if (is_backside) // tile where movement starts
8923 if (dx < 0 || dy < 0)
8925 g_em->src_offset_x = cx * step;
8926 g_em->src_offset_y = cy * step;
8930 g_em->dst_offset_x = cx * step;
8931 g_em->dst_offset_y = cy * step;
8934 else // tile where movement ends
8936 if (dx < 0 || dy < 0)
8938 g_em->dst_offset_x = cx * step;
8939 g_em->dst_offset_y = cy * step;
8943 g_em->src_offset_x = cx * step;
8944 g_em->src_offset_y = cy * step;
8948 g_em->width = TILEX - cx * step;
8949 g_em->height = TILEY - cy * step;
8952 // create unique graphic identifier to decide if tile must be redrawn
8953 /* bit 31 - 16 (16 bit): EM style graphic
8954 bit 15 - 12 ( 4 bit): EM style frame
8955 bit 11 - 6 ( 6 bit): graphic width
8956 bit 5 - 0 ( 6 bit): graphic height */
8957 g_em->unique_identifier =
8958 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8962 for (i = 0; i < GAME_TILE_MAX; i++)
8964 for (j = 0; j < 8; j++)
8966 int element = object_mapping[i].element_rnd;
8967 int action = object_mapping[i].action;
8968 int direction = object_mapping[i].direction;
8969 boolean is_backside = object_mapping[i].is_backside;
8970 int graphic_action = el_act_dir2img(element, action, direction);
8971 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8973 if ((action == ACTION_SMASHED_BY_ROCK ||
8974 action == ACTION_SMASHED_BY_SPRING ||
8975 action == ACTION_EATING) &&
8976 graphic_action == graphic_default)
8978 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8979 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8980 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8981 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8984 // no separate animation for "smashed by rock" -- use rock instead
8985 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8986 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8988 g_em->bitmap = g_xx->bitmap;
8989 g_em->src_x = g_xx->src_x;
8990 g_em->src_y = g_xx->src_y;
8991 g_em->src_offset_x = g_xx->src_offset_x;
8992 g_em->src_offset_y = g_xx->src_offset_y;
8993 g_em->dst_offset_x = g_xx->dst_offset_x;
8994 g_em->dst_offset_y = g_xx->dst_offset_y;
8995 g_em->width = g_xx->width;
8996 g_em->height = g_xx->height;
8997 g_em->unique_identifier = g_xx->unique_identifier;
9000 g_em->preserve_background = TRUE;
9005 for (p = 0; p < MAX_PLAYERS; p++)
9007 for (i = 0; i < PLY_MAX; i++)
9009 int element = player_mapping[p][i].element_rnd;
9010 int action = player_mapping[p][i].action;
9011 int direction = player_mapping[p][i].direction;
9013 for (j = 0; j < 8; j++)
9015 int effective_element = element;
9016 int effective_action = action;
9017 int graphic = (direction == MV_NONE ?
9018 el_act2img(effective_element, effective_action) :
9019 el_act_dir2img(effective_element, effective_action,
9021 struct GraphicInfo *g = &graphic_info[graphic];
9022 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9026 int frame = getAnimationFrame(g->anim_frames,
9029 g->anim_start_frame,
9032 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9034 g_em->bitmap = src_bitmap;
9035 g_em->src_x = src_x;
9036 g_em->src_y = src_y;
9037 g_em->src_offset_x = 0;
9038 g_em->src_offset_y = 0;
9039 g_em->dst_offset_x = 0;
9040 g_em->dst_offset_y = 0;
9041 g_em->width = TILEX;
9042 g_em->height = TILEY;
9048 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9049 boolean any_player_moving,
9050 boolean any_player_snapping,
9051 boolean any_player_dropping)
9053 if (frame == 7 && !any_player_dropping)
9055 if (!local_player->was_waiting)
9057 if (!CheckSaveEngineSnapshotToList())
9060 local_player->was_waiting = TRUE;
9063 else if (any_player_moving || any_player_snapping || any_player_dropping)
9065 local_player->was_waiting = FALSE;
9069 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9070 boolean murphy_is_dropping)
9072 if (murphy_is_waiting)
9074 if (!local_player->was_waiting)
9076 if (!CheckSaveEngineSnapshotToList())
9079 local_player->was_waiting = TRUE;
9084 local_player->was_waiting = FALSE;
9088 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9089 boolean button_released)
9091 if (button_released)
9093 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9094 CheckSaveEngineSnapshotToList();
9096 else if (element_clicked)
9098 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9099 CheckSaveEngineSnapshotToList();
9101 game.snapshot.changed_action = TRUE;
9105 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9106 boolean any_player_moving,
9107 boolean any_player_snapping,
9108 boolean any_player_dropping)
9110 if (tape.single_step && tape.recording && !tape.pausing)
9111 if (frame == 7 && !any_player_dropping)
9112 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9114 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9115 any_player_snapping, any_player_dropping);
9117 return tape.pausing;
9120 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9121 boolean murphy_is_dropping)
9123 boolean murphy_starts_dropping = FALSE;
9126 for (i = 0; i < MAX_PLAYERS; i++)
9127 if (stored_player[i].force_dropping)
9128 murphy_starts_dropping = TRUE;
9130 if (tape.single_step && tape.recording && !tape.pausing)
9131 if (murphy_is_waiting && !murphy_starts_dropping)
9132 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9134 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9137 void CheckSingleStepMode_MM(boolean element_clicked,
9138 boolean button_released)
9140 if (tape.single_step && tape.recording && !tape.pausing)
9141 if (button_released)
9142 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9144 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9147 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9148 int graphic, int sync_frame, int x, int y)
9150 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9152 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9155 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9157 return (IS_NEXT_FRAME(sync_frame, graphic));
9160 int getGraphicInfo_Delay(int graphic)
9162 return graphic_info[graphic].anim_delay;
9165 void PlayMenuSoundExt(int sound)
9167 if (sound == SND_UNDEFINED)
9170 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9171 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9174 if (IS_LOOP_SOUND(sound))
9175 PlaySoundLoop(sound);
9180 void PlayMenuSound(void)
9182 PlayMenuSoundExt(menu.sound[game_status]);
9185 void PlayMenuSoundStereo(int sound, int stereo_position)
9187 if (sound == SND_UNDEFINED)
9190 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9191 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9194 if (IS_LOOP_SOUND(sound))
9195 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9197 PlaySoundStereo(sound, stereo_position);
9200 void PlayMenuSoundIfLoopExt(int sound)
9202 if (sound == SND_UNDEFINED)
9205 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9206 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9209 if (IS_LOOP_SOUND(sound))
9210 PlaySoundLoop(sound);
9213 void PlayMenuSoundIfLoop(void)
9215 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9218 void PlayMenuMusicExt(int music)
9220 if (music == MUS_UNDEFINED)
9223 if (!setup.sound_music)
9226 if (IS_LOOP_MUSIC(music))
9227 PlayMusicLoop(music);
9232 void PlayMenuMusic(void)
9234 char *curr_music = getCurrentlyPlayingMusicFilename();
9235 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9237 if (!strEqual(curr_music, next_music))
9238 PlayMenuMusicExt(menu.music[game_status]);
9241 void PlayMenuSoundsAndMusic(void)
9247 static void FadeMenuSounds(void)
9252 static void FadeMenuMusic(void)
9254 char *curr_music = getCurrentlyPlayingMusicFilename();
9255 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9257 if (!strEqual(curr_music, next_music))
9261 void FadeMenuSoundsAndMusic(void)
9267 void PlaySoundActivating(void)
9270 PlaySound(SND_MENU_ITEM_ACTIVATING);
9274 void PlaySoundSelecting(void)
9277 PlaySound(SND_MENU_ITEM_SELECTING);
9281 void ToggleFullscreenIfNeeded(void)
9283 // if setup and video fullscreen state are already matching, nothing do do
9284 if (setup.fullscreen == video.fullscreen_enabled ||
9285 !video.fullscreen_available)
9288 SDLSetWindowFullscreen(setup.fullscreen);
9290 // set setup value according to successfully changed fullscreen mode
9291 setup.fullscreen = video.fullscreen_enabled;
9294 void ChangeWindowScalingIfNeeded(void)
9296 // if setup and video window scaling are already matching, nothing do do
9297 if (setup.window_scaling_percent == video.window_scaling_percent ||
9298 video.fullscreen_enabled)
9301 SDLSetWindowScaling(setup.window_scaling_percent);
9303 // set setup value according to successfully changed window scaling
9304 setup.window_scaling_percent = video.window_scaling_percent;
9307 void ChangeVsyncModeIfNeeded(void)
9309 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9310 int video_vsync_mode = video.vsync_mode;
9312 // if setup and video vsync mode are already matching, nothing do do
9313 if (setup_vsync_mode == video_vsync_mode)
9316 // if renderer is using OpenGL, vsync mode can directly be changed
9317 SDLSetScreenVsyncMode(setup.vsync_mode);
9319 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9320 if (video.vsync_mode == video_vsync_mode)
9322 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9324 // save backbuffer content which gets lost when re-creating screen
9325 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9327 // force re-creating screen and renderer to set new vsync mode
9328 video.fullscreen_enabled = !setup.fullscreen;
9330 // when creating new renderer, destroy textures linked to old renderer
9331 FreeAllImageTextures(); // needs old renderer to free the textures
9333 // re-create screen and renderer (including change of vsync mode)
9334 ChangeVideoModeIfNeeded(setup.fullscreen);
9336 // set setup value according to successfully changed fullscreen mode
9337 setup.fullscreen = video.fullscreen_enabled;
9339 // restore backbuffer content from temporary backbuffer backup bitmap
9340 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9341 FreeBitmap(tmp_backbuffer);
9343 // update visible window/screen
9344 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9346 // when changing vsync mode, re-create textures for new renderer
9347 InitImageTextures();
9350 // set setup value according to successfully changed vsync mode
9351 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9354 static void JoinRectangles(int *x, int *y, int *width, int *height,
9355 int x2, int y2, int width2, int height2)
9357 // do not join with "off-screen" rectangle
9358 if (x2 == -1 || y2 == -1)
9363 *width = MAX(*width, width2);
9364 *height = MAX(*height, height2);
9367 void SetAnimStatus(int anim_status_new)
9369 if (anim_status_new == GAME_MODE_MAIN)
9370 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9371 else if (anim_status_new == GAME_MODE_NAMES)
9372 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9373 else if (anim_status_new == GAME_MODE_SCORES)
9374 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9376 global.anim_status_next = anim_status_new;
9378 // directly set screen modes that are entered without fading
9379 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9380 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9381 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9382 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9383 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9384 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9385 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9386 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9387 global.anim_status = global.anim_status_next;
9390 void SetGameStatus(int game_status_new)
9392 if (game_status_new != game_status)
9393 game_status_last_screen = game_status;
9395 game_status = game_status_new;
9397 SetAnimStatus(game_status_new);
9400 void SetFontStatus(int game_status_new)
9402 static int last_game_status = -1;
9404 if (game_status_new != -1)
9406 // set game status for font use after storing last game status
9407 last_game_status = game_status;
9408 game_status = game_status_new;
9412 // reset game status after font use from last stored game status
9413 game_status = last_game_status;
9417 void ResetFontStatus(void)
9422 void SetLevelSetInfo(char *identifier, int level_nr)
9424 setString(&levelset.identifier, identifier);
9426 levelset.level_nr = level_nr;
9429 boolean CheckIfAllViewportsHaveChanged(void)
9431 // if game status has not changed, viewports have not changed either
9432 if (game_status == game_status_last)
9435 // check if all viewports have changed with current game status
9437 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9438 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9439 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9440 int new_real_sx = vp_playfield->x;
9441 int new_real_sy = vp_playfield->y;
9442 int new_full_sxsize = vp_playfield->width;
9443 int new_full_sysize = vp_playfield->height;
9444 int new_dx = vp_door_1->x;
9445 int new_dy = vp_door_1->y;
9446 int new_dxsize = vp_door_1->width;
9447 int new_dysize = vp_door_1->height;
9448 int new_vx = vp_door_2->x;
9449 int new_vy = vp_door_2->y;
9450 int new_vxsize = vp_door_2->width;
9451 int new_vysize = vp_door_2->height;
9453 boolean playfield_viewport_has_changed =
9454 (new_real_sx != REAL_SX ||
9455 new_real_sy != REAL_SY ||
9456 new_full_sxsize != FULL_SXSIZE ||
9457 new_full_sysize != FULL_SYSIZE);
9459 boolean door_1_viewport_has_changed =
9462 new_dxsize != DXSIZE ||
9463 new_dysize != DYSIZE);
9465 boolean door_2_viewport_has_changed =
9468 new_vxsize != VXSIZE ||
9469 new_vysize != VYSIZE ||
9470 game_status_last == GAME_MODE_EDITOR);
9472 return (playfield_viewport_has_changed &&
9473 door_1_viewport_has_changed &&
9474 door_2_viewport_has_changed);
9477 boolean CheckFadeAll(void)
9479 return (CheckIfGlobalBorderHasChanged() ||
9480 CheckIfAllViewportsHaveChanged());
9483 void ChangeViewportPropertiesIfNeeded(void)
9485 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9486 FALSE : setup.small_game_graphics);
9487 int gfx_game_mode = game_status;
9488 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9490 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9491 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9492 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9493 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9494 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9495 int new_win_xsize = vp_window->width;
9496 int new_win_ysize = vp_window->height;
9497 int border_left = vp_playfield->border_left;
9498 int border_right = vp_playfield->border_right;
9499 int border_top = vp_playfield->border_top;
9500 int border_bottom = vp_playfield->border_bottom;
9501 int new_sx = vp_playfield->x + border_left;
9502 int new_sy = vp_playfield->y + border_top;
9503 int new_sxsize = vp_playfield->width - border_left - border_right;
9504 int new_sysize = vp_playfield->height - border_top - border_bottom;
9505 int new_real_sx = vp_playfield->x;
9506 int new_real_sy = vp_playfield->y;
9507 int new_full_sxsize = vp_playfield->width;
9508 int new_full_sysize = vp_playfield->height;
9509 int new_dx = vp_door_1->x;
9510 int new_dy = vp_door_1->y;
9511 int new_dxsize = vp_door_1->width;
9512 int new_dysize = vp_door_1->height;
9513 int new_vx = vp_door_2->x;
9514 int new_vy = vp_door_2->y;
9515 int new_vxsize = vp_door_2->width;
9516 int new_vysize = vp_door_2->height;
9517 int new_ex = vp_door_3->x;
9518 int new_ey = vp_door_3->y;
9519 int new_exsize = vp_door_3->width;
9520 int new_eysize = vp_door_3->height;
9521 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9522 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9523 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9524 int new_scr_fieldx = new_sxsize / tilesize;
9525 int new_scr_fieldy = new_sysize / tilesize;
9526 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9527 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9528 boolean init_gfx_buffers = FALSE;
9529 boolean init_video_buffer = FALSE;
9530 boolean init_gadgets_and_anims = FALSE;
9531 boolean init_em_graphics = FALSE;
9533 if (new_win_xsize != WIN_XSIZE ||
9534 new_win_ysize != WIN_YSIZE)
9536 WIN_XSIZE = new_win_xsize;
9537 WIN_YSIZE = new_win_ysize;
9539 init_video_buffer = TRUE;
9540 init_gfx_buffers = TRUE;
9541 init_gadgets_and_anims = TRUE;
9543 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9546 if (new_scr_fieldx != SCR_FIELDX ||
9547 new_scr_fieldy != SCR_FIELDY)
9549 // this always toggles between MAIN and GAME when using small tile size
9551 SCR_FIELDX = new_scr_fieldx;
9552 SCR_FIELDY = new_scr_fieldy;
9554 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9565 new_sxsize != SXSIZE ||
9566 new_sysize != SYSIZE ||
9567 new_dxsize != DXSIZE ||
9568 new_dysize != DYSIZE ||
9569 new_vxsize != VXSIZE ||
9570 new_vysize != VYSIZE ||
9571 new_exsize != EXSIZE ||
9572 new_eysize != EYSIZE ||
9573 new_real_sx != REAL_SX ||
9574 new_real_sy != REAL_SY ||
9575 new_full_sxsize != FULL_SXSIZE ||
9576 new_full_sysize != FULL_SYSIZE ||
9577 new_tilesize_var != TILESIZE_VAR
9580 // ------------------------------------------------------------------------
9581 // determine next fading area for changed viewport definitions
9582 // ------------------------------------------------------------------------
9584 // start with current playfield area (default fading area)
9587 FADE_SXSIZE = FULL_SXSIZE;
9588 FADE_SYSIZE = FULL_SYSIZE;
9590 // add new playfield area if position or size has changed
9591 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9592 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9594 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9595 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9598 // add current and new door 1 area if position or size has changed
9599 if (new_dx != DX || new_dy != DY ||
9600 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9602 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9603 DX, DY, DXSIZE, DYSIZE);
9604 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9605 new_dx, new_dy, new_dxsize, new_dysize);
9608 // add current and new door 2 area if position or size has changed
9609 if (new_vx != VX || new_vy != VY ||
9610 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9612 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9613 VX, VY, VXSIZE, VYSIZE);
9614 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9615 new_vx, new_vy, new_vxsize, new_vysize);
9618 // ------------------------------------------------------------------------
9619 // handle changed tile size
9620 // ------------------------------------------------------------------------
9622 if (new_tilesize_var != TILESIZE_VAR)
9624 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9626 // changing tile size invalidates scroll values of engine snapshots
9627 FreeEngineSnapshotSingle();
9629 // changing tile size requires update of graphic mapping for EM engine
9630 init_em_graphics = TRUE;
9641 SXSIZE = new_sxsize;
9642 SYSIZE = new_sysize;
9643 DXSIZE = new_dxsize;
9644 DYSIZE = new_dysize;
9645 VXSIZE = new_vxsize;
9646 VYSIZE = new_vysize;
9647 EXSIZE = new_exsize;
9648 EYSIZE = new_eysize;
9649 REAL_SX = new_real_sx;
9650 REAL_SY = new_real_sy;
9651 FULL_SXSIZE = new_full_sxsize;
9652 FULL_SYSIZE = new_full_sysize;
9653 TILESIZE_VAR = new_tilesize_var;
9655 init_gfx_buffers = TRUE;
9656 init_gadgets_and_anims = TRUE;
9658 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9659 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9662 if (init_gfx_buffers)
9664 // Debug("tools:viewport", "init_gfx_buffers");
9666 SCR_FIELDX = new_scr_fieldx_buffers;
9667 SCR_FIELDY = new_scr_fieldy_buffers;
9671 SCR_FIELDX = new_scr_fieldx;
9672 SCR_FIELDY = new_scr_fieldy;
9674 SetDrawDeactivationMask(REDRAW_NONE);
9675 SetDrawBackgroundMask(REDRAW_FIELD);
9678 if (init_video_buffer)
9680 // Debug("tools:viewport", "init_video_buffer");
9682 FreeAllImageTextures(); // needs old renderer to free the textures
9684 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9685 InitImageTextures();
9688 if (init_gadgets_and_anims)
9690 // Debug("tools:viewport", "init_gadgets_and_anims");
9693 InitGlobalAnimations();
9696 if (init_em_graphics)
9698 InitGraphicInfo_EM();