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)
651 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
654 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
656 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
659 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
661 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
662 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
664 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
667 void BlitScreenToBitmap(Bitmap *target_bitmap)
669 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
670 BlitScreenToBitmap_EM(target_bitmap);
671 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
672 BlitScreenToBitmap_SP(target_bitmap);
673 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
674 BlitScreenToBitmap_MM(target_bitmap);
675 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
676 BlitScreenToBitmap_RND(target_bitmap);
678 redraw_mask |= REDRAW_FIELD;
681 static void DrawFramesPerSecond(void)
684 int font_nr = FONT_TEXT_2;
685 int font_width = getFontWidth(font_nr);
686 int draw_deactivation_mask = GetDrawDeactivationMask();
687 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
689 // draw FPS with leading space (needed if field buffer deactivated)
690 sprintf(text, " %04.1f fps", global.frames_per_second);
692 // override draw deactivation mask (required for invisible warp mode)
693 SetDrawDeactivationMask(REDRAW_NONE);
695 // draw opaque FPS if field buffer deactivated, else draw masked FPS
696 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
697 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
699 // set draw deactivation mask to previous value
700 SetDrawDeactivationMask(draw_deactivation_mask);
702 // force full-screen redraw in this frame
703 redraw_mask = REDRAW_ALL;
707 static void PrintFrameTimeDebugging(void)
709 static unsigned int last_counter = 0;
710 unsigned int counter = Counter();
711 int diff_1 = counter - last_counter;
712 int diff_2 = diff_1 - GAME_FRAME_DELAY;
714 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
715 char diff_bar[2 * diff_2_max + 5];
719 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
721 for (i = 0; i < diff_2_max; i++)
722 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
723 i >= diff_2_max - diff_2_cut ? '-' : ' ');
725 diff_bar[pos++] = '|';
727 for (i = 0; i < diff_2_max; i++)
728 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
730 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
732 diff_bar[pos++] = '\0';
734 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
737 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
740 last_counter = counter;
744 static int unifiedRedrawMask(int mask)
746 if (mask & REDRAW_ALL)
749 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
755 static boolean equalRedrawMasks(int mask_1, int mask_2)
757 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
760 void BackToFront(void)
762 static int last_redraw_mask = REDRAW_NONE;
764 // force screen redraw in every frame to continue drawing global animations
765 // (but always use the last redraw mask to prevent unwanted side effects)
766 if (redraw_mask == REDRAW_NONE)
767 redraw_mask = last_redraw_mask;
769 last_redraw_mask = redraw_mask;
772 // masked border now drawn immediately when blitting backbuffer to window
774 // draw masked border to all viewports, if defined
775 DrawMaskedBorder(redraw_mask);
778 // draw frames per second (only if debug mode is enabled)
779 if (redraw_mask & REDRAW_FPS)
780 DrawFramesPerSecond();
782 // remove playfield redraw before potentially merging with doors redraw
783 if (DrawingDeactivated(REAL_SX, REAL_SY))
784 redraw_mask &= ~REDRAW_FIELD;
786 // redraw complete window if both playfield and (some) doors need redraw
787 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
788 redraw_mask = REDRAW_ALL;
790 /* although redrawing the whole window would be fine for normal gameplay,
791 being able to only redraw the playfield is required for deactivating
792 certain drawing areas (mainly playfield) to work, which is needed for
793 warp-forward to be fast enough (by skipping redraw of most frames) */
795 if (redraw_mask & REDRAW_ALL)
797 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
799 else if (redraw_mask & REDRAW_FIELD)
801 BlitBitmap(backbuffer, window,
802 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
804 else if (redraw_mask & REDRAW_DOORS)
806 // merge door areas to prevent calling screen redraw more than once
812 if (redraw_mask & REDRAW_DOOR_1)
816 x2 = MAX(x2, DX + DXSIZE);
817 y2 = MAX(y2, DY + DYSIZE);
820 if (redraw_mask & REDRAW_DOOR_2)
824 x2 = MAX(x2, VX + VXSIZE);
825 y2 = MAX(y2, VY + VYSIZE);
828 if (redraw_mask & REDRAW_DOOR_3)
832 x2 = MAX(x2, EX + EXSIZE);
833 y2 = MAX(y2, EY + EYSIZE);
836 // make sure that at least one pixel is blitted, and inside the screen
837 // (else nothing is blitted, causing the animations not to be updated)
838 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
839 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
840 x2 = MIN(MAX(1, x2), WIN_XSIZE);
841 y2 = MIN(MAX(1, y2), WIN_YSIZE);
843 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
846 redraw_mask = REDRAW_NONE;
849 PrintFrameTimeDebugging();
853 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
855 unsigned int frame_delay_value_old = GetVideoFrameDelay();
857 SetVideoFrameDelay(frame_delay_value);
861 SetVideoFrameDelay(frame_delay_value_old);
864 static int fade_type_skip = FADE_TYPE_NONE;
866 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
868 void (*draw_border_function)(void) = NULL;
869 int x, y, width, height;
870 int fade_delay, post_delay;
872 if (fade_type == FADE_TYPE_FADE_OUT)
874 if (fade_type_skip != FADE_TYPE_NONE)
876 // skip all fade operations until specified fade operation
877 if (fade_type & fade_type_skip)
878 fade_type_skip = FADE_TYPE_NONE;
883 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
887 redraw_mask |= fade_mask;
889 if (fade_type == FADE_TYPE_SKIP)
891 fade_type_skip = fade_mode;
896 fade_delay = fading.fade_delay;
897 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
899 if (fade_type_skip != FADE_TYPE_NONE)
901 // skip all fade operations until specified fade operation
902 if (fade_type & fade_type_skip)
903 fade_type_skip = FADE_TYPE_NONE;
908 if (global.autoplay_leveldir)
913 if (fade_mask == REDRAW_FIELD)
918 height = FADE_SYSIZE;
920 if (border.draw_masked_when_fading)
921 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
923 DrawMaskedBorder_FIELD(); // draw once
933 // when switching screens without fading, set fade delay to zero
934 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
937 // do not display black frame when fading out without fade delay
938 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
941 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
942 draw_border_function);
944 redraw_mask &= ~fade_mask;
946 ClearAutoRepeatKeyEvents();
949 static void SetScreenStates_BeforeFadingIn(void)
951 // temporarily set screen mode for animations to screen after fading in
952 global.anim_status = global.anim_status_next;
954 // store backbuffer with all animations that will be started after fading in
955 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
957 // set screen mode for animations back to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
961 static void SetScreenStates_AfterFadingIn(void)
963 // store new source screen (to use correct masked border for fading)
964 gfx.fade_border_source_status = global.border_status;
966 global.anim_status = global.anim_status_next;
969 static void SetScreenStates_BeforeFadingOut(void)
971 // store new target screen (to use correct masked border for fading)
972 gfx.fade_border_target_status = game_status;
974 // set screen mode for animations to fading
975 global.anim_status = GAME_MODE_PSEUDO_FADING;
977 // store backbuffer with all animations that will be stopped for fading out
978 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
981 static void SetScreenStates_AfterFadingOut(void)
983 global.border_status = game_status;
985 // always use global border for PLAYING when restarting the game
986 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
987 global.border_status = GAME_MODE_PLAYING;
990 void FadeIn(int fade_mask)
992 SetScreenStates_BeforeFadingIn();
995 DrawMaskedBorder(REDRAW_ALL);
998 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
999 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1001 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1005 FADE_SXSIZE = FULL_SXSIZE;
1006 FADE_SYSIZE = FULL_SYSIZE;
1008 // activate virtual buttons depending on upcoming game status
1009 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1010 game_status == GAME_MODE_PLAYING && !tape.playing)
1011 SetOverlayActive(TRUE);
1013 SetScreenStates_AfterFadingIn();
1015 // force update of global animation status in case of rapid screen changes
1016 redraw_mask = REDRAW_ALL;
1020 void FadeOut(int fade_mask)
1022 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1023 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1024 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1027 SetScreenStates_BeforeFadingOut();
1029 SetTileCursorActive(FALSE);
1030 SetOverlayActive(FALSE);
1033 DrawMaskedBorder(REDRAW_ALL);
1036 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1037 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1039 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1041 SetScreenStates_AfterFadingOut();
1044 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1046 static struct TitleFadingInfo fading_leave_stored;
1049 fading_leave_stored = fading_leave;
1051 fading = fading_leave_stored;
1054 void FadeSetEnterMenu(void)
1056 fading = menu.enter_menu;
1058 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1061 void FadeSetLeaveMenu(void)
1063 fading = menu.leave_menu;
1065 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1068 void FadeSetEnterScreen(void)
1070 fading = menu.enter_screen[game_status];
1072 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1075 void FadeSetNextScreen(void)
1077 fading = menu.next_screen[game_status];
1079 // (do not overwrite fade mode set by FadeSetEnterScreen)
1080 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1083 void FadeSetLeaveScreen(void)
1085 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1088 void FadeSetFromType(int type)
1090 if (type & TYPE_ENTER_SCREEN)
1091 FadeSetEnterScreen();
1092 else if (type & TYPE_ENTER)
1094 else if (type & TYPE_LEAVE)
1098 void FadeSetDisabled(void)
1100 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1102 fading = fading_none;
1105 void FadeSkipNextFadeIn(void)
1107 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1110 void FadeSkipNextFadeOut(void)
1112 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1115 static int getGlobalGameStatus(int status)
1117 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1118 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1122 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1124 if (graphic == IMG_UNDEFINED)
1125 return IMG_UNDEFINED;
1127 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1129 return (graphic_info[graphic].bitmap != NULL || redefined ?
1130 graphic : default_graphic);
1133 static int getBackgroundImage(int graphic)
1135 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1138 static int getGlobalBorderImage(int graphic)
1140 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1143 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1145 int status = getGlobalGameStatus(status_raw);
1147 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1148 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1149 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1150 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1152 int graphic_final = getGlobalBorderImage(graphic);
1154 return graphic_info[graphic_final].bitmap;
1157 void SetBackgroundImage(int graphic, int redraw_mask)
1159 struct GraphicInfo *g = &graphic_info[graphic];
1160 struct GraphicInfo g_undefined = { 0 };
1162 if (graphic == IMG_UNDEFINED)
1165 // always use original size bitmap for backgrounds, if existing
1166 Bitmap *bitmap = (g->bitmaps != NULL &&
1167 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1168 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1170 // remove every mask before setting mask for window, and
1171 // remove window area mask before setting mask for main or door area
1172 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1174 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1175 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1176 SetBackgroundBitmap(bitmap, redraw_mask,
1178 g->width, g->height);
1181 void SetWindowBackgroundImageIfDefined(int graphic)
1183 if (graphic_info[graphic].bitmap)
1184 SetBackgroundImage(graphic, REDRAW_ALL);
1187 void SetMainBackgroundImageIfDefined(int graphic)
1189 if (graphic_info[graphic].bitmap)
1190 SetBackgroundImage(graphic, REDRAW_FIELD);
1193 void SetDoorBackgroundImageIfDefined(int graphic)
1195 if (graphic_info[graphic].bitmap)
1196 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1199 void SetWindowBackgroundImage(int graphic)
1201 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1204 void SetMainBackgroundImage(int graphic)
1206 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1209 void SetDoorBackgroundImage(int graphic)
1211 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1214 void SetPanelBackground(void)
1216 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1219 void DrawBackground(int x, int y, int width, int height)
1221 // "drawto" might still point to playfield buffer here (hall of fame)
1222 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1224 if (IN_GFX_FIELD_FULL(x, y))
1225 redraw_mask |= REDRAW_FIELD;
1226 else if (IN_GFX_DOOR_1(x, y))
1227 redraw_mask |= REDRAW_DOOR_1;
1228 else if (IN_GFX_DOOR_2(x, y))
1229 redraw_mask |= REDRAW_DOOR_2;
1230 else if (IN_GFX_DOOR_3(x, y))
1231 redraw_mask |= REDRAW_DOOR_3;
1234 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1236 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1238 if (font->bitmap == NULL)
1241 DrawBackground(x, y, width, height);
1244 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1246 struct GraphicInfo *g = &graphic_info[graphic];
1248 if (g->bitmap == NULL)
1251 DrawBackground(x, y, width, height);
1254 static int game_status_last = -1;
1255 static Bitmap *global_border_bitmap_last = NULL;
1256 static Bitmap *global_border_bitmap = NULL;
1257 static int real_sx_last = -1, real_sy_last = -1;
1258 static int full_sxsize_last = -1, full_sysize_last = -1;
1259 static int dx_last = -1, dy_last = -1;
1260 static int dxsize_last = -1, dysize_last = -1;
1261 static int vx_last = -1, vy_last = -1;
1262 static int vxsize_last = -1, vysize_last = -1;
1263 static int ex_last = -1, ey_last = -1;
1264 static int exsize_last = -1, eysize_last = -1;
1266 boolean CheckIfGlobalBorderHasChanged(void)
1268 // if game status has not changed, global border has not changed either
1269 if (game_status == game_status_last)
1272 // determine and store new global border bitmap for current game status
1273 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1275 return (global_border_bitmap_last != global_border_bitmap);
1278 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1280 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1281 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1283 // if game status has not changed, nothing has to be redrawn
1284 if (game_status == game_status_last)
1287 // redraw if last screen was title screen
1288 if (game_status_last == GAME_MODE_TITLE)
1291 // redraw if global screen border has changed
1292 if (CheckIfGlobalBorderHasChanged())
1295 // redraw if position or size of playfield area has changed
1296 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1297 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1300 // redraw if position or size of door area has changed
1301 if (dx_last != DX || dy_last != DY ||
1302 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1305 // redraw if position or size of tape area has changed
1306 if (vx_last != VX || vy_last != VY ||
1307 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1310 // redraw if position or size of editor area has changed
1311 if (ex_last != EX || ey_last != EY ||
1312 exsize_last != EXSIZE || eysize_last != EYSIZE)
1319 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1322 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1324 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1327 void RedrawGlobalBorder(void)
1329 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1331 RedrawGlobalBorderFromBitmap(bitmap);
1333 redraw_mask = REDRAW_ALL;
1336 static void RedrawGlobalBorderIfNeeded(void)
1338 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1339 if (game_status == game_status_last)
1343 // copy current draw buffer to later copy back areas that have not changed
1344 if (game_status_last != GAME_MODE_TITLE)
1345 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1347 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1348 if (CheckIfGlobalBorderRedrawIsNeeded())
1350 // determine and store new global border bitmap for current game status
1351 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1354 // redraw global screen border (or clear, if defined to be empty)
1355 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1357 if (game_status == GAME_MODE_EDITOR)
1358 DrawSpecialEditorDoor();
1360 // copy previous playfield and door areas, if they are defined on both
1361 // previous and current screen and if they still have the same size
1363 if (real_sx_last != -1 && real_sy_last != -1 &&
1364 REAL_SX != -1 && REAL_SY != -1 &&
1365 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1366 BlitBitmap(bitmap_db_store_1, backbuffer,
1367 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1370 if (dx_last != -1 && dy_last != -1 &&
1371 DX != -1 && DY != -1 &&
1372 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1373 BlitBitmap(bitmap_db_store_1, backbuffer,
1374 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1376 if (game_status != GAME_MODE_EDITOR)
1378 if (vx_last != -1 && vy_last != -1 &&
1379 VX != -1 && VY != -1 &&
1380 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1381 BlitBitmap(bitmap_db_store_1, backbuffer,
1382 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1386 if (ex_last != -1 && ey_last != -1 &&
1387 EX != -1 && EY != -1 &&
1388 exsize_last == EXSIZE && eysize_last == EYSIZE)
1389 BlitBitmap(bitmap_db_store_1, backbuffer,
1390 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1393 redraw_mask = REDRAW_ALL;
1396 game_status_last = game_status;
1398 global_border_bitmap_last = global_border_bitmap;
1400 real_sx_last = REAL_SX;
1401 real_sy_last = REAL_SY;
1402 full_sxsize_last = FULL_SXSIZE;
1403 full_sysize_last = FULL_SYSIZE;
1406 dxsize_last = DXSIZE;
1407 dysize_last = DYSIZE;
1410 vxsize_last = VXSIZE;
1411 vysize_last = VYSIZE;
1414 exsize_last = EXSIZE;
1415 eysize_last = EYSIZE;
1418 void ClearField(void)
1420 RedrawGlobalBorderIfNeeded();
1422 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1423 // (when entering hall of fame after playing)
1424 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1426 // !!! maybe this should be done before clearing the background !!!
1427 if (game_status == GAME_MODE_PLAYING)
1429 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1430 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1434 SetDrawtoField(DRAW_TO_BACKBUFFER);
1438 void MarkTileDirty(int x, int y)
1440 redraw_mask |= REDRAW_FIELD;
1443 void SetBorderElement(void)
1447 BorderElement = EL_EMPTY;
1449 // only the R'n'D game engine may use an additional steelwall border
1450 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1453 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1455 for (x = 0; x < lev_fieldx; x++)
1457 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1458 BorderElement = EL_STEELWALL;
1460 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1466 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1467 int max_array_fieldx, int max_array_fieldy,
1468 short field[max_array_fieldx][max_array_fieldy],
1469 int max_fieldx, int max_fieldy)
1471 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1472 struct XY *check = xy_topdown;
1473 int old_element = field[start_x][start_y];
1476 // do nothing if start field already has the desired content
1477 if (old_element == fill_element)
1480 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1482 while (stack_pos > 0)
1484 struct XY current = stack_buffer[--stack_pos];
1487 field[current.x][current.y] = fill_element;
1489 for (i = 0; i < 4; i++)
1491 int x = current.x + check[i].x;
1492 int y = current.y + check[i].y;
1494 // check for stack buffer overflow (should not happen)
1495 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1496 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1498 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1499 stack_buffer[stack_pos++] = (struct XY){ x, y };
1504 void FloodFillLevel(int from_x, int from_y, int fill_element,
1505 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1506 int max_fieldx, int max_fieldy)
1508 FloodFillLevelExt(from_x, from_y, fill_element,
1509 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1510 max_fieldx, max_fieldy);
1513 void SetRandomAnimationValue(int x, int y)
1515 gfx.anim_random_frame = GfxRandom[x][y];
1518 int getGraphicAnimationFrame(int graphic, int sync_frame)
1520 // animation synchronized with global frame counter, not move position
1521 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1522 sync_frame = FrameCounter;
1523 else if (graphic_info[graphic].anim_global_anim_sync)
1524 sync_frame = getGlobalAnimSyncFrame();
1526 return getAnimationFrame(graphic_info[graphic].anim_frames,
1527 graphic_info[graphic].anim_delay,
1528 graphic_info[graphic].anim_mode,
1529 graphic_info[graphic].anim_start_frame,
1533 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1535 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1537 struct GraphicInfo *g = &graphic_info[graphic];
1538 int xsize = MAX(1, g->anim_frames_per_line);
1539 int ysize = MAX(1, g->anim_frames / xsize);
1540 int xoffset = g->anim_start_frame % xsize;
1541 int yoffset = g->anim_start_frame % ysize;
1542 // may be needed if screen field is significantly larger than playfield
1543 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1544 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1545 int sync_frame = y * xsize + x;
1547 return sync_frame % g->anim_frames;
1549 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1551 struct GraphicInfo *g = &graphic_info[graphic];
1552 // may be needed if screen field is significantly larger than playfield
1553 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1554 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1555 int sync_frame = GfxRandomStatic[x][y];
1557 return sync_frame % g->anim_frames;
1561 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1563 return getGraphicAnimationFrame(graphic, sync_frame);
1567 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1569 struct GraphicInfo *g = &graphic_info[graphic];
1570 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1572 if (tilesize == gfx.standard_tile_size)
1573 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1574 else if (tilesize == game.tile_size)
1575 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1577 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1580 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1581 boolean get_backside)
1583 struct GraphicInfo *g = &graphic_info[graphic];
1584 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1585 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1587 if (g->offset_y == 0) // frames are ordered horizontally
1589 int max_width = g->anim_frames_per_line * g->width;
1590 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1592 *x = pos % max_width;
1593 *y = src_y % g->height + pos / max_width * g->height;
1595 else if (g->offset_x == 0) // frames are ordered vertically
1597 int max_height = g->anim_frames_per_line * g->height;
1598 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1600 *x = src_x % g->width + pos / max_height * g->width;
1601 *y = pos % max_height;
1603 else // frames are ordered diagonally
1605 *x = src_x + frame * g->offset_x;
1606 *y = src_y + frame * g->offset_y;
1610 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1611 Bitmap **bitmap, int *x, int *y,
1612 boolean get_backside)
1614 struct GraphicInfo *g = &graphic_info[graphic];
1616 // if no graphics defined at all, use fallback graphics
1617 if (g->bitmaps == NULL)
1618 *g = graphic_info[IMG_CHAR_EXCLAM];
1620 // if no in-game graphics defined, always use standard graphic size
1621 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1622 tilesize = TILESIZE;
1624 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1625 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1627 *x = *x * tilesize / g->tile_size;
1628 *y = *y * tilesize / g->tile_size;
1631 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1632 Bitmap **bitmap, int *x, int *y)
1634 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1637 void getFixedGraphicSource(int graphic, int frame,
1638 Bitmap **bitmap, int *x, int *y)
1640 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1643 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1645 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1648 void getGlobalAnimGraphicSource(int graphic, int frame,
1649 Bitmap **bitmap, int *x, int *y)
1651 struct GraphicInfo *g = &graphic_info[graphic];
1653 // if no graphics defined at all, use fallback graphics
1654 if (g->bitmaps == NULL)
1655 *g = graphic_info[IMG_CHAR_EXCLAM];
1657 // use original size graphics, if existing, else use standard size graphics
1658 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1659 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1661 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1663 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1666 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1667 int *x, int *y, boolean get_backside)
1669 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1673 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1675 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1678 void DrawGraphic(int x, int y, int graphic, int frame)
1681 if (!IN_SCR_FIELD(x, y))
1683 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1684 Debug("draw:DrawGraphic", "This should never happen!");
1690 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1693 MarkTileDirty(x, y);
1696 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1699 if (!IN_SCR_FIELD(x, y))
1701 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1703 Debug("draw:DrawFixedGraphic", "This should never happen!");
1709 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1711 MarkTileDirty(x, y);
1714 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1720 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1722 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1725 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1731 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1732 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1735 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1738 if (!IN_SCR_FIELD(x, y))
1740 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1742 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1748 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1751 MarkTileDirty(x, y);
1754 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1757 if (!IN_SCR_FIELD(x, y))
1759 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1761 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1767 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1769 MarkTileDirty(x, y);
1772 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1778 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1780 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1784 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1785 int graphic, int frame)
1790 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1792 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1796 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1798 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1800 MarkTileDirty(x / tilesize, y / tilesize);
1803 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1806 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1807 graphic, frame, tilesize);
1808 MarkTileDirty(x / tilesize, y / tilesize);
1811 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1817 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1818 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1821 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1822 int frame, int tilesize)
1827 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1828 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1831 void DrawMiniGraphic(int x, int y, int graphic)
1833 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1834 MarkTileDirty(x / 2, y / 2);
1837 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1842 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1843 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1846 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1847 int graphic, int frame,
1848 int cut_mode, int mask_mode)
1853 int width = TILEX, height = TILEY;
1856 if (dx || dy) // shifted graphic
1858 if (x < BX1) // object enters playfield from the left
1865 else if (x > BX2) // object enters playfield from the right
1871 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1877 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1879 else if (dx) // general horizontal movement
1880 MarkTileDirty(x + SIGN(dx), y);
1882 if (y < BY1) // object enters playfield from the top
1884 if (cut_mode == CUT_BELOW) // object completely above top border
1892 else if (y > BY2) // object enters playfield from the bottom
1898 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1904 else if (dy > 0 && cut_mode == CUT_ABOVE)
1906 if (y == BY2) // object completely above bottom border
1912 MarkTileDirty(x, y + 1);
1913 } // object leaves playfield to the bottom
1914 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1916 else if (dy) // general vertical movement
1917 MarkTileDirty(x, y + SIGN(dy));
1921 if (!IN_SCR_FIELD(x, y))
1923 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1925 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1931 width = width * TILESIZE_VAR / TILESIZE;
1932 height = height * TILESIZE_VAR / TILESIZE;
1933 cx = cx * TILESIZE_VAR / TILESIZE;
1934 cy = cy * TILESIZE_VAR / TILESIZE;
1935 dx = dx * TILESIZE_VAR / TILESIZE;
1936 dy = dy * TILESIZE_VAR / TILESIZE;
1938 if (width > 0 && height > 0)
1940 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1945 dst_x = FX + x * TILEX_VAR + dx;
1946 dst_y = FY + y * TILEY_VAR + dy;
1948 if (mask_mode == USE_MASKING)
1949 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1952 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1955 MarkTileDirty(x, y);
1959 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1960 int graphic, int frame,
1961 int cut_mode, int mask_mode)
1966 int width = TILEX_VAR, height = TILEY_VAR;
1969 int x2 = x + SIGN(dx);
1970 int y2 = y + SIGN(dy);
1972 // movement with two-tile animations must be sync'ed with movement position,
1973 // not with current GfxFrame (which can be higher when using slow movement)
1974 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1975 int anim_frames = graphic_info[graphic].anim_frames;
1977 // (we also need anim_delay here for movement animations with less frames)
1978 int anim_delay = graphic_info[graphic].anim_delay;
1979 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1981 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1982 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1984 // re-calculate animation frame for two-tile movement animation
1985 frame = getGraphicAnimationFrame(graphic, sync_frame);
1987 // check if movement start graphic inside screen area and should be drawn
1988 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1990 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1992 dst_x = FX + x1 * TILEX_VAR;
1993 dst_y = FY + y1 * TILEY_VAR;
1995 if (mask_mode == USE_MASKING)
1996 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1999 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2002 MarkTileDirty(x1, y1);
2005 // check if movement end graphic inside screen area and should be drawn
2006 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2008 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2010 dst_x = FX + x2 * TILEX_VAR;
2011 dst_y = FY + y2 * TILEY_VAR;
2013 if (mask_mode == USE_MASKING)
2014 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2017 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2020 MarkTileDirty(x2, y2);
2024 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2025 int graphic, int frame,
2026 int cut_mode, int mask_mode)
2030 DrawGraphic(x, y, graphic, frame);
2035 if (graphic_info[graphic].double_movement) // EM style movement images
2036 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2038 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2041 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2042 int graphic, int frame, int cut_mode)
2044 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2047 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2048 int cut_mode, int mask_mode)
2050 int lx = LEVELX(x), ly = LEVELY(y);
2054 if (IN_LEV_FIELD(lx, ly))
2056 if (element == EL_EMPTY)
2057 element = GfxElementEmpty[lx][ly];
2059 SetRandomAnimationValue(lx, ly);
2061 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2062 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2064 // do not use double (EM style) movement graphic when not moving
2065 if (graphic_info[graphic].double_movement && !dx && !dy)
2067 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2068 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2071 if (game.use_masked_elements && (dx || dy))
2072 mask_mode = USE_MASKING;
2074 else // border element
2076 graphic = el2img(element);
2077 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2080 if (element == EL_EXPANDABLE_WALL)
2082 boolean left_stopped = FALSE, right_stopped = FALSE;
2084 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2085 left_stopped = TRUE;
2086 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2087 right_stopped = TRUE;
2089 if (left_stopped && right_stopped)
2091 else if (left_stopped)
2093 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2094 frame = graphic_info[graphic].anim_frames - 1;
2096 else if (right_stopped)
2098 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2099 frame = graphic_info[graphic].anim_frames - 1;
2104 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2105 else if (mask_mode == USE_MASKING)
2106 DrawGraphicThruMask(x, y, graphic, frame);
2108 DrawGraphic(x, y, graphic, frame);
2111 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2112 int cut_mode, int mask_mode)
2114 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2115 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2116 cut_mode, mask_mode);
2119 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2122 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2125 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2128 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2131 void DrawLevelElementThruMask(int x, int y, int element)
2133 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2136 void DrawLevelFieldThruMask(int x, int y)
2138 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2141 // !!! implementation of quicksand is totally broken !!!
2142 #define IS_CRUMBLED_TILE(x, y, e) \
2143 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2144 !IS_MOVING(x, y) || \
2145 (e) == EL_QUICKSAND_EMPTYING || \
2146 (e) == EL_QUICKSAND_FAST_EMPTYING))
2148 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2153 int width, height, cx, cy;
2154 int sx = SCREENX(x), sy = SCREENY(y);
2155 int crumbled_border_size = graphic_info[graphic].border_size;
2156 int crumbled_tile_size = graphic_info[graphic].tile_size;
2157 int crumbled_border_size_var =
2158 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2161 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2163 for (i = 1; i < 4; i++)
2165 int dxx = (i & 1 ? dx : 0);
2166 int dyy = (i & 2 ? dy : 0);
2169 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2172 // check if neighbour field is of same crumble type
2173 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2174 graphic_info[graphic].class ==
2175 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2177 // return if check prevents inner corner
2178 if (same == (dxx == dx && dyy == dy))
2182 // if we reach this point, we have an inner corner
2184 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2186 width = crumbled_border_size_var;
2187 height = crumbled_border_size_var;
2188 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2189 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2191 if (game.use_masked_elements)
2193 int graphic0 = el2img(EL_EMPTY);
2194 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2195 Bitmap *src_bitmap0;
2198 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2200 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2202 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2204 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2206 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2209 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2211 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2214 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2219 int width, height, bx, by, cx, cy;
2220 int sx = SCREENX(x), sy = SCREENY(y);
2221 int crumbled_border_size = graphic_info[graphic].border_size;
2222 int crumbled_tile_size = graphic_info[graphic].tile_size;
2223 int crumbled_border_size_var =
2224 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2225 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2228 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2230 // only needed when using masked elements
2231 int graphic0 = el2img(EL_EMPTY);
2232 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2233 Bitmap *src_bitmap0;
2236 if (game.use_masked_elements)
2237 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2239 // draw simple, sloppy, non-corner-accurate crumbled border
2241 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2242 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2243 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2244 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2246 if (game.use_masked_elements)
2248 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2250 FX + sx * TILEX_VAR + cx,
2251 FY + sy * TILEY_VAR + cy);
2253 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2255 FX + sx * TILEX_VAR + cx,
2256 FY + sy * TILEY_VAR + cy);
2259 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2261 FX + sx * TILEX_VAR + cx,
2262 FY + sy * TILEY_VAR + cy);
2264 // (remaining middle border part must be at least as big as corner part)
2265 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2266 crumbled_border_size_var >= TILESIZE_VAR / 3)
2269 // correct corners of crumbled border, if needed
2271 for (i = -1; i <= 1; i += 2)
2273 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2274 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2275 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2278 // check if neighbour field is of same crumble type
2279 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2280 graphic_info[graphic].class ==
2281 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2283 // no crumbled corner, but continued crumbled border
2285 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2286 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2287 int b1 = (i == 1 ? crumbled_border_size_var :
2288 TILESIZE_VAR - 2 * crumbled_border_size_var);
2290 width = crumbled_border_size_var;
2291 height = crumbled_border_size_var;
2293 if (dir == 1 || dir == 2)
2308 if (game.use_masked_elements)
2310 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2312 FX + sx * TILEX_VAR + cx,
2313 FY + sy * TILEY_VAR + cy);
2315 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2317 FX + sx * TILEX_VAR + cx,
2318 FY + sy * TILEY_VAR + cy);
2321 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2323 FX + sx * TILEX_VAR + cx,
2324 FY + sy * TILEY_VAR + cy);
2329 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2331 int sx = SCREENX(x), sy = SCREENY(y);
2334 struct XY *xy = xy_topdown;
2336 if (!IN_LEV_FIELD(x, y))
2339 element = TILE_GFX_ELEMENT(x, y);
2341 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2343 if (!IN_SCR_FIELD(sx, sy))
2346 // crumble field borders towards direct neighbour fields
2347 for (i = 0; i < 4; i++)
2349 int xx = x + xy[i].x;
2350 int yy = y + xy[i].y;
2352 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2355 // check if neighbour field is of same crumble type
2356 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2357 graphic_info[graphic].class ==
2358 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2361 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2364 // crumble inner field corners towards corner neighbour fields
2365 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2366 graphic_info[graphic].anim_frames == 2)
2368 for (i = 0; i < 4; i++)
2370 int dx = (i & 1 ? +1 : -1);
2371 int dy = (i & 2 ? +1 : -1);
2373 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2377 MarkTileDirty(sx, sy);
2379 else // center field is not crumbled -- crumble neighbour fields
2381 // crumble field borders of direct neighbour fields
2382 for (i = 0; i < 4; i++)
2384 int xx = x + xy[i].x;
2385 int yy = y + xy[i].y;
2386 int sxx = sx + xy[i].x;
2387 int syy = sy + xy[i].y;
2389 if (!IN_LEV_FIELD(xx, yy) ||
2390 !IN_SCR_FIELD(sxx, syy))
2393 // do not crumble fields that are being digged or snapped
2394 if (Tile[xx][yy] == EL_EMPTY ||
2395 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2398 element = TILE_GFX_ELEMENT(xx, yy);
2400 if (!IS_CRUMBLED_TILE(xx, yy, element))
2403 graphic = el_act2crm(element, ACTION_DEFAULT);
2405 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2407 MarkTileDirty(sxx, syy);
2410 // crumble inner field corners of corner neighbour fields
2411 for (i = 0; i < 4; i++)
2413 int dx = (i & 1 ? +1 : -1);
2414 int dy = (i & 2 ? +1 : -1);
2420 if (!IN_LEV_FIELD(xx, yy) ||
2421 !IN_SCR_FIELD(sxx, syy))
2424 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2427 element = TILE_GFX_ELEMENT(xx, yy);
2429 if (!IS_CRUMBLED_TILE(xx, yy, element))
2432 graphic = el_act2crm(element, ACTION_DEFAULT);
2434 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2435 graphic_info[graphic].anim_frames == 2)
2436 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2438 MarkTileDirty(sxx, syy);
2443 void DrawLevelFieldCrumbled(int x, int y)
2447 if (!IN_LEV_FIELD(x, y))
2450 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2451 GfxElement[x][y] != EL_UNDEFINED &&
2452 GFX_CRUMBLED(GfxElement[x][y]))
2454 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2459 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2461 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2464 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2467 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2468 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2469 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2470 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2471 int sx = SCREENX(x), sy = SCREENY(y);
2473 DrawScreenGraphic(sx, sy, graphic1, frame1);
2474 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2477 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2479 int sx = SCREENX(x), sy = SCREENY(y);
2480 struct XY *xy = xy_topdown;
2483 // crumble direct neighbour fields (required for field borders)
2484 for (i = 0; i < 4; i++)
2486 int xx = x + xy[i].x;
2487 int yy = y + xy[i].y;
2488 int sxx = sx + xy[i].x;
2489 int syy = sy + xy[i].y;
2491 if (!IN_LEV_FIELD(xx, yy) ||
2492 !IN_SCR_FIELD(sxx, syy) ||
2493 !GFX_CRUMBLED(Tile[xx][yy]) ||
2497 DrawLevelField(xx, yy);
2500 // crumble corner neighbour fields (required for inner field corners)
2501 for (i = 0; i < 4; i++)
2503 int dx = (i & 1 ? +1 : -1);
2504 int dy = (i & 2 ? +1 : -1);
2510 if (!IN_LEV_FIELD(xx, yy) ||
2511 !IN_SCR_FIELD(sxx, syy) ||
2512 !GFX_CRUMBLED(Tile[xx][yy]) ||
2516 int element = TILE_GFX_ELEMENT(xx, yy);
2517 int graphic = el_act2crm(element, ACTION_DEFAULT);
2519 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2520 graphic_info[graphic].anim_frames == 2)
2521 DrawLevelField(xx, yy);
2525 static int getBorderElement(int x, int y)
2529 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2530 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2531 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2532 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2533 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2534 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2535 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2537 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2538 int steel_position = (x == -1 && y == -1 ? 0 :
2539 x == lev_fieldx && y == -1 ? 1 :
2540 x == -1 && y == lev_fieldy ? 2 :
2541 x == lev_fieldx && y == lev_fieldy ? 3 :
2542 x == -1 || x == lev_fieldx ? 4 :
2543 y == -1 || y == lev_fieldy ? 5 : 6);
2545 return border[steel_position][steel_type];
2548 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2550 if (game.use_masked_elements)
2552 if (graphic != el2img(EL_EMPTY))
2553 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2555 DrawGraphicThruMask(x, y, graphic, frame);
2559 DrawGraphic(x, y, graphic, frame);
2563 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2565 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2568 void DrawScreenElement(int x, int y, int element)
2570 int mask_mode = NO_MASKING;
2572 if (game.use_masked_elements)
2574 int lx = LEVELX(x), ly = LEVELY(y);
2576 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2578 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2580 mask_mode = USE_MASKING;
2584 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2585 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2588 void DrawLevelElement(int x, int y, int element)
2590 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2591 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2594 void DrawScreenField(int x, int y)
2596 int lx = LEVELX(x), ly = LEVELY(y);
2597 int element, content;
2599 if (!IN_LEV_FIELD(lx, ly))
2601 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2604 element = getBorderElement(lx, ly);
2606 DrawScreenElement(x, y, element);
2611 element = Tile[lx][ly];
2612 content = Store[lx][ly];
2614 if (IS_MOVING(lx, ly))
2616 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2617 boolean cut_mode = NO_CUTTING;
2619 if (element == EL_QUICKSAND_EMPTYING ||
2620 element == EL_QUICKSAND_FAST_EMPTYING ||
2621 element == EL_MAGIC_WALL_EMPTYING ||
2622 element == EL_BD_MAGIC_WALL_EMPTYING ||
2623 element == EL_DC_MAGIC_WALL_EMPTYING ||
2624 element == EL_AMOEBA_DROPPING)
2625 cut_mode = CUT_ABOVE;
2626 else if (element == EL_QUICKSAND_FILLING ||
2627 element == EL_QUICKSAND_FAST_FILLING ||
2628 element == EL_MAGIC_WALL_FILLING ||
2629 element == EL_BD_MAGIC_WALL_FILLING ||
2630 element == EL_DC_MAGIC_WALL_FILLING)
2631 cut_mode = CUT_BELOW;
2633 if (cut_mode == CUT_ABOVE)
2634 DrawScreenElement(x, y, element);
2636 DrawScreenElement(x, y, EL_EMPTY);
2638 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2640 int dir = MovDir[lx][ly];
2641 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2642 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2644 if (IN_SCR_FIELD(newx, newy))
2645 DrawScreenElement(newx, newy, EL_EMPTY);
2649 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2650 else if (cut_mode == NO_CUTTING)
2651 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2654 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2656 if (cut_mode == CUT_BELOW &&
2657 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2658 DrawLevelElement(lx, ly + 1, element);
2661 if (content == EL_ACID)
2663 int dir = MovDir[lx][ly];
2664 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2665 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2667 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2669 // prevent target field from being drawn again (but without masking)
2670 // (this would happen if target field is scanned after moving element)
2671 Stop[newlx][newly] = TRUE;
2674 else if (IS_BLOCKED(lx, ly))
2679 boolean cut_mode = NO_CUTTING;
2680 int element_old, content_old;
2682 Blocked2Moving(lx, ly, &oldx, &oldy);
2685 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2686 MovDir[oldx][oldy] == MV_RIGHT);
2688 element_old = Tile[oldx][oldy];
2689 content_old = Store[oldx][oldy];
2691 if (element_old == EL_QUICKSAND_EMPTYING ||
2692 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2693 element_old == EL_MAGIC_WALL_EMPTYING ||
2694 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2695 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2696 element_old == EL_AMOEBA_DROPPING)
2697 cut_mode = CUT_ABOVE;
2699 DrawScreenElement(x, y, EL_EMPTY);
2702 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2704 else if (cut_mode == NO_CUTTING)
2705 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2708 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2711 else if (IS_DRAWABLE(element))
2712 DrawScreenElement(x, y, element);
2714 DrawScreenElement(x, y, EL_EMPTY);
2717 void DrawLevelField(int x, int y)
2719 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2720 DrawScreenField(SCREENX(x), SCREENY(y));
2721 else if (IS_MOVING(x, y))
2725 Moving2Blocked(x, y, &newx, &newy);
2726 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2727 DrawScreenField(SCREENX(newx), SCREENY(newy));
2729 else if (IS_BLOCKED(x, y))
2733 Blocked2Moving(x, y, &oldx, &oldy);
2734 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2735 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2739 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2740 int (*el2img_function)(int), boolean masked,
2741 int element_bits_draw)
2743 int element_base = map_mm_wall_element(element);
2744 int element_bits = (IS_DF_WALL(element) ?
2745 element - EL_DF_WALL_START :
2746 IS_MM_WALL(element) ?
2747 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2748 int graphic = el2img_function(element_base);
2749 int tilesize_draw = tilesize / 2;
2754 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2756 for (i = 0; i < 4; i++)
2758 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2759 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2761 if (!(element_bits_draw & (1 << i)))
2764 if (element_bits & (1 << i))
2767 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2768 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2770 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2771 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2776 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2777 tilesize_draw, tilesize_draw);
2782 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2783 boolean masked, int element_bits_draw)
2785 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2786 element, tilesize, el2edimg, masked, element_bits_draw);
2789 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2790 int (*el2img_function)(int))
2792 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2796 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2799 if (IS_MM_WALL(element))
2801 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2802 element, tilesize, el2edimg, masked, 0x000f);
2806 int graphic = el2edimg(element);
2809 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2811 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2815 void DrawSizedElement(int x, int y, int element, int tilesize)
2817 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2820 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2822 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2825 void DrawMiniElement(int x, int y, int element)
2829 graphic = el2edimg(element);
2830 DrawMiniGraphic(x, y, graphic);
2833 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2836 int x = sx + scroll_x, y = sy + scroll_y;
2838 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2839 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2840 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2841 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2843 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2846 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2848 int x = sx + scroll_x, y = sy + scroll_y;
2850 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2851 DrawMiniElement(sx, sy, EL_EMPTY);
2852 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2853 DrawMiniElement(sx, sy, Tile[x][y]);
2855 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2858 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2859 int x, int y, int xsize, int ysize,
2860 int tile_width, int tile_height)
2864 int dst_x = startx + x * tile_width;
2865 int dst_y = starty + y * tile_height;
2866 int width = graphic_info[graphic].width;
2867 int height = graphic_info[graphic].height;
2868 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2869 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2870 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2871 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2872 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2873 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2874 boolean draw_masked = graphic_info[graphic].draw_masked;
2876 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2878 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2880 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2884 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2885 inner_sx + (x - 1) * tile_width % inner_width);
2886 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2887 inner_sy + (y - 1) * tile_height % inner_height);
2890 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2893 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2897 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2898 int x, int y, int xsize, int ysize,
2901 int font_width = getFontWidth(font_nr);
2902 int font_height = getFontHeight(font_nr);
2904 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2905 font_width, font_height);
2908 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2910 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2911 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2912 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2913 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2914 boolean no_delay = (tape.warp_forward);
2915 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2916 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2917 DelayCounter anim_delay = { anim_delay_value };
2918 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2919 int font_width = getFontWidth(font_nr);
2920 int font_height = getFontHeight(font_nr);
2921 int max_xsize = level.envelope[envelope_nr].xsize;
2922 int max_ysize = level.envelope[envelope_nr].ysize;
2923 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2924 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2925 int xend = max_xsize;
2926 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2927 int xstep = (xstart < xend ? 1 : 0);
2928 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2930 int end = MAX(xend - xstart, yend - ystart);
2933 for (i = start; i <= end; i++)
2935 int last_frame = end; // last frame of this "for" loop
2936 int x = xstart + i * xstep;
2937 int y = ystart + i * ystep;
2938 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2939 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2940 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2941 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2944 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2946 BlitScreenToBitmap(backbuffer);
2948 SetDrawtoField(DRAW_TO_BACKBUFFER);
2950 for (yy = 0; yy < ysize; yy++)
2951 for (xx = 0; xx < xsize; xx++)
2952 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2954 DrawTextArea(sx + font_width, sy + font_height,
2955 level.envelope[envelope_nr].text, font_nr, max_xsize,
2956 xsize - 2, ysize - 2, 0, mask_mode,
2957 level.envelope[envelope_nr].autowrap,
2958 level.envelope[envelope_nr].centered, FALSE);
2960 redraw_mask |= REDRAW_FIELD;
2963 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2966 ClearAutoRepeatKeyEvents();
2969 void ShowEnvelope(int envelope_nr)
2971 int element = EL_ENVELOPE_1 + envelope_nr;
2972 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2973 int sound_opening = element_info[element].sound[ACTION_OPENING];
2974 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2975 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2976 boolean no_delay = (tape.warp_forward);
2977 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2978 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2979 int anim_mode = graphic_info[graphic].anim_mode;
2980 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2981 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2982 boolean overlay_enabled = GetOverlayEnabled();
2984 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2986 SetOverlayEnabled(FALSE);
2989 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2991 if (anim_mode == ANIM_DEFAULT)
2992 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2994 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2997 Delay_WithScreenUpdates(wait_delay_value);
2999 WaitForEventToContinue();
3002 SetOverlayEnabled(overlay_enabled);
3004 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3006 if (anim_mode != ANIM_NONE)
3007 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3009 if (anim_mode == ANIM_DEFAULT)
3010 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3012 game.envelope_active = FALSE;
3014 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3016 redraw_mask |= REDRAW_FIELD;
3020 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3021 int xsize, int ysize)
3023 if (!global.use_envelope_request)
3026 if (request.bitmap == NULL ||
3027 xsize > request.xsize ||
3028 ysize > request.ysize)
3030 if (request.bitmap != NULL)
3031 FreeBitmap(request.bitmap);
3033 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3035 SDL_Surface *surface = request.bitmap->surface;
3037 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3038 Fail("SDLGetNativeSurface() failed");
3041 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3043 // create masked surface for request bitmap, if needed
3044 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3046 SDL_Surface *surface = request.bitmap->surface;
3047 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3049 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3050 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3051 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3054 SDLFreeBitmapTextures(request.bitmap);
3055 SDLCreateBitmapTextures(request.bitmap);
3057 // set envelope request run-time values
3060 request.xsize = xsize;
3061 request.ysize = ysize;
3064 void DrawEnvelopeRequestToScreen(int drawing_target)
3066 if (global.use_envelope_request &&
3067 game.request_active &&
3068 drawing_target == DRAW_TO_SCREEN)
3070 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3071 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3072 request.sx, request.sy);
3074 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3075 request.sx, request.sy);
3079 static void setRequestBasePosition(int *x, int *y)
3081 int sx_base, sy_base;
3083 if (request.x != -1)
3084 sx_base = request.x;
3085 else if (request.align == ALIGN_LEFT)
3087 else if (request.align == ALIGN_RIGHT)
3088 sx_base = SX + SXSIZE;
3090 sx_base = SX + SXSIZE / 2;
3092 if (request.y != -1)
3093 sy_base = request.y;
3094 else if (request.valign == VALIGN_TOP)
3096 else if (request.valign == VALIGN_BOTTOM)
3097 sy_base = SY + SYSIZE;
3099 sy_base = SY + SYSIZE / 2;
3105 static void setRequestPositionExt(int *x, int *y, int width, int height,
3106 boolean add_border_size)
3108 int border_size = request.border_size;
3109 int sx_base, sy_base;
3112 setRequestBasePosition(&sx_base, &sy_base);
3114 if (request.align == ALIGN_LEFT)
3116 else if (request.align == ALIGN_RIGHT)
3117 sx = sx_base - width;
3119 sx = sx_base - width / 2;
3121 if (request.valign == VALIGN_TOP)
3123 else if (request.valign == VALIGN_BOTTOM)
3124 sy = sy_base - height;
3126 sy = sy_base - height / 2;
3128 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3129 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3131 if (add_border_size)
3141 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3143 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3146 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3148 DrawBuffer *drawto_last = drawto;
3149 char *text_final = text;
3150 char *text_door_style = NULL;
3151 int graphic = IMG_BACKGROUND_REQUEST;
3152 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3153 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3154 int font_nr = FONT_REQUEST;
3155 int font_width = getFontWidth(font_nr);
3156 int font_height = getFontHeight(font_nr);
3157 int border_size = request.border_size;
3158 int line_spacing = request.line_spacing;
3159 int line_height = font_height + line_spacing;
3160 int max_text_width = request.width - 2 * border_size;
3161 int max_text_height = request.height - 2 * border_size;
3162 int line_length = max_text_width / font_width;
3163 int max_lines = max_text_height / line_height;
3164 int text_width = line_length * font_width;
3165 int width = request.width;
3166 int height = request.height;
3167 int tile_size = MAX(request.step_offset, 1);
3168 int x_steps = width / tile_size;
3169 int y_steps = height / tile_size;
3170 int sx_offset = border_size;
3171 int sy_offset = border_size;
3175 if (request.centered)
3176 sx_offset = (request.width - text_width) / 2;
3178 if (request.wrap_single_words && !request.autowrap)
3180 char *src_text_ptr, *dst_text_ptr;
3182 text_door_style = checked_malloc(2 * strlen(text) + 1);
3184 src_text_ptr = text;
3185 dst_text_ptr = text_door_style;
3187 while (*src_text_ptr)
3189 if (*src_text_ptr == ' ' ||
3190 *src_text_ptr == '?' ||
3191 *src_text_ptr == '!')
3192 *dst_text_ptr++ = '\n';
3194 if (*src_text_ptr != ' ')
3195 *dst_text_ptr++ = *src_text_ptr;
3200 *dst_text_ptr = '\0';
3202 text_final = text_door_style;
3205 setRequestPosition(&sx, &sy, FALSE);
3207 // draw complete envelope request to temporary bitmap
3208 drawto = bitmap_db_store_1;
3210 ClearRectangle(drawto, sx, sy, width, height);
3212 for (y = 0; y < y_steps; y++)
3213 for (x = 0; x < x_steps; x++)
3214 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3215 x, y, x_steps, y_steps,
3216 tile_size, tile_size);
3218 // force DOOR font inside door area
3219 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3221 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3222 line_length, -1, max_lines, line_spacing, mask_mode,
3223 request.autowrap, request.centered, FALSE);
3227 MapToolButtons(req_state);
3229 // restore pointer to drawing buffer
3230 drawto = drawto_last;
3232 // prepare complete envelope request from temporary bitmap
3233 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3235 if (text_door_style)
3236 free(text_door_style);
3239 static void AnimateEnvelopeRequest(int anim_mode, int action)
3241 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3242 int delay_value_normal = request.step_delay;
3243 int delay_value_fast = delay_value_normal / 2;
3244 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3245 boolean no_delay = (tape.warp_forward);
3246 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3247 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3248 DelayCounter anim_delay = { anim_delay_value };
3250 int tile_size = MAX(request.step_offset, 1);
3251 int max_xsize = request.width / tile_size;
3252 int max_ysize = request.height / tile_size;
3253 int max_xsize_inner = max_xsize - 2;
3254 int max_ysize_inner = max_ysize - 2;
3256 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3257 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3258 int xend = max_xsize_inner;
3259 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3260 int xstep = (xstart < xend ? 1 : 0);
3261 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3263 int end = MAX(xend - xstart, yend - ystart);
3266 if (setup.quick_doors)
3273 for (i = start; i <= end; i++)
3275 int last_frame = end; // last frame of this "for" loop
3276 int x = xstart + i * xstep;
3277 int y = ystart + i * ystep;
3278 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3279 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3280 int xsize_size_left = (xsize - 1) * tile_size;
3281 int ysize_size_top = (ysize - 1) * tile_size;
3282 int max_xsize_pos = (max_xsize - 1) * tile_size;
3283 int max_ysize_pos = (max_ysize - 1) * tile_size;
3284 int width = xsize * tile_size;
3285 int height = ysize * tile_size;
3291 HandleGameActions();
3293 setRequestPosition(&src_x, &src_y, FALSE);
3294 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3296 for (yy = 0; yy < 2; yy++)
3298 for (xx = 0; xx < 2; xx++)
3300 int src_xx = src_x + xx * max_xsize_pos;
3301 int src_yy = src_y + yy * max_ysize_pos;
3302 int dst_xx = dst_x + xx * xsize_size_left;
3303 int dst_yy = dst_y + yy * ysize_size_top;
3304 int xx_size = (xx ? tile_size : xsize_size_left);
3305 int yy_size = (yy ? tile_size : ysize_size_top);
3307 // draw partial (animated) envelope request to temporary bitmap
3308 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3309 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3313 // prepare partial (animated) envelope request from temporary bitmap
3314 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3317 redraw_mask |= REDRAW_FIELD;
3321 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3324 ClearAutoRepeatKeyEvents();
3327 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3329 int graphic = IMG_BACKGROUND_REQUEST;
3330 int sound_opening = SND_REQUEST_OPENING;
3331 int sound_closing = SND_REQUEST_CLOSING;
3332 int anim_mode_1 = request.anim_mode; // (higher priority)
3333 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3334 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3335 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3336 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3338 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3340 if (action == ACTION_OPENING)
3342 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3344 if (anim_mode == ANIM_DEFAULT)
3345 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3347 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3351 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3353 if (anim_mode != ANIM_NONE)
3354 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3356 if (anim_mode == ANIM_DEFAULT)
3357 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3360 game.envelope_active = FALSE;
3363 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3365 if (IS_MM_WALL(element))
3367 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3373 int graphic = el2preimg(element);
3375 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3376 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3381 void DrawLevel(int draw_background_mask)
3385 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3386 SetDrawBackgroundMask(draw_background_mask);
3390 for (x = BX1; x <= BX2; x++)
3391 for (y = BY1; y <= BY2; y++)
3392 DrawScreenField(x, y);
3394 redraw_mask |= REDRAW_FIELD;
3397 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3402 for (x = 0; x < size_x; x++)
3403 for (y = 0; y < size_y; y++)
3404 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3406 redraw_mask |= REDRAW_FIELD;
3409 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3413 for (x = 0; x < size_x; x++)
3414 for (y = 0; y < size_y; y++)
3415 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3417 redraw_mask |= REDRAW_FIELD;
3420 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3422 boolean show_level_border = (BorderElement != EL_EMPTY);
3423 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3424 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3425 int tile_size = preview.tile_size;
3426 int preview_width = preview.xsize * tile_size;
3427 int preview_height = preview.ysize * tile_size;
3428 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3429 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3430 int real_preview_width = real_preview_xsize * tile_size;
3431 int real_preview_height = real_preview_ysize * tile_size;
3432 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3433 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3436 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3439 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3441 dst_x += (preview_width - real_preview_width) / 2;
3442 dst_y += (preview_height - real_preview_height) / 2;
3444 for (x = 0; x < real_preview_xsize; x++)
3446 for (y = 0; y < real_preview_ysize; y++)
3448 int lx = from_x + x + (show_level_border ? -1 : 0);
3449 int ly = from_y + y + (show_level_border ? -1 : 0);
3450 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3451 getBorderElement(lx, ly));
3453 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3454 element, tile_size);
3458 redraw_mask |= REDRAW_FIELD;
3461 #define MICROLABEL_EMPTY 0
3462 #define MICROLABEL_LEVEL_NAME 1
3463 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3464 #define MICROLABEL_LEVEL_AUTHOR 3
3465 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3466 #define MICROLABEL_IMPORTED_FROM 5
3467 #define MICROLABEL_IMPORTED_BY_HEAD 6
3468 #define MICROLABEL_IMPORTED_BY 7
3470 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3472 int max_text_width = SXSIZE;
3473 int font_width = getFontWidth(font_nr);
3475 if (pos->align == ALIGN_CENTER)
3476 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3477 else if (pos->align == ALIGN_RIGHT)
3478 max_text_width = pos->x;
3480 max_text_width = SXSIZE - pos->x;
3482 return max_text_width / font_width;
3485 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3487 char label_text[MAX_OUTPUT_LINESIZE + 1];
3488 int max_len_label_text;
3489 int font_nr = pos->font;
3492 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3495 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3496 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3497 mode == MICROLABEL_IMPORTED_BY_HEAD)
3498 font_nr = pos->font_alt;
3500 max_len_label_text = getMaxTextLength(pos, font_nr);
3502 if (pos->size != -1)
3503 max_len_label_text = pos->size;
3505 for (i = 0; i < max_len_label_text; i++)
3506 label_text[i] = ' ';
3507 label_text[max_len_label_text] = '\0';
3509 if (strlen(label_text) > 0)
3510 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3513 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3514 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3515 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3516 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3517 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3518 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3519 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3520 max_len_label_text);
3521 label_text[max_len_label_text] = '\0';
3523 if (strlen(label_text) > 0)
3524 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3526 redraw_mask |= REDRAW_FIELD;
3529 static void DrawPreviewLevelLabel(int mode)
3531 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3534 static void DrawPreviewLevelInfo(int mode)
3536 if (mode == MICROLABEL_LEVEL_NAME)
3537 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3538 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3539 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3542 static void DrawPreviewLevelExt(boolean restart)
3544 static DelayCounter scroll_delay = { 0 };
3545 static DelayCounter label_delay = { 0 };
3546 static int from_x, from_y, scroll_direction;
3547 static int label_state, label_counter;
3548 boolean show_level_border = (BorderElement != EL_EMPTY);
3549 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3550 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3552 scroll_delay.value = preview.step_delay;
3553 label_delay.value = MICROLEVEL_LABEL_DELAY;
3560 if (preview.anim_mode == ANIM_CENTERED)
3562 if (level_xsize > preview.xsize)
3563 from_x = (level_xsize - preview.xsize) / 2;
3564 if (level_ysize > preview.ysize)
3565 from_y = (level_ysize - preview.ysize) / 2;
3568 from_x += preview.xoffset;
3569 from_y += preview.yoffset;
3571 scroll_direction = MV_RIGHT;
3575 DrawPreviewLevelPlayfield(from_x, from_y);
3576 DrawPreviewLevelLabel(label_state);
3578 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3579 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3581 // initialize delay counters
3582 ResetDelayCounter(&scroll_delay);
3583 ResetDelayCounter(&label_delay);
3585 if (leveldir_current->name)
3587 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3588 char label_text[MAX_OUTPUT_LINESIZE + 1];
3589 int font_nr = pos->font;
3590 int max_len_label_text = getMaxTextLength(pos, font_nr);
3592 if (pos->size != -1)
3593 max_len_label_text = pos->size;
3595 strncpy(label_text, leveldir_current->name, max_len_label_text);
3596 label_text[max_len_label_text] = '\0';
3598 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3599 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3605 // scroll preview level, if needed
3606 if (preview.anim_mode != ANIM_NONE &&
3607 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3608 DelayReached(&scroll_delay))
3610 switch (scroll_direction)
3615 from_x -= preview.step_offset;
3616 from_x = (from_x < 0 ? 0 : from_x);
3619 scroll_direction = MV_UP;
3623 if (from_x < level_xsize - preview.xsize)
3625 from_x += preview.step_offset;
3626 from_x = (from_x > level_xsize - preview.xsize ?
3627 level_xsize - preview.xsize : from_x);
3630 scroll_direction = MV_DOWN;
3636 from_y -= preview.step_offset;
3637 from_y = (from_y < 0 ? 0 : from_y);
3640 scroll_direction = MV_RIGHT;
3644 if (from_y < level_ysize - preview.ysize)
3646 from_y += preview.step_offset;
3647 from_y = (from_y > level_ysize - preview.ysize ?
3648 level_ysize - preview.ysize : from_y);
3651 scroll_direction = MV_LEFT;
3658 DrawPreviewLevelPlayfield(from_x, from_y);
3661 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3662 // redraw micro level label, if needed
3663 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3664 !strEqual(level.author, ANONYMOUS_NAME) &&
3665 !strEqual(level.author, leveldir_current->name) &&
3666 DelayReached(&label_delay))
3668 int max_label_counter = 23;
3670 if (leveldir_current->imported_from != NULL &&
3671 strlen(leveldir_current->imported_from) > 0)
3672 max_label_counter += 14;
3673 if (leveldir_current->imported_by != NULL &&
3674 strlen(leveldir_current->imported_by) > 0)
3675 max_label_counter += 14;
3677 label_counter = (label_counter + 1) % max_label_counter;
3678 label_state = (label_counter >= 0 && label_counter <= 7 ?
3679 MICROLABEL_LEVEL_NAME :
3680 label_counter >= 9 && label_counter <= 12 ?
3681 MICROLABEL_LEVEL_AUTHOR_HEAD :
3682 label_counter >= 14 && label_counter <= 21 ?
3683 MICROLABEL_LEVEL_AUTHOR :
3684 label_counter >= 23 && label_counter <= 26 ?
3685 MICROLABEL_IMPORTED_FROM_HEAD :
3686 label_counter >= 28 && label_counter <= 35 ?
3687 MICROLABEL_IMPORTED_FROM :
3688 label_counter >= 37 && label_counter <= 40 ?
3689 MICROLABEL_IMPORTED_BY_HEAD :
3690 label_counter >= 42 && label_counter <= 49 ?
3691 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3693 if (leveldir_current->imported_from == NULL &&
3694 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3695 label_state == MICROLABEL_IMPORTED_FROM))
3696 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3697 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3699 DrawPreviewLevelLabel(label_state);
3703 void DrawPreviewPlayers(void)
3705 if (game_status != GAME_MODE_MAIN)
3708 // do not draw preview players if level preview redefined, but players aren't
3709 if (preview.redefined && !menu.main.preview_players.redefined)
3712 boolean player_found[MAX_PLAYERS];
3713 int num_players = 0;
3716 for (i = 0; i < MAX_PLAYERS; i++)
3717 player_found[i] = FALSE;
3719 // check which players can be found in the level (simple approach)
3720 for (x = 0; x < lev_fieldx; x++)
3722 for (y = 0; y < lev_fieldy; y++)
3724 int element = level.field[x][y];
3726 if (IS_PLAYER_ELEMENT(element))
3728 int player_nr = GET_PLAYER_NR(element);
3730 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3732 if (!player_found[player_nr])
3735 player_found[player_nr] = TRUE;
3740 struct TextPosInfo *pos = &menu.main.preview_players;
3741 int tile_size = pos->tile_size;
3742 int border_size = pos->border_size;
3743 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3744 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3745 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3746 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3747 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3748 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3749 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3750 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3751 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3752 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3753 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3754 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3756 // clear area in which the players will be drawn
3757 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3758 max_players_width, max_players_height);
3760 if (!network.enabled && !setup.team_mode)
3763 // only draw players if level is suited for team mode
3764 if (num_players < 2)
3767 // draw all players that were found in the level
3768 for (i = 0; i < MAX_PLAYERS; i++)
3770 if (player_found[i])
3772 int graphic = el2img(EL_PLAYER_1 + i);
3774 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3776 xpos += player_xoffset;
3777 ypos += player_yoffset;
3782 void DrawPreviewLevelInitial(void)
3784 DrawPreviewLevelExt(TRUE);
3785 DrawPreviewPlayers();
3788 void DrawPreviewLevelAnimation(void)
3790 DrawPreviewLevelExt(FALSE);
3793 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3794 int border_size, int font_nr)
3796 int graphic = el2img(EL_PLAYER_1 + player_nr);
3797 int font_height = getFontHeight(font_nr);
3798 int player_height = MAX(tile_size, font_height);
3799 int xoffset_text = tile_size + border_size;
3800 int yoffset_text = (player_height - font_height) / 2;
3801 int yoffset_graphic = (player_height - tile_size) / 2;
3802 char *player_name = getNetworkPlayerName(player_nr + 1);
3804 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3806 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3809 static void DrawNetworkPlayersExt(boolean force)
3811 if (game_status != GAME_MODE_MAIN)
3814 if (!network.connected && !force)
3817 // do not draw network players if level preview redefined, but players aren't
3818 if (preview.redefined && !menu.main.network_players.redefined)
3821 int num_players = 0;
3824 for (i = 0; i < MAX_PLAYERS; i++)
3825 if (stored_player[i].connected_network)
3828 struct TextPosInfo *pos = &menu.main.network_players;
3829 int tile_size = pos->tile_size;
3830 int border_size = pos->border_size;
3831 int xoffset_text = tile_size + border_size;
3832 int font_nr = pos->font;
3833 int font_width = getFontWidth(font_nr);
3834 int font_height = getFontHeight(font_nr);
3835 int player_height = MAX(tile_size, font_height);
3836 int player_yoffset = player_height + border_size;
3837 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3838 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3839 int all_players_height = num_players * player_yoffset - border_size;
3840 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3841 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3842 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3844 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3845 max_players_width, max_players_height);
3847 // first draw local network player ...
3848 for (i = 0; i < MAX_PLAYERS; i++)
3850 if (stored_player[i].connected_network &&
3851 stored_player[i].connected_locally)
3853 char *player_name = getNetworkPlayerName(i + 1);
3854 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3855 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3857 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3859 ypos += player_yoffset;
3863 // ... then draw all other network players
3864 for (i = 0; i < MAX_PLAYERS; i++)
3866 if (stored_player[i].connected_network &&
3867 !stored_player[i].connected_locally)
3869 char *player_name = getNetworkPlayerName(i + 1);
3870 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3871 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3873 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3875 ypos += player_yoffset;
3880 void DrawNetworkPlayers(void)
3882 DrawNetworkPlayersExt(FALSE);
3885 void ClearNetworkPlayers(void)
3887 DrawNetworkPlayersExt(TRUE);
3890 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3891 int graphic, int lx, int ly,
3894 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3896 if (mask_mode == USE_MASKING)
3897 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3899 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3902 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3903 int graphic, int sync_frame, int mask_mode)
3905 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3907 if (mask_mode == USE_MASKING)
3908 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3910 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3913 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3914 int graphic, int sync_frame, int tilesize,
3917 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3919 if (mask_mode == USE_MASKING)
3920 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3922 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3925 static void DrawGraphicAnimation(int x, int y, int graphic)
3927 int lx = LEVELX(x), ly = LEVELY(y);
3928 int mask_mode = NO_MASKING;
3930 if (!IN_SCR_FIELD(x, y))
3933 if (game.use_masked_elements)
3935 if (Tile[lx][ly] != EL_EMPTY)
3937 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3939 mask_mode = USE_MASKING;
3943 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3944 graphic, lx, ly, mask_mode);
3946 MarkTileDirty(x, y);
3949 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3951 int lx = LEVELX(x), ly = LEVELY(y);
3952 int mask_mode = NO_MASKING;
3954 if (!IN_SCR_FIELD(x, y))
3957 if (game.use_masked_elements)
3959 if (Tile[lx][ly] != EL_EMPTY)
3961 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3963 mask_mode = USE_MASKING;
3967 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3968 graphic, lx, ly, mask_mode);
3970 MarkTileDirty(x, y);
3973 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3975 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3978 void DrawLevelElementAnimation(int x, int y, int element)
3980 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3982 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3985 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3987 int sx = SCREENX(x), sy = SCREENY(y);
3989 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3992 if (Tile[x][y] == EL_EMPTY)
3993 graphic = el2img(GfxElementEmpty[x][y]);
3995 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3998 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4001 DrawGraphicAnimation(sx, sy, graphic);
4004 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4005 DrawLevelFieldCrumbled(x, y);
4007 if (GFX_CRUMBLED(Tile[x][y]))
4008 DrawLevelFieldCrumbled(x, y);
4012 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4014 int sx = SCREENX(x), sy = SCREENY(y);
4017 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4020 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4022 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4025 DrawGraphicAnimation(sx, sy, graphic);
4027 if (GFX_CRUMBLED(element))
4028 DrawLevelFieldCrumbled(x, y);
4031 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4033 if (player->use_murphy)
4035 // this works only because currently only one player can be "murphy" ...
4036 static int last_horizontal_dir = MV_LEFT;
4037 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4039 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4040 last_horizontal_dir = move_dir;
4042 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4044 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4046 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4052 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4055 static boolean equalGraphics(int graphic1, int graphic2)
4057 struct GraphicInfo *g1 = &graphic_info[graphic1];
4058 struct GraphicInfo *g2 = &graphic_info[graphic2];
4060 return (g1->bitmap == g2->bitmap &&
4061 g1->src_x == g2->src_x &&
4062 g1->src_y == g2->src_y &&
4063 g1->anim_frames == g2->anim_frames &&
4064 g1->anim_delay == g2->anim_delay &&
4065 g1->anim_mode == g2->anim_mode);
4068 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4072 DRAW_PLAYER_STAGE_INIT = 0,
4073 DRAW_PLAYER_STAGE_LAST_FIELD,
4074 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4075 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4076 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4077 DRAW_PLAYER_STAGE_PLAYER,
4079 DRAW_PLAYER_STAGE_PLAYER,
4080 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4082 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4083 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4085 NUM_DRAW_PLAYER_STAGES
4088 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4090 static int static_last_player_graphic[MAX_PLAYERS];
4091 static int static_last_player_frame[MAX_PLAYERS];
4092 static boolean static_player_is_opaque[MAX_PLAYERS];
4093 static boolean draw_player[MAX_PLAYERS];
4094 int pnr = player->index_nr;
4096 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4098 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4099 static_last_player_frame[pnr] = player->Frame;
4100 static_player_is_opaque[pnr] = FALSE;
4102 draw_player[pnr] = TRUE;
4105 if (!draw_player[pnr])
4109 if (!IN_LEV_FIELD(player->jx, player->jy))
4111 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4112 Debug("draw:DrawPlayerExt", "This should never happen!");
4114 draw_player[pnr] = FALSE;
4120 int last_player_graphic = static_last_player_graphic[pnr];
4121 int last_player_frame = static_last_player_frame[pnr];
4122 boolean player_is_opaque = static_player_is_opaque[pnr];
4124 int jx = player->jx;
4125 int jy = player->jy;
4126 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4127 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4128 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4129 int last_jx = (player->is_moving ? jx - dx : jx);
4130 int last_jy = (player->is_moving ? jy - dy : jy);
4131 int next_jx = jx + dx;
4132 int next_jy = jy + dy;
4133 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4134 int sx = SCREENX(jx);
4135 int sy = SCREENY(jy);
4136 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4137 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4138 int element = Tile[jx][jy];
4139 int last_element = Tile[last_jx][last_jy];
4140 int action = (player->is_pushing ? ACTION_PUSHING :
4141 player->is_digging ? ACTION_DIGGING :
4142 player->is_collecting ? ACTION_COLLECTING :
4143 player->is_moving ? ACTION_MOVING :
4144 player->is_snapping ? ACTION_SNAPPING :
4145 player->is_dropping ? ACTION_DROPPING :
4146 player->is_waiting ? player->action_waiting :
4149 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4151 // ------------------------------------------------------------------------
4152 // initialize drawing the player
4153 // ------------------------------------------------------------------------
4155 draw_player[pnr] = FALSE;
4157 // GfxElement[][] is set to the element the player is digging or collecting;
4158 // remove also for off-screen player if the player is not moving anymore
4159 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4160 GfxElement[jx][jy] = EL_UNDEFINED;
4162 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4165 if (element == EL_EXPLOSION)
4168 InitPlayerGfxAnimation(player, action, move_dir);
4170 draw_player[pnr] = TRUE;
4172 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4174 // ------------------------------------------------------------------------
4175 // draw things in the field the player is leaving, if needed
4176 // ------------------------------------------------------------------------
4178 if (!IN_SCR_FIELD(sx, sy))
4179 draw_player[pnr] = FALSE;
4181 if (!player->is_moving)
4184 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4186 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4188 if (last_element == EL_DYNAMITE_ACTIVE ||
4189 last_element == EL_EM_DYNAMITE_ACTIVE ||
4190 last_element == EL_SP_DISK_RED_ACTIVE)
4191 DrawDynamite(last_jx, last_jy);
4193 DrawLevelFieldThruMask(last_jx, last_jy);
4195 else if (last_element == EL_DYNAMITE_ACTIVE ||
4196 last_element == EL_EM_DYNAMITE_ACTIVE ||
4197 last_element == EL_SP_DISK_RED_ACTIVE)
4198 DrawDynamite(last_jx, last_jy);
4200 DrawLevelField(last_jx, last_jy);
4202 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4204 // ------------------------------------------------------------------------
4205 // draw things behind the player, if needed
4206 // ------------------------------------------------------------------------
4210 DrawLevelElement(jx, jy, Back[jx][jy]);
4215 if (IS_ACTIVE_BOMB(element))
4217 DrawLevelElement(jx, jy, EL_EMPTY);
4222 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4224 int old_element = GfxElement[jx][jy];
4225 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4226 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4228 if (GFX_CRUMBLED(old_element))
4229 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4231 DrawScreenGraphic(sx, sy, old_graphic, frame);
4233 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4234 static_player_is_opaque[pnr] = TRUE;
4238 GfxElement[jx][jy] = EL_UNDEFINED;
4240 // make sure that pushed elements are drawn with correct frame rate
4241 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4243 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4244 GfxFrame[jx][jy] = player->StepFrame;
4246 DrawLevelField(jx, jy);
4249 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4251 // ------------------------------------------------------------------------
4252 // draw things the player is pushing, if needed
4253 // ------------------------------------------------------------------------
4255 if (!player->is_pushing || !player->is_moving)
4258 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4261 int gfx_frame = GfxFrame[jx][jy];
4263 if (!IS_MOVING(jx, jy)) // push movement already finished
4265 element = Tile[next_jx][next_jy];
4266 gfx_frame = GfxFrame[next_jx][next_jy];
4269 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4270 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4271 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4273 // draw background element under pushed element (like the Sokoban field)
4274 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4276 // this allows transparent pushing animation over non-black background
4279 DrawLevelElement(jx, jy, Back[jx][jy]);
4281 DrawLevelElement(jx, jy, EL_EMPTY);
4284 if (Back[next_jx][next_jy])
4285 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4287 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4289 int px = SCREENX(jx), py = SCREENY(jy);
4290 int pxx = (TILEX - ABS(sxx)) * dx;
4291 int pyy = (TILEY - ABS(syy)) * dy;
4294 // do not draw (EM style) pushing animation when pushing is finished
4295 // (two-tile animations usually do not contain start and end frame)
4296 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4297 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4299 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4301 // masked drawing is needed for EMC style (double) movement graphics
4302 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4303 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4306 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4308 // ------------------------------------------------------------------------
4309 // draw player himself
4310 // ------------------------------------------------------------------------
4312 int graphic = getPlayerGraphic(player, move_dir);
4314 // in the case of changed player action or direction, prevent the current
4315 // animation frame from being restarted for identical animations
4316 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4317 player->Frame = last_player_frame;
4319 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4321 if (player_is_opaque)
4322 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4324 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4326 if (SHIELD_ON(player))
4328 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4329 IMG_SHIELD_NORMAL_ACTIVE);
4330 frame = getGraphicAnimationFrame(graphic, -1);
4332 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4335 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4337 // ------------------------------------------------------------------------
4338 // draw things in front of player (active dynamite or dynabombs)
4339 // ------------------------------------------------------------------------
4341 if (IS_ACTIVE_BOMB(element))
4343 int graphic = el2img(element);
4344 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4346 if (game.emulation == EMU_SUPAPLEX)
4347 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4349 DrawGraphicThruMask(sx, sy, graphic, frame);
4352 if (player_is_moving && last_element == EL_EXPLOSION)
4354 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4355 GfxElement[last_jx][last_jy] : EL_EMPTY);
4356 int graphic = el_act2img(element, ACTION_EXPLODING);
4357 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4358 int phase = ExplodePhase[last_jx][last_jy] - 1;
4359 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4362 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4365 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4367 // ------------------------------------------------------------------------
4368 // draw elements the player is just walking/passing through/under
4369 // ------------------------------------------------------------------------
4371 if (player_is_moving)
4373 // handle the field the player is leaving ...
4374 if (IS_ACCESSIBLE_INSIDE(last_element))
4375 DrawLevelField(last_jx, last_jy);
4376 else if (IS_ACCESSIBLE_UNDER(last_element))
4377 DrawLevelFieldThruMask(last_jx, last_jy);
4380 // do not redraw accessible elements if the player is just pushing them
4381 if (!player_is_moving || !player->is_pushing)
4383 // ... and the field the player is entering
4384 if (IS_ACCESSIBLE_INSIDE(element))
4385 DrawLevelField(jx, jy);
4386 else if (IS_ACCESSIBLE_UNDER(element))
4387 DrawLevelFieldThruMask(jx, jy);
4390 MarkTileDirty(sx, sy);
4394 void DrawPlayer(struct PlayerInfo *player)
4398 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4399 DrawPlayerExt(player, i);
4402 void DrawAllPlayers(void)
4406 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4407 for (j = 0; j < MAX_PLAYERS; j++)
4408 if (stored_player[j].active)
4409 DrawPlayerExt(&stored_player[j], i);
4412 void DrawPlayerField(int x, int y)
4414 if (!IS_PLAYER(x, y))
4417 DrawPlayer(PLAYERINFO(x, y));
4420 // ----------------------------------------------------------------------------
4422 void WaitForEventToContinue(void)
4424 boolean first_wait = TRUE;
4425 boolean still_wait = TRUE;
4427 if (program.headless)
4430 // simulate releasing mouse button over last gadget, if still pressed
4432 HandleGadgets(-1, -1, 0);
4434 button_status = MB_RELEASED;
4437 ClearPlayerAction();
4443 if (NextValidEvent(&event))
4447 case EVENT_BUTTONPRESS:
4448 case EVENT_FINGERPRESS:
4452 case EVENT_BUTTONRELEASE:
4453 case EVENT_FINGERRELEASE:
4454 still_wait = first_wait;
4457 case EVENT_KEYPRESS:
4458 case SDL_CONTROLLERBUTTONDOWN:
4459 case SDL_JOYBUTTONDOWN:
4464 HandleOtherEvents(&event);
4468 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4473 if (!PendingEvent())
4478 #define MAX_REQUEST_LINES 13
4479 #define MAX_REQUEST_LINE_FONT1_LEN 7
4480 #define MAX_REQUEST_LINE_FONT2_LEN 10
4482 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4484 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4485 int draw_buffer_last = GetDrawtoField();
4486 int width = request.width;
4487 int height = request.height;
4491 setRequestPosition(&sx, &sy, FALSE);
4493 button_status = MB_RELEASED;
4495 request_gadget_id = -1;
4502 SetDrawtoField(draw_buffer_game);
4504 HandleGameActions();
4506 SetDrawtoField(DRAW_TO_BACKBUFFER);
4513 while (NextValidEvent(&event))
4517 case EVENT_BUTTONPRESS:
4518 case EVENT_BUTTONRELEASE:
4519 case EVENT_MOTIONNOTIFY:
4521 DrawBuffer *drawto_last = drawto;
4524 if (event.type == EVENT_MOTIONNOTIFY)
4529 motion_status = TRUE;
4530 mx = ((MotionEvent *) &event)->x;
4531 my = ((MotionEvent *) &event)->y;
4535 motion_status = FALSE;
4536 mx = ((ButtonEvent *) &event)->x;
4537 my = ((ButtonEvent *) &event)->y;
4538 if (event.type == EVENT_BUTTONPRESS)
4539 button_status = ((ButtonEvent *) &event)->button;
4541 button_status = MB_RELEASED;
4544 if (global.use_envelope_request)
4546 // draw changed button states to temporary bitmap
4547 drawto = bitmap_db_store_1;
4550 // this sets 'request_gadget_id'
4551 HandleGadgets(mx, my, button_status);
4553 if (global.use_envelope_request)
4555 // restore pointer to drawing buffer
4556 drawto = drawto_last;
4558 // prepare complete envelope request from temporary bitmap
4559 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4563 switch (request_gadget_id)
4565 case TOOL_CTRL_ID_YES:
4566 case TOOL_CTRL_ID_TOUCH_YES:
4569 case TOOL_CTRL_ID_NO:
4570 case TOOL_CTRL_ID_TOUCH_NO:
4573 case TOOL_CTRL_ID_CONFIRM:
4574 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4575 result = TRUE | FALSE;
4578 case TOOL_CTRL_ID_PLAYER_1:
4581 case TOOL_CTRL_ID_PLAYER_2:
4584 case TOOL_CTRL_ID_PLAYER_3:
4587 case TOOL_CTRL_ID_PLAYER_4:
4592 // only check clickable animations if no request gadget clicked
4593 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4600 case SDL_WINDOWEVENT:
4601 HandleWindowEvent((WindowEvent *) &event);
4604 case SDL_APP_WILLENTERBACKGROUND:
4605 case SDL_APP_DIDENTERBACKGROUND:
4606 case SDL_APP_WILLENTERFOREGROUND:
4607 case SDL_APP_DIDENTERFOREGROUND:
4608 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4611 case EVENT_KEYPRESS:
4613 Key key = GetEventKey((KeyEvent *)&event);
4618 if (req_state & REQ_CONFIRM)
4627 #if defined(KSYM_Rewind)
4628 case KSYM_Rewind: // for Amazon Fire TV remote
4637 #if defined(KSYM_FastForward)
4638 case KSYM_FastForward: // for Amazon Fire TV remote
4644 HandleKeysDebug(key, KEY_PRESSED);
4648 if (req_state & REQ_PLAYER)
4650 int old_player_nr = setup.network_player_nr;
4653 result = old_player_nr + 1;
4658 result = old_player_nr + 1;
4689 case EVENT_FINGERRELEASE:
4690 case EVENT_KEYRELEASE:
4691 ClearPlayerAction();
4694 case SDL_CONTROLLERBUTTONDOWN:
4695 switch (event.cbutton.button)
4697 case SDL_CONTROLLER_BUTTON_A:
4698 case SDL_CONTROLLER_BUTTON_X:
4699 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4700 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4704 case SDL_CONTROLLER_BUTTON_B:
4705 case SDL_CONTROLLER_BUTTON_Y:
4706 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4707 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4708 case SDL_CONTROLLER_BUTTON_BACK:
4713 if (req_state & REQ_PLAYER)
4715 int old_player_nr = setup.network_player_nr;
4718 result = old_player_nr + 1;
4720 switch (event.cbutton.button)
4722 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4723 case SDL_CONTROLLER_BUTTON_Y:
4727 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4728 case SDL_CONTROLLER_BUTTON_B:
4732 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4733 case SDL_CONTROLLER_BUTTON_A:
4737 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4738 case SDL_CONTROLLER_BUTTON_X:
4749 case SDL_CONTROLLERBUTTONUP:
4750 HandleJoystickEvent(&event);
4751 ClearPlayerAction();
4755 HandleOtherEvents(&event);
4760 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4762 int joy = AnyJoystick();
4764 if (joy & JOY_BUTTON_1)
4766 else if (joy & JOY_BUTTON_2)
4769 else if (AnyJoystick())
4771 int joy = AnyJoystick();
4773 if (req_state & REQ_PLAYER)
4777 else if (joy & JOY_RIGHT)
4779 else if (joy & JOY_DOWN)
4781 else if (joy & JOY_LEFT)
4789 SetDrawtoField(draw_buffer_last);
4794 static void DoRequestBefore(unsigned int req_state)
4796 if (game_status == GAME_MODE_PLAYING)
4797 BlitScreenToBitmap(backbuffer);
4799 // disable deactivated drawing when quick-loading level tape recording
4800 if (tape.playing && tape.deactivate_display)
4801 TapeDeactivateDisplayOff(TRUE);
4803 SetMouseCursor(CURSOR_DEFAULT);
4805 // pause network game while waiting for request to answer
4806 if (network.enabled &&
4807 game_status == GAME_MODE_PLAYING &&
4808 !game.all_players_gone &&
4809 req_state & REQUEST_WAIT_FOR_INPUT)
4810 SendToServer_PausePlaying();
4812 // simulate releasing mouse button over last gadget, if still pressed
4814 HandleGadgets(-1, -1, 0);
4819 static void DoRequestAfter(unsigned int req_state)
4823 if (game_status == GAME_MODE_PLAYING)
4825 SetPanelBackground();
4826 SetDrawBackgroundMask(REDRAW_DOOR_1);
4830 SetDrawBackgroundMask(REDRAW_FIELD);
4833 // continue network game after request
4834 if (network.enabled &&
4835 game_status == GAME_MODE_PLAYING &&
4836 !game.all_players_gone &&
4837 req_state & REQUEST_WAIT_FOR_INPUT)
4838 SendToServer_ContinuePlaying();
4840 // restore deactivated drawing when quick-loading level tape recording
4841 if (tape.playing && tape.deactivate_display)
4842 TapeDeactivateDisplayOn();
4845 static boolean RequestDoor(char *text, unsigned int req_state)
4847 int draw_buffer_last = GetDrawtoField();
4848 unsigned int old_door_state = GetDoorState();
4849 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4850 int font_nr = FONT_TEXT_2;
4855 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4857 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4858 font_nr = FONT_TEXT_1;
4861 DoRequestBefore(req_state);
4863 // draw released gadget before proceeding
4866 if (old_door_state & DOOR_OPEN_1)
4868 CloseDoor(DOOR_CLOSE_1);
4870 // save old door content
4871 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4872 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4875 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4876 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4878 // clear door drawing field
4879 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4881 // force DOOR font inside door area
4882 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4884 // write text for request
4885 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4887 char text_line[max_request_line_len + 1];
4893 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4895 tc = *(text_ptr + tx);
4896 // if (!tc || tc == ' ')
4897 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4901 if ((tc == '?' || tc == '!') && tl == 0)
4911 strncpy(text_line, text_ptr, tl);
4914 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4915 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4916 text_line, font_nr);
4918 text_ptr += tl + (tc == ' ' ? 1 : 0);
4919 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4924 MapToolButtons(req_state);
4926 // copy request gadgets to door backbuffer
4927 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4929 OpenDoor(DOOR_OPEN_1);
4931 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4933 if (game_status == GAME_MODE_PLAYING)
4935 SetPanelBackground();
4936 SetDrawBackgroundMask(REDRAW_DOOR_1);
4940 SetDrawBackgroundMask(REDRAW_FIELD);
4946 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4948 // ---------- handle request buttons ----------
4949 result = RequestHandleEvents(req_state, draw_buffer_last);
4953 if (!(req_state & REQ_STAY_OPEN))
4955 CloseDoor(DOOR_CLOSE_1);
4957 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4958 (req_state & REQ_REOPEN))
4959 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4962 DoRequestAfter(req_state);
4967 static boolean RequestEnvelope(char *text, unsigned int req_state)
4969 int draw_buffer_last = GetDrawtoField();
4972 DoRequestBefore(req_state);
4974 DrawEnvelopeRequest(text, req_state);
4975 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4977 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4979 if (game_status == GAME_MODE_PLAYING)
4981 SetPanelBackground();
4982 SetDrawBackgroundMask(REDRAW_DOOR_1);
4986 SetDrawBackgroundMask(REDRAW_FIELD);
4992 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4994 // ---------- handle request buttons ----------
4995 result = RequestHandleEvents(req_state, draw_buffer_last);
4999 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5001 DoRequestAfter(req_state);
5006 boolean Request(char *text, unsigned int req_state)
5008 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5009 boolean overlay_enabled = GetOverlayEnabled();
5012 // when showing request dialog after game ended, deactivate game panel
5014 game.panel.active = FALSE;
5016 game.request_active = TRUE;
5018 SetOverlayEnabled(FALSE);
5020 if (global.use_envelope_request)
5021 result = RequestEnvelope(text, req_state);
5023 result = RequestDoor(text, req_state);
5025 SetOverlayEnabled(overlay_enabled);
5027 game.request_active = FALSE;
5032 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5034 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5035 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5038 if (dpo1->sort_priority != dpo2->sort_priority)
5039 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5041 compare_result = dpo1->nr - dpo2->nr;
5043 return compare_result;
5046 void InitGraphicCompatibilityInfo_Doors(void)
5052 struct DoorInfo *door;
5056 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5057 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5059 { -1, -1, -1, NULL }
5061 struct Rect door_rect_list[] =
5063 { DX, DY, DXSIZE, DYSIZE },
5064 { VX, VY, VXSIZE, VYSIZE }
5068 for (i = 0; doors[i].door_token != -1; i++)
5070 int door_token = doors[i].door_token;
5071 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5072 int part_1 = doors[i].part_1;
5073 int part_8 = doors[i].part_8;
5074 int part_2 = part_1 + 1;
5075 int part_3 = part_1 + 2;
5076 struct DoorInfo *door = doors[i].door;
5077 struct Rect *door_rect = &door_rect_list[door_index];
5078 boolean door_gfx_redefined = FALSE;
5080 // check if any door part graphic definitions have been redefined
5082 for (j = 0; door_part_controls[j].door_token != -1; j++)
5084 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5085 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5087 if (dpc->door_token == door_token && fi->redefined)
5088 door_gfx_redefined = TRUE;
5091 // check for old-style door graphic/animation modifications
5093 if (!door_gfx_redefined)
5095 if (door->anim_mode & ANIM_STATIC_PANEL)
5097 door->panel.step_xoffset = 0;
5098 door->panel.step_yoffset = 0;
5101 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5103 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5104 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5105 int num_door_steps, num_panel_steps;
5107 // remove door part graphics other than the two default wings
5109 for (j = 0; door_part_controls[j].door_token != -1; j++)
5111 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5112 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5114 if (dpc->graphic >= part_3 &&
5115 dpc->graphic <= part_8)
5119 // set graphics and screen positions of the default wings
5121 g_part_1->width = door_rect->width;
5122 g_part_1->height = door_rect->height;
5123 g_part_2->width = door_rect->width;
5124 g_part_2->height = door_rect->height;
5125 g_part_2->src_x = door_rect->width;
5126 g_part_2->src_y = g_part_1->src_y;
5128 door->part_2.x = door->part_1.x;
5129 door->part_2.y = door->part_1.y;
5131 if (door->width != -1)
5133 g_part_1->width = door->width;
5134 g_part_2->width = door->width;
5136 // special treatment for graphics and screen position of right wing
5137 g_part_2->src_x += door_rect->width - door->width;
5138 door->part_2.x += door_rect->width - door->width;
5141 if (door->height != -1)
5143 g_part_1->height = door->height;
5144 g_part_2->height = door->height;
5146 // special treatment for graphics and screen position of bottom wing
5147 g_part_2->src_y += door_rect->height - door->height;
5148 door->part_2.y += door_rect->height - door->height;
5151 // set animation delays for the default wings and panels
5153 door->part_1.step_delay = door->step_delay;
5154 door->part_2.step_delay = door->step_delay;
5155 door->panel.step_delay = door->step_delay;
5157 // set animation draw order for the default wings
5159 door->part_1.sort_priority = 2; // draw left wing over ...
5160 door->part_2.sort_priority = 1; // ... right wing
5162 // set animation draw offset for the default wings
5164 if (door->anim_mode & ANIM_HORIZONTAL)
5166 door->part_1.step_xoffset = door->step_offset;
5167 door->part_1.step_yoffset = 0;
5168 door->part_2.step_xoffset = door->step_offset * -1;
5169 door->part_2.step_yoffset = 0;
5171 num_door_steps = g_part_1->width / door->step_offset;
5173 else // ANIM_VERTICAL
5175 door->part_1.step_xoffset = 0;
5176 door->part_1.step_yoffset = door->step_offset;
5177 door->part_2.step_xoffset = 0;
5178 door->part_2.step_yoffset = door->step_offset * -1;
5180 num_door_steps = g_part_1->height / door->step_offset;
5183 // set animation draw offset for the default panels
5185 if (door->step_offset > 1)
5187 num_panel_steps = 2 * door_rect->height / door->step_offset;
5188 door->panel.start_step = num_panel_steps - num_door_steps;
5189 door->panel.start_step_closing = door->panel.start_step;
5193 num_panel_steps = door_rect->height / door->step_offset;
5194 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5195 door->panel.start_step_closing = door->panel.start_step;
5196 door->panel.step_delay *= 2;
5203 void InitDoors(void)
5207 for (i = 0; door_part_controls[i].door_token != -1; i++)
5209 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5210 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5212 // initialize "start_step_opening" and "start_step_closing", if needed
5213 if (dpc->pos->start_step_opening == 0 &&
5214 dpc->pos->start_step_closing == 0)
5216 // dpc->pos->start_step_opening = dpc->pos->start_step;
5217 dpc->pos->start_step_closing = dpc->pos->start_step;
5220 // fill structure for door part draw order (sorted below)
5222 dpo->sort_priority = dpc->pos->sort_priority;
5225 // sort door part controls according to sort_priority and graphic number
5226 qsort(door_part_order, MAX_DOOR_PARTS,
5227 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5230 unsigned int OpenDoor(unsigned int door_state)
5232 if (door_state & DOOR_COPY_BACK)
5234 if (door_state & DOOR_OPEN_1)
5235 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5236 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5238 if (door_state & DOOR_OPEN_2)
5239 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5240 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5242 door_state &= ~DOOR_COPY_BACK;
5245 return MoveDoor(door_state);
5248 unsigned int CloseDoor(unsigned int door_state)
5250 unsigned int old_door_state = GetDoorState();
5252 if (!(door_state & DOOR_NO_COPY_BACK))
5254 if (old_door_state & DOOR_OPEN_1)
5255 BlitBitmap(backbuffer, bitmap_db_door_1,
5256 DX, DY, DXSIZE, DYSIZE, 0, 0);
5258 if (old_door_state & DOOR_OPEN_2)
5259 BlitBitmap(backbuffer, bitmap_db_door_2,
5260 VX, VY, VXSIZE, VYSIZE, 0, 0);
5262 door_state &= ~DOOR_NO_COPY_BACK;
5265 return MoveDoor(door_state);
5268 unsigned int GetDoorState(void)
5270 return MoveDoor(DOOR_GET_STATE);
5273 unsigned int SetDoorState(unsigned int door_state)
5275 return MoveDoor(door_state | DOOR_SET_STATE);
5278 static int euclid(int a, int b)
5280 return (b ? euclid(b, a % b) : a);
5283 unsigned int MoveDoor(unsigned int door_state)
5285 struct Rect door_rect_list[] =
5287 { DX, DY, DXSIZE, DYSIZE },
5288 { VX, VY, VXSIZE, VYSIZE }
5290 static int door1 = DOOR_CLOSE_1;
5291 static int door2 = DOOR_CLOSE_2;
5292 DelayCounter door_delay = { 0 };
5295 if (door_state == DOOR_GET_STATE)
5296 return (door1 | door2);
5298 if (door_state & DOOR_SET_STATE)
5300 if (door_state & DOOR_ACTION_1)
5301 door1 = door_state & DOOR_ACTION_1;
5302 if (door_state & DOOR_ACTION_2)
5303 door2 = door_state & DOOR_ACTION_2;
5305 return (door1 | door2);
5308 if (!(door_state & DOOR_FORCE_REDRAW))
5310 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5311 door_state &= ~DOOR_OPEN_1;
5312 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5313 door_state &= ~DOOR_CLOSE_1;
5314 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5315 door_state &= ~DOOR_OPEN_2;
5316 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5317 door_state &= ~DOOR_CLOSE_2;
5320 if (global.autoplay_leveldir)
5322 door_state |= DOOR_NO_DELAY;
5323 door_state &= ~DOOR_CLOSE_ALL;
5326 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5327 door_state |= DOOR_NO_DELAY;
5329 if (door_state & DOOR_ACTION)
5331 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5332 boolean door_panel_drawn[NUM_DOORS];
5333 boolean panel_has_doors[NUM_DOORS];
5334 boolean door_part_skip[MAX_DOOR_PARTS];
5335 boolean door_part_done[MAX_DOOR_PARTS];
5336 boolean door_part_done_all;
5337 int num_steps[MAX_DOOR_PARTS];
5338 int max_move_delay = 0; // delay for complete animations of all doors
5339 int max_step_delay = 0; // delay (ms) between two animation frames
5340 int num_move_steps = 0; // number of animation steps for all doors
5341 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5342 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5346 for (i = 0; i < NUM_DOORS; i++)
5347 panel_has_doors[i] = FALSE;
5349 for (i = 0; i < MAX_DOOR_PARTS; i++)
5351 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5352 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5353 int door_token = dpc->door_token;
5355 door_part_done[i] = FALSE;
5356 door_part_skip[i] = (!(door_state & door_token) ||
5360 for (i = 0; i < MAX_DOOR_PARTS; i++)
5362 int nr = door_part_order[i].nr;
5363 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5364 struct DoorPartPosInfo *pos = dpc->pos;
5365 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5366 int door_token = dpc->door_token;
5367 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5368 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5369 int step_xoffset = ABS(pos->step_xoffset);
5370 int step_yoffset = ABS(pos->step_yoffset);
5371 int step_delay = pos->step_delay;
5372 int current_door_state = door_state & door_token;
5373 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5374 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5375 boolean part_opening = (is_panel ? door_closing : door_opening);
5376 int start_step = (part_opening ? pos->start_step_opening :
5377 pos->start_step_closing);
5378 float move_xsize = (step_xoffset ? g->width : 0);
5379 float move_ysize = (step_yoffset ? g->height : 0);
5380 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5381 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5382 int move_steps = (move_xsteps && move_ysteps ?
5383 MIN(move_xsteps, move_ysteps) :
5384 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5385 int move_delay = move_steps * step_delay;
5387 if (door_part_skip[nr])
5390 max_move_delay = MAX(max_move_delay, move_delay);
5391 max_step_delay = (max_step_delay == 0 ? step_delay :
5392 euclid(max_step_delay, step_delay));
5393 num_steps[nr] = move_steps;
5397 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5399 panel_has_doors[door_index] = TRUE;
5403 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5405 num_move_steps = max_move_delay / max_step_delay;
5406 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5408 door_delay.value = max_step_delay;
5410 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5412 start = num_move_steps - 1;
5416 // opening door sound has priority over simultaneously closing door
5417 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5419 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5421 if (door_state & DOOR_OPEN_1)
5422 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5423 if (door_state & DOOR_OPEN_2)
5424 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5426 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5428 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5430 if (door_state & DOOR_CLOSE_1)
5431 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5432 if (door_state & DOOR_CLOSE_2)
5433 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5437 for (k = start; k < num_move_steps; k++)
5439 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5441 door_part_done_all = TRUE;
5443 for (i = 0; i < NUM_DOORS; i++)
5444 door_panel_drawn[i] = FALSE;
5446 for (i = 0; i < MAX_DOOR_PARTS; i++)
5448 int nr = door_part_order[i].nr;
5449 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5450 struct DoorPartPosInfo *pos = dpc->pos;
5451 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5452 int door_token = dpc->door_token;
5453 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5454 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5455 boolean is_panel_and_door_has_closed = FALSE;
5456 struct Rect *door_rect = &door_rect_list[door_index];
5457 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5459 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5460 int current_door_state = door_state & door_token;
5461 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5462 boolean door_closing = !door_opening;
5463 boolean part_opening = (is_panel ? door_closing : door_opening);
5464 boolean part_closing = !part_opening;
5465 int start_step = (part_opening ? pos->start_step_opening :
5466 pos->start_step_closing);
5467 int step_delay = pos->step_delay;
5468 int step_factor = step_delay / max_step_delay;
5469 int k1 = (step_factor ? k / step_factor + 1 : k);
5470 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5471 int kk = MAX(0, k2);
5474 int src_x, src_y, src_xx, src_yy;
5475 int dst_x, dst_y, dst_xx, dst_yy;
5478 if (door_part_skip[nr])
5481 if (!(door_state & door_token))
5489 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5490 int kk_door = MAX(0, k2_door);
5491 int sync_frame = kk_door * door_delay.value;
5492 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5494 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5495 &g_src_x, &g_src_y);
5500 if (!door_panel_drawn[door_index])
5502 ClearRectangle(drawto, door_rect->x, door_rect->y,
5503 door_rect->width, door_rect->height);
5505 door_panel_drawn[door_index] = TRUE;
5508 // draw opening or closing door parts
5510 if (pos->step_xoffset < 0) // door part on right side
5513 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5516 if (dst_xx + width > door_rect->width)
5517 width = door_rect->width - dst_xx;
5519 else // door part on left side
5522 dst_xx = pos->x - kk * pos->step_xoffset;
5526 src_xx = ABS(dst_xx);
5530 width = g->width - src_xx;
5532 if (width > door_rect->width)
5533 width = door_rect->width;
5535 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5538 if (pos->step_yoffset < 0) // door part on bottom side
5541 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5544 if (dst_yy + height > door_rect->height)
5545 height = door_rect->height - dst_yy;
5547 else // door part on top side
5550 dst_yy = pos->y - kk * pos->step_yoffset;
5554 src_yy = ABS(dst_yy);
5558 height = g->height - src_yy;
5561 src_x = g_src_x + src_xx;
5562 src_y = g_src_y + src_yy;
5564 dst_x = door_rect->x + dst_xx;
5565 dst_y = door_rect->y + dst_yy;
5567 is_panel_and_door_has_closed =
5570 panel_has_doors[door_index] &&
5571 k >= num_move_steps_doors_only - 1);
5573 if (width >= 0 && width <= g->width &&
5574 height >= 0 && height <= g->height &&
5575 !is_panel_and_door_has_closed)
5577 if (is_panel || !pos->draw_masked)
5578 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5581 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5585 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5587 if ((part_opening && (width < 0 || height < 0)) ||
5588 (part_closing && (width >= g->width && height >= g->height)))
5589 door_part_done[nr] = TRUE;
5591 // continue door part animations, but not panel after door has closed
5592 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5593 door_part_done_all = FALSE;
5596 if (!(door_state & DOOR_NO_DELAY))
5599 HandleGameActions();
5603 SkipUntilDelayReached(&door_delay, &k, last_frame);
5605 // prevent OS (Windows) from complaining about program not responding
5609 if (door_part_done_all)
5613 if (!(door_state & DOOR_NO_DELAY))
5615 // wait for specified door action post delay
5616 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5617 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5618 else if (door_state & DOOR_ACTION_1)
5619 door_delay.value = door_1.post_delay;
5620 else if (door_state & DOOR_ACTION_2)
5621 door_delay.value = door_2.post_delay;
5623 while (!DelayReached(&door_delay))
5626 HandleGameActions();
5633 if (door_state & DOOR_ACTION_1)
5634 door1 = door_state & DOOR_ACTION_1;
5635 if (door_state & DOOR_ACTION_2)
5636 door2 = door_state & DOOR_ACTION_2;
5638 // draw masked border over door area
5639 DrawMaskedBorder(REDRAW_DOOR_1);
5640 DrawMaskedBorder(REDRAW_DOOR_2);
5642 ClearAutoRepeatKeyEvents();
5644 return (door1 | door2);
5647 static boolean useSpecialEditorDoor(void)
5649 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5650 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5652 // do not draw special editor door if editor border defined or redefined
5653 if (graphic_info[graphic].bitmap != NULL || redefined)
5656 // do not draw special editor door if global border defined to be empty
5657 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5660 // do not draw special editor door if viewport definitions do not match
5664 EY + EYSIZE != VY + VYSIZE)
5670 void DrawSpecialEditorDoor(void)
5672 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5673 int top_border_width = gfx1->width;
5674 int top_border_height = gfx1->height;
5675 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5676 int ex = EX - outer_border;
5677 int ey = EY - outer_border;
5678 int vy = VY - outer_border;
5679 int exsize = EXSIZE + 2 * outer_border;
5681 if (!useSpecialEditorDoor())
5684 // draw bigger level editor toolbox window
5685 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5686 top_border_width, top_border_height, ex, ey - top_border_height);
5687 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5688 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5690 redraw_mask |= REDRAW_ALL;
5693 void UndrawSpecialEditorDoor(void)
5695 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5696 int top_border_width = gfx1->width;
5697 int top_border_height = gfx1->height;
5698 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5699 int ex = EX - outer_border;
5700 int ey = EY - outer_border;
5701 int ey_top = ey - top_border_height;
5702 int exsize = EXSIZE + 2 * outer_border;
5703 int eysize = EYSIZE + 2 * outer_border;
5705 if (!useSpecialEditorDoor())
5708 // draw normal tape recorder window
5709 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5711 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5712 ex, ey_top, top_border_width, top_border_height,
5714 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5715 ex, ey, exsize, eysize, ex, ey);
5719 // if screen background is set to "[NONE]", clear editor toolbox window
5720 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5721 ClearRectangle(drawto, ex, ey, exsize, eysize);
5724 redraw_mask |= REDRAW_ALL;
5728 // ---------- new tool button stuff -------------------------------------------
5733 struct TextPosInfo *pos;
5735 boolean is_touch_button;
5737 } toolbutton_info[NUM_TOOL_BUTTONS] =
5740 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5741 TOOL_CTRL_ID_YES, FALSE, "yes"
5744 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5745 TOOL_CTRL_ID_NO, FALSE, "no"
5748 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5749 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5752 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5753 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5756 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5757 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5760 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5761 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5764 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5765 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5768 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5769 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5772 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5773 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5776 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5777 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5781 void CreateToolButtons(void)
5785 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5787 int graphic = toolbutton_info[i].graphic;
5788 struct GraphicInfo *gfx = &graphic_info[graphic];
5789 struct TextPosInfo *pos = toolbutton_info[i].pos;
5790 struct GadgetInfo *gi;
5791 Bitmap *deco_bitmap = None;
5792 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5793 unsigned int event_mask = GD_EVENT_RELEASED;
5794 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5795 int base_x = (is_touch_button ? 0 : DX);
5796 int base_y = (is_touch_button ? 0 : DY);
5797 int gd_x = gfx->src_x;
5798 int gd_y = gfx->src_y;
5799 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5800 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5805 // do not use touch buttons if overlay touch buttons are disabled
5806 if (is_touch_button && !setup.touch.overlay_buttons)
5809 if (global.use_envelope_request && !is_touch_button)
5811 setRequestPosition(&base_x, &base_y, TRUE);
5813 // check if request buttons are outside of envelope and fix, if needed
5814 if (x < 0 || x + gfx->width > request.width ||
5815 y < 0 || y + gfx->height > request.height)
5817 if (id == TOOL_CTRL_ID_YES)
5820 y = request.height - 2 * request.border_size - gfx->height;
5822 else if (id == TOOL_CTRL_ID_NO)
5824 x = request.width - 2 * request.border_size - gfx->width;
5825 y = request.height - 2 * request.border_size - gfx->height;
5827 else if (id == TOOL_CTRL_ID_CONFIRM)
5829 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5830 y = request.height - 2 * request.border_size - gfx->height;
5832 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5834 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5836 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5837 y = request.height - 2 * request.border_size - gfx->height * 2;
5839 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5840 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5845 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5848 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5850 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5851 pos->size, &deco_bitmap, &deco_x, &deco_y);
5852 deco_xpos = (gfx->width - pos->size) / 2;
5853 deco_ypos = (gfx->height - pos->size) / 2;
5856 gi = CreateGadget(GDI_CUSTOM_ID, id,
5857 GDI_IMAGE_ID, graphic,
5858 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5861 GDI_WIDTH, gfx->width,
5862 GDI_HEIGHT, gfx->height,
5863 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5864 GDI_STATE, GD_BUTTON_UNPRESSED,
5865 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5866 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5867 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5868 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5869 GDI_DECORATION_SIZE, pos->size, pos->size,
5870 GDI_DECORATION_SHIFTING, 1, 1,
5871 GDI_DIRECT_DRAW, FALSE,
5872 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5873 GDI_EVENT_MASK, event_mask,
5874 GDI_CALLBACK_ACTION, HandleToolButtons,
5878 Fail("cannot create gadget");
5880 tool_gadget[id] = gi;
5884 void FreeToolButtons(void)
5888 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5889 FreeGadget(tool_gadget[i]);
5892 static void MapToolButtons(unsigned int req_state)
5894 if (req_state & REQ_ASK)
5896 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5897 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5898 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5899 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5901 else if (req_state & REQ_CONFIRM)
5903 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5904 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5906 else if (req_state & REQ_PLAYER)
5908 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5909 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5910 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5911 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5915 static void UnmapToolButtons(void)
5919 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5920 UnmapGadget(tool_gadget[i]);
5923 static void HandleToolButtons(struct GadgetInfo *gi)
5925 request_gadget_id = gi->custom_id;
5928 static struct Mapping_EM_to_RND_object
5931 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5932 boolean is_backside; // backside of moving element
5938 em_object_mapping_list[GAME_TILE_MAX + 1] =
5941 Zborder, FALSE, FALSE,
5945 Zplayer, FALSE, FALSE,
5954 Ztank, FALSE, FALSE,
5958 Zeater, FALSE, FALSE,
5962 Zdynamite, FALSE, FALSE,
5966 Zboom, FALSE, FALSE,
5971 Xchain, FALSE, FALSE,
5972 EL_DEFAULT, ACTION_EXPLODING, -1
5975 Xboom_bug, FALSE, FALSE,
5976 EL_BUG, ACTION_EXPLODING, -1
5979 Xboom_tank, FALSE, FALSE,
5980 EL_SPACESHIP, ACTION_EXPLODING, -1
5983 Xboom_android, FALSE, FALSE,
5984 EL_EMC_ANDROID, ACTION_OTHER, -1
5987 Xboom_1, FALSE, FALSE,
5988 EL_DEFAULT, ACTION_EXPLODING, -1
5991 Xboom_2, FALSE, FALSE,
5992 EL_DEFAULT, ACTION_EXPLODING, -1
5996 Xblank, TRUE, FALSE,
6001 Xsplash_e, FALSE, FALSE,
6002 EL_ACID_SPLASH_RIGHT, -1, -1
6005 Xsplash_w, FALSE, FALSE,
6006 EL_ACID_SPLASH_LEFT, -1, -1
6010 Xplant, TRUE, FALSE,
6011 EL_EMC_PLANT, -1, -1
6014 Yplant, FALSE, FALSE,
6015 EL_EMC_PLANT, -1, -1
6019 Xacid_1, TRUE, FALSE,
6023 Xacid_2, FALSE, FALSE,
6027 Xacid_3, FALSE, FALSE,
6031 Xacid_4, FALSE, FALSE,
6035 Xacid_5, FALSE, FALSE,
6039 Xacid_6, FALSE, FALSE,
6043 Xacid_7, FALSE, FALSE,
6047 Xacid_8, FALSE, FALSE,
6052 Xfake_acid_1, TRUE, FALSE,
6053 EL_EMC_FAKE_ACID, -1, -1
6056 Xfake_acid_2, FALSE, FALSE,
6057 EL_EMC_FAKE_ACID, -1, -1
6060 Xfake_acid_3, FALSE, FALSE,
6061 EL_EMC_FAKE_ACID, -1, -1
6064 Xfake_acid_4, FALSE, FALSE,
6065 EL_EMC_FAKE_ACID, -1, -1
6068 Xfake_acid_5, FALSE, FALSE,
6069 EL_EMC_FAKE_ACID, -1, -1
6072 Xfake_acid_6, FALSE, FALSE,
6073 EL_EMC_FAKE_ACID, -1, -1
6076 Xfake_acid_7, FALSE, FALSE,
6077 EL_EMC_FAKE_ACID, -1, -1
6080 Xfake_acid_8, FALSE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6085 Xfake_acid_1_player, FALSE, FALSE,
6086 EL_EMC_FAKE_ACID, -1, -1
6089 Xfake_acid_2_player, FALSE, FALSE,
6090 EL_EMC_FAKE_ACID, -1, -1
6093 Xfake_acid_3_player, FALSE, FALSE,
6094 EL_EMC_FAKE_ACID, -1, -1
6097 Xfake_acid_4_player, FALSE, FALSE,
6098 EL_EMC_FAKE_ACID, -1, -1
6101 Xfake_acid_5_player, FALSE, FALSE,
6102 EL_EMC_FAKE_ACID, -1, -1
6105 Xfake_acid_6_player, FALSE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_7_player, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_8_player, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6118 Xgrass, TRUE, FALSE,
6119 EL_EMC_GRASS, -1, -1
6122 Ygrass_nB, FALSE, FALSE,
6123 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6126 Ygrass_eB, FALSE, FALSE,
6127 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6130 Ygrass_sB, FALSE, FALSE,
6131 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6134 Ygrass_wB, FALSE, FALSE,
6135 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6143 Ydirt_nB, FALSE, FALSE,
6144 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6147 Ydirt_eB, FALSE, FALSE,
6148 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6151 Ydirt_sB, FALSE, FALSE,
6152 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6155 Ydirt_wB, FALSE, FALSE,
6156 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6160 Xandroid, TRUE, FALSE,
6161 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6164 Xandroid_1_n, FALSE, FALSE,
6165 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6168 Xandroid_2_n, FALSE, FALSE,
6169 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6172 Xandroid_1_e, FALSE, FALSE,
6173 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6176 Xandroid_2_e, FALSE, FALSE,
6177 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6180 Xandroid_1_w, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6184 Xandroid_2_w, FALSE, FALSE,
6185 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6188 Xandroid_1_s, FALSE, FALSE,
6189 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6192 Xandroid_2_s, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6196 Yandroid_n, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6200 Yandroid_nB, FALSE, TRUE,
6201 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6204 Yandroid_ne, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6208 Yandroid_neB, FALSE, TRUE,
6209 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6212 Yandroid_e, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6216 Yandroid_eB, FALSE, TRUE,
6217 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6220 Yandroid_se, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6224 Yandroid_seB, FALSE, TRUE,
6225 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6228 Yandroid_s, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6232 Yandroid_sB, FALSE, TRUE,
6233 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6236 Yandroid_sw, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6240 Yandroid_swB, FALSE, TRUE,
6241 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6244 Yandroid_w, FALSE, FALSE,
6245 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6248 Yandroid_wB, FALSE, TRUE,
6249 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6252 Yandroid_nw, FALSE, FALSE,
6253 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6256 Yandroid_nwB, FALSE, TRUE,
6257 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6261 Xeater_n, TRUE, FALSE,
6262 EL_YAMYAM_UP, -1, -1
6265 Xeater_e, TRUE, FALSE,
6266 EL_YAMYAM_RIGHT, -1, -1
6269 Xeater_w, TRUE, FALSE,
6270 EL_YAMYAM_LEFT, -1, -1
6273 Xeater_s, TRUE, FALSE,
6274 EL_YAMYAM_DOWN, -1, -1
6277 Yeater_n, FALSE, FALSE,
6278 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6281 Yeater_nB, FALSE, TRUE,
6282 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6285 Yeater_e, FALSE, FALSE,
6286 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6289 Yeater_eB, FALSE, TRUE,
6290 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6293 Yeater_s, FALSE, FALSE,
6294 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6297 Yeater_sB, FALSE, TRUE,
6298 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6301 Yeater_w, FALSE, FALSE,
6302 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6305 Yeater_wB, FALSE, TRUE,
6306 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6309 Yeater_stone, FALSE, FALSE,
6310 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6313 Yeater_spring, FALSE, FALSE,
6314 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6318 Xalien, TRUE, FALSE,
6322 Xalien_pause, FALSE, FALSE,
6326 Yalien_n, FALSE, FALSE,
6327 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6330 Yalien_nB, FALSE, TRUE,
6331 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6334 Yalien_e, FALSE, FALSE,
6335 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6338 Yalien_eB, FALSE, TRUE,
6339 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6342 Yalien_s, FALSE, FALSE,
6343 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6346 Yalien_sB, FALSE, TRUE,
6347 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6350 Yalien_w, FALSE, FALSE,
6351 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6354 Yalien_wB, FALSE, TRUE,
6355 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6358 Yalien_stone, FALSE, FALSE,
6359 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6362 Yalien_spring, FALSE, FALSE,
6363 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6367 Xbug_1_n, TRUE, FALSE,
6371 Xbug_1_e, TRUE, FALSE,
6372 EL_BUG_RIGHT, -1, -1
6375 Xbug_1_s, TRUE, FALSE,
6379 Xbug_1_w, TRUE, FALSE,
6383 Xbug_2_n, FALSE, FALSE,
6387 Xbug_2_e, FALSE, FALSE,
6388 EL_BUG_RIGHT, -1, -1
6391 Xbug_2_s, FALSE, FALSE,
6395 Xbug_2_w, FALSE, FALSE,
6399 Ybug_n, FALSE, FALSE,
6400 EL_BUG, ACTION_MOVING, MV_BIT_UP
6403 Ybug_nB, FALSE, TRUE,
6404 EL_BUG, ACTION_MOVING, MV_BIT_UP
6407 Ybug_e, FALSE, FALSE,
6408 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6411 Ybug_eB, FALSE, TRUE,
6412 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6415 Ybug_s, FALSE, FALSE,
6416 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6419 Ybug_sB, FALSE, TRUE,
6420 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6423 Ybug_w, FALSE, FALSE,
6424 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6427 Ybug_wB, FALSE, TRUE,
6428 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6431 Ybug_w_n, FALSE, FALSE,
6432 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6435 Ybug_n_e, FALSE, FALSE,
6436 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6439 Ybug_e_s, FALSE, FALSE,
6440 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6443 Ybug_s_w, FALSE, FALSE,
6444 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6447 Ybug_e_n, FALSE, FALSE,
6448 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6451 Ybug_s_e, FALSE, FALSE,
6452 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6455 Ybug_w_s, FALSE, FALSE,
6456 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6459 Ybug_n_w, FALSE, FALSE,
6460 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6463 Ybug_stone, FALSE, FALSE,
6464 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6467 Ybug_spring, FALSE, FALSE,
6468 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6472 Xtank_1_n, TRUE, FALSE,
6473 EL_SPACESHIP_UP, -1, -1
6476 Xtank_1_e, TRUE, FALSE,
6477 EL_SPACESHIP_RIGHT, -1, -1
6480 Xtank_1_s, TRUE, FALSE,
6481 EL_SPACESHIP_DOWN, -1, -1
6484 Xtank_1_w, TRUE, FALSE,
6485 EL_SPACESHIP_LEFT, -1, -1
6488 Xtank_2_n, FALSE, FALSE,
6489 EL_SPACESHIP_UP, -1, -1
6492 Xtank_2_e, FALSE, FALSE,
6493 EL_SPACESHIP_RIGHT, -1, -1
6496 Xtank_2_s, FALSE, FALSE,
6497 EL_SPACESHIP_DOWN, -1, -1
6500 Xtank_2_w, FALSE, FALSE,
6501 EL_SPACESHIP_LEFT, -1, -1
6504 Ytank_n, FALSE, FALSE,
6505 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6508 Ytank_nB, FALSE, TRUE,
6509 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6512 Ytank_e, FALSE, FALSE,
6513 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6516 Ytank_eB, FALSE, TRUE,
6517 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6520 Ytank_s, FALSE, FALSE,
6521 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6524 Ytank_sB, FALSE, TRUE,
6525 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6528 Ytank_w, FALSE, FALSE,
6529 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6532 Ytank_wB, FALSE, TRUE,
6533 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6536 Ytank_w_n, FALSE, FALSE,
6537 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6540 Ytank_n_e, FALSE, FALSE,
6541 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6544 Ytank_e_s, FALSE, FALSE,
6545 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6548 Ytank_s_w, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6552 Ytank_e_n, FALSE, FALSE,
6553 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6556 Ytank_s_e, FALSE, FALSE,
6557 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6560 Ytank_w_s, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6564 Ytank_n_w, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6568 Ytank_stone, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6572 Ytank_spring, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6577 Xemerald, TRUE, FALSE,
6581 Xemerald_pause, FALSE, FALSE,
6585 Xemerald_fall, FALSE, FALSE,
6589 Xemerald_shine, FALSE, FALSE,
6590 EL_EMERALD, ACTION_TWINKLING, -1
6593 Yemerald_s, FALSE, FALSE,
6594 EL_EMERALD, ACTION_FALLING, -1
6597 Yemerald_sB, FALSE, TRUE,
6598 EL_EMERALD, ACTION_FALLING, -1
6601 Yemerald_e, FALSE, FALSE,
6602 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6605 Yemerald_eB, FALSE, TRUE,
6606 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6609 Yemerald_w, FALSE, FALSE,
6610 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6613 Yemerald_wB, FALSE, TRUE,
6614 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6617 Yemerald_blank, FALSE, FALSE,
6618 EL_EMERALD, ACTION_COLLECTING, -1
6622 Xdiamond, TRUE, FALSE,
6626 Xdiamond_pause, FALSE, FALSE,
6630 Xdiamond_fall, FALSE, FALSE,
6634 Xdiamond_shine, FALSE, FALSE,
6635 EL_DIAMOND, ACTION_TWINKLING, -1
6638 Ydiamond_s, FALSE, FALSE,
6639 EL_DIAMOND, ACTION_FALLING, -1
6642 Ydiamond_sB, FALSE, TRUE,
6643 EL_DIAMOND, ACTION_FALLING, -1
6646 Ydiamond_e, FALSE, FALSE,
6647 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6650 Ydiamond_eB, FALSE, TRUE,
6651 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6654 Ydiamond_w, FALSE, FALSE,
6655 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6658 Ydiamond_wB, FALSE, TRUE,
6659 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6662 Ydiamond_blank, FALSE, FALSE,
6663 EL_DIAMOND, ACTION_COLLECTING, -1
6666 Ydiamond_stone, FALSE, FALSE,
6667 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6671 Xstone, TRUE, FALSE,
6675 Xstone_pause, FALSE, FALSE,
6679 Xstone_fall, FALSE, FALSE,
6683 Ystone_s, FALSE, FALSE,
6684 EL_ROCK, ACTION_FALLING, -1
6687 Ystone_sB, FALSE, TRUE,
6688 EL_ROCK, ACTION_FALLING, -1
6691 Ystone_e, FALSE, FALSE,
6692 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6695 Ystone_eB, FALSE, TRUE,
6696 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6699 Ystone_w, FALSE, FALSE,
6700 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6703 Ystone_wB, FALSE, TRUE,
6704 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6712 Xbomb_pause, FALSE, FALSE,
6716 Xbomb_fall, FALSE, FALSE,
6720 Ybomb_s, FALSE, FALSE,
6721 EL_BOMB, ACTION_FALLING, -1
6724 Ybomb_sB, FALSE, TRUE,
6725 EL_BOMB, ACTION_FALLING, -1
6728 Ybomb_e, FALSE, FALSE,
6729 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6732 Ybomb_eB, FALSE, TRUE,
6733 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6736 Ybomb_w, FALSE, FALSE,
6737 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6740 Ybomb_wB, FALSE, TRUE,
6741 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6744 Ybomb_blank, FALSE, FALSE,
6745 EL_BOMB, ACTION_ACTIVATING, -1
6753 Xnut_pause, FALSE, FALSE,
6757 Xnut_fall, FALSE, FALSE,
6761 Ynut_s, FALSE, FALSE,
6762 EL_NUT, ACTION_FALLING, -1
6765 Ynut_sB, FALSE, TRUE,
6766 EL_NUT, ACTION_FALLING, -1
6769 Ynut_e, FALSE, FALSE,
6770 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6773 Ynut_eB, FALSE, TRUE,
6774 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6777 Ynut_w, FALSE, FALSE,
6778 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6781 Ynut_wB, FALSE, TRUE,
6782 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6785 Ynut_stone, FALSE, FALSE,
6786 EL_NUT, ACTION_BREAKING, -1
6790 Xspring, TRUE, FALSE,
6794 Xspring_pause, FALSE, FALSE,
6798 Xspring_e, TRUE, FALSE,
6799 EL_SPRING_RIGHT, -1, -1
6802 Xspring_w, TRUE, FALSE,
6803 EL_SPRING_LEFT, -1, -1
6806 Xspring_fall, FALSE, FALSE,
6810 Yspring_s, FALSE, FALSE,
6811 EL_SPRING, ACTION_FALLING, -1
6814 Yspring_sB, FALSE, TRUE,
6815 EL_SPRING, ACTION_FALLING, -1
6818 Yspring_e, FALSE, FALSE,
6819 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6822 Yspring_eB, FALSE, TRUE,
6823 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6826 Yspring_w, FALSE, FALSE,
6827 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6830 Yspring_wB, FALSE, TRUE,
6831 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6834 Yspring_alien_e, FALSE, FALSE,
6835 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6838 Yspring_alien_eB, FALSE, TRUE,
6839 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6842 Yspring_alien_w, FALSE, FALSE,
6843 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6846 Yspring_alien_wB, FALSE, TRUE,
6847 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6851 Xpush_emerald_e, FALSE, FALSE,
6852 EL_EMERALD, -1, MV_BIT_RIGHT
6855 Xpush_emerald_w, FALSE, FALSE,
6856 EL_EMERALD, -1, MV_BIT_LEFT
6859 Xpush_diamond_e, FALSE, FALSE,
6860 EL_DIAMOND, -1, MV_BIT_RIGHT
6863 Xpush_diamond_w, FALSE, FALSE,
6864 EL_DIAMOND, -1, MV_BIT_LEFT
6867 Xpush_stone_e, FALSE, FALSE,
6868 EL_ROCK, -1, MV_BIT_RIGHT
6871 Xpush_stone_w, FALSE, FALSE,
6872 EL_ROCK, -1, MV_BIT_LEFT
6875 Xpush_bomb_e, FALSE, FALSE,
6876 EL_BOMB, -1, MV_BIT_RIGHT
6879 Xpush_bomb_w, FALSE, FALSE,
6880 EL_BOMB, -1, MV_BIT_LEFT
6883 Xpush_nut_e, FALSE, FALSE,
6884 EL_NUT, -1, MV_BIT_RIGHT
6887 Xpush_nut_w, FALSE, FALSE,
6888 EL_NUT, -1, MV_BIT_LEFT
6891 Xpush_spring_e, FALSE, FALSE,
6892 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6895 Xpush_spring_w, FALSE, FALSE,
6896 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6900 Xdynamite, TRUE, FALSE,
6901 EL_EM_DYNAMITE, -1, -1
6904 Ydynamite_blank, FALSE, FALSE,
6905 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6908 Xdynamite_1, TRUE, FALSE,
6909 EL_EM_DYNAMITE_ACTIVE, -1, -1
6912 Xdynamite_2, FALSE, FALSE,
6913 EL_EM_DYNAMITE_ACTIVE, -1, -1
6916 Xdynamite_3, FALSE, FALSE,
6917 EL_EM_DYNAMITE_ACTIVE, -1, -1
6920 Xdynamite_4, FALSE, FALSE,
6921 EL_EM_DYNAMITE_ACTIVE, -1, -1
6925 Xkey_1, TRUE, FALSE,
6929 Xkey_2, TRUE, FALSE,
6933 Xkey_3, TRUE, FALSE,
6937 Xkey_4, TRUE, FALSE,
6941 Xkey_5, TRUE, FALSE,
6942 EL_EMC_KEY_5, -1, -1
6945 Xkey_6, TRUE, FALSE,
6946 EL_EMC_KEY_6, -1, -1
6949 Xkey_7, TRUE, FALSE,
6950 EL_EMC_KEY_7, -1, -1
6953 Xkey_8, TRUE, FALSE,
6954 EL_EMC_KEY_8, -1, -1
6958 Xdoor_1, TRUE, FALSE,
6959 EL_EM_GATE_1, -1, -1
6962 Xdoor_2, TRUE, FALSE,
6963 EL_EM_GATE_2, -1, -1
6966 Xdoor_3, TRUE, FALSE,
6967 EL_EM_GATE_3, -1, -1
6970 Xdoor_4, TRUE, FALSE,
6971 EL_EM_GATE_4, -1, -1
6974 Xdoor_5, TRUE, FALSE,
6975 EL_EMC_GATE_5, -1, -1
6978 Xdoor_6, TRUE, FALSE,
6979 EL_EMC_GATE_6, -1, -1
6982 Xdoor_7, TRUE, FALSE,
6983 EL_EMC_GATE_7, -1, -1
6986 Xdoor_8, TRUE, FALSE,
6987 EL_EMC_GATE_8, -1, -1
6991 Xfake_door_1, TRUE, FALSE,
6992 EL_EM_GATE_1_GRAY, -1, -1
6995 Xfake_door_2, TRUE, FALSE,
6996 EL_EM_GATE_2_GRAY, -1, -1
6999 Xfake_door_3, TRUE, FALSE,
7000 EL_EM_GATE_3_GRAY, -1, -1
7003 Xfake_door_4, TRUE, FALSE,
7004 EL_EM_GATE_4_GRAY, -1, -1
7007 Xfake_door_5, TRUE, FALSE,
7008 EL_EMC_GATE_5_GRAY, -1, -1
7011 Xfake_door_6, TRUE, FALSE,
7012 EL_EMC_GATE_6_GRAY, -1, -1
7015 Xfake_door_7, TRUE, FALSE,
7016 EL_EMC_GATE_7_GRAY, -1, -1
7019 Xfake_door_8, TRUE, FALSE,
7020 EL_EMC_GATE_8_GRAY, -1, -1
7024 Xballoon, TRUE, FALSE,
7028 Yballoon_n, FALSE, FALSE,
7029 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7032 Yballoon_nB, FALSE, TRUE,
7033 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7036 Yballoon_e, FALSE, FALSE,
7037 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7040 Yballoon_eB, FALSE, TRUE,
7041 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7044 Yballoon_s, FALSE, FALSE,
7045 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7048 Yballoon_sB, FALSE, TRUE,
7049 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7052 Yballoon_w, FALSE, FALSE,
7053 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7056 Yballoon_wB, FALSE, TRUE,
7057 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7061 Xball_1, TRUE, FALSE,
7062 EL_EMC_MAGIC_BALL, -1, -1
7065 Yball_1, FALSE, FALSE,
7066 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7069 Xball_2, FALSE, FALSE,
7070 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7073 Yball_2, FALSE, FALSE,
7074 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7077 Yball_blank, FALSE, FALSE,
7078 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7082 Xamoeba_1, TRUE, FALSE,
7083 EL_AMOEBA_DRY, ACTION_OTHER, -1
7086 Xamoeba_2, FALSE, FALSE,
7087 EL_AMOEBA_DRY, ACTION_OTHER, -1
7090 Xamoeba_3, FALSE, FALSE,
7091 EL_AMOEBA_DRY, ACTION_OTHER, -1
7094 Xamoeba_4, FALSE, FALSE,
7095 EL_AMOEBA_DRY, ACTION_OTHER, -1
7098 Xamoeba_5, TRUE, FALSE,
7099 EL_AMOEBA_WET, ACTION_OTHER, -1
7102 Xamoeba_6, FALSE, FALSE,
7103 EL_AMOEBA_WET, ACTION_OTHER, -1
7106 Xamoeba_7, FALSE, FALSE,
7107 EL_AMOEBA_WET, ACTION_OTHER, -1
7110 Xamoeba_8, FALSE, FALSE,
7111 EL_AMOEBA_WET, ACTION_OTHER, -1
7116 EL_AMOEBA_DROP, ACTION_GROWING, -1
7119 Xdrip_fall, FALSE, FALSE,
7120 EL_AMOEBA_DROP, -1, -1
7123 Xdrip_stretch, FALSE, FALSE,
7124 EL_AMOEBA_DROP, ACTION_FALLING, -1
7127 Xdrip_stretchB, FALSE, TRUE,
7128 EL_AMOEBA_DROP, ACTION_FALLING, -1
7131 Ydrip_1_s, FALSE, FALSE,
7132 EL_AMOEBA_DROP, ACTION_FALLING, -1
7135 Ydrip_1_sB, FALSE, TRUE,
7136 EL_AMOEBA_DROP, ACTION_FALLING, -1
7139 Ydrip_2_s, FALSE, FALSE,
7140 EL_AMOEBA_DROP, ACTION_FALLING, -1
7143 Ydrip_2_sB, FALSE, TRUE,
7144 EL_AMOEBA_DROP, ACTION_FALLING, -1
7148 Xwonderwall, TRUE, FALSE,
7149 EL_MAGIC_WALL, -1, -1
7152 Ywonderwall, FALSE, FALSE,
7153 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7157 Xwheel, TRUE, FALSE,
7158 EL_ROBOT_WHEEL, -1, -1
7161 Ywheel, FALSE, FALSE,
7162 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7166 Xswitch, TRUE, FALSE,
7167 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7170 Yswitch, FALSE, FALSE,
7171 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7175 Xbumper, TRUE, FALSE,
7176 EL_EMC_SPRING_BUMPER, -1, -1
7179 Ybumper, FALSE, FALSE,
7180 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7184 Xacid_nw, TRUE, FALSE,
7185 EL_ACID_POOL_TOPLEFT, -1, -1
7188 Xacid_ne, TRUE, FALSE,
7189 EL_ACID_POOL_TOPRIGHT, -1, -1
7192 Xacid_sw, TRUE, FALSE,
7193 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7196 Xacid_s, TRUE, FALSE,
7197 EL_ACID_POOL_BOTTOM, -1, -1
7200 Xacid_se, TRUE, FALSE,
7201 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7205 Xfake_blank, TRUE, FALSE,
7206 EL_INVISIBLE_WALL, -1, -1
7209 Yfake_blank, FALSE, FALSE,
7210 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7214 Xfake_grass, TRUE, FALSE,
7215 EL_EMC_FAKE_GRASS, -1, -1
7218 Yfake_grass, FALSE, FALSE,
7219 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7223 Xfake_amoeba, TRUE, FALSE,
7224 EL_EMC_DRIPPER, -1, -1
7227 Yfake_amoeba, FALSE, FALSE,
7228 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7232 Xlenses, TRUE, FALSE,
7233 EL_EMC_LENSES, -1, -1
7237 Xmagnify, TRUE, FALSE,
7238 EL_EMC_MAGNIFIER, -1, -1
7243 EL_QUICKSAND_EMPTY, -1, -1
7246 Xsand_stone, TRUE, FALSE,
7247 EL_QUICKSAND_FULL, -1, -1
7250 Xsand_stonein_1, FALSE, TRUE,
7251 EL_ROCK, ACTION_FILLING, -1
7254 Xsand_stonein_2, FALSE, TRUE,
7255 EL_ROCK, ACTION_FILLING, -1
7258 Xsand_stonein_3, FALSE, TRUE,
7259 EL_ROCK, ACTION_FILLING, -1
7262 Xsand_stonein_4, FALSE, TRUE,
7263 EL_ROCK, ACTION_FILLING, -1
7266 Xsand_sandstone_1, FALSE, FALSE,
7267 EL_QUICKSAND_FILLING, -1, -1
7270 Xsand_sandstone_2, FALSE, FALSE,
7271 EL_QUICKSAND_FILLING, -1, -1
7274 Xsand_sandstone_3, FALSE, FALSE,
7275 EL_QUICKSAND_FILLING, -1, -1
7278 Xsand_sandstone_4, FALSE, FALSE,
7279 EL_QUICKSAND_FILLING, -1, -1
7282 Xsand_stonesand_1, FALSE, FALSE,
7283 EL_QUICKSAND_EMPTYING, -1, -1
7286 Xsand_stonesand_2, FALSE, FALSE,
7287 EL_QUICKSAND_EMPTYING, -1, -1
7290 Xsand_stonesand_3, FALSE, FALSE,
7291 EL_QUICKSAND_EMPTYING, -1, -1
7294 Xsand_stonesand_4, FALSE, FALSE,
7295 EL_QUICKSAND_EMPTYING, -1, -1
7298 Xsand_stoneout_1, FALSE, FALSE,
7299 EL_ROCK, ACTION_EMPTYING, -1
7302 Xsand_stoneout_2, FALSE, FALSE,
7303 EL_ROCK, ACTION_EMPTYING, -1
7306 Xsand_stonesand_quickout_1, FALSE, FALSE,
7307 EL_QUICKSAND_EMPTYING, -1, -1
7310 Xsand_stonesand_quickout_2, FALSE, FALSE,
7311 EL_QUICKSAND_EMPTYING, -1, -1
7315 Xslide_ns, TRUE, FALSE,
7316 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7319 Yslide_ns_blank, FALSE, FALSE,
7320 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7323 Xslide_ew, TRUE, FALSE,
7324 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7327 Yslide_ew_blank, FALSE, FALSE,
7328 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7332 Xwind_n, TRUE, FALSE,
7333 EL_BALLOON_SWITCH_UP, -1, -1
7336 Xwind_e, TRUE, FALSE,
7337 EL_BALLOON_SWITCH_RIGHT, -1, -1
7340 Xwind_s, TRUE, FALSE,
7341 EL_BALLOON_SWITCH_DOWN, -1, -1
7344 Xwind_w, TRUE, FALSE,
7345 EL_BALLOON_SWITCH_LEFT, -1, -1
7348 Xwind_any, TRUE, FALSE,
7349 EL_BALLOON_SWITCH_ANY, -1, -1
7352 Xwind_stop, TRUE, FALSE,
7353 EL_BALLOON_SWITCH_NONE, -1, -1
7358 EL_EM_EXIT_CLOSED, -1, -1
7361 Xexit_1, TRUE, FALSE,
7362 EL_EM_EXIT_OPEN, -1, -1
7365 Xexit_2, FALSE, FALSE,
7366 EL_EM_EXIT_OPEN, -1, -1
7369 Xexit_3, FALSE, FALSE,
7370 EL_EM_EXIT_OPEN, -1, -1
7374 Xpause, FALSE, FALSE,
7379 Xwall_1, TRUE, FALSE,
7383 Xwall_2, TRUE, FALSE,
7384 EL_EMC_WALL_14, -1, -1
7387 Xwall_3, TRUE, FALSE,
7388 EL_EMC_WALL_15, -1, -1
7391 Xwall_4, TRUE, FALSE,
7392 EL_EMC_WALL_16, -1, -1
7396 Xroundwall_1, TRUE, FALSE,
7397 EL_WALL_SLIPPERY, -1, -1
7400 Xroundwall_2, TRUE, FALSE,
7401 EL_EMC_WALL_SLIPPERY_2, -1, -1
7404 Xroundwall_3, TRUE, FALSE,
7405 EL_EMC_WALL_SLIPPERY_3, -1, -1
7408 Xroundwall_4, TRUE, FALSE,
7409 EL_EMC_WALL_SLIPPERY_4, -1, -1
7413 Xsteel_1, TRUE, FALSE,
7414 EL_STEELWALL, -1, -1
7417 Xsteel_2, TRUE, FALSE,
7418 EL_EMC_STEELWALL_2, -1, -1
7421 Xsteel_3, TRUE, FALSE,
7422 EL_EMC_STEELWALL_3, -1, -1
7425 Xsteel_4, TRUE, FALSE,
7426 EL_EMC_STEELWALL_4, -1, -1
7430 Xdecor_1, TRUE, FALSE,
7431 EL_EMC_WALL_8, -1, -1
7434 Xdecor_2, TRUE, FALSE,
7435 EL_EMC_WALL_6, -1, -1
7438 Xdecor_3, TRUE, FALSE,
7439 EL_EMC_WALL_4, -1, -1
7442 Xdecor_4, TRUE, FALSE,
7443 EL_EMC_WALL_7, -1, -1
7446 Xdecor_5, TRUE, FALSE,
7447 EL_EMC_WALL_5, -1, -1
7450 Xdecor_6, TRUE, FALSE,
7451 EL_EMC_WALL_9, -1, -1
7454 Xdecor_7, TRUE, FALSE,
7455 EL_EMC_WALL_10, -1, -1
7458 Xdecor_8, TRUE, FALSE,
7459 EL_EMC_WALL_1, -1, -1
7462 Xdecor_9, TRUE, FALSE,
7463 EL_EMC_WALL_2, -1, -1
7466 Xdecor_10, TRUE, FALSE,
7467 EL_EMC_WALL_3, -1, -1
7470 Xdecor_11, TRUE, FALSE,
7471 EL_EMC_WALL_11, -1, -1
7474 Xdecor_12, TRUE, FALSE,
7475 EL_EMC_WALL_12, -1, -1
7479 Xalpha_0, TRUE, FALSE,
7480 EL_CHAR('0'), -1, -1
7483 Xalpha_1, TRUE, FALSE,
7484 EL_CHAR('1'), -1, -1
7487 Xalpha_2, TRUE, FALSE,
7488 EL_CHAR('2'), -1, -1
7491 Xalpha_3, TRUE, FALSE,
7492 EL_CHAR('3'), -1, -1
7495 Xalpha_4, TRUE, FALSE,
7496 EL_CHAR('4'), -1, -1
7499 Xalpha_5, TRUE, FALSE,
7500 EL_CHAR('5'), -1, -1
7503 Xalpha_6, TRUE, FALSE,
7504 EL_CHAR('6'), -1, -1
7507 Xalpha_7, TRUE, FALSE,
7508 EL_CHAR('7'), -1, -1
7511 Xalpha_8, TRUE, FALSE,
7512 EL_CHAR('8'), -1, -1
7515 Xalpha_9, TRUE, FALSE,
7516 EL_CHAR('9'), -1, -1
7519 Xalpha_excla, TRUE, FALSE,
7520 EL_CHAR('!'), -1, -1
7523 Xalpha_apost, TRUE, FALSE,
7524 EL_CHAR('\''), -1, -1
7527 Xalpha_comma, TRUE, FALSE,
7528 EL_CHAR(','), -1, -1
7531 Xalpha_minus, TRUE, FALSE,
7532 EL_CHAR('-'), -1, -1
7535 Xalpha_perio, TRUE, FALSE,
7536 EL_CHAR('.'), -1, -1
7539 Xalpha_colon, TRUE, FALSE,
7540 EL_CHAR(':'), -1, -1
7543 Xalpha_quest, TRUE, FALSE,
7544 EL_CHAR('?'), -1, -1
7547 Xalpha_a, TRUE, FALSE,
7548 EL_CHAR('A'), -1, -1
7551 Xalpha_b, TRUE, FALSE,
7552 EL_CHAR('B'), -1, -1
7555 Xalpha_c, TRUE, FALSE,
7556 EL_CHAR('C'), -1, -1
7559 Xalpha_d, TRUE, FALSE,
7560 EL_CHAR('D'), -1, -1
7563 Xalpha_e, TRUE, FALSE,
7564 EL_CHAR('E'), -1, -1
7567 Xalpha_f, TRUE, FALSE,
7568 EL_CHAR('F'), -1, -1
7571 Xalpha_g, TRUE, FALSE,
7572 EL_CHAR('G'), -1, -1
7575 Xalpha_h, TRUE, FALSE,
7576 EL_CHAR('H'), -1, -1
7579 Xalpha_i, TRUE, FALSE,
7580 EL_CHAR('I'), -1, -1
7583 Xalpha_j, TRUE, FALSE,
7584 EL_CHAR('J'), -1, -1
7587 Xalpha_k, TRUE, FALSE,
7588 EL_CHAR('K'), -1, -1
7591 Xalpha_l, TRUE, FALSE,
7592 EL_CHAR('L'), -1, -1
7595 Xalpha_m, TRUE, FALSE,
7596 EL_CHAR('M'), -1, -1
7599 Xalpha_n, TRUE, FALSE,
7600 EL_CHAR('N'), -1, -1
7603 Xalpha_o, TRUE, FALSE,
7604 EL_CHAR('O'), -1, -1
7607 Xalpha_p, TRUE, FALSE,
7608 EL_CHAR('P'), -1, -1
7611 Xalpha_q, TRUE, FALSE,
7612 EL_CHAR('Q'), -1, -1
7615 Xalpha_r, TRUE, FALSE,
7616 EL_CHAR('R'), -1, -1
7619 Xalpha_s, TRUE, FALSE,
7620 EL_CHAR('S'), -1, -1
7623 Xalpha_t, TRUE, FALSE,
7624 EL_CHAR('T'), -1, -1
7627 Xalpha_u, TRUE, FALSE,
7628 EL_CHAR('U'), -1, -1
7631 Xalpha_v, TRUE, FALSE,
7632 EL_CHAR('V'), -1, -1
7635 Xalpha_w, TRUE, FALSE,
7636 EL_CHAR('W'), -1, -1
7639 Xalpha_x, TRUE, FALSE,
7640 EL_CHAR('X'), -1, -1
7643 Xalpha_y, TRUE, FALSE,
7644 EL_CHAR('Y'), -1, -1
7647 Xalpha_z, TRUE, FALSE,
7648 EL_CHAR('Z'), -1, -1
7651 Xalpha_arrow_e, TRUE, FALSE,
7652 EL_CHAR('>'), -1, -1
7655 Xalpha_arrow_w, TRUE, FALSE,
7656 EL_CHAR('<'), -1, -1
7659 Xalpha_copyr, TRUE, FALSE,
7660 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7664 Ykey_1_blank, FALSE, FALSE,
7665 EL_EM_KEY_1, ACTION_COLLECTING, -1
7668 Ykey_2_blank, FALSE, FALSE,
7669 EL_EM_KEY_2, ACTION_COLLECTING, -1
7672 Ykey_3_blank, FALSE, FALSE,
7673 EL_EM_KEY_3, ACTION_COLLECTING, -1
7676 Ykey_4_blank, FALSE, FALSE,
7677 EL_EM_KEY_4, ACTION_COLLECTING, -1
7680 Ykey_5_blank, FALSE, FALSE,
7681 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7684 Ykey_6_blank, FALSE, FALSE,
7685 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7688 Ykey_7_blank, FALSE, FALSE,
7689 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7692 Ykey_8_blank, FALSE, FALSE,
7693 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7696 Ylenses_blank, FALSE, FALSE,
7697 EL_EMC_LENSES, ACTION_COLLECTING, -1
7700 Ymagnify_blank, FALSE, FALSE,
7701 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7704 Ygrass_blank, FALSE, FALSE,
7705 EL_EMC_GRASS, ACTION_SNAPPING, -1
7708 Ydirt_blank, FALSE, FALSE,
7709 EL_SAND, ACTION_SNAPPING, -1
7718 static struct Mapping_EM_to_RND_player
7727 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7731 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7735 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7739 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7743 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7747 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7751 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7755 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7759 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7763 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7767 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7771 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7775 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7779 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7783 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7787 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7791 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7795 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7799 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7803 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7807 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7811 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7815 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7819 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7823 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7827 EL_PLAYER_1, ACTION_DEFAULT, -1,
7831 EL_PLAYER_2, ACTION_DEFAULT, -1,
7835 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7839 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7843 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7847 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7851 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7855 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7859 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7863 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7867 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7871 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7875 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7879 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7883 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7887 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7891 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7895 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7899 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7903 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7907 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7911 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7915 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7919 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7923 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7927 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7931 EL_PLAYER_3, ACTION_DEFAULT, -1,
7935 EL_PLAYER_4, ACTION_DEFAULT, -1,
7944 int map_element_RND_to_EM_cave(int element_rnd)
7946 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7947 static boolean mapping_initialized = FALSE;
7949 if (!mapping_initialized)
7953 // return "Xalpha_quest" for all undefined elements in mapping array
7954 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7955 mapping_RND_to_EM[i] = Xalpha_quest;
7957 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7958 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7959 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7960 em_object_mapping_list[i].element_em;
7962 mapping_initialized = TRUE;
7965 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7967 Warn("invalid RND level element %d", element_rnd);
7972 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7975 int map_element_EM_to_RND_cave(int element_em_cave)
7977 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7978 static boolean mapping_initialized = FALSE;
7980 if (!mapping_initialized)
7984 // return "EL_UNKNOWN" for all undefined elements in mapping array
7985 for (i = 0; i < GAME_TILE_MAX; i++)
7986 mapping_EM_to_RND[i] = EL_UNKNOWN;
7988 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7989 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7990 em_object_mapping_list[i].element_rnd;
7992 mapping_initialized = TRUE;
7995 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7997 Warn("invalid EM cave element %d", element_em_cave);
8002 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8005 int map_element_EM_to_RND_game(int element_em_game)
8007 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8008 static boolean mapping_initialized = FALSE;
8010 if (!mapping_initialized)
8014 // return "EL_UNKNOWN" for all undefined elements in mapping array
8015 for (i = 0; i < GAME_TILE_MAX; i++)
8016 mapping_EM_to_RND[i] = EL_UNKNOWN;
8018 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8019 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8020 em_object_mapping_list[i].element_rnd;
8022 mapping_initialized = TRUE;
8025 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8027 Warn("invalid EM game element %d", element_em_game);
8032 return mapping_EM_to_RND[element_em_game];
8035 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8037 struct LevelInfo_EM *level_em = level->native_em_level;
8038 struct CAVE *cav = level_em->cav;
8041 for (i = 0; i < GAME_TILE_MAX; i++)
8042 cav->android_array[i] = Cblank;
8044 for (i = 0; i < level->num_android_clone_elements; i++)
8046 int element_rnd = level->android_clone_element[i];
8047 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8049 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8050 if (em_object_mapping_list[j].element_rnd == element_rnd)
8051 cav->android_array[em_object_mapping_list[j].element_em] =
8056 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8058 struct LevelInfo_EM *level_em = level->native_em_level;
8059 struct CAVE *cav = level_em->cav;
8062 level->num_android_clone_elements = 0;
8064 for (i = 0; i < GAME_TILE_MAX; i++)
8066 int element_em_cave = cav->android_array[i];
8068 boolean element_found = FALSE;
8070 if (element_em_cave == Cblank)
8073 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8075 for (j = 0; j < level->num_android_clone_elements; j++)
8076 if (level->android_clone_element[j] == element_rnd)
8077 element_found = TRUE;
8081 level->android_clone_element[level->num_android_clone_elements++] =
8084 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8089 if (level->num_android_clone_elements == 0)
8091 level->num_android_clone_elements = 1;
8092 level->android_clone_element[0] = EL_EMPTY;
8096 int map_direction_RND_to_EM(int direction)
8098 return (direction == MV_UP ? 0 :
8099 direction == MV_RIGHT ? 1 :
8100 direction == MV_DOWN ? 2 :
8101 direction == MV_LEFT ? 3 :
8105 int map_direction_EM_to_RND(int direction)
8107 return (direction == 0 ? MV_UP :
8108 direction == 1 ? MV_RIGHT :
8109 direction == 2 ? MV_DOWN :
8110 direction == 3 ? MV_LEFT :
8114 int map_element_RND_to_SP(int element_rnd)
8116 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8118 if (element_rnd >= EL_SP_START &&
8119 element_rnd <= EL_SP_END)
8120 element_sp = element_rnd - EL_SP_START;
8121 else if (element_rnd == EL_EMPTY_SPACE)
8123 else if (element_rnd == EL_INVISIBLE_WALL)
8129 int map_element_SP_to_RND(int element_sp)
8131 int element_rnd = EL_UNKNOWN;
8133 if (element_sp >= 0x00 &&
8135 element_rnd = EL_SP_START + element_sp;
8136 else if (element_sp == 0x28)
8137 element_rnd = EL_INVISIBLE_WALL;
8142 int map_action_SP_to_RND(int action_sp)
8146 case actActive: return ACTION_ACTIVE;
8147 case actImpact: return ACTION_IMPACT;
8148 case actExploding: return ACTION_EXPLODING;
8149 case actDigging: return ACTION_DIGGING;
8150 case actSnapping: return ACTION_SNAPPING;
8151 case actCollecting: return ACTION_COLLECTING;
8152 case actPassing: return ACTION_PASSING;
8153 case actPushing: return ACTION_PUSHING;
8154 case actDropping: return ACTION_DROPPING;
8156 default: return ACTION_DEFAULT;
8160 int map_element_RND_to_MM(int element_rnd)
8162 return (element_rnd >= EL_MM_START_1 &&
8163 element_rnd <= EL_MM_END_1 ?
8164 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8166 element_rnd >= EL_MM_START_2 &&
8167 element_rnd <= EL_MM_END_2 ?
8168 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8170 element_rnd >= EL_MM_START_3 &&
8171 element_rnd <= EL_MM_END_3 ?
8172 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8174 element_rnd >= EL_CHAR_START &&
8175 element_rnd <= EL_CHAR_END ?
8176 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8178 element_rnd >= EL_MM_RUNTIME_START &&
8179 element_rnd <= EL_MM_RUNTIME_END ?
8180 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8182 EL_MM_EMPTY_NATIVE);
8185 int map_element_MM_to_RND(int element_mm)
8187 return (element_mm == EL_MM_EMPTY_NATIVE ||
8188 element_mm == EL_DF_EMPTY_NATIVE ?
8191 element_mm >= EL_MM_START_1_NATIVE &&
8192 element_mm <= EL_MM_END_1_NATIVE ?
8193 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8195 element_mm >= EL_MM_START_2_NATIVE &&
8196 element_mm <= EL_MM_END_2_NATIVE ?
8197 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8199 element_mm >= EL_MM_START_3_NATIVE &&
8200 element_mm <= EL_MM_END_3_NATIVE ?
8201 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8203 element_mm >= EL_MM_CHAR_START_NATIVE &&
8204 element_mm <= EL_MM_CHAR_END_NATIVE ?
8205 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8207 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8208 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8209 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8214 int map_action_MM_to_RND(int action_mm)
8216 // all MM actions are defined to exactly match their RND counterparts
8220 int map_sound_MM_to_RND(int sound_mm)
8224 case SND_MM_GAME_LEVELTIME_CHARGING:
8225 return SND_GAME_LEVELTIME_CHARGING;
8227 case SND_MM_GAME_HEALTH_CHARGING:
8228 return SND_GAME_HEALTH_CHARGING;
8231 return SND_UNDEFINED;
8235 int map_mm_wall_element(int element)
8237 return (element >= EL_MM_STEEL_WALL_START &&
8238 element <= EL_MM_STEEL_WALL_END ?
8241 element >= EL_MM_WOODEN_WALL_START &&
8242 element <= EL_MM_WOODEN_WALL_END ?
8245 element >= EL_MM_ICE_WALL_START &&
8246 element <= EL_MM_ICE_WALL_END ?
8249 element >= EL_MM_AMOEBA_WALL_START &&
8250 element <= EL_MM_AMOEBA_WALL_END ?
8253 element >= EL_DF_STEEL_WALL_START &&
8254 element <= EL_DF_STEEL_WALL_END ?
8257 element >= EL_DF_WOODEN_WALL_START &&
8258 element <= EL_DF_WOODEN_WALL_END ?
8264 int map_mm_wall_element_editor(int element)
8268 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8269 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8270 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8271 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8272 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8273 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8275 default: return element;
8279 int get_next_element(int element)
8283 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8284 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8285 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8286 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8287 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8288 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8289 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8290 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8291 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8292 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8293 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8295 default: return element;
8299 int el2img_mm(int element_mm)
8301 return el2img(map_element_MM_to_RND(element_mm));
8304 int el_act2img_mm(int element_mm, int action)
8306 return el_act2img(map_element_MM_to_RND(element_mm), action);
8309 int el_act_dir2img(int element, int action, int direction)
8311 element = GFX_ELEMENT(element);
8312 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8314 // direction_graphic[][] == graphic[] for undefined direction graphics
8315 return element_info[element].direction_graphic[action][direction];
8318 static int el_act_dir2crm(int element, int action, int direction)
8320 element = GFX_ELEMENT(element);
8321 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8323 // direction_graphic[][] == graphic[] for undefined direction graphics
8324 return element_info[element].direction_crumbled[action][direction];
8327 int el_act2img(int element, int action)
8329 element = GFX_ELEMENT(element);
8331 return element_info[element].graphic[action];
8334 int el_act2crm(int element, int action)
8336 element = GFX_ELEMENT(element);
8338 return element_info[element].crumbled[action];
8341 int el_dir2img(int element, int direction)
8343 element = GFX_ELEMENT(element);
8345 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8348 int el2baseimg(int element)
8350 return element_info[element].graphic[ACTION_DEFAULT];
8353 int el2img(int element)
8355 element = GFX_ELEMENT(element);
8357 return element_info[element].graphic[ACTION_DEFAULT];
8360 int el2edimg(int element)
8362 element = GFX_ELEMENT(element);
8364 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8367 int el2preimg(int element)
8369 element = GFX_ELEMENT(element);
8371 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8374 int el2panelimg(int element)
8376 element = GFX_ELEMENT(element);
8378 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8381 int font2baseimg(int font_nr)
8383 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8386 int getBeltNrFromBeltElement(int element)
8388 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8389 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8390 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8393 int getBeltNrFromBeltActiveElement(int element)
8395 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8396 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8397 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8400 int getBeltNrFromBeltSwitchElement(int element)
8402 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8403 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8404 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8407 int getBeltDirNrFromBeltElement(int element)
8409 static int belt_base_element[4] =
8411 EL_CONVEYOR_BELT_1_LEFT,
8412 EL_CONVEYOR_BELT_2_LEFT,
8413 EL_CONVEYOR_BELT_3_LEFT,
8414 EL_CONVEYOR_BELT_4_LEFT
8417 int belt_nr = getBeltNrFromBeltElement(element);
8418 int belt_dir_nr = element - belt_base_element[belt_nr];
8420 return (belt_dir_nr % 3);
8423 int getBeltDirNrFromBeltSwitchElement(int element)
8425 static int belt_base_element[4] =
8427 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8428 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8429 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8430 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8433 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8434 int belt_dir_nr = element - belt_base_element[belt_nr];
8436 return (belt_dir_nr % 3);
8439 int getBeltDirFromBeltElement(int element)
8441 static int belt_move_dir[3] =
8448 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8450 return belt_move_dir[belt_dir_nr];
8453 int getBeltDirFromBeltSwitchElement(int element)
8455 static int belt_move_dir[3] =
8462 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8464 return belt_move_dir[belt_dir_nr];
8467 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8469 static int belt_base_element[4] =
8471 EL_CONVEYOR_BELT_1_LEFT,
8472 EL_CONVEYOR_BELT_2_LEFT,
8473 EL_CONVEYOR_BELT_3_LEFT,
8474 EL_CONVEYOR_BELT_4_LEFT
8477 return belt_base_element[belt_nr] + belt_dir_nr;
8480 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8482 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8484 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8487 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8489 static int belt_base_element[4] =
8491 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8492 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8493 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8494 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8497 return belt_base_element[belt_nr] + belt_dir_nr;
8500 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8502 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8504 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8507 boolean swapTiles_EM(boolean is_pre_emc_cave)
8509 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8512 boolean getTeamMode_EM(void)
8514 return game.team_mode || network_playing;
8517 boolean isActivePlayer_EM(int player_nr)
8519 return stored_player[player_nr].active;
8522 unsigned int InitRND(int seed)
8524 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8525 return InitEngineRandom_EM(seed);
8526 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8527 return InitEngineRandom_SP(seed);
8528 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8529 return InitEngineRandom_MM(seed);
8531 return InitEngineRandom_RND(seed);
8534 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8535 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8537 static int get_effective_element_EM(int tile, int frame_em)
8539 int element = object_mapping[tile].element_rnd;
8540 int action = object_mapping[tile].action;
8541 boolean is_backside = object_mapping[tile].is_backside;
8542 boolean action_removing = (action == ACTION_DIGGING ||
8543 action == ACTION_SNAPPING ||
8544 action == ACTION_COLLECTING);
8552 return (frame_em > 5 ? EL_EMPTY : element);
8558 else // frame_em == 7
8569 case Ydiamond_stone:
8573 case Xdrip_stretchB:
8589 case Ymagnify_blank:
8592 case Xsand_stonein_1:
8593 case Xsand_stonein_2:
8594 case Xsand_stonein_3:
8595 case Xsand_stonein_4:
8599 return (is_backside || action_removing ? EL_EMPTY : element);
8604 static boolean check_linear_animation_EM(int tile)
8608 case Xsand_stonesand_1:
8609 case Xsand_stonesand_quickout_1:
8610 case Xsand_sandstone_1:
8611 case Xsand_stonein_1:
8612 case Xsand_stoneout_1:
8640 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8641 boolean has_crumbled_graphics,
8642 int crumbled, int sync_frame)
8644 // if element can be crumbled, but certain action graphics are just empty
8645 // space (like instantly snapping sand to empty space in 1 frame), do not
8646 // treat these empty space graphics as crumbled graphics in EMC engine
8647 if (crumbled == IMG_EMPTY_SPACE)
8648 has_crumbled_graphics = FALSE;
8650 if (has_crumbled_graphics)
8652 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8653 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8654 g_crumbled->anim_delay,
8655 g_crumbled->anim_mode,
8656 g_crumbled->anim_start_frame,
8659 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8660 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8662 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8663 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8665 g_em->has_crumbled_graphics = TRUE;
8669 g_em->crumbled_bitmap = NULL;
8670 g_em->crumbled_src_x = 0;
8671 g_em->crumbled_src_y = 0;
8672 g_em->crumbled_border_size = 0;
8673 g_em->crumbled_tile_size = 0;
8675 g_em->has_crumbled_graphics = FALSE;
8680 void ResetGfxAnimation_EM(int x, int y, int tile)
8686 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8687 int tile, int frame_em, int x, int y)
8689 int action = object_mapping[tile].action;
8690 int direction = object_mapping[tile].direction;
8691 int effective_element = get_effective_element_EM(tile, frame_em);
8692 int graphic = (direction == MV_NONE ?
8693 el_act2img(effective_element, action) :
8694 el_act_dir2img(effective_element, action, direction));
8695 struct GraphicInfo *g = &graphic_info[graphic];
8697 boolean action_removing = (action == ACTION_DIGGING ||
8698 action == ACTION_SNAPPING ||
8699 action == ACTION_COLLECTING);
8700 boolean action_moving = (action == ACTION_FALLING ||
8701 action == ACTION_MOVING ||
8702 action == ACTION_PUSHING ||
8703 action == ACTION_EATING ||
8704 action == ACTION_FILLING ||
8705 action == ACTION_EMPTYING);
8706 boolean action_falling = (action == ACTION_FALLING ||
8707 action == ACTION_FILLING ||
8708 action == ACTION_EMPTYING);
8710 // special case: graphic uses "2nd movement tile" and has defined
8711 // 7 frames for movement animation (or less) => use default graphic
8712 // for last (8th) frame which ends the movement animation
8713 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8715 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8716 graphic = (direction == MV_NONE ?
8717 el_act2img(effective_element, action) :
8718 el_act_dir2img(effective_element, action, direction));
8720 g = &graphic_info[graphic];
8723 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8727 else if (action_moving)
8729 boolean is_backside = object_mapping[tile].is_backside;
8733 int direction = object_mapping[tile].direction;
8734 int move_dir = (action_falling ? MV_DOWN : direction);
8739 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8740 if (g->double_movement && frame_em == 0)
8744 if (move_dir == MV_LEFT)
8745 GfxFrame[x - 1][y] = GfxFrame[x][y];
8746 else if (move_dir == MV_RIGHT)
8747 GfxFrame[x + 1][y] = GfxFrame[x][y];
8748 else if (move_dir == MV_UP)
8749 GfxFrame[x][y - 1] = GfxFrame[x][y];
8750 else if (move_dir == MV_DOWN)
8751 GfxFrame[x][y + 1] = GfxFrame[x][y];
8758 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8759 if (tile == Xsand_stonesand_quickout_1 ||
8760 tile == Xsand_stonesand_quickout_2)
8764 if (graphic_info[graphic].anim_global_sync)
8765 sync_frame = FrameCounter;
8766 else if (graphic_info[graphic].anim_global_anim_sync)
8767 sync_frame = getGlobalAnimSyncFrame();
8768 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8769 sync_frame = GfxFrame[x][y];
8771 sync_frame = 0; // playfield border (pseudo steel)
8773 SetRandomAnimationValue(x, y);
8775 int frame = getAnimationFrame(g->anim_frames,
8778 g->anim_start_frame,
8781 g_em->unique_identifier =
8782 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8785 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8786 int tile, int frame_em, int x, int y)
8788 int action = object_mapping[tile].action;
8789 int direction = object_mapping[tile].direction;
8790 boolean is_backside = object_mapping[tile].is_backside;
8791 int effective_element = get_effective_element_EM(tile, frame_em);
8792 int effective_action = action;
8793 int graphic = (direction == MV_NONE ?
8794 el_act2img(effective_element, effective_action) :
8795 el_act_dir2img(effective_element, effective_action,
8797 int crumbled = (direction == MV_NONE ?
8798 el_act2crm(effective_element, effective_action) :
8799 el_act_dir2crm(effective_element, effective_action,
8801 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8802 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8803 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8804 struct GraphicInfo *g = &graphic_info[graphic];
8807 // special case: graphic uses "2nd movement tile" and has defined
8808 // 7 frames for movement animation (or less) => use default graphic
8809 // for last (8th) frame which ends the movement animation
8810 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8812 effective_action = ACTION_DEFAULT;
8813 graphic = (direction == MV_NONE ?
8814 el_act2img(effective_element, effective_action) :
8815 el_act_dir2img(effective_element, effective_action,
8817 crumbled = (direction == MV_NONE ?
8818 el_act2crm(effective_element, effective_action) :
8819 el_act_dir2crm(effective_element, effective_action,
8822 g = &graphic_info[graphic];
8825 if (graphic_info[graphic].anim_global_sync)
8826 sync_frame = FrameCounter;
8827 else if (graphic_info[graphic].anim_global_anim_sync)
8828 sync_frame = getGlobalAnimSyncFrame();
8829 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8830 sync_frame = GfxFrame[x][y];
8832 sync_frame = 0; // playfield border (pseudo steel)
8834 SetRandomAnimationValue(x, y);
8836 int frame = getAnimationFrame(g->anim_frames,
8839 g->anim_start_frame,
8842 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8843 g->double_movement && is_backside);
8845 // (updating the "crumbled" graphic definitions is probably not really needed,
8846 // as animations for crumbled graphics can't be longer than one EMC cycle)
8847 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8851 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8852 int player_nr, int anim, int frame_em)
8854 int element = player_mapping[player_nr][anim].element_rnd;
8855 int action = player_mapping[player_nr][anim].action;
8856 int direction = player_mapping[player_nr][anim].direction;
8857 int graphic = (direction == MV_NONE ?
8858 el_act2img(element, action) :
8859 el_act_dir2img(element, action, direction));
8860 struct GraphicInfo *g = &graphic_info[graphic];
8863 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8865 stored_player[player_nr].StepFrame = frame_em;
8867 sync_frame = stored_player[player_nr].Frame;
8869 int frame = getAnimationFrame(g->anim_frames,
8872 g->anim_start_frame,
8875 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8876 &g_em->src_x, &g_em->src_y, FALSE);
8879 void InitGraphicInfo_EM(void)
8883 // always start with reliable default values
8884 for (i = 0; i < GAME_TILE_MAX; i++)
8886 object_mapping[i].element_rnd = EL_UNKNOWN;
8887 object_mapping[i].is_backside = FALSE;
8888 object_mapping[i].action = ACTION_DEFAULT;
8889 object_mapping[i].direction = MV_NONE;
8892 // always start with reliable default values
8893 for (p = 0; p < MAX_PLAYERS; p++)
8895 for (i = 0; i < PLY_MAX; i++)
8897 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8898 player_mapping[p][i].action = ACTION_DEFAULT;
8899 player_mapping[p][i].direction = MV_NONE;
8903 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8905 int e = em_object_mapping_list[i].element_em;
8907 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8908 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8910 if (em_object_mapping_list[i].action != -1)
8911 object_mapping[e].action = em_object_mapping_list[i].action;
8913 if (em_object_mapping_list[i].direction != -1)
8914 object_mapping[e].direction =
8915 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8918 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8920 int a = em_player_mapping_list[i].action_em;
8921 int p = em_player_mapping_list[i].player_nr;
8923 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8925 if (em_player_mapping_list[i].action != -1)
8926 player_mapping[p][a].action = em_player_mapping_list[i].action;
8928 if (em_player_mapping_list[i].direction != -1)
8929 player_mapping[p][a].direction =
8930 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8933 for (i = 0; i < GAME_TILE_MAX; i++)
8935 int element = object_mapping[i].element_rnd;
8936 int action = object_mapping[i].action;
8937 int direction = object_mapping[i].direction;
8938 boolean is_backside = object_mapping[i].is_backside;
8939 boolean action_exploding = ((action == ACTION_EXPLODING ||
8940 action == ACTION_SMASHED_BY_ROCK ||
8941 action == ACTION_SMASHED_BY_SPRING) &&
8942 element != EL_DIAMOND);
8943 boolean action_active = (action == ACTION_ACTIVE);
8944 boolean action_other = (action == ACTION_OTHER);
8946 for (j = 0; j < 8; j++)
8948 int effective_element = get_effective_element_EM(i, j);
8949 int effective_action = (j < 7 ? action :
8950 i == Xdrip_stretch ? action :
8951 i == Xdrip_stretchB ? action :
8952 i == Ydrip_1_s ? action :
8953 i == Ydrip_1_sB ? action :
8954 i == Yball_1 ? action :
8955 i == Xball_2 ? action :
8956 i == Yball_2 ? action :
8957 i == Yball_blank ? action :
8958 i == Ykey_1_blank ? action :
8959 i == Ykey_2_blank ? action :
8960 i == Ykey_3_blank ? action :
8961 i == Ykey_4_blank ? action :
8962 i == Ykey_5_blank ? action :
8963 i == Ykey_6_blank ? action :
8964 i == Ykey_7_blank ? action :
8965 i == Ykey_8_blank ? action :
8966 i == Ylenses_blank ? action :
8967 i == Ymagnify_blank ? action :
8968 i == Ygrass_blank ? action :
8969 i == Ydirt_blank ? action :
8970 i == Xsand_stonein_1 ? action :
8971 i == Xsand_stonein_2 ? action :
8972 i == Xsand_stonein_3 ? action :
8973 i == Xsand_stonein_4 ? action :
8974 i == Xsand_stoneout_1 ? action :
8975 i == Xsand_stoneout_2 ? action :
8976 i == Xboom_android ? ACTION_EXPLODING :
8977 action_exploding ? ACTION_EXPLODING :
8978 action_active ? action :
8979 action_other ? action :
8981 int graphic = (el_act_dir2img(effective_element, effective_action,
8983 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8985 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8986 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8987 boolean has_action_graphics = (graphic != base_graphic);
8988 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8989 struct GraphicInfo *g = &graphic_info[graphic];
8990 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8993 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8994 boolean special_animation = (action != ACTION_DEFAULT &&
8995 g->anim_frames == 3 &&
8996 g->anim_delay == 2 &&
8997 g->anim_mode & ANIM_LINEAR);
8998 int sync_frame = (i == Xdrip_stretch ? 7 :
8999 i == Xdrip_stretchB ? 7 :
9000 i == Ydrip_2_s ? j + 8 :
9001 i == Ydrip_2_sB ? j + 8 :
9010 i == Xfake_acid_1 ? 0 :
9011 i == Xfake_acid_2 ? 10 :
9012 i == Xfake_acid_3 ? 20 :
9013 i == Xfake_acid_4 ? 30 :
9014 i == Xfake_acid_5 ? 40 :
9015 i == Xfake_acid_6 ? 50 :
9016 i == Xfake_acid_7 ? 60 :
9017 i == Xfake_acid_8 ? 70 :
9018 i == Xfake_acid_1_player ? 0 :
9019 i == Xfake_acid_2_player ? 10 :
9020 i == Xfake_acid_3_player ? 20 :
9021 i == Xfake_acid_4_player ? 30 :
9022 i == Xfake_acid_5_player ? 40 :
9023 i == Xfake_acid_6_player ? 50 :
9024 i == Xfake_acid_7_player ? 60 :
9025 i == Xfake_acid_8_player ? 70 :
9027 i == Yball_2 ? j + 8 :
9028 i == Yball_blank ? j + 1 :
9029 i == Ykey_1_blank ? j + 1 :
9030 i == Ykey_2_blank ? j + 1 :
9031 i == Ykey_3_blank ? j + 1 :
9032 i == Ykey_4_blank ? j + 1 :
9033 i == Ykey_5_blank ? j + 1 :
9034 i == Ykey_6_blank ? j + 1 :
9035 i == Ykey_7_blank ? j + 1 :
9036 i == Ykey_8_blank ? j + 1 :
9037 i == Ylenses_blank ? j + 1 :
9038 i == Ymagnify_blank ? j + 1 :
9039 i == Ygrass_blank ? j + 1 :
9040 i == Ydirt_blank ? j + 1 :
9041 i == Xamoeba_1 ? 0 :
9042 i == Xamoeba_2 ? 1 :
9043 i == Xamoeba_3 ? 2 :
9044 i == Xamoeba_4 ? 3 :
9045 i == Xamoeba_5 ? 0 :
9046 i == Xamoeba_6 ? 1 :
9047 i == Xamoeba_7 ? 2 :
9048 i == Xamoeba_8 ? 3 :
9049 i == Xexit_2 ? j + 8 :
9050 i == Xexit_3 ? j + 16 :
9051 i == Xdynamite_1 ? 0 :
9052 i == Xdynamite_2 ? 8 :
9053 i == Xdynamite_3 ? 16 :
9054 i == Xdynamite_4 ? 24 :
9055 i == Xsand_stonein_1 ? j + 1 :
9056 i == Xsand_stonein_2 ? j + 9 :
9057 i == Xsand_stonein_3 ? j + 17 :
9058 i == Xsand_stonein_4 ? j + 25 :
9059 i == Xsand_stoneout_1 && j == 0 ? 0 :
9060 i == Xsand_stoneout_1 && j == 1 ? 0 :
9061 i == Xsand_stoneout_1 && j == 2 ? 1 :
9062 i == Xsand_stoneout_1 && j == 3 ? 2 :
9063 i == Xsand_stoneout_1 && j == 4 ? 2 :
9064 i == Xsand_stoneout_1 && j == 5 ? 3 :
9065 i == Xsand_stoneout_1 && j == 6 ? 4 :
9066 i == Xsand_stoneout_1 && j == 7 ? 4 :
9067 i == Xsand_stoneout_2 && j == 0 ? 5 :
9068 i == Xsand_stoneout_2 && j == 1 ? 6 :
9069 i == Xsand_stoneout_2 && j == 2 ? 7 :
9070 i == Xsand_stoneout_2 && j == 3 ? 8 :
9071 i == Xsand_stoneout_2 && j == 4 ? 9 :
9072 i == Xsand_stoneout_2 && j == 5 ? 11 :
9073 i == Xsand_stoneout_2 && j == 6 ? 13 :
9074 i == Xsand_stoneout_2 && j == 7 ? 15 :
9075 i == Xboom_bug && j == 1 ? 2 :
9076 i == Xboom_bug && j == 2 ? 2 :
9077 i == Xboom_bug && j == 3 ? 4 :
9078 i == Xboom_bug && j == 4 ? 4 :
9079 i == Xboom_bug && j == 5 ? 2 :
9080 i == Xboom_bug && j == 6 ? 2 :
9081 i == Xboom_bug && j == 7 ? 0 :
9082 i == Xboom_tank && j == 1 ? 2 :
9083 i == Xboom_tank && j == 2 ? 2 :
9084 i == Xboom_tank && j == 3 ? 4 :
9085 i == Xboom_tank && j == 4 ? 4 :
9086 i == Xboom_tank && j == 5 ? 2 :
9087 i == Xboom_tank && j == 6 ? 2 :
9088 i == Xboom_tank && j == 7 ? 0 :
9089 i == Xboom_android && j == 7 ? 6 :
9090 i == Xboom_1 && j == 1 ? 2 :
9091 i == Xboom_1 && j == 2 ? 2 :
9092 i == Xboom_1 && j == 3 ? 4 :
9093 i == Xboom_1 && j == 4 ? 4 :
9094 i == Xboom_1 && j == 5 ? 6 :
9095 i == Xboom_1 && j == 6 ? 6 :
9096 i == Xboom_1 && j == 7 ? 8 :
9097 i == Xboom_2 && j == 0 ? 8 :
9098 i == Xboom_2 && j == 1 ? 8 :
9099 i == Xboom_2 && j == 2 ? 10 :
9100 i == Xboom_2 && j == 3 ? 10 :
9101 i == Xboom_2 && j == 4 ? 10 :
9102 i == Xboom_2 && j == 5 ? 12 :
9103 i == Xboom_2 && j == 6 ? 12 :
9104 i == Xboom_2 && j == 7 ? 12 :
9105 special_animation && j == 4 ? 3 :
9106 effective_action != action ? 0 :
9108 int frame = getAnimationFrame(g->anim_frames,
9111 g->anim_start_frame,
9114 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9115 g->double_movement && is_backside);
9117 g_em->bitmap = src_bitmap;
9118 g_em->src_x = src_x;
9119 g_em->src_y = src_y;
9120 g_em->src_offset_x = 0;
9121 g_em->src_offset_y = 0;
9122 g_em->dst_offset_x = 0;
9123 g_em->dst_offset_y = 0;
9124 g_em->width = TILEX;
9125 g_em->height = TILEY;
9127 g_em->preserve_background = FALSE;
9129 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9132 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9133 effective_action == ACTION_MOVING ||
9134 effective_action == ACTION_PUSHING ||
9135 effective_action == ACTION_EATING)) ||
9136 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9137 effective_action == ACTION_EMPTYING)))
9140 (effective_action == ACTION_FALLING ||
9141 effective_action == ACTION_FILLING ||
9142 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9143 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9144 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9145 int num_steps = (i == Ydrip_1_s ? 16 :
9146 i == Ydrip_1_sB ? 16 :
9147 i == Ydrip_2_s ? 16 :
9148 i == Ydrip_2_sB ? 16 :
9149 i == Xsand_stonein_1 ? 32 :
9150 i == Xsand_stonein_2 ? 32 :
9151 i == Xsand_stonein_3 ? 32 :
9152 i == Xsand_stonein_4 ? 32 :
9153 i == Xsand_stoneout_1 ? 16 :
9154 i == Xsand_stoneout_2 ? 16 : 8);
9155 int cx = ABS(dx) * (TILEX / num_steps);
9156 int cy = ABS(dy) * (TILEY / num_steps);
9157 int step_frame = (i == Ydrip_2_s ? j + 8 :
9158 i == Ydrip_2_sB ? j + 8 :
9159 i == Xsand_stonein_2 ? j + 8 :
9160 i == Xsand_stonein_3 ? j + 16 :
9161 i == Xsand_stonein_4 ? j + 24 :
9162 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9163 int step = (is_backside ? step_frame : num_steps - step_frame);
9165 if (is_backside) // tile where movement starts
9167 if (dx < 0 || dy < 0)
9169 g_em->src_offset_x = cx * step;
9170 g_em->src_offset_y = cy * step;
9174 g_em->dst_offset_x = cx * step;
9175 g_em->dst_offset_y = cy * step;
9178 else // tile where movement ends
9180 if (dx < 0 || dy < 0)
9182 g_em->dst_offset_x = cx * step;
9183 g_em->dst_offset_y = cy * step;
9187 g_em->src_offset_x = cx * step;
9188 g_em->src_offset_y = cy * step;
9192 g_em->width = TILEX - cx * step;
9193 g_em->height = TILEY - cy * step;
9196 // create unique graphic identifier to decide if tile must be redrawn
9197 /* bit 31 - 16 (16 bit): EM style graphic
9198 bit 15 - 12 ( 4 bit): EM style frame
9199 bit 11 - 6 ( 6 bit): graphic width
9200 bit 5 - 0 ( 6 bit): graphic height */
9201 g_em->unique_identifier =
9202 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9206 for (i = 0; i < GAME_TILE_MAX; i++)
9208 for (j = 0; j < 8; j++)
9210 int element = object_mapping[i].element_rnd;
9211 int action = object_mapping[i].action;
9212 int direction = object_mapping[i].direction;
9213 boolean is_backside = object_mapping[i].is_backside;
9214 int graphic_action = el_act_dir2img(element, action, direction);
9215 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9217 if ((action == ACTION_SMASHED_BY_ROCK ||
9218 action == ACTION_SMASHED_BY_SPRING ||
9219 action == ACTION_EATING) &&
9220 graphic_action == graphic_default)
9222 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9223 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9224 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9225 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9228 // no separate animation for "smashed by rock" -- use rock instead
9229 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9230 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9232 g_em->bitmap = g_xx->bitmap;
9233 g_em->src_x = g_xx->src_x;
9234 g_em->src_y = g_xx->src_y;
9235 g_em->src_offset_x = g_xx->src_offset_x;
9236 g_em->src_offset_y = g_xx->src_offset_y;
9237 g_em->dst_offset_x = g_xx->dst_offset_x;
9238 g_em->dst_offset_y = g_xx->dst_offset_y;
9239 g_em->width = g_xx->width;
9240 g_em->height = g_xx->height;
9241 g_em->unique_identifier = g_xx->unique_identifier;
9244 g_em->preserve_background = TRUE;
9249 for (p = 0; p < MAX_PLAYERS; p++)
9251 for (i = 0; i < PLY_MAX; i++)
9253 int element = player_mapping[p][i].element_rnd;
9254 int action = player_mapping[p][i].action;
9255 int direction = player_mapping[p][i].direction;
9257 for (j = 0; j < 8; j++)
9259 int effective_element = element;
9260 int effective_action = action;
9261 int graphic = (direction == MV_NONE ?
9262 el_act2img(effective_element, effective_action) :
9263 el_act_dir2img(effective_element, effective_action,
9265 struct GraphicInfo *g = &graphic_info[graphic];
9266 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9270 int frame = getAnimationFrame(g->anim_frames,
9273 g->anim_start_frame,
9276 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9278 g_em->bitmap = src_bitmap;
9279 g_em->src_x = src_x;
9280 g_em->src_y = src_y;
9281 g_em->src_offset_x = 0;
9282 g_em->src_offset_y = 0;
9283 g_em->dst_offset_x = 0;
9284 g_em->dst_offset_y = 0;
9285 g_em->width = TILEX;
9286 g_em->height = TILEY;
9292 static void CheckSaveEngineSnapshot_EM(int frame,
9293 boolean any_player_moving,
9294 boolean any_player_snapping,
9295 boolean any_player_dropping)
9297 if (frame == 7 && !any_player_dropping)
9299 if (!local_player->was_waiting)
9301 if (!CheckSaveEngineSnapshotToList())
9304 local_player->was_waiting = TRUE;
9307 else if (any_player_moving || any_player_snapping || any_player_dropping)
9309 local_player->was_waiting = FALSE;
9313 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9314 boolean murphy_is_dropping)
9316 if (murphy_is_waiting)
9318 if (!local_player->was_waiting)
9320 if (!CheckSaveEngineSnapshotToList())
9323 local_player->was_waiting = TRUE;
9328 local_player->was_waiting = FALSE;
9332 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9333 boolean button_released)
9335 if (button_released)
9337 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9338 CheckSaveEngineSnapshotToList();
9340 else if (element_clicked)
9342 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9343 CheckSaveEngineSnapshotToList();
9345 game.snapshot.changed_action = TRUE;
9349 boolean CheckSingleStepMode_EM(int frame,
9350 boolean any_player_moving,
9351 boolean any_player_snapping,
9352 boolean any_player_dropping)
9354 if (tape.single_step && tape.recording && !tape.pausing)
9355 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9356 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9358 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9359 any_player_snapping, any_player_dropping);
9361 return tape.pausing;
9364 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9365 boolean murphy_is_dropping)
9367 boolean murphy_starts_dropping = FALSE;
9370 for (i = 0; i < MAX_PLAYERS; i++)
9371 if (stored_player[i].force_dropping)
9372 murphy_starts_dropping = TRUE;
9374 if (tape.single_step && tape.recording && !tape.pausing)
9375 if (murphy_is_waiting && !murphy_starts_dropping)
9376 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9378 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9381 void CheckSingleStepMode_MM(boolean element_clicked,
9382 boolean button_released)
9384 if (tape.single_step && tape.recording && !tape.pausing)
9385 if (button_released)
9386 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9388 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9391 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9392 int graphic, int sync_frame)
9394 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9396 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9399 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9401 return (IS_NEXT_FRAME(sync_frame, graphic));
9404 int getGraphicInfo_Delay(int graphic)
9406 return graphic_info[graphic].anim_delay;
9409 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9411 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9414 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9420 void PlayMenuSoundExt(int sound)
9422 if (sound == SND_UNDEFINED)
9425 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9426 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9429 if (IS_LOOP_SOUND(sound))
9430 PlaySoundLoop(sound);
9435 void PlayMenuSound(void)
9437 PlayMenuSoundExt(menu.sound[game_status]);
9440 void PlayMenuSoundStereo(int sound, int stereo_position)
9442 if (sound == SND_UNDEFINED)
9445 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9446 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9449 if (IS_LOOP_SOUND(sound))
9450 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9452 PlaySoundStereo(sound, stereo_position);
9455 void PlayMenuSoundIfLoopExt(int sound)
9457 if (sound == SND_UNDEFINED)
9460 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9461 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9464 if (IS_LOOP_SOUND(sound))
9465 PlaySoundLoop(sound);
9468 void PlayMenuSoundIfLoop(void)
9470 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9473 void PlayMenuMusicExt(int music)
9475 if (music == MUS_UNDEFINED)
9478 if (!setup.sound_music)
9481 if (IS_LOOP_MUSIC(music))
9482 PlayMusicLoop(music);
9487 void PlayMenuMusic(void)
9489 char *curr_music = getCurrentlyPlayingMusicFilename();
9490 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9492 if (!strEqual(curr_music, next_music))
9493 PlayMenuMusicExt(menu.music[game_status]);
9496 void PlayMenuSoundsAndMusic(void)
9502 static void FadeMenuSounds(void)
9507 static void FadeMenuMusic(void)
9509 char *curr_music = getCurrentlyPlayingMusicFilename();
9510 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9512 if (!strEqual(curr_music, next_music))
9516 void FadeMenuSoundsAndMusic(void)
9522 void PlaySoundActivating(void)
9525 PlaySound(SND_MENU_ITEM_ACTIVATING);
9529 void PlaySoundSelecting(void)
9532 PlaySound(SND_MENU_ITEM_SELECTING);
9536 void ToggleFullscreenIfNeeded(void)
9538 // if setup and video fullscreen state are already matching, nothing do do
9539 if (setup.fullscreen == video.fullscreen_enabled ||
9540 !video.fullscreen_available)
9543 SDLSetWindowFullscreen(setup.fullscreen);
9545 // set setup value according to successfully changed fullscreen mode
9546 setup.fullscreen = video.fullscreen_enabled;
9549 void ChangeWindowScalingIfNeeded(void)
9551 // if setup and video window scaling are already matching, nothing do do
9552 if (setup.window_scaling_percent == video.window_scaling_percent ||
9553 video.fullscreen_enabled)
9556 SDLSetWindowScaling(setup.window_scaling_percent);
9558 // set setup value according to successfully changed window scaling
9559 setup.window_scaling_percent = video.window_scaling_percent;
9562 void ChangeVsyncModeIfNeeded(void)
9564 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9565 int video_vsync_mode = video.vsync_mode;
9567 // if setup and video vsync mode are already matching, nothing do do
9568 if (setup_vsync_mode == video_vsync_mode)
9571 // if renderer is using OpenGL, vsync mode can directly be changed
9572 SDLSetScreenVsyncMode(setup.vsync_mode);
9574 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9575 if (video.vsync_mode == video_vsync_mode)
9577 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9579 // save backbuffer content which gets lost when re-creating screen
9580 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9582 // force re-creating screen and renderer to set new vsync mode
9583 video.fullscreen_enabled = !setup.fullscreen;
9585 // when creating new renderer, destroy textures linked to old renderer
9586 FreeAllImageTextures(); // needs old renderer to free the textures
9588 // re-create screen and renderer (including change of vsync mode)
9589 ChangeVideoModeIfNeeded(setup.fullscreen);
9591 // set setup value according to successfully changed fullscreen mode
9592 setup.fullscreen = video.fullscreen_enabled;
9594 // restore backbuffer content from temporary backbuffer backup bitmap
9595 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9596 FreeBitmap(tmp_backbuffer);
9598 // update visible window/screen
9599 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9601 // when changing vsync mode, re-create textures for new renderer
9602 InitImageTextures();
9605 // set setup value according to successfully changed vsync mode
9606 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9609 static void JoinRectangles(int *x, int *y, int *width, int *height,
9610 int x2, int y2, int width2, int height2)
9612 // do not join with "off-screen" rectangle
9613 if (x2 == -1 || y2 == -1)
9618 *width = MAX(*width, width2);
9619 *height = MAX(*height, height2);
9622 void SetAnimStatus(int anim_status_new)
9624 if (anim_status_new == GAME_MODE_MAIN)
9625 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9626 else if (anim_status_new == GAME_MODE_NAMES)
9627 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9628 else if (anim_status_new == GAME_MODE_SCORES)
9629 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9631 global.anim_status_next = anim_status_new;
9633 // directly set screen modes that are entered without fading
9634 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9635 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9636 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9637 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9638 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9639 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9640 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9641 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9642 global.anim_status = global.anim_status_next;
9645 void SetGameStatus(int game_status_new)
9647 if (game_status_new != game_status)
9648 game_status_last_screen = game_status;
9650 game_status = game_status_new;
9652 SetAnimStatus(game_status_new);
9655 void SetFontStatus(int game_status_new)
9657 static int last_game_status = -1;
9659 if (game_status_new != -1)
9661 // set game status for font use after storing last game status
9662 last_game_status = game_status;
9663 game_status = game_status_new;
9667 // reset game status after font use from last stored game status
9668 game_status = last_game_status;
9672 void ResetFontStatus(void)
9677 void SetLevelSetInfo(char *identifier, int level_nr)
9679 setString(&levelset.identifier, identifier);
9681 levelset.level_nr = level_nr;
9684 boolean CheckIfAllViewportsHaveChanged(void)
9686 // if game status has not changed, viewports have not changed either
9687 if (game_status == game_status_last)
9690 // check if all viewports have changed with current game status
9692 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9693 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9694 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9695 int new_real_sx = vp_playfield->x;
9696 int new_real_sy = vp_playfield->y;
9697 int new_full_sxsize = vp_playfield->width;
9698 int new_full_sysize = vp_playfield->height;
9699 int new_dx = vp_door_1->x;
9700 int new_dy = vp_door_1->y;
9701 int new_dxsize = vp_door_1->width;
9702 int new_dysize = vp_door_1->height;
9703 int new_vx = vp_door_2->x;
9704 int new_vy = vp_door_2->y;
9705 int new_vxsize = vp_door_2->width;
9706 int new_vysize = vp_door_2->height;
9708 boolean playfield_viewport_has_changed =
9709 (new_real_sx != REAL_SX ||
9710 new_real_sy != REAL_SY ||
9711 new_full_sxsize != FULL_SXSIZE ||
9712 new_full_sysize != FULL_SYSIZE);
9714 boolean door_1_viewport_has_changed =
9717 new_dxsize != DXSIZE ||
9718 new_dysize != DYSIZE);
9720 boolean door_2_viewport_has_changed =
9723 new_vxsize != VXSIZE ||
9724 new_vysize != VYSIZE ||
9725 game_status_last == GAME_MODE_EDITOR);
9727 return (playfield_viewport_has_changed &&
9728 door_1_viewport_has_changed &&
9729 door_2_viewport_has_changed);
9732 boolean CheckFadeAll(void)
9734 return (CheckIfGlobalBorderHasChanged() ||
9735 CheckIfAllViewportsHaveChanged());
9738 void ChangeViewportPropertiesIfNeeded(void)
9740 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9741 FALSE : setup.small_game_graphics);
9742 int gfx_game_mode = getGlobalGameStatus(game_status);
9743 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9745 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9746 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9747 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9748 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9749 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9750 int new_win_xsize = vp_window->width;
9751 int new_win_ysize = vp_window->height;
9752 int border_left = vp_playfield->border_left;
9753 int border_right = vp_playfield->border_right;
9754 int border_top = vp_playfield->border_top;
9755 int border_bottom = vp_playfield->border_bottom;
9756 int new_sx = vp_playfield->x + border_left;
9757 int new_sy = vp_playfield->y + border_top;
9758 int new_sxsize = vp_playfield->width - border_left - border_right;
9759 int new_sysize = vp_playfield->height - border_top - border_bottom;
9760 int new_real_sx = vp_playfield->x;
9761 int new_real_sy = vp_playfield->y;
9762 int new_full_sxsize = vp_playfield->width;
9763 int new_full_sysize = vp_playfield->height;
9764 int new_dx = vp_door_1->x;
9765 int new_dy = vp_door_1->y;
9766 int new_dxsize = vp_door_1->width;
9767 int new_dysize = vp_door_1->height;
9768 int new_vx = vp_door_2->x;
9769 int new_vy = vp_door_2->y;
9770 int new_vxsize = vp_door_2->width;
9771 int new_vysize = vp_door_2->height;
9772 int new_ex = vp_door_3->x;
9773 int new_ey = vp_door_3->y;
9774 int new_exsize = vp_door_3->width;
9775 int new_eysize = vp_door_3->height;
9776 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9777 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9778 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9779 int new_scr_fieldx = new_sxsize / tilesize;
9780 int new_scr_fieldy = new_sysize / tilesize;
9781 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9782 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9783 boolean init_gfx_buffers = FALSE;
9784 boolean init_video_buffer = FALSE;
9785 boolean init_gadgets_and_anims = FALSE;
9786 boolean init_em_graphics = FALSE;
9788 if (new_win_xsize != WIN_XSIZE ||
9789 new_win_ysize != WIN_YSIZE)
9791 WIN_XSIZE = new_win_xsize;
9792 WIN_YSIZE = new_win_ysize;
9794 init_video_buffer = TRUE;
9795 init_gfx_buffers = TRUE;
9796 init_gadgets_and_anims = TRUE;
9798 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9801 if (new_scr_fieldx != SCR_FIELDX ||
9802 new_scr_fieldy != SCR_FIELDY)
9804 // this always toggles between MAIN and GAME when using small tile size
9806 SCR_FIELDX = new_scr_fieldx;
9807 SCR_FIELDY = new_scr_fieldy;
9809 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9820 new_sxsize != SXSIZE ||
9821 new_sysize != SYSIZE ||
9822 new_dxsize != DXSIZE ||
9823 new_dysize != DYSIZE ||
9824 new_vxsize != VXSIZE ||
9825 new_vysize != VYSIZE ||
9826 new_exsize != EXSIZE ||
9827 new_eysize != EYSIZE ||
9828 new_real_sx != REAL_SX ||
9829 new_real_sy != REAL_SY ||
9830 new_full_sxsize != FULL_SXSIZE ||
9831 new_full_sysize != FULL_SYSIZE ||
9832 new_tilesize_var != TILESIZE_VAR
9835 // ------------------------------------------------------------------------
9836 // determine next fading area for changed viewport definitions
9837 // ------------------------------------------------------------------------
9839 // start with current playfield area (default fading area)
9842 FADE_SXSIZE = FULL_SXSIZE;
9843 FADE_SYSIZE = FULL_SYSIZE;
9845 // add new playfield area if position or size has changed
9846 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9847 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9849 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9850 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9853 // add current and new door 1 area if position or size has changed
9854 if (new_dx != DX || new_dy != DY ||
9855 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9857 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9858 DX, DY, DXSIZE, DYSIZE);
9859 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9860 new_dx, new_dy, new_dxsize, new_dysize);
9863 // add current and new door 2 area if position or size has changed
9864 if (new_vx != VX || new_vy != VY ||
9865 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9867 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9868 VX, VY, VXSIZE, VYSIZE);
9869 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9870 new_vx, new_vy, new_vxsize, new_vysize);
9873 // ------------------------------------------------------------------------
9874 // handle changed tile size
9875 // ------------------------------------------------------------------------
9877 if (new_tilesize_var != TILESIZE_VAR)
9879 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9881 // changing tile size invalidates scroll values of engine snapshots
9882 FreeEngineSnapshotSingle();
9884 // changing tile size requires update of graphic mapping for EM engine
9885 init_em_graphics = TRUE;
9896 SXSIZE = new_sxsize;
9897 SYSIZE = new_sysize;
9898 DXSIZE = new_dxsize;
9899 DYSIZE = new_dysize;
9900 VXSIZE = new_vxsize;
9901 VYSIZE = new_vysize;
9902 EXSIZE = new_exsize;
9903 EYSIZE = new_eysize;
9904 REAL_SX = new_real_sx;
9905 REAL_SY = new_real_sy;
9906 FULL_SXSIZE = new_full_sxsize;
9907 FULL_SYSIZE = new_full_sysize;
9908 TILESIZE_VAR = new_tilesize_var;
9910 init_gfx_buffers = TRUE;
9911 init_gadgets_and_anims = TRUE;
9913 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9914 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9917 if (init_gfx_buffers)
9919 // Debug("tools:viewport", "init_gfx_buffers");
9921 SCR_FIELDX = new_scr_fieldx_buffers;
9922 SCR_FIELDY = new_scr_fieldy_buffers;
9926 SCR_FIELDX = new_scr_fieldx;
9927 SCR_FIELDY = new_scr_fieldy;
9929 SetDrawDeactivationMask(REDRAW_NONE);
9930 SetDrawBackgroundMask(REDRAW_FIELD);
9933 if (init_video_buffer)
9935 // Debug("tools:viewport", "init_video_buffer");
9937 FreeAllImageTextures(); // needs old renderer to free the textures
9939 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9940 InitImageTextures();
9943 if (init_gadgets_and_anims)
9945 // Debug("tools:viewport", "init_gadgets_and_anims");
9948 InitGlobalAnimations();
9951 if (init_em_graphics)
9953 InitGraphicInfo_EM();
9957 void OpenURL(char *url)
9959 #if SDL_VERSION_ATLEAST(2,0,14)
9962 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9963 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9964 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9968 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9970 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9974 // ============================================================================
9976 // ============================================================================
9978 #if defined(PLATFORM_WINDOWS)
9979 /* FILETIME of Jan 1 1970 00:00:00. */
9980 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9983 * timezone information is stored outside the kernel so tzp isn't used anymore.
9985 * Note: this function is not for Win32 high precision timing purpose. See
9988 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9991 SYSTEMTIME system_time;
9992 ULARGE_INTEGER ularge;
9994 GetSystemTime(&system_time);
9995 SystemTimeToFileTime(&system_time, &file_time);
9996 ularge.LowPart = file_time.dwLowDateTime;
9997 ularge.HighPart = file_time.dwHighDateTime;
9999 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10000 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10006 static char *test_init_uuid_random_function_simple(void)
10008 static char seed_text[100];
10009 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10011 sprintf(seed_text, "%d", seed);
10016 static char *test_init_uuid_random_function_better(void)
10018 static char seed_text[100];
10019 struct timeval current_time;
10021 gettimeofday(¤t_time, NULL);
10023 prng_seed_bytes(¤t_time, sizeof(current_time));
10025 sprintf(seed_text, "%ld.%ld",
10026 (long)current_time.tv_sec,
10027 (long)current_time.tv_usec);
10032 #if defined(PLATFORM_WINDOWS)
10033 static char *test_init_uuid_random_function_better_windows(void)
10035 static char seed_text[100];
10036 struct timeval current_time;
10038 gettimeofday_windows(¤t_time, NULL);
10040 prng_seed_bytes(¤t_time, sizeof(current_time));
10042 sprintf(seed_text, "%ld.%ld",
10043 (long)current_time.tv_sec,
10044 (long)current_time.tv_usec);
10050 static unsigned int test_uuid_random_function_simple(int max)
10052 return GetSimpleRandom(max);
10055 static unsigned int test_uuid_random_function_better(int max)
10057 return (max > 0 ? prng_get_uint() % max : 0);
10060 #if defined(PLATFORM_WINDOWS)
10061 #define NUM_UUID_TESTS 3
10063 #define NUM_UUID_TESTS 2
10066 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10068 struct hashtable *hash_seeds =
10069 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10070 struct hashtable *hash_uuids =
10071 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10072 static char message[100];
10075 char *random_name = (nr == 0 ? "simple" : "better");
10076 char *random_type = (always_seed ? "always" : "only once");
10077 char *(*init_random_function)(void) =
10079 test_init_uuid_random_function_simple :
10080 test_init_uuid_random_function_better);
10081 unsigned int (*random_function)(int) =
10083 test_uuid_random_function_simple :
10084 test_uuid_random_function_better);
10087 #if defined(PLATFORM_WINDOWS)
10090 random_name = "windows";
10091 init_random_function = test_init_uuid_random_function_better_windows;
10097 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10098 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10100 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10101 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10102 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10104 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10108 // always initialize random number generator at least once
10109 init_random_function();
10111 unsigned int time_start = SDL_GetTicks();
10113 for (i = 0; i < num_uuids; i++)
10117 char *seed = getStringCopy(init_random_function());
10119 hashtable_remove(hash_seeds, seed);
10120 hashtable_insert(hash_seeds, seed, "1");
10123 char *uuid = getStringCopy(getUUIDExt(random_function));
10125 hashtable_remove(hash_uuids, uuid);
10126 hashtable_insert(hash_uuids, uuid, "1");
10129 int num_unique_seeds = hashtable_count(hash_seeds);
10130 int num_unique_uuids = hashtable_count(hash_uuids);
10132 unsigned int time_needed = SDL_GetTicks() - time_start;
10134 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10136 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10139 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10141 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10142 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10144 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10146 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10148 Request(message, REQ_CONFIRM);
10150 hashtable_destroy(hash_seeds, 0);
10151 hashtable_destroy(hash_uuids, 0);
10154 void TestGeneratingUUIDs(void)
10156 int num_uuids = 1000000;
10159 for (i = 0; i < NUM_UUID_TESTS; i++)
10160 for (j = 0; j < 2; j++)
10161 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10163 CloseAllAndExit(0);