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 // (replace with setting corresponding request background)
4977 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4978 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4980 // clear door drawing field
4981 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4983 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4985 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4987 if (game_status == GAME_MODE_PLAYING)
4989 SetPanelBackground();
4990 SetDrawBackgroundMask(REDRAW_DOOR_1);
4994 SetDrawBackgroundMask(REDRAW_FIELD);
5000 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5002 // ---------- handle request buttons ----------
5003 result = RequestHandleEvents(req_state, draw_buffer_last);
5007 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5009 DoRequestAfter(req_state);
5014 boolean Request(char *text, unsigned int req_state)
5016 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5017 boolean overlay_enabled = GetOverlayEnabled();
5020 // when showing request dialog after game ended, deactivate game panel
5022 game.panel.active = FALSE;
5024 game.request_active = TRUE;
5026 SetOverlayEnabled(FALSE);
5028 if (global.use_envelope_request)
5029 result = RequestEnvelope(text, req_state);
5031 result = RequestDoor(text, req_state);
5033 SetOverlayEnabled(overlay_enabled);
5035 game.request_active = FALSE;
5040 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5042 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5043 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5046 if (dpo1->sort_priority != dpo2->sort_priority)
5047 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5049 compare_result = dpo1->nr - dpo2->nr;
5051 return compare_result;
5054 void InitGraphicCompatibilityInfo_Doors(void)
5060 struct DoorInfo *door;
5064 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5065 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5067 { -1, -1, -1, NULL }
5069 struct Rect door_rect_list[] =
5071 { DX, DY, DXSIZE, DYSIZE },
5072 { VX, VY, VXSIZE, VYSIZE }
5076 for (i = 0; doors[i].door_token != -1; i++)
5078 int door_token = doors[i].door_token;
5079 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5080 int part_1 = doors[i].part_1;
5081 int part_8 = doors[i].part_8;
5082 int part_2 = part_1 + 1;
5083 int part_3 = part_1 + 2;
5084 struct DoorInfo *door = doors[i].door;
5085 struct Rect *door_rect = &door_rect_list[door_index];
5086 boolean door_gfx_redefined = FALSE;
5088 // check if any door part graphic definitions have been redefined
5090 for (j = 0; door_part_controls[j].door_token != -1; j++)
5092 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5093 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5095 if (dpc->door_token == door_token && fi->redefined)
5096 door_gfx_redefined = TRUE;
5099 // check for old-style door graphic/animation modifications
5101 if (!door_gfx_redefined)
5103 if (door->anim_mode & ANIM_STATIC_PANEL)
5105 door->panel.step_xoffset = 0;
5106 door->panel.step_yoffset = 0;
5109 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5111 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5112 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5113 int num_door_steps, num_panel_steps;
5115 // remove door part graphics other than the two default wings
5117 for (j = 0; door_part_controls[j].door_token != -1; j++)
5119 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5120 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5122 if (dpc->graphic >= part_3 &&
5123 dpc->graphic <= part_8)
5127 // set graphics and screen positions of the default wings
5129 g_part_1->width = door_rect->width;
5130 g_part_1->height = door_rect->height;
5131 g_part_2->width = door_rect->width;
5132 g_part_2->height = door_rect->height;
5133 g_part_2->src_x = door_rect->width;
5134 g_part_2->src_y = g_part_1->src_y;
5136 door->part_2.x = door->part_1.x;
5137 door->part_2.y = door->part_1.y;
5139 if (door->width != -1)
5141 g_part_1->width = door->width;
5142 g_part_2->width = door->width;
5144 // special treatment for graphics and screen position of right wing
5145 g_part_2->src_x += door_rect->width - door->width;
5146 door->part_2.x += door_rect->width - door->width;
5149 if (door->height != -1)
5151 g_part_1->height = door->height;
5152 g_part_2->height = door->height;
5154 // special treatment for graphics and screen position of bottom wing
5155 g_part_2->src_y += door_rect->height - door->height;
5156 door->part_2.y += door_rect->height - door->height;
5159 // set animation delays for the default wings and panels
5161 door->part_1.step_delay = door->step_delay;
5162 door->part_2.step_delay = door->step_delay;
5163 door->panel.step_delay = door->step_delay;
5165 // set animation draw order for the default wings
5167 door->part_1.sort_priority = 2; // draw left wing over ...
5168 door->part_2.sort_priority = 1; // ... right wing
5170 // set animation draw offset for the default wings
5172 if (door->anim_mode & ANIM_HORIZONTAL)
5174 door->part_1.step_xoffset = door->step_offset;
5175 door->part_1.step_yoffset = 0;
5176 door->part_2.step_xoffset = door->step_offset * -1;
5177 door->part_2.step_yoffset = 0;
5179 num_door_steps = g_part_1->width / door->step_offset;
5181 else // ANIM_VERTICAL
5183 door->part_1.step_xoffset = 0;
5184 door->part_1.step_yoffset = door->step_offset;
5185 door->part_2.step_xoffset = 0;
5186 door->part_2.step_yoffset = door->step_offset * -1;
5188 num_door_steps = g_part_1->height / door->step_offset;
5191 // set animation draw offset for the default panels
5193 if (door->step_offset > 1)
5195 num_panel_steps = 2 * door_rect->height / door->step_offset;
5196 door->panel.start_step = num_panel_steps - num_door_steps;
5197 door->panel.start_step_closing = door->panel.start_step;
5201 num_panel_steps = door_rect->height / door->step_offset;
5202 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5203 door->panel.start_step_closing = door->panel.start_step;
5204 door->panel.step_delay *= 2;
5211 void InitDoors(void)
5215 for (i = 0; door_part_controls[i].door_token != -1; i++)
5217 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5218 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5220 // initialize "start_step_opening" and "start_step_closing", if needed
5221 if (dpc->pos->start_step_opening == 0 &&
5222 dpc->pos->start_step_closing == 0)
5224 // dpc->pos->start_step_opening = dpc->pos->start_step;
5225 dpc->pos->start_step_closing = dpc->pos->start_step;
5228 // fill structure for door part draw order (sorted below)
5230 dpo->sort_priority = dpc->pos->sort_priority;
5233 // sort door part controls according to sort_priority and graphic number
5234 qsort(door_part_order, MAX_DOOR_PARTS,
5235 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5238 unsigned int OpenDoor(unsigned int door_state)
5240 if (door_state & DOOR_COPY_BACK)
5242 if (door_state & DOOR_OPEN_1)
5243 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5244 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5246 if (door_state & DOOR_OPEN_2)
5247 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5248 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5250 door_state &= ~DOOR_COPY_BACK;
5253 return MoveDoor(door_state);
5256 unsigned int CloseDoor(unsigned int door_state)
5258 unsigned int old_door_state = GetDoorState();
5260 if (!(door_state & DOOR_NO_COPY_BACK))
5262 if (old_door_state & DOOR_OPEN_1)
5263 BlitBitmap(backbuffer, bitmap_db_door_1,
5264 DX, DY, DXSIZE, DYSIZE, 0, 0);
5266 if (old_door_state & DOOR_OPEN_2)
5267 BlitBitmap(backbuffer, bitmap_db_door_2,
5268 VX, VY, VXSIZE, VYSIZE, 0, 0);
5270 door_state &= ~DOOR_NO_COPY_BACK;
5273 return MoveDoor(door_state);
5276 unsigned int GetDoorState(void)
5278 return MoveDoor(DOOR_GET_STATE);
5281 unsigned int SetDoorState(unsigned int door_state)
5283 return MoveDoor(door_state | DOOR_SET_STATE);
5286 static int euclid(int a, int b)
5288 return (b ? euclid(b, a % b) : a);
5291 unsigned int MoveDoor(unsigned int door_state)
5293 struct Rect door_rect_list[] =
5295 { DX, DY, DXSIZE, DYSIZE },
5296 { VX, VY, VXSIZE, VYSIZE }
5298 static int door1 = DOOR_CLOSE_1;
5299 static int door2 = DOOR_CLOSE_2;
5300 DelayCounter door_delay = { 0 };
5303 if (door_state == DOOR_GET_STATE)
5304 return (door1 | door2);
5306 if (door_state & DOOR_SET_STATE)
5308 if (door_state & DOOR_ACTION_1)
5309 door1 = door_state & DOOR_ACTION_1;
5310 if (door_state & DOOR_ACTION_2)
5311 door2 = door_state & DOOR_ACTION_2;
5313 return (door1 | door2);
5316 if (!(door_state & DOOR_FORCE_REDRAW))
5318 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5319 door_state &= ~DOOR_OPEN_1;
5320 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5321 door_state &= ~DOOR_CLOSE_1;
5322 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5323 door_state &= ~DOOR_OPEN_2;
5324 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5325 door_state &= ~DOOR_CLOSE_2;
5328 if (global.autoplay_leveldir)
5330 door_state |= DOOR_NO_DELAY;
5331 door_state &= ~DOOR_CLOSE_ALL;
5334 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5335 door_state |= DOOR_NO_DELAY;
5337 if (door_state & DOOR_ACTION)
5339 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5340 boolean door_panel_drawn[NUM_DOORS];
5341 boolean panel_has_doors[NUM_DOORS];
5342 boolean door_part_skip[MAX_DOOR_PARTS];
5343 boolean door_part_done[MAX_DOOR_PARTS];
5344 boolean door_part_done_all;
5345 int num_steps[MAX_DOOR_PARTS];
5346 int max_move_delay = 0; // delay for complete animations of all doors
5347 int max_step_delay = 0; // delay (ms) between two animation frames
5348 int num_move_steps = 0; // number of animation steps for all doors
5349 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5350 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5354 for (i = 0; i < NUM_DOORS; i++)
5355 panel_has_doors[i] = FALSE;
5357 for (i = 0; i < MAX_DOOR_PARTS; i++)
5359 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5360 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5361 int door_token = dpc->door_token;
5363 door_part_done[i] = FALSE;
5364 door_part_skip[i] = (!(door_state & door_token) ||
5368 for (i = 0; i < MAX_DOOR_PARTS; i++)
5370 int nr = door_part_order[i].nr;
5371 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5372 struct DoorPartPosInfo *pos = dpc->pos;
5373 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5374 int door_token = dpc->door_token;
5375 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5376 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5377 int step_xoffset = ABS(pos->step_xoffset);
5378 int step_yoffset = ABS(pos->step_yoffset);
5379 int step_delay = pos->step_delay;
5380 int current_door_state = door_state & door_token;
5381 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5382 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5383 boolean part_opening = (is_panel ? door_closing : door_opening);
5384 int start_step = (part_opening ? pos->start_step_opening :
5385 pos->start_step_closing);
5386 float move_xsize = (step_xoffset ? g->width : 0);
5387 float move_ysize = (step_yoffset ? g->height : 0);
5388 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5389 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5390 int move_steps = (move_xsteps && move_ysteps ?
5391 MIN(move_xsteps, move_ysteps) :
5392 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5393 int move_delay = move_steps * step_delay;
5395 if (door_part_skip[nr])
5398 max_move_delay = MAX(max_move_delay, move_delay);
5399 max_step_delay = (max_step_delay == 0 ? step_delay :
5400 euclid(max_step_delay, step_delay));
5401 num_steps[nr] = move_steps;
5405 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5407 panel_has_doors[door_index] = TRUE;
5411 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5413 num_move_steps = max_move_delay / max_step_delay;
5414 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5416 door_delay.value = max_step_delay;
5418 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5420 start = num_move_steps - 1;
5424 // opening door sound has priority over simultaneously closing door
5425 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5427 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5429 if (door_state & DOOR_OPEN_1)
5430 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5431 if (door_state & DOOR_OPEN_2)
5432 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5434 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5436 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5438 if (door_state & DOOR_CLOSE_1)
5439 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5440 if (door_state & DOOR_CLOSE_2)
5441 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5445 for (k = start; k < num_move_steps; k++)
5447 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5449 door_part_done_all = TRUE;
5451 for (i = 0; i < NUM_DOORS; i++)
5452 door_panel_drawn[i] = FALSE;
5454 for (i = 0; i < MAX_DOOR_PARTS; i++)
5456 int nr = door_part_order[i].nr;
5457 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5458 struct DoorPartPosInfo *pos = dpc->pos;
5459 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5460 int door_token = dpc->door_token;
5461 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5462 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5463 boolean is_panel_and_door_has_closed = FALSE;
5464 struct Rect *door_rect = &door_rect_list[door_index];
5465 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5467 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5468 int current_door_state = door_state & door_token;
5469 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5470 boolean door_closing = !door_opening;
5471 boolean part_opening = (is_panel ? door_closing : door_opening);
5472 boolean part_closing = !part_opening;
5473 int start_step = (part_opening ? pos->start_step_opening :
5474 pos->start_step_closing);
5475 int step_delay = pos->step_delay;
5476 int step_factor = step_delay / max_step_delay;
5477 int k1 = (step_factor ? k / step_factor + 1 : k);
5478 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5479 int kk = MAX(0, k2);
5482 int src_x, src_y, src_xx, src_yy;
5483 int dst_x, dst_y, dst_xx, dst_yy;
5486 if (door_part_skip[nr])
5489 if (!(door_state & door_token))
5497 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5498 int kk_door = MAX(0, k2_door);
5499 int sync_frame = kk_door * door_delay.value;
5500 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5502 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5503 &g_src_x, &g_src_y);
5508 if (!door_panel_drawn[door_index])
5510 ClearRectangle(drawto, door_rect->x, door_rect->y,
5511 door_rect->width, door_rect->height);
5513 door_panel_drawn[door_index] = TRUE;
5516 // draw opening or closing door parts
5518 if (pos->step_xoffset < 0) // door part on right side
5521 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5524 if (dst_xx + width > door_rect->width)
5525 width = door_rect->width - dst_xx;
5527 else // door part on left side
5530 dst_xx = pos->x - kk * pos->step_xoffset;
5534 src_xx = ABS(dst_xx);
5538 width = g->width - src_xx;
5540 if (width > door_rect->width)
5541 width = door_rect->width;
5543 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5546 if (pos->step_yoffset < 0) // door part on bottom side
5549 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5552 if (dst_yy + height > door_rect->height)
5553 height = door_rect->height - dst_yy;
5555 else // door part on top side
5558 dst_yy = pos->y - kk * pos->step_yoffset;
5562 src_yy = ABS(dst_yy);
5566 height = g->height - src_yy;
5569 src_x = g_src_x + src_xx;
5570 src_y = g_src_y + src_yy;
5572 dst_x = door_rect->x + dst_xx;
5573 dst_y = door_rect->y + dst_yy;
5575 is_panel_and_door_has_closed =
5578 panel_has_doors[door_index] &&
5579 k >= num_move_steps_doors_only - 1);
5581 if (width >= 0 && width <= g->width &&
5582 height >= 0 && height <= g->height &&
5583 !is_panel_and_door_has_closed)
5585 if (is_panel || !pos->draw_masked)
5586 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5589 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5593 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5595 if ((part_opening && (width < 0 || height < 0)) ||
5596 (part_closing && (width >= g->width && height >= g->height)))
5597 door_part_done[nr] = TRUE;
5599 // continue door part animations, but not panel after door has closed
5600 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5601 door_part_done_all = FALSE;
5604 if (!(door_state & DOOR_NO_DELAY))
5607 HandleGameActions();
5611 SkipUntilDelayReached(&door_delay, &k, last_frame);
5613 // prevent OS (Windows) from complaining about program not responding
5617 if (door_part_done_all)
5621 if (!(door_state & DOOR_NO_DELAY))
5623 // wait for specified door action post delay
5624 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5625 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5626 else if (door_state & DOOR_ACTION_1)
5627 door_delay.value = door_1.post_delay;
5628 else if (door_state & DOOR_ACTION_2)
5629 door_delay.value = door_2.post_delay;
5631 while (!DelayReached(&door_delay))
5634 HandleGameActions();
5641 if (door_state & DOOR_ACTION_1)
5642 door1 = door_state & DOOR_ACTION_1;
5643 if (door_state & DOOR_ACTION_2)
5644 door2 = door_state & DOOR_ACTION_2;
5646 // draw masked border over door area
5647 DrawMaskedBorder(REDRAW_DOOR_1);
5648 DrawMaskedBorder(REDRAW_DOOR_2);
5650 ClearAutoRepeatKeyEvents();
5652 return (door1 | door2);
5655 static boolean useSpecialEditorDoor(void)
5657 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5658 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5660 // do not draw special editor door if editor border defined or redefined
5661 if (graphic_info[graphic].bitmap != NULL || redefined)
5664 // do not draw special editor door if global border defined to be empty
5665 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5668 // do not draw special editor door if viewport definitions do not match
5672 EY + EYSIZE != VY + VYSIZE)
5678 void DrawSpecialEditorDoor(void)
5680 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5681 int top_border_width = gfx1->width;
5682 int top_border_height = gfx1->height;
5683 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5684 int ex = EX - outer_border;
5685 int ey = EY - outer_border;
5686 int vy = VY - outer_border;
5687 int exsize = EXSIZE + 2 * outer_border;
5689 if (!useSpecialEditorDoor())
5692 // draw bigger level editor toolbox window
5693 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5694 top_border_width, top_border_height, ex, ey - top_border_height);
5695 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5696 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5698 redraw_mask |= REDRAW_ALL;
5701 void UndrawSpecialEditorDoor(void)
5703 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5704 int top_border_width = gfx1->width;
5705 int top_border_height = gfx1->height;
5706 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5707 int ex = EX - outer_border;
5708 int ey = EY - outer_border;
5709 int ey_top = ey - top_border_height;
5710 int exsize = EXSIZE + 2 * outer_border;
5711 int eysize = EYSIZE + 2 * outer_border;
5713 if (!useSpecialEditorDoor())
5716 // draw normal tape recorder window
5717 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5719 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5720 ex, ey_top, top_border_width, top_border_height,
5722 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5723 ex, ey, exsize, eysize, ex, ey);
5727 // if screen background is set to "[NONE]", clear editor toolbox window
5728 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5729 ClearRectangle(drawto, ex, ey, exsize, eysize);
5732 redraw_mask |= REDRAW_ALL;
5736 // ---------- new tool button stuff -------------------------------------------
5741 struct TextPosInfo *pos;
5743 boolean is_touch_button;
5745 } toolbutton_info[NUM_TOOL_BUTTONS] =
5748 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5749 TOOL_CTRL_ID_YES, FALSE, "yes"
5752 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5753 TOOL_CTRL_ID_NO, FALSE, "no"
5756 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5757 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5760 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5761 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5764 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5765 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5768 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5769 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5772 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5773 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5776 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5777 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5780 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5781 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5784 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5785 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5789 void CreateToolButtons(void)
5793 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5795 int graphic = toolbutton_info[i].graphic;
5796 struct GraphicInfo *gfx = &graphic_info[graphic];
5797 struct TextPosInfo *pos = toolbutton_info[i].pos;
5798 struct GadgetInfo *gi;
5799 Bitmap *deco_bitmap = None;
5800 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5801 unsigned int event_mask = GD_EVENT_RELEASED;
5802 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5803 int base_x = (is_touch_button ? 0 : DX);
5804 int base_y = (is_touch_button ? 0 : DY);
5805 int gd_x = gfx->src_x;
5806 int gd_y = gfx->src_y;
5807 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5808 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5813 // do not use touch buttons if overlay touch buttons are disabled
5814 if (is_touch_button && !setup.touch.overlay_buttons)
5817 if (global.use_envelope_request && !is_touch_button)
5819 setRequestPosition(&base_x, &base_y, TRUE);
5821 // check if request buttons are outside of envelope and fix, if needed
5822 if (x < 0 || x + gfx->width > request.width ||
5823 y < 0 || y + gfx->height > request.height)
5825 if (id == TOOL_CTRL_ID_YES)
5828 y = request.height - 2 * request.border_size - gfx->height;
5830 else if (id == TOOL_CTRL_ID_NO)
5832 x = request.width - 2 * request.border_size - gfx->width;
5833 y = request.height - 2 * request.border_size - gfx->height;
5835 else if (id == TOOL_CTRL_ID_CONFIRM)
5837 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5838 y = request.height - 2 * request.border_size - gfx->height;
5840 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5842 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5844 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5845 y = request.height - 2 * request.border_size - gfx->height * 2;
5847 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5848 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5853 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5856 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5858 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5859 pos->size, &deco_bitmap, &deco_x, &deco_y);
5860 deco_xpos = (gfx->width - pos->size) / 2;
5861 deco_ypos = (gfx->height - pos->size) / 2;
5864 gi = CreateGadget(GDI_CUSTOM_ID, id,
5865 GDI_IMAGE_ID, graphic,
5866 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5869 GDI_WIDTH, gfx->width,
5870 GDI_HEIGHT, gfx->height,
5871 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5872 GDI_STATE, GD_BUTTON_UNPRESSED,
5873 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5874 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5875 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5876 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5877 GDI_DECORATION_SIZE, pos->size, pos->size,
5878 GDI_DECORATION_SHIFTING, 1, 1,
5879 GDI_DIRECT_DRAW, FALSE,
5880 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5881 GDI_EVENT_MASK, event_mask,
5882 GDI_CALLBACK_ACTION, HandleToolButtons,
5886 Fail("cannot create gadget");
5888 tool_gadget[id] = gi;
5892 void FreeToolButtons(void)
5896 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5897 FreeGadget(tool_gadget[i]);
5900 static void MapToolButtons(unsigned int req_state)
5902 if (req_state & REQ_ASK)
5904 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5905 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5906 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5907 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5909 else if (req_state & REQ_CONFIRM)
5911 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5912 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5914 else if (req_state & REQ_PLAYER)
5916 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5917 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5918 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5919 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5923 static void UnmapToolButtons(void)
5927 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5928 UnmapGadget(tool_gadget[i]);
5931 static void HandleToolButtons(struct GadgetInfo *gi)
5933 request_gadget_id = gi->custom_id;
5936 static struct Mapping_EM_to_RND_object
5939 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5940 boolean is_backside; // backside of moving element
5946 em_object_mapping_list[GAME_TILE_MAX + 1] =
5949 Zborder, FALSE, FALSE,
5953 Zplayer, FALSE, FALSE,
5962 Ztank, FALSE, FALSE,
5966 Zeater, FALSE, FALSE,
5970 Zdynamite, FALSE, FALSE,
5974 Zboom, FALSE, FALSE,
5979 Xchain, FALSE, FALSE,
5980 EL_DEFAULT, ACTION_EXPLODING, -1
5983 Xboom_bug, FALSE, FALSE,
5984 EL_BUG, ACTION_EXPLODING, -1
5987 Xboom_tank, FALSE, FALSE,
5988 EL_SPACESHIP, ACTION_EXPLODING, -1
5991 Xboom_android, FALSE, FALSE,
5992 EL_EMC_ANDROID, ACTION_OTHER, -1
5995 Xboom_1, FALSE, FALSE,
5996 EL_DEFAULT, ACTION_EXPLODING, -1
5999 Xboom_2, FALSE, FALSE,
6000 EL_DEFAULT, ACTION_EXPLODING, -1
6004 Xblank, TRUE, FALSE,
6009 Xsplash_e, FALSE, FALSE,
6010 EL_ACID_SPLASH_RIGHT, -1, -1
6013 Xsplash_w, FALSE, FALSE,
6014 EL_ACID_SPLASH_LEFT, -1, -1
6018 Xplant, TRUE, FALSE,
6019 EL_EMC_PLANT, -1, -1
6022 Yplant, FALSE, FALSE,
6023 EL_EMC_PLANT, -1, -1
6027 Xacid_1, TRUE, FALSE,
6031 Xacid_2, FALSE, FALSE,
6035 Xacid_3, FALSE, FALSE,
6039 Xacid_4, FALSE, FALSE,
6043 Xacid_5, FALSE, FALSE,
6047 Xacid_6, FALSE, FALSE,
6051 Xacid_7, FALSE, FALSE,
6055 Xacid_8, FALSE, FALSE,
6060 Xfake_acid_1, TRUE, FALSE,
6061 EL_EMC_FAKE_ACID, -1, -1
6064 Xfake_acid_2, FALSE, FALSE,
6065 EL_EMC_FAKE_ACID, -1, -1
6068 Xfake_acid_3, FALSE, FALSE,
6069 EL_EMC_FAKE_ACID, -1, -1
6072 Xfake_acid_4, FALSE, FALSE,
6073 EL_EMC_FAKE_ACID, -1, -1
6076 Xfake_acid_5, FALSE, FALSE,
6077 EL_EMC_FAKE_ACID, -1, -1
6080 Xfake_acid_6, FALSE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_7, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_8, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6093 Xfake_acid_1_player, FALSE, FALSE,
6094 EL_EMC_FAKE_ACID, -1, -1
6097 Xfake_acid_2_player, FALSE, FALSE,
6098 EL_EMC_FAKE_ACID, -1, -1
6101 Xfake_acid_3_player, FALSE, FALSE,
6102 EL_EMC_FAKE_ACID, -1, -1
6105 Xfake_acid_4_player, FALSE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_5_player, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_6_player, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_7_player, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_8_player, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6126 Xgrass, TRUE, FALSE,
6127 EL_EMC_GRASS, -1, -1
6130 Ygrass_nB, FALSE, FALSE,
6131 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6134 Ygrass_eB, FALSE, FALSE,
6135 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6138 Ygrass_sB, FALSE, FALSE,
6139 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6142 Ygrass_wB, FALSE, FALSE,
6143 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6151 Ydirt_nB, FALSE, FALSE,
6152 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6155 Ydirt_eB, FALSE, FALSE,
6156 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6159 Ydirt_sB, FALSE, FALSE,
6160 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6163 Ydirt_wB, FALSE, FALSE,
6164 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6168 Xandroid, TRUE, FALSE,
6169 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6172 Xandroid_1_n, FALSE, FALSE,
6173 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6176 Xandroid_2_n, FALSE, FALSE,
6177 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6180 Xandroid_1_e, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6184 Xandroid_2_e, FALSE, FALSE,
6185 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6188 Xandroid_1_w, FALSE, FALSE,
6189 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6192 Xandroid_2_w, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6196 Xandroid_1_s, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6200 Xandroid_2_s, FALSE, FALSE,
6201 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6204 Yandroid_n, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6208 Yandroid_nB, FALSE, TRUE,
6209 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6212 Yandroid_ne, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6216 Yandroid_neB, FALSE, TRUE,
6217 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6220 Yandroid_e, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6224 Yandroid_eB, FALSE, TRUE,
6225 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6228 Yandroid_se, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6232 Yandroid_seB, FALSE, TRUE,
6233 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6236 Yandroid_s, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6240 Yandroid_sB, FALSE, TRUE,
6241 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6244 Yandroid_sw, FALSE, FALSE,
6245 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6248 Yandroid_swB, FALSE, TRUE,
6249 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6252 Yandroid_w, FALSE, FALSE,
6253 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6256 Yandroid_wB, FALSE, TRUE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6260 Yandroid_nw, FALSE, FALSE,
6261 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6264 Yandroid_nwB, FALSE, TRUE,
6265 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6269 Xeater_n, TRUE, FALSE,
6270 EL_YAMYAM_UP, -1, -1
6273 Xeater_e, TRUE, FALSE,
6274 EL_YAMYAM_RIGHT, -1, -1
6277 Xeater_w, TRUE, FALSE,
6278 EL_YAMYAM_LEFT, -1, -1
6281 Xeater_s, TRUE, FALSE,
6282 EL_YAMYAM_DOWN, -1, -1
6285 Yeater_n, FALSE, FALSE,
6286 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6289 Yeater_nB, FALSE, TRUE,
6290 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6293 Yeater_e, FALSE, FALSE,
6294 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6297 Yeater_eB, FALSE, TRUE,
6298 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6301 Yeater_s, FALSE, FALSE,
6302 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6305 Yeater_sB, FALSE, TRUE,
6306 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6309 Yeater_w, FALSE, FALSE,
6310 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6313 Yeater_wB, FALSE, TRUE,
6314 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6317 Yeater_stone, FALSE, FALSE,
6318 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6321 Yeater_spring, FALSE, FALSE,
6322 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6326 Xalien, TRUE, FALSE,
6330 Xalien_pause, FALSE, FALSE,
6334 Yalien_n, FALSE, FALSE,
6335 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6338 Yalien_nB, FALSE, TRUE,
6339 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6342 Yalien_e, FALSE, FALSE,
6343 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6346 Yalien_eB, FALSE, TRUE,
6347 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6350 Yalien_s, FALSE, FALSE,
6351 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6354 Yalien_sB, FALSE, TRUE,
6355 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6358 Yalien_w, FALSE, FALSE,
6359 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6362 Yalien_wB, FALSE, TRUE,
6363 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6366 Yalien_stone, FALSE, FALSE,
6367 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6370 Yalien_spring, FALSE, FALSE,
6371 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6375 Xbug_1_n, TRUE, FALSE,
6379 Xbug_1_e, TRUE, FALSE,
6380 EL_BUG_RIGHT, -1, -1
6383 Xbug_1_s, TRUE, FALSE,
6387 Xbug_1_w, TRUE, FALSE,
6391 Xbug_2_n, FALSE, FALSE,
6395 Xbug_2_e, FALSE, FALSE,
6396 EL_BUG_RIGHT, -1, -1
6399 Xbug_2_s, FALSE, FALSE,
6403 Xbug_2_w, FALSE, FALSE,
6407 Ybug_n, FALSE, FALSE,
6408 EL_BUG, ACTION_MOVING, MV_BIT_UP
6411 Ybug_nB, FALSE, TRUE,
6412 EL_BUG, ACTION_MOVING, MV_BIT_UP
6415 Ybug_e, FALSE, FALSE,
6416 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6419 Ybug_eB, FALSE, TRUE,
6420 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6423 Ybug_s, FALSE, FALSE,
6424 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6427 Ybug_sB, FALSE, TRUE,
6428 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6431 Ybug_w, FALSE, FALSE,
6432 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6435 Ybug_wB, FALSE, TRUE,
6436 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6439 Ybug_w_n, FALSE, FALSE,
6440 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6443 Ybug_n_e, FALSE, FALSE,
6444 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6447 Ybug_e_s, FALSE, FALSE,
6448 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6451 Ybug_s_w, FALSE, FALSE,
6452 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6455 Ybug_e_n, FALSE, FALSE,
6456 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6459 Ybug_s_e, FALSE, FALSE,
6460 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6463 Ybug_w_s, FALSE, FALSE,
6464 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6467 Ybug_n_w, FALSE, FALSE,
6468 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6471 Ybug_stone, FALSE, FALSE,
6472 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6475 Ybug_spring, FALSE, FALSE,
6476 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6480 Xtank_1_n, TRUE, FALSE,
6481 EL_SPACESHIP_UP, -1, -1
6484 Xtank_1_e, TRUE, FALSE,
6485 EL_SPACESHIP_RIGHT, -1, -1
6488 Xtank_1_s, TRUE, FALSE,
6489 EL_SPACESHIP_DOWN, -1, -1
6492 Xtank_1_w, TRUE, FALSE,
6493 EL_SPACESHIP_LEFT, -1, -1
6496 Xtank_2_n, FALSE, FALSE,
6497 EL_SPACESHIP_UP, -1, -1
6500 Xtank_2_e, FALSE, FALSE,
6501 EL_SPACESHIP_RIGHT, -1, -1
6504 Xtank_2_s, FALSE, FALSE,
6505 EL_SPACESHIP_DOWN, -1, -1
6508 Xtank_2_w, FALSE, FALSE,
6509 EL_SPACESHIP_LEFT, -1, -1
6512 Ytank_n, FALSE, FALSE,
6513 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6516 Ytank_nB, FALSE, TRUE,
6517 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6520 Ytank_e, FALSE, FALSE,
6521 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6524 Ytank_eB, FALSE, TRUE,
6525 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6528 Ytank_s, FALSE, FALSE,
6529 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6532 Ytank_sB, FALSE, TRUE,
6533 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6536 Ytank_w, FALSE, FALSE,
6537 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6540 Ytank_wB, FALSE, TRUE,
6541 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6544 Ytank_w_n, FALSE, FALSE,
6545 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6548 Ytank_n_e, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6552 Ytank_e_s, FALSE, FALSE,
6553 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6556 Ytank_s_w, FALSE, FALSE,
6557 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6560 Ytank_e_n, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6564 Ytank_s_e, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6568 Ytank_w_s, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6572 Ytank_n_w, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6576 Ytank_stone, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6580 Ytank_spring, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6585 Xemerald, TRUE, FALSE,
6589 Xemerald_pause, FALSE, FALSE,
6593 Xemerald_fall, FALSE, FALSE,
6597 Xemerald_shine, FALSE, FALSE,
6598 EL_EMERALD, ACTION_TWINKLING, -1
6601 Yemerald_s, FALSE, FALSE,
6602 EL_EMERALD, ACTION_FALLING, -1
6605 Yemerald_sB, FALSE, TRUE,
6606 EL_EMERALD, ACTION_FALLING, -1
6609 Yemerald_e, FALSE, FALSE,
6610 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6613 Yemerald_eB, FALSE, TRUE,
6614 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6617 Yemerald_w, FALSE, FALSE,
6618 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6621 Yemerald_wB, FALSE, TRUE,
6622 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6625 Yemerald_blank, FALSE, FALSE,
6626 EL_EMERALD, ACTION_COLLECTING, -1
6630 Xdiamond, TRUE, FALSE,
6634 Xdiamond_pause, FALSE, FALSE,
6638 Xdiamond_fall, FALSE, FALSE,
6642 Xdiamond_shine, FALSE, FALSE,
6643 EL_DIAMOND, ACTION_TWINKLING, -1
6646 Ydiamond_s, FALSE, FALSE,
6647 EL_DIAMOND, ACTION_FALLING, -1
6650 Ydiamond_sB, FALSE, TRUE,
6651 EL_DIAMOND, ACTION_FALLING, -1
6654 Ydiamond_e, FALSE, FALSE,
6655 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6658 Ydiamond_eB, FALSE, TRUE,
6659 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6662 Ydiamond_w, FALSE, FALSE,
6663 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6666 Ydiamond_wB, FALSE, TRUE,
6667 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6670 Ydiamond_blank, FALSE, FALSE,
6671 EL_DIAMOND, ACTION_COLLECTING, -1
6674 Ydiamond_stone, FALSE, FALSE,
6675 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6679 Xstone, TRUE, FALSE,
6683 Xstone_pause, FALSE, FALSE,
6687 Xstone_fall, FALSE, FALSE,
6691 Ystone_s, FALSE, FALSE,
6692 EL_ROCK, ACTION_FALLING, -1
6695 Ystone_sB, FALSE, TRUE,
6696 EL_ROCK, ACTION_FALLING, -1
6699 Ystone_e, FALSE, FALSE,
6700 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6703 Ystone_eB, FALSE, TRUE,
6704 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6707 Ystone_w, FALSE, FALSE,
6708 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6711 Ystone_wB, FALSE, TRUE,
6712 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6720 Xbomb_pause, FALSE, FALSE,
6724 Xbomb_fall, FALSE, FALSE,
6728 Ybomb_s, FALSE, FALSE,
6729 EL_BOMB, ACTION_FALLING, -1
6732 Ybomb_sB, FALSE, TRUE,
6733 EL_BOMB, ACTION_FALLING, -1
6736 Ybomb_e, FALSE, FALSE,
6737 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6740 Ybomb_eB, FALSE, TRUE,
6741 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6744 Ybomb_w, FALSE, FALSE,
6745 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6748 Ybomb_wB, FALSE, TRUE,
6749 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6752 Ybomb_blank, FALSE, FALSE,
6753 EL_BOMB, ACTION_ACTIVATING, -1
6761 Xnut_pause, FALSE, FALSE,
6765 Xnut_fall, FALSE, FALSE,
6769 Ynut_s, FALSE, FALSE,
6770 EL_NUT, ACTION_FALLING, -1
6773 Ynut_sB, FALSE, TRUE,
6774 EL_NUT, ACTION_FALLING, -1
6777 Ynut_e, FALSE, FALSE,
6778 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6781 Ynut_eB, FALSE, TRUE,
6782 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6785 Ynut_w, FALSE, FALSE,
6786 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6789 Ynut_wB, FALSE, TRUE,
6790 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6793 Ynut_stone, FALSE, FALSE,
6794 EL_NUT, ACTION_BREAKING, -1
6798 Xspring, TRUE, FALSE,
6802 Xspring_pause, FALSE, FALSE,
6806 Xspring_e, TRUE, FALSE,
6807 EL_SPRING_RIGHT, -1, -1
6810 Xspring_w, TRUE, FALSE,
6811 EL_SPRING_LEFT, -1, -1
6814 Xspring_fall, FALSE, FALSE,
6818 Yspring_s, FALSE, FALSE,
6819 EL_SPRING, ACTION_FALLING, -1
6822 Yspring_sB, FALSE, TRUE,
6823 EL_SPRING, ACTION_FALLING, -1
6826 Yspring_e, FALSE, FALSE,
6827 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6830 Yspring_eB, FALSE, TRUE,
6831 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6834 Yspring_w, FALSE, FALSE,
6835 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6838 Yspring_wB, FALSE, TRUE,
6839 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6842 Yspring_alien_e, FALSE, FALSE,
6843 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6846 Yspring_alien_eB, FALSE, TRUE,
6847 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6850 Yspring_alien_w, FALSE, FALSE,
6851 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6854 Yspring_alien_wB, FALSE, TRUE,
6855 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6859 Xpush_emerald_e, FALSE, FALSE,
6860 EL_EMERALD, -1, MV_BIT_RIGHT
6863 Xpush_emerald_w, FALSE, FALSE,
6864 EL_EMERALD, -1, MV_BIT_LEFT
6867 Xpush_diamond_e, FALSE, FALSE,
6868 EL_DIAMOND, -1, MV_BIT_RIGHT
6871 Xpush_diamond_w, FALSE, FALSE,
6872 EL_DIAMOND, -1, MV_BIT_LEFT
6875 Xpush_stone_e, FALSE, FALSE,
6876 EL_ROCK, -1, MV_BIT_RIGHT
6879 Xpush_stone_w, FALSE, FALSE,
6880 EL_ROCK, -1, MV_BIT_LEFT
6883 Xpush_bomb_e, FALSE, FALSE,
6884 EL_BOMB, -1, MV_BIT_RIGHT
6887 Xpush_bomb_w, FALSE, FALSE,
6888 EL_BOMB, -1, MV_BIT_LEFT
6891 Xpush_nut_e, FALSE, FALSE,
6892 EL_NUT, -1, MV_BIT_RIGHT
6895 Xpush_nut_w, FALSE, FALSE,
6896 EL_NUT, -1, MV_BIT_LEFT
6899 Xpush_spring_e, FALSE, FALSE,
6900 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6903 Xpush_spring_w, FALSE, FALSE,
6904 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6908 Xdynamite, TRUE, FALSE,
6909 EL_EM_DYNAMITE, -1, -1
6912 Ydynamite_blank, FALSE, FALSE,
6913 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6916 Xdynamite_1, TRUE, FALSE,
6917 EL_EM_DYNAMITE_ACTIVE, -1, -1
6920 Xdynamite_2, FALSE, FALSE,
6921 EL_EM_DYNAMITE_ACTIVE, -1, -1
6924 Xdynamite_3, FALSE, FALSE,
6925 EL_EM_DYNAMITE_ACTIVE, -1, -1
6928 Xdynamite_4, FALSE, FALSE,
6929 EL_EM_DYNAMITE_ACTIVE, -1, -1
6933 Xkey_1, TRUE, FALSE,
6937 Xkey_2, TRUE, FALSE,
6941 Xkey_3, TRUE, FALSE,
6945 Xkey_4, TRUE, FALSE,
6949 Xkey_5, TRUE, FALSE,
6950 EL_EMC_KEY_5, -1, -1
6953 Xkey_6, TRUE, FALSE,
6954 EL_EMC_KEY_6, -1, -1
6957 Xkey_7, TRUE, FALSE,
6958 EL_EMC_KEY_7, -1, -1
6961 Xkey_8, TRUE, FALSE,
6962 EL_EMC_KEY_8, -1, -1
6966 Xdoor_1, TRUE, FALSE,
6967 EL_EM_GATE_1, -1, -1
6970 Xdoor_2, TRUE, FALSE,
6971 EL_EM_GATE_2, -1, -1
6974 Xdoor_3, TRUE, FALSE,
6975 EL_EM_GATE_3, -1, -1
6978 Xdoor_4, TRUE, FALSE,
6979 EL_EM_GATE_4, -1, -1
6982 Xdoor_5, TRUE, FALSE,
6983 EL_EMC_GATE_5, -1, -1
6986 Xdoor_6, TRUE, FALSE,
6987 EL_EMC_GATE_6, -1, -1
6990 Xdoor_7, TRUE, FALSE,
6991 EL_EMC_GATE_7, -1, -1
6994 Xdoor_8, TRUE, FALSE,
6995 EL_EMC_GATE_8, -1, -1
6999 Xfake_door_1, TRUE, FALSE,
7000 EL_EM_GATE_1_GRAY, -1, -1
7003 Xfake_door_2, TRUE, FALSE,
7004 EL_EM_GATE_2_GRAY, -1, -1
7007 Xfake_door_3, TRUE, FALSE,
7008 EL_EM_GATE_3_GRAY, -1, -1
7011 Xfake_door_4, TRUE, FALSE,
7012 EL_EM_GATE_4_GRAY, -1, -1
7015 Xfake_door_5, TRUE, FALSE,
7016 EL_EMC_GATE_5_GRAY, -1, -1
7019 Xfake_door_6, TRUE, FALSE,
7020 EL_EMC_GATE_6_GRAY, -1, -1
7023 Xfake_door_7, TRUE, FALSE,
7024 EL_EMC_GATE_7_GRAY, -1, -1
7027 Xfake_door_8, TRUE, FALSE,
7028 EL_EMC_GATE_8_GRAY, -1, -1
7032 Xballoon, TRUE, FALSE,
7036 Yballoon_n, FALSE, FALSE,
7037 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7040 Yballoon_nB, FALSE, TRUE,
7041 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7044 Yballoon_e, FALSE, FALSE,
7045 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7048 Yballoon_eB, FALSE, TRUE,
7049 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7052 Yballoon_s, FALSE, FALSE,
7053 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7056 Yballoon_sB, FALSE, TRUE,
7057 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7060 Yballoon_w, FALSE, FALSE,
7061 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7064 Yballoon_wB, FALSE, TRUE,
7065 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7069 Xball_1, TRUE, FALSE,
7070 EL_EMC_MAGIC_BALL, -1, -1
7073 Yball_1, FALSE, FALSE,
7074 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7077 Xball_2, FALSE, FALSE,
7078 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7081 Yball_2, FALSE, FALSE,
7082 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7085 Yball_blank, FALSE, FALSE,
7086 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7090 Xamoeba_1, TRUE, FALSE,
7091 EL_AMOEBA_DRY, ACTION_OTHER, -1
7094 Xamoeba_2, FALSE, FALSE,
7095 EL_AMOEBA_DRY, ACTION_OTHER, -1
7098 Xamoeba_3, FALSE, FALSE,
7099 EL_AMOEBA_DRY, ACTION_OTHER, -1
7102 Xamoeba_4, FALSE, FALSE,
7103 EL_AMOEBA_DRY, ACTION_OTHER, -1
7106 Xamoeba_5, TRUE, FALSE,
7107 EL_AMOEBA_WET, ACTION_OTHER, -1
7110 Xamoeba_6, FALSE, FALSE,
7111 EL_AMOEBA_WET, ACTION_OTHER, -1
7114 Xamoeba_7, FALSE, FALSE,
7115 EL_AMOEBA_WET, ACTION_OTHER, -1
7118 Xamoeba_8, FALSE, FALSE,
7119 EL_AMOEBA_WET, ACTION_OTHER, -1
7124 EL_AMOEBA_DROP, ACTION_GROWING, -1
7127 Xdrip_fall, FALSE, FALSE,
7128 EL_AMOEBA_DROP, -1, -1
7131 Xdrip_stretch, FALSE, FALSE,
7132 EL_AMOEBA_DROP, ACTION_FALLING, -1
7135 Xdrip_stretchB, FALSE, TRUE,
7136 EL_AMOEBA_DROP, ACTION_FALLING, -1
7139 Ydrip_1_s, FALSE, FALSE,
7140 EL_AMOEBA_DROP, ACTION_FALLING, -1
7143 Ydrip_1_sB, FALSE, TRUE,
7144 EL_AMOEBA_DROP, ACTION_FALLING, -1
7147 Ydrip_2_s, FALSE, FALSE,
7148 EL_AMOEBA_DROP, ACTION_FALLING, -1
7151 Ydrip_2_sB, FALSE, TRUE,
7152 EL_AMOEBA_DROP, ACTION_FALLING, -1
7156 Xwonderwall, TRUE, FALSE,
7157 EL_MAGIC_WALL, -1, -1
7160 Ywonderwall, FALSE, FALSE,
7161 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7165 Xwheel, TRUE, FALSE,
7166 EL_ROBOT_WHEEL, -1, -1
7169 Ywheel, FALSE, FALSE,
7170 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7174 Xswitch, TRUE, FALSE,
7175 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7178 Yswitch, FALSE, FALSE,
7179 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7183 Xbumper, TRUE, FALSE,
7184 EL_EMC_SPRING_BUMPER, -1, -1
7187 Ybumper, FALSE, FALSE,
7188 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7192 Xacid_nw, TRUE, FALSE,
7193 EL_ACID_POOL_TOPLEFT, -1, -1
7196 Xacid_ne, TRUE, FALSE,
7197 EL_ACID_POOL_TOPRIGHT, -1, -1
7200 Xacid_sw, TRUE, FALSE,
7201 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7204 Xacid_s, TRUE, FALSE,
7205 EL_ACID_POOL_BOTTOM, -1, -1
7208 Xacid_se, TRUE, FALSE,
7209 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7213 Xfake_blank, TRUE, FALSE,
7214 EL_INVISIBLE_WALL, -1, -1
7217 Yfake_blank, FALSE, FALSE,
7218 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7222 Xfake_grass, TRUE, FALSE,
7223 EL_EMC_FAKE_GRASS, -1, -1
7226 Yfake_grass, FALSE, FALSE,
7227 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7231 Xfake_amoeba, TRUE, FALSE,
7232 EL_EMC_DRIPPER, -1, -1
7235 Yfake_amoeba, FALSE, FALSE,
7236 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7240 Xlenses, TRUE, FALSE,
7241 EL_EMC_LENSES, -1, -1
7245 Xmagnify, TRUE, FALSE,
7246 EL_EMC_MAGNIFIER, -1, -1
7251 EL_QUICKSAND_EMPTY, -1, -1
7254 Xsand_stone, TRUE, FALSE,
7255 EL_QUICKSAND_FULL, -1, -1
7258 Xsand_stonein_1, FALSE, TRUE,
7259 EL_ROCK, ACTION_FILLING, -1
7262 Xsand_stonein_2, FALSE, TRUE,
7263 EL_ROCK, ACTION_FILLING, -1
7266 Xsand_stonein_3, FALSE, TRUE,
7267 EL_ROCK, ACTION_FILLING, -1
7270 Xsand_stonein_4, FALSE, TRUE,
7271 EL_ROCK, ACTION_FILLING, -1
7274 Xsand_sandstone_1, FALSE, FALSE,
7275 EL_QUICKSAND_FILLING, -1, -1
7278 Xsand_sandstone_2, FALSE, FALSE,
7279 EL_QUICKSAND_FILLING, -1, -1
7282 Xsand_sandstone_3, FALSE, FALSE,
7283 EL_QUICKSAND_FILLING, -1, -1
7286 Xsand_sandstone_4, FALSE, FALSE,
7287 EL_QUICKSAND_FILLING, -1, -1
7290 Xsand_stonesand_1, FALSE, FALSE,
7291 EL_QUICKSAND_EMPTYING, -1, -1
7294 Xsand_stonesand_2, FALSE, FALSE,
7295 EL_QUICKSAND_EMPTYING, -1, -1
7298 Xsand_stonesand_3, FALSE, FALSE,
7299 EL_QUICKSAND_EMPTYING, -1, -1
7302 Xsand_stonesand_4, FALSE, FALSE,
7303 EL_QUICKSAND_EMPTYING, -1, -1
7306 Xsand_stoneout_1, FALSE, FALSE,
7307 EL_ROCK, ACTION_EMPTYING, -1
7310 Xsand_stoneout_2, FALSE, FALSE,
7311 EL_ROCK, ACTION_EMPTYING, -1
7314 Xsand_stonesand_quickout_1, FALSE, FALSE,
7315 EL_QUICKSAND_EMPTYING, -1, -1
7318 Xsand_stonesand_quickout_2, FALSE, FALSE,
7319 EL_QUICKSAND_EMPTYING, -1, -1
7323 Xslide_ns, TRUE, FALSE,
7324 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7327 Yslide_ns_blank, FALSE, FALSE,
7328 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7331 Xslide_ew, TRUE, FALSE,
7332 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7335 Yslide_ew_blank, FALSE, FALSE,
7336 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7340 Xwind_n, TRUE, FALSE,
7341 EL_BALLOON_SWITCH_UP, -1, -1
7344 Xwind_e, TRUE, FALSE,
7345 EL_BALLOON_SWITCH_RIGHT, -1, -1
7348 Xwind_s, TRUE, FALSE,
7349 EL_BALLOON_SWITCH_DOWN, -1, -1
7352 Xwind_w, TRUE, FALSE,
7353 EL_BALLOON_SWITCH_LEFT, -1, -1
7356 Xwind_any, TRUE, FALSE,
7357 EL_BALLOON_SWITCH_ANY, -1, -1
7360 Xwind_stop, TRUE, FALSE,
7361 EL_BALLOON_SWITCH_NONE, -1, -1
7366 EL_EM_EXIT_CLOSED, -1, -1
7369 Xexit_1, TRUE, FALSE,
7370 EL_EM_EXIT_OPEN, -1, -1
7373 Xexit_2, FALSE, FALSE,
7374 EL_EM_EXIT_OPEN, -1, -1
7377 Xexit_3, FALSE, FALSE,
7378 EL_EM_EXIT_OPEN, -1, -1
7382 Xpause, FALSE, FALSE,
7387 Xwall_1, TRUE, FALSE,
7391 Xwall_2, TRUE, FALSE,
7392 EL_EMC_WALL_14, -1, -1
7395 Xwall_3, TRUE, FALSE,
7396 EL_EMC_WALL_15, -1, -1
7399 Xwall_4, TRUE, FALSE,
7400 EL_EMC_WALL_16, -1, -1
7404 Xroundwall_1, TRUE, FALSE,
7405 EL_WALL_SLIPPERY, -1, -1
7408 Xroundwall_2, TRUE, FALSE,
7409 EL_EMC_WALL_SLIPPERY_2, -1, -1
7412 Xroundwall_3, TRUE, FALSE,
7413 EL_EMC_WALL_SLIPPERY_3, -1, -1
7416 Xroundwall_4, TRUE, FALSE,
7417 EL_EMC_WALL_SLIPPERY_4, -1, -1
7421 Xsteel_1, TRUE, FALSE,
7422 EL_STEELWALL, -1, -1
7425 Xsteel_2, TRUE, FALSE,
7426 EL_EMC_STEELWALL_2, -1, -1
7429 Xsteel_3, TRUE, FALSE,
7430 EL_EMC_STEELWALL_3, -1, -1
7433 Xsteel_4, TRUE, FALSE,
7434 EL_EMC_STEELWALL_4, -1, -1
7438 Xdecor_1, TRUE, FALSE,
7439 EL_EMC_WALL_8, -1, -1
7442 Xdecor_2, TRUE, FALSE,
7443 EL_EMC_WALL_6, -1, -1
7446 Xdecor_3, TRUE, FALSE,
7447 EL_EMC_WALL_4, -1, -1
7450 Xdecor_4, TRUE, FALSE,
7451 EL_EMC_WALL_7, -1, -1
7454 Xdecor_5, TRUE, FALSE,
7455 EL_EMC_WALL_5, -1, -1
7458 Xdecor_6, TRUE, FALSE,
7459 EL_EMC_WALL_9, -1, -1
7462 Xdecor_7, TRUE, FALSE,
7463 EL_EMC_WALL_10, -1, -1
7466 Xdecor_8, TRUE, FALSE,
7467 EL_EMC_WALL_1, -1, -1
7470 Xdecor_9, TRUE, FALSE,
7471 EL_EMC_WALL_2, -1, -1
7474 Xdecor_10, TRUE, FALSE,
7475 EL_EMC_WALL_3, -1, -1
7478 Xdecor_11, TRUE, FALSE,
7479 EL_EMC_WALL_11, -1, -1
7482 Xdecor_12, TRUE, FALSE,
7483 EL_EMC_WALL_12, -1, -1
7487 Xalpha_0, TRUE, FALSE,
7488 EL_CHAR('0'), -1, -1
7491 Xalpha_1, TRUE, FALSE,
7492 EL_CHAR('1'), -1, -1
7495 Xalpha_2, TRUE, FALSE,
7496 EL_CHAR('2'), -1, -1
7499 Xalpha_3, TRUE, FALSE,
7500 EL_CHAR('3'), -1, -1
7503 Xalpha_4, TRUE, FALSE,
7504 EL_CHAR('4'), -1, -1
7507 Xalpha_5, TRUE, FALSE,
7508 EL_CHAR('5'), -1, -1
7511 Xalpha_6, TRUE, FALSE,
7512 EL_CHAR('6'), -1, -1
7515 Xalpha_7, TRUE, FALSE,
7516 EL_CHAR('7'), -1, -1
7519 Xalpha_8, TRUE, FALSE,
7520 EL_CHAR('8'), -1, -1
7523 Xalpha_9, TRUE, FALSE,
7524 EL_CHAR('9'), -1, -1
7527 Xalpha_excla, TRUE, FALSE,
7528 EL_CHAR('!'), -1, -1
7531 Xalpha_apost, TRUE, FALSE,
7532 EL_CHAR('\''), -1, -1
7535 Xalpha_comma, TRUE, FALSE,
7536 EL_CHAR(','), -1, -1
7539 Xalpha_minus, TRUE, FALSE,
7540 EL_CHAR('-'), -1, -1
7543 Xalpha_perio, TRUE, FALSE,
7544 EL_CHAR('.'), -1, -1
7547 Xalpha_colon, TRUE, FALSE,
7548 EL_CHAR(':'), -1, -1
7551 Xalpha_quest, TRUE, FALSE,
7552 EL_CHAR('?'), -1, -1
7555 Xalpha_a, TRUE, FALSE,
7556 EL_CHAR('A'), -1, -1
7559 Xalpha_b, TRUE, FALSE,
7560 EL_CHAR('B'), -1, -1
7563 Xalpha_c, TRUE, FALSE,
7564 EL_CHAR('C'), -1, -1
7567 Xalpha_d, TRUE, FALSE,
7568 EL_CHAR('D'), -1, -1
7571 Xalpha_e, TRUE, FALSE,
7572 EL_CHAR('E'), -1, -1
7575 Xalpha_f, TRUE, FALSE,
7576 EL_CHAR('F'), -1, -1
7579 Xalpha_g, TRUE, FALSE,
7580 EL_CHAR('G'), -1, -1
7583 Xalpha_h, TRUE, FALSE,
7584 EL_CHAR('H'), -1, -1
7587 Xalpha_i, TRUE, FALSE,
7588 EL_CHAR('I'), -1, -1
7591 Xalpha_j, TRUE, FALSE,
7592 EL_CHAR('J'), -1, -1
7595 Xalpha_k, TRUE, FALSE,
7596 EL_CHAR('K'), -1, -1
7599 Xalpha_l, TRUE, FALSE,
7600 EL_CHAR('L'), -1, -1
7603 Xalpha_m, TRUE, FALSE,
7604 EL_CHAR('M'), -1, -1
7607 Xalpha_n, TRUE, FALSE,
7608 EL_CHAR('N'), -1, -1
7611 Xalpha_o, TRUE, FALSE,
7612 EL_CHAR('O'), -1, -1
7615 Xalpha_p, TRUE, FALSE,
7616 EL_CHAR('P'), -1, -1
7619 Xalpha_q, TRUE, FALSE,
7620 EL_CHAR('Q'), -1, -1
7623 Xalpha_r, TRUE, FALSE,
7624 EL_CHAR('R'), -1, -1
7627 Xalpha_s, TRUE, FALSE,
7628 EL_CHAR('S'), -1, -1
7631 Xalpha_t, TRUE, FALSE,
7632 EL_CHAR('T'), -1, -1
7635 Xalpha_u, TRUE, FALSE,
7636 EL_CHAR('U'), -1, -1
7639 Xalpha_v, TRUE, FALSE,
7640 EL_CHAR('V'), -1, -1
7643 Xalpha_w, TRUE, FALSE,
7644 EL_CHAR('W'), -1, -1
7647 Xalpha_x, TRUE, FALSE,
7648 EL_CHAR('X'), -1, -1
7651 Xalpha_y, TRUE, FALSE,
7652 EL_CHAR('Y'), -1, -1
7655 Xalpha_z, TRUE, FALSE,
7656 EL_CHAR('Z'), -1, -1
7659 Xalpha_arrow_e, TRUE, FALSE,
7660 EL_CHAR('>'), -1, -1
7663 Xalpha_arrow_w, TRUE, FALSE,
7664 EL_CHAR('<'), -1, -1
7667 Xalpha_copyr, TRUE, FALSE,
7668 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7672 Ykey_1_blank, FALSE, FALSE,
7673 EL_EM_KEY_1, ACTION_COLLECTING, -1
7676 Ykey_2_blank, FALSE, FALSE,
7677 EL_EM_KEY_2, ACTION_COLLECTING, -1
7680 Ykey_3_blank, FALSE, FALSE,
7681 EL_EM_KEY_3, ACTION_COLLECTING, -1
7684 Ykey_4_blank, FALSE, FALSE,
7685 EL_EM_KEY_4, ACTION_COLLECTING, -1
7688 Ykey_5_blank, FALSE, FALSE,
7689 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7692 Ykey_6_blank, FALSE, FALSE,
7693 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7696 Ykey_7_blank, FALSE, FALSE,
7697 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7700 Ykey_8_blank, FALSE, FALSE,
7701 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7704 Ylenses_blank, FALSE, FALSE,
7705 EL_EMC_LENSES, ACTION_COLLECTING, -1
7708 Ymagnify_blank, FALSE, FALSE,
7709 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7712 Ygrass_blank, FALSE, FALSE,
7713 EL_EMC_GRASS, ACTION_SNAPPING, -1
7716 Ydirt_blank, FALSE, FALSE,
7717 EL_SAND, ACTION_SNAPPING, -1
7726 static struct Mapping_EM_to_RND_player
7735 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7739 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7743 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7747 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7751 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7755 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7759 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7763 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7767 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7771 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7775 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7779 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7783 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7787 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7791 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7795 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7799 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7803 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7807 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7811 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7815 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7819 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7823 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7827 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7831 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7835 EL_PLAYER_1, ACTION_DEFAULT, -1,
7839 EL_PLAYER_2, ACTION_DEFAULT, -1,
7843 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7847 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7851 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7855 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7859 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7863 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7867 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7871 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7875 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7879 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7883 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7887 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7891 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7895 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7899 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7903 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7907 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7911 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7915 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7919 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7923 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7927 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7931 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7935 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7939 EL_PLAYER_3, ACTION_DEFAULT, -1,
7943 EL_PLAYER_4, ACTION_DEFAULT, -1,
7952 int map_element_RND_to_EM_cave(int element_rnd)
7954 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7955 static boolean mapping_initialized = FALSE;
7957 if (!mapping_initialized)
7961 // return "Xalpha_quest" for all undefined elements in mapping array
7962 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7963 mapping_RND_to_EM[i] = Xalpha_quest;
7965 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7966 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7967 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7968 em_object_mapping_list[i].element_em;
7970 mapping_initialized = TRUE;
7973 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7975 Warn("invalid RND level element %d", element_rnd);
7980 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7983 int map_element_EM_to_RND_cave(int element_em_cave)
7985 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7986 static boolean mapping_initialized = FALSE;
7988 if (!mapping_initialized)
7992 // return "EL_UNKNOWN" for all undefined elements in mapping array
7993 for (i = 0; i < GAME_TILE_MAX; i++)
7994 mapping_EM_to_RND[i] = EL_UNKNOWN;
7996 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7997 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7998 em_object_mapping_list[i].element_rnd;
8000 mapping_initialized = TRUE;
8003 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8005 Warn("invalid EM cave element %d", element_em_cave);
8010 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8013 int map_element_EM_to_RND_game(int element_em_game)
8015 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8016 static boolean mapping_initialized = FALSE;
8018 if (!mapping_initialized)
8022 // return "EL_UNKNOWN" for all undefined elements in mapping array
8023 for (i = 0; i < GAME_TILE_MAX; i++)
8024 mapping_EM_to_RND[i] = EL_UNKNOWN;
8026 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8027 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8028 em_object_mapping_list[i].element_rnd;
8030 mapping_initialized = TRUE;
8033 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8035 Warn("invalid EM game element %d", element_em_game);
8040 return mapping_EM_to_RND[element_em_game];
8043 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8045 struct LevelInfo_EM *level_em = level->native_em_level;
8046 struct CAVE *cav = level_em->cav;
8049 for (i = 0; i < GAME_TILE_MAX; i++)
8050 cav->android_array[i] = Cblank;
8052 for (i = 0; i < level->num_android_clone_elements; i++)
8054 int element_rnd = level->android_clone_element[i];
8055 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8057 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8058 if (em_object_mapping_list[j].element_rnd == element_rnd)
8059 cav->android_array[em_object_mapping_list[j].element_em] =
8064 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8066 struct LevelInfo_EM *level_em = level->native_em_level;
8067 struct CAVE *cav = level_em->cav;
8070 level->num_android_clone_elements = 0;
8072 for (i = 0; i < GAME_TILE_MAX; i++)
8074 int element_em_cave = cav->android_array[i];
8076 boolean element_found = FALSE;
8078 if (element_em_cave == Cblank)
8081 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8083 for (j = 0; j < level->num_android_clone_elements; j++)
8084 if (level->android_clone_element[j] == element_rnd)
8085 element_found = TRUE;
8089 level->android_clone_element[level->num_android_clone_elements++] =
8092 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8097 if (level->num_android_clone_elements == 0)
8099 level->num_android_clone_elements = 1;
8100 level->android_clone_element[0] = EL_EMPTY;
8104 int map_direction_RND_to_EM(int direction)
8106 return (direction == MV_UP ? 0 :
8107 direction == MV_RIGHT ? 1 :
8108 direction == MV_DOWN ? 2 :
8109 direction == MV_LEFT ? 3 :
8113 int map_direction_EM_to_RND(int direction)
8115 return (direction == 0 ? MV_UP :
8116 direction == 1 ? MV_RIGHT :
8117 direction == 2 ? MV_DOWN :
8118 direction == 3 ? MV_LEFT :
8122 int map_element_RND_to_SP(int element_rnd)
8124 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8126 if (element_rnd >= EL_SP_START &&
8127 element_rnd <= EL_SP_END)
8128 element_sp = element_rnd - EL_SP_START;
8129 else if (element_rnd == EL_EMPTY_SPACE)
8131 else if (element_rnd == EL_INVISIBLE_WALL)
8137 int map_element_SP_to_RND(int element_sp)
8139 int element_rnd = EL_UNKNOWN;
8141 if (element_sp >= 0x00 &&
8143 element_rnd = EL_SP_START + element_sp;
8144 else if (element_sp == 0x28)
8145 element_rnd = EL_INVISIBLE_WALL;
8150 int map_action_SP_to_RND(int action_sp)
8154 case actActive: return ACTION_ACTIVE;
8155 case actImpact: return ACTION_IMPACT;
8156 case actExploding: return ACTION_EXPLODING;
8157 case actDigging: return ACTION_DIGGING;
8158 case actSnapping: return ACTION_SNAPPING;
8159 case actCollecting: return ACTION_COLLECTING;
8160 case actPassing: return ACTION_PASSING;
8161 case actPushing: return ACTION_PUSHING;
8162 case actDropping: return ACTION_DROPPING;
8164 default: return ACTION_DEFAULT;
8168 int map_element_RND_to_MM(int element_rnd)
8170 return (element_rnd >= EL_MM_START_1 &&
8171 element_rnd <= EL_MM_END_1 ?
8172 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8174 element_rnd >= EL_MM_START_2 &&
8175 element_rnd <= EL_MM_END_2 ?
8176 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8178 element_rnd >= EL_MM_START_3 &&
8179 element_rnd <= EL_MM_END_3 ?
8180 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8182 element_rnd >= EL_CHAR_START &&
8183 element_rnd <= EL_CHAR_END ?
8184 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8186 element_rnd >= EL_MM_RUNTIME_START &&
8187 element_rnd <= EL_MM_RUNTIME_END ?
8188 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8190 EL_MM_EMPTY_NATIVE);
8193 int map_element_MM_to_RND(int element_mm)
8195 return (element_mm == EL_MM_EMPTY_NATIVE ||
8196 element_mm == EL_DF_EMPTY_NATIVE ?
8199 element_mm >= EL_MM_START_1_NATIVE &&
8200 element_mm <= EL_MM_END_1_NATIVE ?
8201 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8203 element_mm >= EL_MM_START_2_NATIVE &&
8204 element_mm <= EL_MM_END_2_NATIVE ?
8205 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8207 element_mm >= EL_MM_START_3_NATIVE &&
8208 element_mm <= EL_MM_END_3_NATIVE ?
8209 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8211 element_mm >= EL_MM_CHAR_START_NATIVE &&
8212 element_mm <= EL_MM_CHAR_END_NATIVE ?
8213 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8215 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8216 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8217 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8222 int map_action_MM_to_RND(int action_mm)
8224 // all MM actions are defined to exactly match their RND counterparts
8228 int map_sound_MM_to_RND(int sound_mm)
8232 case SND_MM_GAME_LEVELTIME_CHARGING:
8233 return SND_GAME_LEVELTIME_CHARGING;
8235 case SND_MM_GAME_HEALTH_CHARGING:
8236 return SND_GAME_HEALTH_CHARGING;
8239 return SND_UNDEFINED;
8243 int map_mm_wall_element(int element)
8245 return (element >= EL_MM_STEEL_WALL_START &&
8246 element <= EL_MM_STEEL_WALL_END ?
8249 element >= EL_MM_WOODEN_WALL_START &&
8250 element <= EL_MM_WOODEN_WALL_END ?
8253 element >= EL_MM_ICE_WALL_START &&
8254 element <= EL_MM_ICE_WALL_END ?
8257 element >= EL_MM_AMOEBA_WALL_START &&
8258 element <= EL_MM_AMOEBA_WALL_END ?
8261 element >= EL_DF_STEEL_WALL_START &&
8262 element <= EL_DF_STEEL_WALL_END ?
8265 element >= EL_DF_WOODEN_WALL_START &&
8266 element <= EL_DF_WOODEN_WALL_END ?
8272 int map_mm_wall_element_editor(int element)
8276 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8277 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8278 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8279 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8280 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8281 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8283 default: return element;
8287 int get_next_element(int element)
8291 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8292 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8293 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8294 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8295 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8296 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8297 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8298 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8299 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8300 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8301 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8303 default: return element;
8307 int el2img_mm(int element_mm)
8309 return el2img(map_element_MM_to_RND(element_mm));
8312 int el_act2img_mm(int element_mm, int action)
8314 return el_act2img(map_element_MM_to_RND(element_mm), action);
8317 int el_act_dir2img(int element, int action, int direction)
8319 element = GFX_ELEMENT(element);
8320 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8322 // direction_graphic[][] == graphic[] for undefined direction graphics
8323 return element_info[element].direction_graphic[action][direction];
8326 static int el_act_dir2crm(int element, int action, int direction)
8328 element = GFX_ELEMENT(element);
8329 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8331 // direction_graphic[][] == graphic[] for undefined direction graphics
8332 return element_info[element].direction_crumbled[action][direction];
8335 int el_act2img(int element, int action)
8337 element = GFX_ELEMENT(element);
8339 return element_info[element].graphic[action];
8342 int el_act2crm(int element, int action)
8344 element = GFX_ELEMENT(element);
8346 return element_info[element].crumbled[action];
8349 int el_dir2img(int element, int direction)
8351 element = GFX_ELEMENT(element);
8353 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8356 int el2baseimg(int element)
8358 return element_info[element].graphic[ACTION_DEFAULT];
8361 int el2img(int element)
8363 element = GFX_ELEMENT(element);
8365 return element_info[element].graphic[ACTION_DEFAULT];
8368 int el2edimg(int element)
8370 element = GFX_ELEMENT(element);
8372 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8375 int el2preimg(int element)
8377 element = GFX_ELEMENT(element);
8379 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8382 int el2panelimg(int element)
8384 element = GFX_ELEMENT(element);
8386 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8389 int font2baseimg(int font_nr)
8391 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8394 int getBeltNrFromBeltElement(int element)
8396 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8397 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8398 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8401 int getBeltNrFromBeltActiveElement(int element)
8403 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8404 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8405 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8408 int getBeltNrFromBeltSwitchElement(int element)
8410 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8411 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8412 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8415 int getBeltDirNrFromBeltElement(int element)
8417 static int belt_base_element[4] =
8419 EL_CONVEYOR_BELT_1_LEFT,
8420 EL_CONVEYOR_BELT_2_LEFT,
8421 EL_CONVEYOR_BELT_3_LEFT,
8422 EL_CONVEYOR_BELT_4_LEFT
8425 int belt_nr = getBeltNrFromBeltElement(element);
8426 int belt_dir_nr = element - belt_base_element[belt_nr];
8428 return (belt_dir_nr % 3);
8431 int getBeltDirNrFromBeltSwitchElement(int element)
8433 static int belt_base_element[4] =
8435 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8436 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8437 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8438 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8441 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8442 int belt_dir_nr = element - belt_base_element[belt_nr];
8444 return (belt_dir_nr % 3);
8447 int getBeltDirFromBeltElement(int element)
8449 static int belt_move_dir[3] =
8456 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8458 return belt_move_dir[belt_dir_nr];
8461 int getBeltDirFromBeltSwitchElement(int element)
8463 static int belt_move_dir[3] =
8470 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8472 return belt_move_dir[belt_dir_nr];
8475 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8477 static int belt_base_element[4] =
8479 EL_CONVEYOR_BELT_1_LEFT,
8480 EL_CONVEYOR_BELT_2_LEFT,
8481 EL_CONVEYOR_BELT_3_LEFT,
8482 EL_CONVEYOR_BELT_4_LEFT
8485 return belt_base_element[belt_nr] + belt_dir_nr;
8488 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8490 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8492 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8495 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8497 static int belt_base_element[4] =
8499 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8500 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8501 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8502 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8505 return belt_base_element[belt_nr] + belt_dir_nr;
8508 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8510 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8512 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8515 boolean swapTiles_EM(boolean is_pre_emc_cave)
8517 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8520 boolean getTeamMode_EM(void)
8522 return game.team_mode || network_playing;
8525 boolean isActivePlayer_EM(int player_nr)
8527 return stored_player[player_nr].active;
8530 unsigned int InitRND(int seed)
8532 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8533 return InitEngineRandom_EM(seed);
8534 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8535 return InitEngineRandom_SP(seed);
8536 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8537 return InitEngineRandom_MM(seed);
8539 return InitEngineRandom_RND(seed);
8542 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8543 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8545 static int get_effective_element_EM(int tile, int frame_em)
8547 int element = object_mapping[tile].element_rnd;
8548 int action = object_mapping[tile].action;
8549 boolean is_backside = object_mapping[tile].is_backside;
8550 boolean action_removing = (action == ACTION_DIGGING ||
8551 action == ACTION_SNAPPING ||
8552 action == ACTION_COLLECTING);
8560 return (frame_em > 5 ? EL_EMPTY : element);
8566 else // frame_em == 7
8577 case Ydiamond_stone:
8581 case Xdrip_stretchB:
8597 case Ymagnify_blank:
8600 case Xsand_stonein_1:
8601 case Xsand_stonein_2:
8602 case Xsand_stonein_3:
8603 case Xsand_stonein_4:
8607 return (is_backside || action_removing ? EL_EMPTY : element);
8612 static boolean check_linear_animation_EM(int tile)
8616 case Xsand_stonesand_1:
8617 case Xsand_stonesand_quickout_1:
8618 case Xsand_sandstone_1:
8619 case Xsand_stonein_1:
8620 case Xsand_stoneout_1:
8648 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8649 boolean has_crumbled_graphics,
8650 int crumbled, int sync_frame)
8652 // if element can be crumbled, but certain action graphics are just empty
8653 // space (like instantly snapping sand to empty space in 1 frame), do not
8654 // treat these empty space graphics as crumbled graphics in EMC engine
8655 if (crumbled == IMG_EMPTY_SPACE)
8656 has_crumbled_graphics = FALSE;
8658 if (has_crumbled_graphics)
8660 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8661 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8662 g_crumbled->anim_delay,
8663 g_crumbled->anim_mode,
8664 g_crumbled->anim_start_frame,
8667 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8668 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8670 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8671 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8673 g_em->has_crumbled_graphics = TRUE;
8677 g_em->crumbled_bitmap = NULL;
8678 g_em->crumbled_src_x = 0;
8679 g_em->crumbled_src_y = 0;
8680 g_em->crumbled_border_size = 0;
8681 g_em->crumbled_tile_size = 0;
8683 g_em->has_crumbled_graphics = FALSE;
8688 void ResetGfxAnimation_EM(int x, int y, int tile)
8694 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8695 int tile, int frame_em, int x, int y)
8697 int action = object_mapping[tile].action;
8698 int direction = object_mapping[tile].direction;
8699 int effective_element = get_effective_element_EM(tile, frame_em);
8700 int graphic = (direction == MV_NONE ?
8701 el_act2img(effective_element, action) :
8702 el_act_dir2img(effective_element, action, direction));
8703 struct GraphicInfo *g = &graphic_info[graphic];
8705 boolean action_removing = (action == ACTION_DIGGING ||
8706 action == ACTION_SNAPPING ||
8707 action == ACTION_COLLECTING);
8708 boolean action_moving = (action == ACTION_FALLING ||
8709 action == ACTION_MOVING ||
8710 action == ACTION_PUSHING ||
8711 action == ACTION_EATING ||
8712 action == ACTION_FILLING ||
8713 action == ACTION_EMPTYING);
8714 boolean action_falling = (action == ACTION_FALLING ||
8715 action == ACTION_FILLING ||
8716 action == ACTION_EMPTYING);
8718 // special case: graphic uses "2nd movement tile" and has defined
8719 // 7 frames for movement animation (or less) => use default graphic
8720 // for last (8th) frame which ends the movement animation
8721 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8723 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8724 graphic = (direction == MV_NONE ?
8725 el_act2img(effective_element, action) :
8726 el_act_dir2img(effective_element, action, direction));
8728 g = &graphic_info[graphic];
8731 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8735 else if (action_moving)
8737 boolean is_backside = object_mapping[tile].is_backside;
8741 int direction = object_mapping[tile].direction;
8742 int move_dir = (action_falling ? MV_DOWN : direction);
8747 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8748 if (g->double_movement && frame_em == 0)
8752 if (move_dir == MV_LEFT)
8753 GfxFrame[x - 1][y] = GfxFrame[x][y];
8754 else if (move_dir == MV_RIGHT)
8755 GfxFrame[x + 1][y] = GfxFrame[x][y];
8756 else if (move_dir == MV_UP)
8757 GfxFrame[x][y - 1] = GfxFrame[x][y];
8758 else if (move_dir == MV_DOWN)
8759 GfxFrame[x][y + 1] = GfxFrame[x][y];
8766 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8767 if (tile == Xsand_stonesand_quickout_1 ||
8768 tile == Xsand_stonesand_quickout_2)
8772 if (graphic_info[graphic].anim_global_sync)
8773 sync_frame = FrameCounter;
8774 else if (graphic_info[graphic].anim_global_anim_sync)
8775 sync_frame = getGlobalAnimSyncFrame();
8776 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8777 sync_frame = GfxFrame[x][y];
8779 sync_frame = 0; // playfield border (pseudo steel)
8781 SetRandomAnimationValue(x, y);
8783 int frame = getAnimationFrame(g->anim_frames,
8786 g->anim_start_frame,
8789 g_em->unique_identifier =
8790 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8793 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8794 int tile, int frame_em, int x, int y)
8796 int action = object_mapping[tile].action;
8797 int direction = object_mapping[tile].direction;
8798 boolean is_backside = object_mapping[tile].is_backside;
8799 int effective_element = get_effective_element_EM(tile, frame_em);
8800 int effective_action = action;
8801 int graphic = (direction == MV_NONE ?
8802 el_act2img(effective_element, effective_action) :
8803 el_act_dir2img(effective_element, effective_action,
8805 int crumbled = (direction == MV_NONE ?
8806 el_act2crm(effective_element, effective_action) :
8807 el_act_dir2crm(effective_element, effective_action,
8809 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8810 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8811 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8812 struct GraphicInfo *g = &graphic_info[graphic];
8815 // special case: graphic uses "2nd movement tile" and has defined
8816 // 7 frames for movement animation (or less) => use default graphic
8817 // for last (8th) frame which ends the movement animation
8818 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8820 effective_action = ACTION_DEFAULT;
8821 graphic = (direction == MV_NONE ?
8822 el_act2img(effective_element, effective_action) :
8823 el_act_dir2img(effective_element, effective_action,
8825 crumbled = (direction == MV_NONE ?
8826 el_act2crm(effective_element, effective_action) :
8827 el_act_dir2crm(effective_element, effective_action,
8830 g = &graphic_info[graphic];
8833 if (graphic_info[graphic].anim_global_sync)
8834 sync_frame = FrameCounter;
8835 else if (graphic_info[graphic].anim_global_anim_sync)
8836 sync_frame = getGlobalAnimSyncFrame();
8837 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8838 sync_frame = GfxFrame[x][y];
8840 sync_frame = 0; // playfield border (pseudo steel)
8842 SetRandomAnimationValue(x, y);
8844 int frame = getAnimationFrame(g->anim_frames,
8847 g->anim_start_frame,
8850 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8851 g->double_movement && is_backside);
8853 // (updating the "crumbled" graphic definitions is probably not really needed,
8854 // as animations for crumbled graphics can't be longer than one EMC cycle)
8855 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8859 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8860 int player_nr, int anim, int frame_em)
8862 int element = player_mapping[player_nr][anim].element_rnd;
8863 int action = player_mapping[player_nr][anim].action;
8864 int direction = player_mapping[player_nr][anim].direction;
8865 int graphic = (direction == MV_NONE ?
8866 el_act2img(element, action) :
8867 el_act_dir2img(element, action, direction));
8868 struct GraphicInfo *g = &graphic_info[graphic];
8871 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8873 stored_player[player_nr].StepFrame = frame_em;
8875 sync_frame = stored_player[player_nr].Frame;
8877 int frame = getAnimationFrame(g->anim_frames,
8880 g->anim_start_frame,
8883 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8884 &g_em->src_x, &g_em->src_y, FALSE);
8887 void InitGraphicInfo_EM(void)
8891 // always start with reliable default values
8892 for (i = 0; i < GAME_TILE_MAX; i++)
8894 object_mapping[i].element_rnd = EL_UNKNOWN;
8895 object_mapping[i].is_backside = FALSE;
8896 object_mapping[i].action = ACTION_DEFAULT;
8897 object_mapping[i].direction = MV_NONE;
8900 // always start with reliable default values
8901 for (p = 0; p < MAX_PLAYERS; p++)
8903 for (i = 0; i < PLY_MAX; i++)
8905 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8906 player_mapping[p][i].action = ACTION_DEFAULT;
8907 player_mapping[p][i].direction = MV_NONE;
8911 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8913 int e = em_object_mapping_list[i].element_em;
8915 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8916 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8918 if (em_object_mapping_list[i].action != -1)
8919 object_mapping[e].action = em_object_mapping_list[i].action;
8921 if (em_object_mapping_list[i].direction != -1)
8922 object_mapping[e].direction =
8923 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8926 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8928 int a = em_player_mapping_list[i].action_em;
8929 int p = em_player_mapping_list[i].player_nr;
8931 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8933 if (em_player_mapping_list[i].action != -1)
8934 player_mapping[p][a].action = em_player_mapping_list[i].action;
8936 if (em_player_mapping_list[i].direction != -1)
8937 player_mapping[p][a].direction =
8938 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8941 for (i = 0; i < GAME_TILE_MAX; i++)
8943 int element = object_mapping[i].element_rnd;
8944 int action = object_mapping[i].action;
8945 int direction = object_mapping[i].direction;
8946 boolean is_backside = object_mapping[i].is_backside;
8947 boolean action_exploding = ((action == ACTION_EXPLODING ||
8948 action == ACTION_SMASHED_BY_ROCK ||
8949 action == ACTION_SMASHED_BY_SPRING) &&
8950 element != EL_DIAMOND);
8951 boolean action_active = (action == ACTION_ACTIVE);
8952 boolean action_other = (action == ACTION_OTHER);
8954 for (j = 0; j < 8; j++)
8956 int effective_element = get_effective_element_EM(i, j);
8957 int effective_action = (j < 7 ? action :
8958 i == Xdrip_stretch ? action :
8959 i == Xdrip_stretchB ? action :
8960 i == Ydrip_1_s ? action :
8961 i == Ydrip_1_sB ? action :
8962 i == Yball_1 ? action :
8963 i == Xball_2 ? action :
8964 i == Yball_2 ? action :
8965 i == Yball_blank ? action :
8966 i == Ykey_1_blank ? action :
8967 i == Ykey_2_blank ? action :
8968 i == Ykey_3_blank ? action :
8969 i == Ykey_4_blank ? action :
8970 i == Ykey_5_blank ? action :
8971 i == Ykey_6_blank ? action :
8972 i == Ykey_7_blank ? action :
8973 i == Ykey_8_blank ? action :
8974 i == Ylenses_blank ? action :
8975 i == Ymagnify_blank ? action :
8976 i == Ygrass_blank ? action :
8977 i == Ydirt_blank ? action :
8978 i == Xsand_stonein_1 ? action :
8979 i == Xsand_stonein_2 ? action :
8980 i == Xsand_stonein_3 ? action :
8981 i == Xsand_stonein_4 ? action :
8982 i == Xsand_stoneout_1 ? action :
8983 i == Xsand_stoneout_2 ? action :
8984 i == Xboom_android ? ACTION_EXPLODING :
8985 action_exploding ? ACTION_EXPLODING :
8986 action_active ? action :
8987 action_other ? action :
8989 int graphic = (el_act_dir2img(effective_element, effective_action,
8991 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8993 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8994 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8995 boolean has_action_graphics = (graphic != base_graphic);
8996 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8997 struct GraphicInfo *g = &graphic_info[graphic];
8998 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9001 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9002 boolean special_animation = (action != ACTION_DEFAULT &&
9003 g->anim_frames == 3 &&
9004 g->anim_delay == 2 &&
9005 g->anim_mode & ANIM_LINEAR);
9006 int sync_frame = (i == Xdrip_stretch ? 7 :
9007 i == Xdrip_stretchB ? 7 :
9008 i == Ydrip_2_s ? j + 8 :
9009 i == Ydrip_2_sB ? j + 8 :
9018 i == Xfake_acid_1 ? 0 :
9019 i == Xfake_acid_2 ? 10 :
9020 i == Xfake_acid_3 ? 20 :
9021 i == Xfake_acid_4 ? 30 :
9022 i == Xfake_acid_5 ? 40 :
9023 i == Xfake_acid_6 ? 50 :
9024 i == Xfake_acid_7 ? 60 :
9025 i == Xfake_acid_8 ? 70 :
9026 i == Xfake_acid_1_player ? 0 :
9027 i == Xfake_acid_2_player ? 10 :
9028 i == Xfake_acid_3_player ? 20 :
9029 i == Xfake_acid_4_player ? 30 :
9030 i == Xfake_acid_5_player ? 40 :
9031 i == Xfake_acid_6_player ? 50 :
9032 i == Xfake_acid_7_player ? 60 :
9033 i == Xfake_acid_8_player ? 70 :
9035 i == Yball_2 ? j + 8 :
9036 i == Yball_blank ? j + 1 :
9037 i == Ykey_1_blank ? j + 1 :
9038 i == Ykey_2_blank ? j + 1 :
9039 i == Ykey_3_blank ? j + 1 :
9040 i == Ykey_4_blank ? j + 1 :
9041 i == Ykey_5_blank ? j + 1 :
9042 i == Ykey_6_blank ? j + 1 :
9043 i == Ykey_7_blank ? j + 1 :
9044 i == Ykey_8_blank ? j + 1 :
9045 i == Ylenses_blank ? j + 1 :
9046 i == Ymagnify_blank ? j + 1 :
9047 i == Ygrass_blank ? j + 1 :
9048 i == Ydirt_blank ? j + 1 :
9049 i == Xamoeba_1 ? 0 :
9050 i == Xamoeba_2 ? 1 :
9051 i == Xamoeba_3 ? 2 :
9052 i == Xamoeba_4 ? 3 :
9053 i == Xamoeba_5 ? 0 :
9054 i == Xamoeba_6 ? 1 :
9055 i == Xamoeba_7 ? 2 :
9056 i == Xamoeba_8 ? 3 :
9057 i == Xexit_2 ? j + 8 :
9058 i == Xexit_3 ? j + 16 :
9059 i == Xdynamite_1 ? 0 :
9060 i == Xdynamite_2 ? 8 :
9061 i == Xdynamite_3 ? 16 :
9062 i == Xdynamite_4 ? 24 :
9063 i == Xsand_stonein_1 ? j + 1 :
9064 i == Xsand_stonein_2 ? j + 9 :
9065 i == Xsand_stonein_3 ? j + 17 :
9066 i == Xsand_stonein_4 ? j + 25 :
9067 i == Xsand_stoneout_1 && j == 0 ? 0 :
9068 i == Xsand_stoneout_1 && j == 1 ? 0 :
9069 i == Xsand_stoneout_1 && j == 2 ? 1 :
9070 i == Xsand_stoneout_1 && j == 3 ? 2 :
9071 i == Xsand_stoneout_1 && j == 4 ? 2 :
9072 i == Xsand_stoneout_1 && j == 5 ? 3 :
9073 i == Xsand_stoneout_1 && j == 6 ? 4 :
9074 i == Xsand_stoneout_1 && j == 7 ? 4 :
9075 i == Xsand_stoneout_2 && j == 0 ? 5 :
9076 i == Xsand_stoneout_2 && j == 1 ? 6 :
9077 i == Xsand_stoneout_2 && j == 2 ? 7 :
9078 i == Xsand_stoneout_2 && j == 3 ? 8 :
9079 i == Xsand_stoneout_2 && j == 4 ? 9 :
9080 i == Xsand_stoneout_2 && j == 5 ? 11 :
9081 i == Xsand_stoneout_2 && j == 6 ? 13 :
9082 i == Xsand_stoneout_2 && j == 7 ? 15 :
9083 i == Xboom_bug && j == 1 ? 2 :
9084 i == Xboom_bug && j == 2 ? 2 :
9085 i == Xboom_bug && j == 3 ? 4 :
9086 i == Xboom_bug && j == 4 ? 4 :
9087 i == Xboom_bug && j == 5 ? 2 :
9088 i == Xboom_bug && j == 6 ? 2 :
9089 i == Xboom_bug && j == 7 ? 0 :
9090 i == Xboom_tank && j == 1 ? 2 :
9091 i == Xboom_tank && j == 2 ? 2 :
9092 i == Xboom_tank && j == 3 ? 4 :
9093 i == Xboom_tank && j == 4 ? 4 :
9094 i == Xboom_tank && j == 5 ? 2 :
9095 i == Xboom_tank && j == 6 ? 2 :
9096 i == Xboom_tank && j == 7 ? 0 :
9097 i == Xboom_android && j == 7 ? 6 :
9098 i == Xboom_1 && j == 1 ? 2 :
9099 i == Xboom_1 && j == 2 ? 2 :
9100 i == Xboom_1 && j == 3 ? 4 :
9101 i == Xboom_1 && j == 4 ? 4 :
9102 i == Xboom_1 && j == 5 ? 6 :
9103 i == Xboom_1 && j == 6 ? 6 :
9104 i == Xboom_1 && j == 7 ? 8 :
9105 i == Xboom_2 && j == 0 ? 8 :
9106 i == Xboom_2 && j == 1 ? 8 :
9107 i == Xboom_2 && j == 2 ? 10 :
9108 i == Xboom_2 && j == 3 ? 10 :
9109 i == Xboom_2 && j == 4 ? 10 :
9110 i == Xboom_2 && j == 5 ? 12 :
9111 i == Xboom_2 && j == 6 ? 12 :
9112 i == Xboom_2 && j == 7 ? 12 :
9113 special_animation && j == 4 ? 3 :
9114 effective_action != action ? 0 :
9116 int frame = getAnimationFrame(g->anim_frames,
9119 g->anim_start_frame,
9122 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9123 g->double_movement && is_backside);
9125 g_em->bitmap = src_bitmap;
9126 g_em->src_x = src_x;
9127 g_em->src_y = src_y;
9128 g_em->src_offset_x = 0;
9129 g_em->src_offset_y = 0;
9130 g_em->dst_offset_x = 0;
9131 g_em->dst_offset_y = 0;
9132 g_em->width = TILEX;
9133 g_em->height = TILEY;
9135 g_em->preserve_background = FALSE;
9137 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9140 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9141 effective_action == ACTION_MOVING ||
9142 effective_action == ACTION_PUSHING ||
9143 effective_action == ACTION_EATING)) ||
9144 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9145 effective_action == ACTION_EMPTYING)))
9148 (effective_action == ACTION_FALLING ||
9149 effective_action == ACTION_FILLING ||
9150 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9151 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9152 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9153 int num_steps = (i == Ydrip_1_s ? 16 :
9154 i == Ydrip_1_sB ? 16 :
9155 i == Ydrip_2_s ? 16 :
9156 i == Ydrip_2_sB ? 16 :
9157 i == Xsand_stonein_1 ? 32 :
9158 i == Xsand_stonein_2 ? 32 :
9159 i == Xsand_stonein_3 ? 32 :
9160 i == Xsand_stonein_4 ? 32 :
9161 i == Xsand_stoneout_1 ? 16 :
9162 i == Xsand_stoneout_2 ? 16 : 8);
9163 int cx = ABS(dx) * (TILEX / num_steps);
9164 int cy = ABS(dy) * (TILEY / num_steps);
9165 int step_frame = (i == Ydrip_2_s ? j + 8 :
9166 i == Ydrip_2_sB ? j + 8 :
9167 i == Xsand_stonein_2 ? j + 8 :
9168 i == Xsand_stonein_3 ? j + 16 :
9169 i == Xsand_stonein_4 ? j + 24 :
9170 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9171 int step = (is_backside ? step_frame : num_steps - step_frame);
9173 if (is_backside) // tile where movement starts
9175 if (dx < 0 || dy < 0)
9177 g_em->src_offset_x = cx * step;
9178 g_em->src_offset_y = cy * step;
9182 g_em->dst_offset_x = cx * step;
9183 g_em->dst_offset_y = cy * step;
9186 else // tile where movement ends
9188 if (dx < 0 || dy < 0)
9190 g_em->dst_offset_x = cx * step;
9191 g_em->dst_offset_y = cy * step;
9195 g_em->src_offset_x = cx * step;
9196 g_em->src_offset_y = cy * step;
9200 g_em->width = TILEX - cx * step;
9201 g_em->height = TILEY - cy * step;
9204 // create unique graphic identifier to decide if tile must be redrawn
9205 /* bit 31 - 16 (16 bit): EM style graphic
9206 bit 15 - 12 ( 4 bit): EM style frame
9207 bit 11 - 6 ( 6 bit): graphic width
9208 bit 5 - 0 ( 6 bit): graphic height */
9209 g_em->unique_identifier =
9210 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9214 for (i = 0; i < GAME_TILE_MAX; i++)
9216 for (j = 0; j < 8; j++)
9218 int element = object_mapping[i].element_rnd;
9219 int action = object_mapping[i].action;
9220 int direction = object_mapping[i].direction;
9221 boolean is_backside = object_mapping[i].is_backside;
9222 int graphic_action = el_act_dir2img(element, action, direction);
9223 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9225 if ((action == ACTION_SMASHED_BY_ROCK ||
9226 action == ACTION_SMASHED_BY_SPRING ||
9227 action == ACTION_EATING) &&
9228 graphic_action == graphic_default)
9230 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9231 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9232 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9233 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9236 // no separate animation for "smashed by rock" -- use rock instead
9237 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9238 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9240 g_em->bitmap = g_xx->bitmap;
9241 g_em->src_x = g_xx->src_x;
9242 g_em->src_y = g_xx->src_y;
9243 g_em->src_offset_x = g_xx->src_offset_x;
9244 g_em->src_offset_y = g_xx->src_offset_y;
9245 g_em->dst_offset_x = g_xx->dst_offset_x;
9246 g_em->dst_offset_y = g_xx->dst_offset_y;
9247 g_em->width = g_xx->width;
9248 g_em->height = g_xx->height;
9249 g_em->unique_identifier = g_xx->unique_identifier;
9252 g_em->preserve_background = TRUE;
9257 for (p = 0; p < MAX_PLAYERS; p++)
9259 for (i = 0; i < PLY_MAX; i++)
9261 int element = player_mapping[p][i].element_rnd;
9262 int action = player_mapping[p][i].action;
9263 int direction = player_mapping[p][i].direction;
9265 for (j = 0; j < 8; j++)
9267 int effective_element = element;
9268 int effective_action = action;
9269 int graphic = (direction == MV_NONE ?
9270 el_act2img(effective_element, effective_action) :
9271 el_act_dir2img(effective_element, effective_action,
9273 struct GraphicInfo *g = &graphic_info[graphic];
9274 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9278 int frame = getAnimationFrame(g->anim_frames,
9281 g->anim_start_frame,
9284 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9286 g_em->bitmap = src_bitmap;
9287 g_em->src_x = src_x;
9288 g_em->src_y = src_y;
9289 g_em->src_offset_x = 0;
9290 g_em->src_offset_y = 0;
9291 g_em->dst_offset_x = 0;
9292 g_em->dst_offset_y = 0;
9293 g_em->width = TILEX;
9294 g_em->height = TILEY;
9300 static void CheckSaveEngineSnapshot_EM(int frame,
9301 boolean any_player_moving,
9302 boolean any_player_snapping,
9303 boolean any_player_dropping)
9305 if (frame == 7 && !any_player_dropping)
9307 if (!local_player->was_waiting)
9309 if (!CheckSaveEngineSnapshotToList())
9312 local_player->was_waiting = TRUE;
9315 else if (any_player_moving || any_player_snapping || any_player_dropping)
9317 local_player->was_waiting = FALSE;
9321 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9322 boolean murphy_is_dropping)
9324 if (murphy_is_waiting)
9326 if (!local_player->was_waiting)
9328 if (!CheckSaveEngineSnapshotToList())
9331 local_player->was_waiting = TRUE;
9336 local_player->was_waiting = FALSE;
9340 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9341 boolean button_released)
9343 if (button_released)
9345 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9346 CheckSaveEngineSnapshotToList();
9348 else if (element_clicked)
9350 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9351 CheckSaveEngineSnapshotToList();
9353 game.snapshot.changed_action = TRUE;
9357 boolean CheckSingleStepMode_EM(int frame,
9358 boolean any_player_moving,
9359 boolean any_player_snapping,
9360 boolean any_player_dropping)
9362 if (tape.single_step && tape.recording && !tape.pausing)
9363 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9364 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9366 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9367 any_player_snapping, any_player_dropping);
9369 return tape.pausing;
9372 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9373 boolean murphy_is_dropping)
9375 boolean murphy_starts_dropping = FALSE;
9378 for (i = 0; i < MAX_PLAYERS; i++)
9379 if (stored_player[i].force_dropping)
9380 murphy_starts_dropping = TRUE;
9382 if (tape.single_step && tape.recording && !tape.pausing)
9383 if (murphy_is_waiting && !murphy_starts_dropping)
9384 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9386 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9389 void CheckSingleStepMode_MM(boolean element_clicked,
9390 boolean button_released)
9392 if (tape.single_step && tape.recording && !tape.pausing)
9393 if (button_released)
9394 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9396 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9399 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9400 int graphic, int sync_frame)
9402 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9404 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9407 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9409 return (IS_NEXT_FRAME(sync_frame, graphic));
9412 int getGraphicInfo_Delay(int graphic)
9414 return graphic_info[graphic].anim_delay;
9417 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9419 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9422 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9428 void PlayMenuSoundExt(int sound)
9430 if (sound == SND_UNDEFINED)
9433 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9434 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9437 if (IS_LOOP_SOUND(sound))
9438 PlaySoundLoop(sound);
9443 void PlayMenuSound(void)
9445 PlayMenuSoundExt(menu.sound[game_status]);
9448 void PlayMenuSoundStereo(int sound, int stereo_position)
9450 if (sound == SND_UNDEFINED)
9453 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9454 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9457 if (IS_LOOP_SOUND(sound))
9458 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9460 PlaySoundStereo(sound, stereo_position);
9463 void PlayMenuSoundIfLoopExt(int sound)
9465 if (sound == SND_UNDEFINED)
9468 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9469 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9472 if (IS_LOOP_SOUND(sound))
9473 PlaySoundLoop(sound);
9476 void PlayMenuSoundIfLoop(void)
9478 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9481 void PlayMenuMusicExt(int music)
9483 if (music == MUS_UNDEFINED)
9486 if (!setup.sound_music)
9489 if (IS_LOOP_MUSIC(music))
9490 PlayMusicLoop(music);
9495 void PlayMenuMusic(void)
9497 char *curr_music = getCurrentlyPlayingMusicFilename();
9498 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9500 if (!strEqual(curr_music, next_music))
9501 PlayMenuMusicExt(menu.music[game_status]);
9504 void PlayMenuSoundsAndMusic(void)
9510 static void FadeMenuSounds(void)
9515 static void FadeMenuMusic(void)
9517 char *curr_music = getCurrentlyPlayingMusicFilename();
9518 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9520 if (!strEqual(curr_music, next_music))
9524 void FadeMenuSoundsAndMusic(void)
9530 void PlaySoundActivating(void)
9533 PlaySound(SND_MENU_ITEM_ACTIVATING);
9537 void PlaySoundSelecting(void)
9540 PlaySound(SND_MENU_ITEM_SELECTING);
9544 void ToggleFullscreenIfNeeded(void)
9546 // if setup and video fullscreen state are already matching, nothing do do
9547 if (setup.fullscreen == video.fullscreen_enabled ||
9548 !video.fullscreen_available)
9551 SDLSetWindowFullscreen(setup.fullscreen);
9553 // set setup value according to successfully changed fullscreen mode
9554 setup.fullscreen = video.fullscreen_enabled;
9557 void ChangeWindowScalingIfNeeded(void)
9559 // if setup and video window scaling are already matching, nothing do do
9560 if (setup.window_scaling_percent == video.window_scaling_percent ||
9561 video.fullscreen_enabled)
9564 SDLSetWindowScaling(setup.window_scaling_percent);
9566 // set setup value according to successfully changed window scaling
9567 setup.window_scaling_percent = video.window_scaling_percent;
9570 void ChangeVsyncModeIfNeeded(void)
9572 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9573 int video_vsync_mode = video.vsync_mode;
9575 // if setup and video vsync mode are already matching, nothing do do
9576 if (setup_vsync_mode == video_vsync_mode)
9579 // if renderer is using OpenGL, vsync mode can directly be changed
9580 SDLSetScreenVsyncMode(setup.vsync_mode);
9582 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9583 if (video.vsync_mode == video_vsync_mode)
9585 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9587 // save backbuffer content which gets lost when re-creating screen
9588 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9590 // force re-creating screen and renderer to set new vsync mode
9591 video.fullscreen_enabled = !setup.fullscreen;
9593 // when creating new renderer, destroy textures linked to old renderer
9594 FreeAllImageTextures(); // needs old renderer to free the textures
9596 // re-create screen and renderer (including change of vsync mode)
9597 ChangeVideoModeIfNeeded(setup.fullscreen);
9599 // set setup value according to successfully changed fullscreen mode
9600 setup.fullscreen = video.fullscreen_enabled;
9602 // restore backbuffer content from temporary backbuffer backup bitmap
9603 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9604 FreeBitmap(tmp_backbuffer);
9606 // update visible window/screen
9607 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9609 // when changing vsync mode, re-create textures for new renderer
9610 InitImageTextures();
9613 // set setup value according to successfully changed vsync mode
9614 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9617 static void JoinRectangles(int *x, int *y, int *width, int *height,
9618 int x2, int y2, int width2, int height2)
9620 // do not join with "off-screen" rectangle
9621 if (x2 == -1 || y2 == -1)
9626 *width = MAX(*width, width2);
9627 *height = MAX(*height, height2);
9630 void SetAnimStatus(int anim_status_new)
9632 if (anim_status_new == GAME_MODE_MAIN)
9633 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9634 else if (anim_status_new == GAME_MODE_NAMES)
9635 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9636 else if (anim_status_new == GAME_MODE_SCORES)
9637 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9639 global.anim_status_next = anim_status_new;
9641 // directly set screen modes that are entered without fading
9642 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9643 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9644 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9645 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9646 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9647 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9648 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9649 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9650 global.anim_status = global.anim_status_next;
9653 void SetGameStatus(int game_status_new)
9655 if (game_status_new != game_status)
9656 game_status_last_screen = game_status;
9658 game_status = game_status_new;
9660 SetAnimStatus(game_status_new);
9663 void SetFontStatus(int game_status_new)
9665 static int last_game_status = -1;
9667 if (game_status_new != -1)
9669 // set game status for font use after storing last game status
9670 last_game_status = game_status;
9671 game_status = game_status_new;
9675 // reset game status after font use from last stored game status
9676 game_status = last_game_status;
9680 void ResetFontStatus(void)
9685 void SetLevelSetInfo(char *identifier, int level_nr)
9687 setString(&levelset.identifier, identifier);
9689 levelset.level_nr = level_nr;
9692 boolean CheckIfAllViewportsHaveChanged(void)
9694 // if game status has not changed, viewports have not changed either
9695 if (game_status == game_status_last)
9698 // check if all viewports have changed with current game status
9700 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9701 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9702 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9703 int new_real_sx = vp_playfield->x;
9704 int new_real_sy = vp_playfield->y;
9705 int new_full_sxsize = vp_playfield->width;
9706 int new_full_sysize = vp_playfield->height;
9707 int new_dx = vp_door_1->x;
9708 int new_dy = vp_door_1->y;
9709 int new_dxsize = vp_door_1->width;
9710 int new_dysize = vp_door_1->height;
9711 int new_vx = vp_door_2->x;
9712 int new_vy = vp_door_2->y;
9713 int new_vxsize = vp_door_2->width;
9714 int new_vysize = vp_door_2->height;
9716 boolean playfield_viewport_has_changed =
9717 (new_real_sx != REAL_SX ||
9718 new_real_sy != REAL_SY ||
9719 new_full_sxsize != FULL_SXSIZE ||
9720 new_full_sysize != FULL_SYSIZE);
9722 boolean door_1_viewport_has_changed =
9725 new_dxsize != DXSIZE ||
9726 new_dysize != DYSIZE);
9728 boolean door_2_viewport_has_changed =
9731 new_vxsize != VXSIZE ||
9732 new_vysize != VYSIZE ||
9733 game_status_last == GAME_MODE_EDITOR);
9735 return (playfield_viewport_has_changed &&
9736 door_1_viewport_has_changed &&
9737 door_2_viewport_has_changed);
9740 boolean CheckFadeAll(void)
9742 return (CheckIfGlobalBorderHasChanged() ||
9743 CheckIfAllViewportsHaveChanged());
9746 void ChangeViewportPropertiesIfNeeded(void)
9748 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9749 FALSE : setup.small_game_graphics);
9750 int gfx_game_mode = getGlobalGameStatus(game_status);
9751 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9753 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9754 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9755 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9756 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9757 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9758 int new_win_xsize = vp_window->width;
9759 int new_win_ysize = vp_window->height;
9760 int border_left = vp_playfield->border_left;
9761 int border_right = vp_playfield->border_right;
9762 int border_top = vp_playfield->border_top;
9763 int border_bottom = vp_playfield->border_bottom;
9764 int new_sx = vp_playfield->x + border_left;
9765 int new_sy = vp_playfield->y + border_top;
9766 int new_sxsize = vp_playfield->width - border_left - border_right;
9767 int new_sysize = vp_playfield->height - border_top - border_bottom;
9768 int new_real_sx = vp_playfield->x;
9769 int new_real_sy = vp_playfield->y;
9770 int new_full_sxsize = vp_playfield->width;
9771 int new_full_sysize = vp_playfield->height;
9772 int new_dx = vp_door_1->x;
9773 int new_dy = vp_door_1->y;
9774 int new_dxsize = vp_door_1->width;
9775 int new_dysize = vp_door_1->height;
9776 int new_vx = vp_door_2->x;
9777 int new_vy = vp_door_2->y;
9778 int new_vxsize = vp_door_2->width;
9779 int new_vysize = vp_door_2->height;
9780 int new_ex = vp_door_3->x;
9781 int new_ey = vp_door_3->y;
9782 int new_exsize = vp_door_3->width;
9783 int new_eysize = vp_door_3->height;
9784 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9785 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9786 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9787 int new_scr_fieldx = new_sxsize / tilesize;
9788 int new_scr_fieldy = new_sysize / tilesize;
9789 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9790 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9791 boolean init_gfx_buffers = FALSE;
9792 boolean init_video_buffer = FALSE;
9793 boolean init_gadgets_and_anims = FALSE;
9794 boolean init_em_graphics = FALSE;
9796 if (new_win_xsize != WIN_XSIZE ||
9797 new_win_ysize != WIN_YSIZE)
9799 WIN_XSIZE = new_win_xsize;
9800 WIN_YSIZE = new_win_ysize;
9802 init_video_buffer = TRUE;
9803 init_gfx_buffers = TRUE;
9804 init_gadgets_and_anims = TRUE;
9806 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9809 if (new_scr_fieldx != SCR_FIELDX ||
9810 new_scr_fieldy != SCR_FIELDY)
9812 // this always toggles between MAIN and GAME when using small tile size
9814 SCR_FIELDX = new_scr_fieldx;
9815 SCR_FIELDY = new_scr_fieldy;
9817 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9828 new_sxsize != SXSIZE ||
9829 new_sysize != SYSIZE ||
9830 new_dxsize != DXSIZE ||
9831 new_dysize != DYSIZE ||
9832 new_vxsize != VXSIZE ||
9833 new_vysize != VYSIZE ||
9834 new_exsize != EXSIZE ||
9835 new_eysize != EYSIZE ||
9836 new_real_sx != REAL_SX ||
9837 new_real_sy != REAL_SY ||
9838 new_full_sxsize != FULL_SXSIZE ||
9839 new_full_sysize != FULL_SYSIZE ||
9840 new_tilesize_var != TILESIZE_VAR
9843 // ------------------------------------------------------------------------
9844 // determine next fading area for changed viewport definitions
9845 // ------------------------------------------------------------------------
9847 // start with current playfield area (default fading area)
9850 FADE_SXSIZE = FULL_SXSIZE;
9851 FADE_SYSIZE = FULL_SYSIZE;
9853 // add new playfield area if position or size has changed
9854 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9855 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9857 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9858 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9861 // add current and new door 1 area if position or size has changed
9862 if (new_dx != DX || new_dy != DY ||
9863 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9865 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9866 DX, DY, DXSIZE, DYSIZE);
9867 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9868 new_dx, new_dy, new_dxsize, new_dysize);
9871 // add current and new door 2 area if position or size has changed
9872 if (new_vx != VX || new_vy != VY ||
9873 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9875 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9876 VX, VY, VXSIZE, VYSIZE);
9877 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9878 new_vx, new_vy, new_vxsize, new_vysize);
9881 // ------------------------------------------------------------------------
9882 // handle changed tile size
9883 // ------------------------------------------------------------------------
9885 if (new_tilesize_var != TILESIZE_VAR)
9887 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9889 // changing tile size invalidates scroll values of engine snapshots
9890 FreeEngineSnapshotSingle();
9892 // changing tile size requires update of graphic mapping for EM engine
9893 init_em_graphics = TRUE;
9904 SXSIZE = new_sxsize;
9905 SYSIZE = new_sysize;
9906 DXSIZE = new_dxsize;
9907 DYSIZE = new_dysize;
9908 VXSIZE = new_vxsize;
9909 VYSIZE = new_vysize;
9910 EXSIZE = new_exsize;
9911 EYSIZE = new_eysize;
9912 REAL_SX = new_real_sx;
9913 REAL_SY = new_real_sy;
9914 FULL_SXSIZE = new_full_sxsize;
9915 FULL_SYSIZE = new_full_sysize;
9916 TILESIZE_VAR = new_tilesize_var;
9918 init_gfx_buffers = TRUE;
9919 init_gadgets_and_anims = TRUE;
9921 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9922 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9925 if (init_gfx_buffers)
9927 // Debug("tools:viewport", "init_gfx_buffers");
9929 SCR_FIELDX = new_scr_fieldx_buffers;
9930 SCR_FIELDY = new_scr_fieldy_buffers;
9934 SCR_FIELDX = new_scr_fieldx;
9935 SCR_FIELDY = new_scr_fieldy;
9937 SetDrawDeactivationMask(REDRAW_NONE);
9938 SetDrawBackgroundMask(REDRAW_FIELD);
9941 if (init_video_buffer)
9943 // Debug("tools:viewport", "init_video_buffer");
9945 FreeAllImageTextures(); // needs old renderer to free the textures
9947 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9948 InitImageTextures();
9951 if (init_gadgets_and_anims)
9953 // Debug("tools:viewport", "init_gadgets_and_anims");
9956 InitGlobalAnimations();
9959 if (init_em_graphics)
9961 InitGraphicInfo_EM();
9965 void OpenURL(char *url)
9967 #if SDL_VERSION_ATLEAST(2,0,14)
9970 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9971 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9972 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9976 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9978 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9982 // ============================================================================
9984 // ============================================================================
9986 #if defined(PLATFORM_WINDOWS)
9987 /* FILETIME of Jan 1 1970 00:00:00. */
9988 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9991 * timezone information is stored outside the kernel so tzp isn't used anymore.
9993 * Note: this function is not for Win32 high precision timing purpose. See
9996 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9999 SYSTEMTIME system_time;
10000 ULARGE_INTEGER ularge;
10002 GetSystemTime(&system_time);
10003 SystemTimeToFileTime(&system_time, &file_time);
10004 ularge.LowPart = file_time.dwLowDateTime;
10005 ularge.HighPart = file_time.dwHighDateTime;
10007 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10008 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10014 static char *test_init_uuid_random_function_simple(void)
10016 static char seed_text[100];
10017 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10019 sprintf(seed_text, "%d", seed);
10024 static char *test_init_uuid_random_function_better(void)
10026 static char seed_text[100];
10027 struct timeval current_time;
10029 gettimeofday(¤t_time, NULL);
10031 prng_seed_bytes(¤t_time, sizeof(current_time));
10033 sprintf(seed_text, "%ld.%ld",
10034 (long)current_time.tv_sec,
10035 (long)current_time.tv_usec);
10040 #if defined(PLATFORM_WINDOWS)
10041 static char *test_init_uuid_random_function_better_windows(void)
10043 static char seed_text[100];
10044 struct timeval current_time;
10046 gettimeofday_windows(¤t_time, NULL);
10048 prng_seed_bytes(¤t_time, sizeof(current_time));
10050 sprintf(seed_text, "%ld.%ld",
10051 (long)current_time.tv_sec,
10052 (long)current_time.tv_usec);
10058 static unsigned int test_uuid_random_function_simple(int max)
10060 return GetSimpleRandom(max);
10063 static unsigned int test_uuid_random_function_better(int max)
10065 return (max > 0 ? prng_get_uint() % max : 0);
10068 #if defined(PLATFORM_WINDOWS)
10069 #define NUM_UUID_TESTS 3
10071 #define NUM_UUID_TESTS 2
10074 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10076 struct hashtable *hash_seeds =
10077 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10078 struct hashtable *hash_uuids =
10079 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10080 static char message[100];
10083 char *random_name = (nr == 0 ? "simple" : "better");
10084 char *random_type = (always_seed ? "always" : "only once");
10085 char *(*init_random_function)(void) =
10087 test_init_uuid_random_function_simple :
10088 test_init_uuid_random_function_better);
10089 unsigned int (*random_function)(int) =
10091 test_uuid_random_function_simple :
10092 test_uuid_random_function_better);
10095 #if defined(PLATFORM_WINDOWS)
10098 random_name = "windows";
10099 init_random_function = test_init_uuid_random_function_better_windows;
10105 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10106 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10108 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10109 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10110 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10112 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10116 // always initialize random number generator at least once
10117 init_random_function();
10119 unsigned int time_start = SDL_GetTicks();
10121 for (i = 0; i < num_uuids; i++)
10125 char *seed = getStringCopy(init_random_function());
10127 hashtable_remove(hash_seeds, seed);
10128 hashtable_insert(hash_seeds, seed, "1");
10131 char *uuid = getStringCopy(getUUIDExt(random_function));
10133 hashtable_remove(hash_uuids, uuid);
10134 hashtable_insert(hash_uuids, uuid, "1");
10137 int num_unique_seeds = hashtable_count(hash_seeds);
10138 int num_unique_uuids = hashtable_count(hash_uuids);
10140 unsigned int time_needed = SDL_GetTicks() - time_start;
10142 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10144 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10147 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10149 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10150 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10152 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10154 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10156 Request(message, REQ_CONFIRM);
10158 hashtable_destroy(hash_seeds, 0);
10159 hashtable_destroy(hash_uuids, 0);
10162 void TestGeneratingUUIDs(void)
10164 int num_uuids = 1000000;
10167 for (i = 0; i < NUM_UUID_TESTS; i++)
10168 for (j = 0; j < 2; j++)
10169 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10171 CloseAllAndExit(0);