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 DrawEnvelopeRequest(text, req_state);
3344 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3346 if (anim_mode == ANIM_DEFAULT)
3347 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3349 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3353 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3355 if (anim_mode != ANIM_NONE)
3356 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3358 if (anim_mode == ANIM_DEFAULT)
3359 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3362 game.envelope_active = FALSE;
3365 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3367 if (IS_MM_WALL(element))
3369 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3375 int graphic = el2preimg(element);
3377 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3378 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3383 void DrawLevel(int draw_background_mask)
3387 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3388 SetDrawBackgroundMask(draw_background_mask);
3392 for (x = BX1; x <= BX2; x++)
3393 for (y = BY1; y <= BY2; y++)
3394 DrawScreenField(x, y);
3396 redraw_mask |= REDRAW_FIELD;
3399 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3404 for (x = 0; x < size_x; x++)
3405 for (y = 0; y < size_y; y++)
3406 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3408 redraw_mask |= REDRAW_FIELD;
3411 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3415 for (x = 0; x < size_x; x++)
3416 for (y = 0; y < size_y; y++)
3417 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3419 redraw_mask |= REDRAW_FIELD;
3422 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3424 boolean show_level_border = (BorderElement != EL_EMPTY);
3425 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3426 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3427 int tile_size = preview.tile_size;
3428 int preview_width = preview.xsize * tile_size;
3429 int preview_height = preview.ysize * tile_size;
3430 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3431 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3432 int real_preview_width = real_preview_xsize * tile_size;
3433 int real_preview_height = real_preview_ysize * tile_size;
3434 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3435 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3438 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3441 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3443 dst_x += (preview_width - real_preview_width) / 2;
3444 dst_y += (preview_height - real_preview_height) / 2;
3446 for (x = 0; x < real_preview_xsize; x++)
3448 for (y = 0; y < real_preview_ysize; y++)
3450 int lx = from_x + x + (show_level_border ? -1 : 0);
3451 int ly = from_y + y + (show_level_border ? -1 : 0);
3452 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3453 getBorderElement(lx, ly));
3455 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3456 element, tile_size);
3460 redraw_mask |= REDRAW_FIELD;
3463 #define MICROLABEL_EMPTY 0
3464 #define MICROLABEL_LEVEL_NAME 1
3465 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3466 #define MICROLABEL_LEVEL_AUTHOR 3
3467 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3468 #define MICROLABEL_IMPORTED_FROM 5
3469 #define MICROLABEL_IMPORTED_BY_HEAD 6
3470 #define MICROLABEL_IMPORTED_BY 7
3472 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3474 int max_text_width = SXSIZE;
3475 int font_width = getFontWidth(font_nr);
3477 if (pos->align == ALIGN_CENTER)
3478 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3479 else if (pos->align == ALIGN_RIGHT)
3480 max_text_width = pos->x;
3482 max_text_width = SXSIZE - pos->x;
3484 return max_text_width / font_width;
3487 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3489 char label_text[MAX_OUTPUT_LINESIZE + 1];
3490 int max_len_label_text;
3491 int font_nr = pos->font;
3494 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3497 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3498 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3499 mode == MICROLABEL_IMPORTED_BY_HEAD)
3500 font_nr = pos->font_alt;
3502 max_len_label_text = getMaxTextLength(pos, font_nr);
3504 if (pos->size != -1)
3505 max_len_label_text = pos->size;
3507 for (i = 0; i < max_len_label_text; i++)
3508 label_text[i] = ' ';
3509 label_text[max_len_label_text] = '\0';
3511 if (strlen(label_text) > 0)
3512 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3515 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3516 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3517 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3518 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3519 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3520 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3521 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3522 max_len_label_text);
3523 label_text[max_len_label_text] = '\0';
3525 if (strlen(label_text) > 0)
3526 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3528 redraw_mask |= REDRAW_FIELD;
3531 static void DrawPreviewLevelLabel(int mode)
3533 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3536 static void DrawPreviewLevelInfo(int mode)
3538 if (mode == MICROLABEL_LEVEL_NAME)
3539 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3540 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3541 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3544 static void DrawPreviewLevelExt(boolean restart)
3546 static DelayCounter scroll_delay = { 0 };
3547 static DelayCounter label_delay = { 0 };
3548 static int from_x, from_y, scroll_direction;
3549 static int label_state, label_counter;
3550 boolean show_level_border = (BorderElement != EL_EMPTY);
3551 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3552 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3554 scroll_delay.value = preview.step_delay;
3555 label_delay.value = MICROLEVEL_LABEL_DELAY;
3562 if (preview.anim_mode == ANIM_CENTERED)
3564 if (level_xsize > preview.xsize)
3565 from_x = (level_xsize - preview.xsize) / 2;
3566 if (level_ysize > preview.ysize)
3567 from_y = (level_ysize - preview.ysize) / 2;
3570 from_x += preview.xoffset;
3571 from_y += preview.yoffset;
3573 scroll_direction = MV_RIGHT;
3577 DrawPreviewLevelPlayfield(from_x, from_y);
3578 DrawPreviewLevelLabel(label_state);
3580 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3581 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3583 // initialize delay counters
3584 ResetDelayCounter(&scroll_delay);
3585 ResetDelayCounter(&label_delay);
3587 if (leveldir_current->name)
3589 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3590 char label_text[MAX_OUTPUT_LINESIZE + 1];
3591 int font_nr = pos->font;
3592 int max_len_label_text = getMaxTextLength(pos, font_nr);
3594 if (pos->size != -1)
3595 max_len_label_text = pos->size;
3597 strncpy(label_text, leveldir_current->name, max_len_label_text);
3598 label_text[max_len_label_text] = '\0';
3600 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3601 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3607 // scroll preview level, if needed
3608 if (preview.anim_mode != ANIM_NONE &&
3609 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3610 DelayReached(&scroll_delay))
3612 switch (scroll_direction)
3617 from_x -= preview.step_offset;
3618 from_x = (from_x < 0 ? 0 : from_x);
3621 scroll_direction = MV_UP;
3625 if (from_x < level_xsize - preview.xsize)
3627 from_x += preview.step_offset;
3628 from_x = (from_x > level_xsize - preview.xsize ?
3629 level_xsize - preview.xsize : from_x);
3632 scroll_direction = MV_DOWN;
3638 from_y -= preview.step_offset;
3639 from_y = (from_y < 0 ? 0 : from_y);
3642 scroll_direction = MV_RIGHT;
3646 if (from_y < level_ysize - preview.ysize)
3648 from_y += preview.step_offset;
3649 from_y = (from_y > level_ysize - preview.ysize ?
3650 level_ysize - preview.ysize : from_y);
3653 scroll_direction = MV_LEFT;
3660 DrawPreviewLevelPlayfield(from_x, from_y);
3663 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3664 // redraw micro level label, if needed
3665 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3666 !strEqual(level.author, ANONYMOUS_NAME) &&
3667 !strEqual(level.author, leveldir_current->name) &&
3668 DelayReached(&label_delay))
3670 int max_label_counter = 23;
3672 if (leveldir_current->imported_from != NULL &&
3673 strlen(leveldir_current->imported_from) > 0)
3674 max_label_counter += 14;
3675 if (leveldir_current->imported_by != NULL &&
3676 strlen(leveldir_current->imported_by) > 0)
3677 max_label_counter += 14;
3679 label_counter = (label_counter + 1) % max_label_counter;
3680 label_state = (label_counter >= 0 && label_counter <= 7 ?
3681 MICROLABEL_LEVEL_NAME :
3682 label_counter >= 9 && label_counter <= 12 ?
3683 MICROLABEL_LEVEL_AUTHOR_HEAD :
3684 label_counter >= 14 && label_counter <= 21 ?
3685 MICROLABEL_LEVEL_AUTHOR :
3686 label_counter >= 23 && label_counter <= 26 ?
3687 MICROLABEL_IMPORTED_FROM_HEAD :
3688 label_counter >= 28 && label_counter <= 35 ?
3689 MICROLABEL_IMPORTED_FROM :
3690 label_counter >= 37 && label_counter <= 40 ?
3691 MICROLABEL_IMPORTED_BY_HEAD :
3692 label_counter >= 42 && label_counter <= 49 ?
3693 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3695 if (leveldir_current->imported_from == NULL &&
3696 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3697 label_state == MICROLABEL_IMPORTED_FROM))
3698 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3699 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3701 DrawPreviewLevelLabel(label_state);
3705 void DrawPreviewPlayers(void)
3707 if (game_status != GAME_MODE_MAIN)
3710 // do not draw preview players if level preview redefined, but players aren't
3711 if (preview.redefined && !menu.main.preview_players.redefined)
3714 boolean player_found[MAX_PLAYERS];
3715 int num_players = 0;
3718 for (i = 0; i < MAX_PLAYERS; i++)
3719 player_found[i] = FALSE;
3721 // check which players can be found in the level (simple approach)
3722 for (x = 0; x < lev_fieldx; x++)
3724 for (y = 0; y < lev_fieldy; y++)
3726 int element = level.field[x][y];
3728 if (IS_PLAYER_ELEMENT(element))
3730 int player_nr = GET_PLAYER_NR(element);
3732 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3734 if (!player_found[player_nr])
3737 player_found[player_nr] = TRUE;
3742 struct TextPosInfo *pos = &menu.main.preview_players;
3743 int tile_size = pos->tile_size;
3744 int border_size = pos->border_size;
3745 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3746 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3747 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3748 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3749 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3750 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3751 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3752 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3753 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3754 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3755 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3756 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3758 // clear area in which the players will be drawn
3759 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3760 max_players_width, max_players_height);
3762 if (!network.enabled && !setup.team_mode)
3765 // only draw players if level is suited for team mode
3766 if (num_players < 2)
3769 // draw all players that were found in the level
3770 for (i = 0; i < MAX_PLAYERS; i++)
3772 if (player_found[i])
3774 int graphic = el2img(EL_PLAYER_1 + i);
3776 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3778 xpos += player_xoffset;
3779 ypos += player_yoffset;
3784 void DrawPreviewLevelInitial(void)
3786 DrawPreviewLevelExt(TRUE);
3787 DrawPreviewPlayers();
3790 void DrawPreviewLevelAnimation(void)
3792 DrawPreviewLevelExt(FALSE);
3795 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3796 int border_size, int font_nr)
3798 int graphic = el2img(EL_PLAYER_1 + player_nr);
3799 int font_height = getFontHeight(font_nr);
3800 int player_height = MAX(tile_size, font_height);
3801 int xoffset_text = tile_size + border_size;
3802 int yoffset_text = (player_height - font_height) / 2;
3803 int yoffset_graphic = (player_height - tile_size) / 2;
3804 char *player_name = getNetworkPlayerName(player_nr + 1);
3806 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3808 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3811 static void DrawNetworkPlayersExt(boolean force)
3813 if (game_status != GAME_MODE_MAIN)
3816 if (!network.connected && !force)
3819 // do not draw network players if level preview redefined, but players aren't
3820 if (preview.redefined && !menu.main.network_players.redefined)
3823 int num_players = 0;
3826 for (i = 0; i < MAX_PLAYERS; i++)
3827 if (stored_player[i].connected_network)
3830 struct TextPosInfo *pos = &menu.main.network_players;
3831 int tile_size = pos->tile_size;
3832 int border_size = pos->border_size;
3833 int xoffset_text = tile_size + border_size;
3834 int font_nr = pos->font;
3835 int font_width = getFontWidth(font_nr);
3836 int font_height = getFontHeight(font_nr);
3837 int player_height = MAX(tile_size, font_height);
3838 int player_yoffset = player_height + border_size;
3839 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3840 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3841 int all_players_height = num_players * player_yoffset - border_size;
3842 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3843 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3844 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3846 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3847 max_players_width, max_players_height);
3849 // first draw local network player ...
3850 for (i = 0; i < MAX_PLAYERS; i++)
3852 if (stored_player[i].connected_network &&
3853 stored_player[i].connected_locally)
3855 char *player_name = getNetworkPlayerName(i + 1);
3856 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3857 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3859 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3861 ypos += player_yoffset;
3865 // ... then draw all other network players
3866 for (i = 0; i < MAX_PLAYERS; i++)
3868 if (stored_player[i].connected_network &&
3869 !stored_player[i].connected_locally)
3871 char *player_name = getNetworkPlayerName(i + 1);
3872 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3873 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3875 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3877 ypos += player_yoffset;
3882 void DrawNetworkPlayers(void)
3884 DrawNetworkPlayersExt(FALSE);
3887 void ClearNetworkPlayers(void)
3889 DrawNetworkPlayersExt(TRUE);
3892 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3893 int graphic, int lx, int ly,
3896 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3898 if (mask_mode == USE_MASKING)
3899 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3901 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3904 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3905 int graphic, int sync_frame, int mask_mode)
3907 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3909 if (mask_mode == USE_MASKING)
3910 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3912 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3915 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3916 int graphic, int sync_frame, int tilesize,
3919 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3921 if (mask_mode == USE_MASKING)
3922 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3924 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3927 static void DrawGraphicAnimation(int x, int y, int graphic)
3929 int lx = LEVELX(x), ly = LEVELY(y);
3930 int mask_mode = NO_MASKING;
3932 if (!IN_SCR_FIELD(x, y))
3935 if (game.use_masked_elements)
3937 if (Tile[lx][ly] != EL_EMPTY)
3939 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3941 mask_mode = USE_MASKING;
3945 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3946 graphic, lx, ly, mask_mode);
3948 MarkTileDirty(x, y);
3951 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3953 int lx = LEVELX(x), ly = LEVELY(y);
3954 int mask_mode = NO_MASKING;
3956 if (!IN_SCR_FIELD(x, y))
3959 if (game.use_masked_elements)
3961 if (Tile[lx][ly] != EL_EMPTY)
3963 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3965 mask_mode = USE_MASKING;
3969 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3970 graphic, lx, ly, mask_mode);
3972 MarkTileDirty(x, y);
3975 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3977 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3980 void DrawLevelElementAnimation(int x, int y, int element)
3982 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3984 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3987 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3989 int sx = SCREENX(x), sy = SCREENY(y);
3991 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3994 if (Tile[x][y] == EL_EMPTY)
3995 graphic = el2img(GfxElementEmpty[x][y]);
3997 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4000 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4003 DrawGraphicAnimation(sx, sy, graphic);
4006 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4007 DrawLevelFieldCrumbled(x, y);
4009 if (GFX_CRUMBLED(Tile[x][y]))
4010 DrawLevelFieldCrumbled(x, y);
4014 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4016 int sx = SCREENX(x), sy = SCREENY(y);
4019 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4022 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4024 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4027 DrawGraphicAnimation(sx, sy, graphic);
4029 if (GFX_CRUMBLED(element))
4030 DrawLevelFieldCrumbled(x, y);
4033 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4035 if (player->use_murphy)
4037 // this works only because currently only one player can be "murphy" ...
4038 static int last_horizontal_dir = MV_LEFT;
4039 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4041 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4042 last_horizontal_dir = move_dir;
4044 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4046 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4048 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4054 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4057 static boolean equalGraphics(int graphic1, int graphic2)
4059 struct GraphicInfo *g1 = &graphic_info[graphic1];
4060 struct GraphicInfo *g2 = &graphic_info[graphic2];
4062 return (g1->bitmap == g2->bitmap &&
4063 g1->src_x == g2->src_x &&
4064 g1->src_y == g2->src_y &&
4065 g1->anim_frames == g2->anim_frames &&
4066 g1->anim_delay == g2->anim_delay &&
4067 g1->anim_mode == g2->anim_mode);
4070 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4074 DRAW_PLAYER_STAGE_INIT = 0,
4075 DRAW_PLAYER_STAGE_LAST_FIELD,
4076 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4077 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4078 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4079 DRAW_PLAYER_STAGE_PLAYER,
4081 DRAW_PLAYER_STAGE_PLAYER,
4082 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4084 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4085 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4087 NUM_DRAW_PLAYER_STAGES
4090 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4092 static int static_last_player_graphic[MAX_PLAYERS];
4093 static int static_last_player_frame[MAX_PLAYERS];
4094 static boolean static_player_is_opaque[MAX_PLAYERS];
4095 static boolean draw_player[MAX_PLAYERS];
4096 int pnr = player->index_nr;
4098 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4100 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4101 static_last_player_frame[pnr] = player->Frame;
4102 static_player_is_opaque[pnr] = FALSE;
4104 draw_player[pnr] = TRUE;
4107 if (!draw_player[pnr])
4111 if (!IN_LEV_FIELD(player->jx, player->jy))
4113 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4114 Debug("draw:DrawPlayerExt", "This should never happen!");
4116 draw_player[pnr] = FALSE;
4122 int last_player_graphic = static_last_player_graphic[pnr];
4123 int last_player_frame = static_last_player_frame[pnr];
4124 boolean player_is_opaque = static_player_is_opaque[pnr];
4126 int jx = player->jx;
4127 int jy = player->jy;
4128 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4129 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4130 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4131 int last_jx = (player->is_moving ? jx - dx : jx);
4132 int last_jy = (player->is_moving ? jy - dy : jy);
4133 int next_jx = jx + dx;
4134 int next_jy = jy + dy;
4135 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4136 int sx = SCREENX(jx);
4137 int sy = SCREENY(jy);
4138 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4139 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4140 int element = Tile[jx][jy];
4141 int last_element = Tile[last_jx][last_jy];
4142 int action = (player->is_pushing ? ACTION_PUSHING :
4143 player->is_digging ? ACTION_DIGGING :
4144 player->is_collecting ? ACTION_COLLECTING :
4145 player->is_moving ? ACTION_MOVING :
4146 player->is_snapping ? ACTION_SNAPPING :
4147 player->is_dropping ? ACTION_DROPPING :
4148 player->is_waiting ? player->action_waiting :
4151 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4153 // ------------------------------------------------------------------------
4154 // initialize drawing the player
4155 // ------------------------------------------------------------------------
4157 draw_player[pnr] = FALSE;
4159 // GfxElement[][] is set to the element the player is digging or collecting;
4160 // remove also for off-screen player if the player is not moving anymore
4161 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4162 GfxElement[jx][jy] = EL_UNDEFINED;
4164 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4167 if (element == EL_EXPLOSION)
4170 InitPlayerGfxAnimation(player, action, move_dir);
4172 draw_player[pnr] = TRUE;
4174 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4176 // ------------------------------------------------------------------------
4177 // draw things in the field the player is leaving, if needed
4178 // ------------------------------------------------------------------------
4180 if (!IN_SCR_FIELD(sx, sy))
4181 draw_player[pnr] = FALSE;
4183 if (!player->is_moving)
4186 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4188 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4190 if (last_element == EL_DYNAMITE_ACTIVE ||
4191 last_element == EL_EM_DYNAMITE_ACTIVE ||
4192 last_element == EL_SP_DISK_RED_ACTIVE)
4193 DrawDynamite(last_jx, last_jy);
4195 DrawLevelFieldThruMask(last_jx, last_jy);
4197 else if (last_element == EL_DYNAMITE_ACTIVE ||
4198 last_element == EL_EM_DYNAMITE_ACTIVE ||
4199 last_element == EL_SP_DISK_RED_ACTIVE)
4200 DrawDynamite(last_jx, last_jy);
4202 DrawLevelField(last_jx, last_jy);
4204 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4206 // ------------------------------------------------------------------------
4207 // draw things behind the player, if needed
4208 // ------------------------------------------------------------------------
4212 DrawLevelElement(jx, jy, Back[jx][jy]);
4217 if (IS_ACTIVE_BOMB(element))
4219 DrawLevelElement(jx, jy, EL_EMPTY);
4224 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4226 int old_element = GfxElement[jx][jy];
4227 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4228 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4230 if (GFX_CRUMBLED(old_element))
4231 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4233 DrawScreenGraphic(sx, sy, old_graphic, frame);
4235 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4236 static_player_is_opaque[pnr] = TRUE;
4240 GfxElement[jx][jy] = EL_UNDEFINED;
4242 // make sure that pushed elements are drawn with correct frame rate
4243 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4245 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4246 GfxFrame[jx][jy] = player->StepFrame;
4248 DrawLevelField(jx, jy);
4251 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4253 // ------------------------------------------------------------------------
4254 // draw things the player is pushing, if needed
4255 // ------------------------------------------------------------------------
4257 if (!player->is_pushing || !player->is_moving)
4260 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4263 int gfx_frame = GfxFrame[jx][jy];
4265 if (!IS_MOVING(jx, jy)) // push movement already finished
4267 element = Tile[next_jx][next_jy];
4268 gfx_frame = GfxFrame[next_jx][next_jy];
4271 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4272 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4273 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4275 // draw background element under pushed element (like the Sokoban field)
4276 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4278 // this allows transparent pushing animation over non-black background
4281 DrawLevelElement(jx, jy, Back[jx][jy]);
4283 DrawLevelElement(jx, jy, EL_EMPTY);
4286 if (Back[next_jx][next_jy])
4287 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4289 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4291 int px = SCREENX(jx), py = SCREENY(jy);
4292 int pxx = (TILEX - ABS(sxx)) * dx;
4293 int pyy = (TILEY - ABS(syy)) * dy;
4296 // do not draw (EM style) pushing animation when pushing is finished
4297 // (two-tile animations usually do not contain start and end frame)
4298 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4299 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4301 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4303 // masked drawing is needed for EMC style (double) movement graphics
4304 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4305 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4308 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4310 // ------------------------------------------------------------------------
4311 // draw player himself
4312 // ------------------------------------------------------------------------
4314 int graphic = getPlayerGraphic(player, move_dir);
4316 // in the case of changed player action or direction, prevent the current
4317 // animation frame from being restarted for identical animations
4318 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4319 player->Frame = last_player_frame;
4321 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4323 if (player_is_opaque)
4324 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4326 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4328 if (SHIELD_ON(player))
4330 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4331 IMG_SHIELD_NORMAL_ACTIVE);
4332 frame = getGraphicAnimationFrame(graphic, -1);
4334 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4337 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4339 // ------------------------------------------------------------------------
4340 // draw things in front of player (active dynamite or dynabombs)
4341 // ------------------------------------------------------------------------
4343 if (IS_ACTIVE_BOMB(element))
4345 int graphic = el2img(element);
4346 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4348 if (game.emulation == EMU_SUPAPLEX)
4349 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4351 DrawGraphicThruMask(sx, sy, graphic, frame);
4354 if (player_is_moving && last_element == EL_EXPLOSION)
4356 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4357 GfxElement[last_jx][last_jy] : EL_EMPTY);
4358 int graphic = el_act2img(element, ACTION_EXPLODING);
4359 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4360 int phase = ExplodePhase[last_jx][last_jy] - 1;
4361 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4364 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4367 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4369 // ------------------------------------------------------------------------
4370 // draw elements the player is just walking/passing through/under
4371 // ------------------------------------------------------------------------
4373 if (player_is_moving)
4375 // handle the field the player is leaving ...
4376 if (IS_ACCESSIBLE_INSIDE(last_element))
4377 DrawLevelField(last_jx, last_jy);
4378 else if (IS_ACCESSIBLE_UNDER(last_element))
4379 DrawLevelFieldThruMask(last_jx, last_jy);
4382 // do not redraw accessible elements if the player is just pushing them
4383 if (!player_is_moving || !player->is_pushing)
4385 // ... and the field the player is entering
4386 if (IS_ACCESSIBLE_INSIDE(element))
4387 DrawLevelField(jx, jy);
4388 else if (IS_ACCESSIBLE_UNDER(element))
4389 DrawLevelFieldThruMask(jx, jy);
4392 MarkTileDirty(sx, sy);
4396 void DrawPlayer(struct PlayerInfo *player)
4400 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4401 DrawPlayerExt(player, i);
4404 void DrawAllPlayers(void)
4408 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4409 for (j = 0; j < MAX_PLAYERS; j++)
4410 if (stored_player[j].active)
4411 DrawPlayerExt(&stored_player[j], i);
4414 void DrawPlayerField(int x, int y)
4416 if (!IS_PLAYER(x, y))
4419 DrawPlayer(PLAYERINFO(x, y));
4422 // ----------------------------------------------------------------------------
4424 void WaitForEventToContinue(void)
4426 boolean first_wait = TRUE;
4427 boolean still_wait = TRUE;
4429 if (program.headless)
4432 // simulate releasing mouse button over last gadget, if still pressed
4434 HandleGadgets(-1, -1, 0);
4436 button_status = MB_RELEASED;
4439 ClearPlayerAction();
4445 if (NextValidEvent(&event))
4449 case EVENT_BUTTONPRESS:
4450 case EVENT_FINGERPRESS:
4454 case EVENT_BUTTONRELEASE:
4455 case EVENT_FINGERRELEASE:
4456 still_wait = first_wait;
4459 case EVENT_KEYPRESS:
4460 case SDL_CONTROLLERBUTTONDOWN:
4461 case SDL_JOYBUTTONDOWN:
4466 HandleOtherEvents(&event);
4470 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4475 if (!PendingEvent())
4480 #define MAX_REQUEST_LINES 13
4481 #define MAX_REQUEST_LINE_FONT1_LEN 7
4482 #define MAX_REQUEST_LINE_FONT2_LEN 10
4484 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4486 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4487 int draw_buffer_last = GetDrawtoField();
4488 int width = request.width;
4489 int height = request.height;
4493 setRequestPosition(&sx, &sy, FALSE);
4495 button_status = MB_RELEASED;
4497 request_gadget_id = -1;
4504 SetDrawtoField(draw_buffer_game);
4506 HandleGameActions();
4508 SetDrawtoField(DRAW_TO_BACKBUFFER);
4515 while (NextValidEvent(&event))
4519 case EVENT_BUTTONPRESS:
4520 case EVENT_BUTTONRELEASE:
4521 case EVENT_MOTIONNOTIFY:
4523 DrawBuffer *drawto_last = drawto;
4526 if (event.type == EVENT_MOTIONNOTIFY)
4531 motion_status = TRUE;
4532 mx = ((MotionEvent *) &event)->x;
4533 my = ((MotionEvent *) &event)->y;
4537 motion_status = FALSE;
4538 mx = ((ButtonEvent *) &event)->x;
4539 my = ((ButtonEvent *) &event)->y;
4540 if (event.type == EVENT_BUTTONPRESS)
4541 button_status = ((ButtonEvent *) &event)->button;
4543 button_status = MB_RELEASED;
4546 if (global.use_envelope_request)
4548 // draw changed button states to temporary bitmap
4549 drawto = bitmap_db_store_1;
4552 // this sets 'request_gadget_id'
4553 HandleGadgets(mx, my, button_status);
4555 if (global.use_envelope_request)
4557 // restore pointer to drawing buffer
4558 drawto = drawto_last;
4560 // prepare complete envelope request from temporary bitmap
4561 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4565 switch (request_gadget_id)
4567 case TOOL_CTRL_ID_YES:
4568 case TOOL_CTRL_ID_TOUCH_YES:
4571 case TOOL_CTRL_ID_NO:
4572 case TOOL_CTRL_ID_TOUCH_NO:
4575 case TOOL_CTRL_ID_CONFIRM:
4576 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4577 result = TRUE | FALSE;
4580 case TOOL_CTRL_ID_PLAYER_1:
4583 case TOOL_CTRL_ID_PLAYER_2:
4586 case TOOL_CTRL_ID_PLAYER_3:
4589 case TOOL_CTRL_ID_PLAYER_4:
4594 // only check clickable animations if no request gadget clicked
4595 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4602 case SDL_WINDOWEVENT:
4603 HandleWindowEvent((WindowEvent *) &event);
4606 case SDL_APP_WILLENTERBACKGROUND:
4607 case SDL_APP_DIDENTERBACKGROUND:
4608 case SDL_APP_WILLENTERFOREGROUND:
4609 case SDL_APP_DIDENTERFOREGROUND:
4610 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4613 case EVENT_KEYPRESS:
4615 Key key = GetEventKey((KeyEvent *)&event);
4620 if (req_state & REQ_CONFIRM)
4629 #if defined(KSYM_Rewind)
4630 case KSYM_Rewind: // for Amazon Fire TV remote
4639 #if defined(KSYM_FastForward)
4640 case KSYM_FastForward: // for Amazon Fire TV remote
4646 HandleKeysDebug(key, KEY_PRESSED);
4650 if (req_state & REQ_PLAYER)
4652 int old_player_nr = setup.network_player_nr;
4655 result = old_player_nr + 1;
4660 result = old_player_nr + 1;
4691 case EVENT_FINGERRELEASE:
4692 case EVENT_KEYRELEASE:
4693 ClearPlayerAction();
4696 case SDL_CONTROLLERBUTTONDOWN:
4697 switch (event.cbutton.button)
4699 case SDL_CONTROLLER_BUTTON_A:
4700 case SDL_CONTROLLER_BUTTON_X:
4701 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4702 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4706 case SDL_CONTROLLER_BUTTON_B:
4707 case SDL_CONTROLLER_BUTTON_Y:
4708 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4709 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4710 case SDL_CONTROLLER_BUTTON_BACK:
4715 if (req_state & REQ_PLAYER)
4717 int old_player_nr = setup.network_player_nr;
4720 result = old_player_nr + 1;
4722 switch (event.cbutton.button)
4724 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4725 case SDL_CONTROLLER_BUTTON_Y:
4729 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4730 case SDL_CONTROLLER_BUTTON_B:
4734 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4735 case SDL_CONTROLLER_BUTTON_A:
4739 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4740 case SDL_CONTROLLER_BUTTON_X:
4751 case SDL_CONTROLLERBUTTONUP:
4752 HandleJoystickEvent(&event);
4753 ClearPlayerAction();
4757 HandleOtherEvents(&event);
4762 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4764 int joy = AnyJoystick();
4766 if (joy & JOY_BUTTON_1)
4768 else if (joy & JOY_BUTTON_2)
4771 else if (AnyJoystick())
4773 int joy = AnyJoystick();
4775 if (req_state & REQ_PLAYER)
4779 else if (joy & JOY_RIGHT)
4781 else if (joy & JOY_DOWN)
4783 else if (joy & JOY_LEFT)
4791 SetDrawtoField(draw_buffer_last);
4796 static void DoRequestBefore(unsigned int req_state)
4798 if (game_status == GAME_MODE_PLAYING)
4799 BlitScreenToBitmap(backbuffer);
4801 // disable deactivated drawing when quick-loading level tape recording
4802 if (tape.playing && tape.deactivate_display)
4803 TapeDeactivateDisplayOff(TRUE);
4805 SetMouseCursor(CURSOR_DEFAULT);
4807 // pause network game while waiting for request to answer
4808 if (network.enabled &&
4809 game_status == GAME_MODE_PLAYING &&
4810 !game.all_players_gone &&
4811 req_state & REQUEST_WAIT_FOR_INPUT)
4812 SendToServer_PausePlaying();
4814 // simulate releasing mouse button over last gadget, if still pressed
4816 HandleGadgets(-1, -1, 0);
4821 static void DoRequestAfter(unsigned int req_state)
4825 if (game_status == GAME_MODE_PLAYING)
4827 SetPanelBackground();
4828 SetDrawBackgroundMask(REDRAW_DOOR_1);
4832 SetDrawBackgroundMask(REDRAW_FIELD);
4835 // continue network game after request
4836 if (network.enabled &&
4837 game_status == GAME_MODE_PLAYING &&
4838 !game.all_players_gone &&
4839 req_state & REQUEST_WAIT_FOR_INPUT)
4840 SendToServer_ContinuePlaying();
4842 // restore deactivated drawing when quick-loading level tape recording
4843 if (tape.playing && tape.deactivate_display)
4844 TapeDeactivateDisplayOn();
4847 static boolean RequestDoor(char *text, unsigned int req_state)
4849 int draw_buffer_last = GetDrawtoField();
4850 unsigned int old_door_state = GetDoorState();
4851 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4852 int font_nr = FONT_TEXT_2;
4857 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4859 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4860 font_nr = FONT_TEXT_1;
4863 DoRequestBefore(req_state);
4865 // draw released gadget before proceeding
4868 if (old_door_state & DOOR_OPEN_1)
4870 CloseDoor(DOOR_CLOSE_1);
4872 // save old door content
4873 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4874 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4877 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4878 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4880 // clear door drawing field
4881 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4883 // force DOOR font inside door area
4884 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4886 // write text for request
4887 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4889 char text_line[max_request_line_len + 1];
4895 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4897 tc = *(text_ptr + tx);
4898 // if (!tc || tc == ' ')
4899 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4903 if ((tc == '?' || tc == '!') && tl == 0)
4913 strncpy(text_line, text_ptr, tl);
4916 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4917 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4918 text_line, font_nr);
4920 text_ptr += tl + (tc == ' ' ? 1 : 0);
4921 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4926 MapToolButtons(req_state);
4928 // copy request gadgets to door backbuffer
4929 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4931 OpenDoor(DOOR_OPEN_1);
4933 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4935 if (game_status == GAME_MODE_PLAYING)
4937 SetPanelBackground();
4938 SetDrawBackgroundMask(REDRAW_DOOR_1);
4942 SetDrawBackgroundMask(REDRAW_FIELD);
4948 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4950 // ---------- handle request buttons ----------
4951 result = RequestHandleEvents(req_state, draw_buffer_last);
4955 if (!(req_state & REQ_STAY_OPEN))
4957 CloseDoor(DOOR_CLOSE_1);
4959 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4960 (req_state & REQ_REOPEN))
4961 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4964 DoRequestAfter(req_state);
4969 static boolean RequestEnvelope(char *text, unsigned int req_state)
4971 int draw_buffer_last = GetDrawtoField();
4974 DoRequestBefore(req_state);
4976 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4978 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4980 if (game_status == GAME_MODE_PLAYING)
4982 SetPanelBackground();
4983 SetDrawBackgroundMask(REDRAW_DOOR_1);
4987 SetDrawBackgroundMask(REDRAW_FIELD);
4993 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4995 // ---------- handle request buttons ----------
4996 result = RequestHandleEvents(req_state, draw_buffer_last);
5000 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5002 DoRequestAfter(req_state);
5007 boolean Request(char *text, unsigned int req_state)
5009 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5010 boolean overlay_enabled = GetOverlayEnabled();
5013 // when showing request dialog after game ended, deactivate game panel
5015 game.panel.active = FALSE;
5017 game.request_active = TRUE;
5019 SetOverlayEnabled(FALSE);
5021 if (global.use_envelope_request)
5022 result = RequestEnvelope(text, req_state);
5024 result = RequestDoor(text, req_state);
5026 SetOverlayEnabled(overlay_enabled);
5028 game.request_active = FALSE;
5033 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5035 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5036 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5039 if (dpo1->sort_priority != dpo2->sort_priority)
5040 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5042 compare_result = dpo1->nr - dpo2->nr;
5044 return compare_result;
5047 void InitGraphicCompatibilityInfo_Doors(void)
5053 struct DoorInfo *door;
5057 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5058 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5060 { -1, -1, -1, NULL }
5062 struct Rect door_rect_list[] =
5064 { DX, DY, DXSIZE, DYSIZE },
5065 { VX, VY, VXSIZE, VYSIZE }
5069 for (i = 0; doors[i].door_token != -1; i++)
5071 int door_token = doors[i].door_token;
5072 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5073 int part_1 = doors[i].part_1;
5074 int part_8 = doors[i].part_8;
5075 int part_2 = part_1 + 1;
5076 int part_3 = part_1 + 2;
5077 struct DoorInfo *door = doors[i].door;
5078 struct Rect *door_rect = &door_rect_list[door_index];
5079 boolean door_gfx_redefined = FALSE;
5081 // check if any door part graphic definitions have been redefined
5083 for (j = 0; door_part_controls[j].door_token != -1; j++)
5085 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5086 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5088 if (dpc->door_token == door_token && fi->redefined)
5089 door_gfx_redefined = TRUE;
5092 // check for old-style door graphic/animation modifications
5094 if (!door_gfx_redefined)
5096 if (door->anim_mode & ANIM_STATIC_PANEL)
5098 door->panel.step_xoffset = 0;
5099 door->panel.step_yoffset = 0;
5102 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5104 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5105 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5106 int num_door_steps, num_panel_steps;
5108 // remove door part graphics other than the two default wings
5110 for (j = 0; door_part_controls[j].door_token != -1; j++)
5112 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5113 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5115 if (dpc->graphic >= part_3 &&
5116 dpc->graphic <= part_8)
5120 // set graphics and screen positions of the default wings
5122 g_part_1->width = door_rect->width;
5123 g_part_1->height = door_rect->height;
5124 g_part_2->width = door_rect->width;
5125 g_part_2->height = door_rect->height;
5126 g_part_2->src_x = door_rect->width;
5127 g_part_2->src_y = g_part_1->src_y;
5129 door->part_2.x = door->part_1.x;
5130 door->part_2.y = door->part_1.y;
5132 if (door->width != -1)
5134 g_part_1->width = door->width;
5135 g_part_2->width = door->width;
5137 // special treatment for graphics and screen position of right wing
5138 g_part_2->src_x += door_rect->width - door->width;
5139 door->part_2.x += door_rect->width - door->width;
5142 if (door->height != -1)
5144 g_part_1->height = door->height;
5145 g_part_2->height = door->height;
5147 // special treatment for graphics and screen position of bottom wing
5148 g_part_2->src_y += door_rect->height - door->height;
5149 door->part_2.y += door_rect->height - door->height;
5152 // set animation delays for the default wings and panels
5154 door->part_1.step_delay = door->step_delay;
5155 door->part_2.step_delay = door->step_delay;
5156 door->panel.step_delay = door->step_delay;
5158 // set animation draw order for the default wings
5160 door->part_1.sort_priority = 2; // draw left wing over ...
5161 door->part_2.sort_priority = 1; // ... right wing
5163 // set animation draw offset for the default wings
5165 if (door->anim_mode & ANIM_HORIZONTAL)
5167 door->part_1.step_xoffset = door->step_offset;
5168 door->part_1.step_yoffset = 0;
5169 door->part_2.step_xoffset = door->step_offset * -1;
5170 door->part_2.step_yoffset = 0;
5172 num_door_steps = g_part_1->width / door->step_offset;
5174 else // ANIM_VERTICAL
5176 door->part_1.step_xoffset = 0;
5177 door->part_1.step_yoffset = door->step_offset;
5178 door->part_2.step_xoffset = 0;
5179 door->part_2.step_yoffset = door->step_offset * -1;
5181 num_door_steps = g_part_1->height / door->step_offset;
5184 // set animation draw offset for the default panels
5186 if (door->step_offset > 1)
5188 num_panel_steps = 2 * door_rect->height / door->step_offset;
5189 door->panel.start_step = num_panel_steps - num_door_steps;
5190 door->panel.start_step_closing = door->panel.start_step;
5194 num_panel_steps = door_rect->height / door->step_offset;
5195 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5196 door->panel.start_step_closing = door->panel.start_step;
5197 door->panel.step_delay *= 2;
5204 void InitDoors(void)
5208 for (i = 0; door_part_controls[i].door_token != -1; i++)
5210 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5211 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5213 // initialize "start_step_opening" and "start_step_closing", if needed
5214 if (dpc->pos->start_step_opening == 0 &&
5215 dpc->pos->start_step_closing == 0)
5217 // dpc->pos->start_step_opening = dpc->pos->start_step;
5218 dpc->pos->start_step_closing = dpc->pos->start_step;
5221 // fill structure for door part draw order (sorted below)
5223 dpo->sort_priority = dpc->pos->sort_priority;
5226 // sort door part controls according to sort_priority and graphic number
5227 qsort(door_part_order, MAX_DOOR_PARTS,
5228 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5231 unsigned int OpenDoor(unsigned int door_state)
5233 if (door_state & DOOR_COPY_BACK)
5235 if (door_state & DOOR_OPEN_1)
5236 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5237 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5239 if (door_state & DOOR_OPEN_2)
5240 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5241 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5243 door_state &= ~DOOR_COPY_BACK;
5246 return MoveDoor(door_state);
5249 unsigned int CloseDoor(unsigned int door_state)
5251 unsigned int old_door_state = GetDoorState();
5253 if (!(door_state & DOOR_NO_COPY_BACK))
5255 if (old_door_state & DOOR_OPEN_1)
5256 BlitBitmap(backbuffer, bitmap_db_door_1,
5257 DX, DY, DXSIZE, DYSIZE, 0, 0);
5259 if (old_door_state & DOOR_OPEN_2)
5260 BlitBitmap(backbuffer, bitmap_db_door_2,
5261 VX, VY, VXSIZE, VYSIZE, 0, 0);
5263 door_state &= ~DOOR_NO_COPY_BACK;
5266 return MoveDoor(door_state);
5269 unsigned int GetDoorState(void)
5271 return MoveDoor(DOOR_GET_STATE);
5274 unsigned int SetDoorState(unsigned int door_state)
5276 return MoveDoor(door_state | DOOR_SET_STATE);
5279 static int euclid(int a, int b)
5281 return (b ? euclid(b, a % b) : a);
5284 unsigned int MoveDoor(unsigned int door_state)
5286 struct Rect door_rect_list[] =
5288 { DX, DY, DXSIZE, DYSIZE },
5289 { VX, VY, VXSIZE, VYSIZE }
5291 static int door1 = DOOR_CLOSE_1;
5292 static int door2 = DOOR_CLOSE_2;
5293 DelayCounter door_delay = { 0 };
5296 if (door_state == DOOR_GET_STATE)
5297 return (door1 | door2);
5299 if (door_state & DOOR_SET_STATE)
5301 if (door_state & DOOR_ACTION_1)
5302 door1 = door_state & DOOR_ACTION_1;
5303 if (door_state & DOOR_ACTION_2)
5304 door2 = door_state & DOOR_ACTION_2;
5306 return (door1 | door2);
5309 if (!(door_state & DOOR_FORCE_REDRAW))
5311 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5312 door_state &= ~DOOR_OPEN_1;
5313 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5314 door_state &= ~DOOR_CLOSE_1;
5315 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5316 door_state &= ~DOOR_OPEN_2;
5317 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5318 door_state &= ~DOOR_CLOSE_2;
5321 if (global.autoplay_leveldir)
5323 door_state |= DOOR_NO_DELAY;
5324 door_state &= ~DOOR_CLOSE_ALL;
5327 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5328 door_state |= DOOR_NO_DELAY;
5330 if (door_state & DOOR_ACTION)
5332 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5333 boolean door_panel_drawn[NUM_DOORS];
5334 boolean panel_has_doors[NUM_DOORS];
5335 boolean door_part_skip[MAX_DOOR_PARTS];
5336 boolean door_part_done[MAX_DOOR_PARTS];
5337 boolean door_part_done_all;
5338 int num_steps[MAX_DOOR_PARTS];
5339 int max_move_delay = 0; // delay for complete animations of all doors
5340 int max_step_delay = 0; // delay (ms) between two animation frames
5341 int num_move_steps = 0; // number of animation steps for all doors
5342 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5343 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5347 for (i = 0; i < NUM_DOORS; i++)
5348 panel_has_doors[i] = FALSE;
5350 for (i = 0; i < MAX_DOOR_PARTS; i++)
5352 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5353 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5354 int door_token = dpc->door_token;
5356 door_part_done[i] = FALSE;
5357 door_part_skip[i] = (!(door_state & door_token) ||
5361 for (i = 0; i < MAX_DOOR_PARTS; i++)
5363 int nr = door_part_order[i].nr;
5364 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5365 struct DoorPartPosInfo *pos = dpc->pos;
5366 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5367 int door_token = dpc->door_token;
5368 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5369 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5370 int step_xoffset = ABS(pos->step_xoffset);
5371 int step_yoffset = ABS(pos->step_yoffset);
5372 int step_delay = pos->step_delay;
5373 int current_door_state = door_state & door_token;
5374 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5375 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5376 boolean part_opening = (is_panel ? door_closing : door_opening);
5377 int start_step = (part_opening ? pos->start_step_opening :
5378 pos->start_step_closing);
5379 float move_xsize = (step_xoffset ? g->width : 0);
5380 float move_ysize = (step_yoffset ? g->height : 0);
5381 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5382 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5383 int move_steps = (move_xsteps && move_ysteps ?
5384 MIN(move_xsteps, move_ysteps) :
5385 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5386 int move_delay = move_steps * step_delay;
5388 if (door_part_skip[nr])
5391 max_move_delay = MAX(max_move_delay, move_delay);
5392 max_step_delay = (max_step_delay == 0 ? step_delay :
5393 euclid(max_step_delay, step_delay));
5394 num_steps[nr] = move_steps;
5398 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5400 panel_has_doors[door_index] = TRUE;
5404 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5406 num_move_steps = max_move_delay / max_step_delay;
5407 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5409 door_delay.value = max_step_delay;
5411 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5413 start = num_move_steps - 1;
5417 // opening door sound has priority over simultaneously closing door
5418 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5420 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5422 if (door_state & DOOR_OPEN_1)
5423 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5424 if (door_state & DOOR_OPEN_2)
5425 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5427 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5429 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5431 if (door_state & DOOR_CLOSE_1)
5432 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5433 if (door_state & DOOR_CLOSE_2)
5434 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5438 for (k = start; k < num_move_steps; k++)
5440 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5442 door_part_done_all = TRUE;
5444 for (i = 0; i < NUM_DOORS; i++)
5445 door_panel_drawn[i] = FALSE;
5447 for (i = 0; i < MAX_DOOR_PARTS; i++)
5449 int nr = door_part_order[i].nr;
5450 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5451 struct DoorPartPosInfo *pos = dpc->pos;
5452 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5453 int door_token = dpc->door_token;
5454 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5455 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5456 boolean is_panel_and_door_has_closed = FALSE;
5457 struct Rect *door_rect = &door_rect_list[door_index];
5458 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5460 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5461 int current_door_state = door_state & door_token;
5462 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5463 boolean door_closing = !door_opening;
5464 boolean part_opening = (is_panel ? door_closing : door_opening);
5465 boolean part_closing = !part_opening;
5466 int start_step = (part_opening ? pos->start_step_opening :
5467 pos->start_step_closing);
5468 int step_delay = pos->step_delay;
5469 int step_factor = step_delay / max_step_delay;
5470 int k1 = (step_factor ? k / step_factor + 1 : k);
5471 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5472 int kk = MAX(0, k2);
5475 int src_x, src_y, src_xx, src_yy;
5476 int dst_x, dst_y, dst_xx, dst_yy;
5479 if (door_part_skip[nr])
5482 if (!(door_state & door_token))
5490 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5491 int kk_door = MAX(0, k2_door);
5492 int sync_frame = kk_door * door_delay.value;
5493 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5495 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5496 &g_src_x, &g_src_y);
5501 if (!door_panel_drawn[door_index])
5503 ClearRectangle(drawto, door_rect->x, door_rect->y,
5504 door_rect->width, door_rect->height);
5506 door_panel_drawn[door_index] = TRUE;
5509 // draw opening or closing door parts
5511 if (pos->step_xoffset < 0) // door part on right side
5514 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5517 if (dst_xx + width > door_rect->width)
5518 width = door_rect->width - dst_xx;
5520 else // door part on left side
5523 dst_xx = pos->x - kk * pos->step_xoffset;
5527 src_xx = ABS(dst_xx);
5531 width = g->width - src_xx;
5533 if (width > door_rect->width)
5534 width = door_rect->width;
5536 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5539 if (pos->step_yoffset < 0) // door part on bottom side
5542 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5545 if (dst_yy + height > door_rect->height)
5546 height = door_rect->height - dst_yy;
5548 else // door part on top side
5551 dst_yy = pos->y - kk * pos->step_yoffset;
5555 src_yy = ABS(dst_yy);
5559 height = g->height - src_yy;
5562 src_x = g_src_x + src_xx;
5563 src_y = g_src_y + src_yy;
5565 dst_x = door_rect->x + dst_xx;
5566 dst_y = door_rect->y + dst_yy;
5568 is_panel_and_door_has_closed =
5571 panel_has_doors[door_index] &&
5572 k >= num_move_steps_doors_only - 1);
5574 if (width >= 0 && width <= g->width &&
5575 height >= 0 && height <= g->height &&
5576 !is_panel_and_door_has_closed)
5578 if (is_panel || !pos->draw_masked)
5579 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5582 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5586 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5588 if ((part_opening && (width < 0 || height < 0)) ||
5589 (part_closing && (width >= g->width && height >= g->height)))
5590 door_part_done[nr] = TRUE;
5592 // continue door part animations, but not panel after door has closed
5593 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5594 door_part_done_all = FALSE;
5597 if (!(door_state & DOOR_NO_DELAY))
5600 HandleGameActions();
5604 SkipUntilDelayReached(&door_delay, &k, last_frame);
5606 // prevent OS (Windows) from complaining about program not responding
5610 if (door_part_done_all)
5614 if (!(door_state & DOOR_NO_DELAY))
5616 // wait for specified door action post delay
5617 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5618 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5619 else if (door_state & DOOR_ACTION_1)
5620 door_delay.value = door_1.post_delay;
5621 else if (door_state & DOOR_ACTION_2)
5622 door_delay.value = door_2.post_delay;
5624 while (!DelayReached(&door_delay))
5627 HandleGameActions();
5634 if (door_state & DOOR_ACTION_1)
5635 door1 = door_state & DOOR_ACTION_1;
5636 if (door_state & DOOR_ACTION_2)
5637 door2 = door_state & DOOR_ACTION_2;
5639 // draw masked border over door area
5640 DrawMaskedBorder(REDRAW_DOOR_1);
5641 DrawMaskedBorder(REDRAW_DOOR_2);
5643 ClearAutoRepeatKeyEvents();
5645 return (door1 | door2);
5648 static boolean useSpecialEditorDoor(void)
5650 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5651 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5653 // do not draw special editor door if editor border defined or redefined
5654 if (graphic_info[graphic].bitmap != NULL || redefined)
5657 // do not draw special editor door if global border defined to be empty
5658 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5661 // do not draw special editor door if viewport definitions do not match
5665 EY + EYSIZE != VY + VYSIZE)
5671 void DrawSpecialEditorDoor(void)
5673 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5674 int top_border_width = gfx1->width;
5675 int top_border_height = gfx1->height;
5676 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5677 int ex = EX - outer_border;
5678 int ey = EY - outer_border;
5679 int vy = VY - outer_border;
5680 int exsize = EXSIZE + 2 * outer_border;
5682 if (!useSpecialEditorDoor())
5685 // draw bigger level editor toolbox window
5686 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5687 top_border_width, top_border_height, ex, ey - top_border_height);
5688 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5689 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5691 redraw_mask |= REDRAW_ALL;
5694 void UndrawSpecialEditorDoor(void)
5696 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5697 int top_border_width = gfx1->width;
5698 int top_border_height = gfx1->height;
5699 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5700 int ex = EX - outer_border;
5701 int ey = EY - outer_border;
5702 int ey_top = ey - top_border_height;
5703 int exsize = EXSIZE + 2 * outer_border;
5704 int eysize = EYSIZE + 2 * outer_border;
5706 if (!useSpecialEditorDoor())
5709 // draw normal tape recorder window
5710 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5712 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5713 ex, ey_top, top_border_width, top_border_height,
5715 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5716 ex, ey, exsize, eysize, ex, ey);
5720 // if screen background is set to "[NONE]", clear editor toolbox window
5721 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5722 ClearRectangle(drawto, ex, ey, exsize, eysize);
5725 redraw_mask |= REDRAW_ALL;
5729 // ---------- new tool button stuff -------------------------------------------
5734 struct TextPosInfo *pos;
5736 boolean is_touch_button;
5738 } toolbutton_info[NUM_TOOL_BUTTONS] =
5741 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5742 TOOL_CTRL_ID_YES, FALSE, "yes"
5745 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5746 TOOL_CTRL_ID_NO, FALSE, "no"
5749 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5750 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5753 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5754 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5757 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5758 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5761 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5762 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5765 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5766 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5769 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5770 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5773 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5774 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5777 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5778 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5782 void CreateToolButtons(void)
5786 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5788 int graphic = toolbutton_info[i].graphic;
5789 struct GraphicInfo *gfx = &graphic_info[graphic];
5790 struct TextPosInfo *pos = toolbutton_info[i].pos;
5791 struct GadgetInfo *gi;
5792 Bitmap *deco_bitmap = None;
5793 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5794 unsigned int event_mask = GD_EVENT_RELEASED;
5795 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5796 int base_x = (is_touch_button ? 0 : DX);
5797 int base_y = (is_touch_button ? 0 : DY);
5798 int gd_x = gfx->src_x;
5799 int gd_y = gfx->src_y;
5800 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5801 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5806 // do not use touch buttons if overlay touch buttons are disabled
5807 if (is_touch_button && !setup.touch.overlay_buttons)
5810 if (global.use_envelope_request && !is_touch_button)
5812 setRequestPosition(&base_x, &base_y, TRUE);
5814 // check if request buttons are outside of envelope and fix, if needed
5815 if (x < 0 || x + gfx->width > request.width ||
5816 y < 0 || y + gfx->height > request.height)
5818 if (id == TOOL_CTRL_ID_YES)
5821 y = request.height - 2 * request.border_size - gfx->height;
5823 else if (id == TOOL_CTRL_ID_NO)
5825 x = request.width - 2 * request.border_size - gfx->width;
5826 y = request.height - 2 * request.border_size - gfx->height;
5828 else if (id == TOOL_CTRL_ID_CONFIRM)
5830 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5831 y = request.height - 2 * request.border_size - gfx->height;
5833 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5835 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5837 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5838 y = request.height - 2 * request.border_size - gfx->height * 2;
5840 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5841 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5846 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5849 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5851 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5852 pos->size, &deco_bitmap, &deco_x, &deco_y);
5853 deco_xpos = (gfx->width - pos->size) / 2;
5854 deco_ypos = (gfx->height - pos->size) / 2;
5857 gi = CreateGadget(GDI_CUSTOM_ID, id,
5858 GDI_IMAGE_ID, graphic,
5859 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5862 GDI_WIDTH, gfx->width,
5863 GDI_HEIGHT, gfx->height,
5864 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5865 GDI_STATE, GD_BUTTON_UNPRESSED,
5866 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5867 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5868 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5869 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5870 GDI_DECORATION_SIZE, pos->size, pos->size,
5871 GDI_DECORATION_SHIFTING, 1, 1,
5872 GDI_DIRECT_DRAW, FALSE,
5873 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5874 GDI_EVENT_MASK, event_mask,
5875 GDI_CALLBACK_ACTION, HandleToolButtons,
5879 Fail("cannot create gadget");
5881 tool_gadget[id] = gi;
5885 void FreeToolButtons(void)
5889 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5890 FreeGadget(tool_gadget[i]);
5893 static void MapToolButtons(unsigned int req_state)
5895 if (req_state & REQ_ASK)
5897 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5898 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5899 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5900 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5902 else if (req_state & REQ_CONFIRM)
5904 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5905 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5907 else if (req_state & REQ_PLAYER)
5909 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5910 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5911 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5912 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5916 static void UnmapToolButtons(void)
5920 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5921 UnmapGadget(tool_gadget[i]);
5924 static void HandleToolButtons(struct GadgetInfo *gi)
5926 request_gadget_id = gi->custom_id;
5929 static struct Mapping_EM_to_RND_object
5932 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5933 boolean is_backside; // backside of moving element
5939 em_object_mapping_list[GAME_TILE_MAX + 1] =
5942 Zborder, FALSE, FALSE,
5946 Zplayer, FALSE, FALSE,
5955 Ztank, FALSE, FALSE,
5959 Zeater, FALSE, FALSE,
5963 Zdynamite, FALSE, FALSE,
5967 Zboom, FALSE, FALSE,
5972 Xchain, FALSE, FALSE,
5973 EL_DEFAULT, ACTION_EXPLODING, -1
5976 Xboom_bug, FALSE, FALSE,
5977 EL_BUG, ACTION_EXPLODING, -1
5980 Xboom_tank, FALSE, FALSE,
5981 EL_SPACESHIP, ACTION_EXPLODING, -1
5984 Xboom_android, FALSE, FALSE,
5985 EL_EMC_ANDROID, ACTION_OTHER, -1
5988 Xboom_1, FALSE, FALSE,
5989 EL_DEFAULT, ACTION_EXPLODING, -1
5992 Xboom_2, FALSE, FALSE,
5993 EL_DEFAULT, ACTION_EXPLODING, -1
5997 Xblank, TRUE, FALSE,
6002 Xsplash_e, FALSE, FALSE,
6003 EL_ACID_SPLASH_RIGHT, -1, -1
6006 Xsplash_w, FALSE, FALSE,
6007 EL_ACID_SPLASH_LEFT, -1, -1
6011 Xplant, TRUE, FALSE,
6012 EL_EMC_PLANT, -1, -1
6015 Yplant, FALSE, FALSE,
6016 EL_EMC_PLANT, -1, -1
6020 Xacid_1, TRUE, FALSE,
6024 Xacid_2, FALSE, FALSE,
6028 Xacid_3, FALSE, FALSE,
6032 Xacid_4, FALSE, FALSE,
6036 Xacid_5, FALSE, FALSE,
6040 Xacid_6, FALSE, FALSE,
6044 Xacid_7, FALSE, FALSE,
6048 Xacid_8, FALSE, FALSE,
6053 Xfake_acid_1, TRUE, FALSE,
6054 EL_EMC_FAKE_ACID, -1, -1
6057 Xfake_acid_2, FALSE, FALSE,
6058 EL_EMC_FAKE_ACID, -1, -1
6061 Xfake_acid_3, FALSE, FALSE,
6062 EL_EMC_FAKE_ACID, -1, -1
6065 Xfake_acid_4, FALSE, FALSE,
6066 EL_EMC_FAKE_ACID, -1, -1
6069 Xfake_acid_5, FALSE, FALSE,
6070 EL_EMC_FAKE_ACID, -1, -1
6073 Xfake_acid_6, FALSE, FALSE,
6074 EL_EMC_FAKE_ACID, -1, -1
6077 Xfake_acid_7, FALSE, FALSE,
6078 EL_EMC_FAKE_ACID, -1, -1
6081 Xfake_acid_8, FALSE, FALSE,
6082 EL_EMC_FAKE_ACID, -1, -1
6086 Xfake_acid_1_player, FALSE, FALSE,
6087 EL_EMC_FAKE_ACID, -1, -1
6090 Xfake_acid_2_player, FALSE, FALSE,
6091 EL_EMC_FAKE_ACID, -1, -1
6094 Xfake_acid_3_player, FALSE, FALSE,
6095 EL_EMC_FAKE_ACID, -1, -1
6098 Xfake_acid_4_player, FALSE, FALSE,
6099 EL_EMC_FAKE_ACID, -1, -1
6102 Xfake_acid_5_player, FALSE, FALSE,
6103 EL_EMC_FAKE_ACID, -1, -1
6106 Xfake_acid_6_player, FALSE, FALSE,
6107 EL_EMC_FAKE_ACID, -1, -1
6110 Xfake_acid_7_player, FALSE, FALSE,
6111 EL_EMC_FAKE_ACID, -1, -1
6114 Xfake_acid_8_player, FALSE, FALSE,
6115 EL_EMC_FAKE_ACID, -1, -1
6119 Xgrass, TRUE, FALSE,
6120 EL_EMC_GRASS, -1, -1
6123 Ygrass_nB, FALSE, FALSE,
6124 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6127 Ygrass_eB, FALSE, FALSE,
6128 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6131 Ygrass_sB, FALSE, FALSE,
6132 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6135 Ygrass_wB, FALSE, FALSE,
6136 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6144 Ydirt_nB, FALSE, FALSE,
6145 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6148 Ydirt_eB, FALSE, FALSE,
6149 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6152 Ydirt_sB, FALSE, FALSE,
6153 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6156 Ydirt_wB, FALSE, FALSE,
6157 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6161 Xandroid, TRUE, FALSE,
6162 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6165 Xandroid_1_n, FALSE, FALSE,
6166 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6169 Xandroid_2_n, FALSE, FALSE,
6170 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6173 Xandroid_1_e, FALSE, FALSE,
6174 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6177 Xandroid_2_e, FALSE, FALSE,
6178 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6181 Xandroid_1_w, FALSE, FALSE,
6182 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6185 Xandroid_2_w, FALSE, FALSE,
6186 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6189 Xandroid_1_s, FALSE, FALSE,
6190 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6193 Xandroid_2_s, FALSE, FALSE,
6194 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6197 Yandroid_n, FALSE, FALSE,
6198 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6201 Yandroid_nB, FALSE, TRUE,
6202 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6205 Yandroid_ne, FALSE, FALSE,
6206 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6209 Yandroid_neB, FALSE, TRUE,
6210 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6213 Yandroid_e, FALSE, FALSE,
6214 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6217 Yandroid_eB, FALSE, TRUE,
6218 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6221 Yandroid_se, FALSE, FALSE,
6222 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6225 Yandroid_seB, FALSE, TRUE,
6226 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6229 Yandroid_s, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6233 Yandroid_sB, FALSE, TRUE,
6234 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6237 Yandroid_sw, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6241 Yandroid_swB, FALSE, TRUE,
6242 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6245 Yandroid_w, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6249 Yandroid_wB, FALSE, TRUE,
6250 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6253 Yandroid_nw, FALSE, FALSE,
6254 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6257 Yandroid_nwB, FALSE, TRUE,
6258 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6262 Xeater_n, TRUE, FALSE,
6263 EL_YAMYAM_UP, -1, -1
6266 Xeater_e, TRUE, FALSE,
6267 EL_YAMYAM_RIGHT, -1, -1
6270 Xeater_w, TRUE, FALSE,
6271 EL_YAMYAM_LEFT, -1, -1
6274 Xeater_s, TRUE, FALSE,
6275 EL_YAMYAM_DOWN, -1, -1
6278 Yeater_n, FALSE, FALSE,
6279 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6282 Yeater_nB, FALSE, TRUE,
6283 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6286 Yeater_e, FALSE, FALSE,
6287 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6290 Yeater_eB, FALSE, TRUE,
6291 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6294 Yeater_s, FALSE, FALSE,
6295 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6298 Yeater_sB, FALSE, TRUE,
6299 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6302 Yeater_w, FALSE, FALSE,
6303 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6306 Yeater_wB, FALSE, TRUE,
6307 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6310 Yeater_stone, FALSE, FALSE,
6311 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6314 Yeater_spring, FALSE, FALSE,
6315 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6319 Xalien, TRUE, FALSE,
6323 Xalien_pause, FALSE, FALSE,
6327 Yalien_n, FALSE, FALSE,
6328 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6331 Yalien_nB, FALSE, TRUE,
6332 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6335 Yalien_e, FALSE, FALSE,
6336 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6339 Yalien_eB, FALSE, TRUE,
6340 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6343 Yalien_s, FALSE, FALSE,
6344 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6347 Yalien_sB, FALSE, TRUE,
6348 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6351 Yalien_w, FALSE, FALSE,
6352 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6355 Yalien_wB, FALSE, TRUE,
6356 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6359 Yalien_stone, FALSE, FALSE,
6360 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6363 Yalien_spring, FALSE, FALSE,
6364 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6368 Xbug_1_n, TRUE, FALSE,
6372 Xbug_1_e, TRUE, FALSE,
6373 EL_BUG_RIGHT, -1, -1
6376 Xbug_1_s, TRUE, FALSE,
6380 Xbug_1_w, TRUE, FALSE,
6384 Xbug_2_n, FALSE, FALSE,
6388 Xbug_2_e, FALSE, FALSE,
6389 EL_BUG_RIGHT, -1, -1
6392 Xbug_2_s, FALSE, FALSE,
6396 Xbug_2_w, FALSE, FALSE,
6400 Ybug_n, FALSE, FALSE,
6401 EL_BUG, ACTION_MOVING, MV_BIT_UP
6404 Ybug_nB, FALSE, TRUE,
6405 EL_BUG, ACTION_MOVING, MV_BIT_UP
6408 Ybug_e, FALSE, FALSE,
6409 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6412 Ybug_eB, FALSE, TRUE,
6413 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6416 Ybug_s, FALSE, FALSE,
6417 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6420 Ybug_sB, FALSE, TRUE,
6421 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6424 Ybug_w, FALSE, FALSE,
6425 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6428 Ybug_wB, FALSE, TRUE,
6429 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6432 Ybug_w_n, FALSE, FALSE,
6433 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6436 Ybug_n_e, FALSE, FALSE,
6437 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6440 Ybug_e_s, FALSE, FALSE,
6441 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6444 Ybug_s_w, FALSE, FALSE,
6445 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6448 Ybug_e_n, FALSE, FALSE,
6449 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6452 Ybug_s_e, FALSE, FALSE,
6453 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6456 Ybug_w_s, FALSE, FALSE,
6457 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6460 Ybug_n_w, FALSE, FALSE,
6461 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6464 Ybug_stone, FALSE, FALSE,
6465 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6468 Ybug_spring, FALSE, FALSE,
6469 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6473 Xtank_1_n, TRUE, FALSE,
6474 EL_SPACESHIP_UP, -1, -1
6477 Xtank_1_e, TRUE, FALSE,
6478 EL_SPACESHIP_RIGHT, -1, -1
6481 Xtank_1_s, TRUE, FALSE,
6482 EL_SPACESHIP_DOWN, -1, -1
6485 Xtank_1_w, TRUE, FALSE,
6486 EL_SPACESHIP_LEFT, -1, -1
6489 Xtank_2_n, FALSE, FALSE,
6490 EL_SPACESHIP_UP, -1, -1
6493 Xtank_2_e, FALSE, FALSE,
6494 EL_SPACESHIP_RIGHT, -1, -1
6497 Xtank_2_s, FALSE, FALSE,
6498 EL_SPACESHIP_DOWN, -1, -1
6501 Xtank_2_w, FALSE, FALSE,
6502 EL_SPACESHIP_LEFT, -1, -1
6505 Ytank_n, FALSE, FALSE,
6506 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6509 Ytank_nB, FALSE, TRUE,
6510 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6513 Ytank_e, FALSE, FALSE,
6514 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6517 Ytank_eB, FALSE, TRUE,
6518 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6521 Ytank_s, FALSE, FALSE,
6522 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6525 Ytank_sB, FALSE, TRUE,
6526 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6529 Ytank_w, FALSE, FALSE,
6530 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6533 Ytank_wB, FALSE, TRUE,
6534 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6537 Ytank_w_n, FALSE, FALSE,
6538 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6541 Ytank_n_e, FALSE, FALSE,
6542 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6545 Ytank_e_s, FALSE, FALSE,
6546 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6549 Ytank_s_w, FALSE, FALSE,
6550 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6553 Ytank_e_n, FALSE, FALSE,
6554 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6557 Ytank_s_e, FALSE, FALSE,
6558 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6561 Ytank_w_s, FALSE, FALSE,
6562 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6565 Ytank_n_w, FALSE, FALSE,
6566 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6569 Ytank_stone, FALSE, FALSE,
6570 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6573 Ytank_spring, FALSE, FALSE,
6574 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6578 Xemerald, TRUE, FALSE,
6582 Xemerald_pause, FALSE, FALSE,
6586 Xemerald_fall, FALSE, FALSE,
6590 Xemerald_shine, FALSE, FALSE,
6591 EL_EMERALD, ACTION_TWINKLING, -1
6594 Yemerald_s, FALSE, FALSE,
6595 EL_EMERALD, ACTION_FALLING, -1
6598 Yemerald_sB, FALSE, TRUE,
6599 EL_EMERALD, ACTION_FALLING, -1
6602 Yemerald_e, FALSE, FALSE,
6603 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6606 Yemerald_eB, FALSE, TRUE,
6607 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6610 Yemerald_w, FALSE, FALSE,
6611 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6614 Yemerald_wB, FALSE, TRUE,
6615 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6618 Yemerald_blank, FALSE, FALSE,
6619 EL_EMERALD, ACTION_COLLECTING, -1
6623 Xdiamond, TRUE, FALSE,
6627 Xdiamond_pause, FALSE, FALSE,
6631 Xdiamond_fall, FALSE, FALSE,
6635 Xdiamond_shine, FALSE, FALSE,
6636 EL_DIAMOND, ACTION_TWINKLING, -1
6639 Ydiamond_s, FALSE, FALSE,
6640 EL_DIAMOND, ACTION_FALLING, -1
6643 Ydiamond_sB, FALSE, TRUE,
6644 EL_DIAMOND, ACTION_FALLING, -1
6647 Ydiamond_e, FALSE, FALSE,
6648 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6651 Ydiamond_eB, FALSE, TRUE,
6652 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6655 Ydiamond_w, FALSE, FALSE,
6656 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6659 Ydiamond_wB, FALSE, TRUE,
6660 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6663 Ydiamond_blank, FALSE, FALSE,
6664 EL_DIAMOND, ACTION_COLLECTING, -1
6667 Ydiamond_stone, FALSE, FALSE,
6668 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6672 Xstone, TRUE, FALSE,
6676 Xstone_pause, FALSE, FALSE,
6680 Xstone_fall, FALSE, FALSE,
6684 Ystone_s, FALSE, FALSE,
6685 EL_ROCK, ACTION_FALLING, -1
6688 Ystone_sB, FALSE, TRUE,
6689 EL_ROCK, ACTION_FALLING, -1
6692 Ystone_e, FALSE, FALSE,
6693 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6696 Ystone_eB, FALSE, TRUE,
6697 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6700 Ystone_w, FALSE, FALSE,
6701 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6704 Ystone_wB, FALSE, TRUE,
6705 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6713 Xbomb_pause, FALSE, FALSE,
6717 Xbomb_fall, FALSE, FALSE,
6721 Ybomb_s, FALSE, FALSE,
6722 EL_BOMB, ACTION_FALLING, -1
6725 Ybomb_sB, FALSE, TRUE,
6726 EL_BOMB, ACTION_FALLING, -1
6729 Ybomb_e, FALSE, FALSE,
6730 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6733 Ybomb_eB, FALSE, TRUE,
6734 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6737 Ybomb_w, FALSE, FALSE,
6738 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6741 Ybomb_wB, FALSE, TRUE,
6742 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6745 Ybomb_blank, FALSE, FALSE,
6746 EL_BOMB, ACTION_ACTIVATING, -1
6754 Xnut_pause, FALSE, FALSE,
6758 Xnut_fall, FALSE, FALSE,
6762 Ynut_s, FALSE, FALSE,
6763 EL_NUT, ACTION_FALLING, -1
6766 Ynut_sB, FALSE, TRUE,
6767 EL_NUT, ACTION_FALLING, -1
6770 Ynut_e, FALSE, FALSE,
6771 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6774 Ynut_eB, FALSE, TRUE,
6775 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6778 Ynut_w, FALSE, FALSE,
6779 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6782 Ynut_wB, FALSE, TRUE,
6783 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6786 Ynut_stone, FALSE, FALSE,
6787 EL_NUT, ACTION_BREAKING, -1
6791 Xspring, TRUE, FALSE,
6795 Xspring_pause, FALSE, FALSE,
6799 Xspring_e, TRUE, FALSE,
6800 EL_SPRING_RIGHT, -1, -1
6803 Xspring_w, TRUE, FALSE,
6804 EL_SPRING_LEFT, -1, -1
6807 Xspring_fall, FALSE, FALSE,
6811 Yspring_s, FALSE, FALSE,
6812 EL_SPRING, ACTION_FALLING, -1
6815 Yspring_sB, FALSE, TRUE,
6816 EL_SPRING, ACTION_FALLING, -1
6819 Yspring_e, FALSE, FALSE,
6820 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6823 Yspring_eB, FALSE, TRUE,
6824 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6827 Yspring_w, FALSE, FALSE,
6828 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6831 Yspring_wB, FALSE, TRUE,
6832 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6835 Yspring_alien_e, FALSE, FALSE,
6836 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6839 Yspring_alien_eB, FALSE, TRUE,
6840 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6843 Yspring_alien_w, FALSE, FALSE,
6844 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6847 Yspring_alien_wB, FALSE, TRUE,
6848 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6852 Xpush_emerald_e, FALSE, FALSE,
6853 EL_EMERALD, -1, MV_BIT_RIGHT
6856 Xpush_emerald_w, FALSE, FALSE,
6857 EL_EMERALD, -1, MV_BIT_LEFT
6860 Xpush_diamond_e, FALSE, FALSE,
6861 EL_DIAMOND, -1, MV_BIT_RIGHT
6864 Xpush_diamond_w, FALSE, FALSE,
6865 EL_DIAMOND, -1, MV_BIT_LEFT
6868 Xpush_stone_e, FALSE, FALSE,
6869 EL_ROCK, -1, MV_BIT_RIGHT
6872 Xpush_stone_w, FALSE, FALSE,
6873 EL_ROCK, -1, MV_BIT_LEFT
6876 Xpush_bomb_e, FALSE, FALSE,
6877 EL_BOMB, -1, MV_BIT_RIGHT
6880 Xpush_bomb_w, FALSE, FALSE,
6881 EL_BOMB, -1, MV_BIT_LEFT
6884 Xpush_nut_e, FALSE, FALSE,
6885 EL_NUT, -1, MV_BIT_RIGHT
6888 Xpush_nut_w, FALSE, FALSE,
6889 EL_NUT, -1, MV_BIT_LEFT
6892 Xpush_spring_e, FALSE, FALSE,
6893 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6896 Xpush_spring_w, FALSE, FALSE,
6897 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6901 Xdynamite, TRUE, FALSE,
6902 EL_EM_DYNAMITE, -1, -1
6905 Ydynamite_blank, FALSE, FALSE,
6906 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6909 Xdynamite_1, TRUE, FALSE,
6910 EL_EM_DYNAMITE_ACTIVE, -1, -1
6913 Xdynamite_2, FALSE, FALSE,
6914 EL_EM_DYNAMITE_ACTIVE, -1, -1
6917 Xdynamite_3, FALSE, FALSE,
6918 EL_EM_DYNAMITE_ACTIVE, -1, -1
6921 Xdynamite_4, FALSE, FALSE,
6922 EL_EM_DYNAMITE_ACTIVE, -1, -1
6926 Xkey_1, TRUE, FALSE,
6930 Xkey_2, TRUE, FALSE,
6934 Xkey_3, TRUE, FALSE,
6938 Xkey_4, TRUE, FALSE,
6942 Xkey_5, TRUE, FALSE,
6943 EL_EMC_KEY_5, -1, -1
6946 Xkey_6, TRUE, FALSE,
6947 EL_EMC_KEY_6, -1, -1
6950 Xkey_7, TRUE, FALSE,
6951 EL_EMC_KEY_7, -1, -1
6954 Xkey_8, TRUE, FALSE,
6955 EL_EMC_KEY_8, -1, -1
6959 Xdoor_1, TRUE, FALSE,
6960 EL_EM_GATE_1, -1, -1
6963 Xdoor_2, TRUE, FALSE,
6964 EL_EM_GATE_2, -1, -1
6967 Xdoor_3, TRUE, FALSE,
6968 EL_EM_GATE_3, -1, -1
6971 Xdoor_4, TRUE, FALSE,
6972 EL_EM_GATE_4, -1, -1
6975 Xdoor_5, TRUE, FALSE,
6976 EL_EMC_GATE_5, -1, -1
6979 Xdoor_6, TRUE, FALSE,
6980 EL_EMC_GATE_6, -1, -1
6983 Xdoor_7, TRUE, FALSE,
6984 EL_EMC_GATE_7, -1, -1
6987 Xdoor_8, TRUE, FALSE,
6988 EL_EMC_GATE_8, -1, -1
6992 Xfake_door_1, TRUE, FALSE,
6993 EL_EM_GATE_1_GRAY, -1, -1
6996 Xfake_door_2, TRUE, FALSE,
6997 EL_EM_GATE_2_GRAY, -1, -1
7000 Xfake_door_3, TRUE, FALSE,
7001 EL_EM_GATE_3_GRAY, -1, -1
7004 Xfake_door_4, TRUE, FALSE,
7005 EL_EM_GATE_4_GRAY, -1, -1
7008 Xfake_door_5, TRUE, FALSE,
7009 EL_EMC_GATE_5_GRAY, -1, -1
7012 Xfake_door_6, TRUE, FALSE,
7013 EL_EMC_GATE_6_GRAY, -1, -1
7016 Xfake_door_7, TRUE, FALSE,
7017 EL_EMC_GATE_7_GRAY, -1, -1
7020 Xfake_door_8, TRUE, FALSE,
7021 EL_EMC_GATE_8_GRAY, -1, -1
7025 Xballoon, TRUE, FALSE,
7029 Yballoon_n, FALSE, FALSE,
7030 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7033 Yballoon_nB, FALSE, TRUE,
7034 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7037 Yballoon_e, FALSE, FALSE,
7038 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7041 Yballoon_eB, FALSE, TRUE,
7042 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7045 Yballoon_s, FALSE, FALSE,
7046 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7049 Yballoon_sB, FALSE, TRUE,
7050 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7053 Yballoon_w, FALSE, FALSE,
7054 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7057 Yballoon_wB, FALSE, TRUE,
7058 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7062 Xball_1, TRUE, FALSE,
7063 EL_EMC_MAGIC_BALL, -1, -1
7066 Yball_1, FALSE, FALSE,
7067 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7070 Xball_2, FALSE, FALSE,
7071 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7074 Yball_2, FALSE, FALSE,
7075 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7078 Yball_blank, FALSE, FALSE,
7079 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7083 Xamoeba_1, TRUE, FALSE,
7084 EL_AMOEBA_DRY, ACTION_OTHER, -1
7087 Xamoeba_2, FALSE, FALSE,
7088 EL_AMOEBA_DRY, ACTION_OTHER, -1
7091 Xamoeba_3, FALSE, FALSE,
7092 EL_AMOEBA_DRY, ACTION_OTHER, -1
7095 Xamoeba_4, FALSE, FALSE,
7096 EL_AMOEBA_DRY, ACTION_OTHER, -1
7099 Xamoeba_5, TRUE, FALSE,
7100 EL_AMOEBA_WET, ACTION_OTHER, -1
7103 Xamoeba_6, FALSE, FALSE,
7104 EL_AMOEBA_WET, ACTION_OTHER, -1
7107 Xamoeba_7, FALSE, FALSE,
7108 EL_AMOEBA_WET, ACTION_OTHER, -1
7111 Xamoeba_8, FALSE, FALSE,
7112 EL_AMOEBA_WET, ACTION_OTHER, -1
7117 EL_AMOEBA_DROP, ACTION_GROWING, -1
7120 Xdrip_fall, FALSE, FALSE,
7121 EL_AMOEBA_DROP, -1, -1
7124 Xdrip_stretch, FALSE, FALSE,
7125 EL_AMOEBA_DROP, ACTION_FALLING, -1
7128 Xdrip_stretchB, FALSE, TRUE,
7129 EL_AMOEBA_DROP, ACTION_FALLING, -1
7132 Ydrip_1_s, FALSE, FALSE,
7133 EL_AMOEBA_DROP, ACTION_FALLING, -1
7136 Ydrip_1_sB, FALSE, TRUE,
7137 EL_AMOEBA_DROP, ACTION_FALLING, -1
7140 Ydrip_2_s, FALSE, FALSE,
7141 EL_AMOEBA_DROP, ACTION_FALLING, -1
7144 Ydrip_2_sB, FALSE, TRUE,
7145 EL_AMOEBA_DROP, ACTION_FALLING, -1
7149 Xwonderwall, TRUE, FALSE,
7150 EL_MAGIC_WALL, -1, -1
7153 Ywonderwall, FALSE, FALSE,
7154 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7158 Xwheel, TRUE, FALSE,
7159 EL_ROBOT_WHEEL, -1, -1
7162 Ywheel, FALSE, FALSE,
7163 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7167 Xswitch, TRUE, FALSE,
7168 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7171 Yswitch, FALSE, FALSE,
7172 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7176 Xbumper, TRUE, FALSE,
7177 EL_EMC_SPRING_BUMPER, -1, -1
7180 Ybumper, FALSE, FALSE,
7181 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7185 Xacid_nw, TRUE, FALSE,
7186 EL_ACID_POOL_TOPLEFT, -1, -1
7189 Xacid_ne, TRUE, FALSE,
7190 EL_ACID_POOL_TOPRIGHT, -1, -1
7193 Xacid_sw, TRUE, FALSE,
7194 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7197 Xacid_s, TRUE, FALSE,
7198 EL_ACID_POOL_BOTTOM, -1, -1
7201 Xacid_se, TRUE, FALSE,
7202 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7206 Xfake_blank, TRUE, FALSE,
7207 EL_INVISIBLE_WALL, -1, -1
7210 Yfake_blank, FALSE, FALSE,
7211 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7215 Xfake_grass, TRUE, FALSE,
7216 EL_EMC_FAKE_GRASS, -1, -1
7219 Yfake_grass, FALSE, FALSE,
7220 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7224 Xfake_amoeba, TRUE, FALSE,
7225 EL_EMC_DRIPPER, -1, -1
7228 Yfake_amoeba, FALSE, FALSE,
7229 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7233 Xlenses, TRUE, FALSE,
7234 EL_EMC_LENSES, -1, -1
7238 Xmagnify, TRUE, FALSE,
7239 EL_EMC_MAGNIFIER, -1, -1
7244 EL_QUICKSAND_EMPTY, -1, -1
7247 Xsand_stone, TRUE, FALSE,
7248 EL_QUICKSAND_FULL, -1, -1
7251 Xsand_stonein_1, FALSE, TRUE,
7252 EL_ROCK, ACTION_FILLING, -1
7255 Xsand_stonein_2, FALSE, TRUE,
7256 EL_ROCK, ACTION_FILLING, -1
7259 Xsand_stonein_3, FALSE, TRUE,
7260 EL_ROCK, ACTION_FILLING, -1
7263 Xsand_stonein_4, FALSE, TRUE,
7264 EL_ROCK, ACTION_FILLING, -1
7267 Xsand_sandstone_1, FALSE, FALSE,
7268 EL_QUICKSAND_FILLING, -1, -1
7271 Xsand_sandstone_2, FALSE, FALSE,
7272 EL_QUICKSAND_FILLING, -1, -1
7275 Xsand_sandstone_3, FALSE, FALSE,
7276 EL_QUICKSAND_FILLING, -1, -1
7279 Xsand_sandstone_4, FALSE, FALSE,
7280 EL_QUICKSAND_FILLING, -1, -1
7283 Xsand_stonesand_1, FALSE, FALSE,
7284 EL_QUICKSAND_EMPTYING, -1, -1
7287 Xsand_stonesand_2, FALSE, FALSE,
7288 EL_QUICKSAND_EMPTYING, -1, -1
7291 Xsand_stonesand_3, FALSE, FALSE,
7292 EL_QUICKSAND_EMPTYING, -1, -1
7295 Xsand_stonesand_4, FALSE, FALSE,
7296 EL_QUICKSAND_EMPTYING, -1, -1
7299 Xsand_stoneout_1, FALSE, FALSE,
7300 EL_ROCK, ACTION_EMPTYING, -1
7303 Xsand_stoneout_2, FALSE, FALSE,
7304 EL_ROCK, ACTION_EMPTYING, -1
7307 Xsand_stonesand_quickout_1, FALSE, FALSE,
7308 EL_QUICKSAND_EMPTYING, -1, -1
7311 Xsand_stonesand_quickout_2, FALSE, FALSE,
7312 EL_QUICKSAND_EMPTYING, -1, -1
7316 Xslide_ns, TRUE, FALSE,
7317 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7320 Yslide_ns_blank, FALSE, FALSE,
7321 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7324 Xslide_ew, TRUE, FALSE,
7325 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7328 Yslide_ew_blank, FALSE, FALSE,
7329 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7333 Xwind_n, TRUE, FALSE,
7334 EL_BALLOON_SWITCH_UP, -1, -1
7337 Xwind_e, TRUE, FALSE,
7338 EL_BALLOON_SWITCH_RIGHT, -1, -1
7341 Xwind_s, TRUE, FALSE,
7342 EL_BALLOON_SWITCH_DOWN, -1, -1
7345 Xwind_w, TRUE, FALSE,
7346 EL_BALLOON_SWITCH_LEFT, -1, -1
7349 Xwind_any, TRUE, FALSE,
7350 EL_BALLOON_SWITCH_ANY, -1, -1
7353 Xwind_stop, TRUE, FALSE,
7354 EL_BALLOON_SWITCH_NONE, -1, -1
7359 EL_EM_EXIT_CLOSED, -1, -1
7362 Xexit_1, TRUE, FALSE,
7363 EL_EM_EXIT_OPEN, -1, -1
7366 Xexit_2, FALSE, FALSE,
7367 EL_EM_EXIT_OPEN, -1, -1
7370 Xexit_3, FALSE, FALSE,
7371 EL_EM_EXIT_OPEN, -1, -1
7375 Xpause, FALSE, FALSE,
7380 Xwall_1, TRUE, FALSE,
7384 Xwall_2, TRUE, FALSE,
7385 EL_EMC_WALL_14, -1, -1
7388 Xwall_3, TRUE, FALSE,
7389 EL_EMC_WALL_15, -1, -1
7392 Xwall_4, TRUE, FALSE,
7393 EL_EMC_WALL_16, -1, -1
7397 Xroundwall_1, TRUE, FALSE,
7398 EL_WALL_SLIPPERY, -1, -1
7401 Xroundwall_2, TRUE, FALSE,
7402 EL_EMC_WALL_SLIPPERY_2, -1, -1
7405 Xroundwall_3, TRUE, FALSE,
7406 EL_EMC_WALL_SLIPPERY_3, -1, -1
7409 Xroundwall_4, TRUE, FALSE,
7410 EL_EMC_WALL_SLIPPERY_4, -1, -1
7414 Xsteel_1, TRUE, FALSE,
7415 EL_STEELWALL, -1, -1
7418 Xsteel_2, TRUE, FALSE,
7419 EL_EMC_STEELWALL_2, -1, -1
7422 Xsteel_3, TRUE, FALSE,
7423 EL_EMC_STEELWALL_3, -1, -1
7426 Xsteel_4, TRUE, FALSE,
7427 EL_EMC_STEELWALL_4, -1, -1
7431 Xdecor_1, TRUE, FALSE,
7432 EL_EMC_WALL_8, -1, -1
7435 Xdecor_2, TRUE, FALSE,
7436 EL_EMC_WALL_6, -1, -1
7439 Xdecor_3, TRUE, FALSE,
7440 EL_EMC_WALL_4, -1, -1
7443 Xdecor_4, TRUE, FALSE,
7444 EL_EMC_WALL_7, -1, -1
7447 Xdecor_5, TRUE, FALSE,
7448 EL_EMC_WALL_5, -1, -1
7451 Xdecor_6, TRUE, FALSE,
7452 EL_EMC_WALL_9, -1, -1
7455 Xdecor_7, TRUE, FALSE,
7456 EL_EMC_WALL_10, -1, -1
7459 Xdecor_8, TRUE, FALSE,
7460 EL_EMC_WALL_1, -1, -1
7463 Xdecor_9, TRUE, FALSE,
7464 EL_EMC_WALL_2, -1, -1
7467 Xdecor_10, TRUE, FALSE,
7468 EL_EMC_WALL_3, -1, -1
7471 Xdecor_11, TRUE, FALSE,
7472 EL_EMC_WALL_11, -1, -1
7475 Xdecor_12, TRUE, FALSE,
7476 EL_EMC_WALL_12, -1, -1
7480 Xalpha_0, TRUE, FALSE,
7481 EL_CHAR('0'), -1, -1
7484 Xalpha_1, TRUE, FALSE,
7485 EL_CHAR('1'), -1, -1
7488 Xalpha_2, TRUE, FALSE,
7489 EL_CHAR('2'), -1, -1
7492 Xalpha_3, TRUE, FALSE,
7493 EL_CHAR('3'), -1, -1
7496 Xalpha_4, TRUE, FALSE,
7497 EL_CHAR('4'), -1, -1
7500 Xalpha_5, TRUE, FALSE,
7501 EL_CHAR('5'), -1, -1
7504 Xalpha_6, TRUE, FALSE,
7505 EL_CHAR('6'), -1, -1
7508 Xalpha_7, TRUE, FALSE,
7509 EL_CHAR('7'), -1, -1
7512 Xalpha_8, TRUE, FALSE,
7513 EL_CHAR('8'), -1, -1
7516 Xalpha_9, TRUE, FALSE,
7517 EL_CHAR('9'), -1, -1
7520 Xalpha_excla, TRUE, FALSE,
7521 EL_CHAR('!'), -1, -1
7524 Xalpha_apost, TRUE, FALSE,
7525 EL_CHAR('\''), -1, -1
7528 Xalpha_comma, TRUE, FALSE,
7529 EL_CHAR(','), -1, -1
7532 Xalpha_minus, TRUE, FALSE,
7533 EL_CHAR('-'), -1, -1
7536 Xalpha_perio, TRUE, FALSE,
7537 EL_CHAR('.'), -1, -1
7540 Xalpha_colon, TRUE, FALSE,
7541 EL_CHAR(':'), -1, -1
7544 Xalpha_quest, TRUE, FALSE,
7545 EL_CHAR('?'), -1, -1
7548 Xalpha_a, TRUE, FALSE,
7549 EL_CHAR('A'), -1, -1
7552 Xalpha_b, TRUE, FALSE,
7553 EL_CHAR('B'), -1, -1
7556 Xalpha_c, TRUE, FALSE,
7557 EL_CHAR('C'), -1, -1
7560 Xalpha_d, TRUE, FALSE,
7561 EL_CHAR('D'), -1, -1
7564 Xalpha_e, TRUE, FALSE,
7565 EL_CHAR('E'), -1, -1
7568 Xalpha_f, TRUE, FALSE,
7569 EL_CHAR('F'), -1, -1
7572 Xalpha_g, TRUE, FALSE,
7573 EL_CHAR('G'), -1, -1
7576 Xalpha_h, TRUE, FALSE,
7577 EL_CHAR('H'), -1, -1
7580 Xalpha_i, TRUE, FALSE,
7581 EL_CHAR('I'), -1, -1
7584 Xalpha_j, TRUE, FALSE,
7585 EL_CHAR('J'), -1, -1
7588 Xalpha_k, TRUE, FALSE,
7589 EL_CHAR('K'), -1, -1
7592 Xalpha_l, TRUE, FALSE,
7593 EL_CHAR('L'), -1, -1
7596 Xalpha_m, TRUE, FALSE,
7597 EL_CHAR('M'), -1, -1
7600 Xalpha_n, TRUE, FALSE,
7601 EL_CHAR('N'), -1, -1
7604 Xalpha_o, TRUE, FALSE,
7605 EL_CHAR('O'), -1, -1
7608 Xalpha_p, TRUE, FALSE,
7609 EL_CHAR('P'), -1, -1
7612 Xalpha_q, TRUE, FALSE,
7613 EL_CHAR('Q'), -1, -1
7616 Xalpha_r, TRUE, FALSE,
7617 EL_CHAR('R'), -1, -1
7620 Xalpha_s, TRUE, FALSE,
7621 EL_CHAR('S'), -1, -1
7624 Xalpha_t, TRUE, FALSE,
7625 EL_CHAR('T'), -1, -1
7628 Xalpha_u, TRUE, FALSE,
7629 EL_CHAR('U'), -1, -1
7632 Xalpha_v, TRUE, FALSE,
7633 EL_CHAR('V'), -1, -1
7636 Xalpha_w, TRUE, FALSE,
7637 EL_CHAR('W'), -1, -1
7640 Xalpha_x, TRUE, FALSE,
7641 EL_CHAR('X'), -1, -1
7644 Xalpha_y, TRUE, FALSE,
7645 EL_CHAR('Y'), -1, -1
7648 Xalpha_z, TRUE, FALSE,
7649 EL_CHAR('Z'), -1, -1
7652 Xalpha_arrow_e, TRUE, FALSE,
7653 EL_CHAR('>'), -1, -1
7656 Xalpha_arrow_w, TRUE, FALSE,
7657 EL_CHAR('<'), -1, -1
7660 Xalpha_copyr, TRUE, FALSE,
7661 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7665 Ykey_1_blank, FALSE, FALSE,
7666 EL_EM_KEY_1, ACTION_COLLECTING, -1
7669 Ykey_2_blank, FALSE, FALSE,
7670 EL_EM_KEY_2, ACTION_COLLECTING, -1
7673 Ykey_3_blank, FALSE, FALSE,
7674 EL_EM_KEY_3, ACTION_COLLECTING, -1
7677 Ykey_4_blank, FALSE, FALSE,
7678 EL_EM_KEY_4, ACTION_COLLECTING, -1
7681 Ykey_5_blank, FALSE, FALSE,
7682 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7685 Ykey_6_blank, FALSE, FALSE,
7686 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7689 Ykey_7_blank, FALSE, FALSE,
7690 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7693 Ykey_8_blank, FALSE, FALSE,
7694 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7697 Ylenses_blank, FALSE, FALSE,
7698 EL_EMC_LENSES, ACTION_COLLECTING, -1
7701 Ymagnify_blank, FALSE, FALSE,
7702 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7705 Ygrass_blank, FALSE, FALSE,
7706 EL_EMC_GRASS, ACTION_SNAPPING, -1
7709 Ydirt_blank, FALSE, FALSE,
7710 EL_SAND, ACTION_SNAPPING, -1
7719 static struct Mapping_EM_to_RND_player
7728 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7732 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7736 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7740 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7744 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7748 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7752 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7756 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7760 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7764 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7768 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7772 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7776 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7780 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7784 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7788 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7792 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7796 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7800 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7804 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7808 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7812 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7816 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7820 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7824 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7828 EL_PLAYER_1, ACTION_DEFAULT, -1,
7832 EL_PLAYER_2, ACTION_DEFAULT, -1,
7836 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7840 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7844 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7848 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7852 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7856 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7860 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7864 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7868 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7872 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7876 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7880 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7884 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7888 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7892 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7896 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7900 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7904 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7908 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7912 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7916 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7920 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7924 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7928 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7932 EL_PLAYER_3, ACTION_DEFAULT, -1,
7936 EL_PLAYER_4, ACTION_DEFAULT, -1,
7945 int map_element_RND_to_EM_cave(int element_rnd)
7947 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7948 static boolean mapping_initialized = FALSE;
7950 if (!mapping_initialized)
7954 // return "Xalpha_quest" for all undefined elements in mapping array
7955 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7956 mapping_RND_to_EM[i] = Xalpha_quest;
7958 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7959 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7960 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7961 em_object_mapping_list[i].element_em;
7963 mapping_initialized = TRUE;
7966 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7968 Warn("invalid RND level element %d", element_rnd);
7973 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7976 int map_element_EM_to_RND_cave(int element_em_cave)
7978 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7979 static boolean mapping_initialized = FALSE;
7981 if (!mapping_initialized)
7985 // return "EL_UNKNOWN" for all undefined elements in mapping array
7986 for (i = 0; i < GAME_TILE_MAX; i++)
7987 mapping_EM_to_RND[i] = EL_UNKNOWN;
7989 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7990 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7991 em_object_mapping_list[i].element_rnd;
7993 mapping_initialized = TRUE;
7996 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7998 Warn("invalid EM cave element %d", element_em_cave);
8003 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8006 int map_element_EM_to_RND_game(int element_em_game)
8008 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8009 static boolean mapping_initialized = FALSE;
8011 if (!mapping_initialized)
8015 // return "EL_UNKNOWN" for all undefined elements in mapping array
8016 for (i = 0; i < GAME_TILE_MAX; i++)
8017 mapping_EM_to_RND[i] = EL_UNKNOWN;
8019 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8020 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8021 em_object_mapping_list[i].element_rnd;
8023 mapping_initialized = TRUE;
8026 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8028 Warn("invalid EM game element %d", element_em_game);
8033 return mapping_EM_to_RND[element_em_game];
8036 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8038 struct LevelInfo_EM *level_em = level->native_em_level;
8039 struct CAVE *cav = level_em->cav;
8042 for (i = 0; i < GAME_TILE_MAX; i++)
8043 cav->android_array[i] = Cblank;
8045 for (i = 0; i < level->num_android_clone_elements; i++)
8047 int element_rnd = level->android_clone_element[i];
8048 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8050 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8051 if (em_object_mapping_list[j].element_rnd == element_rnd)
8052 cav->android_array[em_object_mapping_list[j].element_em] =
8057 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8059 struct LevelInfo_EM *level_em = level->native_em_level;
8060 struct CAVE *cav = level_em->cav;
8063 level->num_android_clone_elements = 0;
8065 for (i = 0; i < GAME_TILE_MAX; i++)
8067 int element_em_cave = cav->android_array[i];
8069 boolean element_found = FALSE;
8071 if (element_em_cave == Cblank)
8074 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8076 for (j = 0; j < level->num_android_clone_elements; j++)
8077 if (level->android_clone_element[j] == element_rnd)
8078 element_found = TRUE;
8082 level->android_clone_element[level->num_android_clone_elements++] =
8085 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8090 if (level->num_android_clone_elements == 0)
8092 level->num_android_clone_elements = 1;
8093 level->android_clone_element[0] = EL_EMPTY;
8097 int map_direction_RND_to_EM(int direction)
8099 return (direction == MV_UP ? 0 :
8100 direction == MV_RIGHT ? 1 :
8101 direction == MV_DOWN ? 2 :
8102 direction == MV_LEFT ? 3 :
8106 int map_direction_EM_to_RND(int direction)
8108 return (direction == 0 ? MV_UP :
8109 direction == 1 ? MV_RIGHT :
8110 direction == 2 ? MV_DOWN :
8111 direction == 3 ? MV_LEFT :
8115 int map_element_RND_to_SP(int element_rnd)
8117 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8119 if (element_rnd >= EL_SP_START &&
8120 element_rnd <= EL_SP_END)
8121 element_sp = element_rnd - EL_SP_START;
8122 else if (element_rnd == EL_EMPTY_SPACE)
8124 else if (element_rnd == EL_INVISIBLE_WALL)
8130 int map_element_SP_to_RND(int element_sp)
8132 int element_rnd = EL_UNKNOWN;
8134 if (element_sp >= 0x00 &&
8136 element_rnd = EL_SP_START + element_sp;
8137 else if (element_sp == 0x28)
8138 element_rnd = EL_INVISIBLE_WALL;
8143 int map_action_SP_to_RND(int action_sp)
8147 case actActive: return ACTION_ACTIVE;
8148 case actImpact: return ACTION_IMPACT;
8149 case actExploding: return ACTION_EXPLODING;
8150 case actDigging: return ACTION_DIGGING;
8151 case actSnapping: return ACTION_SNAPPING;
8152 case actCollecting: return ACTION_COLLECTING;
8153 case actPassing: return ACTION_PASSING;
8154 case actPushing: return ACTION_PUSHING;
8155 case actDropping: return ACTION_DROPPING;
8157 default: return ACTION_DEFAULT;
8161 int map_element_RND_to_MM(int element_rnd)
8163 return (element_rnd >= EL_MM_START_1 &&
8164 element_rnd <= EL_MM_END_1 ?
8165 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8167 element_rnd >= EL_MM_START_2 &&
8168 element_rnd <= EL_MM_END_2 ?
8169 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8171 element_rnd >= EL_MM_START_3 &&
8172 element_rnd <= EL_MM_END_3 ?
8173 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8175 element_rnd >= EL_CHAR_START &&
8176 element_rnd <= EL_CHAR_END ?
8177 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8179 element_rnd >= EL_MM_RUNTIME_START &&
8180 element_rnd <= EL_MM_RUNTIME_END ?
8181 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8183 EL_MM_EMPTY_NATIVE);
8186 int map_element_MM_to_RND(int element_mm)
8188 return (element_mm == EL_MM_EMPTY_NATIVE ||
8189 element_mm == EL_DF_EMPTY_NATIVE ?
8192 element_mm >= EL_MM_START_1_NATIVE &&
8193 element_mm <= EL_MM_END_1_NATIVE ?
8194 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8196 element_mm >= EL_MM_START_2_NATIVE &&
8197 element_mm <= EL_MM_END_2_NATIVE ?
8198 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8200 element_mm >= EL_MM_START_3_NATIVE &&
8201 element_mm <= EL_MM_END_3_NATIVE ?
8202 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8204 element_mm >= EL_MM_CHAR_START_NATIVE &&
8205 element_mm <= EL_MM_CHAR_END_NATIVE ?
8206 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8208 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8209 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8210 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8215 int map_action_MM_to_RND(int action_mm)
8217 // all MM actions are defined to exactly match their RND counterparts
8221 int map_sound_MM_to_RND(int sound_mm)
8225 case SND_MM_GAME_LEVELTIME_CHARGING:
8226 return SND_GAME_LEVELTIME_CHARGING;
8228 case SND_MM_GAME_HEALTH_CHARGING:
8229 return SND_GAME_HEALTH_CHARGING;
8232 return SND_UNDEFINED;
8236 int map_mm_wall_element(int element)
8238 return (element >= EL_MM_STEEL_WALL_START &&
8239 element <= EL_MM_STEEL_WALL_END ?
8242 element >= EL_MM_WOODEN_WALL_START &&
8243 element <= EL_MM_WOODEN_WALL_END ?
8246 element >= EL_MM_ICE_WALL_START &&
8247 element <= EL_MM_ICE_WALL_END ?
8250 element >= EL_MM_AMOEBA_WALL_START &&
8251 element <= EL_MM_AMOEBA_WALL_END ?
8254 element >= EL_DF_STEEL_WALL_START &&
8255 element <= EL_DF_STEEL_WALL_END ?
8258 element >= EL_DF_WOODEN_WALL_START &&
8259 element <= EL_DF_WOODEN_WALL_END ?
8265 int map_mm_wall_element_editor(int element)
8269 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8270 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8271 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8272 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8273 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8274 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8276 default: return element;
8280 int get_next_element(int element)
8284 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8285 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8286 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8287 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8288 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8289 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8290 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8291 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8292 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8293 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8294 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8296 default: return element;
8300 int el2img_mm(int element_mm)
8302 return el2img(map_element_MM_to_RND(element_mm));
8305 int el_act2img_mm(int element_mm, int action)
8307 return el_act2img(map_element_MM_to_RND(element_mm), action);
8310 int el_act_dir2img(int element, int action, int direction)
8312 element = GFX_ELEMENT(element);
8313 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8315 // direction_graphic[][] == graphic[] for undefined direction graphics
8316 return element_info[element].direction_graphic[action][direction];
8319 static int el_act_dir2crm(int element, int action, int direction)
8321 element = GFX_ELEMENT(element);
8322 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8324 // direction_graphic[][] == graphic[] for undefined direction graphics
8325 return element_info[element].direction_crumbled[action][direction];
8328 int el_act2img(int element, int action)
8330 element = GFX_ELEMENT(element);
8332 return element_info[element].graphic[action];
8335 int el_act2crm(int element, int action)
8337 element = GFX_ELEMENT(element);
8339 return element_info[element].crumbled[action];
8342 int el_dir2img(int element, int direction)
8344 element = GFX_ELEMENT(element);
8346 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8349 int el2baseimg(int element)
8351 return element_info[element].graphic[ACTION_DEFAULT];
8354 int el2img(int element)
8356 element = GFX_ELEMENT(element);
8358 return element_info[element].graphic[ACTION_DEFAULT];
8361 int el2edimg(int element)
8363 element = GFX_ELEMENT(element);
8365 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8368 int el2preimg(int element)
8370 element = GFX_ELEMENT(element);
8372 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8375 int el2panelimg(int element)
8377 element = GFX_ELEMENT(element);
8379 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8382 int font2baseimg(int font_nr)
8384 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8387 int getBeltNrFromBeltElement(int element)
8389 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8390 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8391 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8394 int getBeltNrFromBeltActiveElement(int element)
8396 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8397 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8398 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8401 int getBeltNrFromBeltSwitchElement(int element)
8403 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8404 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8405 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8408 int getBeltDirNrFromBeltElement(int element)
8410 static int belt_base_element[4] =
8412 EL_CONVEYOR_BELT_1_LEFT,
8413 EL_CONVEYOR_BELT_2_LEFT,
8414 EL_CONVEYOR_BELT_3_LEFT,
8415 EL_CONVEYOR_BELT_4_LEFT
8418 int belt_nr = getBeltNrFromBeltElement(element);
8419 int belt_dir_nr = element - belt_base_element[belt_nr];
8421 return (belt_dir_nr % 3);
8424 int getBeltDirNrFromBeltSwitchElement(int element)
8426 static int belt_base_element[4] =
8428 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8429 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8430 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8431 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8434 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8435 int belt_dir_nr = element - belt_base_element[belt_nr];
8437 return (belt_dir_nr % 3);
8440 int getBeltDirFromBeltElement(int element)
8442 static int belt_move_dir[3] =
8449 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8451 return belt_move_dir[belt_dir_nr];
8454 int getBeltDirFromBeltSwitchElement(int element)
8456 static int belt_move_dir[3] =
8463 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8465 return belt_move_dir[belt_dir_nr];
8468 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8470 static int belt_base_element[4] =
8472 EL_CONVEYOR_BELT_1_LEFT,
8473 EL_CONVEYOR_BELT_2_LEFT,
8474 EL_CONVEYOR_BELT_3_LEFT,
8475 EL_CONVEYOR_BELT_4_LEFT
8478 return belt_base_element[belt_nr] + belt_dir_nr;
8481 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8483 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8485 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8488 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8490 static int belt_base_element[4] =
8492 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8493 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8494 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8495 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8498 return belt_base_element[belt_nr] + belt_dir_nr;
8501 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8503 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8505 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8508 boolean swapTiles_EM(boolean is_pre_emc_cave)
8510 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8513 boolean getTeamMode_EM(void)
8515 return game.team_mode || network_playing;
8518 boolean isActivePlayer_EM(int player_nr)
8520 return stored_player[player_nr].active;
8523 unsigned int InitRND(int seed)
8525 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8526 return InitEngineRandom_EM(seed);
8527 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8528 return InitEngineRandom_SP(seed);
8529 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8530 return InitEngineRandom_MM(seed);
8532 return InitEngineRandom_RND(seed);
8535 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8536 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8538 static int get_effective_element_EM(int tile, int frame_em)
8540 int element = object_mapping[tile].element_rnd;
8541 int action = object_mapping[tile].action;
8542 boolean is_backside = object_mapping[tile].is_backside;
8543 boolean action_removing = (action == ACTION_DIGGING ||
8544 action == ACTION_SNAPPING ||
8545 action == ACTION_COLLECTING);
8553 return (frame_em > 5 ? EL_EMPTY : element);
8559 else // frame_em == 7
8570 case Ydiamond_stone:
8574 case Xdrip_stretchB:
8590 case Ymagnify_blank:
8593 case Xsand_stonein_1:
8594 case Xsand_stonein_2:
8595 case Xsand_stonein_3:
8596 case Xsand_stonein_4:
8600 return (is_backside || action_removing ? EL_EMPTY : element);
8605 static boolean check_linear_animation_EM(int tile)
8609 case Xsand_stonesand_1:
8610 case Xsand_stonesand_quickout_1:
8611 case Xsand_sandstone_1:
8612 case Xsand_stonein_1:
8613 case Xsand_stoneout_1:
8641 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8642 boolean has_crumbled_graphics,
8643 int crumbled, int sync_frame)
8645 // if element can be crumbled, but certain action graphics are just empty
8646 // space (like instantly snapping sand to empty space in 1 frame), do not
8647 // treat these empty space graphics as crumbled graphics in EMC engine
8648 if (crumbled == IMG_EMPTY_SPACE)
8649 has_crumbled_graphics = FALSE;
8651 if (has_crumbled_graphics)
8653 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8654 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8655 g_crumbled->anim_delay,
8656 g_crumbled->anim_mode,
8657 g_crumbled->anim_start_frame,
8660 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8661 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8663 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8664 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8666 g_em->has_crumbled_graphics = TRUE;
8670 g_em->crumbled_bitmap = NULL;
8671 g_em->crumbled_src_x = 0;
8672 g_em->crumbled_src_y = 0;
8673 g_em->crumbled_border_size = 0;
8674 g_em->crumbled_tile_size = 0;
8676 g_em->has_crumbled_graphics = FALSE;
8681 void ResetGfxAnimation_EM(int x, int y, int tile)
8687 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8688 int tile, int frame_em, int x, int y)
8690 int action = object_mapping[tile].action;
8691 int direction = object_mapping[tile].direction;
8692 int effective_element = get_effective_element_EM(tile, frame_em);
8693 int graphic = (direction == MV_NONE ?
8694 el_act2img(effective_element, action) :
8695 el_act_dir2img(effective_element, action, direction));
8696 struct GraphicInfo *g = &graphic_info[graphic];
8698 boolean action_removing = (action == ACTION_DIGGING ||
8699 action == ACTION_SNAPPING ||
8700 action == ACTION_COLLECTING);
8701 boolean action_moving = (action == ACTION_FALLING ||
8702 action == ACTION_MOVING ||
8703 action == ACTION_PUSHING ||
8704 action == ACTION_EATING ||
8705 action == ACTION_FILLING ||
8706 action == ACTION_EMPTYING);
8707 boolean action_falling = (action == ACTION_FALLING ||
8708 action == ACTION_FILLING ||
8709 action == ACTION_EMPTYING);
8711 // special case: graphic uses "2nd movement tile" and has defined
8712 // 7 frames for movement animation (or less) => use default graphic
8713 // for last (8th) frame which ends the movement animation
8714 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8716 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8717 graphic = (direction == MV_NONE ?
8718 el_act2img(effective_element, action) :
8719 el_act_dir2img(effective_element, action, direction));
8721 g = &graphic_info[graphic];
8724 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8728 else if (action_moving)
8730 boolean is_backside = object_mapping[tile].is_backside;
8734 int direction = object_mapping[tile].direction;
8735 int move_dir = (action_falling ? MV_DOWN : direction);
8740 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8741 if (g->double_movement && frame_em == 0)
8745 if (move_dir == MV_LEFT)
8746 GfxFrame[x - 1][y] = GfxFrame[x][y];
8747 else if (move_dir == MV_RIGHT)
8748 GfxFrame[x + 1][y] = GfxFrame[x][y];
8749 else if (move_dir == MV_UP)
8750 GfxFrame[x][y - 1] = GfxFrame[x][y];
8751 else if (move_dir == MV_DOWN)
8752 GfxFrame[x][y + 1] = GfxFrame[x][y];
8759 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8760 if (tile == Xsand_stonesand_quickout_1 ||
8761 tile == Xsand_stonesand_quickout_2)
8765 if (graphic_info[graphic].anim_global_sync)
8766 sync_frame = FrameCounter;
8767 else if (graphic_info[graphic].anim_global_anim_sync)
8768 sync_frame = getGlobalAnimSyncFrame();
8769 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8770 sync_frame = GfxFrame[x][y];
8772 sync_frame = 0; // playfield border (pseudo steel)
8774 SetRandomAnimationValue(x, y);
8776 int frame = getAnimationFrame(g->anim_frames,
8779 g->anim_start_frame,
8782 g_em->unique_identifier =
8783 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8786 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8787 int tile, int frame_em, int x, int y)
8789 int action = object_mapping[tile].action;
8790 int direction = object_mapping[tile].direction;
8791 boolean is_backside = object_mapping[tile].is_backside;
8792 int effective_element = get_effective_element_EM(tile, frame_em);
8793 int effective_action = action;
8794 int graphic = (direction == MV_NONE ?
8795 el_act2img(effective_element, effective_action) :
8796 el_act_dir2img(effective_element, effective_action,
8798 int crumbled = (direction == MV_NONE ?
8799 el_act2crm(effective_element, effective_action) :
8800 el_act_dir2crm(effective_element, effective_action,
8802 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8803 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8804 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8805 struct GraphicInfo *g = &graphic_info[graphic];
8808 // special case: graphic uses "2nd movement tile" and has defined
8809 // 7 frames for movement animation (or less) => use default graphic
8810 // for last (8th) frame which ends the movement animation
8811 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8813 effective_action = ACTION_DEFAULT;
8814 graphic = (direction == MV_NONE ?
8815 el_act2img(effective_element, effective_action) :
8816 el_act_dir2img(effective_element, effective_action,
8818 crumbled = (direction == MV_NONE ?
8819 el_act2crm(effective_element, effective_action) :
8820 el_act_dir2crm(effective_element, effective_action,
8823 g = &graphic_info[graphic];
8826 if (graphic_info[graphic].anim_global_sync)
8827 sync_frame = FrameCounter;
8828 else if (graphic_info[graphic].anim_global_anim_sync)
8829 sync_frame = getGlobalAnimSyncFrame();
8830 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8831 sync_frame = GfxFrame[x][y];
8833 sync_frame = 0; // playfield border (pseudo steel)
8835 SetRandomAnimationValue(x, y);
8837 int frame = getAnimationFrame(g->anim_frames,
8840 g->anim_start_frame,
8843 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8844 g->double_movement && is_backside);
8846 // (updating the "crumbled" graphic definitions is probably not really needed,
8847 // as animations for crumbled graphics can't be longer than one EMC cycle)
8848 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8852 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8853 int player_nr, int anim, int frame_em)
8855 int element = player_mapping[player_nr][anim].element_rnd;
8856 int action = player_mapping[player_nr][anim].action;
8857 int direction = player_mapping[player_nr][anim].direction;
8858 int graphic = (direction == MV_NONE ?
8859 el_act2img(element, action) :
8860 el_act_dir2img(element, action, direction));
8861 struct GraphicInfo *g = &graphic_info[graphic];
8864 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8866 stored_player[player_nr].StepFrame = frame_em;
8868 sync_frame = stored_player[player_nr].Frame;
8870 int frame = getAnimationFrame(g->anim_frames,
8873 g->anim_start_frame,
8876 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8877 &g_em->src_x, &g_em->src_y, FALSE);
8880 void InitGraphicInfo_EM(void)
8884 // always start with reliable default values
8885 for (i = 0; i < GAME_TILE_MAX; i++)
8887 object_mapping[i].element_rnd = EL_UNKNOWN;
8888 object_mapping[i].is_backside = FALSE;
8889 object_mapping[i].action = ACTION_DEFAULT;
8890 object_mapping[i].direction = MV_NONE;
8893 // always start with reliable default values
8894 for (p = 0; p < MAX_PLAYERS; p++)
8896 for (i = 0; i < PLY_MAX; i++)
8898 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8899 player_mapping[p][i].action = ACTION_DEFAULT;
8900 player_mapping[p][i].direction = MV_NONE;
8904 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8906 int e = em_object_mapping_list[i].element_em;
8908 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8909 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8911 if (em_object_mapping_list[i].action != -1)
8912 object_mapping[e].action = em_object_mapping_list[i].action;
8914 if (em_object_mapping_list[i].direction != -1)
8915 object_mapping[e].direction =
8916 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8919 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8921 int a = em_player_mapping_list[i].action_em;
8922 int p = em_player_mapping_list[i].player_nr;
8924 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8926 if (em_player_mapping_list[i].action != -1)
8927 player_mapping[p][a].action = em_player_mapping_list[i].action;
8929 if (em_player_mapping_list[i].direction != -1)
8930 player_mapping[p][a].direction =
8931 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8934 for (i = 0; i < GAME_TILE_MAX; i++)
8936 int element = object_mapping[i].element_rnd;
8937 int action = object_mapping[i].action;
8938 int direction = object_mapping[i].direction;
8939 boolean is_backside = object_mapping[i].is_backside;
8940 boolean action_exploding = ((action == ACTION_EXPLODING ||
8941 action == ACTION_SMASHED_BY_ROCK ||
8942 action == ACTION_SMASHED_BY_SPRING) &&
8943 element != EL_DIAMOND);
8944 boolean action_active = (action == ACTION_ACTIVE);
8945 boolean action_other = (action == ACTION_OTHER);
8947 for (j = 0; j < 8; j++)
8949 int effective_element = get_effective_element_EM(i, j);
8950 int effective_action = (j < 7 ? action :
8951 i == Xdrip_stretch ? action :
8952 i == Xdrip_stretchB ? action :
8953 i == Ydrip_1_s ? action :
8954 i == Ydrip_1_sB ? action :
8955 i == Yball_1 ? action :
8956 i == Xball_2 ? action :
8957 i == Yball_2 ? action :
8958 i == Yball_blank ? action :
8959 i == Ykey_1_blank ? action :
8960 i == Ykey_2_blank ? action :
8961 i == Ykey_3_blank ? action :
8962 i == Ykey_4_blank ? action :
8963 i == Ykey_5_blank ? action :
8964 i == Ykey_6_blank ? action :
8965 i == Ykey_7_blank ? action :
8966 i == Ykey_8_blank ? action :
8967 i == Ylenses_blank ? action :
8968 i == Ymagnify_blank ? action :
8969 i == Ygrass_blank ? action :
8970 i == Ydirt_blank ? action :
8971 i == Xsand_stonein_1 ? action :
8972 i == Xsand_stonein_2 ? action :
8973 i == Xsand_stonein_3 ? action :
8974 i == Xsand_stonein_4 ? action :
8975 i == Xsand_stoneout_1 ? action :
8976 i == Xsand_stoneout_2 ? action :
8977 i == Xboom_android ? ACTION_EXPLODING :
8978 action_exploding ? ACTION_EXPLODING :
8979 action_active ? action :
8980 action_other ? action :
8982 int graphic = (el_act_dir2img(effective_element, effective_action,
8984 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8986 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8987 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8988 boolean has_action_graphics = (graphic != base_graphic);
8989 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8990 struct GraphicInfo *g = &graphic_info[graphic];
8991 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8994 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8995 boolean special_animation = (action != ACTION_DEFAULT &&
8996 g->anim_frames == 3 &&
8997 g->anim_delay == 2 &&
8998 g->anim_mode & ANIM_LINEAR);
8999 int sync_frame = (i == Xdrip_stretch ? 7 :
9000 i == Xdrip_stretchB ? 7 :
9001 i == Ydrip_2_s ? j + 8 :
9002 i == Ydrip_2_sB ? j + 8 :
9011 i == Xfake_acid_1 ? 0 :
9012 i == Xfake_acid_2 ? 10 :
9013 i == Xfake_acid_3 ? 20 :
9014 i == Xfake_acid_4 ? 30 :
9015 i == Xfake_acid_5 ? 40 :
9016 i == Xfake_acid_6 ? 50 :
9017 i == Xfake_acid_7 ? 60 :
9018 i == Xfake_acid_8 ? 70 :
9019 i == Xfake_acid_1_player ? 0 :
9020 i == Xfake_acid_2_player ? 10 :
9021 i == Xfake_acid_3_player ? 20 :
9022 i == Xfake_acid_4_player ? 30 :
9023 i == Xfake_acid_5_player ? 40 :
9024 i == Xfake_acid_6_player ? 50 :
9025 i == Xfake_acid_7_player ? 60 :
9026 i == Xfake_acid_8_player ? 70 :
9028 i == Yball_2 ? j + 8 :
9029 i == Yball_blank ? j + 1 :
9030 i == Ykey_1_blank ? j + 1 :
9031 i == Ykey_2_blank ? j + 1 :
9032 i == Ykey_3_blank ? j + 1 :
9033 i == Ykey_4_blank ? j + 1 :
9034 i == Ykey_5_blank ? j + 1 :
9035 i == Ykey_6_blank ? j + 1 :
9036 i == Ykey_7_blank ? j + 1 :
9037 i == Ykey_8_blank ? j + 1 :
9038 i == Ylenses_blank ? j + 1 :
9039 i == Ymagnify_blank ? j + 1 :
9040 i == Ygrass_blank ? j + 1 :
9041 i == Ydirt_blank ? j + 1 :
9042 i == Xamoeba_1 ? 0 :
9043 i == Xamoeba_2 ? 1 :
9044 i == Xamoeba_3 ? 2 :
9045 i == Xamoeba_4 ? 3 :
9046 i == Xamoeba_5 ? 0 :
9047 i == Xamoeba_6 ? 1 :
9048 i == Xamoeba_7 ? 2 :
9049 i == Xamoeba_8 ? 3 :
9050 i == Xexit_2 ? j + 8 :
9051 i == Xexit_3 ? j + 16 :
9052 i == Xdynamite_1 ? 0 :
9053 i == Xdynamite_2 ? 8 :
9054 i == Xdynamite_3 ? 16 :
9055 i == Xdynamite_4 ? 24 :
9056 i == Xsand_stonein_1 ? j + 1 :
9057 i == Xsand_stonein_2 ? j + 9 :
9058 i == Xsand_stonein_3 ? j + 17 :
9059 i == Xsand_stonein_4 ? j + 25 :
9060 i == Xsand_stoneout_1 && j == 0 ? 0 :
9061 i == Xsand_stoneout_1 && j == 1 ? 0 :
9062 i == Xsand_stoneout_1 && j == 2 ? 1 :
9063 i == Xsand_stoneout_1 && j == 3 ? 2 :
9064 i == Xsand_stoneout_1 && j == 4 ? 2 :
9065 i == Xsand_stoneout_1 && j == 5 ? 3 :
9066 i == Xsand_stoneout_1 && j == 6 ? 4 :
9067 i == Xsand_stoneout_1 && j == 7 ? 4 :
9068 i == Xsand_stoneout_2 && j == 0 ? 5 :
9069 i == Xsand_stoneout_2 && j == 1 ? 6 :
9070 i == Xsand_stoneout_2 && j == 2 ? 7 :
9071 i == Xsand_stoneout_2 && j == 3 ? 8 :
9072 i == Xsand_stoneout_2 && j == 4 ? 9 :
9073 i == Xsand_stoneout_2 && j == 5 ? 11 :
9074 i == Xsand_stoneout_2 && j == 6 ? 13 :
9075 i == Xsand_stoneout_2 && j == 7 ? 15 :
9076 i == Xboom_bug && j == 1 ? 2 :
9077 i == Xboom_bug && j == 2 ? 2 :
9078 i == Xboom_bug && j == 3 ? 4 :
9079 i == Xboom_bug && j == 4 ? 4 :
9080 i == Xboom_bug && j == 5 ? 2 :
9081 i == Xboom_bug && j == 6 ? 2 :
9082 i == Xboom_bug && j == 7 ? 0 :
9083 i == Xboom_tank && j == 1 ? 2 :
9084 i == Xboom_tank && j == 2 ? 2 :
9085 i == Xboom_tank && j == 3 ? 4 :
9086 i == Xboom_tank && j == 4 ? 4 :
9087 i == Xboom_tank && j == 5 ? 2 :
9088 i == Xboom_tank && j == 6 ? 2 :
9089 i == Xboom_tank && j == 7 ? 0 :
9090 i == Xboom_android && j == 7 ? 6 :
9091 i == Xboom_1 && j == 1 ? 2 :
9092 i == Xboom_1 && j == 2 ? 2 :
9093 i == Xboom_1 && j == 3 ? 4 :
9094 i == Xboom_1 && j == 4 ? 4 :
9095 i == Xboom_1 && j == 5 ? 6 :
9096 i == Xboom_1 && j == 6 ? 6 :
9097 i == Xboom_1 && j == 7 ? 8 :
9098 i == Xboom_2 && j == 0 ? 8 :
9099 i == Xboom_2 && j == 1 ? 8 :
9100 i == Xboom_2 && j == 2 ? 10 :
9101 i == Xboom_2 && j == 3 ? 10 :
9102 i == Xboom_2 && j == 4 ? 10 :
9103 i == Xboom_2 && j == 5 ? 12 :
9104 i == Xboom_2 && j == 6 ? 12 :
9105 i == Xboom_2 && j == 7 ? 12 :
9106 special_animation && j == 4 ? 3 :
9107 effective_action != action ? 0 :
9109 int frame = getAnimationFrame(g->anim_frames,
9112 g->anim_start_frame,
9115 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9116 g->double_movement && is_backside);
9118 g_em->bitmap = src_bitmap;
9119 g_em->src_x = src_x;
9120 g_em->src_y = src_y;
9121 g_em->src_offset_x = 0;
9122 g_em->src_offset_y = 0;
9123 g_em->dst_offset_x = 0;
9124 g_em->dst_offset_y = 0;
9125 g_em->width = TILEX;
9126 g_em->height = TILEY;
9128 g_em->preserve_background = FALSE;
9130 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9133 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9134 effective_action == ACTION_MOVING ||
9135 effective_action == ACTION_PUSHING ||
9136 effective_action == ACTION_EATING)) ||
9137 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9138 effective_action == ACTION_EMPTYING)))
9141 (effective_action == ACTION_FALLING ||
9142 effective_action == ACTION_FILLING ||
9143 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9144 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9145 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9146 int num_steps = (i == Ydrip_1_s ? 16 :
9147 i == Ydrip_1_sB ? 16 :
9148 i == Ydrip_2_s ? 16 :
9149 i == Ydrip_2_sB ? 16 :
9150 i == Xsand_stonein_1 ? 32 :
9151 i == Xsand_stonein_2 ? 32 :
9152 i == Xsand_stonein_3 ? 32 :
9153 i == Xsand_stonein_4 ? 32 :
9154 i == Xsand_stoneout_1 ? 16 :
9155 i == Xsand_stoneout_2 ? 16 : 8);
9156 int cx = ABS(dx) * (TILEX / num_steps);
9157 int cy = ABS(dy) * (TILEY / num_steps);
9158 int step_frame = (i == Ydrip_2_s ? j + 8 :
9159 i == Ydrip_2_sB ? j + 8 :
9160 i == Xsand_stonein_2 ? j + 8 :
9161 i == Xsand_stonein_3 ? j + 16 :
9162 i == Xsand_stonein_4 ? j + 24 :
9163 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9164 int step = (is_backside ? step_frame : num_steps - step_frame);
9166 if (is_backside) // tile where movement starts
9168 if (dx < 0 || dy < 0)
9170 g_em->src_offset_x = cx * step;
9171 g_em->src_offset_y = cy * step;
9175 g_em->dst_offset_x = cx * step;
9176 g_em->dst_offset_y = cy * step;
9179 else // tile where movement ends
9181 if (dx < 0 || dy < 0)
9183 g_em->dst_offset_x = cx * step;
9184 g_em->dst_offset_y = cy * step;
9188 g_em->src_offset_x = cx * step;
9189 g_em->src_offset_y = cy * step;
9193 g_em->width = TILEX - cx * step;
9194 g_em->height = TILEY - cy * step;
9197 // create unique graphic identifier to decide if tile must be redrawn
9198 /* bit 31 - 16 (16 bit): EM style graphic
9199 bit 15 - 12 ( 4 bit): EM style frame
9200 bit 11 - 6 ( 6 bit): graphic width
9201 bit 5 - 0 ( 6 bit): graphic height */
9202 g_em->unique_identifier =
9203 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9207 for (i = 0; i < GAME_TILE_MAX; i++)
9209 for (j = 0; j < 8; j++)
9211 int element = object_mapping[i].element_rnd;
9212 int action = object_mapping[i].action;
9213 int direction = object_mapping[i].direction;
9214 boolean is_backside = object_mapping[i].is_backside;
9215 int graphic_action = el_act_dir2img(element, action, direction);
9216 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9218 if ((action == ACTION_SMASHED_BY_ROCK ||
9219 action == ACTION_SMASHED_BY_SPRING ||
9220 action == ACTION_EATING) &&
9221 graphic_action == graphic_default)
9223 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9224 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9225 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9226 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9229 // no separate animation for "smashed by rock" -- use rock instead
9230 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9231 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9233 g_em->bitmap = g_xx->bitmap;
9234 g_em->src_x = g_xx->src_x;
9235 g_em->src_y = g_xx->src_y;
9236 g_em->src_offset_x = g_xx->src_offset_x;
9237 g_em->src_offset_y = g_xx->src_offset_y;
9238 g_em->dst_offset_x = g_xx->dst_offset_x;
9239 g_em->dst_offset_y = g_xx->dst_offset_y;
9240 g_em->width = g_xx->width;
9241 g_em->height = g_xx->height;
9242 g_em->unique_identifier = g_xx->unique_identifier;
9245 g_em->preserve_background = TRUE;
9250 for (p = 0; p < MAX_PLAYERS; p++)
9252 for (i = 0; i < PLY_MAX; i++)
9254 int element = player_mapping[p][i].element_rnd;
9255 int action = player_mapping[p][i].action;
9256 int direction = player_mapping[p][i].direction;
9258 for (j = 0; j < 8; j++)
9260 int effective_element = element;
9261 int effective_action = action;
9262 int graphic = (direction == MV_NONE ?
9263 el_act2img(effective_element, effective_action) :
9264 el_act_dir2img(effective_element, effective_action,
9266 struct GraphicInfo *g = &graphic_info[graphic];
9267 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9271 int frame = getAnimationFrame(g->anim_frames,
9274 g->anim_start_frame,
9277 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9279 g_em->bitmap = src_bitmap;
9280 g_em->src_x = src_x;
9281 g_em->src_y = src_y;
9282 g_em->src_offset_x = 0;
9283 g_em->src_offset_y = 0;
9284 g_em->dst_offset_x = 0;
9285 g_em->dst_offset_y = 0;
9286 g_em->width = TILEX;
9287 g_em->height = TILEY;
9293 static void CheckSaveEngineSnapshot_EM(int frame,
9294 boolean any_player_moving,
9295 boolean any_player_snapping,
9296 boolean any_player_dropping)
9298 if (frame == 7 && !any_player_dropping)
9300 if (!local_player->was_waiting)
9302 if (!CheckSaveEngineSnapshotToList())
9305 local_player->was_waiting = TRUE;
9308 else if (any_player_moving || any_player_snapping || any_player_dropping)
9310 local_player->was_waiting = FALSE;
9314 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9315 boolean murphy_is_dropping)
9317 if (murphy_is_waiting)
9319 if (!local_player->was_waiting)
9321 if (!CheckSaveEngineSnapshotToList())
9324 local_player->was_waiting = TRUE;
9329 local_player->was_waiting = FALSE;
9333 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9334 boolean button_released)
9336 if (button_released)
9338 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9339 CheckSaveEngineSnapshotToList();
9341 else if (element_clicked)
9343 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9344 CheckSaveEngineSnapshotToList();
9346 game.snapshot.changed_action = TRUE;
9350 boolean CheckSingleStepMode_EM(int frame,
9351 boolean any_player_moving,
9352 boolean any_player_snapping,
9353 boolean any_player_dropping)
9355 if (tape.single_step && tape.recording && !tape.pausing)
9356 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9357 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9359 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9360 any_player_snapping, any_player_dropping);
9362 return tape.pausing;
9365 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9366 boolean murphy_is_dropping)
9368 boolean murphy_starts_dropping = FALSE;
9371 for (i = 0; i < MAX_PLAYERS; i++)
9372 if (stored_player[i].force_dropping)
9373 murphy_starts_dropping = TRUE;
9375 if (tape.single_step && tape.recording && !tape.pausing)
9376 if (murphy_is_waiting && !murphy_starts_dropping)
9377 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9379 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9382 void CheckSingleStepMode_MM(boolean element_clicked,
9383 boolean button_released)
9385 if (tape.single_step && tape.recording && !tape.pausing)
9386 if (button_released)
9387 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9389 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9392 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9393 int graphic, int sync_frame)
9395 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9397 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9400 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9402 return (IS_NEXT_FRAME(sync_frame, graphic));
9405 int getGraphicInfo_Delay(int graphic)
9407 return graphic_info[graphic].anim_delay;
9410 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9412 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9415 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9421 void PlayMenuSoundExt(int sound)
9423 if (sound == SND_UNDEFINED)
9426 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9427 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9430 if (IS_LOOP_SOUND(sound))
9431 PlaySoundLoop(sound);
9436 void PlayMenuSound(void)
9438 PlayMenuSoundExt(menu.sound[game_status]);
9441 void PlayMenuSoundStereo(int sound, int stereo_position)
9443 if (sound == SND_UNDEFINED)
9446 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9447 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9450 if (IS_LOOP_SOUND(sound))
9451 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9453 PlaySoundStereo(sound, stereo_position);
9456 void PlayMenuSoundIfLoopExt(int sound)
9458 if (sound == SND_UNDEFINED)
9461 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9462 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9465 if (IS_LOOP_SOUND(sound))
9466 PlaySoundLoop(sound);
9469 void PlayMenuSoundIfLoop(void)
9471 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9474 void PlayMenuMusicExt(int music)
9476 if (music == MUS_UNDEFINED)
9479 if (!setup.sound_music)
9482 if (IS_LOOP_MUSIC(music))
9483 PlayMusicLoop(music);
9488 void PlayMenuMusic(void)
9490 char *curr_music = getCurrentlyPlayingMusicFilename();
9491 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9493 if (!strEqual(curr_music, next_music))
9494 PlayMenuMusicExt(menu.music[game_status]);
9497 void PlayMenuSoundsAndMusic(void)
9503 static void FadeMenuSounds(void)
9508 static void FadeMenuMusic(void)
9510 char *curr_music = getCurrentlyPlayingMusicFilename();
9511 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9513 if (!strEqual(curr_music, next_music))
9517 void FadeMenuSoundsAndMusic(void)
9523 void PlaySoundActivating(void)
9526 PlaySound(SND_MENU_ITEM_ACTIVATING);
9530 void PlaySoundSelecting(void)
9533 PlaySound(SND_MENU_ITEM_SELECTING);
9537 void ToggleFullscreenIfNeeded(void)
9539 // if setup and video fullscreen state are already matching, nothing do do
9540 if (setup.fullscreen == video.fullscreen_enabled ||
9541 !video.fullscreen_available)
9544 SDLSetWindowFullscreen(setup.fullscreen);
9546 // set setup value according to successfully changed fullscreen mode
9547 setup.fullscreen = video.fullscreen_enabled;
9550 void ChangeWindowScalingIfNeeded(void)
9552 // if setup and video window scaling are already matching, nothing do do
9553 if (setup.window_scaling_percent == video.window_scaling_percent ||
9554 video.fullscreen_enabled)
9557 SDLSetWindowScaling(setup.window_scaling_percent);
9559 // set setup value according to successfully changed window scaling
9560 setup.window_scaling_percent = video.window_scaling_percent;
9563 void ChangeVsyncModeIfNeeded(void)
9565 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9566 int video_vsync_mode = video.vsync_mode;
9568 // if setup and video vsync mode are already matching, nothing do do
9569 if (setup_vsync_mode == video_vsync_mode)
9572 // if renderer is using OpenGL, vsync mode can directly be changed
9573 SDLSetScreenVsyncMode(setup.vsync_mode);
9575 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9576 if (video.vsync_mode == video_vsync_mode)
9578 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9580 // save backbuffer content which gets lost when re-creating screen
9581 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9583 // force re-creating screen and renderer to set new vsync mode
9584 video.fullscreen_enabled = !setup.fullscreen;
9586 // when creating new renderer, destroy textures linked to old renderer
9587 FreeAllImageTextures(); // needs old renderer to free the textures
9589 // re-create screen and renderer (including change of vsync mode)
9590 ChangeVideoModeIfNeeded(setup.fullscreen);
9592 // set setup value according to successfully changed fullscreen mode
9593 setup.fullscreen = video.fullscreen_enabled;
9595 // restore backbuffer content from temporary backbuffer backup bitmap
9596 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9597 FreeBitmap(tmp_backbuffer);
9599 // update visible window/screen
9600 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9602 // when changing vsync mode, re-create textures for new renderer
9603 InitImageTextures();
9606 // set setup value according to successfully changed vsync mode
9607 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9610 static void JoinRectangles(int *x, int *y, int *width, int *height,
9611 int x2, int y2, int width2, int height2)
9613 // do not join with "off-screen" rectangle
9614 if (x2 == -1 || y2 == -1)
9619 *width = MAX(*width, width2);
9620 *height = MAX(*height, height2);
9623 void SetAnimStatus(int anim_status_new)
9625 if (anim_status_new == GAME_MODE_MAIN)
9626 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9627 else if (anim_status_new == GAME_MODE_NAMES)
9628 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9629 else if (anim_status_new == GAME_MODE_SCORES)
9630 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9632 global.anim_status_next = anim_status_new;
9634 // directly set screen modes that are entered without fading
9635 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9636 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9637 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9638 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9639 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9640 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9641 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9642 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9643 global.anim_status = global.anim_status_next;
9646 void SetGameStatus(int game_status_new)
9648 if (game_status_new != game_status)
9649 game_status_last_screen = game_status;
9651 game_status = game_status_new;
9653 SetAnimStatus(game_status_new);
9656 void SetFontStatus(int game_status_new)
9658 static int last_game_status = -1;
9660 if (game_status_new != -1)
9662 // set game status for font use after storing last game status
9663 last_game_status = game_status;
9664 game_status = game_status_new;
9668 // reset game status after font use from last stored game status
9669 game_status = last_game_status;
9673 void ResetFontStatus(void)
9678 void SetLevelSetInfo(char *identifier, int level_nr)
9680 setString(&levelset.identifier, identifier);
9682 levelset.level_nr = level_nr;
9685 boolean CheckIfAllViewportsHaveChanged(void)
9687 // if game status has not changed, viewports have not changed either
9688 if (game_status == game_status_last)
9691 // check if all viewports have changed with current game status
9693 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9694 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9695 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9696 int new_real_sx = vp_playfield->x;
9697 int new_real_sy = vp_playfield->y;
9698 int new_full_sxsize = vp_playfield->width;
9699 int new_full_sysize = vp_playfield->height;
9700 int new_dx = vp_door_1->x;
9701 int new_dy = vp_door_1->y;
9702 int new_dxsize = vp_door_1->width;
9703 int new_dysize = vp_door_1->height;
9704 int new_vx = vp_door_2->x;
9705 int new_vy = vp_door_2->y;
9706 int new_vxsize = vp_door_2->width;
9707 int new_vysize = vp_door_2->height;
9709 boolean playfield_viewport_has_changed =
9710 (new_real_sx != REAL_SX ||
9711 new_real_sy != REAL_SY ||
9712 new_full_sxsize != FULL_SXSIZE ||
9713 new_full_sysize != FULL_SYSIZE);
9715 boolean door_1_viewport_has_changed =
9718 new_dxsize != DXSIZE ||
9719 new_dysize != DYSIZE);
9721 boolean door_2_viewport_has_changed =
9724 new_vxsize != VXSIZE ||
9725 new_vysize != VYSIZE ||
9726 game_status_last == GAME_MODE_EDITOR);
9728 return (playfield_viewport_has_changed &&
9729 door_1_viewport_has_changed &&
9730 door_2_viewport_has_changed);
9733 boolean CheckFadeAll(void)
9735 return (CheckIfGlobalBorderHasChanged() ||
9736 CheckIfAllViewportsHaveChanged());
9739 void ChangeViewportPropertiesIfNeeded(void)
9741 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9742 FALSE : setup.small_game_graphics);
9743 int gfx_game_mode = getGlobalGameStatus(game_status);
9744 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9746 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9747 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9748 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9749 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9750 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9751 int new_win_xsize = vp_window->width;
9752 int new_win_ysize = vp_window->height;
9753 int border_left = vp_playfield->border_left;
9754 int border_right = vp_playfield->border_right;
9755 int border_top = vp_playfield->border_top;
9756 int border_bottom = vp_playfield->border_bottom;
9757 int new_sx = vp_playfield->x + border_left;
9758 int new_sy = vp_playfield->y + border_top;
9759 int new_sxsize = vp_playfield->width - border_left - border_right;
9760 int new_sysize = vp_playfield->height - border_top - border_bottom;
9761 int new_real_sx = vp_playfield->x;
9762 int new_real_sy = vp_playfield->y;
9763 int new_full_sxsize = vp_playfield->width;
9764 int new_full_sysize = vp_playfield->height;
9765 int new_dx = vp_door_1->x;
9766 int new_dy = vp_door_1->y;
9767 int new_dxsize = vp_door_1->width;
9768 int new_dysize = vp_door_1->height;
9769 int new_vx = vp_door_2->x;
9770 int new_vy = vp_door_2->y;
9771 int new_vxsize = vp_door_2->width;
9772 int new_vysize = vp_door_2->height;
9773 int new_ex = vp_door_3->x;
9774 int new_ey = vp_door_3->y;
9775 int new_exsize = vp_door_3->width;
9776 int new_eysize = vp_door_3->height;
9777 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9778 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9779 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9780 int new_scr_fieldx = new_sxsize / tilesize;
9781 int new_scr_fieldy = new_sysize / tilesize;
9782 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9783 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9784 boolean init_gfx_buffers = FALSE;
9785 boolean init_video_buffer = FALSE;
9786 boolean init_gadgets_and_anims = FALSE;
9787 boolean init_em_graphics = FALSE;
9789 if (new_win_xsize != WIN_XSIZE ||
9790 new_win_ysize != WIN_YSIZE)
9792 WIN_XSIZE = new_win_xsize;
9793 WIN_YSIZE = new_win_ysize;
9795 init_video_buffer = TRUE;
9796 init_gfx_buffers = TRUE;
9797 init_gadgets_and_anims = TRUE;
9799 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9802 if (new_scr_fieldx != SCR_FIELDX ||
9803 new_scr_fieldy != SCR_FIELDY)
9805 // this always toggles between MAIN and GAME when using small tile size
9807 SCR_FIELDX = new_scr_fieldx;
9808 SCR_FIELDY = new_scr_fieldy;
9810 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9821 new_sxsize != SXSIZE ||
9822 new_sysize != SYSIZE ||
9823 new_dxsize != DXSIZE ||
9824 new_dysize != DYSIZE ||
9825 new_vxsize != VXSIZE ||
9826 new_vysize != VYSIZE ||
9827 new_exsize != EXSIZE ||
9828 new_eysize != EYSIZE ||
9829 new_real_sx != REAL_SX ||
9830 new_real_sy != REAL_SY ||
9831 new_full_sxsize != FULL_SXSIZE ||
9832 new_full_sysize != FULL_SYSIZE ||
9833 new_tilesize_var != TILESIZE_VAR
9836 // ------------------------------------------------------------------------
9837 // determine next fading area for changed viewport definitions
9838 // ------------------------------------------------------------------------
9840 // start with current playfield area (default fading area)
9843 FADE_SXSIZE = FULL_SXSIZE;
9844 FADE_SYSIZE = FULL_SYSIZE;
9846 // add new playfield area if position or size has changed
9847 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9848 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9850 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9851 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9854 // add current and new door 1 area if position or size has changed
9855 if (new_dx != DX || new_dy != DY ||
9856 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9858 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9859 DX, DY, DXSIZE, DYSIZE);
9860 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9861 new_dx, new_dy, new_dxsize, new_dysize);
9864 // add current and new door 2 area if position or size has changed
9865 if (new_vx != VX || new_vy != VY ||
9866 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9868 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9869 VX, VY, VXSIZE, VYSIZE);
9870 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9871 new_vx, new_vy, new_vxsize, new_vysize);
9874 // ------------------------------------------------------------------------
9875 // handle changed tile size
9876 // ------------------------------------------------------------------------
9878 if (new_tilesize_var != TILESIZE_VAR)
9880 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9882 // changing tile size invalidates scroll values of engine snapshots
9883 FreeEngineSnapshotSingle();
9885 // changing tile size requires update of graphic mapping for EM engine
9886 init_em_graphics = TRUE;
9897 SXSIZE = new_sxsize;
9898 SYSIZE = new_sysize;
9899 DXSIZE = new_dxsize;
9900 DYSIZE = new_dysize;
9901 VXSIZE = new_vxsize;
9902 VYSIZE = new_vysize;
9903 EXSIZE = new_exsize;
9904 EYSIZE = new_eysize;
9905 REAL_SX = new_real_sx;
9906 REAL_SY = new_real_sy;
9907 FULL_SXSIZE = new_full_sxsize;
9908 FULL_SYSIZE = new_full_sysize;
9909 TILESIZE_VAR = new_tilesize_var;
9911 init_gfx_buffers = TRUE;
9912 init_gadgets_and_anims = TRUE;
9914 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9915 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9918 if (init_gfx_buffers)
9920 // Debug("tools:viewport", "init_gfx_buffers");
9922 SCR_FIELDX = new_scr_fieldx_buffers;
9923 SCR_FIELDY = new_scr_fieldy_buffers;
9927 SCR_FIELDX = new_scr_fieldx;
9928 SCR_FIELDY = new_scr_fieldy;
9930 SetDrawDeactivationMask(REDRAW_NONE);
9931 SetDrawBackgroundMask(REDRAW_FIELD);
9934 if (init_video_buffer)
9936 // Debug("tools:viewport", "init_video_buffer");
9938 FreeAllImageTextures(); // needs old renderer to free the textures
9940 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9941 InitImageTextures();
9944 if (init_gadgets_and_anims)
9946 // Debug("tools:viewport", "init_gadgets_and_anims");
9949 InitGlobalAnimations();
9952 if (init_em_graphics)
9954 InitGraphicInfo_EM();
9958 void OpenURL(char *url)
9960 #if SDL_VERSION_ATLEAST(2,0,14)
9963 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9964 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9965 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9969 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9971 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9975 // ============================================================================
9977 // ============================================================================
9979 #if defined(PLATFORM_WINDOWS)
9980 /* FILETIME of Jan 1 1970 00:00:00. */
9981 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9984 * timezone information is stored outside the kernel so tzp isn't used anymore.
9986 * Note: this function is not for Win32 high precision timing purpose. See
9989 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9992 SYSTEMTIME system_time;
9993 ULARGE_INTEGER ularge;
9995 GetSystemTime(&system_time);
9996 SystemTimeToFileTime(&system_time, &file_time);
9997 ularge.LowPart = file_time.dwLowDateTime;
9998 ularge.HighPart = file_time.dwHighDateTime;
10000 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10001 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10007 static char *test_init_uuid_random_function_simple(void)
10009 static char seed_text[100];
10010 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10012 sprintf(seed_text, "%d", seed);
10017 static char *test_init_uuid_random_function_better(void)
10019 static char seed_text[100];
10020 struct timeval current_time;
10022 gettimeofday(¤t_time, NULL);
10024 prng_seed_bytes(¤t_time, sizeof(current_time));
10026 sprintf(seed_text, "%ld.%ld",
10027 (long)current_time.tv_sec,
10028 (long)current_time.tv_usec);
10033 #if defined(PLATFORM_WINDOWS)
10034 static char *test_init_uuid_random_function_better_windows(void)
10036 static char seed_text[100];
10037 struct timeval current_time;
10039 gettimeofday_windows(¤t_time, NULL);
10041 prng_seed_bytes(¤t_time, sizeof(current_time));
10043 sprintf(seed_text, "%ld.%ld",
10044 (long)current_time.tv_sec,
10045 (long)current_time.tv_usec);
10051 static unsigned int test_uuid_random_function_simple(int max)
10053 return GetSimpleRandom(max);
10056 static unsigned int test_uuid_random_function_better(int max)
10058 return (max > 0 ? prng_get_uint() % max : 0);
10061 #if defined(PLATFORM_WINDOWS)
10062 #define NUM_UUID_TESTS 3
10064 #define NUM_UUID_TESTS 2
10067 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10069 struct hashtable *hash_seeds =
10070 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10071 struct hashtable *hash_uuids =
10072 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10073 static char message[100];
10076 char *random_name = (nr == 0 ? "simple" : "better");
10077 char *random_type = (always_seed ? "always" : "only once");
10078 char *(*init_random_function)(void) =
10080 test_init_uuid_random_function_simple :
10081 test_init_uuid_random_function_better);
10082 unsigned int (*random_function)(int) =
10084 test_uuid_random_function_simple :
10085 test_uuid_random_function_better);
10088 #if defined(PLATFORM_WINDOWS)
10091 random_name = "windows";
10092 init_random_function = test_init_uuid_random_function_better_windows;
10098 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10099 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10101 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10102 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10103 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10105 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10109 // always initialize random number generator at least once
10110 init_random_function();
10112 unsigned int time_start = SDL_GetTicks();
10114 for (i = 0; i < num_uuids; i++)
10118 char *seed = getStringCopy(init_random_function());
10120 hashtable_remove(hash_seeds, seed);
10121 hashtable_insert(hash_seeds, seed, "1");
10124 char *uuid = getStringCopy(getUUIDExt(random_function));
10126 hashtable_remove(hash_uuids, uuid);
10127 hashtable_insert(hash_uuids, uuid, "1");
10130 int num_unique_seeds = hashtable_count(hash_seeds);
10131 int num_unique_uuids = hashtable_count(hash_uuids);
10133 unsigned int time_needed = SDL_GetTicks() - time_start;
10135 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10137 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10140 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10142 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10143 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10145 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10147 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10149 Request(message, REQ_CONFIRM);
10151 hashtable_destroy(hash_seeds, 0);
10152 hashtable_destroy(hash_uuids, 0);
10155 void TestGeneratingUUIDs(void)
10157 int num_uuids = 1000000;
10160 for (i = 0; i < NUM_UUID_TESTS; i++)
10161 for (j = 0; j < 2; j++)
10162 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10164 CloseAllAndExit(0);