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,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void MapToolButtons(unsigned int);
179 static void UnmapToolButtons(void);
180 static void HandleToolButtons(struct GadgetInfo *);
181 static int el_act_dir2crm(int, int, int);
182 static int el_act2crm(int, int);
184 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
185 static int request_gadget_id = -1;
187 static char *print_if_not_empty(int element)
189 static char *s = NULL;
190 char *token_name = element_info[element].token_name;
195 s = checked_malloc(strlen(token_name) + 10 + 1);
197 if (element != EL_EMPTY)
198 sprintf(s, "%d\t['%s']", element, token_name);
200 sprintf(s, "%d", element);
205 int getFieldbufferOffsetX_RND(int dir, int pos)
207 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
208 int dx = (dir & MV_HORIZONTAL ? pos : 0);
209 int dx_var = dx * TILESIZE_VAR / TILESIZE;
212 if (EVEN(SCR_FIELDX))
214 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
215 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
217 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
218 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
220 fx += (dx_var > 0 ? TILEX_VAR : 0);
227 if (full_lev_fieldx <= SCR_FIELDX)
229 if (EVEN(SCR_FIELDX))
230 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
232 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 int getFieldbufferOffsetY_RND(int dir, int pos)
240 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
241 int dy = (dir & MV_VERTICAL ? pos : 0);
242 int dy_var = dy * TILESIZE_VAR / TILESIZE;
245 if (EVEN(SCR_FIELDY))
247 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
248 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
250 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
251 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
253 fy += (dy_var > 0 ? TILEY_VAR : 0);
260 if (full_lev_fieldy <= SCR_FIELDY)
262 if (EVEN(SCR_FIELDY))
263 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
265 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 static int getLevelFromScreenX_RND(int sx)
273 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
276 int lx = LEVELX((px + dx) / TILESIZE_VAR);
281 static int getLevelFromScreenY_RND(int sy)
283 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
286 int ly = LEVELY((py + dy) / TILESIZE_VAR);
291 static int getLevelFromScreenX_EM(int sx)
293 int level_xsize = level.native_em_level->cav->width;
294 int full_xsize = level_xsize * TILESIZE_VAR;
296 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
298 int fx = getFieldbufferOffsetX_EM();
301 int lx = LEVELX((px + dx) / TILESIZE_VAR);
306 static int getLevelFromScreenY_EM(int sy)
308 int level_ysize = level.native_em_level->cav->height;
309 int full_ysize = level_ysize * TILESIZE_VAR;
311 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
313 int fy = getFieldbufferOffsetY_EM();
316 int ly = LEVELY((py + dy) / TILESIZE_VAR);
321 static int getLevelFromScreenX_SP(int sx)
323 int menBorder = setup.sp_show_border_elements;
324 int level_xsize = level.native_sp_level->width;
325 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
327 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
329 int fx = getFieldbufferOffsetX_SP();
332 int lx = LEVELX((px + dx) / TILESIZE_VAR);
337 static int getLevelFromScreenY_SP(int sy)
339 int menBorder = setup.sp_show_border_elements;
340 int level_ysize = level.native_sp_level->height;
341 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
343 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
345 int fy = getFieldbufferOffsetY_SP();
348 int ly = LEVELY((py + dy) / TILESIZE_VAR);
353 static int getLevelFromScreenX_MM(int sx)
355 int level_xsize = level.native_mm_level->fieldx;
356 int full_xsize = level_xsize * TILESIZE_VAR;
358 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
361 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
366 static int getLevelFromScreenY_MM(int sy)
368 int level_ysize = level.native_mm_level->fieldy;
369 int full_ysize = level_ysize * TILESIZE_VAR;
371 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
374 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
379 int getLevelFromScreenX(int x)
381 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
382 return getLevelFromScreenX_EM(x);
383 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
384 return getLevelFromScreenX_SP(x);
385 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
386 return getLevelFromScreenX_MM(x);
388 return getLevelFromScreenX_RND(x);
391 int getLevelFromScreenY(int y)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenY_EM(y);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenY_SP(y);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenY_MM(y);
400 return getLevelFromScreenY_RND(y);
403 int getScreenFieldSizeX(void)
405 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
408 int getScreenFieldSizeY(void)
410 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
413 void DumpTile(int x, int y)
420 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
423 if (!IN_LEV_FIELD(x, y))
425 Info("(not in level field)");
431 token_name = element_info[Tile[x][y]].token_name;
433 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
434 Info("Back: %s", print_if_not_empty(Back[x][y]));
435 Info("Store: %s", print_if_not_empty(Store[x][y]));
436 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
437 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
438 Info("MovPos: %d", MovPos[x][y]);
439 Info("MovDir: %d", MovDir[x][y]);
440 Info("MovDelay: %d", MovDelay[x][y]);
441 Info("ChangeDelay: %d", ChangeDelay[x][y]);
442 Info("CustomValue: %d", CustomValue[x][y]);
443 Info("GfxElement: %d", GfxElement[x][y]);
444 Info("GfxAction: %d", GfxAction[x][y]);
445 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
446 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 int GetDrawtoField(void)
486 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
489 static void RedrawPlayfield_RND(void)
491 if (game.envelope_active)
494 DrawLevel(REDRAW_ALL);
498 void RedrawPlayfield(void)
500 if (game_status != GAME_MODE_PLAYING)
503 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
504 RedrawPlayfield_EM(TRUE);
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
506 RedrawPlayfield_SP(TRUE);
507 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
508 RedrawPlayfield_MM();
509 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
510 RedrawPlayfield_RND();
512 BlitScreenToBitmap(backbuffer);
514 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
518 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
521 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
522 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
524 // may happen for "border.draw_masked.*" with undefined "global.border.*"
525 if (src_bitmap == NULL)
528 if (x == -1 && y == -1)
531 if (draw_target == DRAW_TO_SCREEN)
532 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
534 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
537 static void DrawMaskedBorderExt_FIELD(int draw_target)
539 if (global.border_status >= GAME_MODE_MAIN &&
540 global.border_status <= GAME_MODE_PLAYING &&
541 border.draw_masked[global.border_status])
542 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
546 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_1))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 (global.border_status != GAME_MODE_EDITOR ||
555 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
556 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
559 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
561 // when drawing to backbuffer, never draw border over open doors
562 if (draw_target == DRAW_TO_BACKBUFFER &&
563 (GetDoorState() & DOOR_OPEN_2))
566 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
567 global.border_status != GAME_MODE_EDITOR)
568 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
571 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
573 // currently not available
576 static void DrawMaskedBorderExt_ALL(int draw_target)
578 DrawMaskedBorderExt_FIELD(draw_target);
579 DrawMaskedBorderExt_DOOR_1(draw_target);
580 DrawMaskedBorderExt_DOOR_2(draw_target);
581 DrawMaskedBorderExt_DOOR_3(draw_target);
584 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
586 // never draw masked screen borders on borderless screens
587 if (global.border_status == GAME_MODE_LOADING ||
588 global.border_status == GAME_MODE_TITLE)
591 if (redraw_mask & REDRAW_ALL)
592 DrawMaskedBorderExt_ALL(draw_target);
595 if (redraw_mask & REDRAW_FIELD)
596 DrawMaskedBorderExt_FIELD(draw_target);
597 if (redraw_mask & REDRAW_DOOR_1)
598 DrawMaskedBorderExt_DOOR_1(draw_target);
599 if (redraw_mask & REDRAW_DOOR_2)
600 DrawMaskedBorderExt_DOOR_2(draw_target);
601 if (redraw_mask & REDRAW_DOOR_3)
602 DrawMaskedBorderExt_DOOR_3(draw_target);
606 void DrawMaskedBorder_FIELD(void)
608 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
611 void DrawMaskedBorder(int redraw_mask)
613 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
616 void DrawMaskedBorderToTarget(int draw_target)
618 if (draw_target == DRAW_TO_BACKBUFFER ||
619 draw_target == DRAW_TO_SCREEN)
621 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
625 int last_border_status = global.border_status;
627 if (draw_target == DRAW_TO_FADE_SOURCE)
629 global.border_status = gfx.fade_border_source_status;
630 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
632 else if (draw_target == DRAW_TO_FADE_TARGET)
634 global.border_status = gfx.fade_border_target_status;
635 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
638 // always use global border for PLAYING when restarting the game
639 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
640 global.border_status = GAME_MODE_PLAYING;
642 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
644 global.border_status = last_border_status;
645 gfx.masked_border_bitmap_ptr = backbuffer;
649 void DrawTileCursor(int draw_target, int drawing_stage)
651 int tile_cursor_active = (game_status == GAME_MODE_PLAYING);
653 DrawTileCursor_MM(draw_target, drawing_stage, tile_cursor_active);
656 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
658 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
661 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
663 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
664 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
666 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
669 void BlitScreenToBitmap(Bitmap *target_bitmap)
671 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
672 BlitScreenToBitmap_EM(target_bitmap);
673 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
674 BlitScreenToBitmap_SP(target_bitmap);
675 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
676 BlitScreenToBitmap_MM(target_bitmap);
677 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
678 BlitScreenToBitmap_RND(target_bitmap);
680 redraw_mask |= REDRAW_FIELD;
683 static void DrawFramesPerSecond(void)
686 int font_nr = FONT_TEXT_2;
687 int font_width = getFontWidth(font_nr);
688 int draw_deactivation_mask = GetDrawDeactivationMask();
689 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
691 // draw FPS with leading space (needed if field buffer deactivated)
692 sprintf(text, " %04.1f fps", global.frames_per_second);
694 // override draw deactivation mask (required for invisible warp mode)
695 SetDrawDeactivationMask(REDRAW_NONE);
697 // draw opaque FPS if field buffer deactivated, else draw masked FPS
698 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
699 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
701 // set draw deactivation mask to previous value
702 SetDrawDeactivationMask(draw_deactivation_mask);
704 // force full-screen redraw in this frame
705 redraw_mask = REDRAW_ALL;
709 static void PrintFrameTimeDebugging(void)
711 static unsigned int last_counter = 0;
712 unsigned int counter = Counter();
713 int diff_1 = counter - last_counter;
714 int diff_2 = diff_1 - GAME_FRAME_DELAY;
716 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
717 char diff_bar[2 * diff_2_max + 5];
721 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
723 for (i = 0; i < diff_2_max; i++)
724 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
725 i >= diff_2_max - diff_2_cut ? '-' : ' ');
727 diff_bar[pos++] = '|';
729 for (i = 0; i < diff_2_max; i++)
730 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
732 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
734 diff_bar[pos++] = '\0';
736 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
739 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
742 last_counter = counter;
746 static int unifiedRedrawMask(int mask)
748 if (mask & REDRAW_ALL)
751 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
757 static boolean equalRedrawMasks(int mask_1, int mask_2)
759 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
762 void BackToFront(void)
764 static int last_redraw_mask = REDRAW_NONE;
766 // force screen redraw in every frame to continue drawing global animations
767 // (but always use the last redraw mask to prevent unwanted side effects)
768 if (redraw_mask == REDRAW_NONE)
769 redraw_mask = last_redraw_mask;
771 last_redraw_mask = redraw_mask;
774 // masked border now drawn immediately when blitting backbuffer to window
776 // draw masked border to all viewports, if defined
777 DrawMaskedBorder(redraw_mask);
780 // draw frames per second (only if debug mode is enabled)
781 if (redraw_mask & REDRAW_FPS)
782 DrawFramesPerSecond();
784 // remove playfield redraw before potentially merging with doors redraw
785 if (DrawingDeactivated(REAL_SX, REAL_SY))
786 redraw_mask &= ~REDRAW_FIELD;
788 // redraw complete window if both playfield and (some) doors need redraw
789 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
790 redraw_mask = REDRAW_ALL;
792 /* although redrawing the whole window would be fine for normal gameplay,
793 being able to only redraw the playfield is required for deactivating
794 certain drawing areas (mainly playfield) to work, which is needed for
795 warp-forward to be fast enough (by skipping redraw of most frames) */
797 if (redraw_mask & REDRAW_ALL)
799 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
801 else if (redraw_mask & REDRAW_FIELD)
803 BlitBitmap(backbuffer, window,
804 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
806 else if (redraw_mask & REDRAW_DOORS)
808 // merge door areas to prevent calling screen redraw more than once
814 if (redraw_mask & REDRAW_DOOR_1)
818 x2 = MAX(x2, DX + DXSIZE);
819 y2 = MAX(y2, DY + DYSIZE);
822 if (redraw_mask & REDRAW_DOOR_2)
826 x2 = MAX(x2, VX + VXSIZE);
827 y2 = MAX(y2, VY + VYSIZE);
830 if (redraw_mask & REDRAW_DOOR_3)
834 x2 = MAX(x2, EX + EXSIZE);
835 y2 = MAX(y2, EY + EYSIZE);
838 // make sure that at least one pixel is blitted, and inside the screen
839 // (else nothing is blitted, causing the animations not to be updated)
840 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
841 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
842 x2 = MIN(MAX(1, x2), WIN_XSIZE);
843 y2 = MIN(MAX(1, y2), WIN_YSIZE);
845 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
848 redraw_mask = REDRAW_NONE;
851 PrintFrameTimeDebugging();
855 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
857 unsigned int frame_delay_value_old = GetVideoFrameDelay();
859 SetVideoFrameDelay(frame_delay_value);
863 SetVideoFrameDelay(frame_delay_value_old);
866 static int fade_type_skip = FADE_TYPE_NONE;
868 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
870 void (*draw_border_function)(void) = NULL;
871 int x, y, width, height;
872 int fade_delay, post_delay;
874 if (fade_type == FADE_TYPE_FADE_OUT)
876 if (fade_type_skip != FADE_TYPE_NONE)
878 // skip all fade operations until specified fade operation
879 if (fade_type & fade_type_skip)
880 fade_type_skip = FADE_TYPE_NONE;
885 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
889 redraw_mask |= fade_mask;
891 if (fade_type == FADE_TYPE_SKIP)
893 fade_type_skip = fade_mode;
898 fade_delay = fading.fade_delay;
899 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
901 if (fade_type_skip != FADE_TYPE_NONE)
903 // skip all fade operations until specified fade operation
904 if (fade_type & fade_type_skip)
905 fade_type_skip = FADE_TYPE_NONE;
910 if (global.autoplay_leveldir)
915 if (fade_mask == REDRAW_FIELD)
920 height = FADE_SYSIZE;
922 if (border.draw_masked_when_fading)
923 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
925 DrawMaskedBorder_FIELD(); // draw once
935 // when switching screens without fading, set fade delay to zero
936 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
939 // do not display black frame when fading out without fade delay
940 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
943 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
944 draw_border_function);
946 redraw_mask &= ~fade_mask;
948 ClearAutoRepeatKeyEvents();
951 static void SetScreenStates_BeforeFadingIn(void)
953 // temporarily set screen mode for animations to screen after fading in
954 global.anim_status = global.anim_status_next;
956 // store backbuffer with all animations that will be started after fading in
957 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
959 // set screen mode for animations back to fading
960 global.anim_status = GAME_MODE_PSEUDO_FADING;
963 static void SetScreenStates_AfterFadingIn(void)
965 // store new source screen (to use correct masked border for fading)
966 gfx.fade_border_source_status = global.border_status;
968 global.anim_status = global.anim_status_next;
971 static void SetScreenStates_BeforeFadingOut(void)
973 // store new target screen (to use correct masked border for fading)
974 gfx.fade_border_target_status = game_status;
976 // set screen mode for animations to fading
977 global.anim_status = GAME_MODE_PSEUDO_FADING;
979 // store backbuffer with all animations that will be stopped for fading out
980 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
983 static void SetScreenStates_AfterFadingOut(void)
985 global.border_status = game_status;
987 // always use global border for PLAYING when restarting the game
988 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
989 global.border_status = GAME_MODE_PLAYING;
992 void FadeIn(int fade_mask)
994 SetScreenStates_BeforeFadingIn();
997 DrawMaskedBorder(REDRAW_ALL);
1000 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1001 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1003 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1007 FADE_SXSIZE = FULL_SXSIZE;
1008 FADE_SYSIZE = FULL_SYSIZE;
1010 // activate virtual buttons depending on upcoming game status
1011 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1012 game_status == GAME_MODE_PLAYING && !tape.playing)
1013 SetOverlayActive(TRUE);
1015 SetScreenStates_AfterFadingIn();
1017 // force update of global animation status in case of rapid screen changes
1018 redraw_mask = REDRAW_ALL;
1022 void FadeOut(int fade_mask)
1024 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1025 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1026 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1029 SetScreenStates_BeforeFadingOut();
1031 SetTileCursorActive(FALSE);
1032 SetOverlayActive(FALSE);
1035 DrawMaskedBorder(REDRAW_ALL);
1038 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1039 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1041 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1043 SetScreenStates_AfterFadingOut();
1046 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1048 static struct TitleFadingInfo fading_leave_stored;
1051 fading_leave_stored = fading_leave;
1053 fading = fading_leave_stored;
1056 void FadeSetEnterMenu(void)
1058 fading = menu.enter_menu;
1060 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1063 void FadeSetLeaveMenu(void)
1065 fading = menu.leave_menu;
1067 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1070 void FadeSetEnterScreen(void)
1072 fading = menu.enter_screen[game_status];
1074 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1077 void FadeSetNextScreen(void)
1079 fading = menu.next_screen[game_status];
1081 // (do not overwrite fade mode set by FadeSetEnterScreen)
1082 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1085 void FadeSetLeaveScreen(void)
1087 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1090 void FadeSetFromType(int type)
1092 if (type & TYPE_ENTER_SCREEN)
1093 FadeSetEnterScreen();
1094 else if (type & TYPE_ENTER)
1096 else if (type & TYPE_LEAVE)
1100 void FadeSetDisabled(void)
1102 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1104 fading = fading_none;
1107 void FadeSkipNextFadeIn(void)
1109 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1112 void FadeSkipNextFadeOut(void)
1114 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1117 static int getGlobalGameStatus(int status)
1119 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1120 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1124 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1126 if (graphic == IMG_UNDEFINED)
1127 return IMG_UNDEFINED;
1129 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1131 return (graphic_info[graphic].bitmap != NULL || redefined ?
1132 graphic : default_graphic);
1135 static int getBackgroundImage(int graphic)
1137 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1140 static int getGlobalBorderImage(int graphic)
1142 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1145 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1147 int status = getGlobalGameStatus(status_raw);
1149 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1150 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1151 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1152 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1154 int graphic_final = getGlobalBorderImage(graphic);
1156 return graphic_info[graphic_final].bitmap;
1159 void SetBackgroundImage(int graphic, int redraw_mask)
1161 struct GraphicInfo *g = &graphic_info[graphic];
1162 struct GraphicInfo g_undefined = { 0 };
1164 if (graphic == IMG_UNDEFINED)
1167 // always use original size bitmap for backgrounds, if existing
1168 Bitmap *bitmap = (g->bitmaps != NULL &&
1169 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1170 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1172 // remove every mask before setting mask for window, and
1173 // remove window area mask before setting mask for main or door area
1174 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1176 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1177 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1178 SetBackgroundBitmap(bitmap, redraw_mask,
1180 g->width, g->height);
1183 void SetWindowBackgroundImageIfDefined(int graphic)
1185 if (graphic_info[graphic].bitmap)
1186 SetBackgroundImage(graphic, REDRAW_ALL);
1189 void SetMainBackgroundImageIfDefined(int graphic)
1191 if (graphic_info[graphic].bitmap)
1192 SetBackgroundImage(graphic, REDRAW_FIELD);
1195 void SetDoorBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1201 void SetWindowBackgroundImage(int graphic)
1203 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1206 void SetMainBackgroundImage(int graphic)
1208 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1211 void SetDoorBackgroundImage(int graphic)
1213 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1216 void SetPanelBackground(void)
1218 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1221 void DrawBackground(int x, int y, int width, int height)
1223 // "drawto" might still point to playfield buffer here (hall of fame)
1224 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1226 if (IN_GFX_FIELD_FULL(x, y))
1227 redraw_mask |= REDRAW_FIELD;
1228 else if (IN_GFX_DOOR_1(x, y))
1229 redraw_mask |= REDRAW_DOOR_1;
1230 else if (IN_GFX_DOOR_2(x, y))
1231 redraw_mask |= REDRAW_DOOR_2;
1232 else if (IN_GFX_DOOR_3(x, y))
1233 redraw_mask |= REDRAW_DOOR_3;
1236 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1238 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1240 if (font->bitmap == NULL)
1243 DrawBackground(x, y, width, height);
1246 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1248 struct GraphicInfo *g = &graphic_info[graphic];
1250 if (g->bitmap == NULL)
1253 DrawBackground(x, y, width, height);
1256 static int game_status_last = -1;
1257 static Bitmap *global_border_bitmap_last = NULL;
1258 static Bitmap *global_border_bitmap = NULL;
1259 static int real_sx_last = -1, real_sy_last = -1;
1260 static int full_sxsize_last = -1, full_sysize_last = -1;
1261 static int dx_last = -1, dy_last = -1;
1262 static int dxsize_last = -1, dysize_last = -1;
1263 static int vx_last = -1, vy_last = -1;
1264 static int vxsize_last = -1, vysize_last = -1;
1265 static int ex_last = -1, ey_last = -1;
1266 static int exsize_last = -1, eysize_last = -1;
1268 boolean CheckIfGlobalBorderHasChanged(void)
1270 // if game status has not changed, global border has not changed either
1271 if (game_status == game_status_last)
1274 // determine and store new global border bitmap for current game status
1275 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1277 return (global_border_bitmap_last != global_border_bitmap);
1280 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1282 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1283 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1285 // if game status has not changed, nothing has to be redrawn
1286 if (game_status == game_status_last)
1289 // redraw if last screen was title screen
1290 if (game_status_last == GAME_MODE_TITLE)
1293 // redraw if global screen border has changed
1294 if (CheckIfGlobalBorderHasChanged())
1297 // redraw if position or size of playfield area has changed
1298 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1299 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1302 // redraw if position or size of door area has changed
1303 if (dx_last != DX || dy_last != DY ||
1304 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1307 // redraw if position or size of tape area has changed
1308 if (vx_last != VX || vy_last != VY ||
1309 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1312 // redraw if position or size of editor area has changed
1313 if (ex_last != EX || ey_last != EY ||
1314 exsize_last != EXSIZE || eysize_last != EYSIZE)
1321 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1324 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1326 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1329 void RedrawGlobalBorder(void)
1331 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1333 RedrawGlobalBorderFromBitmap(bitmap);
1335 redraw_mask = REDRAW_ALL;
1338 static void RedrawGlobalBorderIfNeeded(void)
1340 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1341 if (game_status == game_status_last)
1345 // copy current draw buffer to later copy back areas that have not changed
1346 if (game_status_last != GAME_MODE_TITLE)
1347 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1349 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1350 if (CheckIfGlobalBorderRedrawIsNeeded())
1352 // determine and store new global border bitmap for current game status
1353 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1356 // redraw global screen border (or clear, if defined to be empty)
1357 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1359 if (game_status == GAME_MODE_EDITOR)
1360 DrawSpecialEditorDoor();
1362 // copy previous playfield and door areas, if they are defined on both
1363 // previous and current screen and if they still have the same size
1365 if (real_sx_last != -1 && real_sy_last != -1 &&
1366 REAL_SX != -1 && REAL_SY != -1 &&
1367 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1368 BlitBitmap(bitmap_db_store_1, backbuffer,
1369 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1372 if (dx_last != -1 && dy_last != -1 &&
1373 DX != -1 && DY != -1 &&
1374 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1378 if (game_status != GAME_MODE_EDITOR)
1380 if (vx_last != -1 && vy_last != -1 &&
1381 VX != -1 && VY != -1 &&
1382 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1388 if (ex_last != -1 && ey_last != -1 &&
1389 EX != -1 && EY != -1 &&
1390 exsize_last == EXSIZE && eysize_last == EYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1395 redraw_mask = REDRAW_ALL;
1398 game_status_last = game_status;
1400 global_border_bitmap_last = global_border_bitmap;
1402 real_sx_last = REAL_SX;
1403 real_sy_last = REAL_SY;
1404 full_sxsize_last = FULL_SXSIZE;
1405 full_sysize_last = FULL_SYSIZE;
1408 dxsize_last = DXSIZE;
1409 dysize_last = DYSIZE;
1412 vxsize_last = VXSIZE;
1413 vysize_last = VYSIZE;
1416 exsize_last = EXSIZE;
1417 eysize_last = EYSIZE;
1420 void ClearField(void)
1422 RedrawGlobalBorderIfNeeded();
1424 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1425 // (when entering hall of fame after playing)
1426 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1428 // !!! maybe this should be done before clearing the background !!!
1429 if (game_status == GAME_MODE_PLAYING)
1431 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1432 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1436 SetDrawtoField(DRAW_TO_BACKBUFFER);
1440 void MarkTileDirty(int x, int y)
1442 redraw_mask |= REDRAW_FIELD;
1445 void SetBorderElement(void)
1449 BorderElement = EL_EMPTY;
1451 // only the R'n'D game engine may use an additional steelwall border
1452 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1455 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1457 for (x = 0; x < lev_fieldx; x++)
1459 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1460 BorderElement = EL_STEELWALL;
1462 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1468 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1469 int max_array_fieldx, int max_array_fieldy,
1470 short field[max_array_fieldx][max_array_fieldy],
1471 int max_fieldx, int max_fieldy)
1473 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1474 struct XY *check = xy_topdown;
1475 int old_element = field[start_x][start_y];
1478 // do nothing if start field already has the desired content
1479 if (old_element == fill_element)
1482 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1484 while (stack_pos > 0)
1486 struct XY current = stack_buffer[--stack_pos];
1489 field[current.x][current.y] = fill_element;
1491 for (i = 0; i < 4; i++)
1493 int x = current.x + check[i].x;
1494 int y = current.y + check[i].y;
1496 // check for stack buffer overflow (should not happen)
1497 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1498 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1500 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1501 stack_buffer[stack_pos++] = (struct XY){ x, y };
1506 void FloodFillLevel(int from_x, int from_y, int fill_element,
1507 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1508 int max_fieldx, int max_fieldy)
1510 FloodFillLevelExt(from_x, from_y, fill_element,
1511 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1512 max_fieldx, max_fieldy);
1515 void SetRandomAnimationValue(int x, int y)
1517 gfx.anim_random_frame = GfxRandom[x][y];
1520 void SetAnimationFirstLevel(int first_level)
1522 gfx.anim_first_level = first_level;
1525 int getGraphicAnimationFrame(int graphic, int sync_frame)
1527 // animation synchronized with global frame counter, not move position
1528 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1529 sync_frame = FrameCounter;
1530 else if (graphic_info[graphic].anim_global_anim_sync)
1531 sync_frame = getGlobalAnimSyncFrame();
1533 return getAnimationFrame(graphic_info[graphic].anim_frames,
1534 graphic_info[graphic].anim_delay,
1535 graphic_info[graphic].anim_mode,
1536 graphic_info[graphic].anim_start_frame,
1540 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1542 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1544 struct GraphicInfo *g = &graphic_info[graphic];
1545 int xsize = MAX(1, g->anim_frames_per_line);
1546 int ysize = MAX(1, g->anim_frames / xsize);
1547 int xoffset = g->anim_start_frame % xsize;
1548 int yoffset = g->anim_start_frame % ysize;
1549 // may be needed if screen field is significantly larger than playfield
1550 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1551 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1552 int sync_frame = y * xsize + x;
1554 return sync_frame % g->anim_frames;
1556 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1558 struct GraphicInfo *g = &graphic_info[graphic];
1559 // may be needed if screen field is significantly larger than playfield
1560 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1561 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1562 int sync_frame = GfxRandomStatic[x][y];
1564 return sync_frame % g->anim_frames;
1568 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1570 return getGraphicAnimationFrame(graphic, sync_frame);
1574 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1576 struct GraphicInfo *g = &graphic_info[graphic];
1577 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1579 if (tilesize == gfx.standard_tile_size)
1580 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1581 else if (tilesize == game.tile_size)
1582 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1584 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1587 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1588 boolean get_backside)
1590 struct GraphicInfo *g = &graphic_info[graphic];
1591 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1592 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1594 if (g->offset_y == 0) // frames are ordered horizontally
1596 int max_width = g->anim_frames_per_line * g->width;
1597 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1599 *x = pos % max_width;
1600 *y = src_y % g->height + pos / max_width * g->height;
1602 else if (g->offset_x == 0) // frames are ordered vertically
1604 int max_height = g->anim_frames_per_line * g->height;
1605 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1607 *x = src_x % g->width + pos / max_height * g->width;
1608 *y = pos % max_height;
1610 else // frames are ordered diagonally
1612 *x = src_x + frame * g->offset_x;
1613 *y = src_y + frame * g->offset_y;
1617 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1618 Bitmap **bitmap, int *x, int *y,
1619 boolean get_backside)
1621 struct GraphicInfo *g = &graphic_info[graphic];
1623 // if no graphics defined at all, use fallback graphics
1624 if (g->bitmaps == NULL)
1625 *g = graphic_info[IMG_CHAR_EXCLAM];
1627 // if no in-game graphics defined, always use standard graphic size
1628 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1629 tilesize = TILESIZE;
1631 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1632 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1634 *x = *x * tilesize / g->tile_size;
1635 *y = *y * tilesize / g->tile_size;
1638 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1639 Bitmap **bitmap, int *x, int *y)
1641 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1644 void getFixedGraphicSource(int graphic, int frame,
1645 Bitmap **bitmap, int *x, int *y)
1647 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1650 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1652 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1655 void getGlobalAnimGraphicSource(int graphic, int frame,
1656 Bitmap **bitmap, int *x, int *y)
1658 struct GraphicInfo *g = &graphic_info[graphic];
1660 // if no graphics defined at all, use fallback graphics
1661 if (g->bitmaps == NULL)
1662 *g = graphic_info[IMG_CHAR_EXCLAM];
1664 // use original size graphics, if existing, else use standard size graphics
1665 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1666 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1668 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1670 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1673 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1674 int *x, int *y, boolean get_backside)
1676 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1680 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1682 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1685 void DrawGraphic(int x, int y, int graphic, int frame)
1688 if (!IN_SCR_FIELD(x, y))
1690 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1691 Debug("draw:DrawGraphic", "This should never happen!");
1697 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1700 MarkTileDirty(x, y);
1703 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1706 if (!IN_SCR_FIELD(x, y))
1708 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1710 Debug("draw:DrawFixedGraphic", "This should never happen!");
1716 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1718 MarkTileDirty(x, y);
1721 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1727 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1729 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1732 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1738 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1739 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1742 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1745 if (!IN_SCR_FIELD(x, y))
1747 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1749 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1755 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1758 MarkTileDirty(x, y);
1761 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1764 if (!IN_SCR_FIELD(x, y))
1766 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1768 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1774 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1776 MarkTileDirty(x, y);
1779 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1785 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1787 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1791 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1792 int graphic, int frame)
1797 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1799 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1803 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1805 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1807 MarkTileDirty(x / tilesize, y / tilesize);
1810 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1813 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1814 graphic, frame, tilesize);
1815 MarkTileDirty(x / tilesize, y / tilesize);
1818 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1824 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1825 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1828 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1829 int frame, int tilesize)
1834 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1835 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1838 void DrawMiniGraphic(int x, int y, int graphic)
1840 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1841 MarkTileDirty(x / 2, y / 2);
1844 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1849 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1850 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1853 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1854 int graphic, int frame,
1855 int cut_mode, int mask_mode)
1860 int width = TILEX, height = TILEY;
1863 if (dx || dy) // shifted graphic
1865 if (x < BX1) // object enters playfield from the left
1872 else if (x > BX2) // object enters playfield from the right
1878 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1884 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1886 else if (dx) // general horizontal movement
1887 MarkTileDirty(x + SIGN(dx), y);
1889 if (y < BY1) // object enters playfield from the top
1891 if (cut_mode == CUT_BELOW) // object completely above top border
1899 else if (y > BY2) // object enters playfield from the bottom
1905 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1911 else if (dy > 0 && cut_mode == CUT_ABOVE)
1913 if (y == BY2) // object completely above bottom border
1919 MarkTileDirty(x, y + 1);
1920 } // object leaves playfield to the bottom
1921 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1923 else if (dy) // general vertical movement
1924 MarkTileDirty(x, y + SIGN(dy));
1928 if (!IN_SCR_FIELD(x, y))
1930 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1932 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1938 width = width * TILESIZE_VAR / TILESIZE;
1939 height = height * TILESIZE_VAR / TILESIZE;
1940 cx = cx * TILESIZE_VAR / TILESIZE;
1941 cy = cy * TILESIZE_VAR / TILESIZE;
1942 dx = dx * TILESIZE_VAR / TILESIZE;
1943 dy = dy * TILESIZE_VAR / TILESIZE;
1945 if (width > 0 && height > 0)
1947 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1952 dst_x = FX + x * TILEX_VAR + dx;
1953 dst_y = FY + y * TILEY_VAR + dy;
1955 if (mask_mode == USE_MASKING)
1956 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1959 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1962 MarkTileDirty(x, y);
1966 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1967 int graphic, int frame,
1968 int cut_mode, int mask_mode)
1973 int width = TILEX_VAR, height = TILEY_VAR;
1976 int x2 = x + SIGN(dx);
1977 int y2 = y + SIGN(dy);
1979 // movement with two-tile animations must be sync'ed with movement position,
1980 // not with current GfxFrame (which can be higher when using slow movement)
1981 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1982 int anim_frames = graphic_info[graphic].anim_frames;
1984 // (we also need anim_delay here for movement animations with less frames)
1985 int anim_delay = graphic_info[graphic].anim_delay;
1986 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1988 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1989 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1991 // re-calculate animation frame for two-tile movement animation
1992 frame = getGraphicAnimationFrame(graphic, sync_frame);
1994 // check if movement start graphic inside screen area and should be drawn
1995 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1997 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1999 dst_x = FX + x1 * TILEX_VAR;
2000 dst_y = FY + y1 * TILEY_VAR;
2002 if (mask_mode == USE_MASKING)
2003 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2006 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2009 MarkTileDirty(x1, y1);
2012 // check if movement end graphic inside screen area and should be drawn
2013 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2015 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2017 dst_x = FX + x2 * TILEX_VAR;
2018 dst_y = FY + y2 * TILEY_VAR;
2020 if (mask_mode == USE_MASKING)
2021 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2024 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2027 MarkTileDirty(x2, y2);
2031 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2032 int graphic, int frame,
2033 int cut_mode, int mask_mode)
2037 DrawGraphic(x, y, graphic, frame);
2042 if (graphic_info[graphic].double_movement) // EM style movement images
2043 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2045 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2048 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2049 int graphic, int frame, int cut_mode)
2051 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2054 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2055 int cut_mode, int mask_mode)
2057 int lx = LEVELX(x), ly = LEVELY(y);
2061 if (IN_LEV_FIELD(lx, ly))
2063 if (element == EL_EMPTY)
2064 element = GfxElementEmpty[lx][ly];
2066 SetRandomAnimationValue(lx, ly);
2068 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2069 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2071 // do not use double (EM style) movement graphic when not moving
2072 if (graphic_info[graphic].double_movement && !dx && !dy)
2074 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2075 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2078 if (game.use_masked_elements && (dx || dy))
2079 mask_mode = USE_MASKING;
2081 else // border element
2083 graphic = el2img(element);
2084 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2087 if (element == EL_EXPANDABLE_WALL)
2089 boolean left_stopped = FALSE, right_stopped = FALSE;
2091 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2092 left_stopped = TRUE;
2093 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2094 right_stopped = TRUE;
2096 if (left_stopped && right_stopped)
2098 else if (left_stopped)
2100 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2101 frame = graphic_info[graphic].anim_frames - 1;
2103 else if (right_stopped)
2105 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2106 frame = graphic_info[graphic].anim_frames - 1;
2111 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2112 else if (mask_mode == USE_MASKING)
2113 DrawGraphicThruMask(x, y, graphic, frame);
2115 DrawGraphic(x, y, graphic, frame);
2118 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2119 int cut_mode, int mask_mode)
2121 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2122 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2123 cut_mode, mask_mode);
2126 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2129 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2132 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2135 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2138 void DrawLevelElementThruMask(int x, int y, int element)
2140 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2143 void DrawLevelFieldThruMask(int x, int y)
2145 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2148 // !!! implementation of quicksand is totally broken !!!
2149 #define IS_CRUMBLED_TILE(x, y, e) \
2150 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2151 !IS_MOVING(x, y) || \
2152 (e) == EL_QUICKSAND_EMPTYING || \
2153 (e) == EL_QUICKSAND_FAST_EMPTYING))
2155 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2160 int width, height, cx, cy;
2161 int sx = SCREENX(x), sy = SCREENY(y);
2162 int crumbled_border_size = graphic_info[graphic].border_size;
2163 int crumbled_tile_size = graphic_info[graphic].tile_size;
2164 int crumbled_border_size_var =
2165 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2168 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2170 for (i = 1; i < 4; i++)
2172 int dxx = (i & 1 ? dx : 0);
2173 int dyy = (i & 2 ? dy : 0);
2176 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2179 // check if neighbour field is of same crumble type
2180 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2181 graphic_info[graphic].class ==
2182 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2184 // return if check prevents inner corner
2185 if (same == (dxx == dx && dyy == dy))
2189 // if we reach this point, we have an inner corner
2191 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2193 width = crumbled_border_size_var;
2194 height = crumbled_border_size_var;
2195 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2196 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2198 if (game.use_masked_elements)
2200 int graphic0 = el2img(EL_EMPTY);
2201 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2202 Bitmap *src_bitmap0;
2205 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2207 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2209 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2211 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2213 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2216 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2218 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2221 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2226 int width, height, bx, by, cx, cy;
2227 int sx = SCREENX(x), sy = SCREENY(y);
2228 int crumbled_border_size = graphic_info[graphic].border_size;
2229 int crumbled_tile_size = graphic_info[graphic].tile_size;
2230 int crumbled_border_size_var =
2231 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2232 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2235 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2237 // only needed when using masked elements
2238 int graphic0 = el2img(EL_EMPTY);
2239 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2240 Bitmap *src_bitmap0;
2243 if (game.use_masked_elements)
2244 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2246 // draw simple, sloppy, non-corner-accurate crumbled border
2248 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2249 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2250 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2251 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2253 if (game.use_masked_elements)
2255 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2257 FX + sx * TILEX_VAR + cx,
2258 FY + sy * TILEY_VAR + cy);
2260 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2262 FX + sx * TILEX_VAR + cx,
2263 FY + sy * TILEY_VAR + cy);
2266 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2268 FX + sx * TILEX_VAR + cx,
2269 FY + sy * TILEY_VAR + cy);
2271 // (remaining middle border part must be at least as big as corner part)
2272 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2273 crumbled_border_size_var >= TILESIZE_VAR / 3)
2276 // correct corners of crumbled border, if needed
2278 for (i = -1; i <= 1; i += 2)
2280 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2281 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2282 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2285 // check if neighbour field is of same crumble type
2286 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2287 graphic_info[graphic].class ==
2288 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2290 // no crumbled corner, but continued crumbled border
2292 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2293 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2294 int b1 = (i == 1 ? crumbled_border_size_var :
2295 TILESIZE_VAR - 2 * crumbled_border_size_var);
2297 width = crumbled_border_size_var;
2298 height = crumbled_border_size_var;
2300 if (dir == 1 || dir == 2)
2315 if (game.use_masked_elements)
2317 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2319 FX + sx * TILEX_VAR + cx,
2320 FY + sy * TILEY_VAR + cy);
2322 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2324 FX + sx * TILEX_VAR + cx,
2325 FY + sy * TILEY_VAR + cy);
2328 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2330 FX + sx * TILEX_VAR + cx,
2331 FY + sy * TILEY_VAR + cy);
2336 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2338 int sx = SCREENX(x), sy = SCREENY(y);
2341 struct XY *xy = xy_topdown;
2343 if (!IN_LEV_FIELD(x, y))
2346 element = TILE_GFX_ELEMENT(x, y);
2348 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2350 if (!IN_SCR_FIELD(sx, sy))
2353 // crumble field borders towards direct neighbour fields
2354 for (i = 0; i < 4; i++)
2356 int xx = x + xy[i].x;
2357 int yy = y + xy[i].y;
2359 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2362 // check if neighbour field is of same crumble type
2363 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2364 graphic_info[graphic].class ==
2365 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2368 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2371 // crumble inner field corners towards corner neighbour fields
2372 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2373 graphic_info[graphic].anim_frames == 2)
2375 for (i = 0; i < 4; i++)
2377 int dx = (i & 1 ? +1 : -1);
2378 int dy = (i & 2 ? +1 : -1);
2380 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2384 MarkTileDirty(sx, sy);
2386 else // center field is not crumbled -- crumble neighbour fields
2388 // crumble field borders of direct neighbour fields
2389 for (i = 0; i < 4; i++)
2391 int xx = x + xy[i].x;
2392 int yy = y + xy[i].y;
2393 int sxx = sx + xy[i].x;
2394 int syy = sy + xy[i].y;
2396 if (!IN_LEV_FIELD(xx, yy) ||
2397 !IN_SCR_FIELD(sxx, syy))
2400 // do not crumble fields that are being digged or snapped
2401 if (Tile[xx][yy] == EL_EMPTY ||
2402 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2405 element = TILE_GFX_ELEMENT(xx, yy);
2407 if (!IS_CRUMBLED_TILE(xx, yy, element))
2410 graphic = el_act2crm(element, ACTION_DEFAULT);
2412 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2414 MarkTileDirty(sxx, syy);
2417 // crumble inner field corners of corner neighbour fields
2418 for (i = 0; i < 4; i++)
2420 int dx = (i & 1 ? +1 : -1);
2421 int dy = (i & 2 ? +1 : -1);
2427 if (!IN_LEV_FIELD(xx, yy) ||
2428 !IN_SCR_FIELD(sxx, syy))
2431 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2434 element = TILE_GFX_ELEMENT(xx, yy);
2436 if (!IS_CRUMBLED_TILE(xx, yy, element))
2439 graphic = el_act2crm(element, ACTION_DEFAULT);
2441 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2442 graphic_info[graphic].anim_frames == 2)
2443 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2445 MarkTileDirty(sxx, syy);
2450 void DrawLevelFieldCrumbled(int x, int y)
2454 if (!IN_LEV_FIELD(x, y))
2457 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2458 GfxElement[x][y] != EL_UNDEFINED &&
2459 GFX_CRUMBLED(GfxElement[x][y]))
2461 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2466 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2468 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2471 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2474 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2475 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2476 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2477 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2478 int sx = SCREENX(x), sy = SCREENY(y);
2480 DrawScreenGraphic(sx, sy, graphic1, frame1);
2481 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2484 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2486 int sx = SCREENX(x), sy = SCREENY(y);
2487 struct XY *xy = xy_topdown;
2490 // crumble direct neighbour fields (required for field borders)
2491 for (i = 0; i < 4; i++)
2493 int xx = x + xy[i].x;
2494 int yy = y + xy[i].y;
2495 int sxx = sx + xy[i].x;
2496 int syy = sy + xy[i].y;
2498 if (!IN_LEV_FIELD(xx, yy) ||
2499 !IN_SCR_FIELD(sxx, syy) ||
2500 !GFX_CRUMBLED(Tile[xx][yy]) ||
2504 DrawLevelField(xx, yy);
2507 // crumble corner neighbour fields (required for inner field corners)
2508 for (i = 0; i < 4; i++)
2510 int dx = (i & 1 ? +1 : -1);
2511 int dy = (i & 2 ? +1 : -1);
2517 if (!IN_LEV_FIELD(xx, yy) ||
2518 !IN_SCR_FIELD(sxx, syy) ||
2519 !GFX_CRUMBLED(Tile[xx][yy]) ||
2523 int element = TILE_GFX_ELEMENT(xx, yy);
2524 int graphic = el_act2crm(element, ACTION_DEFAULT);
2526 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2527 graphic_info[graphic].anim_frames == 2)
2528 DrawLevelField(xx, yy);
2532 static int getBorderElement(int x, int y)
2536 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2537 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2538 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2539 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2540 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2541 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2542 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2544 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2545 int steel_position = (x == -1 && y == -1 ? 0 :
2546 x == lev_fieldx && y == -1 ? 1 :
2547 x == -1 && y == lev_fieldy ? 2 :
2548 x == lev_fieldx && y == lev_fieldy ? 3 :
2549 x == -1 || x == lev_fieldx ? 4 :
2550 y == -1 || y == lev_fieldy ? 5 : 6);
2552 return border[steel_position][steel_type];
2555 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2557 if (game.use_masked_elements)
2559 if (graphic != el2img(EL_EMPTY))
2560 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2562 DrawGraphicThruMask(x, y, graphic, frame);
2566 DrawGraphic(x, y, graphic, frame);
2570 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2572 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2575 void DrawScreenElement(int x, int y, int element)
2577 int mask_mode = NO_MASKING;
2579 if (game.use_masked_elements)
2581 int lx = LEVELX(x), ly = LEVELY(y);
2583 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2585 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2587 mask_mode = USE_MASKING;
2591 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2592 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2595 void DrawLevelElement(int x, int y, int element)
2597 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2598 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2601 void DrawScreenField(int x, int y)
2603 int lx = LEVELX(x), ly = LEVELY(y);
2604 int element, content;
2606 if (!IN_LEV_FIELD(lx, ly))
2608 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2611 element = getBorderElement(lx, ly);
2613 DrawScreenElement(x, y, element);
2618 element = Tile[lx][ly];
2619 content = Store[lx][ly];
2621 if (IS_MOVING(lx, ly))
2623 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2624 boolean cut_mode = NO_CUTTING;
2626 if (element == EL_QUICKSAND_EMPTYING ||
2627 element == EL_QUICKSAND_FAST_EMPTYING ||
2628 element == EL_MAGIC_WALL_EMPTYING ||
2629 element == EL_BD_MAGIC_WALL_EMPTYING ||
2630 element == EL_DC_MAGIC_WALL_EMPTYING ||
2631 element == EL_AMOEBA_DROPPING)
2632 cut_mode = CUT_ABOVE;
2633 else if (element == EL_QUICKSAND_FILLING ||
2634 element == EL_QUICKSAND_FAST_FILLING ||
2635 element == EL_MAGIC_WALL_FILLING ||
2636 element == EL_BD_MAGIC_WALL_FILLING ||
2637 element == EL_DC_MAGIC_WALL_FILLING)
2638 cut_mode = CUT_BELOW;
2640 if (cut_mode == CUT_ABOVE)
2641 DrawScreenElement(x, y, element);
2643 DrawScreenElement(x, y, EL_EMPTY);
2645 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2647 int dir = MovDir[lx][ly];
2648 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2649 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2651 if (IN_SCR_FIELD(newx, newy))
2652 DrawScreenElement(newx, newy, EL_EMPTY);
2656 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2657 else if (cut_mode == NO_CUTTING)
2658 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2661 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2663 if (cut_mode == CUT_BELOW &&
2664 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2665 DrawLevelElement(lx, ly + 1, element);
2668 if (content == EL_ACID)
2670 int dir = MovDir[lx][ly];
2671 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2672 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2674 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2676 // prevent target field from being drawn again (but without masking)
2677 // (this would happen if target field is scanned after moving element)
2678 Stop[newlx][newly] = TRUE;
2681 else if (IS_BLOCKED(lx, ly))
2686 boolean cut_mode = NO_CUTTING;
2687 int element_old, content_old;
2689 Blocked2Moving(lx, ly, &oldx, &oldy);
2692 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2693 MovDir[oldx][oldy] == MV_RIGHT);
2695 element_old = Tile[oldx][oldy];
2696 content_old = Store[oldx][oldy];
2698 if (element_old == EL_QUICKSAND_EMPTYING ||
2699 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2700 element_old == EL_MAGIC_WALL_EMPTYING ||
2701 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2702 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2703 element_old == EL_AMOEBA_DROPPING)
2704 cut_mode = CUT_ABOVE;
2706 DrawScreenElement(x, y, EL_EMPTY);
2709 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2711 else if (cut_mode == NO_CUTTING)
2712 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2715 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2718 else if (IS_DRAWABLE(element))
2719 DrawScreenElement(x, y, element);
2721 DrawScreenElement(x, y, EL_EMPTY);
2724 void DrawLevelField(int x, int y)
2726 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2727 DrawScreenField(SCREENX(x), SCREENY(y));
2728 else if (IS_MOVING(x, y))
2732 Moving2Blocked(x, y, &newx, &newy);
2733 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2734 DrawScreenField(SCREENX(newx), SCREENY(newy));
2736 else if (IS_BLOCKED(x, y))
2740 Blocked2Moving(x, y, &oldx, &oldy);
2741 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2742 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2746 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2747 int (*el2img_function)(int), boolean masked,
2748 int element_bits_draw)
2750 int element_base = map_mm_wall_element(element);
2751 int element_bits = (IS_DF_WALL(element) ?
2752 element - EL_DF_WALL_START :
2753 IS_MM_WALL(element) ?
2754 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2755 int graphic = el2img_function(element_base);
2756 int tilesize_draw = tilesize / 2;
2761 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2763 for (i = 0; i < 4; i++)
2765 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2766 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2768 if (!(element_bits_draw & (1 << i)))
2771 if (element_bits & (1 << i))
2774 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2775 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2777 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2778 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2783 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2784 tilesize_draw, tilesize_draw);
2789 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2790 boolean masked, int element_bits_draw)
2792 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2793 element, tilesize, el2edimg, masked, element_bits_draw);
2796 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2797 int (*el2img_function)(int))
2799 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2803 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2806 if (IS_MM_WALL(element))
2808 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2809 element, tilesize, el2edimg, masked, 0x000f);
2813 int graphic = el2edimg(element);
2816 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2818 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2822 void DrawSizedElement(int x, int y, int element, int tilesize)
2824 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2827 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2829 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2832 void DrawMiniElement(int x, int y, int element)
2836 graphic = el2edimg(element);
2837 DrawMiniGraphic(x, y, graphic);
2840 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2843 int x = sx + scroll_x, y = sy + scroll_y;
2845 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2846 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2847 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2848 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2850 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2853 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2855 int x = sx + scroll_x, y = sy + scroll_y;
2857 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2858 DrawMiniElement(sx, sy, EL_EMPTY);
2859 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2860 DrawMiniElement(sx, sy, Tile[x][y]);
2862 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2865 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2866 int x, int y, int xsize, int ysize,
2867 int tile_width, int tile_height)
2871 int dst_x = startx + x * tile_width;
2872 int dst_y = starty + y * tile_height;
2873 int width = graphic_info[graphic].width;
2874 int height = graphic_info[graphic].height;
2875 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2876 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2877 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2878 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2879 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2880 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2881 boolean draw_masked = graphic_info[graphic].draw_masked;
2883 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2885 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2887 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2891 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2892 inner_sx + (x - 1) * tile_width % inner_width);
2893 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2894 inner_sy + (y - 1) * tile_height % inner_height);
2897 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2900 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2904 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2905 int x, int y, int xsize, int ysize,
2908 int font_width = getFontWidth(font_nr);
2909 int font_height = getFontHeight(font_nr);
2911 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2912 font_width, font_height);
2915 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2917 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2918 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2919 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2920 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2921 boolean no_delay = (tape.warp_forward);
2922 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2923 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2924 DelayCounter anim_delay = { anim_delay_value };
2925 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2926 int font_width = getFontWidth(font_nr);
2927 int font_height = getFontHeight(font_nr);
2928 int max_xsize = level.envelope[envelope_nr].xsize;
2929 int max_ysize = level.envelope[envelope_nr].ysize;
2930 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2931 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2932 int xend = max_xsize;
2933 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2934 int xstep = (xstart < xend ? 1 : 0);
2935 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2937 int end = MAX(xend - xstart, yend - ystart);
2940 for (i = start; i <= end; i++)
2942 int last_frame = end; // last frame of this "for" loop
2943 int x = xstart + i * xstep;
2944 int y = ystart + i * ystep;
2945 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2946 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2947 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2948 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2951 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2953 BlitScreenToBitmap(backbuffer);
2955 SetDrawtoField(DRAW_TO_BACKBUFFER);
2957 for (yy = 0; yy < ysize; yy++)
2958 for (xx = 0; xx < xsize; xx++)
2959 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2961 DrawTextArea(sx + font_width, sy + font_height,
2962 level.envelope[envelope_nr].text, font_nr, max_xsize,
2963 xsize - 2, ysize - 2, 0, mask_mode,
2964 level.envelope[envelope_nr].autowrap,
2965 level.envelope[envelope_nr].centered, FALSE);
2967 redraw_mask |= REDRAW_FIELD;
2970 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2973 ClearAutoRepeatKeyEvents();
2976 void ShowEnvelope(int envelope_nr)
2978 int element = EL_ENVELOPE_1 + envelope_nr;
2979 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2980 int sound_opening = element_info[element].sound[ACTION_OPENING];
2981 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2982 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2983 boolean no_delay = (tape.warp_forward);
2984 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2985 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2986 int anim_mode = graphic_info[graphic].anim_mode;
2987 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2988 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2989 boolean overlay_enabled = GetOverlayEnabled();
2991 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2993 SetOverlayEnabled(FALSE);
2996 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2998 if (anim_mode == ANIM_DEFAULT)
2999 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
3001 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
3004 Delay_WithScreenUpdates(wait_delay_value);
3006 WaitForEventToContinue();
3009 SetOverlayEnabled(overlay_enabled);
3011 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3013 if (anim_mode != ANIM_NONE)
3014 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3016 if (anim_mode == ANIM_DEFAULT)
3017 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3019 game.envelope_active = FALSE;
3021 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3023 redraw_mask |= REDRAW_FIELD;
3027 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3028 int xsize, int ysize)
3030 if (!global.use_envelope_request)
3033 if (request.bitmap == NULL ||
3034 xsize > request.xsize ||
3035 ysize > request.ysize)
3037 if (request.bitmap != NULL)
3038 FreeBitmap(request.bitmap);
3040 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3042 SDL_Surface *surface = request.bitmap->surface;
3044 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3045 Fail("SDLGetNativeSurface() failed");
3048 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3050 // create masked surface for request bitmap, if needed
3051 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3053 SDL_Surface *surface = request.bitmap->surface;
3054 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3056 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3057 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3058 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3061 SDLFreeBitmapTextures(request.bitmap);
3062 SDLCreateBitmapTextures(request.bitmap);
3064 ResetBitmapAlpha(request.bitmap);
3066 // set envelope request run-time values
3069 request.xsize = xsize;
3070 request.ysize = ysize;
3073 void DrawEnvelopeRequestToScreen(int drawing_target)
3075 if (global.use_envelope_request &&
3076 game.request_active &&
3077 drawing_target == DRAW_TO_SCREEN)
3079 struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST];
3081 SetBitmapAlphaNextBlit(request.bitmap, g->alpha);
3084 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3085 request.sx, request.sy);
3087 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3088 request.sx, request.sy);
3092 static void setRequestBasePosition(int *x, int *y)
3094 int sx_base, sy_base;
3096 if (request.x != -1)
3097 sx_base = request.x;
3098 else if (request.align == ALIGN_LEFT)
3100 else if (request.align == ALIGN_RIGHT)
3101 sx_base = SX + SXSIZE;
3103 sx_base = SX + SXSIZE / 2;
3105 if (request.y != -1)
3106 sy_base = request.y;
3107 else if (request.valign == VALIGN_TOP)
3109 else if (request.valign == VALIGN_BOTTOM)
3110 sy_base = SY + SYSIZE;
3112 sy_base = SY + SYSIZE / 2;
3118 static void setRequestPositionExt(int *x, int *y, int width, int height,
3119 boolean add_border_size)
3121 int border_size = request.border_size;
3122 int sx_base, sy_base;
3125 setRequestBasePosition(&sx_base, &sy_base);
3127 if (request.align == ALIGN_LEFT)
3129 else if (request.align == ALIGN_RIGHT)
3130 sx = sx_base - width;
3132 sx = sx_base - width / 2;
3134 if (request.valign == VALIGN_TOP)
3136 else if (request.valign == VALIGN_BOTTOM)
3137 sy = sy_base - height;
3139 sy = sy_base - height / 2;
3141 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3142 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3144 if (add_border_size)
3154 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3156 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3159 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3161 char *text_final = text;
3162 char *text_door_style = NULL;
3163 int graphic = IMG_BACKGROUND_REQUEST;
3164 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3165 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3166 int font_nr = FONT_REQUEST;
3167 int font_width = getFontWidth(font_nr);
3168 int font_height = getFontHeight(font_nr);
3169 int border_size = request.border_size;
3170 int line_spacing = request.line_spacing;
3171 int line_height = font_height + line_spacing;
3172 int max_text_width = request.width - 2 * border_size;
3173 int max_text_height = request.height - 2 * border_size;
3174 int line_length = max_text_width / font_width;
3175 int max_lines = max_text_height / line_height;
3176 int text_width = line_length * font_width;
3177 int sx_offset = border_size;
3178 int sy_offset = border_size;
3180 // force DOOR font inside door area
3181 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3183 if (request.centered)
3184 sx_offset = (request.width - text_width) / 2;
3186 if (request.wrap_single_words && !request.autowrap)
3188 char *src_text_ptr, *dst_text_ptr;
3190 if (maxWordLengthInRequestString(text) > line_length)
3192 font_nr = FONT_REQUEST_NARROW;
3193 font_width = getFontWidth(font_nr);
3194 line_length = max_text_width / font_width;
3197 text_door_style = checked_malloc(2 * strlen(text) + 1);
3199 src_text_ptr = text;
3200 dst_text_ptr = text_door_style;
3202 while (*src_text_ptr)
3204 if (*src_text_ptr == ' ' ||
3205 *src_text_ptr == '?' ||
3206 *src_text_ptr == '!')
3207 *dst_text_ptr++ = '\n';
3209 if (*src_text_ptr != ' ')
3210 *dst_text_ptr++ = *src_text_ptr;
3215 *dst_text_ptr = '\0';
3217 text_final = text_door_style;
3220 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3221 line_length, -1, max_lines, line_spacing, mask_mode,
3222 request.autowrap, request.centered, FALSE);
3224 if (text_door_style)
3225 free(text_door_style);
3230 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3232 DrawBuffer *drawto_last = drawto;
3233 int graphic = IMG_BACKGROUND_REQUEST;
3234 int width = request.width;
3235 int height = request.height;
3236 int tile_size = MAX(request.step_offset, 1);
3237 int x_steps = width / tile_size;
3238 int y_steps = height / tile_size;
3242 setRequestPosition(&sx, &sy, FALSE);
3244 // draw complete envelope request to temporary bitmap
3245 drawto = bitmap_db_store_1;
3247 ClearRectangle(drawto, sx, sy, width, height);
3249 for (y = 0; y < y_steps; y++)
3250 for (x = 0; x < x_steps; x++)
3251 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3252 x, y, x_steps, y_steps,
3253 tile_size, tile_size);
3255 // write text for request
3256 DrawEnvelopeRequestText(sx, sy, text);
3258 MapToolButtons(req_state);
3260 // restore pointer to drawing buffer
3261 drawto = drawto_last;
3263 // prepare complete envelope request from temporary bitmap
3264 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3267 static void AnimateEnvelopeRequest(int anim_mode, int action)
3269 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3270 int delay_value_normal = request.step_delay;
3271 int delay_value_fast = delay_value_normal / 2;
3272 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3273 boolean no_delay = (tape.warp_forward);
3274 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3275 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3276 DelayCounter anim_delay = { anim_delay_value };
3278 int tile_size = MAX(request.step_offset, 1);
3279 int max_xsize = request.width / tile_size;
3280 int max_ysize = request.height / tile_size;
3281 int max_xsize_inner = max_xsize - 2;
3282 int max_ysize_inner = max_ysize - 2;
3284 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3285 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3286 int xend = max_xsize_inner;
3287 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3288 int xstep = (xstart < xend ? 1 : 0);
3289 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3291 int end = MAX(xend - xstart, yend - ystart);
3294 if (setup.quick_doors)
3301 for (i = start; i <= end; i++)
3303 int last_frame = end; // last frame of this "for" loop
3304 int x = xstart + i * xstep;
3305 int y = ystart + i * ystep;
3306 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3307 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3308 int xsize_size_left = (xsize - 1) * tile_size;
3309 int ysize_size_top = (ysize - 1) * tile_size;
3310 int max_xsize_pos = (max_xsize - 1) * tile_size;
3311 int max_ysize_pos = (max_ysize - 1) * tile_size;
3312 int width = xsize * tile_size;
3313 int height = ysize * tile_size;
3319 HandleGameActions();
3321 setRequestPosition(&src_x, &src_y, FALSE);
3322 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3324 for (yy = 0; yy < 2; yy++)
3326 for (xx = 0; xx < 2; xx++)
3328 int src_xx = src_x + xx * max_xsize_pos;
3329 int src_yy = src_y + yy * max_ysize_pos;
3330 int dst_xx = dst_x + xx * xsize_size_left;
3331 int dst_yy = dst_y + yy * ysize_size_top;
3332 int xx_size = (xx ? tile_size : xsize_size_left);
3333 int yy_size = (yy ? tile_size : ysize_size_top);
3335 // draw partial (animated) envelope request to temporary bitmap
3336 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3337 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3341 // prepare partial (animated) envelope request from temporary bitmap
3342 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3345 redraw_mask |= REDRAW_FIELD;
3349 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3352 ClearAutoRepeatKeyEvents();
3355 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3357 int graphic = IMG_BACKGROUND_REQUEST;
3358 int sound_opening = SND_REQUEST_OPENING;
3359 int sound_closing = SND_REQUEST_CLOSING;
3360 int anim_mode_1 = request.anim_mode; // (higher priority)
3361 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3362 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3363 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3364 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3366 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3368 if (action == ACTION_OPENING)
3370 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3372 if (anim_mode == ANIM_DEFAULT)
3373 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3375 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3379 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3381 if (anim_mode != ANIM_NONE)
3382 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3384 if (anim_mode == ANIM_DEFAULT)
3385 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3388 game.envelope_active = FALSE;
3391 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3393 if (IS_MM_WALL(element))
3395 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3401 int graphic = el2preimg(element);
3403 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3404 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3409 void DrawLevel(int draw_background_mask)
3413 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3414 SetDrawBackgroundMask(draw_background_mask);
3418 for (x = BX1; x <= BX2; x++)
3419 for (y = BY1; y <= BY2; y++)
3420 DrawScreenField(x, y);
3422 redraw_mask |= REDRAW_FIELD;
3425 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3430 for (x = 0; x < size_x; x++)
3431 for (y = 0; y < size_y; y++)
3432 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3434 redraw_mask |= REDRAW_FIELD;
3437 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3441 for (x = 0; x < size_x; x++)
3442 for (y = 0; y < size_y; y++)
3443 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3445 redraw_mask |= REDRAW_FIELD;
3448 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3450 boolean show_level_border = (BorderElement != EL_EMPTY);
3451 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3452 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3453 int tile_size = preview.tile_size;
3454 int preview_width = preview.xsize * tile_size;
3455 int preview_height = preview.ysize * tile_size;
3456 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3457 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3458 int real_preview_width = real_preview_xsize * tile_size;
3459 int real_preview_height = real_preview_ysize * tile_size;
3460 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3461 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3464 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3467 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3469 dst_x += (preview_width - real_preview_width) / 2;
3470 dst_y += (preview_height - real_preview_height) / 2;
3472 for (x = 0; x < real_preview_xsize; x++)
3474 for (y = 0; y < real_preview_ysize; y++)
3476 int lx = from_x + x + (show_level_border ? -1 : 0);
3477 int ly = from_y + y + (show_level_border ? -1 : 0);
3478 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3479 getBorderElement(lx, ly));
3481 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3482 element, tile_size);
3486 redraw_mask |= REDRAW_FIELD;
3489 #define MICROLABEL_EMPTY 0
3490 #define MICROLABEL_LEVEL_NAME 1
3491 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3492 #define MICROLABEL_LEVEL_AUTHOR 3
3493 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3494 #define MICROLABEL_IMPORTED_FROM 5
3495 #define MICROLABEL_IMPORTED_BY_HEAD 6
3496 #define MICROLABEL_IMPORTED_BY 7
3498 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3500 int max_text_width = SXSIZE;
3501 int font_width = getFontWidth(font_nr);
3503 if (pos->align == ALIGN_CENTER)
3504 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3505 else if (pos->align == ALIGN_RIGHT)
3506 max_text_width = pos->x;
3508 max_text_width = SXSIZE - pos->x;
3510 return max_text_width / font_width;
3513 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3515 char label_text[MAX_OUTPUT_LINESIZE + 1];
3516 int max_len_label_text;
3517 int font_nr = pos->font;
3520 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3523 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3524 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3525 mode == MICROLABEL_IMPORTED_BY_HEAD)
3526 font_nr = pos->font_alt;
3528 max_len_label_text = getMaxTextLength(pos, font_nr);
3530 if (pos->size != -1)
3531 max_len_label_text = pos->size;
3533 for (i = 0; i < max_len_label_text; i++)
3534 label_text[i] = ' ';
3535 label_text[max_len_label_text] = '\0';
3537 if (strlen(label_text) > 0)
3538 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3541 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3542 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3543 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3544 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3545 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3546 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3547 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3548 max_len_label_text);
3549 label_text[max_len_label_text] = '\0';
3551 if (strlen(label_text) > 0)
3552 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3554 redraw_mask |= REDRAW_FIELD;
3557 static void DrawPreviewLevelLabel(int mode)
3559 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3562 static void DrawPreviewLevelInfo(int mode)
3564 if (mode == MICROLABEL_LEVEL_NAME)
3565 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3566 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3567 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3570 static void DrawPreviewLevelExt(boolean restart)
3572 static DelayCounter scroll_delay = { 0 };
3573 static DelayCounter label_delay = { 0 };
3574 static int from_x, from_y, scroll_direction;
3575 static int label_state, label_counter;
3576 boolean show_level_border = (BorderElement != EL_EMPTY);
3577 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3578 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3580 scroll_delay.value = preview.step_delay;
3581 label_delay.value = MICROLEVEL_LABEL_DELAY;
3588 if (preview.anim_mode == ANIM_CENTERED)
3590 if (level_xsize > preview.xsize)
3591 from_x = (level_xsize - preview.xsize) / 2;
3592 if (level_ysize > preview.ysize)
3593 from_y = (level_ysize - preview.ysize) / 2;
3596 from_x += preview.xoffset;
3597 from_y += preview.yoffset;
3599 scroll_direction = MV_RIGHT;
3603 DrawPreviewLevelPlayfield(from_x, from_y);
3604 DrawPreviewLevelLabel(label_state);
3606 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3607 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3609 // initialize delay counters
3610 ResetDelayCounter(&scroll_delay);
3611 ResetDelayCounter(&label_delay);
3613 if (leveldir_current->name)
3615 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3616 char label_text[MAX_OUTPUT_LINESIZE + 1];
3617 int font_nr = pos->font;
3618 int max_len_label_text = getMaxTextLength(pos, font_nr);
3620 if (pos->size != -1)
3621 max_len_label_text = pos->size;
3623 strncpy(label_text, leveldir_current->name, max_len_label_text);
3624 label_text[max_len_label_text] = '\0';
3626 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3627 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3633 // scroll preview level, if needed
3634 if (preview.anim_mode != ANIM_NONE &&
3635 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3636 DelayReached(&scroll_delay))
3638 switch (scroll_direction)
3643 from_x -= preview.step_offset;
3644 from_x = (from_x < 0 ? 0 : from_x);
3647 scroll_direction = MV_UP;
3651 if (from_x < level_xsize - preview.xsize)
3653 from_x += preview.step_offset;
3654 from_x = (from_x > level_xsize - preview.xsize ?
3655 level_xsize - preview.xsize : from_x);
3658 scroll_direction = MV_DOWN;
3664 from_y -= preview.step_offset;
3665 from_y = (from_y < 0 ? 0 : from_y);
3668 scroll_direction = MV_RIGHT;
3672 if (from_y < level_ysize - preview.ysize)
3674 from_y += preview.step_offset;
3675 from_y = (from_y > level_ysize - preview.ysize ?
3676 level_ysize - preview.ysize : from_y);
3679 scroll_direction = MV_LEFT;
3686 DrawPreviewLevelPlayfield(from_x, from_y);
3689 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3690 // redraw micro level label, if needed
3691 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3692 !strEqual(level.author, ANONYMOUS_NAME) &&
3693 !strEqual(level.author, leveldir_current->name) &&
3694 DelayReached(&label_delay))
3696 int max_label_counter = 23;
3698 if (leveldir_current->imported_from != NULL &&
3699 strlen(leveldir_current->imported_from) > 0)
3700 max_label_counter += 14;
3701 if (leveldir_current->imported_by != NULL &&
3702 strlen(leveldir_current->imported_by) > 0)
3703 max_label_counter += 14;
3705 label_counter = (label_counter + 1) % max_label_counter;
3706 label_state = (label_counter >= 0 && label_counter <= 7 ?
3707 MICROLABEL_LEVEL_NAME :
3708 label_counter >= 9 && label_counter <= 12 ?
3709 MICROLABEL_LEVEL_AUTHOR_HEAD :
3710 label_counter >= 14 && label_counter <= 21 ?
3711 MICROLABEL_LEVEL_AUTHOR :
3712 label_counter >= 23 && label_counter <= 26 ?
3713 MICROLABEL_IMPORTED_FROM_HEAD :
3714 label_counter >= 28 && label_counter <= 35 ?
3715 MICROLABEL_IMPORTED_FROM :
3716 label_counter >= 37 && label_counter <= 40 ?
3717 MICROLABEL_IMPORTED_BY_HEAD :
3718 label_counter >= 42 && label_counter <= 49 ?
3719 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3721 if (leveldir_current->imported_from == NULL &&
3722 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3723 label_state == MICROLABEL_IMPORTED_FROM))
3724 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3725 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3727 DrawPreviewLevelLabel(label_state);
3731 void DrawPreviewPlayers(void)
3733 if (game_status != GAME_MODE_MAIN)
3736 // do not draw preview players if level preview redefined, but players aren't
3737 if (preview.redefined && !menu.main.preview_players.redefined)
3740 boolean player_found[MAX_PLAYERS];
3741 int num_players = 0;
3744 for (i = 0; i < MAX_PLAYERS; i++)
3745 player_found[i] = FALSE;
3747 // check which players can be found in the level (simple approach)
3748 for (x = 0; x < lev_fieldx; x++)
3750 for (y = 0; y < lev_fieldy; y++)
3752 int element = level.field[x][y];
3754 if (IS_PLAYER_ELEMENT(element))
3756 int player_nr = GET_PLAYER_NR(element);
3758 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3760 if (!player_found[player_nr])
3763 player_found[player_nr] = TRUE;
3768 struct TextPosInfo *pos = &menu.main.preview_players;
3769 int tile_size = pos->tile_size;
3770 int border_size = pos->border_size;
3771 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3772 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3773 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3774 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3775 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3776 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3777 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3778 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3779 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3780 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3781 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3782 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3784 // clear area in which the players will be drawn
3785 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3786 max_players_width, max_players_height);
3788 if (!network.enabled && !setup.team_mode)
3791 // only draw players if level is suited for team mode
3792 if (num_players < 2)
3795 // draw all players that were found in the level
3796 for (i = 0; i < MAX_PLAYERS; i++)
3798 if (player_found[i])
3800 int graphic = el2img(EL_PLAYER_1 + i);
3802 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3804 xpos += player_xoffset;
3805 ypos += player_yoffset;
3810 void DrawPreviewLevelInitial(void)
3812 DrawPreviewLevelExt(TRUE);
3813 DrawPreviewPlayers();
3816 void DrawPreviewLevelAnimation(void)
3818 DrawPreviewLevelExt(FALSE);
3821 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3822 int border_size, int font_nr)
3824 int graphic = el2img(EL_PLAYER_1 + player_nr);
3825 int font_height = getFontHeight(font_nr);
3826 int player_height = MAX(tile_size, font_height);
3827 int xoffset_text = tile_size + border_size;
3828 int yoffset_text = (player_height - font_height) / 2;
3829 int yoffset_graphic = (player_height - tile_size) / 2;
3830 char *player_name = getNetworkPlayerName(player_nr + 1);
3832 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3834 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3837 static void DrawNetworkPlayersExt(boolean force)
3839 if (game_status != GAME_MODE_MAIN)
3842 if (!network.connected && !force)
3845 // do not draw network players if level preview redefined, but players aren't
3846 if (preview.redefined && !menu.main.network_players.redefined)
3849 int num_players = 0;
3852 for (i = 0; i < MAX_PLAYERS; i++)
3853 if (stored_player[i].connected_network)
3856 struct TextPosInfo *pos = &menu.main.network_players;
3857 int tile_size = pos->tile_size;
3858 int border_size = pos->border_size;
3859 int xoffset_text = tile_size + border_size;
3860 int font_nr = pos->font;
3861 int font_width = getFontWidth(font_nr);
3862 int font_height = getFontHeight(font_nr);
3863 int player_height = MAX(tile_size, font_height);
3864 int player_yoffset = player_height + border_size;
3865 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3866 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3867 int all_players_height = num_players * player_yoffset - border_size;
3868 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3869 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3870 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3872 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3873 max_players_width, max_players_height);
3875 // first draw local network player ...
3876 for (i = 0; i < MAX_PLAYERS; i++)
3878 if (stored_player[i].connected_network &&
3879 stored_player[i].connected_locally)
3881 char *player_name = getNetworkPlayerName(i + 1);
3882 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3883 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3885 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3887 ypos += player_yoffset;
3891 // ... then draw all other network players
3892 for (i = 0; i < MAX_PLAYERS; i++)
3894 if (stored_player[i].connected_network &&
3895 !stored_player[i].connected_locally)
3897 char *player_name = getNetworkPlayerName(i + 1);
3898 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3899 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3901 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3903 ypos += player_yoffset;
3908 void DrawNetworkPlayers(void)
3910 DrawNetworkPlayersExt(FALSE);
3913 void ClearNetworkPlayers(void)
3915 DrawNetworkPlayersExt(TRUE);
3918 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3919 int graphic, int lx, int ly,
3922 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3924 if (mask_mode == USE_MASKING)
3925 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3927 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3930 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3931 int graphic, int sync_frame, int mask_mode)
3933 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3935 if (mask_mode == USE_MASKING)
3936 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3938 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3941 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3942 int graphic, int sync_frame, int tilesize,
3945 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3947 if (mask_mode == USE_MASKING)
3948 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3950 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3953 static void DrawGraphicAnimation(int x, int y, int graphic)
3955 int lx = LEVELX(x), ly = LEVELY(y);
3956 int mask_mode = NO_MASKING;
3958 if (!IN_SCR_FIELD(x, y))
3961 if (game.use_masked_elements)
3963 if (Tile[lx][ly] != EL_EMPTY)
3965 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3967 mask_mode = USE_MASKING;
3971 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3972 graphic, lx, ly, mask_mode);
3974 MarkTileDirty(x, y);
3977 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3979 int lx = LEVELX(x), ly = LEVELY(y);
3980 int mask_mode = NO_MASKING;
3982 if (!IN_SCR_FIELD(x, y))
3985 if (game.use_masked_elements)
3987 if (Tile[lx][ly] != EL_EMPTY)
3989 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3991 mask_mode = USE_MASKING;
3995 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3996 graphic, lx, ly, mask_mode);
3998 MarkTileDirty(x, y);
4001 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4003 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4006 void DrawLevelElementAnimation(int x, int y, int element)
4008 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4010 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4013 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4015 int sx = SCREENX(x), sy = SCREENY(y);
4017 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4020 if (Tile[x][y] == EL_EMPTY)
4021 graphic = el2img(GfxElementEmpty[x][y]);
4023 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4026 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4029 DrawGraphicAnimation(sx, sy, graphic);
4032 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4033 DrawLevelFieldCrumbled(x, y);
4035 if (GFX_CRUMBLED(Tile[x][y]))
4036 DrawLevelFieldCrumbled(x, y);
4040 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4042 int sx = SCREENX(x), sy = SCREENY(y);
4045 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4048 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4050 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4053 DrawGraphicAnimation(sx, sy, graphic);
4055 if (GFX_CRUMBLED(element))
4056 DrawLevelFieldCrumbled(x, y);
4059 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4061 if (player->use_murphy)
4063 // this works only because currently only one player can be "murphy" ...
4064 static int last_horizontal_dir = MV_LEFT;
4065 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4067 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4068 last_horizontal_dir = move_dir;
4070 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4072 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4074 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4080 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4083 static boolean equalGraphics(int graphic1, int graphic2)
4085 struct GraphicInfo *g1 = &graphic_info[graphic1];
4086 struct GraphicInfo *g2 = &graphic_info[graphic2];
4088 return (g1->bitmap == g2->bitmap &&
4089 g1->src_x == g2->src_x &&
4090 g1->src_y == g2->src_y &&
4091 g1->anim_frames == g2->anim_frames &&
4092 g1->anim_delay == g2->anim_delay &&
4093 g1->anim_mode == g2->anim_mode);
4096 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4100 DRAW_PLAYER_STAGE_INIT = 0,
4101 DRAW_PLAYER_STAGE_LAST_FIELD,
4102 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4103 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4104 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4105 DRAW_PLAYER_STAGE_PLAYER,
4107 DRAW_PLAYER_STAGE_PLAYER,
4108 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4110 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4111 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4113 NUM_DRAW_PLAYER_STAGES
4116 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4118 static int static_last_player_graphic[MAX_PLAYERS];
4119 static int static_last_player_frame[MAX_PLAYERS];
4120 static boolean static_player_is_opaque[MAX_PLAYERS];
4121 static boolean draw_player[MAX_PLAYERS];
4122 int pnr = player->index_nr;
4124 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4126 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4127 static_last_player_frame[pnr] = player->Frame;
4128 static_player_is_opaque[pnr] = FALSE;
4130 draw_player[pnr] = TRUE;
4133 if (!draw_player[pnr])
4137 if (!IN_LEV_FIELD(player->jx, player->jy))
4139 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4140 Debug("draw:DrawPlayerExt", "This should never happen!");
4142 draw_player[pnr] = FALSE;
4148 int last_player_graphic = static_last_player_graphic[pnr];
4149 int last_player_frame = static_last_player_frame[pnr];
4150 boolean player_is_opaque = static_player_is_opaque[pnr];
4152 int jx = player->jx;
4153 int jy = player->jy;
4154 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4155 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4156 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4157 int last_jx = (player->is_moving ? jx - dx : jx);
4158 int last_jy = (player->is_moving ? jy - dy : jy);
4159 int next_jx = jx + dx;
4160 int next_jy = jy + dy;
4161 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4162 int sx = SCREENX(jx);
4163 int sy = SCREENY(jy);
4164 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4165 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4166 int element = Tile[jx][jy];
4167 int last_element = Tile[last_jx][last_jy];
4168 int action = (player->is_pushing ? ACTION_PUSHING :
4169 player->is_digging ? ACTION_DIGGING :
4170 player->is_collecting ? ACTION_COLLECTING :
4171 player->is_moving ? ACTION_MOVING :
4172 player->is_snapping ? ACTION_SNAPPING :
4173 player->is_dropping ? ACTION_DROPPING :
4174 player->is_waiting ? player->action_waiting :
4177 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4179 // ------------------------------------------------------------------------
4180 // initialize drawing the player
4181 // ------------------------------------------------------------------------
4183 draw_player[pnr] = FALSE;
4185 // GfxElement[][] is set to the element the player is digging or collecting;
4186 // remove also for off-screen player if the player is not moving anymore
4187 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4188 GfxElement[jx][jy] = EL_UNDEFINED;
4190 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4193 if (element == EL_EXPLOSION)
4196 InitPlayerGfxAnimation(player, action, move_dir);
4198 draw_player[pnr] = TRUE;
4200 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4202 // ------------------------------------------------------------------------
4203 // draw things in the field the player is leaving, if needed
4204 // ------------------------------------------------------------------------
4206 if (!IN_SCR_FIELD(sx, sy))
4207 draw_player[pnr] = FALSE;
4209 if (!player->is_moving)
4212 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4214 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4216 if (last_element == EL_DYNAMITE_ACTIVE ||
4217 last_element == EL_EM_DYNAMITE_ACTIVE ||
4218 last_element == EL_SP_DISK_RED_ACTIVE)
4219 DrawDynamite(last_jx, last_jy);
4221 DrawLevelFieldThruMask(last_jx, last_jy);
4223 else if (last_element == EL_DYNAMITE_ACTIVE ||
4224 last_element == EL_EM_DYNAMITE_ACTIVE ||
4225 last_element == EL_SP_DISK_RED_ACTIVE)
4226 DrawDynamite(last_jx, last_jy);
4228 DrawLevelField(last_jx, last_jy);
4230 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4232 // ------------------------------------------------------------------------
4233 // draw things behind the player, if needed
4234 // ------------------------------------------------------------------------
4238 DrawLevelElement(jx, jy, Back[jx][jy]);
4243 if (IS_ACTIVE_BOMB(element))
4245 DrawLevelElement(jx, jy, EL_EMPTY);
4250 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4252 int old_element = GfxElement[jx][jy];
4253 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4254 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4256 if (GFX_CRUMBLED(old_element))
4257 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4259 DrawScreenGraphic(sx, sy, old_graphic, frame);
4261 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4262 static_player_is_opaque[pnr] = TRUE;
4266 GfxElement[jx][jy] = EL_UNDEFINED;
4268 // make sure that pushed elements are drawn with correct frame rate
4269 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4271 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4272 GfxFrame[jx][jy] = player->StepFrame;
4274 DrawLevelField(jx, jy);
4277 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4279 // ------------------------------------------------------------------------
4280 // draw things the player is pushing, if needed
4281 // ------------------------------------------------------------------------
4283 if (!player->is_pushing || !player->is_moving)
4286 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4289 int gfx_frame = GfxFrame[jx][jy];
4291 if (!IS_MOVING(jx, jy)) // push movement already finished
4293 element = Tile[next_jx][next_jy];
4294 gfx_frame = GfxFrame[next_jx][next_jy];
4297 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4298 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4299 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4301 // draw background element under pushed element (like the Sokoban field)
4302 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4304 // this allows transparent pushing animation over non-black background
4307 DrawLevelElement(jx, jy, Back[jx][jy]);
4309 DrawLevelElement(jx, jy, EL_EMPTY);
4312 if (Back[next_jx][next_jy])
4313 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4315 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4317 int px = SCREENX(jx), py = SCREENY(jy);
4318 int pxx = (TILEX - ABS(sxx)) * dx;
4319 int pyy = (TILEY - ABS(syy)) * dy;
4322 // do not draw (EM style) pushing animation when pushing is finished
4323 // (two-tile animations usually do not contain start and end frame)
4324 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4325 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4327 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4329 // masked drawing is needed for EMC style (double) movement graphics
4330 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4331 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4334 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4336 // ------------------------------------------------------------------------
4337 // draw player himself
4338 // ------------------------------------------------------------------------
4340 int graphic = getPlayerGraphic(player, move_dir);
4342 // in the case of changed player action or direction, prevent the current
4343 // animation frame from being restarted for identical animations
4344 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4345 player->Frame = last_player_frame;
4347 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4349 if (player_is_opaque)
4350 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4352 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4354 if (SHIELD_ON(player))
4356 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4357 IMG_SHIELD_NORMAL_ACTIVE);
4358 frame = getGraphicAnimationFrame(graphic, -1);
4360 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4363 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4365 // ------------------------------------------------------------------------
4366 // draw things in front of player (active dynamite or dynabombs)
4367 // ------------------------------------------------------------------------
4369 if (IS_ACTIVE_BOMB(element))
4371 int graphic = el2img(element);
4372 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4374 if (game.emulation == EMU_SUPAPLEX)
4375 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4377 DrawGraphicThruMask(sx, sy, graphic, frame);
4380 if (player_is_moving && last_element == EL_EXPLOSION)
4382 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4383 GfxElement[last_jx][last_jy] : EL_EMPTY);
4384 int graphic = el_act2img(element, ACTION_EXPLODING);
4385 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4386 int phase = ExplodePhase[last_jx][last_jy] - 1;
4387 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4390 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4393 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4395 // ------------------------------------------------------------------------
4396 // draw elements the player is just walking/passing through/under
4397 // ------------------------------------------------------------------------
4399 if (player_is_moving)
4401 // handle the field the player is leaving ...
4402 if (IS_ACCESSIBLE_INSIDE(last_element))
4403 DrawLevelField(last_jx, last_jy);
4404 else if (IS_ACCESSIBLE_UNDER(last_element))
4405 DrawLevelFieldThruMask(last_jx, last_jy);
4408 // do not redraw accessible elements if the player is just pushing them
4409 if (!player_is_moving || !player->is_pushing)
4411 // ... and the field the player is entering
4412 if (IS_ACCESSIBLE_INSIDE(element))
4413 DrawLevelField(jx, jy);
4414 else if (IS_ACCESSIBLE_UNDER(element))
4415 DrawLevelFieldThruMask(jx, jy);
4418 MarkTileDirty(sx, sy);
4422 void DrawPlayer(struct PlayerInfo *player)
4426 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4427 DrawPlayerExt(player, i);
4430 void DrawAllPlayers(void)
4434 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4435 for (j = 0; j < MAX_PLAYERS; j++)
4436 if (stored_player[j].active)
4437 DrawPlayerExt(&stored_player[j], i);
4440 void DrawPlayerField(int x, int y)
4442 if (!IS_PLAYER(x, y))
4445 DrawPlayer(PLAYERINFO(x, y));
4448 // ----------------------------------------------------------------------------
4450 void WaitForEventToContinue(void)
4452 boolean first_wait = TRUE;
4453 boolean still_wait = TRUE;
4455 if (program.headless)
4458 // simulate releasing mouse button over last gadget, if still pressed
4460 HandleGadgets(-1, -1, 0);
4462 button_status = MB_RELEASED;
4465 ClearPlayerAction();
4471 if (NextValidEvent(&event))
4475 case EVENT_BUTTONPRESS:
4476 case EVENT_FINGERPRESS:
4480 case EVENT_BUTTONRELEASE:
4481 case EVENT_FINGERRELEASE:
4482 still_wait = first_wait;
4485 case EVENT_KEYPRESS:
4486 case SDL_CONTROLLERBUTTONDOWN:
4487 case SDL_JOYBUTTONDOWN:
4492 HandleOtherEvents(&event);
4496 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4501 if (!PendingEvent())
4506 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4508 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4509 int draw_buffer_last = GetDrawtoField();
4510 int width = request.width;
4511 int height = request.height;
4515 setRequestPosition(&sx, &sy, FALSE);
4517 button_status = MB_RELEASED;
4519 request_gadget_id = -1;
4526 SetDrawtoField(draw_buffer_game);
4528 HandleGameActions();
4530 SetDrawtoField(DRAW_TO_BACKBUFFER);
4537 while (NextValidEvent(&event))
4541 case EVENT_BUTTONPRESS:
4542 case EVENT_BUTTONRELEASE:
4543 case EVENT_MOTIONNOTIFY:
4545 DrawBuffer *drawto_last = drawto;
4548 if (event.type == EVENT_MOTIONNOTIFY)
4553 motion_status = TRUE;
4554 mx = ((MotionEvent *) &event)->x;
4555 my = ((MotionEvent *) &event)->y;
4559 motion_status = FALSE;
4560 mx = ((ButtonEvent *) &event)->x;
4561 my = ((ButtonEvent *) &event)->y;
4562 if (event.type == EVENT_BUTTONPRESS)
4563 button_status = ((ButtonEvent *) &event)->button;
4565 button_status = MB_RELEASED;
4568 if (global.use_envelope_request)
4570 // draw changed button states to temporary bitmap
4571 drawto = bitmap_db_store_1;
4574 // this sets 'request_gadget_id'
4575 HandleGadgets(mx, my, button_status);
4577 if (global.use_envelope_request)
4579 // restore pointer to drawing buffer
4580 drawto = drawto_last;
4582 // prepare complete envelope request from temporary bitmap
4583 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4587 switch (request_gadget_id)
4589 case TOOL_CTRL_ID_YES:
4590 case TOOL_CTRL_ID_TOUCH_YES:
4593 case TOOL_CTRL_ID_NO:
4594 case TOOL_CTRL_ID_TOUCH_NO:
4597 case TOOL_CTRL_ID_CONFIRM:
4598 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4599 result = TRUE | FALSE;
4602 case TOOL_CTRL_ID_PLAYER_1:
4605 case TOOL_CTRL_ID_PLAYER_2:
4608 case TOOL_CTRL_ID_PLAYER_3:
4611 case TOOL_CTRL_ID_PLAYER_4:
4619 // only needed to handle clickable pointer animations here
4620 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4625 case SDL_WINDOWEVENT:
4626 HandleWindowEvent((WindowEvent *) &event);
4629 case SDL_APP_WILLENTERBACKGROUND:
4630 case SDL_APP_DIDENTERBACKGROUND:
4631 case SDL_APP_WILLENTERFOREGROUND:
4632 case SDL_APP_DIDENTERFOREGROUND:
4633 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4636 case EVENT_KEYPRESS:
4638 Key key = GetEventKey((KeyEvent *)&event);
4643 if (req_state & REQ_CONFIRM)
4652 #if defined(KSYM_Rewind)
4653 case KSYM_Rewind: // for Amazon Fire TV remote
4662 #if defined(KSYM_FastForward)
4663 case KSYM_FastForward: // for Amazon Fire TV remote
4669 HandleKeysDebug(key, KEY_PRESSED);
4673 if (req_state & REQ_PLAYER)
4675 int old_player_nr = setup.network_player_nr;
4678 result = old_player_nr + 1;
4683 result = old_player_nr + 1;
4714 case EVENT_FINGERRELEASE:
4715 case EVENT_KEYRELEASE:
4716 ClearPlayerAction();
4719 case SDL_CONTROLLERBUTTONDOWN:
4720 switch (event.cbutton.button)
4722 case SDL_CONTROLLER_BUTTON_A:
4723 case SDL_CONTROLLER_BUTTON_X:
4724 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4725 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4729 case SDL_CONTROLLER_BUTTON_B:
4730 case SDL_CONTROLLER_BUTTON_Y:
4731 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4732 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4733 case SDL_CONTROLLER_BUTTON_BACK:
4738 if (req_state & REQ_PLAYER)
4740 int old_player_nr = setup.network_player_nr;
4743 result = old_player_nr + 1;
4745 switch (event.cbutton.button)
4747 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4748 case SDL_CONTROLLER_BUTTON_Y:
4752 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4753 case SDL_CONTROLLER_BUTTON_B:
4757 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4758 case SDL_CONTROLLER_BUTTON_A:
4762 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4763 case SDL_CONTROLLER_BUTTON_X:
4774 case SDL_CONTROLLERBUTTONUP:
4775 HandleJoystickEvent(&event);
4776 ClearPlayerAction();
4780 HandleOtherEvents(&event);
4785 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4787 int joy = AnyJoystick();
4789 if (joy & JOY_BUTTON_1)
4791 else if (joy & JOY_BUTTON_2)
4794 else if (AnyJoystick())
4796 int joy = AnyJoystick();
4798 if (req_state & REQ_PLAYER)
4802 else if (joy & JOY_RIGHT)
4804 else if (joy & JOY_DOWN)
4806 else if (joy & JOY_LEFT)
4814 SetDrawtoField(draw_buffer_last);
4819 static void DoRequestBefore(void)
4821 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4823 // when showing request dialog after game ended, deactivate game panel
4825 game.panel.active = FALSE;
4827 if (game_status == GAME_MODE_PLAYING)
4828 BlitScreenToBitmap(backbuffer);
4830 // disable deactivated drawing when quick-loading level tape recording
4831 if (tape.playing && tape.deactivate_display)
4832 TapeDeactivateDisplayOff(TRUE);
4834 SetMouseCursor(CURSOR_DEFAULT);
4836 // pause network game while waiting for request to answer
4837 if (network.enabled &&
4838 game_status == GAME_MODE_PLAYING &&
4839 !game.all_players_gone)
4840 SendToServer_PausePlaying();
4842 // simulate releasing mouse button over last gadget, if still pressed
4844 HandleGadgets(-1, -1, 0);
4849 static void DoRequestAfter(void)
4853 if (game_status == GAME_MODE_PLAYING)
4855 SetPanelBackground();
4856 SetDrawBackgroundMask(REDRAW_DOOR_1);
4860 SetDrawBackgroundMask(REDRAW_FIELD);
4863 // continue network game after request
4864 if (network.enabled &&
4865 game_status == GAME_MODE_PLAYING &&
4866 !game.all_players_gone)
4867 SendToServer_ContinuePlaying();
4869 // restore deactivated drawing when quick-loading level tape recording
4870 if (tape.playing && tape.deactivate_display)
4871 TapeDeactivateDisplayOn();
4874 static void setRequestDoorTextProperties(char *text,
4879 int *set_max_line_length)
4881 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4882 struct TextPosInfo *pos = &request.button.confirm;
4883 int button_ypos = pos->y;
4884 int font_nr = FONT_TEXT_2;
4885 int font_width = getFontWidth(font_nr);
4886 int font_height = getFontHeight(font_nr);
4887 int line_height = font_height + line_spacing;
4888 int max_text_width = vp_door_1->width;
4889 int max_text_height = button_ypos - 2 * text_spacing;
4890 int max_line_length = max_text_width / font_width;
4891 int max_lines = max_text_height / line_height;
4893 if (maxWordLengthInRequestString(text) > max_line_length)
4895 font_nr = FONT_TEXT_1;
4896 font_width = getFontWidth(font_nr);
4897 max_line_length = max_text_width / font_width;
4900 *set_font_nr = font_nr;
4901 *set_max_lines = max_lines;
4902 *set_max_line_length = max_line_length;
4905 static void DrawRequestDoorText(char *text)
4907 char *text_ptr = text;
4908 int text_spacing = 8;
4909 int line_spacing = 2;
4910 int max_request_lines;
4911 int max_request_line_len;
4915 // force DOOR font inside door area
4916 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4918 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4919 &max_request_lines, &max_request_line_len);
4921 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4923 char text_line[max_request_line_len + 1];
4929 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4931 tc = *(text_ptr + tx);
4932 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4936 if ((tc == '?' || tc == '!') && tl == 0)
4946 strncpy(text_line, text_ptr, tl);
4949 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4950 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4951 text_line, font_nr);
4953 text_ptr += tl + (tc == ' ' ? 1 : 0);
4959 static int RequestDoor(char *text, unsigned int req_state)
4961 unsigned int old_door_state = GetDoorState();
4962 int draw_buffer_last = GetDrawtoField();
4965 if (old_door_state & DOOR_OPEN_1)
4967 CloseDoor(DOOR_CLOSE_1);
4969 // save old door content
4970 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4971 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4974 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4975 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4977 // clear door drawing field
4978 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4980 // write text for request
4981 DrawRequestDoorText(text);
4983 MapToolButtons(req_state);
4985 // copy request gadgets to door backbuffer
4986 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4988 OpenDoor(DOOR_OPEN_1);
4990 // ---------- handle request buttons ----------
4991 result = RequestHandleEvents(req_state, draw_buffer_last);
4995 if (!(req_state & REQ_STAY_OPEN))
4997 CloseDoor(DOOR_CLOSE_1);
4999 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5000 (req_state & REQ_REOPEN))
5001 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5007 static int RequestEnvelope(char *text, unsigned int req_state)
5009 int draw_buffer_last = GetDrawtoField();
5012 DrawEnvelopeRequest(text, req_state);
5013 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5015 // ---------- handle request buttons ----------
5016 result = RequestHandleEvents(req_state, draw_buffer_last);
5020 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5025 int Request(char *text, unsigned int req_state)
5027 boolean overlay_enabled = GetOverlayEnabled();
5030 game.request_active = TRUE;
5032 SetOverlayEnabled(FALSE);
5036 if (global.use_envelope_request)
5037 result = RequestEnvelope(text, req_state);
5039 result = RequestDoor(text, req_state);
5043 SetOverlayEnabled(overlay_enabled);
5045 game.request_active = FALSE;
5050 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5052 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5053 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5056 if (dpo1->sort_priority != dpo2->sort_priority)
5057 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5059 compare_result = dpo1->nr - dpo2->nr;
5061 return compare_result;
5064 void InitGraphicCompatibilityInfo_Doors(void)
5070 struct DoorInfo *door;
5074 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5075 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5077 { -1, -1, -1, NULL }
5079 struct Rect door_rect_list[] =
5081 { DX, DY, DXSIZE, DYSIZE },
5082 { VX, VY, VXSIZE, VYSIZE }
5086 for (i = 0; doors[i].door_token != -1; i++)
5088 int door_token = doors[i].door_token;
5089 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5090 int part_1 = doors[i].part_1;
5091 int part_8 = doors[i].part_8;
5092 int part_2 = part_1 + 1;
5093 int part_3 = part_1 + 2;
5094 struct DoorInfo *door = doors[i].door;
5095 struct Rect *door_rect = &door_rect_list[door_index];
5096 boolean door_gfx_redefined = FALSE;
5098 // check if any door part graphic definitions have been redefined
5100 for (j = 0; door_part_controls[j].door_token != -1; j++)
5102 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5103 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5105 if (dpc->door_token == door_token && fi->redefined)
5106 door_gfx_redefined = TRUE;
5109 // check for old-style door graphic/animation modifications
5111 if (!door_gfx_redefined)
5113 if (door->anim_mode & ANIM_STATIC_PANEL)
5115 door->panel.step_xoffset = 0;
5116 door->panel.step_yoffset = 0;
5119 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5121 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5122 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5123 int num_door_steps, num_panel_steps;
5125 // remove door part graphics other than the two default wings
5127 for (j = 0; door_part_controls[j].door_token != -1; j++)
5129 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5130 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5132 if (dpc->graphic >= part_3 &&
5133 dpc->graphic <= part_8)
5137 // set graphics and screen positions of the default wings
5139 g_part_1->width = door_rect->width;
5140 g_part_1->height = door_rect->height;
5141 g_part_2->width = door_rect->width;
5142 g_part_2->height = door_rect->height;
5143 g_part_2->src_x = door_rect->width;
5144 g_part_2->src_y = g_part_1->src_y;
5146 door->part_2.x = door->part_1.x;
5147 door->part_2.y = door->part_1.y;
5149 if (door->width != -1)
5151 g_part_1->width = door->width;
5152 g_part_2->width = door->width;
5154 // special treatment for graphics and screen position of right wing
5155 g_part_2->src_x += door_rect->width - door->width;
5156 door->part_2.x += door_rect->width - door->width;
5159 if (door->height != -1)
5161 g_part_1->height = door->height;
5162 g_part_2->height = door->height;
5164 // special treatment for graphics and screen position of bottom wing
5165 g_part_2->src_y += door_rect->height - door->height;
5166 door->part_2.y += door_rect->height - door->height;
5169 // set animation delays for the default wings and panels
5171 door->part_1.step_delay = door->step_delay;
5172 door->part_2.step_delay = door->step_delay;
5173 door->panel.step_delay = door->step_delay;
5175 // set animation draw order for the default wings
5177 door->part_1.sort_priority = 2; // draw left wing over ...
5178 door->part_2.sort_priority = 1; // ... right wing
5180 // set animation draw offset for the default wings
5182 if (door->anim_mode & ANIM_HORIZONTAL)
5184 door->part_1.step_xoffset = door->step_offset;
5185 door->part_1.step_yoffset = 0;
5186 door->part_2.step_xoffset = door->step_offset * -1;
5187 door->part_2.step_yoffset = 0;
5189 num_door_steps = g_part_1->width / door->step_offset;
5191 else // ANIM_VERTICAL
5193 door->part_1.step_xoffset = 0;
5194 door->part_1.step_yoffset = door->step_offset;
5195 door->part_2.step_xoffset = 0;
5196 door->part_2.step_yoffset = door->step_offset * -1;
5198 num_door_steps = g_part_1->height / door->step_offset;
5201 // set animation draw offset for the default panels
5203 if (door->step_offset > 1)
5205 num_panel_steps = 2 * door_rect->height / door->step_offset;
5206 door->panel.start_step = num_panel_steps - num_door_steps;
5207 door->panel.start_step_closing = door->panel.start_step;
5211 num_panel_steps = door_rect->height / door->step_offset;
5212 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5213 door->panel.start_step_closing = door->panel.start_step;
5214 door->panel.step_delay *= 2;
5221 void InitDoors(void)
5225 for (i = 0; door_part_controls[i].door_token != -1; i++)
5227 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5228 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5230 // initialize "start_step_opening" and "start_step_closing", if needed
5231 if (dpc->pos->start_step_opening == 0 &&
5232 dpc->pos->start_step_closing == 0)
5234 // dpc->pos->start_step_opening = dpc->pos->start_step;
5235 dpc->pos->start_step_closing = dpc->pos->start_step;
5238 // fill structure for door part draw order (sorted below)
5240 dpo->sort_priority = dpc->pos->sort_priority;
5243 // sort door part controls according to sort_priority and graphic number
5244 qsort(door_part_order, MAX_DOOR_PARTS,
5245 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5248 unsigned int OpenDoor(unsigned int door_state)
5250 if (door_state & DOOR_COPY_BACK)
5252 if (door_state & DOOR_OPEN_1)
5253 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5254 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5256 if (door_state & DOOR_OPEN_2)
5257 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5258 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5260 door_state &= ~DOOR_COPY_BACK;
5263 return MoveDoor(door_state);
5266 unsigned int CloseDoor(unsigned int door_state)
5268 unsigned int old_door_state = GetDoorState();
5270 if (!(door_state & DOOR_NO_COPY_BACK))
5272 if (old_door_state & DOOR_OPEN_1)
5273 BlitBitmap(backbuffer, bitmap_db_door_1,
5274 DX, DY, DXSIZE, DYSIZE, 0, 0);
5276 if (old_door_state & DOOR_OPEN_2)
5277 BlitBitmap(backbuffer, bitmap_db_door_2,
5278 VX, VY, VXSIZE, VYSIZE, 0, 0);
5280 door_state &= ~DOOR_NO_COPY_BACK;
5283 return MoveDoor(door_state);
5286 unsigned int GetDoorState(void)
5288 return MoveDoor(DOOR_GET_STATE);
5291 unsigned int SetDoorState(unsigned int door_state)
5293 return MoveDoor(door_state | DOOR_SET_STATE);
5296 static int euclid(int a, int b)
5298 return (b ? euclid(b, a % b) : a);
5301 unsigned int MoveDoor(unsigned int door_state)
5303 struct Rect door_rect_list[] =
5305 { DX, DY, DXSIZE, DYSIZE },
5306 { VX, VY, VXSIZE, VYSIZE }
5308 static int door1 = DOOR_CLOSE_1;
5309 static int door2 = DOOR_CLOSE_2;
5310 DelayCounter door_delay = { 0 };
5313 if (door_state == DOOR_GET_STATE)
5314 return (door1 | door2);
5316 if (door_state & DOOR_SET_STATE)
5318 if (door_state & DOOR_ACTION_1)
5319 door1 = door_state & DOOR_ACTION_1;
5320 if (door_state & DOOR_ACTION_2)
5321 door2 = door_state & DOOR_ACTION_2;
5323 return (door1 | door2);
5326 if (!(door_state & DOOR_FORCE_REDRAW))
5328 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5329 door_state &= ~DOOR_OPEN_1;
5330 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5331 door_state &= ~DOOR_CLOSE_1;
5332 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5333 door_state &= ~DOOR_OPEN_2;
5334 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5335 door_state &= ~DOOR_CLOSE_2;
5338 if (global.autoplay_leveldir)
5340 door_state |= DOOR_NO_DELAY;
5341 door_state &= ~DOOR_CLOSE_ALL;
5344 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5345 door_state |= DOOR_NO_DELAY;
5347 if (door_state & DOOR_ACTION)
5349 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5350 boolean door_panel_drawn[NUM_DOORS];
5351 boolean panel_has_doors[NUM_DOORS];
5352 boolean door_part_skip[MAX_DOOR_PARTS];
5353 boolean door_part_done[MAX_DOOR_PARTS];
5354 boolean door_part_done_all;
5355 int num_steps[MAX_DOOR_PARTS];
5356 int max_move_delay = 0; // delay for complete animations of all doors
5357 int max_step_delay = 0; // delay (ms) between two animation frames
5358 int num_move_steps = 0; // number of animation steps for all doors
5359 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5360 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5364 for (i = 0; i < NUM_DOORS; i++)
5365 panel_has_doors[i] = FALSE;
5367 for (i = 0; i < MAX_DOOR_PARTS; i++)
5369 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5370 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5371 int door_token = dpc->door_token;
5373 door_part_done[i] = FALSE;
5374 door_part_skip[i] = (!(door_state & door_token) ||
5378 for (i = 0; i < MAX_DOOR_PARTS; i++)
5380 int nr = door_part_order[i].nr;
5381 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5382 struct DoorPartPosInfo *pos = dpc->pos;
5383 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5384 int door_token = dpc->door_token;
5385 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5386 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5387 int step_xoffset = ABS(pos->step_xoffset);
5388 int step_yoffset = ABS(pos->step_yoffset);
5389 int step_delay = pos->step_delay;
5390 int current_door_state = door_state & door_token;
5391 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5392 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5393 boolean part_opening = (is_panel ? door_closing : door_opening);
5394 int start_step = (part_opening ? pos->start_step_opening :
5395 pos->start_step_closing);
5396 float move_xsize = (step_xoffset ? g->width : 0);
5397 float move_ysize = (step_yoffset ? g->height : 0);
5398 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5399 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5400 int move_steps = (move_xsteps && move_ysteps ?
5401 MIN(move_xsteps, move_ysteps) :
5402 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5403 int move_delay = move_steps * step_delay;
5405 if (door_part_skip[nr])
5408 max_move_delay = MAX(max_move_delay, move_delay);
5409 max_step_delay = (max_step_delay == 0 ? step_delay :
5410 euclid(max_step_delay, step_delay));
5411 num_steps[nr] = move_steps;
5415 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5417 panel_has_doors[door_index] = TRUE;
5421 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5423 num_move_steps = max_move_delay / max_step_delay;
5424 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5426 door_delay.value = max_step_delay;
5428 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5430 start = num_move_steps - 1;
5434 // opening door sound has priority over simultaneously closing door
5435 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5437 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5439 if (door_state & DOOR_OPEN_1)
5440 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5441 if (door_state & DOOR_OPEN_2)
5442 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5444 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5446 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5448 if (door_state & DOOR_CLOSE_1)
5449 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5450 if (door_state & DOOR_CLOSE_2)
5451 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5455 for (k = start; k < num_move_steps; k++)
5457 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5459 door_part_done_all = TRUE;
5461 for (i = 0; i < NUM_DOORS; i++)
5462 door_panel_drawn[i] = FALSE;
5464 for (i = 0; i < MAX_DOOR_PARTS; i++)
5466 int nr = door_part_order[i].nr;
5467 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5468 struct DoorPartPosInfo *pos = dpc->pos;
5469 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5470 int door_token = dpc->door_token;
5471 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5472 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5473 boolean is_panel_and_door_has_closed = FALSE;
5474 struct Rect *door_rect = &door_rect_list[door_index];
5475 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5477 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5478 int current_door_state = door_state & door_token;
5479 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5480 boolean door_closing = !door_opening;
5481 boolean part_opening = (is_panel ? door_closing : door_opening);
5482 boolean part_closing = !part_opening;
5483 int start_step = (part_opening ? pos->start_step_opening :
5484 pos->start_step_closing);
5485 int step_delay = pos->step_delay;
5486 int step_factor = step_delay / max_step_delay;
5487 int k1 = (step_factor ? k / step_factor + 1 : k);
5488 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5489 int kk = MAX(0, k2);
5492 int src_x, src_y, src_xx, src_yy;
5493 int dst_x, dst_y, dst_xx, dst_yy;
5496 if (door_part_skip[nr])
5499 if (!(door_state & door_token))
5507 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5508 int kk_door = MAX(0, k2_door);
5509 int sync_frame = kk_door * door_delay.value;
5510 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5512 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5513 &g_src_x, &g_src_y);
5518 if (!door_panel_drawn[door_index])
5520 ClearRectangle(drawto, door_rect->x, door_rect->y,
5521 door_rect->width, door_rect->height);
5523 door_panel_drawn[door_index] = TRUE;
5526 // draw opening or closing door parts
5528 if (pos->step_xoffset < 0) // door part on right side
5531 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5534 if (dst_xx + width > door_rect->width)
5535 width = door_rect->width - dst_xx;
5537 else // door part on left side
5540 dst_xx = pos->x - kk * pos->step_xoffset;
5544 src_xx = ABS(dst_xx);
5548 width = g->width - src_xx;
5550 if (width > door_rect->width)
5551 width = door_rect->width;
5553 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5556 if (pos->step_yoffset < 0) // door part on bottom side
5559 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5562 if (dst_yy + height > door_rect->height)
5563 height = door_rect->height - dst_yy;
5565 else // door part on top side
5568 dst_yy = pos->y - kk * pos->step_yoffset;
5572 src_yy = ABS(dst_yy);
5576 height = g->height - src_yy;
5579 src_x = g_src_x + src_xx;
5580 src_y = g_src_y + src_yy;
5582 dst_x = door_rect->x + dst_xx;
5583 dst_y = door_rect->y + dst_yy;
5585 is_panel_and_door_has_closed =
5588 panel_has_doors[door_index] &&
5589 k >= num_move_steps_doors_only - 1);
5591 if (width >= 0 && width <= g->width &&
5592 height >= 0 && height <= g->height &&
5593 !is_panel_and_door_has_closed)
5595 if (is_panel || !pos->draw_masked)
5596 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5599 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5603 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5605 if ((part_opening && (width < 0 || height < 0)) ||
5606 (part_closing && (width >= g->width && height >= g->height)))
5607 door_part_done[nr] = TRUE;
5609 // continue door part animations, but not panel after door has closed
5610 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5611 door_part_done_all = FALSE;
5614 if (!(door_state & DOOR_NO_DELAY))
5617 HandleGameActions();
5621 SkipUntilDelayReached(&door_delay, &k, last_frame);
5623 // prevent OS (Windows) from complaining about program not responding
5627 if (door_part_done_all)
5631 if (!(door_state & DOOR_NO_DELAY))
5633 // wait for specified door action post delay
5634 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5635 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5636 else if (door_state & DOOR_ACTION_1)
5637 door_delay.value = door_1.post_delay;
5638 else if (door_state & DOOR_ACTION_2)
5639 door_delay.value = door_2.post_delay;
5641 while (!DelayReached(&door_delay))
5644 HandleGameActions();
5651 if (door_state & DOOR_ACTION_1)
5652 door1 = door_state & DOOR_ACTION_1;
5653 if (door_state & DOOR_ACTION_2)
5654 door2 = door_state & DOOR_ACTION_2;
5656 // draw masked border over door area
5657 DrawMaskedBorder(REDRAW_DOOR_1);
5658 DrawMaskedBorder(REDRAW_DOOR_2);
5660 ClearAutoRepeatKeyEvents();
5662 return (door1 | door2);
5665 static boolean useSpecialEditorDoor(void)
5667 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5668 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5670 // do not draw special editor door if editor border defined or redefined
5671 if (graphic_info[graphic].bitmap != NULL || redefined)
5674 // do not draw special editor door if global border defined to be empty
5675 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5678 // do not draw special editor door if viewport definitions do not match
5682 EY + EYSIZE != VY + VYSIZE)
5688 void DrawSpecialEditorDoor(void)
5690 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5691 int top_border_width = gfx1->width;
5692 int top_border_height = gfx1->height;
5693 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5694 int ex = EX - outer_border;
5695 int ey = EY - outer_border;
5696 int vy = VY - outer_border;
5697 int exsize = EXSIZE + 2 * outer_border;
5699 if (!useSpecialEditorDoor())
5702 // draw bigger level editor toolbox window
5703 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5704 top_border_width, top_border_height, ex, ey - top_border_height);
5705 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5706 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5708 redraw_mask |= REDRAW_ALL;
5711 void UndrawSpecialEditorDoor(void)
5713 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5714 int top_border_width = gfx1->width;
5715 int top_border_height = gfx1->height;
5716 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5717 int ex = EX - outer_border;
5718 int ey = EY - outer_border;
5719 int ey_top = ey - top_border_height;
5720 int exsize = EXSIZE + 2 * outer_border;
5721 int eysize = EYSIZE + 2 * outer_border;
5723 if (!useSpecialEditorDoor())
5726 // draw normal tape recorder window
5727 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5729 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5730 ex, ey_top, top_border_width, top_border_height,
5732 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5733 ex, ey, exsize, eysize, ex, ey);
5737 // if screen background is set to "[NONE]", clear editor toolbox window
5738 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5739 ClearRectangle(drawto, ex, ey, exsize, eysize);
5742 redraw_mask |= REDRAW_ALL;
5746 // ---------- new tool button stuff -------------------------------------------
5751 struct TextPosInfo *pos;
5753 boolean is_touch_button;
5755 } toolbutton_info[NUM_TOOL_BUTTONS] =
5758 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5759 TOOL_CTRL_ID_YES, FALSE, "yes"
5762 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5763 TOOL_CTRL_ID_NO, FALSE, "no"
5766 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5767 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5770 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5771 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5774 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5775 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5778 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5779 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5782 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5783 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5786 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5787 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5790 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5791 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5794 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5795 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5799 void CreateToolButtons(void)
5803 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5805 int graphic = toolbutton_info[i].graphic;
5806 struct GraphicInfo *gfx = &graphic_info[graphic];
5807 struct TextPosInfo *pos = toolbutton_info[i].pos;
5808 struct GadgetInfo *gi;
5809 Bitmap *deco_bitmap = None;
5810 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5811 unsigned int event_mask = GD_EVENT_RELEASED;
5812 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5813 int base_x = (is_touch_button ? 0 : DX);
5814 int base_y = (is_touch_button ? 0 : DY);
5815 int gd_x = gfx->src_x;
5816 int gd_y = gfx->src_y;
5817 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5818 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5823 // do not use touch buttons if overlay touch buttons are disabled
5824 if (is_touch_button && !setup.touch.overlay_buttons)
5827 if (global.use_envelope_request && !is_touch_button)
5829 setRequestPosition(&base_x, &base_y, TRUE);
5831 // check if request buttons are outside of envelope and fix, if needed
5832 if (x < 0 || x + gfx->width > request.width ||
5833 y < 0 || y + gfx->height > request.height)
5835 if (id == TOOL_CTRL_ID_YES)
5838 y = request.height - 2 * request.border_size - gfx->height;
5840 else if (id == TOOL_CTRL_ID_NO)
5842 x = request.width - 2 * request.border_size - gfx->width;
5843 y = request.height - 2 * request.border_size - gfx->height;
5845 else if (id == TOOL_CTRL_ID_CONFIRM)
5847 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5848 y = request.height - 2 * request.border_size - gfx->height;
5850 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5852 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5854 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5855 y = request.height - 2 * request.border_size - gfx->height * 2;
5857 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5858 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5863 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5866 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5868 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5869 pos->size, &deco_bitmap, &deco_x, &deco_y);
5870 deco_xpos = (gfx->width - pos->size) / 2;
5871 deco_ypos = (gfx->height - pos->size) / 2;
5874 gi = CreateGadget(GDI_CUSTOM_ID, id,
5875 GDI_IMAGE_ID, graphic,
5876 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5879 GDI_WIDTH, gfx->width,
5880 GDI_HEIGHT, gfx->height,
5881 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5882 GDI_STATE, GD_BUTTON_UNPRESSED,
5883 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5884 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5885 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5886 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5887 GDI_DECORATION_SIZE, pos->size, pos->size,
5888 GDI_DECORATION_SHIFTING, 1, 1,
5889 GDI_DIRECT_DRAW, FALSE,
5890 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5891 GDI_EVENT_MASK, event_mask,
5892 GDI_CALLBACK_ACTION, HandleToolButtons,
5896 Fail("cannot create gadget");
5898 tool_gadget[id] = gi;
5902 void FreeToolButtons(void)
5906 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5907 FreeGadget(tool_gadget[i]);
5910 static void MapToolButtons(unsigned int req_state)
5912 if (req_state & REQ_ASK)
5914 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5915 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5916 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5917 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5919 else if (req_state & REQ_CONFIRM)
5921 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5922 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5924 else if (req_state & REQ_PLAYER)
5926 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5927 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5928 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5929 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5933 static void UnmapToolButtons(void)
5937 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5938 UnmapGadget(tool_gadget[i]);
5941 static void HandleToolButtons(struct GadgetInfo *gi)
5943 request_gadget_id = gi->custom_id;
5946 static struct Mapping_BD_to_RND_object
5949 boolean is_rnd_to_bd_mapping; // unique mapping BD <-> RND
5955 bd_object_mapping_list[] =
5957 // additional RND style elements mapped to BD style elements must be listed first
5973 EL_STEELWALL, -1, -1
5977 EL_BD_DIAMOND, -1, -1
5997 EL_EXIT_CLOSED, -1, -1
6000 // BD style elements with their corresponding RND style elements
6011 O_DIRT_SLOPED_UP_RIGHT, TRUE,
6012 EL_BD_SAND_SLOPED_UP_RIGHT, -1, -1
6015 O_DIRT_SLOPED_UP_LEFT, TRUE,
6016 EL_BD_SAND_SLOPED_UP_LEFT, -1, -1
6019 O_DIRT_SLOPED_DOWN_LEFT, TRUE,
6020 EL_BD_SAND_SLOPED_DOWN_LEFT, -1, -1
6023 O_DIRT_SLOPED_DOWN_RIGHT, TRUE,
6024 EL_BD_SAND_SLOPED_DOWN_RIGHT, -1, -1
6028 EL_BD_SAND_BALL, -1, -1
6031 O_DIRT_BALL_F, FALSE,
6032 EL_BD_SAND_BALL, ACTION_FALLING, -1
6036 EL_BD_SAND_LOOSE, -1, -1
6039 O_DIRT_LOOSE_F, FALSE,
6040 EL_BD_SAND_LOOSE, ACTION_FALLING, -1
6044 EL_BD_SAND_2, -1, -1
6051 O_BRICK_SLOPED_UP_RIGHT, TRUE,
6052 EL_BD_WALL_SLOPED_UP_RIGHT, -1, -1
6055 O_BRICK_SLOPED_UP_LEFT, TRUE,
6056 EL_BD_WALL_SLOPED_UP_LEFT, -1, -1
6059 O_BRICK_SLOPED_DOWN_LEFT, TRUE,
6060 EL_BD_WALL_SLOPED_DOWN_LEFT, -1, -1
6063 O_BRICK_SLOPED_DOWN_RIGHT, TRUE,
6064 EL_BD_WALL_SLOPED_DOWN_RIGHT, -1, -1
6067 O_BRICK_NON_SLOPED, TRUE,
6068 EL_BD_WALL_NON_SLOPED, -1, -1
6072 EL_BD_MAGIC_WALL, ACTION_ACTIVE, -1
6076 EL_BD_EXIT_CLOSED, -1, -1
6080 EL_BD_EXIT_OPEN, -1, -1
6083 O_PRE_INVIS_OUTBOX, TRUE,
6084 EL_BD_INVISIBLE_EXIT_CLOSED, -1, -1
6087 O_INVIS_OUTBOX, TRUE,
6088 EL_BD_INVISIBLE_EXIT_OPEN, -1, -1
6092 EL_BD_STEELWALL, -1, -1
6095 O_STEEL_SLOPED_UP_RIGHT, TRUE,
6096 EL_BD_STEELWALL_SLOPED_UP_RIGHT, -1, -1
6099 O_STEEL_SLOPED_UP_LEFT, TRUE,
6100 EL_BD_STEELWALL_SLOPED_UP_LEFT, -1, -1
6103 O_STEEL_SLOPED_DOWN_LEFT, TRUE,
6104 EL_BD_STEELWALL_SLOPED_DOWN_LEFT, -1, -1
6107 O_STEEL_SLOPED_DOWN_RIGHT, TRUE,
6108 EL_BD_STEELWALL_SLOPED_DOWN_RIGHT, -1, -1
6111 O_STEEL_EXPLODABLE, TRUE,
6112 EL_BD_STEELWALL_EXPLODABLE, -1, -1
6115 O_STEEL_EATABLE, TRUE,
6116 EL_BD_STEELWALL_DIGGABLE, -1, -1
6119 O_BRICK_EATABLE, TRUE,
6120 EL_BD_WALL_DIGGABLE, -1, -1
6128 EL_BD_ROCK, ACTION_FALLING, -1
6131 O_FLYING_STONE, TRUE,
6132 EL_BD_FLYING_ROCK, -1, -1
6135 O_FLYING_STONE_F, FALSE,
6136 EL_BD_FLYING_ROCK, ACTION_FALLING, -1
6140 EL_BD_MEGA_ROCK, -1, -1
6143 O_MEGA_STONE_F, FALSE,
6144 EL_BD_MEGA_ROCK, ACTION_FALLING, -1
6148 EL_BD_DIAMOND, -1, -1
6152 EL_BD_DIAMOND, ACTION_FALLING, -1
6155 O_FLYING_DIAMOND, TRUE,
6156 EL_BD_FLYING_DIAMOND, -1, -1
6159 O_FLYING_DIAMOND_F, FALSE,
6160 EL_BD_FLYING_DIAMOND, ACTION_FALLING, -1
6168 EL_BD_NUT, ACTION_FALLING, -1
6171 O_BLADDER_SPENDER, TRUE,
6172 EL_BD_BLADDER_SPENDER, -1, -1
6179 O_H_EXPANDING_WALL, TRUE,
6180 EL_BD_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6183 O_V_EXPANDING_WALL, TRUE,
6184 EL_BD_EXPANDABLE_WALL_VERTICAL, -1, -1
6187 O_EXPANDING_WALL, TRUE,
6188 EL_BD_EXPANDABLE_WALL_ANY, -1, -1
6191 O_H_EXPANDING_STEEL_WALL, TRUE,
6192 EL_BD_EXPANDABLE_STEELWALL_HORIZONTAL, -1, -1
6195 O_V_EXPANDING_STEEL_WALL, TRUE,
6196 EL_BD_EXPANDABLE_STEELWALL_VERTICAL, -1, -1
6199 O_EXPANDING_STEEL_WALL, TRUE,
6200 EL_BD_EXPANDABLE_STEELWALL_ANY, -1, -1
6203 O_EXPANDING_WALL_SWITCH, TRUE,
6204 EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1
6207 O_CREATURE_SWITCH, TRUE,
6208 EL_BD_CREATURE_SWITCH, -1, -1
6211 O_BITER_SWITCH, TRUE,
6212 EL_BD_BITER_SWITCH_1, -1, -1
6215 O_REPLICATOR_SWITCH, TRUE,
6216 EL_BD_REPLICATOR_SWITCH, -1, -1
6219 O_CONVEYOR_SWITCH, TRUE,
6220 EL_BD_CONVEYOR_SWITCH, -1, -1
6223 O_CONVEYOR_DIR_SWITCH, TRUE,
6224 EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1
6231 O_FALLING_WALL, TRUE,
6232 EL_BD_FALLING_WALL, -1, -1
6235 O_FALLING_WALL_F, FALSE,
6236 EL_BD_FALLING_WALL, ACTION_FALLING, -1
6243 O_TIME_PENALTY, TRUE,
6244 EL_BD_TIME_PENALTY, -1, -1
6248 EL_BD_GRAVESTONE, -1, -1
6251 O_STONE_GLUED, TRUE,
6252 EL_BD_ROCK_GLUED, -1, -1
6255 O_DIAMOND_GLUED, TRUE,
6256 EL_BD_DIAMOND_GLUED, -1, -1
6259 O_DIAMOND_KEY, TRUE,
6260 EL_BD_DIAMOND_KEY, -1, -1
6263 O_TRAPPED_DIAMOND, TRUE,
6264 EL_BD_TRAPPED_DIAMOND, -1, -1
6272 EL_BD_SAND_GLUED, -1, -1
6288 EL_BD_GATE_1, -1, -1
6292 EL_BD_GATE_2, -1, -1
6296 EL_BD_GATE_3, -1, -1
6303 O_GRAVITY_SWITCH, TRUE,
6304 EL_BD_GRAVITY_SWITCH, -1, -1
6307 O_PNEUMATIC_HAMMER, TRUE,
6308 EL_BD_PNEUMATIC_HAMMER, -1, -1
6312 EL_BD_TELEPORTER, -1, -1
6316 EL_BD_SKELETON, -1, -1
6388 EL_BD_COW_LEFT, -1, -1
6392 EL_BD_COW_UP, -1, -1
6396 EL_BD_COW_RIGHT, -1, -1
6400 EL_BD_COW_DOWN, -1, -1
6403 O_COW_ENCLOSED_1, FALSE,
6404 EL_BD_COW_DOWN, -1, -1
6407 O_COW_ENCLOSED_2, FALSE,
6408 EL_BD_COW_DOWN, -1, -1
6411 O_COW_ENCLOSED_3, FALSE,
6412 EL_BD_COW_DOWN, -1, -1
6415 O_COW_ENCLOSED_4, FALSE,
6416 EL_BD_COW_DOWN, -1, -1
6419 O_COW_ENCLOSED_5, FALSE,
6420 EL_BD_COW_DOWN, -1, -1
6423 O_COW_ENCLOSED_6, FALSE,
6424 EL_BD_COW_DOWN, -1, -1
6427 O_COW_ENCLOSED_7, FALSE,
6428 EL_BD_COW_DOWN, -1, -1
6431 O_WALLED_DIAMOND, TRUE,
6432 EL_BD_WALL_DIAMOND, -1, -1
6435 O_WALLED_KEY_1, TRUE,
6436 EL_BD_WALL_KEY_1, -1, -1
6439 O_WALLED_KEY_2, TRUE,
6440 EL_BD_WALL_KEY_2, -1, -1
6443 O_WALLED_KEY_3, TRUE,
6444 EL_BD_WALL_KEY_3, -1, -1
6448 EL_BD_AMOEBA, -1, -1
6452 EL_BD_AMOEBA_2, -1, -1
6456 EL_BD_REPLICATOR, -1, -1
6459 O_CONVEYOR_LEFT, TRUE,
6460 EL_BD_CONVEYOR_LEFT, -1, -1
6463 O_CONVEYOR_RIGHT, TRUE,
6464 EL_BD_CONVEYOR_RIGHT, -1, -1
6476 EL_BD_VOODOO_DOLL, -1, -1
6484 EL_BD_BLADDER, -1, -1
6488 EL_BD_BLADDER, -1, -1
6492 EL_BD_BLADDER, -1, -1
6496 EL_BD_BLADDER, -1, -1
6500 EL_BD_BLADDER, -1, -1
6504 EL_BD_BLADDER, -1, -1
6508 EL_BD_BLADDER, -1, -1
6512 EL_BD_BLADDER, -1, -1
6516 EL_BD_BLADDER, -1, -1
6519 O_WAITING_STONE, TRUE,
6520 EL_BD_WAITING_ROCK, -1, -1
6523 O_CHASING_STONE, TRUE,
6524 EL_BD_CHASING_ROCK, -1, -1
6532 EL_BD_FIREFLY_LEFT, -1, -1
6536 EL_BD_FIREFLY_UP, -1, -1
6540 EL_BD_FIREFLY_RIGHT, -1, -1
6544 EL_BD_FIREFLY_DOWN, -1, -1
6547 O_ALT_FIREFLY_1, TRUE,
6548 EL_BD_FIREFLY_2_LEFT, -1, -1
6551 O_ALT_FIREFLY_2, TRUE,
6552 EL_BD_FIREFLY_2_UP, -1, -1
6555 O_ALT_FIREFLY_3, TRUE,
6556 EL_BD_FIREFLY_2_RIGHT, -1, -1
6559 O_ALT_FIREFLY_4, TRUE,
6560 EL_BD_FIREFLY_2_DOWN, -1, -1
6564 EL_BD_BUTTERFLY_LEFT, -1, -1
6568 EL_BD_BUTTERFLY_UP, -1, -1
6572 EL_BD_BUTTERFLY_RIGHT, -1, -1
6576 EL_BD_BUTTERFLY_DOWN, -1, -1
6579 O_ALT_BUTTER_1, TRUE,
6580 EL_BD_BUTTERFLY_2_LEFT, -1, -1
6583 O_ALT_BUTTER_2, TRUE,
6584 EL_BD_BUTTERFLY_2_UP, -1, -1
6587 O_ALT_BUTTER_3, TRUE,
6588 EL_BD_BUTTERFLY_2_RIGHT, -1, -1
6591 O_ALT_BUTTER_4, TRUE,
6592 EL_BD_BUTTERFLY_2_DOWN, -1, -1
6596 EL_BD_STONEFLY_LEFT, -1, -1
6600 EL_BD_STONEFLY_UP, -1, -1
6604 EL_BD_STONEFLY_RIGHT, -1, -1
6608 EL_BD_STONEFLY_DOWN, -1, -1
6612 EL_BD_BITER_UP, -1, -1
6616 EL_BD_BITER_RIGHT, -1, -1
6620 EL_BD_BITER_DOWN, -1, -1
6624 EL_BD_BITER_LEFT, -1, -1
6627 O_DRAGONFLY_1, TRUE,
6628 EL_BD_DRAGONFLY_LEFT, -1, -1
6631 O_DRAGONFLY_2, TRUE,
6632 EL_BD_DRAGONFLY_UP, -1, -1
6635 O_DRAGONFLY_3, TRUE,
6636 EL_BD_DRAGONFLY_RIGHT, -1, -1
6639 O_DRAGONFLY_4, TRUE,
6640 EL_BD_DRAGONFLY_DOWN, -1, -1
6644 EL_BD_PLAYER, ACTION_GROWING, -1
6648 EL_BD_PLAYER, ACTION_GROWING, -1
6652 EL_BD_PLAYER, ACTION_GROWING, -1
6656 EL_BD_PLAYER, -1, -1
6659 O_PLAYER_BOMB, TRUE,
6660 EL_BD_PLAYER_WITH_BOMB, -1, -1
6663 O_PLAYER_GLUED, TRUE,
6664 EL_BD_PLAYER_GLUED, -1, -1
6667 O_PLAYER_STIRRING, TRUE,
6668 EL_BD_PLAYER_STIRRING, -1, -1
6675 O_BOMB_TICK_1, FALSE,
6676 EL_BD_BOMB, ACTION_ACTIVE, -1
6679 O_BOMB_TICK_2, FALSE,
6680 EL_BD_BOMB, ACTION_ACTIVE, -1
6683 O_BOMB_TICK_3, FALSE,
6684 EL_BD_BOMB, ACTION_ACTIVE, -1
6687 O_BOMB_TICK_4, FALSE,
6688 EL_BD_BOMB, ACTION_ACTIVE, -1
6691 O_BOMB_TICK_5, FALSE,
6692 EL_BD_BOMB, ACTION_ACTIVE, -1
6695 O_BOMB_TICK_6, FALSE,
6696 EL_BD_BOMB, ACTION_ACTIVE, -1
6699 O_BOMB_TICK_7, FALSE,
6700 EL_BD_BOMB, ACTION_ACTIVE, -1
6704 EL_BD_NITRO_PACK, -1, -1
6707 O_NITRO_PACK_F, FALSE,
6708 EL_BD_NITRO_PACK, ACTION_FALLING, -1
6711 O_NITRO_PACK_EXPLODE, FALSE,
6712 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6715 O_PRE_CLOCK_1, FALSE,
6716 EL_BD_CLOCK, ACTION_GROWING, -1
6719 O_PRE_CLOCK_2, FALSE,
6720 EL_BD_CLOCK, ACTION_GROWING, -1
6723 O_PRE_CLOCK_3, FALSE,
6724 EL_BD_CLOCK, ACTION_GROWING, -1
6727 O_PRE_CLOCK_4, FALSE,
6728 EL_BD_CLOCK, ACTION_GROWING, -1
6732 EL_BD_DIAMOND, ACTION_GROWING, -1
6736 EL_BD_DIAMOND, ACTION_GROWING, -1
6740 EL_BD_DIAMOND, ACTION_GROWING, -1
6744 EL_BD_DIAMOND, ACTION_GROWING, -1
6748 EL_BD_DIAMOND, ACTION_GROWING, -1
6752 EL_DEFAULT, ACTION_EXPLODING, -1
6756 EL_DEFAULT, ACTION_EXPLODING, -1
6760 EL_DEFAULT, ACTION_EXPLODING, -1
6764 EL_DEFAULT, ACTION_EXPLODING, -1
6768 EL_DEFAULT, ACTION_EXPLODING, -1
6771 O_PRE_STONE_1, FALSE,
6772 EL_BD_ROCK, ACTION_GROWING, -1
6775 O_PRE_STONE_2, FALSE,
6776 EL_BD_ROCK, ACTION_GROWING, -1
6779 O_PRE_STONE_3, FALSE,
6780 EL_BD_ROCK, ACTION_GROWING, -1
6783 O_PRE_STONE_4, FALSE,
6784 EL_BD_ROCK, ACTION_GROWING, -1
6787 O_PRE_STEEL_1, FALSE,
6788 EL_BD_STEELWALL, ACTION_GROWING, -1
6791 O_PRE_STEEL_2, FALSE,
6792 EL_BD_STEELWALL, ACTION_GROWING, -1
6795 O_PRE_STEEL_3, FALSE,
6796 EL_BD_STEELWALL, ACTION_GROWING, -1
6799 O_PRE_STEEL_4, FALSE,
6800 EL_BD_STEELWALL, ACTION_GROWING, -1
6803 O_GHOST_EXPL_1, FALSE,
6804 EL_BD_GHOST, ACTION_EXPLODING, -1
6807 O_GHOST_EXPL_2, FALSE,
6808 EL_BD_GHOST, ACTION_EXPLODING, -1
6811 O_GHOST_EXPL_3, FALSE,
6812 EL_BD_GHOST, ACTION_EXPLODING, -1
6815 O_GHOST_EXPL_4, FALSE,
6816 EL_BD_GHOST, ACTION_EXPLODING, -1
6819 O_BOMB_EXPL_1, FALSE,
6820 EL_BD_BOMB, ACTION_EXPLODING, -1
6823 O_BOMB_EXPL_2, FALSE,
6824 EL_BD_BOMB, ACTION_EXPLODING, -1
6827 O_BOMB_EXPL_3, FALSE,
6828 EL_BD_BOMB, ACTION_EXPLODING, -1
6831 O_BOMB_EXPL_4, FALSE,
6832 EL_BD_BOMB, ACTION_EXPLODING, -1
6835 O_NITRO_EXPL_1, FALSE,
6836 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6839 O_NITRO_EXPL_2, FALSE,
6840 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6843 O_NITRO_EXPL_3, FALSE,
6844 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6847 O_NITRO_EXPL_4, FALSE,
6848 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6851 O_AMOEBA_2_EXPL_1, FALSE,
6852 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6855 O_AMOEBA_2_EXPL_2, FALSE,
6856 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6859 O_AMOEBA_2_EXPL_3, FALSE,
6860 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6863 O_AMOEBA_2_EXPL_4, FALSE,
6864 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6867 O_NUT_EXPL_1, FALSE,
6868 EL_BD_NUT, ACTION_BREAKING, -1
6871 O_NUT_EXPL_2, FALSE,
6872 EL_BD_NUT, ACTION_BREAKING, -1
6875 O_NUT_EXPL_3, FALSE,
6876 EL_BD_NUT, ACTION_BREAKING, -1
6879 O_NUT_EXPL_4, FALSE,
6880 EL_BD_NUT, ACTION_BREAKING, -1
6883 O_PLAYER_PNEUMATIC_LEFT, FALSE,
6884 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_LEFT
6887 O_PLAYER_PNEUMATIC_RIGHT, FALSE,
6888 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_RIGHT
6891 O_PNEUMATIC_ACTIVE_LEFT, TRUE,
6892 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_LEFT
6895 O_PNEUMATIC_ACTIVE_RIGHT, TRUE,
6896 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_RIGHT
6899 // helper (runtime) elements
6902 O_FAKE_BONUS, FALSE,
6903 EL_BD_FAKE_BONUS, -1, -1
6906 O_INBOX_CLOSED, FALSE,
6910 O_INBOX_OPEN, FALSE,
6911 EL_BD_INBOX, ACTION_OPENING, -1
6914 O_OUTBOX_CLOSED, FALSE,
6915 EL_BD_EXIT_CLOSED, -1, -1
6918 O_OUTBOX_OPEN, FALSE,
6919 EL_BD_EXIT_OPEN, -1, -1
6923 EL_BD_COVERED, -1, -1
6926 O_PLAYER_LEFT, FALSE,
6927 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_LEFT
6930 O_PLAYER_RIGHT, FALSE,
6931 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_RIGHT
6934 O_PLAYER_BLINK, FALSE,
6935 EL_BD_PLAYER, ACTION_BORING_1, -1
6938 O_PLAYER_TAP, FALSE,
6939 EL_BD_PLAYER, ACTION_BORING_2, -1
6942 O_PLAYER_TAP_BLINK, FALSE,
6943 EL_BD_PLAYER, ACTION_BORING_3, -1
6946 O_CREATURE_SWITCH_ON, FALSE,
6947 EL_BD_CREATURE_SWITCH_ACTIVE, -1, -1
6950 O_EXPANDING_WALL_SWITCH_HORIZ, FALSE,
6951 EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1
6954 O_EXPANDING_WALL_SWITCH_VERT, FALSE,
6955 EL_BD_EXPANDABLE_WALL_SWITCH_VERTICAL, -1, -1
6958 O_GRAVITY_SWITCH_ACTIVE, FALSE,
6959 EL_BD_GRAVITY_SWITCH_ACTIVE, -1, -1
6962 O_REPLICATOR_SWITCH_OFF, FALSE,
6963 EL_BD_REPLICATOR_SWITCH, -1, -1
6966 O_REPLICATOR_SWITCH_ON, FALSE,
6967 EL_BD_REPLICATOR_SWITCH_ACTIVE, -1, -1
6970 O_CONVEYOR_DIR_NORMAL, FALSE,
6971 EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1
6974 O_CONVEYOR_DIR_CHANGED, FALSE,
6975 EL_BD_CONVEYOR_DIR_SWITCH_LEFT, -1, -1
6978 O_CONVEYOR_SWITCH_OFF, FALSE,
6979 EL_BD_CONVEYOR_SWITCH, -1, -1
6982 O_CONVEYOR_SWITCH_ON, FALSE,
6983 EL_BD_CONVEYOR_SWITCH_ACTIVE, -1, -1
6986 O_MAGIC_WALL_ACTIVE, FALSE,
6987 EL_BD_MAGIC_WALL_ACTIVE, -1, -1
6990 O_REPLICATOR_ACTIVE, FALSE,
6991 EL_BD_REPLICATOR_ACTIVE, -1, -1
6994 O_CONVEYOR_LEFT_ACTIVE, FALSE,
6995 EL_BD_CONVEYOR_LEFT_ACTIVE, -1, -1
6998 O_CONVEYOR_RIGHT_ACTIVE, FALSE,
6999 EL_BD_CONVEYOR_RIGHT_ACTIVE, -1, -1
7002 O_BITER_SWITCH_1, FALSE,
7003 EL_BD_BITER_SWITCH_1, -1, -1
7006 O_BITER_SWITCH_2, FALSE,
7007 EL_BD_BITER_SWITCH_2, -1, -1
7010 O_BITER_SWITCH_3, FALSE,
7011 EL_BD_BITER_SWITCH_3, -1, -1
7014 O_BITER_SWITCH_4, FALSE,
7015 EL_BD_BITER_SWITCH_4, -1, -1
7024 int map_element_RND_to_BD(int element_rnd)
7026 static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS];
7027 static boolean mapping_initialized = FALSE;
7029 if (!mapping_initialized)
7033 // return "O_UNKNOWN" for all undefined elements in mapping array
7034 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7035 mapping_RND_to_BD[i] = O_UNKNOWN;
7037 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7038 if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7039 mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] =
7040 bd_object_mapping_list[i].element_bd;
7042 mapping_initialized = TRUE;
7045 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7047 Warn("invalid RND element %d", element_rnd);
7052 return mapping_RND_to_BD[element_rnd];
7055 int map_element_BD_to_RND(int element_bd)
7057 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7058 static boolean mapping_initialized = FALSE;
7060 if (!mapping_initialized)
7064 // return "EL_UNKNOWN" for all undefined elements in mapping array
7065 for (i = 0; i < O_MAX_ALL; i++)
7066 mapping_BD_to_RND[i] = EL_UNKNOWN;
7068 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7069 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7070 bd_object_mapping_list[i].element_rnd;
7072 mapping_initialized = TRUE;
7075 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7077 Warn("invalid BD element %d", element_bd);
7082 return mapping_BD_to_RND[element_bd];
7085 static struct Mapping_EM_to_RND_object
7088 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
7089 boolean is_backside; // backside of moving element
7095 em_object_mapping_list[GAME_TILE_MAX + 1] =
7098 Zborder, FALSE, FALSE,
7102 Zplayer, FALSE, FALSE,
7111 Ztank, FALSE, FALSE,
7115 Zeater, FALSE, FALSE,
7119 Zdynamite, FALSE, FALSE,
7123 Zboom, FALSE, FALSE,
7128 Xchain, FALSE, FALSE,
7129 EL_DEFAULT, ACTION_EXPLODING, -1
7132 Xboom_bug, FALSE, FALSE,
7133 EL_BUG, ACTION_EXPLODING, -1
7136 Xboom_tank, FALSE, FALSE,
7137 EL_SPACESHIP, ACTION_EXPLODING, -1
7140 Xboom_android, FALSE, FALSE,
7141 EL_EMC_ANDROID, ACTION_OTHER, -1
7144 Xboom_1, FALSE, FALSE,
7145 EL_DEFAULT, ACTION_EXPLODING, -1
7148 Xboom_2, FALSE, FALSE,
7149 EL_DEFAULT, ACTION_EXPLODING, -1
7153 Xblank, TRUE, FALSE,
7158 Xsplash_e, FALSE, FALSE,
7159 EL_ACID_SPLASH_RIGHT, -1, -1
7162 Xsplash_w, FALSE, FALSE,
7163 EL_ACID_SPLASH_LEFT, -1, -1
7167 Xplant, TRUE, FALSE,
7168 EL_EMC_PLANT, -1, -1
7171 Yplant, FALSE, FALSE,
7172 EL_EMC_PLANT, -1, -1
7176 Xacid_1, TRUE, FALSE,
7180 Xacid_2, FALSE, FALSE,
7184 Xacid_3, FALSE, FALSE,
7188 Xacid_4, FALSE, FALSE,
7192 Xacid_5, FALSE, FALSE,
7196 Xacid_6, FALSE, FALSE,
7200 Xacid_7, FALSE, FALSE,
7204 Xacid_8, FALSE, FALSE,
7209 Xfake_acid_1, TRUE, FALSE,
7210 EL_EMC_FAKE_ACID, -1, -1
7213 Xfake_acid_2, FALSE, FALSE,
7214 EL_EMC_FAKE_ACID, -1, -1
7217 Xfake_acid_3, FALSE, FALSE,
7218 EL_EMC_FAKE_ACID, -1, -1
7221 Xfake_acid_4, FALSE, FALSE,
7222 EL_EMC_FAKE_ACID, -1, -1
7225 Xfake_acid_5, FALSE, FALSE,
7226 EL_EMC_FAKE_ACID, -1, -1
7229 Xfake_acid_6, FALSE, FALSE,
7230 EL_EMC_FAKE_ACID, -1, -1
7233 Xfake_acid_7, FALSE, FALSE,
7234 EL_EMC_FAKE_ACID, -1, -1
7237 Xfake_acid_8, FALSE, FALSE,
7238 EL_EMC_FAKE_ACID, -1, -1
7242 Xfake_acid_1_player, FALSE, FALSE,
7243 EL_EMC_FAKE_ACID, -1, -1
7246 Xfake_acid_2_player, FALSE, FALSE,
7247 EL_EMC_FAKE_ACID, -1, -1
7250 Xfake_acid_3_player, FALSE, FALSE,
7251 EL_EMC_FAKE_ACID, -1, -1
7254 Xfake_acid_4_player, FALSE, FALSE,
7255 EL_EMC_FAKE_ACID, -1, -1
7258 Xfake_acid_5_player, FALSE, FALSE,
7259 EL_EMC_FAKE_ACID, -1, -1
7262 Xfake_acid_6_player, FALSE, FALSE,
7263 EL_EMC_FAKE_ACID, -1, -1
7266 Xfake_acid_7_player, FALSE, FALSE,
7267 EL_EMC_FAKE_ACID, -1, -1
7270 Xfake_acid_8_player, FALSE, FALSE,
7271 EL_EMC_FAKE_ACID, -1, -1
7275 Xgrass, TRUE, FALSE,
7276 EL_EMC_GRASS, -1, -1
7279 Ygrass_nB, FALSE, FALSE,
7280 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
7283 Ygrass_eB, FALSE, FALSE,
7284 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
7287 Ygrass_sB, FALSE, FALSE,
7288 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
7291 Ygrass_wB, FALSE, FALSE,
7292 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
7300 Ydirt_nB, FALSE, FALSE,
7301 EL_SAND, ACTION_DIGGING, MV_BIT_UP
7304 Ydirt_eB, FALSE, FALSE,
7305 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
7308 Ydirt_sB, FALSE, FALSE,
7309 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
7312 Ydirt_wB, FALSE, FALSE,
7313 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
7317 Xandroid, TRUE, FALSE,
7318 EL_EMC_ANDROID, ACTION_ACTIVE, -1
7321 Xandroid_1_n, FALSE, FALSE,
7322 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7325 Xandroid_2_n, FALSE, FALSE,
7326 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7329 Xandroid_1_e, FALSE, FALSE,
7330 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7333 Xandroid_2_e, FALSE, FALSE,
7334 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7337 Xandroid_1_w, FALSE, FALSE,
7338 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7341 Xandroid_2_w, FALSE, FALSE,
7342 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7345 Xandroid_1_s, FALSE, FALSE,
7346 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7349 Xandroid_2_s, FALSE, FALSE,
7350 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7353 Yandroid_n, FALSE, FALSE,
7354 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7357 Yandroid_nB, FALSE, TRUE,
7358 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7361 Yandroid_ne, FALSE, FALSE,
7362 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
7365 Yandroid_neB, FALSE, TRUE,
7366 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
7369 Yandroid_e, FALSE, FALSE,
7370 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
7373 Yandroid_eB, FALSE, TRUE,
7374 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
7377 Yandroid_se, FALSE, FALSE,
7378 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
7381 Yandroid_seB, FALSE, TRUE,
7382 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
7385 Yandroid_s, FALSE, FALSE,
7386 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
7389 Yandroid_sB, FALSE, TRUE,
7390 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
7393 Yandroid_sw, FALSE, FALSE,
7394 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
7397 Yandroid_swB, FALSE, TRUE,
7398 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
7401 Yandroid_w, FALSE, FALSE,
7402 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
7405 Yandroid_wB, FALSE, TRUE,
7406 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
7409 Yandroid_nw, FALSE, FALSE,
7410 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
7413 Yandroid_nwB, FALSE, TRUE,
7414 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
7418 Xeater_n, TRUE, FALSE,
7419 EL_YAMYAM_UP, -1, -1
7422 Xeater_e, TRUE, FALSE,
7423 EL_YAMYAM_RIGHT, -1, -1
7426 Xeater_w, TRUE, FALSE,
7427 EL_YAMYAM_LEFT, -1, -1
7430 Xeater_s, TRUE, FALSE,
7431 EL_YAMYAM_DOWN, -1, -1
7434 Yeater_n, FALSE, FALSE,
7435 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
7438 Yeater_nB, FALSE, TRUE,
7439 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
7442 Yeater_e, FALSE, FALSE,
7443 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
7446 Yeater_eB, FALSE, TRUE,
7447 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
7450 Yeater_s, FALSE, FALSE,
7451 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
7454 Yeater_sB, FALSE, TRUE,
7455 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
7458 Yeater_w, FALSE, FALSE,
7459 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
7462 Yeater_wB, FALSE, TRUE,
7463 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
7466 Yeater_stone, FALSE, FALSE,
7467 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
7470 Yeater_spring, FALSE, FALSE,
7471 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
7475 Xalien, TRUE, FALSE,
7479 Xalien_pause, FALSE, FALSE,
7483 Yalien_n, FALSE, FALSE,
7484 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
7487 Yalien_nB, FALSE, TRUE,
7488 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
7491 Yalien_e, FALSE, FALSE,
7492 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
7495 Yalien_eB, FALSE, TRUE,
7496 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
7499 Yalien_s, FALSE, FALSE,
7500 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
7503 Yalien_sB, FALSE, TRUE,
7504 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
7507 Yalien_w, FALSE, FALSE,
7508 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
7511 Yalien_wB, FALSE, TRUE,
7512 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
7515 Yalien_stone, FALSE, FALSE,
7516 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
7519 Yalien_spring, FALSE, FALSE,
7520 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
7524 Xbug_1_n, TRUE, FALSE,
7528 Xbug_1_e, TRUE, FALSE,
7529 EL_BUG_RIGHT, -1, -1
7532 Xbug_1_s, TRUE, FALSE,
7536 Xbug_1_w, TRUE, FALSE,
7540 Xbug_2_n, FALSE, FALSE,
7544 Xbug_2_e, FALSE, FALSE,
7545 EL_BUG_RIGHT, -1, -1
7548 Xbug_2_s, FALSE, FALSE,
7552 Xbug_2_w, FALSE, FALSE,
7556 Ybug_n, FALSE, FALSE,
7557 EL_BUG, ACTION_MOVING, MV_BIT_UP
7560 Ybug_nB, FALSE, TRUE,
7561 EL_BUG, ACTION_MOVING, MV_BIT_UP
7564 Ybug_e, FALSE, FALSE,
7565 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
7568 Ybug_eB, FALSE, TRUE,
7569 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
7572 Ybug_s, FALSE, FALSE,
7573 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
7576 Ybug_sB, FALSE, TRUE,
7577 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
7580 Ybug_w, FALSE, FALSE,
7581 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
7584 Ybug_wB, FALSE, TRUE,
7585 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
7588 Ybug_w_n, FALSE, FALSE,
7589 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7592 Ybug_n_e, FALSE, FALSE,
7593 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7596 Ybug_e_s, FALSE, FALSE,
7597 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7600 Ybug_s_w, FALSE, FALSE,
7601 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7604 Ybug_e_n, FALSE, FALSE,
7605 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7608 Ybug_s_e, FALSE, FALSE,
7609 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7612 Ybug_w_s, FALSE, FALSE,
7613 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7616 Ybug_n_w, FALSE, FALSE,
7617 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7620 Ybug_stone, FALSE, FALSE,
7621 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
7624 Ybug_spring, FALSE, FALSE,
7625 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
7629 Xtank_1_n, TRUE, FALSE,
7630 EL_SPACESHIP_UP, -1, -1
7633 Xtank_1_e, TRUE, FALSE,
7634 EL_SPACESHIP_RIGHT, -1, -1
7637 Xtank_1_s, TRUE, FALSE,
7638 EL_SPACESHIP_DOWN, -1, -1
7641 Xtank_1_w, TRUE, FALSE,
7642 EL_SPACESHIP_LEFT, -1, -1
7645 Xtank_2_n, FALSE, FALSE,
7646 EL_SPACESHIP_UP, -1, -1
7649 Xtank_2_e, FALSE, FALSE,
7650 EL_SPACESHIP_RIGHT, -1, -1
7653 Xtank_2_s, FALSE, FALSE,
7654 EL_SPACESHIP_DOWN, -1, -1
7657 Xtank_2_w, FALSE, FALSE,
7658 EL_SPACESHIP_LEFT, -1, -1
7661 Ytank_n, FALSE, FALSE,
7662 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
7665 Ytank_nB, FALSE, TRUE,
7666 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
7669 Ytank_e, FALSE, FALSE,
7670 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
7673 Ytank_eB, FALSE, TRUE,
7674 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
7677 Ytank_s, FALSE, FALSE,
7678 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
7681 Ytank_sB, FALSE, TRUE,
7682 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
7685 Ytank_w, FALSE, FALSE,
7686 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
7689 Ytank_wB, FALSE, TRUE,
7690 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
7693 Ytank_w_n, FALSE, FALSE,
7694 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7697 Ytank_n_e, FALSE, FALSE,
7698 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7701 Ytank_e_s, FALSE, FALSE,
7702 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7705 Ytank_s_w, FALSE, FALSE,
7706 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7709 Ytank_e_n, FALSE, FALSE,
7710 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7713 Ytank_s_e, FALSE, FALSE,
7714 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7717 Ytank_w_s, FALSE, FALSE,
7718 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7721 Ytank_n_w, FALSE, FALSE,
7722 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7725 Ytank_stone, FALSE, FALSE,
7726 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
7729 Ytank_spring, FALSE, FALSE,
7730 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
7734 Xemerald, TRUE, FALSE,
7738 Xemerald_pause, FALSE, FALSE,
7742 Xemerald_fall, FALSE, FALSE,
7746 Xemerald_shine, FALSE, FALSE,
7747 EL_EMERALD, ACTION_TWINKLING, -1
7750 Yemerald_s, FALSE, FALSE,
7751 EL_EMERALD, ACTION_FALLING, -1
7754 Yemerald_sB, FALSE, TRUE,
7755 EL_EMERALD, ACTION_FALLING, -1
7758 Yemerald_e, FALSE, FALSE,
7759 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
7762 Yemerald_eB, FALSE, TRUE,
7763 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
7766 Yemerald_w, FALSE, FALSE,
7767 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
7770 Yemerald_wB, FALSE, TRUE,
7771 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
7774 Yemerald_blank, FALSE, FALSE,
7775 EL_EMERALD, ACTION_COLLECTING, -1
7779 Xdiamond, TRUE, FALSE,
7783 Xdiamond_pause, FALSE, FALSE,
7787 Xdiamond_fall, FALSE, FALSE,
7791 Xdiamond_shine, FALSE, FALSE,
7792 EL_DIAMOND, ACTION_TWINKLING, -1
7795 Ydiamond_s, FALSE, FALSE,
7796 EL_DIAMOND, ACTION_FALLING, -1
7799 Ydiamond_sB, FALSE, TRUE,
7800 EL_DIAMOND, ACTION_FALLING, -1
7803 Ydiamond_e, FALSE, FALSE,
7804 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
7807 Ydiamond_eB, FALSE, TRUE,
7808 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
7811 Ydiamond_w, FALSE, FALSE,
7812 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
7815 Ydiamond_wB, FALSE, TRUE,
7816 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
7819 Ydiamond_blank, FALSE, FALSE,
7820 EL_DIAMOND, ACTION_COLLECTING, -1
7823 Ydiamond_stone, FALSE, FALSE,
7824 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
7828 Xstone, TRUE, FALSE,
7832 Xstone_pause, FALSE, FALSE,
7836 Xstone_fall, FALSE, FALSE,
7840 Ystone_s, FALSE, FALSE,
7841 EL_ROCK, ACTION_FALLING, -1
7844 Ystone_sB, FALSE, TRUE,
7845 EL_ROCK, ACTION_FALLING, -1
7848 Ystone_e, FALSE, FALSE,
7849 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
7852 Ystone_eB, FALSE, TRUE,
7853 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
7856 Ystone_w, FALSE, FALSE,
7857 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
7860 Ystone_wB, FALSE, TRUE,
7861 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
7869 Xbomb_pause, FALSE, FALSE,
7873 Xbomb_fall, FALSE, FALSE,
7877 Ybomb_s, FALSE, FALSE,
7878 EL_BOMB, ACTION_FALLING, -1
7881 Ybomb_sB, FALSE, TRUE,
7882 EL_BOMB, ACTION_FALLING, -1
7885 Ybomb_e, FALSE, FALSE,
7886 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
7889 Ybomb_eB, FALSE, TRUE,
7890 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
7893 Ybomb_w, FALSE, FALSE,
7894 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
7897 Ybomb_wB, FALSE, TRUE,
7898 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
7901 Ybomb_blank, FALSE, FALSE,
7902 EL_BOMB, ACTION_ACTIVATING, -1
7910 Xnut_pause, FALSE, FALSE,
7914 Xnut_fall, FALSE, FALSE,
7918 Ynut_s, FALSE, FALSE,
7919 EL_NUT, ACTION_FALLING, -1
7922 Ynut_sB, FALSE, TRUE,
7923 EL_NUT, ACTION_FALLING, -1
7926 Ynut_e, FALSE, FALSE,
7927 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
7930 Ynut_eB, FALSE, TRUE,
7931 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
7934 Ynut_w, FALSE, FALSE,
7935 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
7938 Ynut_wB, FALSE, TRUE,
7939 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
7942 Ynut_stone, FALSE, FALSE,
7943 EL_NUT, ACTION_BREAKING, -1
7947 Xspring, TRUE, FALSE,
7951 Xspring_pause, FALSE, FALSE,
7955 Xspring_e, TRUE, FALSE,
7956 EL_SPRING_RIGHT, -1, -1
7959 Xspring_w, TRUE, FALSE,
7960 EL_SPRING_LEFT, -1, -1
7963 Xspring_fall, FALSE, FALSE,
7967 Yspring_s, FALSE, FALSE,
7968 EL_SPRING, ACTION_FALLING, -1
7971 Yspring_sB, FALSE, TRUE,
7972 EL_SPRING, ACTION_FALLING, -1
7975 Yspring_e, FALSE, FALSE,
7976 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
7979 Yspring_eB, FALSE, TRUE,
7980 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
7983 Yspring_w, FALSE, FALSE,
7984 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
7987 Yspring_wB, FALSE, TRUE,
7988 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
7991 Yspring_alien_e, FALSE, FALSE,
7992 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
7995 Yspring_alien_eB, FALSE, TRUE,
7996 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
7999 Yspring_alien_w, FALSE, FALSE,
8000 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8003 Yspring_alien_wB, FALSE, TRUE,
8004 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8008 Xpush_emerald_e, FALSE, FALSE,
8009 EL_EMERALD, -1, MV_BIT_RIGHT
8012 Xpush_emerald_w, FALSE, FALSE,
8013 EL_EMERALD, -1, MV_BIT_LEFT
8016 Xpush_diamond_e, FALSE, FALSE,
8017 EL_DIAMOND, -1, MV_BIT_RIGHT
8020 Xpush_diamond_w, FALSE, FALSE,
8021 EL_DIAMOND, -1, MV_BIT_LEFT
8024 Xpush_stone_e, FALSE, FALSE,
8025 EL_ROCK, -1, MV_BIT_RIGHT
8028 Xpush_stone_w, FALSE, FALSE,
8029 EL_ROCK, -1, MV_BIT_LEFT
8032 Xpush_bomb_e, FALSE, FALSE,
8033 EL_BOMB, -1, MV_BIT_RIGHT
8036 Xpush_bomb_w, FALSE, FALSE,
8037 EL_BOMB, -1, MV_BIT_LEFT
8040 Xpush_nut_e, FALSE, FALSE,
8041 EL_NUT, -1, MV_BIT_RIGHT
8044 Xpush_nut_w, FALSE, FALSE,
8045 EL_NUT, -1, MV_BIT_LEFT
8048 Xpush_spring_e, FALSE, FALSE,
8049 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
8052 Xpush_spring_w, FALSE, FALSE,
8053 EL_SPRING_LEFT, -1, MV_BIT_LEFT
8057 Xdynamite, TRUE, FALSE,
8058 EL_EM_DYNAMITE, -1, -1
8061 Ydynamite_blank, FALSE, FALSE,
8062 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
8065 Xdynamite_1, TRUE, FALSE,
8066 EL_EM_DYNAMITE_ACTIVE, -1, -1
8069 Xdynamite_2, FALSE, FALSE,
8070 EL_EM_DYNAMITE_ACTIVE, -1, -1
8073 Xdynamite_3, FALSE, FALSE,
8074 EL_EM_DYNAMITE_ACTIVE, -1, -1
8077 Xdynamite_4, FALSE, FALSE,
8078 EL_EM_DYNAMITE_ACTIVE, -1, -1
8082 Xkey_1, TRUE, FALSE,
8086 Xkey_2, TRUE, FALSE,
8090 Xkey_3, TRUE, FALSE,
8094 Xkey_4, TRUE, FALSE,
8098 Xkey_5, TRUE, FALSE,
8099 EL_EMC_KEY_5, -1, -1
8102 Xkey_6, TRUE, FALSE,
8103 EL_EMC_KEY_6, -1, -1
8106 Xkey_7, TRUE, FALSE,
8107 EL_EMC_KEY_7, -1, -1
8110 Xkey_8, TRUE, FALSE,
8111 EL_EMC_KEY_8, -1, -1
8115 Xdoor_1, TRUE, FALSE,
8116 EL_EM_GATE_1, -1, -1
8119 Xdoor_2, TRUE, FALSE,
8120 EL_EM_GATE_2, -1, -1
8123 Xdoor_3, TRUE, FALSE,
8124 EL_EM_GATE_3, -1, -1
8127 Xdoor_4, TRUE, FALSE,
8128 EL_EM_GATE_4, -1, -1
8131 Xdoor_5, TRUE, FALSE,
8132 EL_EMC_GATE_5, -1, -1
8135 Xdoor_6, TRUE, FALSE,
8136 EL_EMC_GATE_6, -1, -1
8139 Xdoor_7, TRUE, FALSE,
8140 EL_EMC_GATE_7, -1, -1
8143 Xdoor_8, TRUE, FALSE,
8144 EL_EMC_GATE_8, -1, -1
8148 Xfake_door_1, TRUE, FALSE,
8149 EL_EM_GATE_1_GRAY, -1, -1
8152 Xfake_door_2, TRUE, FALSE,
8153 EL_EM_GATE_2_GRAY, -1, -1
8156 Xfake_door_3, TRUE, FALSE,
8157 EL_EM_GATE_3_GRAY, -1, -1
8160 Xfake_door_4, TRUE, FALSE,
8161 EL_EM_GATE_4_GRAY, -1, -1
8164 Xfake_door_5, TRUE, FALSE,
8165 EL_EMC_GATE_5_GRAY, -1, -1
8168 Xfake_door_6, TRUE, FALSE,
8169 EL_EMC_GATE_6_GRAY, -1, -1
8172 Xfake_door_7, TRUE, FALSE,
8173 EL_EMC_GATE_7_GRAY, -1, -1
8176 Xfake_door_8, TRUE, FALSE,
8177 EL_EMC_GATE_8_GRAY, -1, -1
8181 Xballoon, TRUE, FALSE,
8185 Yballoon_n, FALSE, FALSE,
8186 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8189 Yballoon_nB, FALSE, TRUE,
8190 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8193 Yballoon_e, FALSE, FALSE,
8194 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8197 Yballoon_eB, FALSE, TRUE,
8198 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8201 Yballoon_s, FALSE, FALSE,
8202 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8205 Yballoon_sB, FALSE, TRUE,
8206 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8209 Yballoon_w, FALSE, FALSE,
8210 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8213 Yballoon_wB, FALSE, TRUE,
8214 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8218 Xball_1, TRUE, FALSE,
8219 EL_EMC_MAGIC_BALL, -1, -1
8222 Yball_1, FALSE, FALSE,
8223 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8226 Xball_2, FALSE, FALSE,
8227 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8230 Yball_2, FALSE, FALSE,
8231 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8234 Yball_blank, FALSE, FALSE,
8235 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
8239 Xamoeba_1, TRUE, FALSE,
8240 EL_AMOEBA_DRY, ACTION_OTHER, -1
8243 Xamoeba_2, FALSE, FALSE,
8244 EL_AMOEBA_DRY, ACTION_OTHER, -1
8247 Xamoeba_3, FALSE, FALSE,
8248 EL_AMOEBA_DRY, ACTION_OTHER, -1
8251 Xamoeba_4, FALSE, FALSE,
8252 EL_AMOEBA_DRY, ACTION_OTHER, -1
8255 Xamoeba_5, TRUE, FALSE,
8256 EL_AMOEBA_WET, ACTION_OTHER, -1
8259 Xamoeba_6, FALSE, FALSE,
8260 EL_AMOEBA_WET, ACTION_OTHER, -1
8263 Xamoeba_7, FALSE, FALSE,
8264 EL_AMOEBA_WET, ACTION_OTHER, -1
8267 Xamoeba_8, FALSE, FALSE,
8268 EL_AMOEBA_WET, ACTION_OTHER, -1
8273 EL_AMOEBA_DROP, ACTION_GROWING, -1
8276 Xdrip_fall, FALSE, FALSE,
8277 EL_AMOEBA_DROP, -1, -1
8280 Xdrip_stretch, FALSE, FALSE,
8281 EL_AMOEBA_DROP, ACTION_FALLING, -1
8284 Xdrip_stretchB, FALSE, TRUE,
8285 EL_AMOEBA_DROP, ACTION_FALLING, -1
8288 Ydrip_1_s, FALSE, FALSE,
8289 EL_AMOEBA_DROP, ACTION_FALLING, -1
8292 Ydrip_1_sB, FALSE, TRUE,
8293 EL_AMOEBA_DROP, ACTION_FALLING, -1
8296 Ydrip_2_s, FALSE, FALSE,
8297 EL_AMOEBA_DROP, ACTION_FALLING, -1
8300 Ydrip_2_sB, FALSE, TRUE,
8301 EL_AMOEBA_DROP, ACTION_FALLING, -1
8305 Xwonderwall, TRUE, FALSE,
8306 EL_MAGIC_WALL, -1, -1
8309 Ywonderwall, FALSE, FALSE,
8310 EL_MAGIC_WALL, ACTION_ACTIVE, -1
8314 Xwheel, TRUE, FALSE,
8315 EL_ROBOT_WHEEL, -1, -1
8318 Ywheel, FALSE, FALSE,
8319 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
8323 Xswitch, TRUE, FALSE,
8324 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
8327 Yswitch, FALSE, FALSE,
8328 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
8332 Xbumper, TRUE, FALSE,
8333 EL_EMC_SPRING_BUMPER, -1, -1
8336 Ybumper, FALSE, FALSE,
8337 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
8341 Xacid_nw, TRUE, FALSE,
8342 EL_ACID_POOL_TOPLEFT, -1, -1
8345 Xacid_ne, TRUE, FALSE,
8346 EL_ACID_POOL_TOPRIGHT, -1, -1
8349 Xacid_sw, TRUE, FALSE,
8350 EL_ACID_POOL_BOTTOMLEFT, -1, -1
8353 Xacid_s, TRUE, FALSE,
8354 EL_ACID_POOL_BOTTOM, -1, -1
8357 Xacid_se, TRUE, FALSE,
8358 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
8362 Xfake_blank, TRUE, FALSE,
8363 EL_INVISIBLE_WALL, -1, -1
8366 Yfake_blank, FALSE, FALSE,
8367 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
8371 Xfake_grass, TRUE, FALSE,
8372 EL_EMC_FAKE_GRASS, -1, -1
8375 Yfake_grass, FALSE, FALSE,
8376 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
8380 Xfake_amoeba, TRUE, FALSE,
8381 EL_EMC_DRIPPER, -1, -1
8384 Yfake_amoeba, FALSE, FALSE,
8385 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
8389 Xlenses, TRUE, FALSE,
8390 EL_EMC_LENSES, -1, -1
8394 Xmagnify, TRUE, FALSE,
8395 EL_EMC_MAGNIFIER, -1, -1
8400 EL_QUICKSAND_EMPTY, -1, -1
8403 Xsand_stone, TRUE, FALSE,
8404 EL_QUICKSAND_FULL, -1, -1
8407 Xsand_stonein_1, FALSE, TRUE,
8408 EL_ROCK, ACTION_FILLING, -1
8411 Xsand_stonein_2, FALSE, TRUE,
8412 EL_ROCK, ACTION_FILLING, -1
8415 Xsand_stonein_3, FALSE, TRUE,
8416 EL_ROCK, ACTION_FILLING, -1
8419 Xsand_stonein_4, FALSE, TRUE,
8420 EL_ROCK, ACTION_FILLING, -1
8423 Xsand_sandstone_1, FALSE, FALSE,
8424 EL_QUICKSAND_FILLING, -1, -1
8427 Xsand_sandstone_2, FALSE, FALSE,
8428 EL_QUICKSAND_FILLING, -1, -1
8431 Xsand_sandstone_3, FALSE, FALSE,
8432 EL_QUICKSAND_FILLING, -1, -1
8435 Xsand_sandstone_4, FALSE, FALSE,
8436 EL_QUICKSAND_FILLING, -1, -1
8439 Xsand_stonesand_1, FALSE, FALSE,
8440 EL_QUICKSAND_EMPTYING, -1, -1
8443 Xsand_stonesand_2, FALSE, FALSE,
8444 EL_QUICKSAND_EMPTYING, -1, -1
8447 Xsand_stonesand_3, FALSE, FALSE,
8448 EL_QUICKSAND_EMPTYING, -1, -1
8451 Xsand_stonesand_4, FALSE, FALSE,
8452 EL_QUICKSAND_EMPTYING, -1, -1
8455 Xsand_stoneout_1, FALSE, FALSE,
8456 EL_ROCK, ACTION_EMPTYING, -1
8459 Xsand_stoneout_2, FALSE, FALSE,
8460 EL_ROCK, ACTION_EMPTYING, -1
8463 Xsand_stonesand_quickout_1, FALSE, FALSE,
8464 EL_QUICKSAND_EMPTYING, -1, -1
8467 Xsand_stonesand_quickout_2, FALSE, FALSE,
8468 EL_QUICKSAND_EMPTYING, -1, -1
8472 Xslide_ns, TRUE, FALSE,
8473 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
8476 Yslide_ns_blank, FALSE, FALSE,
8477 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
8480 Xslide_ew, TRUE, FALSE,
8481 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
8484 Yslide_ew_blank, FALSE, FALSE,
8485 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
8489 Xwind_n, TRUE, FALSE,
8490 EL_BALLOON_SWITCH_UP, -1, -1
8493 Xwind_e, TRUE, FALSE,
8494 EL_BALLOON_SWITCH_RIGHT, -1, -1
8497 Xwind_s, TRUE, FALSE,
8498 EL_BALLOON_SWITCH_DOWN, -1, -1
8501 Xwind_w, TRUE, FALSE,
8502 EL_BALLOON_SWITCH_LEFT, -1, -1
8505 Xwind_any, TRUE, FALSE,
8506 EL_BALLOON_SWITCH_ANY, -1, -1
8509 Xwind_stop, TRUE, FALSE,
8510 EL_BALLOON_SWITCH_NONE, -1, -1
8515 EL_EM_EXIT_CLOSED, -1, -1
8518 Xexit_1, TRUE, FALSE,
8519 EL_EM_EXIT_OPEN, -1, -1
8522 Xexit_2, FALSE, FALSE,
8523 EL_EM_EXIT_OPEN, -1, -1
8526 Xexit_3, FALSE, FALSE,
8527 EL_EM_EXIT_OPEN, -1, -1
8531 Xpause, FALSE, FALSE,
8536 Xwall_1, TRUE, FALSE,
8540 Xwall_2, TRUE, FALSE,
8541 EL_EMC_WALL_14, -1, -1
8544 Xwall_3, TRUE, FALSE,
8545 EL_EMC_WALL_15, -1, -1
8548 Xwall_4, TRUE, FALSE,
8549 EL_EMC_WALL_16, -1, -1
8553 Xroundwall_1, TRUE, FALSE,
8554 EL_WALL_SLIPPERY, -1, -1
8557 Xroundwall_2, TRUE, FALSE,
8558 EL_EMC_WALL_SLIPPERY_2, -1, -1
8561 Xroundwall_3, TRUE, FALSE,
8562 EL_EMC_WALL_SLIPPERY_3, -1, -1
8565 Xroundwall_4, TRUE, FALSE,
8566 EL_EMC_WALL_SLIPPERY_4, -1, -1
8570 Xsteel_1, TRUE, FALSE,
8571 EL_STEELWALL, -1, -1
8574 Xsteel_2, TRUE, FALSE,
8575 EL_EMC_STEELWALL_2, -1, -1
8578 Xsteel_3, TRUE, FALSE,
8579 EL_EMC_STEELWALL_3, -1, -1
8582 Xsteel_4, TRUE, FALSE,
8583 EL_EMC_STEELWALL_4, -1, -1
8587 Xdecor_1, TRUE, FALSE,
8588 EL_EMC_WALL_8, -1, -1
8591 Xdecor_2, TRUE, FALSE,
8592 EL_EMC_WALL_6, -1, -1
8595 Xdecor_3, TRUE, FALSE,
8596 EL_EMC_WALL_4, -1, -1
8599 Xdecor_4, TRUE, FALSE,
8600 EL_EMC_WALL_7, -1, -1
8603 Xdecor_5, TRUE, FALSE,
8604 EL_EMC_WALL_5, -1, -1
8607 Xdecor_6, TRUE, FALSE,
8608 EL_EMC_WALL_9, -1, -1
8611 Xdecor_7, TRUE, FALSE,
8612 EL_EMC_WALL_10, -1, -1
8615 Xdecor_8, TRUE, FALSE,
8616 EL_EMC_WALL_1, -1, -1
8619 Xdecor_9, TRUE, FALSE,
8620 EL_EMC_WALL_2, -1, -1
8623 Xdecor_10, TRUE, FALSE,
8624 EL_EMC_WALL_3, -1, -1
8627 Xdecor_11, TRUE, FALSE,
8628 EL_EMC_WALL_11, -1, -1
8631 Xdecor_12, TRUE, FALSE,
8632 EL_EMC_WALL_12, -1, -1
8636 Xalpha_0, TRUE, FALSE,
8637 EL_CHAR('0'), -1, -1
8640 Xalpha_1, TRUE, FALSE,
8641 EL_CHAR('1'), -1, -1
8644 Xalpha_2, TRUE, FALSE,
8645 EL_CHAR('2'), -1, -1
8648 Xalpha_3, TRUE, FALSE,
8649 EL_CHAR('3'), -1, -1
8652 Xalpha_4, TRUE, FALSE,
8653 EL_CHAR('4'), -1, -1
8656 Xalpha_5, TRUE, FALSE,
8657 EL_CHAR('5'), -1, -1
8660 Xalpha_6, TRUE, FALSE,
8661 EL_CHAR('6'), -1, -1
8664 Xalpha_7, TRUE, FALSE,
8665 EL_CHAR('7'), -1, -1
8668 Xalpha_8, TRUE, FALSE,
8669 EL_CHAR('8'), -1, -1
8672 Xalpha_9, TRUE, FALSE,
8673 EL_CHAR('9'), -1, -1
8676 Xalpha_excla, TRUE, FALSE,
8677 EL_CHAR('!'), -1, -1
8680 Xalpha_apost, TRUE, FALSE,
8681 EL_CHAR('\''), -1, -1
8684 Xalpha_comma, TRUE, FALSE,
8685 EL_CHAR(','), -1, -1
8688 Xalpha_minus, TRUE, FALSE,
8689 EL_CHAR('-'), -1, -1
8692 Xalpha_perio, TRUE, FALSE,
8693 EL_CHAR('.'), -1, -1
8696 Xalpha_colon, TRUE, FALSE,
8697 EL_CHAR(':'), -1, -1
8700 Xalpha_quest, TRUE, FALSE,
8701 EL_CHAR('?'), -1, -1
8704 Xalpha_a, TRUE, FALSE,
8705 EL_CHAR('A'), -1, -1
8708 Xalpha_b, TRUE, FALSE,
8709 EL_CHAR('B'), -1, -1
8712 Xalpha_c, TRUE, FALSE,
8713 EL_CHAR('C'), -1, -1
8716 Xalpha_d, TRUE, FALSE,
8717 EL_CHAR('D'), -1, -1
8720 Xalpha_e, TRUE, FALSE,
8721 EL_CHAR('E'), -1, -1
8724 Xalpha_f, TRUE, FALSE,
8725 EL_CHAR('F'), -1, -1
8728 Xalpha_g, TRUE, FALSE,
8729 EL_CHAR('G'), -1, -1
8732 Xalpha_h, TRUE, FALSE,
8733 EL_CHAR('H'), -1, -1
8736 Xalpha_i, TRUE, FALSE,
8737 EL_CHAR('I'), -1, -1
8740 Xalpha_j, TRUE, FALSE,
8741 EL_CHAR('J'), -1, -1
8744 Xalpha_k, TRUE, FALSE,
8745 EL_CHAR('K'), -1, -1
8748 Xalpha_l, TRUE, FALSE,
8749 EL_CHAR('L'), -1, -1
8752 Xalpha_m, TRUE, FALSE,
8753 EL_CHAR('M'), -1, -1
8756 Xalpha_n, TRUE, FALSE,
8757 EL_CHAR('N'), -1, -1
8760 Xalpha_o, TRUE, FALSE,
8761 EL_CHAR('O'), -1, -1
8764 Xalpha_p, TRUE, FALSE,
8765 EL_CHAR('P'), -1, -1
8768 Xalpha_q, TRUE, FALSE,
8769 EL_CHAR('Q'), -1, -1
8772 Xalpha_r, TRUE, FALSE,
8773 EL_CHAR('R'), -1, -1
8776 Xalpha_s, TRUE, FALSE,
8777 EL_CHAR('S'), -1, -1
8780 Xalpha_t, TRUE, FALSE,
8781 EL_CHAR('T'), -1, -1
8784 Xalpha_u, TRUE, FALSE,
8785 EL_CHAR('U'), -1, -1
8788 Xalpha_v, TRUE, FALSE,
8789 EL_CHAR('V'), -1, -1
8792 Xalpha_w, TRUE, FALSE,
8793 EL_CHAR('W'), -1, -1
8796 Xalpha_x, TRUE, FALSE,
8797 EL_CHAR('X'), -1, -1
8800 Xalpha_y, TRUE, FALSE,
8801 EL_CHAR('Y'), -1, -1
8804 Xalpha_z, TRUE, FALSE,
8805 EL_CHAR('Z'), -1, -1
8808 Xalpha_arrow_e, TRUE, FALSE,
8809 EL_CHAR('>'), -1, -1
8812 Xalpha_arrow_w, TRUE, FALSE,
8813 EL_CHAR('<'), -1, -1
8816 Xalpha_copyr, TRUE, FALSE,
8817 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
8821 Ykey_1_blank, FALSE, FALSE,
8822 EL_EM_KEY_1, ACTION_COLLECTING, -1
8825 Ykey_2_blank, FALSE, FALSE,
8826 EL_EM_KEY_2, ACTION_COLLECTING, -1
8829 Ykey_3_blank, FALSE, FALSE,
8830 EL_EM_KEY_3, ACTION_COLLECTING, -1
8833 Ykey_4_blank, FALSE, FALSE,
8834 EL_EM_KEY_4, ACTION_COLLECTING, -1
8837 Ykey_5_blank, FALSE, FALSE,
8838 EL_EMC_KEY_5, ACTION_COLLECTING, -1
8841 Ykey_6_blank, FALSE, FALSE,
8842 EL_EMC_KEY_6, ACTION_COLLECTING, -1
8845 Ykey_7_blank, FALSE, FALSE,
8846 EL_EMC_KEY_7, ACTION_COLLECTING, -1
8849 Ykey_8_blank, FALSE, FALSE,
8850 EL_EMC_KEY_8, ACTION_COLLECTING, -1
8853 Ylenses_blank, FALSE, FALSE,
8854 EL_EMC_LENSES, ACTION_COLLECTING, -1
8857 Ymagnify_blank, FALSE, FALSE,
8858 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
8861 Ygrass_blank, FALSE, FALSE,
8862 EL_EMC_GRASS, ACTION_SNAPPING, -1
8865 Ydirt_blank, FALSE, FALSE,
8866 EL_SAND, ACTION_SNAPPING, -1
8875 static struct Mapping_EM_to_RND_player
8884 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
8888 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
8892 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
8896 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
8900 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
8904 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
8908 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
8912 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
8916 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
8920 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
8924 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
8928 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
8932 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
8936 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
8940 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
8944 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
8948 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
8952 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
8956 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
8960 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
8964 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
8968 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
8972 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
8976 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
8980 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
8984 EL_PLAYER_1, ACTION_DEFAULT, -1,
8988 EL_PLAYER_2, ACTION_DEFAULT, -1,
8992 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
8996 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
9000 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
9004 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
9008 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
9012 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
9016 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
9020 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
9024 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
9028 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
9032 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
9036 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
9040 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
9044 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
9048 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
9052 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
9056 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
9060 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
9064 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
9068 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
9072 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
9076 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
9080 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
9084 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
9088 EL_PLAYER_3, ACTION_DEFAULT, -1,
9092 EL_PLAYER_4, ACTION_DEFAULT, -1,
9101 int map_element_RND_to_EM_cave(int element_rnd)
9103 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9104 static boolean mapping_initialized = FALSE;
9106 if (!mapping_initialized)
9110 // return "Xalpha_quest" for all undefined elements in mapping array
9111 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9112 mapping_RND_to_EM[i] = Xalpha_quest;
9114 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9115 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9116 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9117 em_object_mapping_list[i].element_em;
9119 mapping_initialized = TRUE;
9122 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9124 Warn("invalid RND level element %d", element_rnd);
9129 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9132 int map_element_EM_to_RND_cave(int element_em_cave)
9134 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9135 static boolean mapping_initialized = FALSE;
9137 if (!mapping_initialized)
9141 // return "EL_UNKNOWN" for all undefined elements in mapping array
9142 for (i = 0; i < GAME_TILE_MAX; i++)
9143 mapping_EM_to_RND[i] = EL_UNKNOWN;
9145 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9146 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9147 em_object_mapping_list[i].element_rnd;
9149 mapping_initialized = TRUE;
9152 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9154 Warn("invalid EM cave element %d", element_em_cave);
9159 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9162 int map_element_EM_to_RND_game(int element_em_game)
9164 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9165 static boolean mapping_initialized = FALSE;
9167 if (!mapping_initialized)
9171 // return "EL_UNKNOWN" for all undefined elements in mapping array
9172 for (i = 0; i < GAME_TILE_MAX; i++)
9173 mapping_EM_to_RND[i] = EL_UNKNOWN;
9175 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9176 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9177 em_object_mapping_list[i].element_rnd;
9179 mapping_initialized = TRUE;
9182 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9184 Warn("invalid EM game element %d", element_em_game);
9189 return mapping_EM_to_RND[element_em_game];
9192 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9194 struct LevelInfo_EM *level_em = level->native_em_level;
9195 struct CAVE *cav = level_em->cav;
9198 for (i = 0; i < GAME_TILE_MAX; i++)
9199 cav->android_array[i] = Cblank;
9201 for (i = 0; i < level->num_android_clone_elements; i++)
9203 int element_rnd = level->android_clone_element[i];
9204 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9206 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9207 if (em_object_mapping_list[j].element_rnd == element_rnd)
9208 cav->android_array[em_object_mapping_list[j].element_em] =
9213 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9215 struct LevelInfo_EM *level_em = level->native_em_level;
9216 struct CAVE *cav = level_em->cav;
9219 level->num_android_clone_elements = 0;
9221 for (i = 0; i < GAME_TILE_MAX; i++)
9223 int element_em_cave = cav->android_array[i];
9225 boolean element_found = FALSE;
9227 if (element_em_cave == Cblank)
9230 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9232 for (j = 0; j < level->num_android_clone_elements; j++)
9233 if (level->android_clone_element[j] == element_rnd)
9234 element_found = TRUE;
9238 level->android_clone_element[level->num_android_clone_elements++] =
9241 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9246 if (level->num_android_clone_elements == 0)
9248 level->num_android_clone_elements = 1;
9249 level->android_clone_element[0] = EL_EMPTY;
9253 int map_direction_RND_to_EM(int direction)
9255 return (direction == MV_UP ? 0 :
9256 direction == MV_RIGHT ? 1 :
9257 direction == MV_DOWN ? 2 :
9258 direction == MV_LEFT ? 3 :
9262 int map_direction_EM_to_RND(int direction)
9264 return (direction == 0 ? MV_UP :
9265 direction == 1 ? MV_RIGHT :
9266 direction == 2 ? MV_DOWN :
9267 direction == 3 ? MV_LEFT :
9271 int map_element_RND_to_SP(int element_rnd)
9273 int element_sp = 0x20; // map unknown elements to yellow "hardware"
9275 if (element_rnd >= EL_SP_START &&
9276 element_rnd <= EL_SP_END)
9277 element_sp = element_rnd - EL_SP_START;
9278 else if (element_rnd == EL_EMPTY_SPACE)
9280 else if (element_rnd == EL_INVISIBLE_WALL)
9286 int map_element_SP_to_RND(int element_sp)
9288 int element_rnd = EL_UNKNOWN;
9290 if (element_sp >= 0x00 &&
9292 element_rnd = EL_SP_START + element_sp;
9293 else if (element_sp == 0x28)
9294 element_rnd = EL_INVISIBLE_WALL;
9299 int map_action_SP_to_RND(int action_sp)
9303 case actActive: return ACTION_ACTIVE;
9304 case actImpact: return ACTION_IMPACT;
9305 case actExploding: return ACTION_EXPLODING;
9306 case actDigging: return ACTION_DIGGING;
9307 case actSnapping: return ACTION_SNAPPING;
9308 case actCollecting: return ACTION_COLLECTING;
9309 case actPassing: return ACTION_PASSING;
9310 case actPushing: return ACTION_PUSHING;
9311 case actDropping: return ACTION_DROPPING;
9313 default: return ACTION_DEFAULT;
9317 int map_element_RND_to_MM(int element_rnd)
9319 return (element_rnd >= EL_MM_START_1 &&
9320 element_rnd <= EL_MM_END_1 ?
9321 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9323 element_rnd >= EL_MM_START_2 &&
9324 element_rnd <= EL_MM_END_2 ?
9325 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9327 element_rnd >= EL_MM_START_3 &&
9328 element_rnd <= EL_MM_END_3 ?
9329 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9331 element_rnd >= EL_CHAR_START &&
9332 element_rnd <= EL_CHAR_END ?
9333 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9335 element_rnd >= EL_MM_RUNTIME_START &&
9336 element_rnd <= EL_MM_RUNTIME_END ?
9337 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9339 EL_MM_EMPTY_NATIVE);
9342 int map_element_MM_to_RND(int element_mm)
9344 return (element_mm == EL_MM_EMPTY_NATIVE ||
9345 element_mm == EL_DF_EMPTY_NATIVE ?
9348 element_mm >= EL_MM_START_1_NATIVE &&
9349 element_mm <= EL_MM_END_1_NATIVE ?
9350 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
9352 element_mm >= EL_MM_START_2_NATIVE &&
9353 element_mm <= EL_MM_END_2_NATIVE ?
9354 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
9356 element_mm >= EL_MM_START_3_NATIVE &&
9357 element_mm <= EL_MM_END_3_NATIVE ?
9358 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
9360 element_mm >= EL_MM_CHAR_START_NATIVE &&
9361 element_mm <= EL_MM_CHAR_END_NATIVE ?
9362 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
9364 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
9365 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
9366 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
9371 int map_action_MM_to_RND(int action_mm)
9373 // all MM actions are defined to exactly match their RND counterparts
9377 int map_sound_MM_to_RND(int sound_mm)
9381 case SND_MM_GAME_LEVELTIME_CHARGING:
9382 return SND_GAME_LEVELTIME_CHARGING;
9384 case SND_MM_GAME_HEALTH_CHARGING:
9385 return SND_GAME_HEALTH_CHARGING;
9388 return SND_UNDEFINED;
9392 int map_mm_wall_element(int element)
9394 return (element >= EL_MM_STEEL_WALL_START &&
9395 element <= EL_MM_STEEL_WALL_END ?
9398 element >= EL_MM_WOODEN_WALL_START &&
9399 element <= EL_MM_WOODEN_WALL_END ?
9402 element >= EL_MM_ICE_WALL_START &&
9403 element <= EL_MM_ICE_WALL_END ?
9406 element >= EL_MM_AMOEBA_WALL_START &&
9407 element <= EL_MM_AMOEBA_WALL_END ?
9410 element >= EL_DF_STEEL_WALL_START &&
9411 element <= EL_DF_STEEL_WALL_END ?
9414 element >= EL_DF_WOODEN_WALL_START &&
9415 element <= EL_DF_WOODEN_WALL_END ?
9421 int map_mm_wall_element_editor(int element)
9425 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
9426 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
9427 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
9428 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
9429 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
9430 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
9432 default: return element;
9436 int get_next_element(int element)
9440 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
9441 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
9442 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
9443 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
9444 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
9445 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
9446 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
9447 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
9448 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
9449 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
9450 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
9452 default: return element;
9456 int el2img_mm(int element_mm)
9458 return el2img(map_element_MM_to_RND(element_mm));
9461 int el_act2img_mm(int element_mm, int action)
9463 return el_act2img(map_element_MM_to_RND(element_mm), action);
9466 int el_act_dir2img(int element, int action, int direction)
9468 element = GFX_ELEMENT(element);
9469 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9471 // direction_graphic[][] == graphic[] for undefined direction graphics
9472 return element_info[element].direction_graphic[action][direction];
9475 static int el_act_dir2crm(int element, int action, int direction)
9477 element = GFX_ELEMENT(element);
9478 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9480 // direction_graphic[][] == graphic[] for undefined direction graphics
9481 return element_info[element].direction_crumbled[action][direction];
9484 int el_act2img(int element, int action)
9486 element = GFX_ELEMENT(element);
9488 return element_info[element].graphic[action];
9491 int el_act2crm(int element, int action)
9493 element = GFX_ELEMENT(element);
9495 return element_info[element].crumbled[action];
9498 int el_dir2img(int element, int direction)
9500 element = GFX_ELEMENT(element);
9502 return el_act_dir2img(element, ACTION_DEFAULT, direction);
9505 int el2baseimg(int element)
9507 return element_info[element].graphic[ACTION_DEFAULT];
9510 int el2img(int element)
9512 element = GFX_ELEMENT(element);
9514 return element_info[element].graphic[ACTION_DEFAULT];
9517 int el2edimg(int element)
9519 element = GFX_ELEMENT(element);
9521 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
9524 int el2preimg(int element)
9526 element = GFX_ELEMENT(element);
9528 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
9531 int el2panelimg(int element)
9533 element = GFX_ELEMENT(element);
9535 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
9538 int font2baseimg(int font_nr)
9540 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
9543 int getBeltNrFromBeltElement(int element)
9545 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
9546 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
9547 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
9550 int getBeltNrFromBeltActiveElement(int element)
9552 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
9553 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
9554 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
9557 int getBeltNrFromBeltSwitchElement(int element)
9559 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
9560 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
9561 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
9564 int getBeltDirNrFromBeltElement(int element)
9566 static int belt_base_element[4] =
9568 EL_CONVEYOR_BELT_1_LEFT,
9569 EL_CONVEYOR_BELT_2_LEFT,
9570 EL_CONVEYOR_BELT_3_LEFT,
9571 EL_CONVEYOR_BELT_4_LEFT
9574 int belt_nr = getBeltNrFromBeltElement(element);
9575 int belt_dir_nr = element - belt_base_element[belt_nr];
9577 return (belt_dir_nr % 3);
9580 int getBeltDirNrFromBeltSwitchElement(int element)
9582 static int belt_base_element[4] =
9584 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9585 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9586 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9587 EL_CONVEYOR_BELT_4_SWITCH_LEFT
9590 int belt_nr = getBeltNrFromBeltSwitchElement(element);
9591 int belt_dir_nr = element - belt_base_element[belt_nr];
9593 return (belt_dir_nr % 3);
9596 int getBeltDirFromBeltElement(int element)
9598 static int belt_move_dir[3] =
9605 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
9607 return belt_move_dir[belt_dir_nr];
9610 int getBeltDirFromBeltSwitchElement(int element)
9612 static int belt_move_dir[3] =
9619 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
9621 return belt_move_dir[belt_dir_nr];
9624 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9626 static int belt_base_element[4] =
9628 EL_CONVEYOR_BELT_1_LEFT,
9629 EL_CONVEYOR_BELT_2_LEFT,
9630 EL_CONVEYOR_BELT_3_LEFT,
9631 EL_CONVEYOR_BELT_4_LEFT
9634 return belt_base_element[belt_nr] + belt_dir_nr;
9637 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9639 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9641 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9644 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9646 static int belt_base_element[4] =
9648 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9649 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9650 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9651 EL_CONVEYOR_BELT_4_SWITCH_LEFT
9654 return belt_base_element[belt_nr] + belt_dir_nr;
9657 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9659 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9661 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9664 boolean swapTiles_EM(boolean is_pre_emc_cave)
9666 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
9669 boolean getTeamMode_EM(void)
9671 return game.team_mode || network_playing;
9674 boolean isActivePlayer_EM(int player_nr)
9676 return stored_player[player_nr].active;
9679 unsigned int InitRND(int seed)
9681 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
9682 return InitEngineRandom_BD(seed);
9683 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9684 return InitEngineRandom_EM(seed);
9685 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
9686 return InitEngineRandom_SP(seed);
9687 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
9688 return InitEngineRandom_MM(seed);
9690 return InitEngineRandom_RND(seed);
9693 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
9694 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
9695 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
9697 static int get_effective_element_EM(int tile, int frame_em)
9699 int element = em_object_mapping[tile].element_rnd;
9700 int action = em_object_mapping[tile].action;
9701 boolean is_backside = em_object_mapping[tile].is_backside;
9702 boolean action_removing = (action == ACTION_DIGGING ||
9703 action == ACTION_SNAPPING ||
9704 action == ACTION_COLLECTING);
9712 return (frame_em > 5 ? EL_EMPTY : element);
9718 else // frame_em == 7
9729 case Ydiamond_stone:
9733 case Xdrip_stretchB:
9749 case Ymagnify_blank:
9752 case Xsand_stonein_1:
9753 case Xsand_stonein_2:
9754 case Xsand_stonein_3:
9755 case Xsand_stonein_4:
9759 return (is_backside || action_removing ? EL_EMPTY : element);
9764 static boolean check_linear_animation_EM(int tile)
9768 case Xsand_stonesand_1:
9769 case Xsand_stonesand_quickout_1:
9770 case Xsand_sandstone_1:
9771 case Xsand_stonein_1:
9772 case Xsand_stoneout_1:
9800 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
9801 boolean has_crumbled_graphics,
9802 int crumbled, int sync_frame)
9804 // if element can be crumbled, but certain action graphics are just empty
9805 // space (like instantly snapping sand to empty space in 1 frame), do not
9806 // treat these empty space graphics as crumbled graphics in EMC engine
9807 if (crumbled == IMG_EMPTY_SPACE)
9808 has_crumbled_graphics = FALSE;
9810 if (has_crumbled_graphics)
9812 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
9813 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
9814 g_crumbled->anim_delay,
9815 g_crumbled->anim_mode,
9816 g_crumbled->anim_start_frame,
9819 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
9820 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
9822 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
9823 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
9825 g_em->has_crumbled_graphics = TRUE;
9829 g_em->crumbled_bitmap = NULL;
9830 g_em->crumbled_src_x = 0;
9831 g_em->crumbled_src_y = 0;
9832 g_em->crumbled_border_size = 0;
9833 g_em->crumbled_tile_size = 0;
9835 g_em->has_crumbled_graphics = FALSE;
9840 void ResetGfxAnimation_EM(int x, int y, int tile)
9846 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
9847 int tile, int frame_em, int x, int y)
9849 int action = em_object_mapping[tile].action;
9850 int direction = em_object_mapping[tile].direction;
9851 int effective_element = get_effective_element_EM(tile, frame_em);
9852 int graphic = (direction == MV_NONE ?
9853 el_act2img(effective_element, action) :
9854 el_act_dir2img(effective_element, action, direction));
9855 struct GraphicInfo *g = &graphic_info[graphic];
9857 boolean action_removing = (action == ACTION_DIGGING ||
9858 action == ACTION_SNAPPING ||
9859 action == ACTION_COLLECTING);
9860 boolean action_moving = (action == ACTION_FALLING ||
9861 action == ACTION_MOVING ||
9862 action == ACTION_PUSHING ||
9863 action == ACTION_EATING ||
9864 action == ACTION_FILLING ||
9865 action == ACTION_EMPTYING);
9866 boolean action_falling = (action == ACTION_FALLING ||
9867 action == ACTION_FILLING ||
9868 action == ACTION_EMPTYING);
9870 // special case: graphic uses "2nd movement tile" and has defined
9871 // 7 frames for movement animation (or less) => use default graphic
9872 // for last (8th) frame which ends the movement animation
9873 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
9875 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
9876 graphic = (direction == MV_NONE ?
9877 el_act2img(effective_element, action) :
9878 el_act_dir2img(effective_element, action, direction));
9880 g = &graphic_info[graphic];
9883 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
9887 else if (action_moving)
9889 boolean is_backside = em_object_mapping[tile].is_backside;
9893 int direction = em_object_mapping[tile].direction;
9894 int move_dir = (action_falling ? MV_DOWN : direction);
9899 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
9900 if (g->double_movement && frame_em == 0)
9904 if (move_dir == MV_LEFT)
9905 GfxFrame[x - 1][y] = GfxFrame[x][y];
9906 else if (move_dir == MV_RIGHT)
9907 GfxFrame[x + 1][y] = GfxFrame[x][y];
9908 else if (move_dir == MV_UP)
9909 GfxFrame[x][y - 1] = GfxFrame[x][y];
9910 else if (move_dir == MV_DOWN)
9911 GfxFrame[x][y + 1] = GfxFrame[x][y];
9918 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
9919 if (tile == Xsand_stonesand_quickout_1 ||
9920 tile == Xsand_stonesand_quickout_2)
9924 if (graphic_info[graphic].anim_global_sync)
9925 sync_frame = FrameCounter;
9926 else if (graphic_info[graphic].anim_global_anim_sync)
9927 sync_frame = getGlobalAnimSyncFrame();
9928 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
9929 sync_frame = GfxFrame[x][y];
9931 sync_frame = 0; // playfield border (pseudo steel)
9933 SetRandomAnimationValue(x, y);
9935 int frame = getAnimationFrame(g->anim_frames,
9938 g->anim_start_frame,
9941 g_em->unique_identifier =
9942 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
9945 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
9946 int tile, int frame_em, int x, int y)
9948 int action = em_object_mapping[tile].action;
9949 int direction = em_object_mapping[tile].direction;
9950 boolean is_backside = em_object_mapping[tile].is_backside;
9951 int effective_element = get_effective_element_EM(tile, frame_em);
9952 int effective_action = action;
9953 int graphic = (direction == MV_NONE ?
9954 el_act2img(effective_element, effective_action) :
9955 el_act_dir2img(effective_element, effective_action,
9957 int crumbled = (direction == MV_NONE ?
9958 el_act2crm(effective_element, effective_action) :
9959 el_act_dir2crm(effective_element, effective_action,
9961 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9962 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9963 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9964 struct GraphicInfo *g = &graphic_info[graphic];
9967 // special case: graphic uses "2nd movement tile" and has defined
9968 // 7 frames for movement animation (or less) => use default graphic
9969 // for last (8th) frame which ends the movement animation
9970 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
9972 effective_action = ACTION_DEFAULT;
9973 graphic = (direction == MV_NONE ?
9974 el_act2img(effective_element, effective_action) :
9975 el_act_dir2img(effective_element, effective_action,
9977 crumbled = (direction == MV_NONE ?
9978 el_act2crm(effective_element, effective_action) :
9979 el_act_dir2crm(effective_element, effective_action,
9982 g = &graphic_info[graphic];
9985 if (graphic_info[graphic].anim_global_sync)
9986 sync_frame = FrameCounter;
9987 else if (graphic_info[graphic].anim_global_anim_sync)
9988 sync_frame = getGlobalAnimSyncFrame();
9989 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
9990 sync_frame = GfxFrame[x][y];
9992 sync_frame = 0; // playfield border (pseudo steel)
9994 SetRandomAnimationValue(x, y);
9996 int frame = getAnimationFrame(g->anim_frames,
9999 g->anim_start_frame,
10002 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10003 g->double_movement && is_backside);
10005 // (updating the "crumbled" graphic definitions is probably not really needed,
10006 // as animations for crumbled graphics can't be longer than one EMC cycle)
10007 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10011 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10012 int player_nr, int anim, int frame_em)
10014 int element = em_player_mapping[player_nr][anim].element_rnd;
10015 int action = em_player_mapping[player_nr][anim].action;
10016 int direction = em_player_mapping[player_nr][anim].direction;
10017 int graphic = (direction == MV_NONE ?
10018 el_act2img(element, action) :
10019 el_act_dir2img(element, action, direction));
10020 struct GraphicInfo *g = &graphic_info[graphic];
10023 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10025 stored_player[player_nr].StepFrame = frame_em;
10027 sync_frame = stored_player[player_nr].Frame;
10029 int frame = getAnimationFrame(g->anim_frames,
10032 g->anim_start_frame,
10035 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10036 &g_em->src_x, &g_em->src_y, FALSE);
10039 #define BD_GFX_RANGE(a, n, i) ((i) >= (a) && (i) < (a) + (n))
10040 #define BD_GFX_FRAME(b, i) (((i) - (b)) * 8)
10042 void InitGraphicInfo_BD(void)
10046 // always start with reliable default values
10047 for (i = 0; i < O_MAX_ALL; i++)
10049 bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10050 bd_object_mapping[i].action = ACTION_DEFAULT;
10051 bd_object_mapping[i].direction = MV_NONE;
10054 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10056 int e = bd_object_mapping_list[i].element_bd;
10058 bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10060 if (bd_object_mapping_list[i].action != -1)
10061 bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10063 if (bd_object_mapping_list[i].direction != -1)
10064 bd_object_mapping[e].direction =
10065 MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10068 for (i = 0; i < O_MAX_ALL; i++)
10070 int element = bd_object_mapping[i].element_rnd;
10071 int action = bd_object_mapping[i].action;
10072 int direction = bd_object_mapping[i].direction;
10074 for (j = 0; j < 8; j++)
10076 int effective_element = element;
10077 int effective_action = action;
10078 int graphic = (el_act_dir2img(effective_element, effective_action,
10080 struct GraphicInfo *g = &graphic_info[graphic];
10081 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10082 Bitmap *src_bitmap;
10084 int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i) ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10085 BD_GFX_RANGE(O_PRE_DIA_1, 5, i) ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10086 BD_GFX_RANGE(O_PRE_STONE_1, 4, i) ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10087 BD_GFX_RANGE(O_PRE_STEEL_1, 4, i) ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10088 BD_GFX_RANGE(O_BOMB_TICK_1, 7, i) ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10089 BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i) ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10090 BD_GFX_RANGE(O_NUT_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10091 BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i) ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10092 BD_GFX_RANGE(O_EXPLODE_1, 5, i) ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10093 BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i) ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10094 BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10095 BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10096 i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10098 int frame = getAnimationFrame(g->anim_frames,
10101 g->anim_start_frame,
10104 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10106 g_bd->bitmap = src_bitmap;
10107 g_bd->src_x = src_x;
10108 g_bd->src_y = src_y;
10109 g_bd->width = TILEX;
10110 g_bd->height = TILEY;
10115 void InitGraphicInfo_EM(void)
10119 // always start with reliable default values
10120 for (i = 0; i < GAME_TILE_MAX; i++)
10122 em_object_mapping[i].element_rnd = EL_UNKNOWN;
10123 em_object_mapping[i].is_backside = FALSE;
10124 em_object_mapping[i].action = ACTION_DEFAULT;
10125 em_object_mapping[i].direction = MV_NONE;
10128 // always start with reliable default values
10129 for (p = 0; p < MAX_PLAYERS; p++)
10131 for (i = 0; i < PLY_MAX; i++)
10133 em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10134 em_player_mapping[p][i].action = ACTION_DEFAULT;
10135 em_player_mapping[p][i].direction = MV_NONE;
10139 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10141 int e = em_object_mapping_list[i].element_em;
10143 em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10144 em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10146 if (em_object_mapping_list[i].action != -1)
10147 em_object_mapping[e].action = em_object_mapping_list[i].action;
10149 if (em_object_mapping_list[i].direction != -1)
10150 em_object_mapping[e].direction =
10151 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10154 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10156 int a = em_player_mapping_list[i].action_em;
10157 int p = em_player_mapping_list[i].player_nr;
10159 em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10161 if (em_player_mapping_list[i].action != -1)
10162 em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10164 if (em_player_mapping_list[i].direction != -1)
10165 em_player_mapping[p][a].direction =
10166 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10169 for (i = 0; i < GAME_TILE_MAX; i++)
10171 int element = em_object_mapping[i].element_rnd;
10172 int action = em_object_mapping[i].action;
10173 int direction = em_object_mapping[i].direction;
10174 boolean is_backside = em_object_mapping[i].is_backside;
10175 boolean action_exploding = ((action == ACTION_EXPLODING ||
10176 action == ACTION_SMASHED_BY_ROCK ||
10177 action == ACTION_SMASHED_BY_SPRING) &&
10178 element != EL_DIAMOND);
10179 boolean action_active = (action == ACTION_ACTIVE);
10180 boolean action_other = (action == ACTION_OTHER);
10182 for (j = 0; j < 8; j++)
10184 int effective_element = get_effective_element_EM(i, j);
10185 int effective_action = (j < 7 ? action :
10186 i == Xdrip_stretch ? action :
10187 i == Xdrip_stretchB ? action :
10188 i == Ydrip_1_s ? action :
10189 i == Ydrip_1_sB ? action :
10190 i == Yball_1 ? action :
10191 i == Xball_2 ? action :
10192 i == Yball_2 ? action :
10193 i == Yball_blank ? action :
10194 i == Ykey_1_blank ? action :
10195 i == Ykey_2_blank ? action :
10196 i == Ykey_3_blank ? action :
10197 i == Ykey_4_blank ? action :
10198 i == Ykey_5_blank ? action :
10199 i == Ykey_6_blank ? action :
10200 i == Ykey_7_blank ? action :
10201 i == Ykey_8_blank ? action :
10202 i == Ylenses_blank ? action :
10203 i == Ymagnify_blank ? action :
10204 i == Ygrass_blank ? action :
10205 i == Ydirt_blank ? action :
10206 i == Xsand_stonein_1 ? action :
10207 i == Xsand_stonein_2 ? action :
10208 i == Xsand_stonein_3 ? action :
10209 i == Xsand_stonein_4 ? action :
10210 i == Xsand_stoneout_1 ? action :
10211 i == Xsand_stoneout_2 ? action :
10212 i == Xboom_android ? ACTION_EXPLODING :
10213 action_exploding ? ACTION_EXPLODING :
10214 action_active ? action :
10215 action_other ? action :
10217 int graphic = (el_act_dir2img(effective_element, effective_action,
10219 int crumbled = (el_act_dir2crm(effective_element, effective_action,
10221 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10222 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10223 boolean has_action_graphics = (graphic != base_graphic);
10224 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10225 struct GraphicInfo *g = &graphic_info[graphic];
10226 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10227 Bitmap *src_bitmap;
10229 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10230 boolean special_animation = (action != ACTION_DEFAULT &&
10231 g->anim_frames == 3 &&
10232 g->anim_delay == 2 &&
10233 g->anim_mode & ANIM_LINEAR);
10234 int sync_frame = (i == Xdrip_stretch ? 7 :
10235 i == Xdrip_stretchB ? 7 :
10236 i == Ydrip_2_s ? j + 8 :
10237 i == Ydrip_2_sB ? j + 8 :
10239 i == Xacid_2 ? 10 :
10240 i == Xacid_3 ? 20 :
10241 i == Xacid_4 ? 30 :
10242 i == Xacid_5 ? 40 :
10243 i == Xacid_6 ? 50 :
10244 i == Xacid_7 ? 60 :
10245 i == Xacid_8 ? 70 :
10246 i == Xfake_acid_1 ? 0 :
10247 i == Xfake_acid_2 ? 10 :
10248 i == Xfake_acid_3 ? 20 :
10249 i == Xfake_acid_4 ? 30 :
10250 i == Xfake_acid_5 ? 40 :
10251 i == Xfake_acid_6 ? 50 :
10252 i == Xfake_acid_7 ? 60 :
10253 i == Xfake_acid_8 ? 70 :
10254 i == Xfake_acid_1_player ? 0 :
10255 i == Xfake_acid_2_player ? 10 :
10256 i == Xfake_acid_3_player ? 20 :
10257 i == Xfake_acid_4_player ? 30 :
10258 i == Xfake_acid_5_player ? 40 :
10259 i == Xfake_acid_6_player ? 50 :
10260 i == Xfake_acid_7_player ? 60 :
10261 i == Xfake_acid_8_player ? 70 :
10263 i == Yball_2 ? j + 8 :
10264 i == Yball_blank ? j + 1 :
10265 i == Ykey_1_blank ? j + 1 :
10266 i == Ykey_2_blank ? j + 1 :
10267 i == Ykey_3_blank ? j + 1 :
10268 i == Ykey_4_blank ? j + 1 :
10269 i == Ykey_5_blank ? j + 1 :
10270 i == Ykey_6_blank ? j + 1 :
10271 i == Ykey_7_blank ? j + 1 :
10272 i == Ykey_8_blank ? j + 1 :
10273 i == Ylenses_blank ? j + 1 :
10274 i == Ymagnify_blank ? j + 1 :
10275 i == Ygrass_blank ? j + 1 :
10276 i == Ydirt_blank ? j + 1 :
10277 i == Xamoeba_1 ? 0 :
10278 i == Xamoeba_2 ? 1 :
10279 i == Xamoeba_3 ? 2 :
10280 i == Xamoeba_4 ? 3 :
10281 i == Xamoeba_5 ? 0 :
10282 i == Xamoeba_6 ? 1 :
10283 i == Xamoeba_7 ? 2 :
10284 i == Xamoeba_8 ? 3 :
10285 i == Xexit_2 ? j + 8 :
10286 i == Xexit_3 ? j + 16 :
10287 i == Xdynamite_1 ? 0 :
10288 i == Xdynamite_2 ? 8 :
10289 i == Xdynamite_3 ? 16 :
10290 i == Xdynamite_4 ? 24 :
10291 i == Xsand_stonein_1 ? j + 1 :
10292 i == Xsand_stonein_2 ? j + 9 :
10293 i == Xsand_stonein_3 ? j + 17 :
10294 i == Xsand_stonein_4 ? j + 25 :
10295 i == Xsand_stoneout_1 && j == 0 ? 0 :
10296 i == Xsand_stoneout_1 && j == 1 ? 0 :
10297 i == Xsand_stoneout_1 && j == 2 ? 1 :
10298 i == Xsand_stoneout_1 && j == 3 ? 2 :
10299 i == Xsand_stoneout_1 && j == 4 ? 2 :
10300 i == Xsand_stoneout_1 && j == 5 ? 3 :
10301 i == Xsand_stoneout_1 && j == 6 ? 4 :
10302 i == Xsand_stoneout_1 && j == 7 ? 4 :
10303 i == Xsand_stoneout_2 && j == 0 ? 5 :
10304 i == Xsand_stoneout_2 && j == 1 ? 6 :
10305 i == Xsand_stoneout_2 && j == 2 ? 7 :
10306 i == Xsand_stoneout_2 && j == 3 ? 8 :
10307 i == Xsand_stoneout_2 && j == 4 ? 9 :
10308 i == Xsand_stoneout_2 && j == 5 ? 11 :
10309 i == Xsand_stoneout_2 && j == 6 ? 13 :
10310 i == Xsand_stoneout_2 && j == 7 ? 15 :
10311 i == Xboom_bug && j == 1 ? 2 :
10312 i == Xboom_bug && j == 2 ? 2 :
10313 i == Xboom_bug && j == 3 ? 4 :
10314 i == Xboom_bug && j == 4 ? 4 :
10315 i == Xboom_bug && j == 5 ? 2 :
10316 i == Xboom_bug && j == 6 ? 2 :
10317 i == Xboom_bug && j == 7 ? 0 :
10318 i == Xboom_tank && j == 1 ? 2 :
10319 i == Xboom_tank && j == 2 ? 2 :
10320 i == Xboom_tank && j == 3 ? 4 :
10321 i == Xboom_tank && j == 4 ? 4 :
10322 i == Xboom_tank && j == 5 ? 2 :
10323 i == Xboom_tank && j == 6 ? 2 :
10324 i == Xboom_tank && j == 7 ? 0 :
10325 i == Xboom_android && j == 7 ? 6 :
10326 i == Xboom_1 && j == 1 ? 2 :
10327 i == Xboom_1 && j == 2 ? 2 :
10328 i == Xboom_1 && j == 3 ? 4 :
10329 i == Xboom_1 && j == 4 ? 4 :
10330 i == Xboom_1 && j == 5 ? 6 :
10331 i == Xboom_1 && j == 6 ? 6 :
10332 i == Xboom_1 && j == 7 ? 8 :
10333 i == Xboom_2 && j == 0 ? 8 :
10334 i == Xboom_2 && j == 1 ? 8 :
10335 i == Xboom_2 && j == 2 ? 10 :
10336 i == Xboom_2 && j == 3 ? 10 :
10337 i == Xboom_2 && j == 4 ? 10 :
10338 i == Xboom_2 && j == 5 ? 12 :
10339 i == Xboom_2 && j == 6 ? 12 :
10340 i == Xboom_2 && j == 7 ? 12 :
10341 special_animation && j == 4 ? 3 :
10342 effective_action != action ? 0 :
10344 int frame = getAnimationFrame(g->anim_frames,
10347 g->anim_start_frame,
10350 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
10351 g->double_movement && is_backside);
10353 g_em->bitmap = src_bitmap;
10354 g_em->src_x = src_x;
10355 g_em->src_y = src_y;
10356 g_em->src_offset_x = 0;
10357 g_em->src_offset_y = 0;
10358 g_em->dst_offset_x = 0;
10359 g_em->dst_offset_y = 0;
10360 g_em->width = TILEX;
10361 g_em->height = TILEY;
10363 g_em->preserve_background = FALSE;
10365 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10368 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
10369 effective_action == ACTION_MOVING ||
10370 effective_action == ACTION_PUSHING ||
10371 effective_action == ACTION_EATING)) ||
10372 (!has_action_graphics && (effective_action == ACTION_FILLING ||
10373 effective_action == ACTION_EMPTYING)))
10376 (effective_action == ACTION_FALLING ||
10377 effective_action == ACTION_FILLING ||
10378 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
10379 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
10380 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
10381 int num_steps = (i == Ydrip_1_s ? 16 :
10382 i == Ydrip_1_sB ? 16 :
10383 i == Ydrip_2_s ? 16 :
10384 i == Ydrip_2_sB ? 16 :
10385 i == Xsand_stonein_1 ? 32 :
10386 i == Xsand_stonein_2 ? 32 :
10387 i == Xsand_stonein_3 ? 32 :
10388 i == Xsand_stonein_4 ? 32 :
10389 i == Xsand_stoneout_1 ? 16 :
10390 i == Xsand_stoneout_2 ? 16 : 8);
10391 int cx = ABS(dx) * (TILEX / num_steps);
10392 int cy = ABS(dy) * (TILEY / num_steps);
10393 int step_frame = (i == Ydrip_2_s ? j + 8 :
10394 i == Ydrip_2_sB ? j + 8 :
10395 i == Xsand_stonein_2 ? j + 8 :
10396 i == Xsand_stonein_3 ? j + 16 :
10397 i == Xsand_stonein_4 ? j + 24 :
10398 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
10399 int step = (is_backside ? step_frame : num_steps - step_frame);
10401 if (is_backside) // tile where movement starts
10403 if (dx < 0 || dy < 0)
10405 g_em->src_offset_x = cx * step;
10406 g_em->src_offset_y = cy * step;
10410 g_em->dst_offset_x = cx * step;
10411 g_em->dst_offset_y = cy * step;
10414 else // tile where movement ends
10416 if (dx < 0 || dy < 0)
10418 g_em->dst_offset_x = cx * step;
10419 g_em->dst_offset_y = cy * step;
10423 g_em->src_offset_x = cx * step;
10424 g_em->src_offset_y = cy * step;
10428 g_em->width = TILEX - cx * step;
10429 g_em->height = TILEY - cy * step;
10432 // create unique graphic identifier to decide if tile must be redrawn
10433 /* bit 31 - 16 (16 bit): EM style graphic
10434 bit 15 - 12 ( 4 bit): EM style frame
10435 bit 11 - 6 ( 6 bit): graphic width
10436 bit 5 - 0 ( 6 bit): graphic height */
10437 g_em->unique_identifier =
10438 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
10442 for (i = 0; i < GAME_TILE_MAX; i++)
10444 for (j = 0; j < 8; j++)
10446 int element = em_object_mapping[i].element_rnd;
10447 int action = em_object_mapping[i].action;
10448 int direction = em_object_mapping[i].direction;
10449 boolean is_backside = em_object_mapping[i].is_backside;
10450 int graphic_action = el_act_dir2img(element, action, direction);
10451 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
10453 if ((action == ACTION_SMASHED_BY_ROCK ||
10454 action == ACTION_SMASHED_BY_SPRING ||
10455 action == ACTION_EATING) &&
10456 graphic_action == graphic_default)
10458 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
10459 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
10460 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
10461 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
10464 // no separate animation for "smashed by rock" -- use rock instead
10465 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10466 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
10468 g_em->bitmap = g_xx->bitmap;
10469 g_em->src_x = g_xx->src_x;
10470 g_em->src_y = g_xx->src_y;
10471 g_em->src_offset_x = g_xx->src_offset_x;
10472 g_em->src_offset_y = g_xx->src_offset_y;
10473 g_em->dst_offset_x = g_xx->dst_offset_x;
10474 g_em->dst_offset_y = g_xx->dst_offset_y;
10475 g_em->width = g_xx->width;
10476 g_em->height = g_xx->height;
10477 g_em->unique_identifier = g_xx->unique_identifier;
10480 g_em->preserve_background = TRUE;
10485 for (p = 0; p < MAX_PLAYERS; p++)
10487 for (i = 0; i < PLY_MAX; i++)
10489 int element = em_player_mapping[p][i].element_rnd;
10490 int action = em_player_mapping[p][i].action;
10491 int direction = em_player_mapping[p][i].direction;
10493 for (j = 0; j < 8; j++)
10495 int effective_element = element;
10496 int effective_action = action;
10497 int graphic = (direction == MV_NONE ?
10498 el_act2img(effective_element, effective_action) :
10499 el_act_dir2img(effective_element, effective_action,
10501 struct GraphicInfo *g = &graphic_info[graphic];
10502 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
10503 Bitmap *src_bitmap;
10505 int sync_frame = j;
10506 int frame = getAnimationFrame(g->anim_frames,
10509 g->anim_start_frame,
10512 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10514 g_em->bitmap = src_bitmap;
10515 g_em->src_x = src_x;
10516 g_em->src_y = src_y;
10517 g_em->src_offset_x = 0;
10518 g_em->src_offset_y = 0;
10519 g_em->dst_offset_x = 0;
10520 g_em->dst_offset_y = 0;
10521 g_em->width = TILEX;
10522 g_em->height = TILEY;
10528 static void CheckSaveEngineSnapshot_EM(int frame,
10529 boolean any_player_moving,
10530 boolean any_player_snapping,
10531 boolean any_player_dropping)
10533 if (frame == 7 && !any_player_dropping)
10535 if (!local_player->was_waiting)
10537 if (!CheckSaveEngineSnapshotToList())
10540 local_player->was_waiting = TRUE;
10543 else if (any_player_moving || any_player_snapping || any_player_dropping)
10545 local_player->was_waiting = FALSE;
10549 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
10550 boolean murphy_is_dropping)
10552 if (murphy_is_waiting)
10554 if (!local_player->was_waiting)
10556 if (!CheckSaveEngineSnapshotToList())
10559 local_player->was_waiting = TRUE;
10564 local_player->was_waiting = FALSE;
10568 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
10569 boolean button_released)
10571 if (button_released)
10573 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
10574 CheckSaveEngineSnapshotToList();
10576 else if (element_clicked)
10578 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
10579 CheckSaveEngineSnapshotToList();
10581 game.snapshot.changed_action = TRUE;
10585 boolean CheckSingleStepMode_EM(int frame,
10586 boolean any_player_moving,
10587 boolean any_player_snapping,
10588 boolean any_player_dropping)
10590 if (tape.single_step && tape.recording && !tape.pausing)
10591 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
10592 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10594 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
10595 any_player_snapping, any_player_dropping);
10597 return tape.pausing;
10600 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
10601 boolean murphy_is_dropping)
10603 boolean murphy_starts_dropping = FALSE;
10606 for (i = 0; i < MAX_PLAYERS; i++)
10607 if (stored_player[i].force_dropping)
10608 murphy_starts_dropping = TRUE;
10610 if (tape.single_step && tape.recording && !tape.pausing)
10611 if (murphy_is_waiting && !murphy_starts_dropping)
10612 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10614 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
10617 void CheckSingleStepMode_MM(boolean element_clicked,
10618 boolean button_released)
10620 if (tape.single_step && tape.recording && !tape.pausing)
10621 if (button_released)
10622 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10624 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
10627 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
10628 int graphic, int sync_frame)
10630 int frame = getGraphicAnimationFrame(graphic, sync_frame);
10632 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
10635 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
10637 return (IS_NEXT_FRAME(sync_frame, graphic));
10640 int getGraphicInfo_Delay(int graphic)
10642 return graphic_info[graphic].anim_delay;
10645 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
10647 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
10650 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
10656 void PlayMenuSoundExt(int sound)
10658 if (sound == SND_UNDEFINED)
10661 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10662 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10665 if (IS_LOOP_SOUND(sound))
10666 PlaySoundLoop(sound);
10671 void PlayMenuSound(void)
10673 PlayMenuSoundExt(menu.sound[game_status]);
10676 void PlayMenuSoundStereo(int sound, int stereo_position)
10678 if (sound == SND_UNDEFINED)
10681 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10682 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10685 if (IS_LOOP_SOUND(sound))
10686 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
10688 PlaySoundStereo(sound, stereo_position);
10691 void PlayMenuSoundIfLoopExt(int sound)
10693 if (sound == SND_UNDEFINED)
10696 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10697 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10700 if (IS_LOOP_SOUND(sound))
10701 PlaySoundLoop(sound);
10704 void PlayMenuSoundIfLoop(void)
10706 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
10709 void PlayMenuMusicExt(int music)
10711 if (music == MUS_UNDEFINED)
10714 if (!setup.sound_music)
10717 if (IS_LOOP_MUSIC(music))
10718 PlayMusicLoop(music);
10723 void PlayMenuMusic(void)
10725 char *curr_music = getCurrentlyPlayingMusicFilename();
10726 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10728 if (!strEqual(curr_music, next_music))
10729 PlayMenuMusicExt(menu.music[game_status]);
10732 void PlayMenuSoundsAndMusic(void)
10738 static void FadeMenuSounds(void)
10743 static void FadeMenuMusic(void)
10745 char *curr_music = getCurrentlyPlayingMusicFilename();
10746 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10748 if (!strEqual(curr_music, next_music))
10752 void FadeMenuSoundsAndMusic(void)
10758 void PlaySoundActivating(void)
10761 PlaySound(SND_MENU_ITEM_ACTIVATING);
10765 void PlaySoundSelecting(void)
10768 PlaySound(SND_MENU_ITEM_SELECTING);
10772 void ToggleFullscreenIfNeeded(void)
10774 // if setup and video fullscreen state are already matching, nothing do do
10775 if (setup.fullscreen == video.fullscreen_enabled ||
10776 !video.fullscreen_available)
10779 SDLSetWindowFullscreen(setup.fullscreen);
10781 // set setup value according to successfully changed fullscreen mode
10782 setup.fullscreen = video.fullscreen_enabled;
10785 void ChangeWindowScalingIfNeeded(void)
10787 // if setup and video window scaling are already matching, nothing do do
10788 if (setup.window_scaling_percent == video.window_scaling_percent ||
10789 video.fullscreen_enabled)
10792 SDLSetWindowScaling(setup.window_scaling_percent);
10794 // set setup value according to successfully changed window scaling
10795 setup.window_scaling_percent = video.window_scaling_percent;
10798 void ChangeVsyncModeIfNeeded(void)
10800 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
10801 int video_vsync_mode = video.vsync_mode;
10803 // if setup and video vsync mode are already matching, nothing do do
10804 if (setup_vsync_mode == video_vsync_mode)
10807 // if renderer is using OpenGL, vsync mode can directly be changed
10808 SDLSetScreenVsyncMode(setup.vsync_mode);
10810 // if vsync mode unchanged, try re-creating renderer to set vsync mode
10811 if (video.vsync_mode == video_vsync_mode)
10813 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
10815 // save backbuffer content which gets lost when re-creating screen
10816 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10818 // force re-creating screen and renderer to set new vsync mode
10819 video.fullscreen_enabled = !setup.fullscreen;
10821 // when creating new renderer, destroy textures linked to old renderer
10822 FreeAllImageTextures(); // needs old renderer to free the textures
10824 // re-create screen and renderer (including change of vsync mode)
10825 ChangeVideoModeIfNeeded(setup.fullscreen);
10827 // set setup value according to successfully changed fullscreen mode
10828 setup.fullscreen = video.fullscreen_enabled;
10830 // restore backbuffer content from temporary backbuffer backup bitmap
10831 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10832 FreeBitmap(tmp_backbuffer);
10834 // update visible window/screen
10835 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10837 // when changing vsync mode, re-create textures for new renderer
10838 InitImageTextures();
10841 // set setup value according to successfully changed vsync mode
10842 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
10845 static void JoinRectangles(int *x, int *y, int *width, int *height,
10846 int x2, int y2, int width2, int height2)
10848 // do not join with "off-screen" rectangle
10849 if (x2 == -1 || y2 == -1)
10854 *width = MAX(*width, width2);
10855 *height = MAX(*height, height2);
10858 void SetAnimStatus(int anim_status_new)
10860 if (anim_status_new == GAME_MODE_MAIN)
10861 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
10862 else if (anim_status_new == GAME_MODE_NAMES)
10863 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
10864 else if (anim_status_new == GAME_MODE_SCORES)
10865 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
10867 global.anim_status_next = anim_status_new;
10869 // directly set screen modes that are entered without fading
10870 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
10871 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
10872 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
10873 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
10874 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
10875 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
10876 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
10877 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
10878 global.anim_status = global.anim_status_next;
10881 void SetGameStatus(int game_status_new)
10883 if (game_status_new != game_status)
10884 game_status_last_screen = game_status;
10886 game_status = game_status_new;
10888 SetAnimStatus(game_status_new);
10891 void SetFontStatus(int game_status_new)
10893 static int last_game_status = -1;
10895 if (game_status_new != -1)
10897 // set game status for font use after storing last game status
10898 last_game_status = game_status;
10899 game_status = game_status_new;
10903 // reset game status after font use from last stored game status
10904 game_status = last_game_status;
10908 void ResetFontStatus(void)
10913 void SetLevelSetInfo(char *identifier, int level_nr)
10915 setString(&levelset.identifier, identifier);
10917 levelset.level_nr = level_nr;
10920 boolean CheckIfAllViewportsHaveChanged(void)
10922 // if game status has not changed, viewports have not changed either
10923 if (game_status == game_status_last)
10926 // check if all viewports have changed with current game status
10928 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
10929 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
10930 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
10931 int new_real_sx = vp_playfield->x;
10932 int new_real_sy = vp_playfield->y;
10933 int new_full_sxsize = vp_playfield->width;
10934 int new_full_sysize = vp_playfield->height;
10935 int new_dx = vp_door_1->x;
10936 int new_dy = vp_door_1->y;
10937 int new_dxsize = vp_door_1->width;
10938 int new_dysize = vp_door_1->height;
10939 int new_vx = vp_door_2->x;
10940 int new_vy = vp_door_2->y;
10941 int new_vxsize = vp_door_2->width;
10942 int new_vysize = vp_door_2->height;
10944 boolean playfield_viewport_has_changed =
10945 (new_real_sx != REAL_SX ||
10946 new_real_sy != REAL_SY ||
10947 new_full_sxsize != FULL_SXSIZE ||
10948 new_full_sysize != FULL_SYSIZE);
10950 boolean door_1_viewport_has_changed =
10953 new_dxsize != DXSIZE ||
10954 new_dysize != DYSIZE);
10956 boolean door_2_viewport_has_changed =
10959 new_vxsize != VXSIZE ||
10960 new_vysize != VYSIZE ||
10961 game_status_last == GAME_MODE_EDITOR);
10963 return (playfield_viewport_has_changed &&
10964 door_1_viewport_has_changed &&
10965 door_2_viewport_has_changed);
10968 boolean CheckFadeAll(void)
10970 return (CheckIfGlobalBorderHasChanged() ||
10971 CheckIfAllViewportsHaveChanged());
10974 void ChangeViewportPropertiesIfNeeded(void)
10976 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
10977 FALSE : setup.small_game_graphics);
10978 int gfx_game_mode = getGlobalGameStatus(game_status);
10979 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
10981 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
10982 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
10983 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
10984 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
10985 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
10986 int new_win_xsize = vp_window->width;
10987 int new_win_ysize = vp_window->height;
10988 int border_left = vp_playfield->border_left;
10989 int border_right = vp_playfield->border_right;
10990 int border_top = vp_playfield->border_top;
10991 int border_bottom = vp_playfield->border_bottom;
10992 int new_sx = vp_playfield->x + border_left;
10993 int new_sy = vp_playfield->y + border_top;
10994 int new_sxsize = vp_playfield->width - border_left - border_right;
10995 int new_sysize = vp_playfield->height - border_top - border_bottom;
10996 int new_real_sx = vp_playfield->x;
10997 int new_real_sy = vp_playfield->y;
10998 int new_full_sxsize = vp_playfield->width;
10999 int new_full_sysize = vp_playfield->height;
11000 int new_dx = vp_door_1->x;
11001 int new_dy = vp_door_1->y;
11002 int new_dxsize = vp_door_1->width;
11003 int new_dysize = vp_door_1->height;
11004 int new_vx = vp_door_2->x;
11005 int new_vy = vp_door_2->y;
11006 int new_vxsize = vp_door_2->width;
11007 int new_vysize = vp_door_2->height;
11008 int new_ex = vp_door_3->x;
11009 int new_ey = vp_door_3->y;
11010 int new_exsize = vp_door_3->width;
11011 int new_eysize = vp_door_3->height;
11012 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11013 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11014 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11015 int new_scr_fieldx = new_sxsize / tilesize;
11016 int new_scr_fieldy = new_sysize / tilesize;
11017 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11018 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11019 boolean init_gfx_buffers = FALSE;
11020 boolean init_video_buffer = FALSE;
11021 boolean init_gadgets_and_anims = FALSE;
11022 boolean init_bd_graphics = FALSE;
11023 boolean init_em_graphics = FALSE;
11025 if (new_win_xsize != WIN_XSIZE ||
11026 new_win_ysize != WIN_YSIZE)
11028 WIN_XSIZE = new_win_xsize;
11029 WIN_YSIZE = new_win_ysize;
11031 init_video_buffer = TRUE;
11032 init_gfx_buffers = TRUE;
11033 init_gadgets_and_anims = TRUE;
11035 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11038 if (new_scr_fieldx != SCR_FIELDX ||
11039 new_scr_fieldy != SCR_FIELDY)
11041 // this always toggles between MAIN and GAME when using small tile size
11043 SCR_FIELDX = new_scr_fieldx;
11044 SCR_FIELDY = new_scr_fieldy;
11046 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11049 if (new_sx != SX ||
11057 new_sxsize != SXSIZE ||
11058 new_sysize != SYSIZE ||
11059 new_dxsize != DXSIZE ||
11060 new_dysize != DYSIZE ||
11061 new_vxsize != VXSIZE ||
11062 new_vysize != VYSIZE ||
11063 new_exsize != EXSIZE ||
11064 new_eysize != EYSIZE ||
11065 new_real_sx != REAL_SX ||
11066 new_real_sy != REAL_SY ||
11067 new_full_sxsize != FULL_SXSIZE ||
11068 new_full_sysize != FULL_SYSIZE ||
11069 new_tilesize_var != TILESIZE_VAR
11072 // ------------------------------------------------------------------------
11073 // determine next fading area for changed viewport definitions
11074 // ------------------------------------------------------------------------
11076 // start with current playfield area (default fading area)
11079 FADE_SXSIZE = FULL_SXSIZE;
11080 FADE_SYSIZE = FULL_SYSIZE;
11082 // add new playfield area if position or size has changed
11083 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11084 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11086 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11087 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11090 // add current and new door 1 area if position or size has changed
11091 if (new_dx != DX || new_dy != DY ||
11092 new_dxsize != DXSIZE || new_dysize != DYSIZE)
11094 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11095 DX, DY, DXSIZE, DYSIZE);
11096 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11097 new_dx, new_dy, new_dxsize, new_dysize);
11100 // add current and new door 2 area if position or size has changed
11101 if (new_vx != VX || new_vy != VY ||
11102 new_vxsize != VXSIZE || new_vysize != VYSIZE)
11104 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11105 VX, VY, VXSIZE, VYSIZE);
11106 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11107 new_vx, new_vy, new_vxsize, new_vysize);
11110 // ------------------------------------------------------------------------
11111 // handle changed tile size
11112 // ------------------------------------------------------------------------
11114 if (new_tilesize_var != TILESIZE_VAR)
11116 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11118 // changing tile size invalidates scroll values of engine snapshots
11119 FreeEngineSnapshotSingle();
11121 // changing tile size requires update of graphic mapping for BD/EM engine
11122 init_bd_graphics = TRUE;
11123 init_em_graphics = TRUE;
11134 SXSIZE = new_sxsize;
11135 SYSIZE = new_sysize;
11136 DXSIZE = new_dxsize;
11137 DYSIZE = new_dysize;
11138 VXSIZE = new_vxsize;
11139 VYSIZE = new_vysize;
11140 EXSIZE = new_exsize;
11141 EYSIZE = new_eysize;
11142 REAL_SX = new_real_sx;
11143 REAL_SY = new_real_sy;
11144 FULL_SXSIZE = new_full_sxsize;
11145 FULL_SYSIZE = new_full_sysize;
11146 TILESIZE_VAR = new_tilesize_var;
11148 init_gfx_buffers = TRUE;
11149 init_gadgets_and_anims = TRUE;
11151 // Debug("tools:viewport", "viewports: init_gfx_buffers");
11152 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11155 if (init_gfx_buffers)
11157 // Debug("tools:viewport", "init_gfx_buffers");
11159 SCR_FIELDX = new_scr_fieldx_buffers;
11160 SCR_FIELDY = new_scr_fieldy_buffers;
11164 SCR_FIELDX = new_scr_fieldx;
11165 SCR_FIELDY = new_scr_fieldy;
11167 SetDrawDeactivationMask(REDRAW_NONE);
11168 SetDrawBackgroundMask(REDRAW_FIELD);
11171 if (init_video_buffer)
11173 // Debug("tools:viewport", "init_video_buffer");
11175 FreeAllImageTextures(); // needs old renderer to free the textures
11177 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11178 InitImageTextures();
11181 if (init_gadgets_and_anims)
11183 // Debug("tools:viewport", "init_gadgets_and_anims");
11186 InitGlobalAnimations();
11189 if (init_bd_graphics)
11191 InitGraphicInfo_BD();
11194 if (init_em_graphics)
11196 InitGraphicInfo_EM();
11200 void OpenURL(char *url)
11202 #if SDL_VERSION_ATLEAST(2,0,14)
11205 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11206 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11207 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11211 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11213 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11217 // ============================================================================
11219 // ============================================================================
11221 #if defined(PLATFORM_WINDOWS)
11222 /* FILETIME of Jan 1 1970 00:00:00. */
11223 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11226 * timezone information is stored outside the kernel so tzp isn't used anymore.
11228 * Note: this function is not for Win32 high precision timing purpose. See
11231 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11233 FILETIME file_time;
11234 SYSTEMTIME system_time;
11235 ULARGE_INTEGER ularge;
11237 GetSystemTime(&system_time);
11238 SystemTimeToFileTime(&system_time, &file_time);
11239 ularge.LowPart = file_time.dwLowDateTime;
11240 ularge.HighPart = file_time.dwHighDateTime;
11242 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11243 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11249 static char *test_init_uuid_random_function_simple(void)
11251 static char seed_text[100];
11252 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
11254 sprintf(seed_text, "%d", seed);
11259 static char *test_init_uuid_random_function_better(void)
11261 static char seed_text[100];
11262 struct timeval current_time;
11264 gettimeofday(¤t_time, NULL);
11266 prng_seed_bytes(¤t_time, sizeof(current_time));
11268 sprintf(seed_text, "%ld.%ld",
11269 (long)current_time.tv_sec,
11270 (long)current_time.tv_usec);
11275 #if defined(PLATFORM_WINDOWS)
11276 static char *test_init_uuid_random_function_better_windows(void)
11278 static char seed_text[100];
11279 struct timeval current_time;
11281 gettimeofday_windows(¤t_time, NULL);
11283 prng_seed_bytes(¤t_time, sizeof(current_time));
11285 sprintf(seed_text, "%ld.%ld",
11286 (long)current_time.tv_sec,
11287 (long)current_time.tv_usec);
11293 static unsigned int test_uuid_random_function_simple(int max)
11295 return GetSimpleRandom(max);
11298 static unsigned int test_uuid_random_function_better(int max)
11300 return (max > 0 ? prng_get_uint() % max : 0);
11303 #if defined(PLATFORM_WINDOWS)
11304 #define NUM_UUID_TESTS 3
11306 #define NUM_UUID_TESTS 2
11309 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
11311 struct hashtable *hash_seeds =
11312 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
11313 struct hashtable *hash_uuids =
11314 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
11315 static char message[100];
11318 char *random_name = (nr == 0 ? "simple" : "better");
11319 char *random_type = (always_seed ? "always" : "only once");
11320 char *(*init_random_function)(void) =
11322 test_init_uuid_random_function_simple :
11323 test_init_uuid_random_function_better);
11324 unsigned int (*random_function)(int) =
11326 test_uuid_random_function_simple :
11327 test_uuid_random_function_better);
11330 #if defined(PLATFORM_WINDOWS)
11333 random_name = "windows";
11334 init_random_function = test_init_uuid_random_function_better_windows;
11340 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
11341 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
11343 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
11344 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
11345 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
11347 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
11351 // always initialize random number generator at least once
11352 init_random_function();
11354 unsigned int time_start = SDL_GetTicks();
11356 for (i = 0; i < num_uuids; i++)
11360 char *seed = getStringCopy(init_random_function());
11362 hashtable_remove(hash_seeds, seed);
11363 hashtable_insert(hash_seeds, seed, "1");
11366 char *uuid = getStringCopy(getUUIDExt(random_function));
11368 hashtable_remove(hash_uuids, uuid);
11369 hashtable_insert(hash_uuids, uuid, "1");
11372 int num_unique_seeds = hashtable_count(hash_seeds);
11373 int num_unique_uuids = hashtable_count(hash_uuids);
11375 unsigned int time_needed = SDL_GetTicks() - time_start;
11377 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
11379 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
11382 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
11384 if (nr == NUM_UUID_TESTS - 1 && always_seed)
11385 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
11387 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
11389 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
11391 Request(message, REQ_CONFIRM);
11393 hashtable_destroy(hash_seeds, 0);
11394 hashtable_destroy(hash_uuids, 0);
11397 void TestGeneratingUUIDs(void)
11399 int num_uuids = 1000000;
11402 for (i = 0; i < NUM_UUID_TESTS; i++)
11403 for (j = 0; j < 2; j++)
11404 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
11406 CloseAllAndExit(0);