1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 void DumpTile(int x, int y)
401 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
404 if (!IN_LEV_FIELD(x, y))
406 Info("(not in level field)");
412 token_name = element_info[Tile[x][y]].token_name;
414 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
415 Info("Back: %s", print_if_not_empty(Back[x][y]));
416 Info("Store: %s", print_if_not_empty(Store[x][y]));
417 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
418 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
419 Info("MovPos: %d", MovPos[x][y]);
420 Info("MovDir: %d", MovDir[x][y]);
421 Info("MovDelay: %d", MovDelay[x][y]);
422 Info("ChangeDelay: %d", ChangeDelay[x][y]);
423 Info("CustomValue: %d", CustomValue[x][y]);
424 Info("GfxElement: %d", GfxElement[x][y]);
425 Info("GfxAction: %d", GfxAction[x][y]);
426 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
427 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
431 void DumpTileFromScreen(int sx, int sy)
433 int lx = getLevelFromScreenX(sx);
434 int ly = getLevelFromScreenY(sy);
439 void SetDrawtoField(int mode)
441 if (mode == DRAW_TO_FIELDBUFFER)
447 BX2 = SCR_FIELDX + 1;
448 BY2 = SCR_FIELDY + 1;
450 drawto_field = fieldbuffer;
452 else // DRAW_TO_BACKBUFFER
458 BX2 = SCR_FIELDX - 1;
459 BY2 = SCR_FIELDY - 1;
461 drawto_field = backbuffer;
465 int GetDrawtoField(void)
467 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
470 static void RedrawPlayfield_RND(void)
472 if (game.envelope_active)
475 DrawLevel(REDRAW_ALL);
479 void RedrawPlayfield(void)
481 if (game_status != GAME_MODE_PLAYING)
484 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
485 RedrawPlayfield_EM(TRUE);
486 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
487 RedrawPlayfield_SP(TRUE);
488 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
489 RedrawPlayfield_MM();
490 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
491 RedrawPlayfield_RND();
493 BlitScreenToBitmap(backbuffer);
495 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
499 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
502 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
503 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
505 if (x == -1 && y == -1)
508 if (draw_target == DRAW_TO_SCREEN)
509 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
511 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
514 static void DrawMaskedBorderExt_FIELD(int draw_target)
516 if (global.border_status >= GAME_MODE_MAIN &&
517 global.border_status <= GAME_MODE_PLAYING &&
518 border.draw_masked[global.border_status])
519 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
523 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
525 // when drawing to backbuffer, never draw border over open doors
526 if (draw_target == DRAW_TO_BACKBUFFER &&
527 (GetDoorState() & DOOR_OPEN_1))
530 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
531 (global.border_status != GAME_MODE_EDITOR ||
532 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
533 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
536 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
538 // when drawing to backbuffer, never draw border over open doors
539 if (draw_target == DRAW_TO_BACKBUFFER &&
540 (GetDoorState() & DOOR_OPEN_2))
543 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
544 global.border_status != GAME_MODE_EDITOR)
545 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
550 // currently not available
553 static void DrawMaskedBorderExt_ALL(int draw_target)
555 DrawMaskedBorderExt_FIELD(draw_target);
556 DrawMaskedBorderExt_DOOR_1(draw_target);
557 DrawMaskedBorderExt_DOOR_2(draw_target);
558 DrawMaskedBorderExt_DOOR_3(draw_target);
561 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
563 // never draw masked screen borders on borderless screens
564 if (global.border_status == GAME_MODE_LOADING ||
565 global.border_status == GAME_MODE_TITLE)
568 if (redraw_mask & REDRAW_ALL)
569 DrawMaskedBorderExt_ALL(draw_target);
572 if (redraw_mask & REDRAW_FIELD)
573 DrawMaskedBorderExt_FIELD(draw_target);
574 if (redraw_mask & REDRAW_DOOR_1)
575 DrawMaskedBorderExt_DOOR_1(draw_target);
576 if (redraw_mask & REDRAW_DOOR_2)
577 DrawMaskedBorderExt_DOOR_2(draw_target);
578 if (redraw_mask & REDRAW_DOOR_3)
579 DrawMaskedBorderExt_DOOR_3(draw_target);
583 void DrawMaskedBorder_FIELD(void)
585 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
588 void DrawMaskedBorder(int redraw_mask)
590 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
593 void DrawMaskedBorderToTarget(int draw_target)
595 if (draw_target == DRAW_TO_BACKBUFFER ||
596 draw_target == DRAW_TO_SCREEN)
598 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
602 int last_border_status = global.border_status;
604 if (draw_target == DRAW_TO_FADE_SOURCE)
606 global.border_status = gfx.fade_border_source_status;
607 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
609 else if (draw_target == DRAW_TO_FADE_TARGET)
611 global.border_status = gfx.fade_border_target_status;
612 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
615 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
617 global.border_status = last_border_status;
618 gfx.masked_border_bitmap_ptr = backbuffer;
622 void DrawTileCursor(int draw_target)
624 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
627 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
629 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
632 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
634 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
635 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
637 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
640 void BlitScreenToBitmap(Bitmap *target_bitmap)
642 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
643 BlitScreenToBitmap_EM(target_bitmap);
644 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
645 BlitScreenToBitmap_SP(target_bitmap);
646 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
647 BlitScreenToBitmap_MM(target_bitmap);
648 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
649 BlitScreenToBitmap_RND(target_bitmap);
651 redraw_mask |= REDRAW_FIELD;
654 static void DrawFramesPerSecond(void)
657 int font_nr = FONT_TEXT_2;
658 int font_width = getFontWidth(font_nr);
659 int draw_deactivation_mask = GetDrawDeactivationMask();
660 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
662 // draw FPS with leading space (needed if field buffer deactivated)
663 sprintf(text, " %04.1f fps", global.frames_per_second);
665 // override draw deactivation mask (required for invisible warp mode)
666 SetDrawDeactivationMask(REDRAW_NONE);
668 // draw opaque FPS if field buffer deactivated, else draw masked FPS
669 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
670 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
672 // set draw deactivation mask to previous value
673 SetDrawDeactivationMask(draw_deactivation_mask);
675 // force full-screen redraw in this frame
676 redraw_mask = REDRAW_ALL;
680 static void PrintFrameTimeDebugging(void)
682 static unsigned int last_counter = 0;
683 unsigned int counter = Counter();
684 int diff_1 = counter - last_counter;
685 int diff_2 = diff_1 - GAME_FRAME_DELAY;
687 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
688 char diff_bar[2 * diff_2_max + 5];
692 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
694 for (i = 0; i < diff_2_max; i++)
695 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
696 i >= diff_2_max - diff_2_cut ? '-' : ' ');
698 diff_bar[pos++] = '|';
700 for (i = 0; i < diff_2_max; i++)
701 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
703 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
705 diff_bar[pos++] = '\0';
707 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
710 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
713 last_counter = counter;
717 static int unifiedRedrawMask(int mask)
719 if (mask & REDRAW_ALL)
722 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
728 static boolean equalRedrawMasks(int mask_1, int mask_2)
730 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
733 void BackToFront(void)
735 static int last_redraw_mask = REDRAW_NONE;
737 // force screen redraw in every frame to continue drawing global animations
738 // (but always use the last redraw mask to prevent unwanted side effects)
739 if (redraw_mask == REDRAW_NONE)
740 redraw_mask = last_redraw_mask;
742 last_redraw_mask = redraw_mask;
745 // masked border now drawn immediately when blitting backbuffer to window
747 // draw masked border to all viewports, if defined
748 DrawMaskedBorder(redraw_mask);
751 // draw frames per second (only if debug mode is enabled)
752 if (redraw_mask & REDRAW_FPS)
753 DrawFramesPerSecond();
755 // remove playfield redraw before potentially merging with doors redraw
756 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
757 redraw_mask &= ~REDRAW_FIELD;
759 // redraw complete window if both playfield and (some) doors need redraw
760 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
761 redraw_mask = REDRAW_ALL;
763 /* although redrawing the whole window would be fine for normal gameplay,
764 being able to only redraw the playfield is required for deactivating
765 certain drawing areas (mainly playfield) to work, which is needed for
766 warp-forward to be fast enough (by skipping redraw of most frames) */
768 if (redraw_mask & REDRAW_ALL)
770 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
772 else if (redraw_mask & REDRAW_FIELD)
774 BlitBitmap(backbuffer, window,
775 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
777 else if (redraw_mask & REDRAW_DOORS)
779 // merge door areas to prevent calling screen redraw more than once
785 if (redraw_mask & REDRAW_DOOR_1)
789 x2 = MAX(x2, DX + DXSIZE);
790 y2 = MAX(y2, DY + DYSIZE);
793 if (redraw_mask & REDRAW_DOOR_2)
797 x2 = MAX(x2, VX + VXSIZE);
798 y2 = MAX(y2, VY + VYSIZE);
801 if (redraw_mask & REDRAW_DOOR_3)
805 x2 = MAX(x2, EX + EXSIZE);
806 y2 = MAX(y2, EY + EYSIZE);
809 // make sure that at least one pixel is blitted, and inside the screen
810 // (else nothing is blitted, causing the animations not to be updated)
811 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
812 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
813 x2 = MIN(MAX(1, x2), WIN_XSIZE);
814 y2 = MIN(MAX(1, y2), WIN_YSIZE);
816 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
819 redraw_mask = REDRAW_NONE;
822 PrintFrameTimeDebugging();
826 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
828 unsigned int frame_delay_value_old = GetVideoFrameDelay();
830 SetVideoFrameDelay(frame_delay_value);
834 SetVideoFrameDelay(frame_delay_value_old);
837 static int fade_type_skip = FADE_TYPE_NONE;
839 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
841 void (*draw_border_function)(void) = NULL;
842 int x, y, width, height;
843 int fade_delay, post_delay;
845 if (fade_type == FADE_TYPE_FADE_OUT)
847 if (fade_type_skip != FADE_TYPE_NONE)
849 // skip all fade operations until specified fade operation
850 if (fade_type & fade_type_skip)
851 fade_type_skip = FADE_TYPE_NONE;
856 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
860 redraw_mask |= fade_mask;
862 if (fade_type == FADE_TYPE_SKIP)
864 fade_type_skip = fade_mode;
869 fade_delay = fading.fade_delay;
870 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
872 if (fade_type_skip != FADE_TYPE_NONE)
874 // skip all fade operations until specified fade operation
875 if (fade_type & fade_type_skip)
876 fade_type_skip = FADE_TYPE_NONE;
881 if (global.autoplay_leveldir)
886 if (fade_mask == REDRAW_FIELD)
891 height = FADE_SYSIZE;
893 if (border.draw_masked_when_fading)
894 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
896 DrawMaskedBorder_FIELD(); // draw once
906 // when switching screens without fading, set fade delay to zero
907 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
910 // do not display black frame when fading out without fade delay
911 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
914 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
915 draw_border_function);
917 redraw_mask &= ~fade_mask;
919 ClearAutoRepeatKeyEvents();
922 static void SetScreenStates_BeforeFadingIn(void)
924 // temporarily set screen mode for animations to screen after fading in
925 global.anim_status = global.anim_status_next;
927 // store backbuffer with all animations that will be started after fading in
928 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
930 // set screen mode for animations back to fading
931 global.anim_status = GAME_MODE_PSEUDO_FADING;
934 static void SetScreenStates_AfterFadingIn(void)
936 // store new source screen (to use correct masked border for fading)
937 gfx.fade_border_source_status = global.border_status;
939 global.anim_status = global.anim_status_next;
942 static void SetScreenStates_BeforeFadingOut(void)
944 // store new target screen (to use correct masked border for fading)
945 gfx.fade_border_target_status = game_status;
947 // set screen mode for animations to fading
948 global.anim_status = GAME_MODE_PSEUDO_FADING;
950 // store backbuffer with all animations that will be stopped for fading out
951 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
954 static void SetScreenStates_AfterFadingOut(void)
956 global.border_status = game_status;
959 void FadeIn(int fade_mask)
961 SetScreenStates_BeforeFadingIn();
964 DrawMaskedBorder(REDRAW_ALL);
967 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
968 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
970 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
974 FADE_SXSIZE = FULL_SXSIZE;
975 FADE_SYSIZE = FULL_SYSIZE;
977 // activate virtual buttons depending on upcoming game status
978 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
979 game_status == GAME_MODE_PLAYING && !tape.playing)
980 SetOverlayActive(TRUE);
982 SetScreenStates_AfterFadingIn();
984 // force update of global animation status in case of rapid screen changes
985 redraw_mask = REDRAW_ALL;
989 void FadeOut(int fade_mask)
991 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
992 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
993 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
996 SetScreenStates_BeforeFadingOut();
998 SetTileCursorActive(FALSE);
999 SetOverlayActive(FALSE);
1002 DrawMaskedBorder(REDRAW_ALL);
1005 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1006 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1008 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1010 SetScreenStates_AfterFadingOut();
1013 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1015 static struct TitleFadingInfo fading_leave_stored;
1018 fading_leave_stored = fading_leave;
1020 fading = fading_leave_stored;
1023 void FadeSetEnterMenu(void)
1025 fading = menu.enter_menu;
1027 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1030 void FadeSetLeaveMenu(void)
1032 fading = menu.leave_menu;
1034 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1037 void FadeSetEnterScreen(void)
1039 fading = menu.enter_screen[game_status];
1041 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1044 void FadeSetNextScreen(void)
1046 fading = menu.next_screen[game_status];
1048 // (do not overwrite fade mode set by FadeSetEnterScreen)
1049 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1052 void FadeSetLeaveScreen(void)
1054 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1057 void FadeSetFromType(int type)
1059 if (type & TYPE_ENTER_SCREEN)
1060 FadeSetEnterScreen();
1061 else if (type & TYPE_ENTER)
1063 else if (type & TYPE_LEAVE)
1067 void FadeSetDisabled(void)
1069 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1071 fading = fading_none;
1074 void FadeSkipNextFadeIn(void)
1076 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1079 void FadeSkipNextFadeOut(void)
1081 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1084 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1086 if (graphic == IMG_UNDEFINED)
1089 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1091 return (graphic_info[graphic].bitmap != NULL || redefined ?
1092 graphic_info[graphic].bitmap :
1093 graphic_info[default_graphic].bitmap);
1096 static Bitmap *getBackgroundBitmap(int graphic)
1098 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1101 static Bitmap *getGlobalBorderBitmap(int graphic)
1103 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1106 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1109 (status == GAME_MODE_MAIN ||
1110 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1111 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1112 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1113 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1116 return getGlobalBorderBitmap(graphic);
1119 void SetWindowBackgroundImageIfDefined(int graphic)
1121 if (graphic_info[graphic].bitmap)
1122 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1125 void SetMainBackgroundImageIfDefined(int graphic)
1127 if (graphic_info[graphic].bitmap)
1128 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1131 void SetDoorBackgroundImageIfDefined(int graphic)
1133 if (graphic_info[graphic].bitmap)
1134 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1137 void SetWindowBackgroundImage(int graphic)
1139 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1142 void SetMainBackgroundImage(int graphic)
1144 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1147 void SetDoorBackgroundImage(int graphic)
1149 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetPanelBackground(void)
1154 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1156 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1157 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1159 SetDoorBackgroundBitmap(bitmap_db_panel);
1162 void DrawBackground(int x, int y, int width, int height)
1164 // "drawto" might still point to playfield buffer here (hall of fame)
1165 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1167 if (IN_GFX_FIELD_FULL(x, y))
1168 redraw_mask |= REDRAW_FIELD;
1169 else if (IN_GFX_DOOR_1(x, y))
1170 redraw_mask |= REDRAW_DOOR_1;
1171 else if (IN_GFX_DOOR_2(x, y))
1172 redraw_mask |= REDRAW_DOOR_2;
1173 else if (IN_GFX_DOOR_3(x, y))
1174 redraw_mask |= REDRAW_DOOR_3;
1177 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1179 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1181 if (font->bitmap == NULL)
1184 DrawBackground(x, y, width, height);
1187 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1189 struct GraphicInfo *g = &graphic_info[graphic];
1191 if (g->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 static int game_status_last = -1;
1198 static Bitmap *global_border_bitmap_last = NULL;
1199 static Bitmap *global_border_bitmap = NULL;
1200 static int real_sx_last = -1, real_sy_last = -1;
1201 static int full_sxsize_last = -1, full_sysize_last = -1;
1202 static int dx_last = -1, dy_last = -1;
1203 static int dxsize_last = -1, dysize_last = -1;
1204 static int vx_last = -1, vy_last = -1;
1205 static int vxsize_last = -1, vysize_last = -1;
1206 static int ex_last = -1, ey_last = -1;
1207 static int exsize_last = -1, eysize_last = -1;
1209 boolean CheckIfGlobalBorderHasChanged(void)
1211 // if game status has not changed, global border has not changed either
1212 if (game_status == game_status_last)
1215 // determine and store new global border bitmap for current game status
1216 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1218 return (global_border_bitmap_last != global_border_bitmap);
1221 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1223 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1224 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1226 // if game status has not changed, nothing has to be redrawn
1227 if (game_status == game_status_last)
1230 // redraw if last screen was title screen
1231 if (game_status_last == GAME_MODE_TITLE)
1234 // redraw if global screen border has changed
1235 if (CheckIfGlobalBorderHasChanged())
1238 // redraw if position or size of playfield area has changed
1239 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1240 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1243 // redraw if position or size of door area has changed
1244 if (dx_last != DX || dy_last != DY ||
1245 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1248 // redraw if position or size of tape area has changed
1249 if (vx_last != VX || vy_last != VY ||
1250 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1253 // redraw if position or size of editor area has changed
1254 if (ex_last != EX || ey_last != EY ||
1255 exsize_last != EXSIZE || eysize_last != EYSIZE)
1262 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1265 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1267 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1270 void RedrawGlobalBorder(void)
1272 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1274 RedrawGlobalBorderFromBitmap(bitmap);
1276 redraw_mask = REDRAW_ALL;
1279 static void RedrawGlobalBorderIfNeeded(void)
1281 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1282 if (game_status == game_status_last)
1286 // copy current draw buffer to later copy back areas that have not changed
1287 if (game_status_last != GAME_MODE_TITLE)
1288 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1290 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1291 if (CheckIfGlobalBorderRedrawIsNeeded())
1293 // determine and store new global border bitmap for current game status
1294 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1297 // redraw global screen border (or clear, if defined to be empty)
1298 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1300 if (game_status == GAME_MODE_EDITOR)
1301 DrawSpecialEditorDoor();
1303 // copy previous playfield and door areas, if they are defined on both
1304 // previous and current screen and if they still have the same size
1306 if (real_sx_last != -1 && real_sy_last != -1 &&
1307 REAL_SX != -1 && REAL_SY != -1 &&
1308 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1309 BlitBitmap(bitmap_db_store_1, backbuffer,
1310 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1313 if (dx_last != -1 && dy_last != -1 &&
1314 DX != -1 && DY != -1 &&
1315 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1316 BlitBitmap(bitmap_db_store_1, backbuffer,
1317 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1319 if (game_status != GAME_MODE_EDITOR)
1321 if (vx_last != -1 && vy_last != -1 &&
1322 VX != -1 && VY != -1 &&
1323 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1324 BlitBitmap(bitmap_db_store_1, backbuffer,
1325 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1329 if (ex_last != -1 && ey_last != -1 &&
1330 EX != -1 && EY != -1 &&
1331 exsize_last == EXSIZE && eysize_last == EYSIZE)
1332 BlitBitmap(bitmap_db_store_1, backbuffer,
1333 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1336 redraw_mask = REDRAW_ALL;
1339 game_status_last = game_status;
1341 global_border_bitmap_last = global_border_bitmap;
1343 real_sx_last = REAL_SX;
1344 real_sy_last = REAL_SY;
1345 full_sxsize_last = FULL_SXSIZE;
1346 full_sysize_last = FULL_SYSIZE;
1349 dxsize_last = DXSIZE;
1350 dysize_last = DYSIZE;
1353 vxsize_last = VXSIZE;
1354 vysize_last = VYSIZE;
1357 exsize_last = EXSIZE;
1358 eysize_last = EYSIZE;
1361 void ClearField(void)
1363 RedrawGlobalBorderIfNeeded();
1365 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1366 // (when entering hall of fame after playing)
1367 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1369 // !!! maybe this should be done before clearing the background !!!
1370 if (game_status == GAME_MODE_PLAYING)
1372 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1373 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1377 SetDrawtoField(DRAW_TO_BACKBUFFER);
1381 void MarkTileDirty(int x, int y)
1383 redraw_mask |= REDRAW_FIELD;
1386 void SetBorderElement(void)
1390 BorderElement = EL_EMPTY;
1392 // only the R'n'D game engine may use an additional steelwall border
1393 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1396 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1398 for (x = 0; x < lev_fieldx; x++)
1400 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1401 BorderElement = EL_STEELWALL;
1403 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1409 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1410 int max_array_fieldx, int max_array_fieldy,
1411 short field[max_array_fieldx][max_array_fieldy],
1412 int max_fieldx, int max_fieldy)
1416 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1417 static int safety = 0;
1419 // check if starting field still has the desired content
1420 if (field[from_x][from_y] == fill_element)
1425 if (safety > max_fieldx * max_fieldy)
1426 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1428 old_element = field[from_x][from_y];
1429 field[from_x][from_y] = fill_element;
1431 for (i = 0; i < 4; i++)
1433 x = from_x + check[i][0];
1434 y = from_y + check[i][1];
1436 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1437 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1438 field, max_fieldx, max_fieldy);
1444 void FloodFillLevel(int from_x, int from_y, int fill_element,
1445 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1446 int max_fieldx, int max_fieldy)
1448 FloodFillLevelExt(from_x, from_y, fill_element,
1449 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1450 max_fieldx, max_fieldy);
1453 void SetRandomAnimationValue(int x, int y)
1455 gfx.anim_random_frame = GfxRandom[x][y];
1458 int getGraphicAnimationFrame(int graphic, int sync_frame)
1460 // animation synchronized with global frame counter, not move position
1461 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1462 sync_frame = FrameCounter;
1464 return getAnimationFrame(graphic_info[graphic].anim_frames,
1465 graphic_info[graphic].anim_delay,
1466 graphic_info[graphic].anim_mode,
1467 graphic_info[graphic].anim_start_frame,
1471 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1473 struct GraphicInfo *g = &graphic_info[graphic];
1474 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1476 if (tilesize == gfx.standard_tile_size)
1477 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1478 else if (tilesize == game.tile_size)
1479 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1481 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1484 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1485 boolean get_backside)
1487 struct GraphicInfo *g = &graphic_info[graphic];
1488 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1489 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1491 if (g->offset_y == 0) // frames are ordered horizontally
1493 int max_width = g->anim_frames_per_line * g->width;
1494 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1496 *x = pos % max_width;
1497 *y = src_y % g->height + pos / max_width * g->height;
1499 else if (g->offset_x == 0) // frames are ordered vertically
1501 int max_height = g->anim_frames_per_line * g->height;
1502 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1504 *x = src_x % g->width + pos / max_height * g->width;
1505 *y = pos % max_height;
1507 else // frames are ordered diagonally
1509 *x = src_x + frame * g->offset_x;
1510 *y = src_y + frame * g->offset_y;
1514 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1515 Bitmap **bitmap, int *x, int *y,
1516 boolean get_backside)
1518 struct GraphicInfo *g = &graphic_info[graphic];
1520 // if no graphics defined at all, use fallback graphics
1521 if (g->bitmaps == NULL)
1522 *g = graphic_info[IMG_CHAR_EXCLAM];
1524 // if no in-game graphics defined, always use standard graphic size
1525 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1526 tilesize = TILESIZE;
1528 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1529 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1531 *x = *x * tilesize / g->tile_size;
1532 *y = *y * tilesize / g->tile_size;
1535 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1536 Bitmap **bitmap, int *x, int *y)
1538 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1541 void getFixedGraphicSource(int graphic, int frame,
1542 Bitmap **bitmap, int *x, int *y)
1544 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1547 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1549 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1552 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1553 int *x, int *y, boolean get_backside)
1555 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1559 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1561 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1564 void DrawGraphic(int x, int y, int graphic, int frame)
1567 if (!IN_SCR_FIELD(x, y))
1569 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1570 Debug("draw:DrawGraphic", "This should never happen!");
1576 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1579 MarkTileDirty(x, y);
1582 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1585 if (!IN_SCR_FIELD(x, y))
1587 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1589 Debug("draw:DrawFixedGraphic", "This should never happen!");
1595 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1597 MarkTileDirty(x, y);
1600 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1606 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1608 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1611 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1617 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1618 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1621 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1624 if (!IN_SCR_FIELD(x, y))
1626 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1628 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1634 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1637 MarkTileDirty(x, y);
1640 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1643 if (!IN_SCR_FIELD(x, y))
1645 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1647 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1653 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1655 MarkTileDirty(x, y);
1658 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1664 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1666 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1670 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1671 int graphic, int frame)
1676 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1678 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1682 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1684 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1686 MarkTileDirty(x / tilesize, y / tilesize);
1689 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1692 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1693 graphic, frame, tilesize);
1694 MarkTileDirty(x / tilesize, y / tilesize);
1697 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1703 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1704 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1707 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1708 int frame, int tilesize)
1713 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1714 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1717 void DrawMiniGraphic(int x, int y, int graphic)
1719 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1720 MarkTileDirty(x / 2, y / 2);
1723 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1728 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1729 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1732 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1733 int graphic, int frame,
1734 int cut_mode, int mask_mode)
1739 int width = TILEX, height = TILEY;
1742 if (dx || dy) // shifted graphic
1744 if (x < BX1) // object enters playfield from the left
1751 else if (x > BX2) // object enters playfield from the right
1757 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1763 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1765 else if (dx) // general horizontal movement
1766 MarkTileDirty(x + SIGN(dx), y);
1768 if (y < BY1) // object enters playfield from the top
1770 if (cut_mode == CUT_BELOW) // object completely above top border
1778 else if (y > BY2) // object enters playfield from the bottom
1784 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1790 else if (dy > 0 && cut_mode == CUT_ABOVE)
1792 if (y == BY2) // object completely above bottom border
1798 MarkTileDirty(x, y + 1);
1799 } // object leaves playfield to the bottom
1800 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1802 else if (dy) // general vertical movement
1803 MarkTileDirty(x, y + SIGN(dy));
1807 if (!IN_SCR_FIELD(x, y))
1809 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1811 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1817 width = width * TILESIZE_VAR / TILESIZE;
1818 height = height * TILESIZE_VAR / TILESIZE;
1819 cx = cx * TILESIZE_VAR / TILESIZE;
1820 cy = cy * TILESIZE_VAR / TILESIZE;
1821 dx = dx * TILESIZE_VAR / TILESIZE;
1822 dy = dy * TILESIZE_VAR / TILESIZE;
1824 if (width > 0 && height > 0)
1826 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1831 dst_x = FX + x * TILEX_VAR + dx;
1832 dst_y = FY + y * TILEY_VAR + dy;
1834 if (mask_mode == USE_MASKING)
1835 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1838 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1841 MarkTileDirty(x, y);
1845 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1846 int graphic, int frame,
1847 int cut_mode, int mask_mode)
1852 int width = TILEX_VAR, height = TILEY_VAR;
1855 int x2 = x + SIGN(dx);
1856 int y2 = y + SIGN(dy);
1858 // movement with two-tile animations must be sync'ed with movement position,
1859 // not with current GfxFrame (which can be higher when using slow movement)
1860 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1861 int anim_frames = graphic_info[graphic].anim_frames;
1863 // (we also need anim_delay here for movement animations with less frames)
1864 int anim_delay = graphic_info[graphic].anim_delay;
1865 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1867 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1868 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1870 // re-calculate animation frame for two-tile movement animation
1871 frame = getGraphicAnimationFrame(graphic, sync_frame);
1873 // check if movement start graphic inside screen area and should be drawn
1874 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1876 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1878 dst_x = FX + x1 * TILEX_VAR;
1879 dst_y = FY + y1 * TILEY_VAR;
1881 if (mask_mode == USE_MASKING)
1882 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1885 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1888 MarkTileDirty(x1, y1);
1891 // check if movement end graphic inside screen area and should be drawn
1892 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1894 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1896 dst_x = FX + x2 * TILEX_VAR;
1897 dst_y = FY + y2 * TILEY_VAR;
1899 if (mask_mode == USE_MASKING)
1900 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1903 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1906 MarkTileDirty(x2, y2);
1910 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1911 int graphic, int frame,
1912 int cut_mode, int mask_mode)
1916 DrawGraphic(x, y, graphic, frame);
1921 if (graphic_info[graphic].double_movement) // EM style movement images
1922 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1924 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1927 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1928 int graphic, int frame, int cut_mode)
1930 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1933 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1934 int cut_mode, int mask_mode)
1936 int lx = LEVELX(x), ly = LEVELY(y);
1940 if (IN_LEV_FIELD(lx, ly))
1942 SetRandomAnimationValue(lx, ly);
1944 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1945 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1947 // do not use double (EM style) movement graphic when not moving
1948 if (graphic_info[graphic].double_movement && !dx && !dy)
1950 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1951 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1954 else // border element
1956 graphic = el2img(element);
1957 frame = getGraphicAnimationFrame(graphic, -1);
1960 if (element == EL_EXPANDABLE_WALL)
1962 boolean left_stopped = FALSE, right_stopped = FALSE;
1964 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1965 left_stopped = TRUE;
1966 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
1967 right_stopped = TRUE;
1969 if (left_stopped && right_stopped)
1971 else if (left_stopped)
1973 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1974 frame = graphic_info[graphic].anim_frames - 1;
1976 else if (right_stopped)
1978 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1979 frame = graphic_info[graphic].anim_frames - 1;
1984 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1985 else if (mask_mode == USE_MASKING)
1986 DrawGraphicThruMask(x, y, graphic, frame);
1988 DrawGraphic(x, y, graphic, frame);
1991 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1992 int cut_mode, int mask_mode)
1994 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1995 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1996 cut_mode, mask_mode);
1999 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2002 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2005 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2008 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2011 void DrawLevelElementThruMask(int x, int y, int element)
2013 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2016 void DrawLevelFieldThruMask(int x, int y)
2018 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2021 // !!! implementation of quicksand is totally broken !!!
2022 #define IS_CRUMBLED_TILE(x, y, e) \
2023 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2024 !IS_MOVING(x, y) || \
2025 (e) == EL_QUICKSAND_EMPTYING || \
2026 (e) == EL_QUICKSAND_FAST_EMPTYING))
2028 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2033 int width, height, cx, cy;
2034 int sx = SCREENX(x), sy = SCREENY(y);
2035 int crumbled_border_size = graphic_info[graphic].border_size;
2036 int crumbled_tile_size = graphic_info[graphic].tile_size;
2037 int crumbled_border_size_var =
2038 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2041 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2043 for (i = 1; i < 4; i++)
2045 int dxx = (i & 1 ? dx : 0);
2046 int dyy = (i & 2 ? dy : 0);
2049 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2052 // check if neighbour field is of same crumble type
2053 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2054 graphic_info[graphic].class ==
2055 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2057 // return if check prevents inner corner
2058 if (same == (dxx == dx && dyy == dy))
2062 // if we reach this point, we have an inner corner
2064 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2066 width = crumbled_border_size_var;
2067 height = crumbled_border_size_var;
2068 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2069 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2071 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2072 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2075 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2080 int width, height, bx, by, cx, cy;
2081 int sx = SCREENX(x), sy = SCREENY(y);
2082 int crumbled_border_size = graphic_info[graphic].border_size;
2083 int crumbled_tile_size = graphic_info[graphic].tile_size;
2084 int crumbled_border_size_var =
2085 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2086 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2089 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2091 // draw simple, sloppy, non-corner-accurate crumbled border
2093 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2094 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2095 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2096 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2098 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2099 FX + sx * TILEX_VAR + cx,
2100 FY + sy * TILEY_VAR + cy);
2102 // (remaining middle border part must be at least as big as corner part)
2103 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2104 crumbled_border_size_var >= TILESIZE_VAR / 3)
2107 // correct corners of crumbled border, if needed
2109 for (i = -1; i <= 1; i += 2)
2111 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2112 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2113 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2116 // check if neighbour field is of same crumble type
2117 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2118 graphic_info[graphic].class ==
2119 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2121 // no crumbled corner, but continued crumbled border
2123 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2124 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2125 int b1 = (i == 1 ? crumbled_border_size_var :
2126 TILESIZE_VAR - 2 * crumbled_border_size_var);
2128 width = crumbled_border_size_var;
2129 height = crumbled_border_size_var;
2131 if (dir == 1 || dir == 2)
2146 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2148 FX + sx * TILEX_VAR + cx,
2149 FY + sy * TILEY_VAR + cy);
2154 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2156 int sx = SCREENX(x), sy = SCREENY(y);
2159 static int xy[4][2] =
2167 if (!IN_LEV_FIELD(x, y))
2170 element = TILE_GFX_ELEMENT(x, y);
2172 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2174 if (!IN_SCR_FIELD(sx, sy))
2177 // crumble field borders towards direct neighbour fields
2178 for (i = 0; i < 4; i++)
2180 int xx = x + xy[i][0];
2181 int yy = y + xy[i][1];
2183 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2186 // check if neighbour field is of same crumble type
2187 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2188 graphic_info[graphic].class ==
2189 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2192 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2195 // crumble inner field corners towards corner neighbour fields
2196 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2197 graphic_info[graphic].anim_frames == 2)
2199 for (i = 0; i < 4; i++)
2201 int dx = (i & 1 ? +1 : -1);
2202 int dy = (i & 2 ? +1 : -1);
2204 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2208 MarkTileDirty(sx, sy);
2210 else // center field is not crumbled -- crumble neighbour fields
2212 // crumble field borders of direct neighbour fields
2213 for (i = 0; i < 4; i++)
2215 int xx = x + xy[i][0];
2216 int yy = y + xy[i][1];
2217 int sxx = sx + xy[i][0];
2218 int syy = sy + xy[i][1];
2220 if (!IN_LEV_FIELD(xx, yy) ||
2221 !IN_SCR_FIELD(sxx, syy))
2224 // do not crumble fields that are being digged or snapped
2225 if (Tile[xx][yy] == EL_EMPTY ||
2226 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2229 element = TILE_GFX_ELEMENT(xx, yy);
2231 if (!IS_CRUMBLED_TILE(xx, yy, element))
2234 graphic = el_act2crm(element, ACTION_DEFAULT);
2236 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2238 MarkTileDirty(sxx, syy);
2241 // crumble inner field corners of corner neighbour fields
2242 for (i = 0; i < 4; i++)
2244 int dx = (i & 1 ? +1 : -1);
2245 int dy = (i & 2 ? +1 : -1);
2251 if (!IN_LEV_FIELD(xx, yy) ||
2252 !IN_SCR_FIELD(sxx, syy))
2255 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2258 element = TILE_GFX_ELEMENT(xx, yy);
2260 if (!IS_CRUMBLED_TILE(xx, yy, element))
2263 graphic = el_act2crm(element, ACTION_DEFAULT);
2265 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2266 graphic_info[graphic].anim_frames == 2)
2267 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2269 MarkTileDirty(sxx, syy);
2274 void DrawLevelFieldCrumbled(int x, int y)
2278 if (!IN_LEV_FIELD(x, y))
2281 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2282 GfxElement[x][y] != EL_UNDEFINED &&
2283 GFX_CRUMBLED(GfxElement[x][y]))
2285 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2290 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2292 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2295 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2298 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2299 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2300 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2301 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2302 int sx = SCREENX(x), sy = SCREENY(y);
2304 DrawGraphic(sx, sy, graphic1, frame1);
2305 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2308 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2310 int sx = SCREENX(x), sy = SCREENY(y);
2311 static int xy[4][2] =
2320 // crumble direct neighbour fields (required for field borders)
2321 for (i = 0; i < 4; i++)
2323 int xx = x + xy[i][0];
2324 int yy = y + xy[i][1];
2325 int sxx = sx + xy[i][0];
2326 int syy = sy + xy[i][1];
2328 if (!IN_LEV_FIELD(xx, yy) ||
2329 !IN_SCR_FIELD(sxx, syy) ||
2330 !GFX_CRUMBLED(Tile[xx][yy]) ||
2334 DrawLevelField(xx, yy);
2337 // crumble corner neighbour fields (required for inner field corners)
2338 for (i = 0; i < 4; i++)
2340 int dx = (i & 1 ? +1 : -1);
2341 int dy = (i & 2 ? +1 : -1);
2347 if (!IN_LEV_FIELD(xx, yy) ||
2348 !IN_SCR_FIELD(sxx, syy) ||
2349 !GFX_CRUMBLED(Tile[xx][yy]) ||
2353 int element = TILE_GFX_ELEMENT(xx, yy);
2354 int graphic = el_act2crm(element, ACTION_DEFAULT);
2356 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2357 graphic_info[graphic].anim_frames == 2)
2358 DrawLevelField(xx, yy);
2362 static int getBorderElement(int x, int y)
2366 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2367 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2368 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2369 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2370 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2371 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2372 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2374 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2375 int steel_position = (x == -1 && y == -1 ? 0 :
2376 x == lev_fieldx && y == -1 ? 1 :
2377 x == -1 && y == lev_fieldy ? 2 :
2378 x == lev_fieldx && y == lev_fieldy ? 3 :
2379 x == -1 || x == lev_fieldx ? 4 :
2380 y == -1 || y == lev_fieldy ? 5 : 6);
2382 return border[steel_position][steel_type];
2385 void DrawScreenElement(int x, int y, int element)
2387 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2388 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2391 void DrawLevelElement(int x, int y, int element)
2393 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2394 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2397 void DrawScreenField(int x, int y)
2399 int lx = LEVELX(x), ly = LEVELY(y);
2400 int element, content;
2402 if (!IN_LEV_FIELD(lx, ly))
2404 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2407 element = getBorderElement(lx, ly);
2409 DrawScreenElement(x, y, element);
2414 element = Tile[lx][ly];
2415 content = Store[lx][ly];
2417 if (IS_MOVING(lx, ly))
2419 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2420 boolean cut_mode = NO_CUTTING;
2422 if (element == EL_QUICKSAND_EMPTYING ||
2423 element == EL_QUICKSAND_FAST_EMPTYING ||
2424 element == EL_MAGIC_WALL_EMPTYING ||
2425 element == EL_BD_MAGIC_WALL_EMPTYING ||
2426 element == EL_DC_MAGIC_WALL_EMPTYING ||
2427 element == EL_AMOEBA_DROPPING)
2428 cut_mode = CUT_ABOVE;
2429 else if (element == EL_QUICKSAND_FILLING ||
2430 element == EL_QUICKSAND_FAST_FILLING ||
2431 element == EL_MAGIC_WALL_FILLING ||
2432 element == EL_BD_MAGIC_WALL_FILLING ||
2433 element == EL_DC_MAGIC_WALL_FILLING)
2434 cut_mode = CUT_BELOW;
2436 if (cut_mode == CUT_ABOVE)
2437 DrawScreenElement(x, y, element);
2439 DrawScreenElement(x, y, EL_EMPTY);
2442 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2443 else if (cut_mode == NO_CUTTING)
2444 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2447 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2449 if (cut_mode == CUT_BELOW &&
2450 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2451 DrawLevelElement(lx, ly + 1, element);
2454 if (content == EL_ACID)
2456 int dir = MovDir[lx][ly];
2457 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2458 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2460 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2462 // prevent target field from being drawn again (but without masking)
2463 // (this would happen if target field is scanned after moving element)
2464 Stop[newlx][newly] = TRUE;
2467 else if (IS_BLOCKED(lx, ly))
2472 boolean cut_mode = NO_CUTTING;
2473 int element_old, content_old;
2475 Blocked2Moving(lx, ly, &oldx, &oldy);
2478 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2479 MovDir[oldx][oldy] == MV_RIGHT);
2481 element_old = Tile[oldx][oldy];
2482 content_old = Store[oldx][oldy];
2484 if (element_old == EL_QUICKSAND_EMPTYING ||
2485 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2486 element_old == EL_MAGIC_WALL_EMPTYING ||
2487 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2488 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2489 element_old == EL_AMOEBA_DROPPING)
2490 cut_mode = CUT_ABOVE;
2492 DrawScreenElement(x, y, EL_EMPTY);
2495 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2497 else if (cut_mode == NO_CUTTING)
2498 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2501 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2504 else if (IS_DRAWABLE(element))
2505 DrawScreenElement(x, y, element);
2507 DrawScreenElement(x, y, EL_EMPTY);
2510 void DrawLevelField(int x, int y)
2512 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2513 DrawScreenField(SCREENX(x), SCREENY(y));
2514 else if (IS_MOVING(x, y))
2518 Moving2Blocked(x, y, &newx, &newy);
2519 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2520 DrawScreenField(SCREENX(newx), SCREENY(newy));
2522 else if (IS_BLOCKED(x, y))
2526 Blocked2Moving(x, y, &oldx, &oldy);
2527 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2528 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2532 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2533 int (*el2img_function)(int), boolean masked,
2534 int element_bits_draw)
2536 int element_base = map_mm_wall_element(element);
2537 int element_bits = (IS_DF_WALL(element) ?
2538 element - EL_DF_WALL_START :
2539 IS_MM_WALL(element) ?
2540 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2541 int graphic = el2img_function(element_base);
2542 int tilesize_draw = tilesize / 2;
2547 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2549 for (i = 0; i < 4; i++)
2551 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2552 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2554 if (!(element_bits_draw & (1 << i)))
2557 if (element_bits & (1 << i))
2560 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2561 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2563 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2564 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2569 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2570 tilesize_draw, tilesize_draw);
2575 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2576 boolean masked, int element_bits_draw)
2578 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2579 element, tilesize, el2edimg, masked, element_bits_draw);
2582 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2583 int (*el2img_function)(int))
2585 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2589 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2592 if (IS_MM_WALL(element))
2594 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2595 element, tilesize, el2edimg, masked, 0x000f);
2599 int graphic = el2edimg(element);
2602 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2604 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2608 void DrawSizedElement(int x, int y, int element, int tilesize)
2610 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2613 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2615 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2618 void DrawMiniElement(int x, int y, int element)
2622 graphic = el2edimg(element);
2623 DrawMiniGraphic(x, y, graphic);
2626 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2629 int x = sx + scroll_x, y = sy + scroll_y;
2631 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2632 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2633 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2634 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2636 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2639 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2641 int x = sx + scroll_x, y = sy + scroll_y;
2643 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2644 DrawMiniElement(sx, sy, EL_EMPTY);
2645 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2646 DrawMiniElement(sx, sy, Tile[x][y]);
2648 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2651 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2652 int x, int y, int xsize, int ysize,
2653 int tile_width, int tile_height)
2657 int dst_x = startx + x * tile_width;
2658 int dst_y = starty + y * tile_height;
2659 int width = graphic_info[graphic].width;
2660 int height = graphic_info[graphic].height;
2661 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2662 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2663 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2664 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2665 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2666 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2667 boolean draw_masked = graphic_info[graphic].draw_masked;
2669 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2671 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2673 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2677 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2678 inner_sx + (x - 1) * tile_width % inner_width);
2679 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2680 inner_sy + (y - 1) * tile_height % inner_height);
2683 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2686 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2690 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2691 int x, int y, int xsize, int ysize,
2694 int font_width = getFontWidth(font_nr);
2695 int font_height = getFontHeight(font_nr);
2697 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2698 font_width, font_height);
2701 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2703 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2704 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2705 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2706 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2707 boolean no_delay = (tape.warp_forward);
2708 unsigned int anim_delay = 0;
2709 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2710 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2711 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2712 int font_width = getFontWidth(font_nr);
2713 int font_height = getFontHeight(font_nr);
2714 int max_xsize = level.envelope[envelope_nr].xsize;
2715 int max_ysize = level.envelope[envelope_nr].ysize;
2716 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2717 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2718 int xend = max_xsize;
2719 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2720 int xstep = (xstart < xend ? 1 : 0);
2721 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2723 int end = MAX(xend - xstart, yend - ystart);
2726 for (i = start; i <= end; i++)
2728 int last_frame = end; // last frame of this "for" loop
2729 int x = xstart + i * xstep;
2730 int y = ystart + i * ystep;
2731 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2732 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2733 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2734 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2737 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2739 BlitScreenToBitmap(backbuffer);
2741 SetDrawtoField(DRAW_TO_BACKBUFFER);
2743 for (yy = 0; yy < ysize; yy++)
2744 for (xx = 0; xx < xsize; xx++)
2745 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2747 DrawTextBuffer(sx + font_width, sy + font_height,
2748 level.envelope[envelope_nr].text, font_nr, max_xsize,
2749 xsize - 2, ysize - 2, 0, mask_mode,
2750 level.envelope[envelope_nr].autowrap,
2751 level.envelope[envelope_nr].centered, FALSE);
2753 redraw_mask |= REDRAW_FIELD;
2756 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2759 ClearAutoRepeatKeyEvents();
2762 void ShowEnvelope(int envelope_nr)
2764 int element = EL_ENVELOPE_1 + envelope_nr;
2765 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2766 int sound_opening = element_info[element].sound[ACTION_OPENING];
2767 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2768 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2769 boolean no_delay = (tape.warp_forward);
2770 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2771 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2772 int anim_mode = graphic_info[graphic].anim_mode;
2773 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2774 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2775 boolean overlay_enabled = GetOverlayEnabled();
2777 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2779 SetOverlayEnabled(FALSE);
2782 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2784 if (anim_mode == ANIM_DEFAULT)
2785 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2787 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2790 Delay_WithScreenUpdates(wait_delay_value);
2792 WaitForEventToContinue();
2795 SetOverlayEnabled(overlay_enabled);
2797 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2799 if (anim_mode != ANIM_NONE)
2800 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2802 if (anim_mode == ANIM_DEFAULT)
2803 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2805 game.envelope_active = FALSE;
2807 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2809 redraw_mask |= REDRAW_FIELD;
2813 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2814 int xsize, int ysize)
2816 if (!global.use_envelope_request ||
2817 request.sort_priority <= 0)
2820 if (request.bitmap == NULL ||
2821 xsize > request.xsize ||
2822 ysize > request.ysize)
2824 if (request.bitmap != NULL)
2825 FreeBitmap(request.bitmap);
2827 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2829 SDL_Surface *surface = request.bitmap->surface;
2831 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2832 Fail("SDLGetNativeSurface() failed");
2835 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2837 SDLFreeBitmapTextures(request.bitmap);
2838 SDLCreateBitmapTextures(request.bitmap);
2840 // set envelope request run-time values
2843 request.xsize = xsize;
2844 request.ysize = ysize;
2847 static void setRequestBasePosition(int *x, int *y)
2849 int sx_base, sy_base;
2851 if (request.x != -1)
2852 sx_base = request.x;
2853 else if (request.align == ALIGN_LEFT)
2855 else if (request.align == ALIGN_RIGHT)
2856 sx_base = SX + SXSIZE;
2858 sx_base = SX + SXSIZE / 2;
2860 if (request.y != -1)
2861 sy_base = request.y;
2862 else if (request.valign == VALIGN_TOP)
2864 else if (request.valign == VALIGN_BOTTOM)
2865 sy_base = SY + SYSIZE;
2867 sy_base = SY + SYSIZE / 2;
2873 static void setRequestPositionExt(int *x, int *y, int width, int height,
2874 boolean add_border_size)
2876 int border_size = request.border_size;
2877 int sx_base, sy_base;
2880 setRequestBasePosition(&sx_base, &sy_base);
2882 if (request.align == ALIGN_LEFT)
2884 else if (request.align == ALIGN_RIGHT)
2885 sx = sx_base - width;
2887 sx = sx_base - width / 2;
2889 if (request.valign == VALIGN_TOP)
2891 else if (request.valign == VALIGN_BOTTOM)
2892 sy = sy_base - height;
2894 sy = sy_base - height / 2;
2896 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2897 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2899 if (add_border_size)
2909 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2911 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2914 static void DrawEnvelopeRequest(char *text)
2916 char *text_final = text;
2917 char *text_door_style = NULL;
2918 int graphic = IMG_BACKGROUND_REQUEST;
2919 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2920 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2921 int font_nr = FONT_REQUEST;
2922 int font_width = getFontWidth(font_nr);
2923 int font_height = getFontHeight(font_nr);
2924 int border_size = request.border_size;
2925 int line_spacing = request.line_spacing;
2926 int line_height = font_height + line_spacing;
2927 int max_text_width = request.width - 2 * border_size;
2928 int max_text_height = request.height - 2 * border_size;
2929 int line_length = max_text_width / font_width;
2930 int max_lines = max_text_height / line_height;
2931 int text_width = line_length * font_width;
2932 int width = request.width;
2933 int height = request.height;
2934 int tile_size = MAX(request.step_offset, 1);
2935 int x_steps = width / tile_size;
2936 int y_steps = height / tile_size;
2937 int sx_offset = border_size;
2938 int sy_offset = border_size;
2942 if (request.centered)
2943 sx_offset = (request.width - text_width) / 2;
2945 if (request.wrap_single_words && !request.autowrap)
2947 char *src_text_ptr, *dst_text_ptr;
2949 text_door_style = checked_malloc(2 * strlen(text) + 1);
2951 src_text_ptr = text;
2952 dst_text_ptr = text_door_style;
2954 while (*src_text_ptr)
2956 if (*src_text_ptr == ' ' ||
2957 *src_text_ptr == '?' ||
2958 *src_text_ptr == '!')
2959 *dst_text_ptr++ = '\n';
2961 if (*src_text_ptr != ' ')
2962 *dst_text_ptr++ = *src_text_ptr;
2967 *dst_text_ptr = '\0';
2969 text_final = text_door_style;
2972 setRequestPosition(&sx, &sy, FALSE);
2974 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2976 for (y = 0; y < y_steps; y++)
2977 for (x = 0; x < x_steps; x++)
2978 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2979 x, y, x_steps, y_steps,
2980 tile_size, tile_size);
2982 // force DOOR font inside door area
2983 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2985 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2986 line_length, -1, max_lines, line_spacing, mask_mode,
2987 request.autowrap, request.centered, FALSE);
2991 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2992 RedrawGadget(tool_gadget[i]);
2994 // store readily prepared envelope request for later use when animating
2995 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2997 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
2999 if (text_door_style)
3000 free(text_door_style);
3003 static void AnimateEnvelopeRequest(int anim_mode, int action)
3005 int graphic = IMG_BACKGROUND_REQUEST;
3006 boolean draw_masked = graphic_info[graphic].draw_masked;
3007 int delay_value_normal = request.step_delay;
3008 int delay_value_fast = delay_value_normal / 2;
3009 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3010 boolean no_delay = (tape.warp_forward);
3011 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3012 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3013 unsigned int anim_delay = 0;
3015 int tile_size = MAX(request.step_offset, 1);
3016 int max_xsize = request.width / tile_size;
3017 int max_ysize = request.height / tile_size;
3018 int max_xsize_inner = max_xsize - 2;
3019 int max_ysize_inner = max_ysize - 2;
3021 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3022 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3023 int xend = max_xsize_inner;
3024 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3025 int xstep = (xstart < xend ? 1 : 0);
3026 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3028 int end = MAX(xend - xstart, yend - ystart);
3031 if (setup.quick_doors)
3038 for (i = start; i <= end; i++)
3040 int last_frame = end; // last frame of this "for" loop
3041 int x = xstart + i * xstep;
3042 int y = ystart + i * ystep;
3043 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3044 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3045 int xsize_size_left = (xsize - 1) * tile_size;
3046 int ysize_size_top = (ysize - 1) * tile_size;
3047 int max_xsize_pos = (max_xsize - 1) * tile_size;
3048 int max_ysize_pos = (max_ysize - 1) * tile_size;
3049 int width = xsize * tile_size;
3050 int height = ysize * tile_size;
3055 setRequestPosition(&src_x, &src_y, FALSE);
3056 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3058 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3060 for (yy = 0; yy < 2; yy++)
3062 for (xx = 0; xx < 2; xx++)
3064 int src_xx = src_x + xx * max_xsize_pos;
3065 int src_yy = src_y + yy * max_ysize_pos;
3066 int dst_xx = dst_x + xx * xsize_size_left;
3067 int dst_yy = dst_y + yy * ysize_size_top;
3068 int xx_size = (xx ? tile_size : xsize_size_left);
3069 int yy_size = (yy ? tile_size : ysize_size_top);
3072 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3073 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3075 BlitBitmap(bitmap_db_store_2, backbuffer,
3076 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3080 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3082 redraw_mask |= REDRAW_FIELD;
3086 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3089 ClearAutoRepeatKeyEvents();
3092 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3094 int graphic = IMG_BACKGROUND_REQUEST;
3095 int sound_opening = SND_REQUEST_OPENING;
3096 int sound_closing = SND_REQUEST_CLOSING;
3097 int anim_mode_1 = request.anim_mode; // (higher priority)
3098 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3099 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3100 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3101 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3103 if (game_status == GAME_MODE_PLAYING)
3104 BlitScreenToBitmap(backbuffer);
3106 SetDrawtoField(DRAW_TO_BACKBUFFER);
3108 // SetDrawBackgroundMask(REDRAW_NONE);
3110 if (action == ACTION_OPENING)
3112 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3114 if (req_state & REQ_ASK)
3116 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3117 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3118 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3119 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3121 else if (req_state & REQ_CONFIRM)
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3124 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3126 else if (req_state & REQ_PLAYER)
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3134 DrawEnvelopeRequest(text);
3137 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3139 if (action == ACTION_OPENING)
3141 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3143 if (anim_mode == ANIM_DEFAULT)
3144 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3146 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3150 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3152 if (anim_mode != ANIM_NONE)
3153 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3155 if (anim_mode == ANIM_DEFAULT)
3156 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3159 game.envelope_active = FALSE;
3161 if (action == ACTION_CLOSING)
3162 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3164 // SetDrawBackgroundMask(last_draw_background_mask);
3166 redraw_mask |= REDRAW_FIELD;
3170 if (action == ACTION_CLOSING &&
3171 game_status == GAME_MODE_PLAYING &&
3172 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3173 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3176 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3178 if (IS_MM_WALL(element))
3180 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3186 int graphic = el2preimg(element);
3188 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3189 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3194 void DrawLevel(int draw_background_mask)
3198 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3199 SetDrawBackgroundMask(draw_background_mask);
3203 for (x = BX1; x <= BX2; x++)
3204 for (y = BY1; y <= BY2; y++)
3205 DrawScreenField(x, y);
3207 redraw_mask |= REDRAW_FIELD;
3210 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3215 for (x = 0; x < size_x; x++)
3216 for (y = 0; y < size_y; y++)
3217 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3219 redraw_mask |= REDRAW_FIELD;
3222 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3226 for (x = 0; x < size_x; x++)
3227 for (y = 0; y < size_y; y++)
3228 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3230 redraw_mask |= REDRAW_FIELD;
3233 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3235 boolean show_level_border = (BorderElement != EL_EMPTY);
3236 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3237 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3238 int tile_size = preview.tile_size;
3239 int preview_width = preview.xsize * tile_size;
3240 int preview_height = preview.ysize * tile_size;
3241 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3242 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3243 int real_preview_width = real_preview_xsize * tile_size;
3244 int real_preview_height = real_preview_ysize * tile_size;
3245 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3246 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3249 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3252 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3254 dst_x += (preview_width - real_preview_width) / 2;
3255 dst_y += (preview_height - real_preview_height) / 2;
3257 for (x = 0; x < real_preview_xsize; x++)
3259 for (y = 0; y < real_preview_ysize; y++)
3261 int lx = from_x + x + (show_level_border ? -1 : 0);
3262 int ly = from_y + y + (show_level_border ? -1 : 0);
3263 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3264 getBorderElement(lx, ly));
3266 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3267 element, tile_size);
3271 redraw_mask |= REDRAW_FIELD;
3274 #define MICROLABEL_EMPTY 0
3275 #define MICROLABEL_LEVEL_NAME 1
3276 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3277 #define MICROLABEL_LEVEL_AUTHOR 3
3278 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3279 #define MICROLABEL_IMPORTED_FROM 5
3280 #define MICROLABEL_IMPORTED_BY_HEAD 6
3281 #define MICROLABEL_IMPORTED_BY 7
3283 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3285 int max_text_width = SXSIZE;
3286 int font_width = getFontWidth(font_nr);
3288 if (pos->align == ALIGN_CENTER)
3289 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3290 else if (pos->align == ALIGN_RIGHT)
3291 max_text_width = pos->x;
3293 max_text_width = SXSIZE - pos->x;
3295 return max_text_width / font_width;
3298 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3300 char label_text[MAX_OUTPUT_LINESIZE + 1];
3301 int max_len_label_text;
3302 int font_nr = pos->font;
3305 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3308 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3309 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3310 mode == MICROLABEL_IMPORTED_BY_HEAD)
3311 font_nr = pos->font_alt;
3313 max_len_label_text = getMaxTextLength(pos, font_nr);
3315 if (pos->size != -1)
3316 max_len_label_text = pos->size;
3318 for (i = 0; i < max_len_label_text; i++)
3319 label_text[i] = ' ';
3320 label_text[max_len_label_text] = '\0';
3322 if (strlen(label_text) > 0)
3323 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3326 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3327 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3328 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3329 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3330 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3331 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3332 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3333 max_len_label_text);
3334 label_text[max_len_label_text] = '\0';
3336 if (strlen(label_text) > 0)
3337 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3339 redraw_mask |= REDRAW_FIELD;
3342 static void DrawPreviewLevelLabel(int mode)
3344 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3347 static void DrawPreviewLevelInfo(int mode)
3349 if (mode == MICROLABEL_LEVEL_NAME)
3350 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3351 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3352 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3355 static void DrawPreviewLevelExt(boolean restart)
3357 static unsigned int scroll_delay = 0;
3358 static unsigned int label_delay = 0;
3359 static int from_x, from_y, scroll_direction;
3360 static int label_state, label_counter;
3361 unsigned int scroll_delay_value = preview.step_delay;
3362 boolean show_level_border = (BorderElement != EL_EMPTY);
3363 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3364 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3371 if (preview.anim_mode == ANIM_CENTERED)
3373 if (level_xsize > preview.xsize)
3374 from_x = (level_xsize - preview.xsize) / 2;
3375 if (level_ysize > preview.ysize)
3376 from_y = (level_ysize - preview.ysize) / 2;
3379 from_x += preview.xoffset;
3380 from_y += preview.yoffset;
3382 scroll_direction = MV_RIGHT;
3386 DrawPreviewLevelPlayfield(from_x, from_y);
3387 DrawPreviewLevelLabel(label_state);
3389 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3390 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3392 // initialize delay counters
3393 DelayReached(&scroll_delay, 0);
3394 DelayReached(&label_delay, 0);
3396 if (leveldir_current->name)
3398 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3399 char label_text[MAX_OUTPUT_LINESIZE + 1];
3400 int font_nr = pos->font;
3401 int max_len_label_text = getMaxTextLength(pos, font_nr);
3403 if (pos->size != -1)
3404 max_len_label_text = pos->size;
3406 strncpy(label_text, leveldir_current->name, max_len_label_text);
3407 label_text[max_len_label_text] = '\0';
3409 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3410 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3416 // scroll preview level, if needed
3417 if (preview.anim_mode != ANIM_NONE &&
3418 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3419 DelayReached(&scroll_delay, scroll_delay_value))
3421 switch (scroll_direction)
3426 from_x -= preview.step_offset;
3427 from_x = (from_x < 0 ? 0 : from_x);
3430 scroll_direction = MV_UP;
3434 if (from_x < level_xsize - preview.xsize)
3436 from_x += preview.step_offset;
3437 from_x = (from_x > level_xsize - preview.xsize ?
3438 level_xsize - preview.xsize : from_x);
3441 scroll_direction = MV_DOWN;
3447 from_y -= preview.step_offset;
3448 from_y = (from_y < 0 ? 0 : from_y);
3451 scroll_direction = MV_RIGHT;
3455 if (from_y < level_ysize - preview.ysize)
3457 from_y += preview.step_offset;
3458 from_y = (from_y > level_ysize - preview.ysize ?
3459 level_ysize - preview.ysize : from_y);
3462 scroll_direction = MV_LEFT;
3469 DrawPreviewLevelPlayfield(from_x, from_y);
3472 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3473 // redraw micro level label, if needed
3474 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3475 !strEqual(level.author, ANONYMOUS_NAME) &&
3476 !strEqual(level.author, leveldir_current->name) &&
3477 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3479 int max_label_counter = 23;
3481 if (leveldir_current->imported_from != NULL &&
3482 strlen(leveldir_current->imported_from) > 0)
3483 max_label_counter += 14;
3484 if (leveldir_current->imported_by != NULL &&
3485 strlen(leveldir_current->imported_by) > 0)
3486 max_label_counter += 14;
3488 label_counter = (label_counter + 1) % max_label_counter;
3489 label_state = (label_counter >= 0 && label_counter <= 7 ?
3490 MICROLABEL_LEVEL_NAME :
3491 label_counter >= 9 && label_counter <= 12 ?
3492 MICROLABEL_LEVEL_AUTHOR_HEAD :
3493 label_counter >= 14 && label_counter <= 21 ?
3494 MICROLABEL_LEVEL_AUTHOR :
3495 label_counter >= 23 && label_counter <= 26 ?
3496 MICROLABEL_IMPORTED_FROM_HEAD :
3497 label_counter >= 28 && label_counter <= 35 ?
3498 MICROLABEL_IMPORTED_FROM :
3499 label_counter >= 37 && label_counter <= 40 ?
3500 MICROLABEL_IMPORTED_BY_HEAD :
3501 label_counter >= 42 && label_counter <= 49 ?
3502 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3504 if (leveldir_current->imported_from == NULL &&
3505 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3506 label_state == MICROLABEL_IMPORTED_FROM))
3507 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3508 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3510 DrawPreviewLevelLabel(label_state);
3514 void DrawPreviewPlayers(void)
3516 if (game_status != GAME_MODE_MAIN)
3519 // do not draw preview players if level preview redefined, but players aren't
3520 if (preview.redefined && !menu.main.preview_players.redefined)
3523 boolean player_found[MAX_PLAYERS];
3524 int num_players = 0;
3527 for (i = 0; i < MAX_PLAYERS; i++)
3528 player_found[i] = FALSE;
3530 // check which players can be found in the level (simple approach)
3531 for (x = 0; x < lev_fieldx; x++)
3533 for (y = 0; y < lev_fieldy; y++)
3535 int element = level.field[x][y];
3537 if (ELEM_IS_PLAYER(element))
3539 int player_nr = GET_PLAYER_NR(element);
3541 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3543 if (!player_found[player_nr])
3546 player_found[player_nr] = TRUE;
3551 struct TextPosInfo *pos = &menu.main.preview_players;
3552 int tile_size = pos->tile_size;
3553 int border_size = pos->border_size;
3554 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3555 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3556 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3557 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3558 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3559 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3560 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3561 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3562 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3563 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3564 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3565 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3567 // clear area in which the players will be drawn
3568 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3569 max_players_width, max_players_height);
3571 if (!network.enabled && !setup.team_mode)
3574 // only draw players if level is suited for team mode
3575 if (num_players < 2)
3578 // draw all players that were found in the level
3579 for (i = 0; i < MAX_PLAYERS; i++)
3581 if (player_found[i])
3583 int graphic = el2img(EL_PLAYER_1 + i);
3585 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3587 xpos += player_xoffset;
3588 ypos += player_yoffset;
3593 void DrawPreviewLevelInitial(void)
3595 DrawPreviewLevelExt(TRUE);
3596 DrawPreviewPlayers();
3599 void DrawPreviewLevelAnimation(void)
3601 DrawPreviewLevelExt(FALSE);
3604 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3605 int border_size, int font_nr)
3607 int graphic = el2img(EL_PLAYER_1 + player_nr);
3608 int font_height = getFontHeight(font_nr);
3609 int player_height = MAX(tile_size, font_height);
3610 int xoffset_text = tile_size + border_size;
3611 int yoffset_text = (player_height - font_height) / 2;
3612 int yoffset_graphic = (player_height - tile_size) / 2;
3613 char *player_name = getNetworkPlayerName(player_nr + 1);
3615 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3617 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3620 static void DrawNetworkPlayersExt(boolean force)
3622 if (game_status != GAME_MODE_MAIN)
3625 if (!network.connected && !force)
3628 // do not draw network players if level preview redefined, but players aren't
3629 if (preview.redefined && !menu.main.network_players.redefined)
3632 int num_players = 0;
3635 for (i = 0; i < MAX_PLAYERS; i++)
3636 if (stored_player[i].connected_network)
3639 struct TextPosInfo *pos = &menu.main.network_players;
3640 int tile_size = pos->tile_size;
3641 int border_size = pos->border_size;
3642 int xoffset_text = tile_size + border_size;
3643 int font_nr = pos->font;
3644 int font_width = getFontWidth(font_nr);
3645 int font_height = getFontHeight(font_nr);
3646 int player_height = MAX(tile_size, font_height);
3647 int player_yoffset = player_height + border_size;
3648 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3649 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3650 int all_players_height = num_players * player_yoffset - border_size;
3651 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3652 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3653 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3655 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3656 max_players_width, max_players_height);
3658 // first draw local network player ...
3659 for (i = 0; i < MAX_PLAYERS; i++)
3661 if (stored_player[i].connected_network &&
3662 stored_player[i].connected_locally)
3664 char *player_name = getNetworkPlayerName(i + 1);
3665 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3666 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3668 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3670 ypos += player_yoffset;
3674 // ... then draw all other network players
3675 for (i = 0; i < MAX_PLAYERS; i++)
3677 if (stored_player[i].connected_network &&
3678 !stored_player[i].connected_locally)
3680 char *player_name = getNetworkPlayerName(i + 1);
3681 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3682 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3684 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3686 ypos += player_yoffset;
3691 void DrawNetworkPlayers(void)
3693 DrawNetworkPlayersExt(FALSE);
3696 void ClearNetworkPlayers(void)
3698 DrawNetworkPlayersExt(TRUE);
3701 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3702 int graphic, int sync_frame,
3705 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3707 if (mask_mode == USE_MASKING)
3708 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3710 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3713 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3714 int graphic, int sync_frame, int mask_mode)
3716 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3718 if (mask_mode == USE_MASKING)
3719 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3721 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3724 static void DrawGraphicAnimation(int x, int y, int graphic)
3726 int lx = LEVELX(x), ly = LEVELY(y);
3728 if (!IN_SCR_FIELD(x, y))
3731 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3732 graphic, GfxFrame[lx][ly], NO_MASKING);
3734 MarkTileDirty(x, y);
3737 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3739 int lx = LEVELX(x), ly = LEVELY(y);
3741 if (!IN_SCR_FIELD(x, y))
3744 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3745 graphic, GfxFrame[lx][ly], NO_MASKING);
3746 MarkTileDirty(x, y);
3749 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3751 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3754 void DrawLevelElementAnimation(int x, int y, int element)
3756 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3758 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3761 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3763 int sx = SCREENX(x), sy = SCREENY(y);
3765 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3768 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3771 DrawGraphicAnimation(sx, sy, graphic);
3774 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3775 DrawLevelFieldCrumbled(x, y);
3777 if (GFX_CRUMBLED(Tile[x][y]))
3778 DrawLevelFieldCrumbled(x, y);
3782 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3784 int sx = SCREENX(x), sy = SCREENY(y);
3787 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3790 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3792 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3795 DrawGraphicAnimation(sx, sy, graphic);
3797 if (GFX_CRUMBLED(element))
3798 DrawLevelFieldCrumbled(x, y);
3801 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3803 if (player->use_murphy)
3805 // this works only because currently only one player can be "murphy" ...
3806 static int last_horizontal_dir = MV_LEFT;
3807 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3809 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3810 last_horizontal_dir = move_dir;
3812 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3814 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3816 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3822 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3825 static boolean equalGraphics(int graphic1, int graphic2)
3827 struct GraphicInfo *g1 = &graphic_info[graphic1];
3828 struct GraphicInfo *g2 = &graphic_info[graphic2];
3830 return (g1->bitmap == g2->bitmap &&
3831 g1->src_x == g2->src_x &&
3832 g1->src_y == g2->src_y &&
3833 g1->anim_frames == g2->anim_frames &&
3834 g1->anim_delay == g2->anim_delay &&
3835 g1->anim_mode == g2->anim_mode);
3838 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3842 DRAW_PLAYER_STAGE_INIT = 0,
3843 DRAW_PLAYER_STAGE_LAST_FIELD,
3844 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3845 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3846 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3847 DRAW_PLAYER_STAGE_PLAYER,
3849 DRAW_PLAYER_STAGE_PLAYER,
3850 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3852 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3853 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3855 NUM_DRAW_PLAYER_STAGES
3858 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3860 static int static_last_player_graphic[MAX_PLAYERS];
3861 static int static_last_player_frame[MAX_PLAYERS];
3862 static boolean static_player_is_opaque[MAX_PLAYERS];
3863 static boolean draw_player[MAX_PLAYERS];
3864 int pnr = player->index_nr;
3866 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3868 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3869 static_last_player_frame[pnr] = player->Frame;
3870 static_player_is_opaque[pnr] = FALSE;
3872 draw_player[pnr] = TRUE;
3875 if (!draw_player[pnr])
3879 if (!IN_LEV_FIELD(player->jx, player->jy))
3881 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3882 Debug("draw:DrawPlayerExt", "This should never happen!");
3884 draw_player[pnr] = FALSE;
3890 int last_player_graphic = static_last_player_graphic[pnr];
3891 int last_player_frame = static_last_player_frame[pnr];
3892 boolean player_is_opaque = static_player_is_opaque[pnr];
3894 int jx = player->jx;
3895 int jy = player->jy;
3896 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3897 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3898 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3899 int last_jx = (player->is_moving ? jx - dx : jx);
3900 int last_jy = (player->is_moving ? jy - dy : jy);
3901 int next_jx = jx + dx;
3902 int next_jy = jy + dy;
3903 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3904 int sx = SCREENX(jx);
3905 int sy = SCREENY(jy);
3906 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3907 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3908 int element = Tile[jx][jy];
3909 int last_element = Tile[last_jx][last_jy];
3910 int action = (player->is_pushing ? ACTION_PUSHING :
3911 player->is_digging ? ACTION_DIGGING :
3912 player->is_collecting ? ACTION_COLLECTING :
3913 player->is_moving ? ACTION_MOVING :
3914 player->is_snapping ? ACTION_SNAPPING :
3915 player->is_dropping ? ACTION_DROPPING :
3916 player->is_waiting ? player->action_waiting :
3919 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3921 // ------------------------------------------------------------------------
3922 // initialize drawing the player
3923 // ------------------------------------------------------------------------
3925 draw_player[pnr] = FALSE;
3927 // GfxElement[][] is set to the element the player is digging or collecting;
3928 // remove also for off-screen player if the player is not moving anymore
3929 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3930 GfxElement[jx][jy] = EL_UNDEFINED;
3932 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3935 if (element == EL_EXPLOSION)
3938 InitPlayerGfxAnimation(player, action, move_dir);
3940 draw_player[pnr] = TRUE;
3942 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3944 // ------------------------------------------------------------------------
3945 // draw things in the field the player is leaving, if needed
3946 // ------------------------------------------------------------------------
3948 if (!IN_SCR_FIELD(sx, sy))
3949 draw_player[pnr] = FALSE;
3951 if (!player->is_moving)
3954 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3956 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3958 if (last_element == EL_DYNAMITE_ACTIVE ||
3959 last_element == EL_EM_DYNAMITE_ACTIVE ||
3960 last_element == EL_SP_DISK_RED_ACTIVE)
3961 DrawDynamite(last_jx, last_jy);
3963 DrawLevelFieldThruMask(last_jx, last_jy);
3965 else if (last_element == EL_DYNAMITE_ACTIVE ||
3966 last_element == EL_EM_DYNAMITE_ACTIVE ||
3967 last_element == EL_SP_DISK_RED_ACTIVE)
3968 DrawDynamite(last_jx, last_jy);
3970 DrawLevelField(last_jx, last_jy);
3972 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3973 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3975 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3977 // ------------------------------------------------------------------------
3978 // draw things behind the player, if needed
3979 // ------------------------------------------------------------------------
3983 DrawLevelElement(jx, jy, Back[jx][jy]);
3988 if (IS_ACTIVE_BOMB(element))
3990 DrawLevelElement(jx, jy, EL_EMPTY);
3995 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3997 int old_element = GfxElement[jx][jy];
3998 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3999 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4001 if (GFX_CRUMBLED(old_element))
4002 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4004 DrawGraphic(sx, sy, old_graphic, frame);
4006 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4007 static_player_is_opaque[pnr] = TRUE;
4011 GfxElement[jx][jy] = EL_UNDEFINED;
4013 // make sure that pushed elements are drawn with correct frame rate
4014 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4016 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4017 GfxFrame[jx][jy] = player->StepFrame;
4019 DrawLevelField(jx, jy);
4022 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4024 // ------------------------------------------------------------------------
4025 // draw things the player is pushing, if needed
4026 // ------------------------------------------------------------------------
4028 if (!player->is_pushing || !player->is_moving)
4031 int gfx_frame = GfxFrame[jx][jy];
4033 if (!IS_MOVING(jx, jy)) // push movement already finished
4035 element = Tile[next_jx][next_jy];
4036 gfx_frame = GfxFrame[next_jx][next_jy];
4039 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4040 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4041 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4043 // draw background element under pushed element (like the Sokoban field)
4044 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4046 // this allows transparent pushing animation over non-black background
4049 DrawLevelElement(jx, jy, Back[jx][jy]);
4051 DrawLevelElement(jx, jy, EL_EMPTY);
4053 if (Back[next_jx][next_jy])
4054 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4056 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4058 else if (Back[next_jx][next_jy])
4059 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4061 int px = SCREENX(jx), py = SCREENY(jy);
4062 int pxx = (TILEX - ABS(sxx)) * dx;
4063 int pyy = (TILEY - ABS(syy)) * dy;
4066 // do not draw (EM style) pushing animation when pushing is finished
4067 // (two-tile animations usually do not contain start and end frame)
4068 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4069 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4071 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4073 // masked drawing is needed for EMC style (double) movement graphics
4074 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4075 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4078 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4080 // ------------------------------------------------------------------------
4081 // draw player himself
4082 // ------------------------------------------------------------------------
4084 int graphic = getPlayerGraphic(player, move_dir);
4086 // in the case of changed player action or direction, prevent the current
4087 // animation frame from being restarted for identical animations
4088 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4089 player->Frame = last_player_frame;
4091 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4093 if (player_is_opaque)
4094 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4096 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4098 if (SHIELD_ON(player))
4100 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4101 IMG_SHIELD_NORMAL_ACTIVE);
4102 frame = getGraphicAnimationFrame(graphic, -1);
4104 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4107 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4109 // ------------------------------------------------------------------------
4110 // draw things in front of player (active dynamite or dynabombs)
4111 // ------------------------------------------------------------------------
4113 if (IS_ACTIVE_BOMB(element))
4115 int graphic = el2img(element);
4116 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4118 if (game.emulation == EMU_SUPAPLEX)
4119 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4121 DrawGraphicThruMask(sx, sy, graphic, frame);
4124 if (player_is_moving && last_element == EL_EXPLOSION)
4126 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4127 GfxElement[last_jx][last_jy] : EL_EMPTY);
4128 int graphic = el_act2img(element, ACTION_EXPLODING);
4129 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4130 int phase = ExplodePhase[last_jx][last_jy] - 1;
4131 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4134 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4137 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4139 // ------------------------------------------------------------------------
4140 // draw elements the player is just walking/passing through/under
4141 // ------------------------------------------------------------------------
4143 if (player_is_moving)
4145 // handle the field the player is leaving ...
4146 if (IS_ACCESSIBLE_INSIDE(last_element))
4147 DrawLevelField(last_jx, last_jy);
4148 else if (IS_ACCESSIBLE_UNDER(last_element))
4149 DrawLevelFieldThruMask(last_jx, last_jy);
4152 // do not redraw accessible elements if the player is just pushing them
4153 if (!player_is_moving || !player->is_pushing)
4155 // ... and the field the player is entering
4156 if (IS_ACCESSIBLE_INSIDE(element))
4157 DrawLevelField(jx, jy);
4158 else if (IS_ACCESSIBLE_UNDER(element))
4159 DrawLevelFieldThruMask(jx, jy);
4162 MarkTileDirty(sx, sy);
4166 void DrawPlayer(struct PlayerInfo *player)
4170 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4171 DrawPlayerExt(player, i);
4174 void DrawAllPlayers(void)
4178 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4179 for (j = 0; j < MAX_PLAYERS; j++)
4180 if (stored_player[j].active)
4181 DrawPlayerExt(&stored_player[j], i);
4184 void DrawPlayerField(int x, int y)
4186 if (!IS_PLAYER(x, y))
4189 DrawPlayer(PLAYERINFO(x, y));
4192 // ----------------------------------------------------------------------------
4194 void WaitForEventToContinue(void)
4196 boolean first_wait = TRUE;
4197 boolean still_wait = TRUE;
4199 if (program.headless)
4202 // simulate releasing mouse button over last gadget, if still pressed
4204 HandleGadgets(-1, -1, 0);
4206 button_status = MB_RELEASED;
4209 ClearPlayerAction();
4215 if (NextValidEvent(&event))
4219 case EVENT_BUTTONPRESS:
4220 case EVENT_FINGERPRESS:
4224 case EVENT_BUTTONRELEASE:
4225 case EVENT_FINGERRELEASE:
4226 still_wait = first_wait;
4229 case EVENT_KEYPRESS:
4230 case SDL_CONTROLLERBUTTONDOWN:
4231 case SDL_JOYBUTTONDOWN:
4236 HandleOtherEvents(&event);
4240 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4245 if (!PendingEvent())
4250 #define MAX_REQUEST_LINES 13
4251 #define MAX_REQUEST_LINE_FONT1_LEN 7
4252 #define MAX_REQUEST_LINE_FONT2_LEN 10
4254 static int RequestHandleEvents(unsigned int req_state)
4256 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4258 int draw_buffer_last = GetDrawtoField();
4259 int width = request.width;
4260 int height = request.height;
4264 // when showing request dialog after game ended, deactivate game panel
4265 if (game_just_ended)
4266 game.panel.active = FALSE;
4268 game.request_active = TRUE;
4270 setRequestPosition(&sx, &sy, FALSE);
4272 button_status = MB_RELEASED;
4274 request_gadget_id = -1;
4279 boolean event_handled = FALSE;
4281 if (game_just_ended)
4283 SetDrawtoField(draw_buffer_last);
4285 HandleGameActions();
4287 SetDrawtoField(DRAW_TO_BACKBUFFER);
4289 if (global.use_envelope_request)
4291 // copy current state of request area to middle of playfield area
4292 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4300 while (NextValidEvent(&event))
4302 event_handled = TRUE;
4306 case EVENT_BUTTONPRESS:
4307 case EVENT_BUTTONRELEASE:
4308 case EVENT_MOTIONNOTIFY:
4312 if (event.type == EVENT_MOTIONNOTIFY)
4317 motion_status = TRUE;
4318 mx = ((MotionEvent *) &event)->x;
4319 my = ((MotionEvent *) &event)->y;
4323 motion_status = FALSE;
4324 mx = ((ButtonEvent *) &event)->x;
4325 my = ((ButtonEvent *) &event)->y;
4326 if (event.type == EVENT_BUTTONPRESS)
4327 button_status = ((ButtonEvent *) &event)->button;
4329 button_status = MB_RELEASED;
4332 // this sets 'request_gadget_id'
4333 HandleGadgets(mx, my, button_status);
4335 switch (request_gadget_id)
4337 case TOOL_CTRL_ID_YES:
4338 case TOOL_CTRL_ID_TOUCH_YES:
4341 case TOOL_CTRL_ID_NO:
4342 case TOOL_CTRL_ID_TOUCH_NO:
4345 case TOOL_CTRL_ID_CONFIRM:
4346 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4347 result = TRUE | FALSE;
4350 case TOOL_CTRL_ID_PLAYER_1:
4353 case TOOL_CTRL_ID_PLAYER_2:
4356 case TOOL_CTRL_ID_PLAYER_3:
4359 case TOOL_CTRL_ID_PLAYER_4:
4364 // only check clickable animations if no request gadget clicked
4365 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4372 case SDL_WINDOWEVENT:
4373 HandleWindowEvent((WindowEvent *) &event);
4376 case SDL_APP_WILLENTERBACKGROUND:
4377 case SDL_APP_DIDENTERBACKGROUND:
4378 case SDL_APP_WILLENTERFOREGROUND:
4379 case SDL_APP_DIDENTERFOREGROUND:
4380 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4383 case EVENT_KEYPRESS:
4385 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4390 if (req_state & REQ_CONFIRM)
4399 #if defined(KSYM_Rewind)
4400 case KSYM_Rewind: // for Amazon Fire TV remote
4409 #if defined(KSYM_FastForward)
4410 case KSYM_FastForward: // for Amazon Fire TV remote
4416 HandleKeysDebug(key, KEY_PRESSED);
4420 if (req_state & REQ_PLAYER)
4422 int old_player_nr = setup.network_player_nr;
4425 result = old_player_nr + 1;
4430 result = old_player_nr + 1;
4461 case EVENT_FINGERRELEASE:
4462 case EVENT_KEYRELEASE:
4463 ClearPlayerAction();
4466 case SDL_CONTROLLERBUTTONDOWN:
4467 switch (event.cbutton.button)
4469 case SDL_CONTROLLER_BUTTON_A:
4470 case SDL_CONTROLLER_BUTTON_X:
4471 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4472 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4476 case SDL_CONTROLLER_BUTTON_B:
4477 case SDL_CONTROLLER_BUTTON_Y:
4478 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4479 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4480 case SDL_CONTROLLER_BUTTON_BACK:
4485 if (req_state & REQ_PLAYER)
4487 int old_player_nr = setup.network_player_nr;
4490 result = old_player_nr + 1;
4492 switch (event.cbutton.button)
4494 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4495 case SDL_CONTROLLER_BUTTON_Y:
4499 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4500 case SDL_CONTROLLER_BUTTON_B:
4504 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4505 case SDL_CONTROLLER_BUTTON_A:
4509 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4510 case SDL_CONTROLLER_BUTTON_X:
4521 case SDL_CONTROLLERBUTTONUP:
4522 HandleJoystickEvent(&event);
4523 ClearPlayerAction();
4527 HandleOtherEvents(&event);
4532 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4534 int joy = AnyJoystick();
4536 if (joy & JOY_BUTTON_1)
4538 else if (joy & JOY_BUTTON_2)
4541 else if (AnyJoystick())
4543 int joy = AnyJoystick();
4545 if (req_state & REQ_PLAYER)
4549 else if (joy & JOY_RIGHT)
4551 else if (joy & JOY_DOWN)
4553 else if (joy & JOY_LEFT)
4560 if (game_just_ended)
4562 if (global.use_envelope_request)
4564 // copy back current state of pressed buttons inside request area
4565 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4569 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4575 SetDrawtoField(draw_buffer_last);
4577 game.request_active = FALSE;
4582 static boolean RequestDoor(char *text, unsigned int req_state)
4584 unsigned int old_door_state;
4585 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4586 int font_nr = FONT_TEXT_2;
4591 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4593 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4594 font_nr = FONT_TEXT_1;
4597 if (game_status == GAME_MODE_PLAYING)
4598 BlitScreenToBitmap(backbuffer);
4600 // disable deactivated drawing when quick-loading level tape recording
4601 if (tape.playing && tape.deactivate_display)
4602 TapeDeactivateDisplayOff(TRUE);
4604 SetMouseCursor(CURSOR_DEFAULT);
4606 // pause network game while waiting for request to answer
4607 if (network.enabled &&
4608 game_status == GAME_MODE_PLAYING &&
4609 !game.all_players_gone &&
4610 req_state & REQUEST_WAIT_FOR_INPUT)
4611 SendToServer_PausePlaying();
4613 old_door_state = GetDoorState();
4615 // simulate releasing mouse button over last gadget, if still pressed
4617 HandleGadgets(-1, -1, 0);
4621 // draw released gadget before proceeding
4624 if (old_door_state & DOOR_OPEN_1)
4626 CloseDoor(DOOR_CLOSE_1);
4628 // save old door content
4629 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4630 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4633 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4634 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4636 // clear door drawing field
4637 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4639 // force DOOR font inside door area
4640 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4642 // write text for request
4643 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4645 char text_line[max_request_line_len + 1];
4651 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4653 tc = *(text_ptr + tx);
4654 // if (!tc || tc == ' ')
4655 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4659 if ((tc == '?' || tc == '!') && tl == 0)
4669 strncpy(text_line, text_ptr, tl);
4672 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4673 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4674 text_line, font_nr);
4676 text_ptr += tl + (tc == ' ' ? 1 : 0);
4677 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4682 if (req_state & REQ_ASK)
4684 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4685 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4687 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4689 else if (req_state & REQ_CONFIRM)
4691 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4692 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4694 else if (req_state & REQ_PLAYER)
4696 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4697 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4698 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4699 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4702 // copy request gadgets to door backbuffer
4703 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4705 OpenDoor(DOOR_OPEN_1);
4707 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4709 if (game_status == GAME_MODE_PLAYING)
4711 SetPanelBackground();
4712 SetDrawBackgroundMask(REDRAW_DOOR_1);
4716 SetDrawBackgroundMask(REDRAW_FIELD);
4722 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4724 // ---------- handle request buttons ----------
4725 result = RequestHandleEvents(req_state);
4729 if (!(req_state & REQ_STAY_OPEN))
4731 CloseDoor(DOOR_CLOSE_1);
4733 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4734 (req_state & REQ_REOPEN))
4735 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4740 if (game_status == GAME_MODE_PLAYING)
4742 SetPanelBackground();
4743 SetDrawBackgroundMask(REDRAW_DOOR_1);
4747 SetDrawBackgroundMask(REDRAW_FIELD);
4750 // continue network game after request
4751 if (network.enabled &&
4752 game_status == GAME_MODE_PLAYING &&
4753 !game.all_players_gone &&
4754 req_state & REQUEST_WAIT_FOR_INPUT)
4755 SendToServer_ContinuePlaying();
4757 // restore deactivated drawing when quick-loading level tape recording
4758 if (tape.playing && tape.deactivate_display)
4759 TapeDeactivateDisplayOn();
4764 static boolean RequestEnvelope(char *text, unsigned int req_state)
4768 if (game_status == GAME_MODE_PLAYING)
4769 BlitScreenToBitmap(backbuffer);
4771 // disable deactivated drawing when quick-loading level tape recording
4772 if (tape.playing && tape.deactivate_display)
4773 TapeDeactivateDisplayOff(TRUE);
4775 SetMouseCursor(CURSOR_DEFAULT);
4777 // pause network game while waiting for request to answer
4778 if (network.enabled &&
4779 game_status == GAME_MODE_PLAYING &&
4780 !game.all_players_gone &&
4781 req_state & REQUEST_WAIT_FOR_INPUT)
4782 SendToServer_PausePlaying();
4784 // simulate releasing mouse button over last gadget, if still pressed
4786 HandleGadgets(-1, -1, 0);
4790 // (replace with setting corresponding request background)
4791 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4792 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4794 // clear door drawing field
4795 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4797 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4799 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4801 if (game_status == GAME_MODE_PLAYING)
4803 SetPanelBackground();
4804 SetDrawBackgroundMask(REDRAW_DOOR_1);
4808 SetDrawBackgroundMask(REDRAW_FIELD);
4814 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4816 // ---------- handle request buttons ----------
4817 result = RequestHandleEvents(req_state);
4821 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4825 if (game_status == GAME_MODE_PLAYING)
4827 SetPanelBackground();
4828 SetDrawBackgroundMask(REDRAW_DOOR_1);
4832 SetDrawBackgroundMask(REDRAW_FIELD);
4835 // continue network game after request
4836 if (network.enabled &&
4837 game_status == GAME_MODE_PLAYING &&
4838 !game.all_players_gone &&
4839 req_state & REQUEST_WAIT_FOR_INPUT)
4840 SendToServer_ContinuePlaying();
4842 // restore deactivated drawing when quick-loading level tape recording
4843 if (tape.playing && tape.deactivate_display)
4844 TapeDeactivateDisplayOn();
4849 boolean Request(char *text, unsigned int req_state)
4851 boolean overlay_enabled = GetOverlayEnabled();
4854 game.request_active_or_moving = TRUE;
4856 SetOverlayEnabled(FALSE);
4858 if (global.use_envelope_request)
4859 result = RequestEnvelope(text, req_state);
4861 result = RequestDoor(text, req_state);
4863 SetOverlayEnabled(overlay_enabled);
4865 game.request_active_or_moving = FALSE;
4870 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4872 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4873 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4876 if (dpo1->sort_priority != dpo2->sort_priority)
4877 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4879 compare_result = dpo1->nr - dpo2->nr;
4881 return compare_result;
4884 void InitGraphicCompatibilityInfo_Doors(void)
4890 struct DoorInfo *door;
4894 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4895 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4897 { -1, -1, -1, NULL }
4899 struct Rect door_rect_list[] =
4901 { DX, DY, DXSIZE, DYSIZE },
4902 { VX, VY, VXSIZE, VYSIZE }
4906 for (i = 0; doors[i].door_token != -1; i++)
4908 int door_token = doors[i].door_token;
4909 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4910 int part_1 = doors[i].part_1;
4911 int part_8 = doors[i].part_8;
4912 int part_2 = part_1 + 1;
4913 int part_3 = part_1 + 2;
4914 struct DoorInfo *door = doors[i].door;
4915 struct Rect *door_rect = &door_rect_list[door_index];
4916 boolean door_gfx_redefined = FALSE;
4918 // check if any door part graphic definitions have been redefined
4920 for (j = 0; door_part_controls[j].door_token != -1; j++)
4922 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4923 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4925 if (dpc->door_token == door_token && fi->redefined)
4926 door_gfx_redefined = TRUE;
4929 // check for old-style door graphic/animation modifications
4931 if (!door_gfx_redefined)
4933 if (door->anim_mode & ANIM_STATIC_PANEL)
4935 door->panel.step_xoffset = 0;
4936 door->panel.step_yoffset = 0;
4939 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4941 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4942 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4943 int num_door_steps, num_panel_steps;
4945 // remove door part graphics other than the two default wings
4947 for (j = 0; door_part_controls[j].door_token != -1; j++)
4949 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4950 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4952 if (dpc->graphic >= part_3 &&
4953 dpc->graphic <= part_8)
4957 // set graphics and screen positions of the default wings
4959 g_part_1->width = door_rect->width;
4960 g_part_1->height = door_rect->height;
4961 g_part_2->width = door_rect->width;
4962 g_part_2->height = door_rect->height;
4963 g_part_2->src_x = door_rect->width;
4964 g_part_2->src_y = g_part_1->src_y;
4966 door->part_2.x = door->part_1.x;
4967 door->part_2.y = door->part_1.y;
4969 if (door->width != -1)
4971 g_part_1->width = door->width;
4972 g_part_2->width = door->width;
4974 // special treatment for graphics and screen position of right wing
4975 g_part_2->src_x += door_rect->width - door->width;
4976 door->part_2.x += door_rect->width - door->width;
4979 if (door->height != -1)
4981 g_part_1->height = door->height;
4982 g_part_2->height = door->height;
4984 // special treatment for graphics and screen position of bottom wing
4985 g_part_2->src_y += door_rect->height - door->height;
4986 door->part_2.y += door_rect->height - door->height;
4989 // set animation delays for the default wings and panels
4991 door->part_1.step_delay = door->step_delay;
4992 door->part_2.step_delay = door->step_delay;
4993 door->panel.step_delay = door->step_delay;
4995 // set animation draw order for the default wings
4997 door->part_1.sort_priority = 2; // draw left wing over ...
4998 door->part_2.sort_priority = 1; // ... right wing
5000 // set animation draw offset for the default wings
5002 if (door->anim_mode & ANIM_HORIZONTAL)
5004 door->part_1.step_xoffset = door->step_offset;
5005 door->part_1.step_yoffset = 0;
5006 door->part_2.step_xoffset = door->step_offset * -1;
5007 door->part_2.step_yoffset = 0;
5009 num_door_steps = g_part_1->width / door->step_offset;
5011 else // ANIM_VERTICAL
5013 door->part_1.step_xoffset = 0;
5014 door->part_1.step_yoffset = door->step_offset;
5015 door->part_2.step_xoffset = 0;
5016 door->part_2.step_yoffset = door->step_offset * -1;
5018 num_door_steps = g_part_1->height / door->step_offset;
5021 // set animation draw offset for the default panels
5023 if (door->step_offset > 1)
5025 num_panel_steps = 2 * door_rect->height / door->step_offset;
5026 door->panel.start_step = num_panel_steps - num_door_steps;
5027 door->panel.start_step_closing = door->panel.start_step;
5031 num_panel_steps = door_rect->height / door->step_offset;
5032 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5033 door->panel.start_step_closing = door->panel.start_step;
5034 door->panel.step_delay *= 2;
5041 void InitDoors(void)
5045 for (i = 0; door_part_controls[i].door_token != -1; i++)
5047 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5048 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5050 // initialize "start_step_opening" and "start_step_closing", if needed
5051 if (dpc->pos->start_step_opening == 0 &&
5052 dpc->pos->start_step_closing == 0)
5054 // dpc->pos->start_step_opening = dpc->pos->start_step;
5055 dpc->pos->start_step_closing = dpc->pos->start_step;
5058 // fill structure for door part draw order (sorted below)
5060 dpo->sort_priority = dpc->pos->sort_priority;
5063 // sort door part controls according to sort_priority and graphic number
5064 qsort(door_part_order, MAX_DOOR_PARTS,
5065 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5068 unsigned int OpenDoor(unsigned int door_state)
5070 if (door_state & DOOR_COPY_BACK)
5072 if (door_state & DOOR_OPEN_1)
5073 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5074 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5076 if (door_state & DOOR_OPEN_2)
5077 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5078 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5080 door_state &= ~DOOR_COPY_BACK;
5083 return MoveDoor(door_state);
5086 unsigned int CloseDoor(unsigned int door_state)
5088 unsigned int old_door_state = GetDoorState();
5090 if (!(door_state & DOOR_NO_COPY_BACK))
5092 if (old_door_state & DOOR_OPEN_1)
5093 BlitBitmap(backbuffer, bitmap_db_door_1,
5094 DX, DY, DXSIZE, DYSIZE, 0, 0);
5096 if (old_door_state & DOOR_OPEN_2)
5097 BlitBitmap(backbuffer, bitmap_db_door_2,
5098 VX, VY, VXSIZE, VYSIZE, 0, 0);
5100 door_state &= ~DOOR_NO_COPY_BACK;
5103 return MoveDoor(door_state);
5106 unsigned int GetDoorState(void)
5108 return MoveDoor(DOOR_GET_STATE);
5111 unsigned int SetDoorState(unsigned int door_state)
5113 return MoveDoor(door_state | DOOR_SET_STATE);
5116 static int euclid(int a, int b)
5118 return (b ? euclid(b, a % b) : a);
5121 unsigned int MoveDoor(unsigned int door_state)
5123 struct Rect door_rect_list[] =
5125 { DX, DY, DXSIZE, DYSIZE },
5126 { VX, VY, VXSIZE, VYSIZE }
5128 static int door1 = DOOR_CLOSE_1;
5129 static int door2 = DOOR_CLOSE_2;
5130 unsigned int door_delay = 0;
5131 unsigned int door_delay_value;
5134 if (door_state == DOOR_GET_STATE)
5135 return (door1 | door2);
5137 if (door_state & DOOR_SET_STATE)
5139 if (door_state & DOOR_ACTION_1)
5140 door1 = door_state & DOOR_ACTION_1;
5141 if (door_state & DOOR_ACTION_2)
5142 door2 = door_state & DOOR_ACTION_2;
5144 return (door1 | door2);
5147 if (!(door_state & DOOR_FORCE_REDRAW))
5149 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5150 door_state &= ~DOOR_OPEN_1;
5151 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5152 door_state &= ~DOOR_CLOSE_1;
5153 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5154 door_state &= ~DOOR_OPEN_2;
5155 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5156 door_state &= ~DOOR_CLOSE_2;
5159 if (global.autoplay_leveldir)
5161 door_state |= DOOR_NO_DELAY;
5162 door_state &= ~DOOR_CLOSE_ALL;
5165 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5166 door_state |= DOOR_NO_DELAY;
5168 if (door_state & DOOR_ACTION)
5170 boolean door_panel_drawn[NUM_DOORS];
5171 boolean panel_has_doors[NUM_DOORS];
5172 boolean door_part_skip[MAX_DOOR_PARTS];
5173 boolean door_part_done[MAX_DOOR_PARTS];
5174 boolean door_part_done_all;
5175 int num_steps[MAX_DOOR_PARTS];
5176 int max_move_delay = 0; // delay for complete animations of all doors
5177 int max_step_delay = 0; // delay (ms) between two animation frames
5178 int num_move_steps = 0; // number of animation steps for all doors
5179 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5180 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5181 int current_move_delay = 0;
5185 for (i = 0; i < NUM_DOORS; i++)
5186 panel_has_doors[i] = FALSE;
5188 for (i = 0; i < MAX_DOOR_PARTS; i++)
5190 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5191 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5192 int door_token = dpc->door_token;
5194 door_part_done[i] = FALSE;
5195 door_part_skip[i] = (!(door_state & door_token) ||
5199 for (i = 0; i < MAX_DOOR_PARTS; i++)
5201 int nr = door_part_order[i].nr;
5202 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5203 struct DoorPartPosInfo *pos = dpc->pos;
5204 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5205 int door_token = dpc->door_token;
5206 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5207 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5208 int step_xoffset = ABS(pos->step_xoffset);
5209 int step_yoffset = ABS(pos->step_yoffset);
5210 int step_delay = pos->step_delay;
5211 int current_door_state = door_state & door_token;
5212 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5213 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5214 boolean part_opening = (is_panel ? door_closing : door_opening);
5215 int start_step = (part_opening ? pos->start_step_opening :
5216 pos->start_step_closing);
5217 float move_xsize = (step_xoffset ? g->width : 0);
5218 float move_ysize = (step_yoffset ? g->height : 0);
5219 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5220 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5221 int move_steps = (move_xsteps && move_ysteps ?
5222 MIN(move_xsteps, move_ysteps) :
5223 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5224 int move_delay = move_steps * step_delay;
5226 if (door_part_skip[nr])
5229 max_move_delay = MAX(max_move_delay, move_delay);
5230 max_step_delay = (max_step_delay == 0 ? step_delay :
5231 euclid(max_step_delay, step_delay));
5232 num_steps[nr] = move_steps;
5236 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5238 panel_has_doors[door_index] = TRUE;
5242 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5244 num_move_steps = max_move_delay / max_step_delay;
5245 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5247 door_delay_value = max_step_delay;
5249 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5251 start = num_move_steps - 1;
5255 // opening door sound has priority over simultaneously closing door
5256 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5258 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5260 if (door_state & DOOR_OPEN_1)
5261 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5262 if (door_state & DOOR_OPEN_2)
5263 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5265 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5267 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5269 if (door_state & DOOR_CLOSE_1)
5270 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5271 if (door_state & DOOR_CLOSE_2)
5272 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5276 for (k = start; k < num_move_steps; k++)
5278 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5280 door_part_done_all = TRUE;
5282 for (i = 0; i < NUM_DOORS; i++)
5283 door_panel_drawn[i] = FALSE;
5285 for (i = 0; i < MAX_DOOR_PARTS; i++)
5287 int nr = door_part_order[i].nr;
5288 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5289 struct DoorPartPosInfo *pos = dpc->pos;
5290 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5291 int door_token = dpc->door_token;
5292 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5293 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5294 boolean is_panel_and_door_has_closed = FALSE;
5295 struct Rect *door_rect = &door_rect_list[door_index];
5296 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5298 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5299 int current_door_state = door_state & door_token;
5300 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5301 boolean door_closing = !door_opening;
5302 boolean part_opening = (is_panel ? door_closing : door_opening);
5303 boolean part_closing = !part_opening;
5304 int start_step = (part_opening ? pos->start_step_opening :
5305 pos->start_step_closing);
5306 int step_delay = pos->step_delay;
5307 int step_factor = step_delay / max_step_delay;
5308 int k1 = (step_factor ? k / step_factor + 1 : k);
5309 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5310 int kk = MAX(0, k2);
5313 int src_x, src_y, src_xx, src_yy;
5314 int dst_x, dst_y, dst_xx, dst_yy;
5317 if (door_part_skip[nr])
5320 if (!(door_state & door_token))
5328 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5329 int kk_door = MAX(0, k2_door);
5330 int sync_frame = kk_door * door_delay_value;
5331 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5333 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5334 &g_src_x, &g_src_y);
5339 if (!door_panel_drawn[door_index])
5341 ClearRectangle(drawto, door_rect->x, door_rect->y,
5342 door_rect->width, door_rect->height);
5344 door_panel_drawn[door_index] = TRUE;
5347 // draw opening or closing door parts
5349 if (pos->step_xoffset < 0) // door part on right side
5352 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5355 if (dst_xx + width > door_rect->width)
5356 width = door_rect->width - dst_xx;
5358 else // door part on left side
5361 dst_xx = pos->x - kk * pos->step_xoffset;
5365 src_xx = ABS(dst_xx);
5369 width = g->width - src_xx;
5371 if (width > door_rect->width)
5372 width = door_rect->width;
5374 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5377 if (pos->step_yoffset < 0) // door part on bottom side
5380 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5383 if (dst_yy + height > door_rect->height)
5384 height = door_rect->height - dst_yy;
5386 else // door part on top side
5389 dst_yy = pos->y - kk * pos->step_yoffset;
5393 src_yy = ABS(dst_yy);
5397 height = g->height - src_yy;
5400 src_x = g_src_x + src_xx;
5401 src_y = g_src_y + src_yy;
5403 dst_x = door_rect->x + dst_xx;
5404 dst_y = door_rect->y + dst_yy;
5406 is_panel_and_door_has_closed =
5409 panel_has_doors[door_index] &&
5410 k >= num_move_steps_doors_only - 1);
5412 if (width >= 0 && width <= g->width &&
5413 height >= 0 && height <= g->height &&
5414 !is_panel_and_door_has_closed)
5416 if (is_panel || !pos->draw_masked)
5417 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5420 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5424 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5426 if ((part_opening && (width < 0 || height < 0)) ||
5427 (part_closing && (width >= g->width && height >= g->height)))
5428 door_part_done[nr] = TRUE;
5430 // continue door part animations, but not panel after door has closed
5431 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5432 door_part_done_all = FALSE;
5435 if (!(door_state & DOOR_NO_DELAY))
5439 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5441 current_move_delay += max_step_delay;
5443 // prevent OS (Windows) from complaining about program not responding
5447 if (door_part_done_all)
5451 if (!(door_state & DOOR_NO_DELAY))
5453 // wait for specified door action post delay
5454 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5455 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5456 else if (door_state & DOOR_ACTION_1)
5457 door_delay_value = door_1.post_delay;
5458 else if (door_state & DOOR_ACTION_2)
5459 door_delay_value = door_2.post_delay;
5461 while (!DelayReached(&door_delay, door_delay_value))
5466 if (door_state & DOOR_ACTION_1)
5467 door1 = door_state & DOOR_ACTION_1;
5468 if (door_state & DOOR_ACTION_2)
5469 door2 = door_state & DOOR_ACTION_2;
5471 // draw masked border over door area
5472 DrawMaskedBorder(REDRAW_DOOR_1);
5473 DrawMaskedBorder(REDRAW_DOOR_2);
5475 ClearAutoRepeatKeyEvents();
5477 return (door1 | door2);
5480 static boolean useSpecialEditorDoor(void)
5482 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5483 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5485 // do not draw special editor door if editor border defined or redefined
5486 if (graphic_info[graphic].bitmap != NULL || redefined)
5489 // do not draw special editor door if global border defined to be empty
5490 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5493 // do not draw special editor door if viewport definitions do not match
5497 EY + EYSIZE != VY + VYSIZE)
5503 void DrawSpecialEditorDoor(void)
5505 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5506 int top_border_width = gfx1->width;
5507 int top_border_height = gfx1->height;
5508 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5509 int ex = EX - outer_border;
5510 int ey = EY - outer_border;
5511 int vy = VY - outer_border;
5512 int exsize = EXSIZE + 2 * outer_border;
5514 if (!useSpecialEditorDoor())
5517 // draw bigger level editor toolbox window
5518 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5519 top_border_width, top_border_height, ex, ey - top_border_height);
5520 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5521 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5523 redraw_mask |= REDRAW_ALL;
5526 void UndrawSpecialEditorDoor(void)
5528 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5529 int top_border_width = gfx1->width;
5530 int top_border_height = gfx1->height;
5531 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5532 int ex = EX - outer_border;
5533 int ey = EY - outer_border;
5534 int ey_top = ey - top_border_height;
5535 int exsize = EXSIZE + 2 * outer_border;
5536 int eysize = EYSIZE + 2 * outer_border;
5538 if (!useSpecialEditorDoor())
5541 // draw normal tape recorder window
5542 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5544 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5545 ex, ey_top, top_border_width, top_border_height,
5547 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5548 ex, ey, exsize, eysize, ex, ey);
5552 // if screen background is set to "[NONE]", clear editor toolbox window
5553 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5554 ClearRectangle(drawto, ex, ey, exsize, eysize);
5557 redraw_mask |= REDRAW_ALL;
5561 // ---------- new tool button stuff -------------------------------------------
5566 struct TextPosInfo *pos;
5568 boolean is_touch_button;
5570 } toolbutton_info[NUM_TOOL_BUTTONS] =
5573 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5574 TOOL_CTRL_ID_YES, FALSE, "yes"
5577 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5578 TOOL_CTRL_ID_NO, FALSE, "no"
5581 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5582 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5585 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5586 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5589 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5590 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5593 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5594 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5597 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5598 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5601 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5602 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5605 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5606 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5609 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5610 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5614 void CreateToolButtons(void)
5618 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5620 int graphic = toolbutton_info[i].graphic;
5621 struct GraphicInfo *gfx = &graphic_info[graphic];
5622 struct TextPosInfo *pos = toolbutton_info[i].pos;
5623 struct GadgetInfo *gi;
5624 Bitmap *deco_bitmap = None;
5625 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5626 unsigned int event_mask = GD_EVENT_RELEASED;
5627 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5628 int base_x = (is_touch_button ? 0 : DX);
5629 int base_y = (is_touch_button ? 0 : DY);
5630 int gd_x = gfx->src_x;
5631 int gd_y = gfx->src_y;
5632 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5633 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5638 if (global.use_envelope_request && !is_touch_button)
5640 setRequestPosition(&base_x, &base_y, TRUE);
5642 // check if request buttons are outside of envelope and fix, if needed
5643 if (x < 0 || x + gfx->width > request.width ||
5644 y < 0 || y + gfx->height > request.height)
5646 if (id == TOOL_CTRL_ID_YES)
5649 y = request.height - 2 * request.border_size - gfx->height;
5651 else if (id == TOOL_CTRL_ID_NO)
5653 x = request.width - 2 * request.border_size - gfx->width;
5654 y = request.height - 2 * request.border_size - gfx->height;
5656 else if (id == TOOL_CTRL_ID_CONFIRM)
5658 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5659 y = request.height - 2 * request.border_size - gfx->height;
5661 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5663 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5665 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5666 y = request.height - 2 * request.border_size - gfx->height * 2;
5668 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5669 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5674 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5676 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5678 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5679 pos->size, &deco_bitmap, &deco_x, &deco_y);
5680 deco_xpos = (gfx->width - pos->size) / 2;
5681 deco_ypos = (gfx->height - pos->size) / 2;
5684 gi = CreateGadget(GDI_CUSTOM_ID, id,
5685 GDI_IMAGE_ID, graphic,
5686 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5689 GDI_WIDTH, gfx->width,
5690 GDI_HEIGHT, gfx->height,
5691 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5692 GDI_STATE, GD_BUTTON_UNPRESSED,
5693 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5694 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5695 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5696 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5697 GDI_DECORATION_SIZE, pos->size, pos->size,
5698 GDI_DECORATION_SHIFTING, 1, 1,
5699 GDI_DIRECT_DRAW, FALSE,
5700 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5701 GDI_EVENT_MASK, event_mask,
5702 GDI_CALLBACK_ACTION, HandleToolButtons,
5706 Fail("cannot create gadget");
5708 tool_gadget[id] = gi;
5712 void FreeToolButtons(void)
5716 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5717 FreeGadget(tool_gadget[i]);
5720 static void UnmapToolButtons(void)
5724 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5725 UnmapGadget(tool_gadget[i]);
5728 static void HandleToolButtons(struct GadgetInfo *gi)
5730 request_gadget_id = gi->custom_id;
5733 static struct Mapping_EM_to_RND_object
5736 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5737 boolean is_backside; // backside of moving element
5743 em_object_mapping_list[GAME_TILE_MAX + 1] =
5746 Zborder, FALSE, FALSE,
5750 Zplayer, FALSE, FALSE,
5759 Ztank, FALSE, FALSE,
5763 Zeater, FALSE, FALSE,
5767 Zdynamite, FALSE, FALSE,
5771 Zboom, FALSE, FALSE,
5776 Xchain, FALSE, FALSE,
5777 EL_DEFAULT, ACTION_EXPLODING, -1
5780 Xboom_bug, FALSE, FALSE,
5781 EL_BUG, ACTION_EXPLODING, -1
5784 Xboom_tank, FALSE, FALSE,
5785 EL_SPACESHIP, ACTION_EXPLODING, -1
5788 Xboom_android, FALSE, FALSE,
5789 EL_EMC_ANDROID, ACTION_OTHER, -1
5792 Xboom_1, FALSE, FALSE,
5793 EL_DEFAULT, ACTION_EXPLODING, -1
5796 Xboom_2, FALSE, FALSE,
5797 EL_DEFAULT, ACTION_EXPLODING, -1
5801 Xblank, TRUE, FALSE,
5806 Xsplash_e, FALSE, FALSE,
5807 EL_ACID_SPLASH_RIGHT, -1, -1
5810 Xsplash_w, FALSE, FALSE,
5811 EL_ACID_SPLASH_LEFT, -1, -1
5815 Xplant, TRUE, FALSE,
5816 EL_EMC_PLANT, -1, -1
5819 Yplant, FALSE, FALSE,
5820 EL_EMC_PLANT, -1, -1
5824 Xacid_1, TRUE, FALSE,
5828 Xacid_2, FALSE, FALSE,
5832 Xacid_3, FALSE, FALSE,
5836 Xacid_4, FALSE, FALSE,
5840 Xacid_5, FALSE, FALSE,
5844 Xacid_6, FALSE, FALSE,
5848 Xacid_7, FALSE, FALSE,
5852 Xacid_8, FALSE, FALSE,
5857 Xfake_acid_1, TRUE, FALSE,
5858 EL_EMC_FAKE_ACID, -1, -1
5861 Xfake_acid_2, FALSE, FALSE,
5862 EL_EMC_FAKE_ACID, -1, -1
5865 Xfake_acid_3, FALSE, FALSE,
5866 EL_EMC_FAKE_ACID, -1, -1
5869 Xfake_acid_4, FALSE, FALSE,
5870 EL_EMC_FAKE_ACID, -1, -1
5873 Xfake_acid_5, FALSE, FALSE,
5874 EL_EMC_FAKE_ACID, -1, -1
5877 Xfake_acid_6, FALSE, FALSE,
5878 EL_EMC_FAKE_ACID, -1, -1
5881 Xfake_acid_7, FALSE, FALSE,
5882 EL_EMC_FAKE_ACID, -1, -1
5885 Xfake_acid_8, FALSE, FALSE,
5886 EL_EMC_FAKE_ACID, -1, -1
5890 Xfake_acid_1_player, FALSE, FALSE,
5891 EL_EMC_FAKE_ACID, -1, -1
5894 Xfake_acid_2_player, FALSE, FALSE,
5895 EL_EMC_FAKE_ACID, -1, -1
5898 Xfake_acid_3_player, FALSE, FALSE,
5899 EL_EMC_FAKE_ACID, -1, -1
5902 Xfake_acid_4_player, FALSE, FALSE,
5903 EL_EMC_FAKE_ACID, -1, -1
5906 Xfake_acid_5_player, FALSE, FALSE,
5907 EL_EMC_FAKE_ACID, -1, -1
5910 Xfake_acid_6_player, FALSE, FALSE,
5911 EL_EMC_FAKE_ACID, -1, -1
5914 Xfake_acid_7_player, FALSE, FALSE,
5915 EL_EMC_FAKE_ACID, -1, -1
5918 Xfake_acid_8_player, FALSE, FALSE,
5919 EL_EMC_FAKE_ACID, -1, -1
5923 Xgrass, TRUE, FALSE,
5924 EL_EMC_GRASS, -1, -1
5927 Ygrass_nB, FALSE, FALSE,
5928 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5931 Ygrass_eB, FALSE, FALSE,
5932 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5935 Ygrass_sB, FALSE, FALSE,
5936 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5939 Ygrass_wB, FALSE, FALSE,
5940 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5948 Ydirt_nB, FALSE, FALSE,
5949 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5952 Ydirt_eB, FALSE, FALSE,
5953 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5956 Ydirt_sB, FALSE, FALSE,
5957 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5960 Ydirt_wB, FALSE, FALSE,
5961 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5965 Xandroid, TRUE, FALSE,
5966 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5969 Xandroid_1_n, FALSE, FALSE,
5970 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5973 Xandroid_2_n, FALSE, FALSE,
5974 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5977 Xandroid_1_e, FALSE, FALSE,
5978 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5981 Xandroid_2_e, FALSE, FALSE,
5982 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5985 Xandroid_1_w, FALSE, FALSE,
5986 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5989 Xandroid_2_w, FALSE, FALSE,
5990 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5993 Xandroid_1_s, FALSE, FALSE,
5994 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5997 Xandroid_2_s, FALSE, FALSE,
5998 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6001 Yandroid_n, FALSE, FALSE,
6002 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6005 Yandroid_nB, FALSE, TRUE,
6006 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6009 Yandroid_ne, FALSE, FALSE,
6010 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6013 Yandroid_neB, FALSE, TRUE,
6014 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6017 Yandroid_e, FALSE, FALSE,
6018 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6021 Yandroid_eB, FALSE, TRUE,
6022 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6025 Yandroid_se, FALSE, FALSE,
6026 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6029 Yandroid_seB, FALSE, TRUE,
6030 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6033 Yandroid_s, FALSE, FALSE,
6034 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6037 Yandroid_sB, FALSE, TRUE,
6038 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6041 Yandroid_sw, FALSE, FALSE,
6042 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6045 Yandroid_swB, FALSE, TRUE,
6046 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6049 Yandroid_w, FALSE, FALSE,
6050 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6053 Yandroid_wB, FALSE, TRUE,
6054 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6057 Yandroid_nw, FALSE, FALSE,
6058 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6061 Yandroid_nwB, FALSE, TRUE,
6062 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6066 Xeater_n, TRUE, FALSE,
6067 EL_YAMYAM_UP, -1, -1
6070 Xeater_e, TRUE, FALSE,
6071 EL_YAMYAM_RIGHT, -1, -1
6074 Xeater_w, TRUE, FALSE,
6075 EL_YAMYAM_LEFT, -1, -1
6078 Xeater_s, TRUE, FALSE,
6079 EL_YAMYAM_DOWN, -1, -1
6082 Yeater_n, FALSE, FALSE,
6083 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6086 Yeater_nB, FALSE, TRUE,
6087 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6090 Yeater_e, FALSE, FALSE,
6091 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6094 Yeater_eB, FALSE, TRUE,
6095 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6098 Yeater_s, FALSE, FALSE,
6099 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6102 Yeater_sB, FALSE, TRUE,
6103 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6106 Yeater_w, FALSE, FALSE,
6107 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6110 Yeater_wB, FALSE, TRUE,
6111 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6114 Yeater_stone, FALSE, FALSE,
6115 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6118 Yeater_spring, FALSE, FALSE,
6119 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6123 Xalien, TRUE, FALSE,
6127 Xalien_pause, FALSE, FALSE,
6131 Yalien_n, FALSE, FALSE,
6132 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6135 Yalien_nB, FALSE, TRUE,
6136 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6139 Yalien_e, FALSE, FALSE,
6140 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6143 Yalien_eB, FALSE, TRUE,
6144 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6147 Yalien_s, FALSE, FALSE,
6148 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6151 Yalien_sB, FALSE, TRUE,
6152 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6155 Yalien_w, FALSE, FALSE,
6156 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6159 Yalien_wB, FALSE, TRUE,
6160 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6163 Yalien_stone, FALSE, FALSE,
6164 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6167 Yalien_spring, FALSE, FALSE,
6168 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6172 Xbug_1_n, TRUE, FALSE,
6176 Xbug_1_e, TRUE, FALSE,
6177 EL_BUG_RIGHT, -1, -1
6180 Xbug_1_s, TRUE, FALSE,
6184 Xbug_1_w, TRUE, FALSE,
6188 Xbug_2_n, FALSE, FALSE,
6192 Xbug_2_e, FALSE, FALSE,
6193 EL_BUG_RIGHT, -1, -1
6196 Xbug_2_s, FALSE, FALSE,
6200 Xbug_2_w, FALSE, FALSE,
6204 Ybug_n, FALSE, FALSE,
6205 EL_BUG, ACTION_MOVING, MV_BIT_UP
6208 Ybug_nB, FALSE, TRUE,
6209 EL_BUG, ACTION_MOVING, MV_BIT_UP
6212 Ybug_e, FALSE, FALSE,
6213 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6216 Ybug_eB, FALSE, TRUE,
6217 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6220 Ybug_s, FALSE, FALSE,
6221 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6224 Ybug_sB, FALSE, TRUE,
6225 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6228 Ybug_w, FALSE, FALSE,
6229 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6232 Ybug_wB, FALSE, TRUE,
6233 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6236 Ybug_w_n, FALSE, FALSE,
6237 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6240 Ybug_n_e, FALSE, FALSE,
6241 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6244 Ybug_e_s, FALSE, FALSE,
6245 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6248 Ybug_s_w, FALSE, FALSE,
6249 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6252 Ybug_e_n, FALSE, FALSE,
6253 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6256 Ybug_s_e, FALSE, FALSE,
6257 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6260 Ybug_w_s, FALSE, FALSE,
6261 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6264 Ybug_n_w, FALSE, FALSE,
6265 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6268 Ybug_stone, FALSE, FALSE,
6269 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6272 Ybug_spring, FALSE, FALSE,
6273 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6277 Xtank_1_n, TRUE, FALSE,
6278 EL_SPACESHIP_UP, -1, -1
6281 Xtank_1_e, TRUE, FALSE,
6282 EL_SPACESHIP_RIGHT, -1, -1
6285 Xtank_1_s, TRUE, FALSE,
6286 EL_SPACESHIP_DOWN, -1, -1
6289 Xtank_1_w, TRUE, FALSE,
6290 EL_SPACESHIP_LEFT, -1, -1
6293 Xtank_2_n, FALSE, FALSE,
6294 EL_SPACESHIP_UP, -1, -1
6297 Xtank_2_e, FALSE, FALSE,
6298 EL_SPACESHIP_RIGHT, -1, -1
6301 Xtank_2_s, FALSE, FALSE,
6302 EL_SPACESHIP_DOWN, -1, -1
6305 Xtank_2_w, FALSE, FALSE,
6306 EL_SPACESHIP_LEFT, -1, -1
6309 Ytank_n, FALSE, FALSE,
6310 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6313 Ytank_nB, FALSE, TRUE,
6314 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6317 Ytank_e, FALSE, FALSE,
6318 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6321 Ytank_eB, FALSE, TRUE,
6322 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6325 Ytank_s, FALSE, FALSE,
6326 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6329 Ytank_sB, FALSE, TRUE,
6330 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6333 Ytank_w, FALSE, FALSE,
6334 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6337 Ytank_wB, FALSE, TRUE,
6338 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6341 Ytank_w_n, FALSE, FALSE,
6342 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6345 Ytank_n_e, FALSE, FALSE,
6346 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6349 Ytank_e_s, FALSE, FALSE,
6350 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6353 Ytank_s_w, FALSE, FALSE,
6354 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6357 Ytank_e_n, FALSE, FALSE,
6358 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6361 Ytank_s_e, FALSE, FALSE,
6362 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6365 Ytank_w_s, FALSE, FALSE,
6366 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6369 Ytank_n_w, FALSE, FALSE,
6370 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6373 Ytank_stone, FALSE, FALSE,
6374 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6377 Ytank_spring, FALSE, FALSE,
6378 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6382 Xemerald, TRUE, FALSE,
6386 Xemerald_pause, FALSE, FALSE,
6390 Xemerald_fall, FALSE, FALSE,
6394 Xemerald_shine, FALSE, FALSE,
6395 EL_EMERALD, ACTION_TWINKLING, -1
6398 Yemerald_s, FALSE, FALSE,
6399 EL_EMERALD, ACTION_FALLING, -1
6402 Yemerald_sB, FALSE, TRUE,
6403 EL_EMERALD, ACTION_FALLING, -1
6406 Yemerald_e, FALSE, FALSE,
6407 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6410 Yemerald_eB, FALSE, TRUE,
6411 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6414 Yemerald_w, FALSE, FALSE,
6415 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6418 Yemerald_wB, FALSE, TRUE,
6419 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6422 Yemerald_blank, FALSE, FALSE,
6423 EL_EMERALD, ACTION_COLLECTING, -1
6427 Xdiamond, TRUE, FALSE,
6431 Xdiamond_pause, FALSE, FALSE,
6435 Xdiamond_fall, FALSE, FALSE,
6439 Xdiamond_shine, FALSE, FALSE,
6440 EL_DIAMOND, ACTION_TWINKLING, -1
6443 Ydiamond_s, FALSE, FALSE,
6444 EL_DIAMOND, ACTION_FALLING, -1
6447 Ydiamond_sB, FALSE, TRUE,
6448 EL_DIAMOND, ACTION_FALLING, -1
6451 Ydiamond_e, FALSE, FALSE,
6452 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6455 Ydiamond_eB, FALSE, TRUE,
6456 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6459 Ydiamond_w, FALSE, FALSE,
6460 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6463 Ydiamond_wB, FALSE, TRUE,
6464 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6467 Ydiamond_blank, FALSE, FALSE,
6468 EL_DIAMOND, ACTION_COLLECTING, -1
6471 Ydiamond_stone, FALSE, FALSE,
6472 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6476 Xstone, TRUE, FALSE,
6480 Xstone_pause, FALSE, FALSE,
6484 Xstone_fall, FALSE, FALSE,
6488 Ystone_s, FALSE, FALSE,
6489 EL_ROCK, ACTION_FALLING, -1
6492 Ystone_sB, FALSE, TRUE,
6493 EL_ROCK, ACTION_FALLING, -1
6496 Ystone_e, FALSE, FALSE,
6497 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6500 Ystone_eB, FALSE, TRUE,
6501 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6504 Ystone_w, FALSE, FALSE,
6505 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6508 Ystone_wB, FALSE, TRUE,
6509 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6517 Xbomb_pause, FALSE, FALSE,
6521 Xbomb_fall, FALSE, FALSE,
6525 Ybomb_s, FALSE, FALSE,
6526 EL_BOMB, ACTION_FALLING, -1
6529 Ybomb_sB, FALSE, TRUE,
6530 EL_BOMB, ACTION_FALLING, -1
6533 Ybomb_e, FALSE, FALSE,
6534 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6537 Ybomb_eB, FALSE, TRUE,
6538 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6541 Ybomb_w, FALSE, FALSE,
6542 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6545 Ybomb_wB, FALSE, TRUE,
6546 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6549 Ybomb_blank, FALSE, FALSE,
6550 EL_BOMB, ACTION_ACTIVATING, -1
6558 Xnut_pause, FALSE, FALSE,
6562 Xnut_fall, FALSE, FALSE,
6566 Ynut_s, FALSE, FALSE,
6567 EL_NUT, ACTION_FALLING, -1
6570 Ynut_sB, FALSE, TRUE,
6571 EL_NUT, ACTION_FALLING, -1
6574 Ynut_e, FALSE, FALSE,
6575 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6578 Ynut_eB, FALSE, TRUE,
6579 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6582 Ynut_w, FALSE, FALSE,
6583 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6586 Ynut_wB, FALSE, TRUE,
6587 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6590 Ynut_stone, FALSE, FALSE,
6591 EL_NUT, ACTION_BREAKING, -1
6595 Xspring, TRUE, FALSE,
6599 Xspring_pause, FALSE, FALSE,
6603 Xspring_e, TRUE, FALSE,
6604 EL_SPRING_RIGHT, -1, -1
6607 Xspring_w, TRUE, FALSE,
6608 EL_SPRING_LEFT, -1, -1
6611 Xspring_fall, FALSE, FALSE,
6615 Yspring_s, FALSE, FALSE,
6616 EL_SPRING, ACTION_FALLING, -1
6619 Yspring_sB, FALSE, TRUE,
6620 EL_SPRING, ACTION_FALLING, -1
6623 Yspring_e, FALSE, FALSE,
6624 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6627 Yspring_eB, FALSE, TRUE,
6628 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6631 Yspring_w, FALSE, FALSE,
6632 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6635 Yspring_wB, FALSE, TRUE,
6636 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6639 Yspring_alien_e, FALSE, FALSE,
6640 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6643 Yspring_alien_eB, FALSE, TRUE,
6644 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6647 Yspring_alien_w, FALSE, FALSE,
6648 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6651 Yspring_alien_wB, FALSE, TRUE,
6652 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6656 Xpush_emerald_e, FALSE, FALSE,
6657 EL_EMERALD, -1, MV_BIT_RIGHT
6660 Xpush_emerald_w, FALSE, FALSE,
6661 EL_EMERALD, -1, MV_BIT_LEFT
6664 Xpush_diamond_e, FALSE, FALSE,
6665 EL_DIAMOND, -1, MV_BIT_RIGHT
6668 Xpush_diamond_w, FALSE, FALSE,
6669 EL_DIAMOND, -1, MV_BIT_LEFT
6672 Xpush_stone_e, FALSE, FALSE,
6673 EL_ROCK, -1, MV_BIT_RIGHT
6676 Xpush_stone_w, FALSE, FALSE,
6677 EL_ROCK, -1, MV_BIT_LEFT
6680 Xpush_bomb_e, FALSE, FALSE,
6681 EL_BOMB, -1, MV_BIT_RIGHT
6684 Xpush_bomb_w, FALSE, FALSE,
6685 EL_BOMB, -1, MV_BIT_LEFT
6688 Xpush_nut_e, FALSE, FALSE,
6689 EL_NUT, -1, MV_BIT_RIGHT
6692 Xpush_nut_w, FALSE, FALSE,
6693 EL_NUT, -1, MV_BIT_LEFT
6696 Xpush_spring_e, FALSE, FALSE,
6697 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6700 Xpush_spring_w, FALSE, FALSE,
6701 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6705 Xdynamite, TRUE, FALSE,
6706 EL_EM_DYNAMITE, -1, -1
6709 Ydynamite_blank, FALSE, FALSE,
6710 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6713 Xdynamite_1, TRUE, FALSE,
6714 EL_EM_DYNAMITE_ACTIVE, -1, -1
6717 Xdynamite_2, FALSE, FALSE,
6718 EL_EM_DYNAMITE_ACTIVE, -1, -1
6721 Xdynamite_3, FALSE, FALSE,
6722 EL_EM_DYNAMITE_ACTIVE, -1, -1
6725 Xdynamite_4, FALSE, FALSE,
6726 EL_EM_DYNAMITE_ACTIVE, -1, -1
6730 Xkey_1, TRUE, FALSE,
6734 Xkey_2, TRUE, FALSE,
6738 Xkey_3, TRUE, FALSE,
6742 Xkey_4, TRUE, FALSE,
6746 Xkey_5, TRUE, FALSE,
6747 EL_EMC_KEY_5, -1, -1
6750 Xkey_6, TRUE, FALSE,
6751 EL_EMC_KEY_6, -1, -1
6754 Xkey_7, TRUE, FALSE,
6755 EL_EMC_KEY_7, -1, -1
6758 Xkey_8, TRUE, FALSE,
6759 EL_EMC_KEY_8, -1, -1
6763 Xdoor_1, TRUE, FALSE,
6764 EL_EM_GATE_1, -1, -1
6767 Xdoor_2, TRUE, FALSE,
6768 EL_EM_GATE_2, -1, -1
6771 Xdoor_3, TRUE, FALSE,
6772 EL_EM_GATE_3, -1, -1
6775 Xdoor_4, TRUE, FALSE,
6776 EL_EM_GATE_4, -1, -1
6779 Xdoor_5, TRUE, FALSE,
6780 EL_EMC_GATE_5, -1, -1
6783 Xdoor_6, TRUE, FALSE,
6784 EL_EMC_GATE_6, -1, -1
6787 Xdoor_7, TRUE, FALSE,
6788 EL_EMC_GATE_7, -1, -1
6791 Xdoor_8, TRUE, FALSE,
6792 EL_EMC_GATE_8, -1, -1
6796 Xfake_door_1, TRUE, FALSE,
6797 EL_EM_GATE_1_GRAY, -1, -1
6800 Xfake_door_2, TRUE, FALSE,
6801 EL_EM_GATE_2_GRAY, -1, -1
6804 Xfake_door_3, TRUE, FALSE,
6805 EL_EM_GATE_3_GRAY, -1, -1
6808 Xfake_door_4, TRUE, FALSE,
6809 EL_EM_GATE_4_GRAY, -1, -1
6812 Xfake_door_5, TRUE, FALSE,
6813 EL_EMC_GATE_5_GRAY, -1, -1
6816 Xfake_door_6, TRUE, FALSE,
6817 EL_EMC_GATE_6_GRAY, -1, -1
6820 Xfake_door_7, TRUE, FALSE,
6821 EL_EMC_GATE_7_GRAY, -1, -1
6824 Xfake_door_8, TRUE, FALSE,
6825 EL_EMC_GATE_8_GRAY, -1, -1
6829 Xballoon, TRUE, FALSE,
6833 Yballoon_n, FALSE, FALSE,
6834 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6837 Yballoon_nB, FALSE, TRUE,
6838 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6841 Yballoon_e, FALSE, FALSE,
6842 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6845 Yballoon_eB, FALSE, TRUE,
6846 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6849 Yballoon_s, FALSE, FALSE,
6850 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6853 Yballoon_sB, FALSE, TRUE,
6854 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6857 Yballoon_w, FALSE, FALSE,
6858 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6861 Yballoon_wB, FALSE, TRUE,
6862 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6866 Xball_1, TRUE, FALSE,
6867 EL_EMC_MAGIC_BALL, -1, -1
6870 Yball_1, FALSE, FALSE,
6871 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6874 Xball_2, FALSE, FALSE,
6875 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6878 Yball_2, FALSE, FALSE,
6879 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6882 Yball_blank, FALSE, FALSE,
6883 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6887 Xamoeba_1, TRUE, FALSE,
6888 EL_AMOEBA_DRY, ACTION_OTHER, -1
6891 Xamoeba_2, FALSE, FALSE,
6892 EL_AMOEBA_DRY, ACTION_OTHER, -1
6895 Xamoeba_3, FALSE, FALSE,
6896 EL_AMOEBA_DRY, ACTION_OTHER, -1
6899 Xamoeba_4, FALSE, FALSE,
6900 EL_AMOEBA_DRY, ACTION_OTHER, -1
6903 Xamoeba_5, TRUE, FALSE,
6904 EL_AMOEBA_WET, ACTION_OTHER, -1
6907 Xamoeba_6, FALSE, FALSE,
6908 EL_AMOEBA_WET, ACTION_OTHER, -1
6911 Xamoeba_7, FALSE, FALSE,
6912 EL_AMOEBA_WET, ACTION_OTHER, -1
6915 Xamoeba_8, FALSE, FALSE,
6916 EL_AMOEBA_WET, ACTION_OTHER, -1
6921 EL_AMOEBA_DROP, ACTION_GROWING, -1
6924 Xdrip_fall, FALSE, FALSE,
6925 EL_AMOEBA_DROP, -1, -1
6928 Xdrip_stretch, FALSE, FALSE,
6929 EL_AMOEBA_DROP, ACTION_FALLING, -1
6932 Xdrip_stretchB, FALSE, TRUE,
6933 EL_AMOEBA_DROP, ACTION_FALLING, -1
6936 Ydrip_1_s, FALSE, FALSE,
6937 EL_AMOEBA_DROP, ACTION_FALLING, -1
6940 Ydrip_1_sB, FALSE, TRUE,
6941 EL_AMOEBA_DROP, ACTION_FALLING, -1
6944 Ydrip_2_s, FALSE, FALSE,
6945 EL_AMOEBA_DROP, ACTION_FALLING, -1
6948 Ydrip_2_sB, FALSE, TRUE,
6949 EL_AMOEBA_DROP, ACTION_FALLING, -1
6953 Xwonderwall, TRUE, FALSE,
6954 EL_MAGIC_WALL, -1, -1
6957 Ywonderwall, FALSE, FALSE,
6958 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6962 Xwheel, TRUE, FALSE,
6963 EL_ROBOT_WHEEL, -1, -1
6966 Ywheel, FALSE, FALSE,
6967 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6971 Xswitch, TRUE, FALSE,
6972 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6975 Yswitch, FALSE, FALSE,
6976 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6980 Xbumper, TRUE, FALSE,
6981 EL_EMC_SPRING_BUMPER, -1, -1
6984 Ybumper, FALSE, FALSE,
6985 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6989 Xacid_nw, TRUE, FALSE,
6990 EL_ACID_POOL_TOPLEFT, -1, -1
6993 Xacid_ne, TRUE, FALSE,
6994 EL_ACID_POOL_TOPRIGHT, -1, -1
6997 Xacid_sw, TRUE, FALSE,
6998 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7001 Xacid_s, TRUE, FALSE,
7002 EL_ACID_POOL_BOTTOM, -1, -1
7005 Xacid_se, TRUE, FALSE,
7006 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7010 Xfake_blank, TRUE, FALSE,
7011 EL_INVISIBLE_WALL, -1, -1
7014 Yfake_blank, FALSE, FALSE,
7015 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7019 Xfake_grass, TRUE, FALSE,
7020 EL_EMC_FAKE_GRASS, -1, -1
7023 Yfake_grass, FALSE, FALSE,
7024 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7028 Xfake_amoeba, TRUE, FALSE,
7029 EL_EMC_DRIPPER, -1, -1
7032 Yfake_amoeba, FALSE, FALSE,
7033 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7037 Xlenses, TRUE, FALSE,
7038 EL_EMC_LENSES, -1, -1
7042 Xmagnify, TRUE, FALSE,
7043 EL_EMC_MAGNIFIER, -1, -1
7048 EL_QUICKSAND_EMPTY, -1, -1
7051 Xsand_stone, TRUE, FALSE,
7052 EL_QUICKSAND_FULL, -1, -1
7055 Xsand_stonein_1, FALSE, TRUE,
7056 EL_ROCK, ACTION_FILLING, -1
7059 Xsand_stonein_2, FALSE, TRUE,
7060 EL_ROCK, ACTION_FILLING, -1
7063 Xsand_stonein_3, FALSE, TRUE,
7064 EL_ROCK, ACTION_FILLING, -1
7067 Xsand_stonein_4, FALSE, TRUE,
7068 EL_ROCK, ACTION_FILLING, -1
7071 Xsand_sandstone_1, FALSE, FALSE,
7072 EL_QUICKSAND_FILLING, -1, -1
7075 Xsand_sandstone_2, FALSE, FALSE,
7076 EL_QUICKSAND_FILLING, -1, -1
7079 Xsand_sandstone_3, FALSE, FALSE,
7080 EL_QUICKSAND_FILLING, -1, -1
7083 Xsand_sandstone_4, FALSE, FALSE,
7084 EL_QUICKSAND_FILLING, -1, -1
7087 Xsand_stonesand_1, FALSE, FALSE,
7088 EL_QUICKSAND_EMPTYING, -1, -1
7091 Xsand_stonesand_2, FALSE, FALSE,
7092 EL_QUICKSAND_EMPTYING, -1, -1
7095 Xsand_stonesand_3, FALSE, FALSE,
7096 EL_QUICKSAND_EMPTYING, -1, -1
7099 Xsand_stonesand_4, FALSE, FALSE,
7100 EL_QUICKSAND_EMPTYING, -1, -1
7103 Xsand_stoneout_1, FALSE, FALSE,
7104 EL_ROCK, ACTION_EMPTYING, -1
7107 Xsand_stoneout_2, FALSE, FALSE,
7108 EL_ROCK, ACTION_EMPTYING, -1
7111 Xsand_stonesand_quickout_1, FALSE, FALSE,
7112 EL_QUICKSAND_EMPTYING, -1, -1
7115 Xsand_stonesand_quickout_2, FALSE, FALSE,
7116 EL_QUICKSAND_EMPTYING, -1, -1
7120 Xslide_ns, TRUE, FALSE,
7121 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7124 Yslide_ns_blank, FALSE, FALSE,
7125 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7128 Xslide_ew, TRUE, FALSE,
7129 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7132 Yslide_ew_blank, FALSE, FALSE,
7133 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7137 Xwind_n, TRUE, FALSE,
7138 EL_BALLOON_SWITCH_UP, -1, -1
7141 Xwind_e, TRUE, FALSE,
7142 EL_BALLOON_SWITCH_RIGHT, -1, -1
7145 Xwind_s, TRUE, FALSE,
7146 EL_BALLOON_SWITCH_DOWN, -1, -1
7149 Xwind_w, TRUE, FALSE,
7150 EL_BALLOON_SWITCH_LEFT, -1, -1
7153 Xwind_any, TRUE, FALSE,
7154 EL_BALLOON_SWITCH_ANY, -1, -1
7157 Xwind_stop, TRUE, FALSE,
7158 EL_BALLOON_SWITCH_NONE, -1, -1
7163 EL_EM_EXIT_CLOSED, -1, -1
7166 Xexit_1, TRUE, FALSE,
7167 EL_EM_EXIT_OPEN, -1, -1
7170 Xexit_2, FALSE, FALSE,
7171 EL_EM_EXIT_OPEN, -1, -1
7174 Xexit_3, FALSE, FALSE,
7175 EL_EM_EXIT_OPEN, -1, -1
7179 Xpause, FALSE, FALSE,
7184 Xwall_1, TRUE, FALSE,
7188 Xwall_2, TRUE, FALSE,
7189 EL_EMC_WALL_14, -1, -1
7192 Xwall_3, TRUE, FALSE,
7193 EL_EMC_WALL_15, -1, -1
7196 Xwall_4, TRUE, FALSE,
7197 EL_EMC_WALL_16, -1, -1
7201 Xroundwall_1, TRUE, FALSE,
7202 EL_WALL_SLIPPERY, -1, -1
7205 Xroundwall_2, TRUE, FALSE,
7206 EL_EMC_WALL_SLIPPERY_2, -1, -1
7209 Xroundwall_3, TRUE, FALSE,
7210 EL_EMC_WALL_SLIPPERY_3, -1, -1
7213 Xroundwall_4, TRUE, FALSE,
7214 EL_EMC_WALL_SLIPPERY_4, -1, -1
7218 Xsteel_1, TRUE, FALSE,
7219 EL_STEELWALL, -1, -1
7222 Xsteel_2, TRUE, FALSE,
7223 EL_EMC_STEELWALL_2, -1, -1
7226 Xsteel_3, TRUE, FALSE,
7227 EL_EMC_STEELWALL_3, -1, -1
7230 Xsteel_4, TRUE, FALSE,
7231 EL_EMC_STEELWALL_4, -1, -1
7235 Xdecor_1, TRUE, FALSE,
7236 EL_EMC_WALL_8, -1, -1
7239 Xdecor_2, TRUE, FALSE,
7240 EL_EMC_WALL_6, -1, -1
7243 Xdecor_3, TRUE, FALSE,
7244 EL_EMC_WALL_4, -1, -1
7247 Xdecor_4, TRUE, FALSE,
7248 EL_EMC_WALL_7, -1, -1
7251 Xdecor_5, TRUE, FALSE,
7252 EL_EMC_WALL_5, -1, -1
7255 Xdecor_6, TRUE, FALSE,
7256 EL_EMC_WALL_9, -1, -1
7259 Xdecor_7, TRUE, FALSE,
7260 EL_EMC_WALL_10, -1, -1
7263 Xdecor_8, TRUE, FALSE,
7264 EL_EMC_WALL_1, -1, -1
7267 Xdecor_9, TRUE, FALSE,
7268 EL_EMC_WALL_2, -1, -1
7271 Xdecor_10, TRUE, FALSE,
7272 EL_EMC_WALL_3, -1, -1
7275 Xdecor_11, TRUE, FALSE,
7276 EL_EMC_WALL_11, -1, -1
7279 Xdecor_12, TRUE, FALSE,
7280 EL_EMC_WALL_12, -1, -1
7284 Xalpha_0, TRUE, FALSE,
7285 EL_CHAR('0'), -1, -1
7288 Xalpha_1, TRUE, FALSE,
7289 EL_CHAR('1'), -1, -1
7292 Xalpha_2, TRUE, FALSE,
7293 EL_CHAR('2'), -1, -1
7296 Xalpha_3, TRUE, FALSE,
7297 EL_CHAR('3'), -1, -1
7300 Xalpha_4, TRUE, FALSE,
7301 EL_CHAR('4'), -1, -1
7304 Xalpha_5, TRUE, FALSE,
7305 EL_CHAR('5'), -1, -1
7308 Xalpha_6, TRUE, FALSE,
7309 EL_CHAR('6'), -1, -1
7312 Xalpha_7, TRUE, FALSE,
7313 EL_CHAR('7'), -1, -1
7316 Xalpha_8, TRUE, FALSE,
7317 EL_CHAR('8'), -1, -1
7320 Xalpha_9, TRUE, FALSE,
7321 EL_CHAR('9'), -1, -1
7324 Xalpha_excla, TRUE, FALSE,
7325 EL_CHAR('!'), -1, -1
7328 Xalpha_apost, TRUE, FALSE,
7329 EL_CHAR('\''), -1, -1
7332 Xalpha_comma, TRUE, FALSE,
7333 EL_CHAR(','), -1, -1
7336 Xalpha_minus, TRUE, FALSE,
7337 EL_CHAR('-'), -1, -1
7340 Xalpha_perio, TRUE, FALSE,
7341 EL_CHAR('.'), -1, -1
7344 Xalpha_colon, TRUE, FALSE,
7345 EL_CHAR(':'), -1, -1
7348 Xalpha_quest, TRUE, FALSE,
7349 EL_CHAR('?'), -1, -1
7352 Xalpha_a, TRUE, FALSE,
7353 EL_CHAR('A'), -1, -1
7356 Xalpha_b, TRUE, FALSE,
7357 EL_CHAR('B'), -1, -1
7360 Xalpha_c, TRUE, FALSE,
7361 EL_CHAR('C'), -1, -1
7364 Xalpha_d, TRUE, FALSE,
7365 EL_CHAR('D'), -1, -1
7368 Xalpha_e, TRUE, FALSE,
7369 EL_CHAR('E'), -1, -1
7372 Xalpha_f, TRUE, FALSE,
7373 EL_CHAR('F'), -1, -1
7376 Xalpha_g, TRUE, FALSE,
7377 EL_CHAR('G'), -1, -1
7380 Xalpha_h, TRUE, FALSE,
7381 EL_CHAR('H'), -1, -1
7384 Xalpha_i, TRUE, FALSE,
7385 EL_CHAR('I'), -1, -1
7388 Xalpha_j, TRUE, FALSE,
7389 EL_CHAR('J'), -1, -1
7392 Xalpha_k, TRUE, FALSE,
7393 EL_CHAR('K'), -1, -1
7396 Xalpha_l, TRUE, FALSE,
7397 EL_CHAR('L'), -1, -1
7400 Xalpha_m, TRUE, FALSE,
7401 EL_CHAR('M'), -1, -1
7404 Xalpha_n, TRUE, FALSE,
7405 EL_CHAR('N'), -1, -1
7408 Xalpha_o, TRUE, FALSE,
7409 EL_CHAR('O'), -1, -1
7412 Xalpha_p, TRUE, FALSE,
7413 EL_CHAR('P'), -1, -1
7416 Xalpha_q, TRUE, FALSE,
7417 EL_CHAR('Q'), -1, -1
7420 Xalpha_r, TRUE, FALSE,
7421 EL_CHAR('R'), -1, -1
7424 Xalpha_s, TRUE, FALSE,
7425 EL_CHAR('S'), -1, -1
7428 Xalpha_t, TRUE, FALSE,
7429 EL_CHAR('T'), -1, -1
7432 Xalpha_u, TRUE, FALSE,
7433 EL_CHAR('U'), -1, -1
7436 Xalpha_v, TRUE, FALSE,
7437 EL_CHAR('V'), -1, -1
7440 Xalpha_w, TRUE, FALSE,
7441 EL_CHAR('W'), -1, -1
7444 Xalpha_x, TRUE, FALSE,
7445 EL_CHAR('X'), -1, -1
7448 Xalpha_y, TRUE, FALSE,
7449 EL_CHAR('Y'), -1, -1
7452 Xalpha_z, TRUE, FALSE,
7453 EL_CHAR('Z'), -1, -1
7456 Xalpha_arrow_e, TRUE, FALSE,
7457 EL_CHAR('>'), -1, -1
7460 Xalpha_arrow_w, TRUE, FALSE,
7461 EL_CHAR('<'), -1, -1
7464 Xalpha_copyr, TRUE, FALSE,
7465 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7469 Ykey_1_blank, FALSE, FALSE,
7470 EL_EM_KEY_1, ACTION_COLLECTING, -1
7473 Ykey_2_blank, FALSE, FALSE,
7474 EL_EM_KEY_2, ACTION_COLLECTING, -1
7477 Ykey_3_blank, FALSE, FALSE,
7478 EL_EM_KEY_3, ACTION_COLLECTING, -1
7481 Ykey_4_blank, FALSE, FALSE,
7482 EL_EM_KEY_4, ACTION_COLLECTING, -1
7485 Ykey_5_blank, FALSE, FALSE,
7486 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7489 Ykey_6_blank, FALSE, FALSE,
7490 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7493 Ykey_7_blank, FALSE, FALSE,
7494 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7497 Ykey_8_blank, FALSE, FALSE,
7498 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7501 Ylenses_blank, FALSE, FALSE,
7502 EL_EMC_LENSES, ACTION_COLLECTING, -1
7505 Ymagnify_blank, FALSE, FALSE,
7506 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7509 Ygrass_blank, FALSE, FALSE,
7510 EL_EMC_GRASS, ACTION_SNAPPING, -1
7513 Ydirt_blank, FALSE, FALSE,
7514 EL_SAND, ACTION_SNAPPING, -1
7523 static struct Mapping_EM_to_RND_player
7532 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7536 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7540 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7544 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7548 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7552 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7556 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7560 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7564 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7568 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7572 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7576 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7580 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7584 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7588 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7592 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7596 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7600 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7604 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7608 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7612 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7616 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7620 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7624 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7628 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7632 EL_PLAYER_1, ACTION_DEFAULT, -1,
7636 EL_PLAYER_2, ACTION_DEFAULT, -1,
7640 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7644 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7648 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7652 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7656 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7660 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7664 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7668 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7672 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7676 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7680 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7684 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7688 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7692 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7696 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7700 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7704 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7708 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7712 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7716 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7720 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7724 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7728 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7732 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7736 EL_PLAYER_3, ACTION_DEFAULT, -1,
7740 EL_PLAYER_4, ACTION_DEFAULT, -1,
7749 int map_element_RND_to_EM_cave(int element_rnd)
7751 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7752 static boolean mapping_initialized = FALSE;
7754 if (!mapping_initialized)
7758 // return "Xalpha_quest" for all undefined elements in mapping array
7759 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7760 mapping_RND_to_EM[i] = Xalpha_quest;
7762 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7763 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7764 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7765 em_object_mapping_list[i].element_em;
7767 mapping_initialized = TRUE;
7770 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7772 Warn("invalid RND level element %d", element_rnd);
7777 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7780 int map_element_EM_to_RND_cave(int element_em_cave)
7782 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7783 static boolean mapping_initialized = FALSE;
7785 if (!mapping_initialized)
7789 // return "EL_UNKNOWN" for all undefined elements in mapping array
7790 for (i = 0; i < GAME_TILE_MAX; i++)
7791 mapping_EM_to_RND[i] = EL_UNKNOWN;
7793 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7794 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7795 em_object_mapping_list[i].element_rnd;
7797 mapping_initialized = TRUE;
7800 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7802 Warn("invalid EM cave element %d", element_em_cave);
7807 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7810 int map_element_EM_to_RND_game(int element_em_game)
7812 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7813 static boolean mapping_initialized = FALSE;
7815 if (!mapping_initialized)
7819 // return "EL_UNKNOWN" for all undefined elements in mapping array
7820 for (i = 0; i < GAME_TILE_MAX; i++)
7821 mapping_EM_to_RND[i] = EL_UNKNOWN;
7823 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7824 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7825 em_object_mapping_list[i].element_rnd;
7827 mapping_initialized = TRUE;
7830 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7832 Warn("invalid EM game element %d", element_em_game);
7837 return mapping_EM_to_RND[element_em_game];
7840 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7842 struct LevelInfo_EM *level_em = level->native_em_level;
7843 struct CAVE *cav = level_em->cav;
7846 for (i = 0; i < GAME_TILE_MAX; i++)
7847 cav->android_array[i] = Cblank;
7849 for (i = 0; i < level->num_android_clone_elements; i++)
7851 int element_rnd = level->android_clone_element[i];
7852 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7854 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7855 if (em_object_mapping_list[j].element_rnd == element_rnd)
7856 cav->android_array[em_object_mapping_list[j].element_em] =
7861 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7863 struct LevelInfo_EM *level_em = level->native_em_level;
7864 struct CAVE *cav = level_em->cav;
7867 level->num_android_clone_elements = 0;
7869 for (i = 0; i < GAME_TILE_MAX; i++)
7871 int element_em_cave = cav->android_array[i];
7873 boolean element_found = FALSE;
7875 if (element_em_cave == Cblank)
7878 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7880 for (j = 0; j < level->num_android_clone_elements; j++)
7881 if (level->android_clone_element[j] == element_rnd)
7882 element_found = TRUE;
7886 level->android_clone_element[level->num_android_clone_elements++] =
7889 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7894 if (level->num_android_clone_elements == 0)
7896 level->num_android_clone_elements = 1;
7897 level->android_clone_element[0] = EL_EMPTY;
7901 int map_direction_RND_to_EM(int direction)
7903 return (direction == MV_UP ? 0 :
7904 direction == MV_RIGHT ? 1 :
7905 direction == MV_DOWN ? 2 :
7906 direction == MV_LEFT ? 3 :
7910 int map_direction_EM_to_RND(int direction)
7912 return (direction == 0 ? MV_UP :
7913 direction == 1 ? MV_RIGHT :
7914 direction == 2 ? MV_DOWN :
7915 direction == 3 ? MV_LEFT :
7919 int map_element_RND_to_SP(int element_rnd)
7921 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7923 if (element_rnd >= EL_SP_START &&
7924 element_rnd <= EL_SP_END)
7925 element_sp = element_rnd - EL_SP_START;
7926 else if (element_rnd == EL_EMPTY_SPACE)
7928 else if (element_rnd == EL_INVISIBLE_WALL)
7934 int map_element_SP_to_RND(int element_sp)
7936 int element_rnd = EL_UNKNOWN;
7938 if (element_sp >= 0x00 &&
7940 element_rnd = EL_SP_START + element_sp;
7941 else if (element_sp == 0x28)
7942 element_rnd = EL_INVISIBLE_WALL;
7947 int map_action_SP_to_RND(int action_sp)
7951 case actActive: return ACTION_ACTIVE;
7952 case actImpact: return ACTION_IMPACT;
7953 case actExploding: return ACTION_EXPLODING;
7954 case actDigging: return ACTION_DIGGING;
7955 case actSnapping: return ACTION_SNAPPING;
7956 case actCollecting: return ACTION_COLLECTING;
7957 case actPassing: return ACTION_PASSING;
7958 case actPushing: return ACTION_PUSHING;
7959 case actDropping: return ACTION_DROPPING;
7961 default: return ACTION_DEFAULT;
7965 int map_element_RND_to_MM(int element_rnd)
7967 return (element_rnd >= EL_MM_START_1 &&
7968 element_rnd <= EL_MM_END_1 ?
7969 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7971 element_rnd >= EL_MM_START_2 &&
7972 element_rnd <= EL_MM_END_2 ?
7973 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7975 element_rnd >= EL_CHAR_START &&
7976 element_rnd <= EL_CHAR_END ?
7977 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7979 element_rnd >= EL_MM_RUNTIME_START &&
7980 element_rnd <= EL_MM_RUNTIME_END ?
7981 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7983 element_rnd >= EL_MM_DUMMY_START &&
7984 element_rnd <= EL_MM_DUMMY_END ?
7985 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7987 EL_MM_EMPTY_NATIVE);
7990 int map_element_MM_to_RND(int element_mm)
7992 return (element_mm == EL_MM_EMPTY_NATIVE ||
7993 element_mm == EL_DF_EMPTY_NATIVE ?
7996 element_mm >= EL_MM_START_1_NATIVE &&
7997 element_mm <= EL_MM_END_1_NATIVE ?
7998 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8000 element_mm >= EL_MM_START_2_NATIVE &&
8001 element_mm <= EL_MM_END_2_NATIVE ?
8002 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8004 element_mm >= EL_MM_CHAR_START_NATIVE &&
8005 element_mm <= EL_MM_CHAR_END_NATIVE ?
8006 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8008 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8009 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8010 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8012 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8013 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8014 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8019 int map_action_MM_to_RND(int action_mm)
8021 // all MM actions are defined to exactly match their RND counterparts
8025 int map_sound_MM_to_RND(int sound_mm)
8029 case SND_MM_GAME_LEVELTIME_CHARGING:
8030 return SND_GAME_LEVELTIME_CHARGING;
8032 case SND_MM_GAME_HEALTH_CHARGING:
8033 return SND_GAME_HEALTH_CHARGING;
8036 return SND_UNDEFINED;
8040 int map_mm_wall_element(int element)
8042 return (element >= EL_MM_STEEL_WALL_START &&
8043 element <= EL_MM_STEEL_WALL_END ?
8046 element >= EL_MM_WOODEN_WALL_START &&
8047 element <= EL_MM_WOODEN_WALL_END ?
8050 element >= EL_MM_ICE_WALL_START &&
8051 element <= EL_MM_ICE_WALL_END ?
8054 element >= EL_MM_AMOEBA_WALL_START &&
8055 element <= EL_MM_AMOEBA_WALL_END ?
8058 element >= EL_DF_STEEL_WALL_START &&
8059 element <= EL_DF_STEEL_WALL_END ?
8062 element >= EL_DF_WOODEN_WALL_START &&
8063 element <= EL_DF_WOODEN_WALL_END ?
8069 int map_mm_wall_element_editor(int element)
8073 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8074 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8075 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8076 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8077 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8078 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8080 default: return element;
8084 int get_next_element(int element)
8088 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8089 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8090 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8091 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8092 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8093 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8094 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8095 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8096 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8097 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8098 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8100 default: return element;
8104 int el2img_mm(int element_mm)
8106 return el2img(map_element_MM_to_RND(element_mm));
8109 int el_act_dir2img(int element, int action, int direction)
8111 element = GFX_ELEMENT(element);
8112 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8114 // direction_graphic[][] == graphic[] for undefined direction graphics
8115 return element_info[element].direction_graphic[action][direction];
8118 static int el_act_dir2crm(int element, int action, int direction)
8120 element = GFX_ELEMENT(element);
8121 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8123 // direction_graphic[][] == graphic[] for undefined direction graphics
8124 return element_info[element].direction_crumbled[action][direction];
8127 int el_act2img(int element, int action)
8129 element = GFX_ELEMENT(element);
8131 return element_info[element].graphic[action];
8134 int el_act2crm(int element, int action)
8136 element = GFX_ELEMENT(element);
8138 return element_info[element].crumbled[action];
8141 int el_dir2img(int element, int direction)
8143 element = GFX_ELEMENT(element);
8145 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8148 int el2baseimg(int element)
8150 return element_info[element].graphic[ACTION_DEFAULT];
8153 int el2img(int element)
8155 element = GFX_ELEMENT(element);
8157 return element_info[element].graphic[ACTION_DEFAULT];
8160 int el2edimg(int element)
8162 element = GFX_ELEMENT(element);
8164 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8167 int el2preimg(int element)
8169 element = GFX_ELEMENT(element);
8171 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8174 int el2panelimg(int element)
8176 element = GFX_ELEMENT(element);
8178 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8181 int font2baseimg(int font_nr)
8183 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8186 int getBeltNrFromBeltElement(int element)
8188 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8189 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8190 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8193 int getBeltNrFromBeltActiveElement(int element)
8195 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8196 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8197 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8200 int getBeltNrFromBeltSwitchElement(int element)
8202 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8203 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8204 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8207 int getBeltDirNrFromBeltElement(int element)
8209 static int belt_base_element[4] =
8211 EL_CONVEYOR_BELT_1_LEFT,
8212 EL_CONVEYOR_BELT_2_LEFT,
8213 EL_CONVEYOR_BELT_3_LEFT,
8214 EL_CONVEYOR_BELT_4_LEFT
8217 int belt_nr = getBeltNrFromBeltElement(element);
8218 int belt_dir_nr = element - belt_base_element[belt_nr];
8220 return (belt_dir_nr % 3);
8223 int getBeltDirNrFromBeltSwitchElement(int element)
8225 static int belt_base_element[4] =
8227 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8228 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8229 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8230 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8233 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8234 int belt_dir_nr = element - belt_base_element[belt_nr];
8236 return (belt_dir_nr % 3);
8239 int getBeltDirFromBeltElement(int element)
8241 static int belt_move_dir[3] =
8248 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8250 return belt_move_dir[belt_dir_nr];
8253 int getBeltDirFromBeltSwitchElement(int element)
8255 static int belt_move_dir[3] =
8262 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8264 return belt_move_dir[belt_dir_nr];
8267 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8269 static int belt_base_element[4] =
8271 EL_CONVEYOR_BELT_1_LEFT,
8272 EL_CONVEYOR_BELT_2_LEFT,
8273 EL_CONVEYOR_BELT_3_LEFT,
8274 EL_CONVEYOR_BELT_4_LEFT
8277 return belt_base_element[belt_nr] + belt_dir_nr;
8280 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8282 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8284 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8287 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8289 static int belt_base_element[4] =
8291 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8292 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8293 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8294 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8297 return belt_base_element[belt_nr] + belt_dir_nr;
8300 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8302 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8304 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8307 boolean swapTiles_EM(boolean is_pre_emc_cave)
8309 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8312 boolean getTeamMode_EM(void)
8314 return game.team_mode || network_playing;
8317 boolean isActivePlayer_EM(int player_nr)
8319 return stored_player[player_nr].active;
8322 unsigned int InitRND(int seed)
8324 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8325 return InitEngineRandom_EM(seed);
8326 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8327 return InitEngineRandom_SP(seed);
8328 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8329 return InitEngineRandom_MM(seed);
8331 return InitEngineRandom_RND(seed);
8334 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8335 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8337 static int get_effective_element_EM(int tile, int frame_em)
8339 int element = object_mapping[tile].element_rnd;
8340 int action = object_mapping[tile].action;
8341 boolean is_backside = object_mapping[tile].is_backside;
8342 boolean action_removing = (action == ACTION_DIGGING ||
8343 action == ACTION_SNAPPING ||
8344 action == ACTION_COLLECTING);
8352 return (frame_em > 5 ? EL_EMPTY : element);
8358 else // frame_em == 7
8369 case Ydiamond_stone:
8373 case Xdrip_stretchB:
8389 case Ymagnify_blank:
8392 case Xsand_stonein_1:
8393 case Xsand_stonein_2:
8394 case Xsand_stonein_3:
8395 case Xsand_stonein_4:
8399 return (is_backside || action_removing ? EL_EMPTY : element);
8404 static boolean check_linear_animation_EM(int tile)
8408 case Xsand_stonesand_1:
8409 case Xsand_stonesand_quickout_1:
8410 case Xsand_sandstone_1:
8411 case Xsand_stonein_1:
8412 case Xsand_stoneout_1:
8440 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8441 boolean has_crumbled_graphics,
8442 int crumbled, int sync_frame)
8444 // if element can be crumbled, but certain action graphics are just empty
8445 // space (like instantly snapping sand to empty space in 1 frame), do not
8446 // treat these empty space graphics as crumbled graphics in EMC engine
8447 if (crumbled == IMG_EMPTY_SPACE)
8448 has_crumbled_graphics = FALSE;
8450 if (has_crumbled_graphics)
8452 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8453 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8454 g_crumbled->anim_delay,
8455 g_crumbled->anim_mode,
8456 g_crumbled->anim_start_frame,
8459 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8460 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8462 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8463 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8465 g_em->has_crumbled_graphics = TRUE;
8469 g_em->crumbled_bitmap = NULL;
8470 g_em->crumbled_src_x = 0;
8471 g_em->crumbled_src_y = 0;
8472 g_em->crumbled_border_size = 0;
8473 g_em->crumbled_tile_size = 0;
8475 g_em->has_crumbled_graphics = FALSE;
8480 void ResetGfxAnimation_EM(int x, int y, int tile)
8486 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8487 int tile, int frame_em, int x, int y)
8489 int action = object_mapping[tile].action;
8490 int direction = object_mapping[tile].direction;
8491 int effective_element = get_effective_element_EM(tile, frame_em);
8492 int graphic = (direction == MV_NONE ?
8493 el_act2img(effective_element, action) :
8494 el_act_dir2img(effective_element, action, direction));
8495 struct GraphicInfo *g = &graphic_info[graphic];
8497 boolean action_removing = (action == ACTION_DIGGING ||
8498 action == ACTION_SNAPPING ||
8499 action == ACTION_COLLECTING);
8500 boolean action_moving = (action == ACTION_FALLING ||
8501 action == ACTION_MOVING ||
8502 action == ACTION_PUSHING ||
8503 action == ACTION_EATING ||
8504 action == ACTION_FILLING ||
8505 action == ACTION_EMPTYING);
8506 boolean action_falling = (action == ACTION_FALLING ||
8507 action == ACTION_FILLING ||
8508 action == ACTION_EMPTYING);
8510 // special case: graphic uses "2nd movement tile" and has defined
8511 // 7 frames for movement animation (or less) => use default graphic
8512 // for last (8th) frame which ends the movement animation
8513 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8515 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8516 graphic = (direction == MV_NONE ?
8517 el_act2img(effective_element, action) :
8518 el_act_dir2img(effective_element, action, direction));
8520 g = &graphic_info[graphic];
8523 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8527 else if (action_moving)
8529 boolean is_backside = object_mapping[tile].is_backside;
8533 int direction = object_mapping[tile].direction;
8534 int move_dir = (action_falling ? MV_DOWN : direction);
8539 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8540 if (g->double_movement && frame_em == 0)
8544 if (move_dir == MV_LEFT)
8545 GfxFrame[x - 1][y] = GfxFrame[x][y];
8546 else if (move_dir == MV_RIGHT)
8547 GfxFrame[x + 1][y] = GfxFrame[x][y];
8548 else if (move_dir == MV_UP)
8549 GfxFrame[x][y - 1] = GfxFrame[x][y];
8550 else if (move_dir == MV_DOWN)
8551 GfxFrame[x][y + 1] = GfxFrame[x][y];
8558 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8559 if (tile == Xsand_stonesand_quickout_1 ||
8560 tile == Xsand_stonesand_quickout_2)
8564 if (graphic_info[graphic].anim_global_sync)
8565 sync_frame = FrameCounter;
8566 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8567 sync_frame = GfxFrame[x][y];
8569 sync_frame = 0; // playfield border (pseudo steel)
8571 SetRandomAnimationValue(x, y);
8573 int frame = getAnimationFrame(g->anim_frames,
8576 g->anim_start_frame,
8579 g_em->unique_identifier =
8580 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8583 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8584 int tile, int frame_em, int x, int y)
8586 int action = object_mapping[tile].action;
8587 int direction = object_mapping[tile].direction;
8588 boolean is_backside = object_mapping[tile].is_backside;
8589 int effective_element = get_effective_element_EM(tile, frame_em);
8590 int effective_action = action;
8591 int graphic = (direction == MV_NONE ?
8592 el_act2img(effective_element, effective_action) :
8593 el_act_dir2img(effective_element, effective_action,
8595 int crumbled = (direction == MV_NONE ?
8596 el_act2crm(effective_element, effective_action) :
8597 el_act_dir2crm(effective_element, effective_action,
8599 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8600 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8601 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8602 struct GraphicInfo *g = &graphic_info[graphic];
8605 // special case: graphic uses "2nd movement tile" and has defined
8606 // 7 frames for movement animation (or less) => use default graphic
8607 // for last (8th) frame which ends the movement animation
8608 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8610 effective_action = ACTION_DEFAULT;
8611 graphic = (direction == MV_NONE ?
8612 el_act2img(effective_element, effective_action) :
8613 el_act_dir2img(effective_element, effective_action,
8615 crumbled = (direction == MV_NONE ?
8616 el_act2crm(effective_element, effective_action) :
8617 el_act_dir2crm(effective_element, effective_action,
8620 g = &graphic_info[graphic];
8623 if (graphic_info[graphic].anim_global_sync)
8624 sync_frame = FrameCounter;
8625 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8626 sync_frame = GfxFrame[x][y];
8628 sync_frame = 0; // playfield border (pseudo steel)
8630 SetRandomAnimationValue(x, y);
8632 int frame = getAnimationFrame(g->anim_frames,
8635 g->anim_start_frame,
8638 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8639 g->double_movement && is_backside);
8641 // (updating the "crumbled" graphic definitions is probably not really needed,
8642 // as animations for crumbled graphics can't be longer than one EMC cycle)
8643 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8647 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8648 int player_nr, int anim, int frame_em)
8650 int element = player_mapping[player_nr][anim].element_rnd;
8651 int action = player_mapping[player_nr][anim].action;
8652 int direction = player_mapping[player_nr][anim].direction;
8653 int graphic = (direction == MV_NONE ?
8654 el_act2img(element, action) :
8655 el_act_dir2img(element, action, direction));
8656 struct GraphicInfo *g = &graphic_info[graphic];
8659 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8661 stored_player[player_nr].StepFrame = frame_em;
8663 sync_frame = stored_player[player_nr].Frame;
8665 int frame = getAnimationFrame(g->anim_frames,
8668 g->anim_start_frame,
8671 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8672 &g_em->src_x, &g_em->src_y, FALSE);
8675 void InitGraphicInfo_EM(void)
8679 // always start with reliable default values
8680 for (i = 0; i < GAME_TILE_MAX; i++)
8682 object_mapping[i].element_rnd = EL_UNKNOWN;
8683 object_mapping[i].is_backside = FALSE;
8684 object_mapping[i].action = ACTION_DEFAULT;
8685 object_mapping[i].direction = MV_NONE;
8688 // always start with reliable default values
8689 for (p = 0; p < MAX_PLAYERS; p++)
8691 for (i = 0; i < PLY_MAX; i++)
8693 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8694 player_mapping[p][i].action = ACTION_DEFAULT;
8695 player_mapping[p][i].direction = MV_NONE;
8699 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8701 int e = em_object_mapping_list[i].element_em;
8703 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8704 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8706 if (em_object_mapping_list[i].action != -1)
8707 object_mapping[e].action = em_object_mapping_list[i].action;
8709 if (em_object_mapping_list[i].direction != -1)
8710 object_mapping[e].direction =
8711 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8714 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8716 int a = em_player_mapping_list[i].action_em;
8717 int p = em_player_mapping_list[i].player_nr;
8719 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8721 if (em_player_mapping_list[i].action != -1)
8722 player_mapping[p][a].action = em_player_mapping_list[i].action;
8724 if (em_player_mapping_list[i].direction != -1)
8725 player_mapping[p][a].direction =
8726 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8729 for (i = 0; i < GAME_TILE_MAX; i++)
8731 int element = object_mapping[i].element_rnd;
8732 int action = object_mapping[i].action;
8733 int direction = object_mapping[i].direction;
8734 boolean is_backside = object_mapping[i].is_backside;
8735 boolean action_exploding = ((action == ACTION_EXPLODING ||
8736 action == ACTION_SMASHED_BY_ROCK ||
8737 action == ACTION_SMASHED_BY_SPRING) &&
8738 element != EL_DIAMOND);
8739 boolean action_active = (action == ACTION_ACTIVE);
8740 boolean action_other = (action == ACTION_OTHER);
8742 for (j = 0; j < 8; j++)
8744 int effective_element = get_effective_element_EM(i, j);
8745 int effective_action = (j < 7 ? action :
8746 i == Xdrip_stretch ? action :
8747 i == Xdrip_stretchB ? action :
8748 i == Ydrip_1_s ? action :
8749 i == Ydrip_1_sB ? action :
8750 i == Yball_1 ? action :
8751 i == Xball_2 ? action :
8752 i == Yball_2 ? action :
8753 i == Yball_blank ? action :
8754 i == Ykey_1_blank ? action :
8755 i == Ykey_2_blank ? action :
8756 i == Ykey_3_blank ? action :
8757 i == Ykey_4_blank ? action :
8758 i == Ykey_5_blank ? action :
8759 i == Ykey_6_blank ? action :
8760 i == Ykey_7_blank ? action :
8761 i == Ykey_8_blank ? action :
8762 i == Ylenses_blank ? action :
8763 i == Ymagnify_blank ? action :
8764 i == Ygrass_blank ? action :
8765 i == Ydirt_blank ? action :
8766 i == Xsand_stonein_1 ? action :
8767 i == Xsand_stonein_2 ? action :
8768 i == Xsand_stonein_3 ? action :
8769 i == Xsand_stonein_4 ? action :
8770 i == Xsand_stoneout_1 ? action :
8771 i == Xsand_stoneout_2 ? action :
8772 i == Xboom_android ? ACTION_EXPLODING :
8773 action_exploding ? ACTION_EXPLODING :
8774 action_active ? action :
8775 action_other ? action :
8777 int graphic = (el_act_dir2img(effective_element, effective_action,
8779 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8781 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8782 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8783 boolean has_action_graphics = (graphic != base_graphic);
8784 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8785 struct GraphicInfo *g = &graphic_info[graphic];
8786 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8789 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8790 boolean special_animation = (action != ACTION_DEFAULT &&
8791 g->anim_frames == 3 &&
8792 g->anim_delay == 2 &&
8793 g->anim_mode & ANIM_LINEAR);
8794 int sync_frame = (i == Xdrip_stretch ? 7 :
8795 i == Xdrip_stretchB ? 7 :
8796 i == Ydrip_2_s ? j + 8 :
8797 i == Ydrip_2_sB ? j + 8 :
8806 i == Xfake_acid_1 ? 0 :
8807 i == Xfake_acid_2 ? 10 :
8808 i == Xfake_acid_3 ? 20 :
8809 i == Xfake_acid_4 ? 30 :
8810 i == Xfake_acid_5 ? 40 :
8811 i == Xfake_acid_6 ? 50 :
8812 i == Xfake_acid_7 ? 60 :
8813 i == Xfake_acid_8 ? 70 :
8814 i == Xfake_acid_1_player ? 0 :
8815 i == Xfake_acid_2_player ? 10 :
8816 i == Xfake_acid_3_player ? 20 :
8817 i == Xfake_acid_4_player ? 30 :
8818 i == Xfake_acid_5_player ? 40 :
8819 i == Xfake_acid_6_player ? 50 :
8820 i == Xfake_acid_7_player ? 60 :
8821 i == Xfake_acid_8_player ? 70 :
8823 i == Yball_2 ? j + 8 :
8824 i == Yball_blank ? j + 1 :
8825 i == Ykey_1_blank ? j + 1 :
8826 i == Ykey_2_blank ? j + 1 :
8827 i == Ykey_3_blank ? j + 1 :
8828 i == Ykey_4_blank ? j + 1 :
8829 i == Ykey_5_blank ? j + 1 :
8830 i == Ykey_6_blank ? j + 1 :
8831 i == Ykey_7_blank ? j + 1 :
8832 i == Ykey_8_blank ? j + 1 :
8833 i == Ylenses_blank ? j + 1 :
8834 i == Ymagnify_blank ? j + 1 :
8835 i == Ygrass_blank ? j + 1 :
8836 i == Ydirt_blank ? j + 1 :
8837 i == Xamoeba_1 ? 0 :
8838 i == Xamoeba_2 ? 1 :
8839 i == Xamoeba_3 ? 2 :
8840 i == Xamoeba_4 ? 3 :
8841 i == Xamoeba_5 ? 0 :
8842 i == Xamoeba_6 ? 1 :
8843 i == Xamoeba_7 ? 2 :
8844 i == Xamoeba_8 ? 3 :
8845 i == Xexit_2 ? j + 8 :
8846 i == Xexit_3 ? j + 16 :
8847 i == Xdynamite_1 ? 0 :
8848 i == Xdynamite_2 ? 8 :
8849 i == Xdynamite_3 ? 16 :
8850 i == Xdynamite_4 ? 24 :
8851 i == Xsand_stonein_1 ? j + 1 :
8852 i == Xsand_stonein_2 ? j + 9 :
8853 i == Xsand_stonein_3 ? j + 17 :
8854 i == Xsand_stonein_4 ? j + 25 :
8855 i == Xsand_stoneout_1 && j == 0 ? 0 :
8856 i == Xsand_stoneout_1 && j == 1 ? 0 :
8857 i == Xsand_stoneout_1 && j == 2 ? 1 :
8858 i == Xsand_stoneout_1 && j == 3 ? 2 :
8859 i == Xsand_stoneout_1 && j == 4 ? 2 :
8860 i == Xsand_stoneout_1 && j == 5 ? 3 :
8861 i == Xsand_stoneout_1 && j == 6 ? 4 :
8862 i == Xsand_stoneout_1 && j == 7 ? 4 :
8863 i == Xsand_stoneout_2 && j == 0 ? 5 :
8864 i == Xsand_stoneout_2 && j == 1 ? 6 :
8865 i == Xsand_stoneout_2 && j == 2 ? 7 :
8866 i == Xsand_stoneout_2 && j == 3 ? 8 :
8867 i == Xsand_stoneout_2 && j == 4 ? 9 :
8868 i == Xsand_stoneout_2 && j == 5 ? 11 :
8869 i == Xsand_stoneout_2 && j == 6 ? 13 :
8870 i == Xsand_stoneout_2 && j == 7 ? 15 :
8871 i == Xboom_bug && j == 1 ? 2 :
8872 i == Xboom_bug && j == 2 ? 2 :
8873 i == Xboom_bug && j == 3 ? 4 :
8874 i == Xboom_bug && j == 4 ? 4 :
8875 i == Xboom_bug && j == 5 ? 2 :
8876 i == Xboom_bug && j == 6 ? 2 :
8877 i == Xboom_bug && j == 7 ? 0 :
8878 i == Xboom_tank && j == 1 ? 2 :
8879 i == Xboom_tank && j == 2 ? 2 :
8880 i == Xboom_tank && j == 3 ? 4 :
8881 i == Xboom_tank && j == 4 ? 4 :
8882 i == Xboom_tank && j == 5 ? 2 :
8883 i == Xboom_tank && j == 6 ? 2 :
8884 i == Xboom_tank && j == 7 ? 0 :
8885 i == Xboom_android && j == 7 ? 6 :
8886 i == Xboom_1 && j == 1 ? 2 :
8887 i == Xboom_1 && j == 2 ? 2 :
8888 i == Xboom_1 && j == 3 ? 4 :
8889 i == Xboom_1 && j == 4 ? 4 :
8890 i == Xboom_1 && j == 5 ? 6 :
8891 i == Xboom_1 && j == 6 ? 6 :
8892 i == Xboom_1 && j == 7 ? 8 :
8893 i == Xboom_2 && j == 0 ? 8 :
8894 i == Xboom_2 && j == 1 ? 8 :
8895 i == Xboom_2 && j == 2 ? 10 :
8896 i == Xboom_2 && j == 3 ? 10 :
8897 i == Xboom_2 && j == 4 ? 10 :
8898 i == Xboom_2 && j == 5 ? 12 :
8899 i == Xboom_2 && j == 6 ? 12 :
8900 i == Xboom_2 && j == 7 ? 12 :
8901 special_animation && j == 4 ? 3 :
8902 effective_action != action ? 0 :
8904 int frame = getAnimationFrame(g->anim_frames,
8907 g->anim_start_frame,
8910 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8911 g->double_movement && is_backside);
8913 g_em->bitmap = src_bitmap;
8914 g_em->src_x = src_x;
8915 g_em->src_y = src_y;
8916 g_em->src_offset_x = 0;
8917 g_em->src_offset_y = 0;
8918 g_em->dst_offset_x = 0;
8919 g_em->dst_offset_y = 0;
8920 g_em->width = TILEX;
8921 g_em->height = TILEY;
8923 g_em->preserve_background = FALSE;
8925 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8928 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8929 effective_action == ACTION_MOVING ||
8930 effective_action == ACTION_PUSHING ||
8931 effective_action == ACTION_EATING)) ||
8932 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8933 effective_action == ACTION_EMPTYING)))
8936 (effective_action == ACTION_FALLING ||
8937 effective_action == ACTION_FILLING ||
8938 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8939 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8940 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8941 int num_steps = (i == Ydrip_1_s ? 16 :
8942 i == Ydrip_1_sB ? 16 :
8943 i == Ydrip_2_s ? 16 :
8944 i == Ydrip_2_sB ? 16 :
8945 i == Xsand_stonein_1 ? 32 :
8946 i == Xsand_stonein_2 ? 32 :
8947 i == Xsand_stonein_3 ? 32 :
8948 i == Xsand_stonein_4 ? 32 :
8949 i == Xsand_stoneout_1 ? 16 :
8950 i == Xsand_stoneout_2 ? 16 : 8);
8951 int cx = ABS(dx) * (TILEX / num_steps);
8952 int cy = ABS(dy) * (TILEY / num_steps);
8953 int step_frame = (i == Ydrip_2_s ? j + 8 :
8954 i == Ydrip_2_sB ? j + 8 :
8955 i == Xsand_stonein_2 ? j + 8 :
8956 i == Xsand_stonein_3 ? j + 16 :
8957 i == Xsand_stonein_4 ? j + 24 :
8958 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8959 int step = (is_backside ? step_frame : num_steps - step_frame);
8961 if (is_backside) // tile where movement starts
8963 if (dx < 0 || dy < 0)
8965 g_em->src_offset_x = cx * step;
8966 g_em->src_offset_y = cy * step;
8970 g_em->dst_offset_x = cx * step;
8971 g_em->dst_offset_y = cy * step;
8974 else // tile where movement ends
8976 if (dx < 0 || dy < 0)
8978 g_em->dst_offset_x = cx * step;
8979 g_em->dst_offset_y = cy * step;
8983 g_em->src_offset_x = cx * step;
8984 g_em->src_offset_y = cy * step;
8988 g_em->width = TILEX - cx * step;
8989 g_em->height = TILEY - cy * step;
8992 // create unique graphic identifier to decide if tile must be redrawn
8993 /* bit 31 - 16 (16 bit): EM style graphic
8994 bit 15 - 12 ( 4 bit): EM style frame
8995 bit 11 - 6 ( 6 bit): graphic width
8996 bit 5 - 0 ( 6 bit): graphic height */
8997 g_em->unique_identifier =
8998 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9002 for (i = 0; i < GAME_TILE_MAX; i++)
9004 for (j = 0; j < 8; j++)
9006 int element = object_mapping[i].element_rnd;
9007 int action = object_mapping[i].action;
9008 int direction = object_mapping[i].direction;
9009 boolean is_backside = object_mapping[i].is_backside;
9010 int graphic_action = el_act_dir2img(element, action, direction);
9011 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9013 if ((action == ACTION_SMASHED_BY_ROCK ||
9014 action == ACTION_SMASHED_BY_SPRING ||
9015 action == ACTION_EATING) &&
9016 graphic_action == graphic_default)
9018 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9019 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9020 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9021 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9024 // no separate animation for "smashed by rock" -- use rock instead
9025 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9026 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9028 g_em->bitmap = g_xx->bitmap;
9029 g_em->src_x = g_xx->src_x;
9030 g_em->src_y = g_xx->src_y;
9031 g_em->src_offset_x = g_xx->src_offset_x;
9032 g_em->src_offset_y = g_xx->src_offset_y;
9033 g_em->dst_offset_x = g_xx->dst_offset_x;
9034 g_em->dst_offset_y = g_xx->dst_offset_y;
9035 g_em->width = g_xx->width;
9036 g_em->height = g_xx->height;
9037 g_em->unique_identifier = g_xx->unique_identifier;
9040 g_em->preserve_background = TRUE;
9045 for (p = 0; p < MAX_PLAYERS; p++)
9047 for (i = 0; i < PLY_MAX; i++)
9049 int element = player_mapping[p][i].element_rnd;
9050 int action = player_mapping[p][i].action;
9051 int direction = player_mapping[p][i].direction;
9053 for (j = 0; j < 8; j++)
9055 int effective_element = element;
9056 int effective_action = action;
9057 int graphic = (direction == MV_NONE ?
9058 el_act2img(effective_element, effective_action) :
9059 el_act_dir2img(effective_element, effective_action,
9061 struct GraphicInfo *g = &graphic_info[graphic];
9062 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9066 int frame = getAnimationFrame(g->anim_frames,
9069 g->anim_start_frame,
9072 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9074 g_em->bitmap = src_bitmap;
9075 g_em->src_x = src_x;
9076 g_em->src_y = src_y;
9077 g_em->src_offset_x = 0;
9078 g_em->src_offset_y = 0;
9079 g_em->dst_offset_x = 0;
9080 g_em->dst_offset_y = 0;
9081 g_em->width = TILEX;
9082 g_em->height = TILEY;
9088 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9089 boolean any_player_moving,
9090 boolean any_player_snapping,
9091 boolean any_player_dropping)
9093 if (frame == 7 && !any_player_dropping)
9095 if (!local_player->was_waiting)
9097 if (!CheckSaveEngineSnapshotToList())
9100 local_player->was_waiting = TRUE;
9103 else if (any_player_moving || any_player_snapping || any_player_dropping)
9105 local_player->was_waiting = FALSE;
9109 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9110 boolean murphy_is_dropping)
9112 if (murphy_is_waiting)
9114 if (!local_player->was_waiting)
9116 if (!CheckSaveEngineSnapshotToList())
9119 local_player->was_waiting = TRUE;
9124 local_player->was_waiting = FALSE;
9128 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9129 boolean button_released)
9131 if (button_released)
9133 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9134 CheckSaveEngineSnapshotToList();
9136 else if (element_clicked)
9138 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9139 CheckSaveEngineSnapshotToList();
9141 game.snapshot.changed_action = TRUE;
9145 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9146 boolean any_player_moving,
9147 boolean any_player_snapping,
9148 boolean any_player_dropping)
9150 if (tape.single_step && tape.recording && !tape.pausing)
9151 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9152 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9154 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9155 any_player_snapping, any_player_dropping);
9157 return tape.pausing;
9160 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9161 boolean murphy_is_dropping)
9163 boolean murphy_starts_dropping = FALSE;
9166 for (i = 0; i < MAX_PLAYERS; i++)
9167 if (stored_player[i].force_dropping)
9168 murphy_starts_dropping = TRUE;
9170 if (tape.single_step && tape.recording && !tape.pausing)
9171 if (murphy_is_waiting && !murphy_starts_dropping)
9172 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9174 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9177 void CheckSingleStepMode_MM(boolean element_clicked,
9178 boolean button_released)
9180 if (tape.single_step && tape.recording && !tape.pausing)
9181 if (button_released)
9182 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9184 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9187 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9188 int graphic, int sync_frame, int x, int y)
9190 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9192 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9195 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9197 return (IS_NEXT_FRAME(sync_frame, graphic));
9200 int getGraphicInfo_Delay(int graphic)
9202 return graphic_info[graphic].anim_delay;
9205 void PlayMenuSoundExt(int sound)
9207 if (sound == SND_UNDEFINED)
9210 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9211 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9214 if (IS_LOOP_SOUND(sound))
9215 PlaySoundLoop(sound);
9220 void PlayMenuSound(void)
9222 PlayMenuSoundExt(menu.sound[game_status]);
9225 void PlayMenuSoundStereo(int sound, int stereo_position)
9227 if (sound == SND_UNDEFINED)
9230 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9231 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9234 if (IS_LOOP_SOUND(sound))
9235 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9237 PlaySoundStereo(sound, stereo_position);
9240 void PlayMenuSoundIfLoopExt(int sound)
9242 if (sound == SND_UNDEFINED)
9245 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9246 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9249 if (IS_LOOP_SOUND(sound))
9250 PlaySoundLoop(sound);
9253 void PlayMenuSoundIfLoop(void)
9255 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9258 void PlayMenuMusicExt(int music)
9260 if (music == MUS_UNDEFINED)
9263 if (!setup.sound_music)
9266 if (IS_LOOP_MUSIC(music))
9267 PlayMusicLoop(music);
9272 void PlayMenuMusic(void)
9274 char *curr_music = getCurrentlyPlayingMusicFilename();
9275 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9277 if (!strEqual(curr_music, next_music))
9278 PlayMenuMusicExt(menu.music[game_status]);
9281 void PlayMenuSoundsAndMusic(void)
9287 static void FadeMenuSounds(void)
9292 static void FadeMenuMusic(void)
9294 char *curr_music = getCurrentlyPlayingMusicFilename();
9295 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9297 if (!strEqual(curr_music, next_music))
9301 void FadeMenuSoundsAndMusic(void)
9307 void PlaySoundActivating(void)
9310 PlaySound(SND_MENU_ITEM_ACTIVATING);
9314 void PlaySoundSelecting(void)
9317 PlaySound(SND_MENU_ITEM_SELECTING);
9321 void ToggleFullscreenIfNeeded(void)
9323 // if setup and video fullscreen state are already matching, nothing do do
9324 if (setup.fullscreen == video.fullscreen_enabled ||
9325 !video.fullscreen_available)
9328 SDLSetWindowFullscreen(setup.fullscreen);
9330 // set setup value according to successfully changed fullscreen mode
9331 setup.fullscreen = video.fullscreen_enabled;
9334 void ChangeWindowScalingIfNeeded(void)
9336 // if setup and video window scaling are already matching, nothing do do
9337 if (setup.window_scaling_percent == video.window_scaling_percent ||
9338 video.fullscreen_enabled)
9341 SDLSetWindowScaling(setup.window_scaling_percent);
9343 // set setup value according to successfully changed window scaling
9344 setup.window_scaling_percent = video.window_scaling_percent;
9347 void ChangeVsyncModeIfNeeded(void)
9349 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9350 int video_vsync_mode = video.vsync_mode;
9352 // if setup and video vsync mode are already matching, nothing do do
9353 if (setup_vsync_mode == video_vsync_mode)
9356 // if renderer is using OpenGL, vsync mode can directly be changed
9357 SDLSetScreenVsyncMode(setup.vsync_mode);
9359 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9360 if (video.vsync_mode == video_vsync_mode)
9362 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9364 // save backbuffer content which gets lost when re-creating screen
9365 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9367 // force re-creating screen and renderer to set new vsync mode
9368 video.fullscreen_enabled = !setup.fullscreen;
9370 // when creating new renderer, destroy textures linked to old renderer
9371 FreeAllImageTextures(); // needs old renderer to free the textures
9373 // re-create screen and renderer (including change of vsync mode)
9374 ChangeVideoModeIfNeeded(setup.fullscreen);
9376 // set setup value according to successfully changed fullscreen mode
9377 setup.fullscreen = video.fullscreen_enabled;
9379 // restore backbuffer content from temporary backbuffer backup bitmap
9380 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9381 FreeBitmap(tmp_backbuffer);
9383 // update visible window/screen
9384 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9386 // when changing vsync mode, re-create textures for new renderer
9387 InitImageTextures();
9390 // set setup value according to successfully changed vsync mode
9391 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9394 static void JoinRectangles(int *x, int *y, int *width, int *height,
9395 int x2, int y2, int width2, int height2)
9397 // do not join with "off-screen" rectangle
9398 if (x2 == -1 || y2 == -1)
9403 *width = MAX(*width, width2);
9404 *height = MAX(*height, height2);
9407 void SetAnimStatus(int anim_status_new)
9409 if (anim_status_new == GAME_MODE_MAIN)
9410 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9411 else if (anim_status_new == GAME_MODE_NAMES)
9412 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9413 else if (anim_status_new == GAME_MODE_SCORES)
9414 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9416 global.anim_status_next = anim_status_new;
9418 // directly set screen modes that are entered without fading
9419 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9420 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9421 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9422 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9423 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9424 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9425 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9426 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9427 global.anim_status = global.anim_status_next;
9430 void SetGameStatus(int game_status_new)
9432 if (game_status_new != game_status)
9433 game_status_last_screen = game_status;
9435 game_status = game_status_new;
9437 SetAnimStatus(game_status_new);
9440 void SetFontStatus(int game_status_new)
9442 static int last_game_status = -1;
9444 if (game_status_new != -1)
9446 // set game status for font use after storing last game status
9447 last_game_status = game_status;
9448 game_status = game_status_new;
9452 // reset game status after font use from last stored game status
9453 game_status = last_game_status;
9457 void ResetFontStatus(void)
9462 void SetLevelSetInfo(char *identifier, int level_nr)
9464 setString(&levelset.identifier, identifier);
9466 levelset.level_nr = level_nr;
9469 boolean CheckIfAllViewportsHaveChanged(void)
9471 // if game status has not changed, viewports have not changed either
9472 if (game_status == game_status_last)
9475 // check if all viewports have changed with current game status
9477 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9478 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9479 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9480 int new_real_sx = vp_playfield->x;
9481 int new_real_sy = vp_playfield->y;
9482 int new_full_sxsize = vp_playfield->width;
9483 int new_full_sysize = vp_playfield->height;
9484 int new_dx = vp_door_1->x;
9485 int new_dy = vp_door_1->y;
9486 int new_dxsize = vp_door_1->width;
9487 int new_dysize = vp_door_1->height;
9488 int new_vx = vp_door_2->x;
9489 int new_vy = vp_door_2->y;
9490 int new_vxsize = vp_door_2->width;
9491 int new_vysize = vp_door_2->height;
9493 boolean playfield_viewport_has_changed =
9494 (new_real_sx != REAL_SX ||
9495 new_real_sy != REAL_SY ||
9496 new_full_sxsize != FULL_SXSIZE ||
9497 new_full_sysize != FULL_SYSIZE);
9499 boolean door_1_viewport_has_changed =
9502 new_dxsize != DXSIZE ||
9503 new_dysize != DYSIZE);
9505 boolean door_2_viewport_has_changed =
9508 new_vxsize != VXSIZE ||
9509 new_vysize != VYSIZE ||
9510 game_status_last == GAME_MODE_EDITOR);
9512 return (playfield_viewport_has_changed &&
9513 door_1_viewport_has_changed &&
9514 door_2_viewport_has_changed);
9517 boolean CheckFadeAll(void)
9519 return (CheckIfGlobalBorderHasChanged() ||
9520 CheckIfAllViewportsHaveChanged());
9523 void ChangeViewportPropertiesIfNeeded(void)
9525 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9526 FALSE : setup.small_game_graphics);
9527 int gfx_game_mode = game_status;
9528 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9530 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9531 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9532 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9533 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9534 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9535 int new_win_xsize = vp_window->width;
9536 int new_win_ysize = vp_window->height;
9537 int border_left = vp_playfield->border_left;
9538 int border_right = vp_playfield->border_right;
9539 int border_top = vp_playfield->border_top;
9540 int border_bottom = vp_playfield->border_bottom;
9541 int new_sx = vp_playfield->x + border_left;
9542 int new_sy = vp_playfield->y + border_top;
9543 int new_sxsize = vp_playfield->width - border_left - border_right;
9544 int new_sysize = vp_playfield->height - border_top - border_bottom;
9545 int new_real_sx = vp_playfield->x;
9546 int new_real_sy = vp_playfield->y;
9547 int new_full_sxsize = vp_playfield->width;
9548 int new_full_sysize = vp_playfield->height;
9549 int new_dx = vp_door_1->x;
9550 int new_dy = vp_door_1->y;
9551 int new_dxsize = vp_door_1->width;
9552 int new_dysize = vp_door_1->height;
9553 int new_vx = vp_door_2->x;
9554 int new_vy = vp_door_2->y;
9555 int new_vxsize = vp_door_2->width;
9556 int new_vysize = vp_door_2->height;
9557 int new_ex = vp_door_3->x;
9558 int new_ey = vp_door_3->y;
9559 int new_exsize = vp_door_3->width;
9560 int new_eysize = vp_door_3->height;
9561 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9562 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9563 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9564 int new_scr_fieldx = new_sxsize / tilesize;
9565 int new_scr_fieldy = new_sysize / tilesize;
9566 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9567 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9568 boolean init_gfx_buffers = FALSE;
9569 boolean init_video_buffer = FALSE;
9570 boolean init_gadgets_and_anims = FALSE;
9571 boolean init_em_graphics = FALSE;
9573 if (new_win_xsize != WIN_XSIZE ||
9574 new_win_ysize != WIN_YSIZE)
9576 WIN_XSIZE = new_win_xsize;
9577 WIN_YSIZE = new_win_ysize;
9579 init_video_buffer = TRUE;
9580 init_gfx_buffers = TRUE;
9581 init_gadgets_and_anims = TRUE;
9583 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9586 if (new_scr_fieldx != SCR_FIELDX ||
9587 new_scr_fieldy != SCR_FIELDY)
9589 // this always toggles between MAIN and GAME when using small tile size
9591 SCR_FIELDX = new_scr_fieldx;
9592 SCR_FIELDY = new_scr_fieldy;
9594 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9605 new_sxsize != SXSIZE ||
9606 new_sysize != SYSIZE ||
9607 new_dxsize != DXSIZE ||
9608 new_dysize != DYSIZE ||
9609 new_vxsize != VXSIZE ||
9610 new_vysize != VYSIZE ||
9611 new_exsize != EXSIZE ||
9612 new_eysize != EYSIZE ||
9613 new_real_sx != REAL_SX ||
9614 new_real_sy != REAL_SY ||
9615 new_full_sxsize != FULL_SXSIZE ||
9616 new_full_sysize != FULL_SYSIZE ||
9617 new_tilesize_var != TILESIZE_VAR
9620 // ------------------------------------------------------------------------
9621 // determine next fading area for changed viewport definitions
9622 // ------------------------------------------------------------------------
9624 // start with current playfield area (default fading area)
9627 FADE_SXSIZE = FULL_SXSIZE;
9628 FADE_SYSIZE = FULL_SYSIZE;
9630 // add new playfield area if position or size has changed
9631 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9632 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9634 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9635 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9638 // add current and new door 1 area if position or size has changed
9639 if (new_dx != DX || new_dy != DY ||
9640 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9642 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9643 DX, DY, DXSIZE, DYSIZE);
9644 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9645 new_dx, new_dy, new_dxsize, new_dysize);
9648 // add current and new door 2 area if position or size has changed
9649 if (new_vx != VX || new_vy != VY ||
9650 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9652 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9653 VX, VY, VXSIZE, VYSIZE);
9654 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9655 new_vx, new_vy, new_vxsize, new_vysize);
9658 // ------------------------------------------------------------------------
9659 // handle changed tile size
9660 // ------------------------------------------------------------------------
9662 if (new_tilesize_var != TILESIZE_VAR)
9664 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9666 // changing tile size invalidates scroll values of engine snapshots
9667 FreeEngineSnapshotSingle();
9669 // changing tile size requires update of graphic mapping for EM engine
9670 init_em_graphics = TRUE;
9681 SXSIZE = new_sxsize;
9682 SYSIZE = new_sysize;
9683 DXSIZE = new_dxsize;
9684 DYSIZE = new_dysize;
9685 VXSIZE = new_vxsize;
9686 VYSIZE = new_vysize;
9687 EXSIZE = new_exsize;
9688 EYSIZE = new_eysize;
9689 REAL_SX = new_real_sx;
9690 REAL_SY = new_real_sy;
9691 FULL_SXSIZE = new_full_sxsize;
9692 FULL_SYSIZE = new_full_sysize;
9693 TILESIZE_VAR = new_tilesize_var;
9695 init_gfx_buffers = TRUE;
9696 init_gadgets_and_anims = TRUE;
9698 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9699 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9702 if (init_gfx_buffers)
9704 // Debug("tools:viewport", "init_gfx_buffers");
9706 SCR_FIELDX = new_scr_fieldx_buffers;
9707 SCR_FIELDY = new_scr_fieldy_buffers;
9711 SCR_FIELDX = new_scr_fieldx;
9712 SCR_FIELDY = new_scr_fieldy;
9714 SetDrawDeactivationMask(REDRAW_NONE);
9715 SetDrawBackgroundMask(REDRAW_FIELD);
9718 if (init_video_buffer)
9720 // Debug("tools:viewport", "init_video_buffer");
9722 FreeAllImageTextures(); // needs old renderer to free the textures
9724 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9725 InitImageTextures();
9728 if (init_gadgets_and_anims)
9730 // Debug("tools:viewport", "init_gadgets_and_anims");
9733 InitGlobalAnimations();
9736 if (init_em_graphics)
9738 InitGraphicInfo_EM();