1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 // may happen for "border.draw_masked.*" with undefined "global.border.*"
516 if (src_bitmap == NULL)
519 if (x == -1 && y == -1)
522 if (draw_target == DRAW_TO_SCREEN)
523 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
525 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
528 static void DrawMaskedBorderExt_FIELD(int draw_target)
530 if (global.border_status >= GAME_MODE_MAIN &&
531 global.border_status <= GAME_MODE_PLAYING &&
532 border.draw_masked[global.border_status])
533 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
537 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
539 // when drawing to backbuffer, never draw border over open doors
540 if (draw_target == DRAW_TO_BACKBUFFER &&
541 (GetDoorState() & DOOR_OPEN_1))
544 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
545 (global.border_status != GAME_MODE_EDITOR ||
546 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
547 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
550 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
552 // when drawing to backbuffer, never draw border over open doors
553 if (draw_target == DRAW_TO_BACKBUFFER &&
554 (GetDoorState() & DOOR_OPEN_2))
557 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
558 global.border_status != GAME_MODE_EDITOR)
559 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
562 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
564 // currently not available
567 static void DrawMaskedBorderExt_ALL(int draw_target)
569 DrawMaskedBorderExt_FIELD(draw_target);
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 DrawMaskedBorderExt_DOOR_2(draw_target);
572 DrawMaskedBorderExt_DOOR_3(draw_target);
575 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
577 // never draw masked screen borders on borderless screens
578 if (global.border_status == GAME_MODE_LOADING ||
579 global.border_status == GAME_MODE_TITLE)
582 if (redraw_mask & REDRAW_ALL)
583 DrawMaskedBorderExt_ALL(draw_target);
586 if (redraw_mask & REDRAW_FIELD)
587 DrawMaskedBorderExt_FIELD(draw_target);
588 if (redraw_mask & REDRAW_DOOR_1)
589 DrawMaskedBorderExt_DOOR_1(draw_target);
590 if (redraw_mask & REDRAW_DOOR_2)
591 DrawMaskedBorderExt_DOOR_2(draw_target);
592 if (redraw_mask & REDRAW_DOOR_3)
593 DrawMaskedBorderExt_DOOR_3(draw_target);
597 void DrawMaskedBorder_FIELD(void)
599 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
602 void DrawMaskedBorder(int redraw_mask)
604 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
607 void DrawMaskedBorderToTarget(int draw_target)
609 if (draw_target == DRAW_TO_BACKBUFFER ||
610 draw_target == DRAW_TO_SCREEN)
612 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
616 int last_border_status = global.border_status;
618 if (draw_target == DRAW_TO_FADE_SOURCE)
620 global.border_status = gfx.fade_border_source_status;
621 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
623 else if (draw_target == DRAW_TO_FADE_TARGET)
625 global.border_status = gfx.fade_border_target_status;
626 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
629 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
631 global.border_status = last_border_status;
632 gfx.masked_border_bitmap_ptr = backbuffer;
636 void DrawTileCursor(int draw_target)
638 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
641 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
643 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
646 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
648 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
649 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
651 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
654 void BlitScreenToBitmap(Bitmap *target_bitmap)
656 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
657 BlitScreenToBitmap_EM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
659 BlitScreenToBitmap_SP(target_bitmap);
660 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
661 BlitScreenToBitmap_MM(target_bitmap);
662 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
663 BlitScreenToBitmap_RND(target_bitmap);
665 redraw_mask |= REDRAW_FIELD;
668 static void DrawFramesPerSecond(void)
671 int font_nr = FONT_TEXT_2;
672 int font_width = getFontWidth(font_nr);
673 int draw_deactivation_mask = GetDrawDeactivationMask();
674 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
676 // draw FPS with leading space (needed if field buffer deactivated)
677 sprintf(text, " %04.1f fps", global.frames_per_second);
679 // override draw deactivation mask (required for invisible warp mode)
680 SetDrawDeactivationMask(REDRAW_NONE);
682 // draw opaque FPS if field buffer deactivated, else draw masked FPS
683 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
684 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
686 // set draw deactivation mask to previous value
687 SetDrawDeactivationMask(draw_deactivation_mask);
689 // force full-screen redraw in this frame
690 redraw_mask = REDRAW_ALL;
694 static void PrintFrameTimeDebugging(void)
696 static unsigned int last_counter = 0;
697 unsigned int counter = Counter();
698 int diff_1 = counter - last_counter;
699 int diff_2 = diff_1 - GAME_FRAME_DELAY;
701 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
702 char diff_bar[2 * diff_2_max + 5];
706 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
708 for (i = 0; i < diff_2_max; i++)
709 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
710 i >= diff_2_max - diff_2_cut ? '-' : ' ');
712 diff_bar[pos++] = '|';
714 for (i = 0; i < diff_2_max; i++)
715 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
717 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
719 diff_bar[pos++] = '\0';
721 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
724 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
727 last_counter = counter;
731 static int unifiedRedrawMask(int mask)
733 if (mask & REDRAW_ALL)
736 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
742 static boolean equalRedrawMasks(int mask_1, int mask_2)
744 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
747 void BackToFront(void)
749 static int last_redraw_mask = REDRAW_NONE;
751 // force screen redraw in every frame to continue drawing global animations
752 // (but always use the last redraw mask to prevent unwanted side effects)
753 if (redraw_mask == REDRAW_NONE)
754 redraw_mask = last_redraw_mask;
756 last_redraw_mask = redraw_mask;
759 // masked border now drawn immediately when blitting backbuffer to window
761 // draw masked border to all viewports, if defined
762 DrawMaskedBorder(redraw_mask);
765 // draw frames per second (only if debug mode is enabled)
766 if (redraw_mask & REDRAW_FPS)
767 DrawFramesPerSecond();
769 // remove playfield redraw before potentially merging with doors redraw
770 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
771 redraw_mask &= ~REDRAW_FIELD;
773 // redraw complete window if both playfield and (some) doors need redraw
774 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
775 redraw_mask = REDRAW_ALL;
777 /* although redrawing the whole window would be fine for normal gameplay,
778 being able to only redraw the playfield is required for deactivating
779 certain drawing areas (mainly playfield) to work, which is needed for
780 warp-forward to be fast enough (by skipping redraw of most frames) */
782 if (redraw_mask & REDRAW_ALL)
784 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
786 else if (redraw_mask & REDRAW_FIELD)
788 BlitBitmap(backbuffer, window,
789 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
791 else if (redraw_mask & REDRAW_DOORS)
793 // merge door areas to prevent calling screen redraw more than once
799 if (redraw_mask & REDRAW_DOOR_1)
803 x2 = MAX(x2, DX + DXSIZE);
804 y2 = MAX(y2, DY + DYSIZE);
807 if (redraw_mask & REDRAW_DOOR_2)
811 x2 = MAX(x2, VX + VXSIZE);
812 y2 = MAX(y2, VY + VYSIZE);
815 if (redraw_mask & REDRAW_DOOR_3)
819 x2 = MAX(x2, EX + EXSIZE);
820 y2 = MAX(y2, EY + EYSIZE);
823 // make sure that at least one pixel is blitted, and inside the screen
824 // (else nothing is blitted, causing the animations not to be updated)
825 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
826 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
827 x2 = MIN(MAX(1, x2), WIN_XSIZE);
828 y2 = MIN(MAX(1, y2), WIN_YSIZE);
830 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
833 redraw_mask = REDRAW_NONE;
836 PrintFrameTimeDebugging();
840 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
842 unsigned int frame_delay_value_old = GetVideoFrameDelay();
844 SetVideoFrameDelay(frame_delay_value);
848 SetVideoFrameDelay(frame_delay_value_old);
851 static int fade_type_skip = FADE_TYPE_NONE;
853 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
855 void (*draw_border_function)(void) = NULL;
856 int x, y, width, height;
857 int fade_delay, post_delay;
859 if (fade_type == FADE_TYPE_FADE_OUT)
861 if (fade_type_skip != FADE_TYPE_NONE)
863 // skip all fade operations until specified fade operation
864 if (fade_type & fade_type_skip)
865 fade_type_skip = FADE_TYPE_NONE;
870 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
874 redraw_mask |= fade_mask;
876 if (fade_type == FADE_TYPE_SKIP)
878 fade_type_skip = fade_mode;
883 fade_delay = fading.fade_delay;
884 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
886 if (fade_type_skip != FADE_TYPE_NONE)
888 // skip all fade operations until specified fade operation
889 if (fade_type & fade_type_skip)
890 fade_type_skip = FADE_TYPE_NONE;
895 if (global.autoplay_leveldir)
900 if (fade_mask == REDRAW_FIELD)
905 height = FADE_SYSIZE;
907 if (border.draw_masked_when_fading)
908 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
910 DrawMaskedBorder_FIELD(); // draw once
920 // when switching screens without fading, set fade delay to zero
921 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
924 // do not display black frame when fading out without fade delay
925 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
928 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
929 draw_border_function);
931 redraw_mask &= ~fade_mask;
933 ClearAutoRepeatKeyEvents();
936 static void SetScreenStates_BeforeFadingIn(void)
938 // temporarily set screen mode for animations to screen after fading in
939 global.anim_status = global.anim_status_next;
941 // store backbuffer with all animations that will be started after fading in
942 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
944 // set screen mode for animations back to fading
945 global.anim_status = GAME_MODE_PSEUDO_FADING;
948 static void SetScreenStates_AfterFadingIn(void)
950 // store new source screen (to use correct masked border for fading)
951 gfx.fade_border_source_status = global.border_status;
953 global.anim_status = global.anim_status_next;
956 static void SetScreenStates_BeforeFadingOut(void)
958 // store new target screen (to use correct masked border for fading)
959 gfx.fade_border_target_status = game_status;
961 // set screen mode for animations to fading
962 global.anim_status = GAME_MODE_PSEUDO_FADING;
964 // store backbuffer with all animations that will be stopped for fading out
965 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
968 static void SetScreenStates_AfterFadingOut(void)
970 global.border_status = game_status;
973 void FadeIn(int fade_mask)
975 SetScreenStates_BeforeFadingIn();
978 DrawMaskedBorder(REDRAW_ALL);
981 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
982 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
984 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
988 FADE_SXSIZE = FULL_SXSIZE;
989 FADE_SYSIZE = FULL_SYSIZE;
991 // activate virtual buttons depending on upcoming game status
992 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
993 game_status == GAME_MODE_PLAYING && !tape.playing)
994 SetOverlayActive(TRUE);
996 SetScreenStates_AfterFadingIn();
998 // force update of global animation status in case of rapid screen changes
999 redraw_mask = REDRAW_ALL;
1003 void FadeOut(int fade_mask)
1005 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1006 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1007 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1010 SetScreenStates_BeforeFadingOut();
1012 SetTileCursorActive(FALSE);
1013 SetOverlayActive(FALSE);
1016 DrawMaskedBorder(REDRAW_ALL);
1019 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1020 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1022 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1024 SetScreenStates_AfterFadingOut();
1027 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1029 static struct TitleFadingInfo fading_leave_stored;
1032 fading_leave_stored = fading_leave;
1034 fading = fading_leave_stored;
1037 void FadeSetEnterMenu(void)
1039 fading = menu.enter_menu;
1041 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1044 void FadeSetLeaveMenu(void)
1046 fading = menu.leave_menu;
1048 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1051 void FadeSetEnterScreen(void)
1053 fading = menu.enter_screen[game_status];
1055 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1058 void FadeSetNextScreen(void)
1060 fading = menu.next_screen[game_status];
1062 // (do not overwrite fade mode set by FadeSetEnterScreen)
1063 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1066 void FadeSetLeaveScreen(void)
1068 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1071 void FadeSetFromType(int type)
1073 if (type & TYPE_ENTER_SCREEN)
1074 FadeSetEnterScreen();
1075 else if (type & TYPE_ENTER)
1077 else if (type & TYPE_LEAVE)
1081 void FadeSetDisabled(void)
1083 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1085 fading = fading_none;
1088 void FadeSkipNextFadeIn(void)
1090 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1093 void FadeSkipNextFadeOut(void)
1095 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1098 static int getGlobalGameStatus(int status)
1100 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1101 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1105 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1107 if (graphic == IMG_UNDEFINED)
1110 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1112 return (graphic_info[graphic].bitmap != NULL || redefined ?
1113 graphic_info[graphic].bitmap :
1114 graphic_info[default_graphic].bitmap);
1117 static Bitmap *getBackgroundBitmap(int graphic)
1119 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1122 static Bitmap *getGlobalBorderBitmap(int graphic)
1124 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1127 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1129 int status = getGlobalGameStatus(status_raw);
1131 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1132 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1133 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1134 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1137 return getGlobalBorderBitmap(graphic);
1140 void SetWindowBackgroundImageIfDefined(int graphic)
1142 if (graphic_info[graphic].bitmap)
1143 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1146 void SetMainBackgroundImageIfDefined(int graphic)
1148 if (graphic_info[graphic].bitmap)
1149 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1152 void SetDoorBackgroundImageIfDefined(int graphic)
1154 if (graphic_info[graphic].bitmap)
1155 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1158 void SetWindowBackgroundImage(int graphic)
1160 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1163 void SetMainBackgroundImage(int graphic)
1165 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1168 void SetDoorBackgroundImage(int graphic)
1170 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1173 void SetPanelBackground(void)
1175 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1177 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1178 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1180 SetDoorBackgroundBitmap(bitmap_db_panel);
1183 void DrawBackground(int x, int y, int width, int height)
1185 // "drawto" might still point to playfield buffer here (hall of fame)
1186 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1188 if (IN_GFX_FIELD_FULL(x, y))
1189 redraw_mask |= REDRAW_FIELD;
1190 else if (IN_GFX_DOOR_1(x, y))
1191 redraw_mask |= REDRAW_DOOR_1;
1192 else if (IN_GFX_DOOR_2(x, y))
1193 redraw_mask |= REDRAW_DOOR_2;
1194 else if (IN_GFX_DOOR_3(x, y))
1195 redraw_mask |= REDRAW_DOOR_3;
1198 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1200 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1202 if (font->bitmap == NULL)
1205 DrawBackground(x, y, width, height);
1208 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1210 struct GraphicInfo *g = &graphic_info[graphic];
1212 if (g->bitmap == NULL)
1215 DrawBackground(x, y, width, height);
1218 static int game_status_last = -1;
1219 static Bitmap *global_border_bitmap_last = NULL;
1220 static Bitmap *global_border_bitmap = NULL;
1221 static int real_sx_last = -1, real_sy_last = -1;
1222 static int full_sxsize_last = -1, full_sysize_last = -1;
1223 static int dx_last = -1, dy_last = -1;
1224 static int dxsize_last = -1, dysize_last = -1;
1225 static int vx_last = -1, vy_last = -1;
1226 static int vxsize_last = -1, vysize_last = -1;
1227 static int ex_last = -1, ey_last = -1;
1228 static int exsize_last = -1, eysize_last = -1;
1230 boolean CheckIfGlobalBorderHasChanged(void)
1232 // if game status has not changed, global border has not changed either
1233 if (game_status == game_status_last)
1236 // determine and store new global border bitmap for current game status
1237 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1239 return (global_border_bitmap_last != global_border_bitmap);
1242 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1244 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1245 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1247 // if game status has not changed, nothing has to be redrawn
1248 if (game_status == game_status_last)
1251 // redraw if last screen was title screen
1252 if (game_status_last == GAME_MODE_TITLE)
1255 // redraw if global screen border has changed
1256 if (CheckIfGlobalBorderHasChanged())
1259 // redraw if position or size of playfield area has changed
1260 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1261 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1264 // redraw if position or size of door area has changed
1265 if (dx_last != DX || dy_last != DY ||
1266 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1269 // redraw if position or size of tape area has changed
1270 if (vx_last != VX || vy_last != VY ||
1271 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1274 // redraw if position or size of editor area has changed
1275 if (ex_last != EX || ey_last != EY ||
1276 exsize_last != EXSIZE || eysize_last != EYSIZE)
1283 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1286 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1288 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1291 void RedrawGlobalBorder(void)
1293 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1295 RedrawGlobalBorderFromBitmap(bitmap);
1297 redraw_mask = REDRAW_ALL;
1300 static void RedrawGlobalBorderIfNeeded(void)
1302 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1303 if (game_status == game_status_last)
1307 // copy current draw buffer to later copy back areas that have not changed
1308 if (game_status_last != GAME_MODE_TITLE)
1309 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1311 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1312 if (CheckIfGlobalBorderRedrawIsNeeded())
1314 // determine and store new global border bitmap for current game status
1315 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1318 // redraw global screen border (or clear, if defined to be empty)
1319 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1321 if (game_status == GAME_MODE_EDITOR)
1322 DrawSpecialEditorDoor();
1324 // copy previous playfield and door areas, if they are defined on both
1325 // previous and current screen and if they still have the same size
1327 if (real_sx_last != -1 && real_sy_last != -1 &&
1328 REAL_SX != -1 && REAL_SY != -1 &&
1329 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1330 BlitBitmap(bitmap_db_store_1, backbuffer,
1331 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1334 if (dx_last != -1 && dy_last != -1 &&
1335 DX != -1 && DY != -1 &&
1336 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1337 BlitBitmap(bitmap_db_store_1, backbuffer,
1338 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1340 if (game_status != GAME_MODE_EDITOR)
1342 if (vx_last != -1 && vy_last != -1 &&
1343 VX != -1 && VY != -1 &&
1344 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1345 BlitBitmap(bitmap_db_store_1, backbuffer,
1346 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1350 if (ex_last != -1 && ey_last != -1 &&
1351 EX != -1 && EY != -1 &&
1352 exsize_last == EXSIZE && eysize_last == EYSIZE)
1353 BlitBitmap(bitmap_db_store_1, backbuffer,
1354 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1357 redraw_mask = REDRAW_ALL;
1360 game_status_last = game_status;
1362 global_border_bitmap_last = global_border_bitmap;
1364 real_sx_last = REAL_SX;
1365 real_sy_last = REAL_SY;
1366 full_sxsize_last = FULL_SXSIZE;
1367 full_sysize_last = FULL_SYSIZE;
1370 dxsize_last = DXSIZE;
1371 dysize_last = DYSIZE;
1374 vxsize_last = VXSIZE;
1375 vysize_last = VYSIZE;
1378 exsize_last = EXSIZE;
1379 eysize_last = EYSIZE;
1382 void ClearField(void)
1384 RedrawGlobalBorderIfNeeded();
1386 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1387 // (when entering hall of fame after playing)
1388 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1390 // !!! maybe this should be done before clearing the background !!!
1391 if (game_status == GAME_MODE_PLAYING)
1393 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1394 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1398 SetDrawtoField(DRAW_TO_BACKBUFFER);
1402 void MarkTileDirty(int x, int y)
1404 redraw_mask |= REDRAW_FIELD;
1407 void SetBorderElement(void)
1411 BorderElement = EL_EMPTY;
1413 // only the R'n'D game engine may use an additional steelwall border
1414 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1417 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1419 for (x = 0; x < lev_fieldx; x++)
1421 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1422 BorderElement = EL_STEELWALL;
1424 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1430 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1431 int max_array_fieldx, int max_array_fieldy,
1432 short field[max_array_fieldx][max_array_fieldy],
1433 int max_fieldx, int max_fieldy)
1435 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1436 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1437 int old_element = field[start_x][start_y];
1440 // do nothing if start field already has the desired content
1441 if (old_element == fill_element)
1444 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1446 while (stack_pos > 0)
1448 struct XY current = stack_buffer[--stack_pos];
1451 field[current.x][current.y] = fill_element;
1453 for (i = 0; i < 4; i++)
1455 int x = current.x + check[i].x;
1456 int y = current.y + check[i].y;
1458 // check for stack buffer overflow (should not happen)
1459 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1460 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1462 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1463 stack_buffer[stack_pos++] = (struct XY){ x, y };
1468 void FloodFillLevel(int from_x, int from_y, int fill_element,
1469 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1470 int max_fieldx, int max_fieldy)
1472 FloodFillLevelExt(from_x, from_y, fill_element,
1473 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1474 max_fieldx, max_fieldy);
1477 void SetRandomAnimationValue(int x, int y)
1479 gfx.anim_random_frame = GfxRandom[x][y];
1482 int getGraphicAnimationFrame(int graphic, int sync_frame)
1484 // animation synchronized with global frame counter, not move position
1485 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1486 sync_frame = FrameCounter;
1488 return getAnimationFrame(graphic_info[graphic].anim_frames,
1489 graphic_info[graphic].anim_delay,
1490 graphic_info[graphic].anim_mode,
1491 graphic_info[graphic].anim_start_frame,
1495 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1497 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1499 struct GraphicInfo *g = &graphic_info[graphic];
1500 int xsize = MAX(1, g->anim_frames_per_line);
1501 int ysize = MAX(1, g->anim_frames / xsize);
1502 int xoffset = g->anim_start_frame % xsize;
1503 int yoffset = g->anim_start_frame % ysize;
1504 // may be needed if screen field is significantly larger than playfield
1505 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1506 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1507 int sync_frame = y * xsize + x;
1509 return sync_frame % g->anim_frames;
1511 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1513 struct GraphicInfo *g = &graphic_info[graphic];
1514 // may be needed if screen field is significantly larger than playfield
1515 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1516 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1517 int sync_frame = GfxRandomStatic[x][y];
1519 return sync_frame % g->anim_frames;
1523 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1525 return getGraphicAnimationFrame(graphic, sync_frame);
1529 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1531 struct GraphicInfo *g = &graphic_info[graphic];
1532 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1534 if (tilesize == gfx.standard_tile_size)
1535 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1536 else if (tilesize == game.tile_size)
1537 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1539 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1542 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1543 boolean get_backside)
1545 struct GraphicInfo *g = &graphic_info[graphic];
1546 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1547 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1549 if (g->offset_y == 0) // frames are ordered horizontally
1551 int max_width = g->anim_frames_per_line * g->width;
1552 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1554 *x = pos % max_width;
1555 *y = src_y % g->height + pos / max_width * g->height;
1557 else if (g->offset_x == 0) // frames are ordered vertically
1559 int max_height = g->anim_frames_per_line * g->height;
1560 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1562 *x = src_x % g->width + pos / max_height * g->width;
1563 *y = pos % max_height;
1565 else // frames are ordered diagonally
1567 *x = src_x + frame * g->offset_x;
1568 *y = src_y + frame * g->offset_y;
1572 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1573 Bitmap **bitmap, int *x, int *y,
1574 boolean get_backside)
1576 struct GraphicInfo *g = &graphic_info[graphic];
1578 // if no graphics defined at all, use fallback graphics
1579 if (g->bitmaps == NULL)
1580 *g = graphic_info[IMG_CHAR_EXCLAM];
1582 // if no in-game graphics defined, always use standard graphic size
1583 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1584 tilesize = TILESIZE;
1586 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1587 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1589 *x = *x * tilesize / g->tile_size;
1590 *y = *y * tilesize / g->tile_size;
1593 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1594 Bitmap **bitmap, int *x, int *y)
1596 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1599 void getFixedGraphicSource(int graphic, int frame,
1600 Bitmap **bitmap, int *x, int *y)
1602 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1605 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1607 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1610 void getGlobalAnimGraphicSource(int graphic, int frame,
1611 Bitmap **bitmap, int *x, int *y)
1613 struct GraphicInfo *g = &graphic_info[graphic];
1615 // if no graphics defined at all, use fallback graphics
1616 if (g->bitmaps == NULL)
1617 *g = graphic_info[IMG_CHAR_EXCLAM];
1619 // use original size graphics, if existing, else use standard size graphics
1620 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1621 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1623 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1625 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1628 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1629 int *x, int *y, boolean get_backside)
1631 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1635 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1637 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1640 void DrawGraphic(int x, int y, int graphic, int frame)
1643 if (!IN_SCR_FIELD(x, y))
1645 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1646 Debug("draw:DrawGraphic", "This should never happen!");
1652 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1655 MarkTileDirty(x, y);
1658 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1661 if (!IN_SCR_FIELD(x, y))
1663 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1665 Debug("draw:DrawFixedGraphic", "This should never happen!");
1671 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1673 MarkTileDirty(x, y);
1676 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1682 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1684 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1687 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1693 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1694 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1697 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1700 if (!IN_SCR_FIELD(x, y))
1702 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1704 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1710 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1713 MarkTileDirty(x, y);
1716 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1719 if (!IN_SCR_FIELD(x, y))
1721 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1723 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1729 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1731 MarkTileDirty(x, y);
1734 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1740 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1742 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1746 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1747 int graphic, int frame)
1752 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1754 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1758 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1760 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1762 MarkTileDirty(x / tilesize, y / tilesize);
1765 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1768 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1769 graphic, frame, tilesize);
1770 MarkTileDirty(x / tilesize, y / tilesize);
1773 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1779 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1780 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1783 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1784 int frame, int tilesize)
1789 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1790 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1793 void DrawMiniGraphic(int x, int y, int graphic)
1795 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1796 MarkTileDirty(x / 2, y / 2);
1799 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1804 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1805 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1808 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1809 int graphic, int frame,
1810 int cut_mode, int mask_mode)
1815 int width = TILEX, height = TILEY;
1818 if (dx || dy) // shifted graphic
1820 if (x < BX1) // object enters playfield from the left
1827 else if (x > BX2) // object enters playfield from the right
1833 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1839 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1841 else if (dx) // general horizontal movement
1842 MarkTileDirty(x + SIGN(dx), y);
1844 if (y < BY1) // object enters playfield from the top
1846 if (cut_mode == CUT_BELOW) // object completely above top border
1854 else if (y > BY2) // object enters playfield from the bottom
1860 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1866 else if (dy > 0 && cut_mode == CUT_ABOVE)
1868 if (y == BY2) // object completely above bottom border
1874 MarkTileDirty(x, y + 1);
1875 } // object leaves playfield to the bottom
1876 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1878 else if (dy) // general vertical movement
1879 MarkTileDirty(x, y + SIGN(dy));
1883 if (!IN_SCR_FIELD(x, y))
1885 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1887 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1893 width = width * TILESIZE_VAR / TILESIZE;
1894 height = height * TILESIZE_VAR / TILESIZE;
1895 cx = cx * TILESIZE_VAR / TILESIZE;
1896 cy = cy * TILESIZE_VAR / TILESIZE;
1897 dx = dx * TILESIZE_VAR / TILESIZE;
1898 dy = dy * TILESIZE_VAR / TILESIZE;
1900 if (width > 0 && height > 0)
1902 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1907 dst_x = FX + x * TILEX_VAR + dx;
1908 dst_y = FY + y * TILEY_VAR + dy;
1910 if (mask_mode == USE_MASKING)
1911 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1914 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1917 MarkTileDirty(x, y);
1921 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1922 int graphic, int frame,
1923 int cut_mode, int mask_mode)
1928 int width = TILEX_VAR, height = TILEY_VAR;
1931 int x2 = x + SIGN(dx);
1932 int y2 = y + SIGN(dy);
1934 // movement with two-tile animations must be sync'ed with movement position,
1935 // not with current GfxFrame (which can be higher when using slow movement)
1936 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1937 int anim_frames = graphic_info[graphic].anim_frames;
1939 // (we also need anim_delay here for movement animations with less frames)
1940 int anim_delay = graphic_info[graphic].anim_delay;
1941 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1943 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1944 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1946 // re-calculate animation frame for two-tile movement animation
1947 frame = getGraphicAnimationFrame(graphic, sync_frame);
1949 // check if movement start graphic inside screen area and should be drawn
1950 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1952 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1954 dst_x = FX + x1 * TILEX_VAR;
1955 dst_y = FY + y1 * TILEY_VAR;
1957 if (mask_mode == USE_MASKING)
1958 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1961 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1964 MarkTileDirty(x1, y1);
1967 // check if movement end graphic inside screen area and should be drawn
1968 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1970 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1972 dst_x = FX + x2 * TILEX_VAR;
1973 dst_y = FY + y2 * TILEY_VAR;
1975 if (mask_mode == USE_MASKING)
1976 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1979 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1982 MarkTileDirty(x2, y2);
1986 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1987 int graphic, int frame,
1988 int cut_mode, int mask_mode)
1992 DrawGraphic(x, y, graphic, frame);
1997 if (graphic_info[graphic].double_movement) // EM style movement images
1998 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2000 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2003 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2004 int graphic, int frame, int cut_mode)
2006 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2009 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2010 int cut_mode, int mask_mode)
2012 int lx = LEVELX(x), ly = LEVELY(y);
2016 if (IN_LEV_FIELD(lx, ly))
2018 if (element == EL_EMPTY)
2019 element = GfxElementEmpty[lx][ly];
2021 SetRandomAnimationValue(lx, ly);
2023 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2024 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2026 // do not use double (EM style) movement graphic when not moving
2027 if (graphic_info[graphic].double_movement && !dx && !dy)
2029 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2030 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2033 if (game.use_masked_elements && (dx || dy))
2034 mask_mode = USE_MASKING;
2036 else // border element
2038 graphic = el2img(element);
2039 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2042 if (element == EL_EXPANDABLE_WALL)
2044 boolean left_stopped = FALSE, right_stopped = FALSE;
2046 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2047 left_stopped = TRUE;
2048 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2049 right_stopped = TRUE;
2051 if (left_stopped && right_stopped)
2053 else if (left_stopped)
2055 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2056 frame = graphic_info[graphic].anim_frames - 1;
2058 else if (right_stopped)
2060 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2061 frame = graphic_info[graphic].anim_frames - 1;
2066 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2067 else if (mask_mode == USE_MASKING)
2068 DrawGraphicThruMask(x, y, graphic, frame);
2070 DrawGraphic(x, y, graphic, frame);
2073 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2074 int cut_mode, int mask_mode)
2076 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2077 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2078 cut_mode, mask_mode);
2081 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2084 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2087 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2090 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2093 void DrawLevelElementThruMask(int x, int y, int element)
2095 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2098 void DrawLevelFieldThruMask(int x, int y)
2100 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2103 // !!! implementation of quicksand is totally broken !!!
2104 #define IS_CRUMBLED_TILE(x, y, e) \
2105 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2106 !IS_MOVING(x, y) || \
2107 (e) == EL_QUICKSAND_EMPTYING || \
2108 (e) == EL_QUICKSAND_FAST_EMPTYING))
2110 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2115 int width, height, cx, cy;
2116 int sx = SCREENX(x), sy = SCREENY(y);
2117 int crumbled_border_size = graphic_info[graphic].border_size;
2118 int crumbled_tile_size = graphic_info[graphic].tile_size;
2119 int crumbled_border_size_var =
2120 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2123 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2125 for (i = 1; i < 4; i++)
2127 int dxx = (i & 1 ? dx : 0);
2128 int dyy = (i & 2 ? dy : 0);
2131 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2134 // check if neighbour field is of same crumble type
2135 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2136 graphic_info[graphic].class ==
2137 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2139 // return if check prevents inner corner
2140 if (same == (dxx == dx && dyy == dy))
2144 // if we reach this point, we have an inner corner
2146 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2148 width = crumbled_border_size_var;
2149 height = crumbled_border_size_var;
2150 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2151 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2153 if (game.use_masked_elements)
2155 int graphic0 = el2img(EL_EMPTY);
2156 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2157 Bitmap *src_bitmap0;
2160 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2162 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2164 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2166 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2168 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2171 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2173 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2176 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2181 int width, height, bx, by, cx, cy;
2182 int sx = SCREENX(x), sy = SCREENY(y);
2183 int crumbled_border_size = graphic_info[graphic].border_size;
2184 int crumbled_tile_size = graphic_info[graphic].tile_size;
2185 int crumbled_border_size_var =
2186 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2187 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2190 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2192 // only needed when using masked elements
2193 int graphic0 = el2img(EL_EMPTY);
2194 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2195 Bitmap *src_bitmap0;
2198 if (game.use_masked_elements)
2199 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2201 // draw simple, sloppy, non-corner-accurate crumbled border
2203 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2204 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2205 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2206 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2208 if (game.use_masked_elements)
2210 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2212 FX + sx * TILEX_VAR + cx,
2213 FY + sy * TILEY_VAR + cy);
2215 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2217 FX + sx * TILEX_VAR + cx,
2218 FY + sy * TILEY_VAR + cy);
2221 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2223 FX + sx * TILEX_VAR + cx,
2224 FY + sy * TILEY_VAR + cy);
2226 // (remaining middle border part must be at least as big as corner part)
2227 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2228 crumbled_border_size_var >= TILESIZE_VAR / 3)
2231 // correct corners of crumbled border, if needed
2233 for (i = -1; i <= 1; i += 2)
2235 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2236 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2237 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2240 // check if neighbour field is of same crumble type
2241 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2242 graphic_info[graphic].class ==
2243 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2245 // no crumbled corner, but continued crumbled border
2247 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2248 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2249 int b1 = (i == 1 ? crumbled_border_size_var :
2250 TILESIZE_VAR - 2 * crumbled_border_size_var);
2252 width = crumbled_border_size_var;
2253 height = crumbled_border_size_var;
2255 if (dir == 1 || dir == 2)
2270 if (game.use_masked_elements)
2272 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2274 FX + sx * TILEX_VAR + cx,
2275 FY + sy * TILEY_VAR + cy);
2277 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2279 FX + sx * TILEX_VAR + cx,
2280 FY + sy * TILEY_VAR + cy);
2283 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2285 FX + sx * TILEX_VAR + cx,
2286 FY + sy * TILEY_VAR + cy);
2291 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2293 int sx = SCREENX(x), sy = SCREENY(y);
2296 static int xy[4][2] =
2304 if (!IN_LEV_FIELD(x, y))
2307 element = TILE_GFX_ELEMENT(x, y);
2309 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2311 if (!IN_SCR_FIELD(sx, sy))
2314 // crumble field borders towards direct neighbour fields
2315 for (i = 0; i < 4; i++)
2317 int xx = x + xy[i][0];
2318 int yy = y + xy[i][1];
2320 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2323 // check if neighbour field is of same crumble type
2324 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2325 graphic_info[graphic].class ==
2326 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2329 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2332 // crumble inner field corners towards corner neighbour fields
2333 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2334 graphic_info[graphic].anim_frames == 2)
2336 for (i = 0; i < 4; i++)
2338 int dx = (i & 1 ? +1 : -1);
2339 int dy = (i & 2 ? +1 : -1);
2341 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2345 MarkTileDirty(sx, sy);
2347 else // center field is not crumbled -- crumble neighbour fields
2349 // crumble field borders of direct neighbour fields
2350 for (i = 0; i < 4; i++)
2352 int xx = x + xy[i][0];
2353 int yy = y + xy[i][1];
2354 int sxx = sx + xy[i][0];
2355 int syy = sy + xy[i][1];
2357 if (!IN_LEV_FIELD(xx, yy) ||
2358 !IN_SCR_FIELD(sxx, syy))
2361 // do not crumble fields that are being digged or snapped
2362 if (Tile[xx][yy] == EL_EMPTY ||
2363 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2366 element = TILE_GFX_ELEMENT(xx, yy);
2368 if (!IS_CRUMBLED_TILE(xx, yy, element))
2371 graphic = el_act2crm(element, ACTION_DEFAULT);
2373 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2375 MarkTileDirty(sxx, syy);
2378 // crumble inner field corners of corner neighbour fields
2379 for (i = 0; i < 4; i++)
2381 int dx = (i & 1 ? +1 : -1);
2382 int dy = (i & 2 ? +1 : -1);
2388 if (!IN_LEV_FIELD(xx, yy) ||
2389 !IN_SCR_FIELD(sxx, syy))
2392 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2395 element = TILE_GFX_ELEMENT(xx, yy);
2397 if (!IS_CRUMBLED_TILE(xx, yy, element))
2400 graphic = el_act2crm(element, ACTION_DEFAULT);
2402 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2403 graphic_info[graphic].anim_frames == 2)
2404 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2406 MarkTileDirty(sxx, syy);
2411 void DrawLevelFieldCrumbled(int x, int y)
2415 if (!IN_LEV_FIELD(x, y))
2418 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2419 GfxElement[x][y] != EL_UNDEFINED &&
2420 GFX_CRUMBLED(GfxElement[x][y]))
2422 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2427 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2429 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2432 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2435 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2436 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2437 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2438 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2439 int sx = SCREENX(x), sy = SCREENY(y);
2441 DrawScreenGraphic(sx, sy, graphic1, frame1);
2442 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2445 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2447 int sx = SCREENX(x), sy = SCREENY(y);
2448 static int xy[4][2] =
2457 // crumble direct neighbour fields (required for field borders)
2458 for (i = 0; i < 4; i++)
2460 int xx = x + xy[i][0];
2461 int yy = y + xy[i][1];
2462 int sxx = sx + xy[i][0];
2463 int syy = sy + xy[i][1];
2465 if (!IN_LEV_FIELD(xx, yy) ||
2466 !IN_SCR_FIELD(sxx, syy) ||
2467 !GFX_CRUMBLED(Tile[xx][yy]) ||
2471 DrawLevelField(xx, yy);
2474 // crumble corner neighbour fields (required for inner field corners)
2475 for (i = 0; i < 4; i++)
2477 int dx = (i & 1 ? +1 : -1);
2478 int dy = (i & 2 ? +1 : -1);
2484 if (!IN_LEV_FIELD(xx, yy) ||
2485 !IN_SCR_FIELD(sxx, syy) ||
2486 !GFX_CRUMBLED(Tile[xx][yy]) ||
2490 int element = TILE_GFX_ELEMENT(xx, yy);
2491 int graphic = el_act2crm(element, ACTION_DEFAULT);
2493 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2494 graphic_info[graphic].anim_frames == 2)
2495 DrawLevelField(xx, yy);
2499 static int getBorderElement(int x, int y)
2503 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2504 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2505 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2506 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2507 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2508 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2509 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2511 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2512 int steel_position = (x == -1 && y == -1 ? 0 :
2513 x == lev_fieldx && y == -1 ? 1 :
2514 x == -1 && y == lev_fieldy ? 2 :
2515 x == lev_fieldx && y == lev_fieldy ? 3 :
2516 x == -1 || x == lev_fieldx ? 4 :
2517 y == -1 || y == lev_fieldy ? 5 : 6);
2519 return border[steel_position][steel_type];
2522 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2524 if (game.use_masked_elements)
2526 if (graphic != el2img(EL_EMPTY))
2527 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2529 DrawGraphicThruMask(x, y, graphic, frame);
2533 DrawGraphic(x, y, graphic, frame);
2537 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2539 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2542 void DrawScreenElement(int x, int y, int element)
2544 int mask_mode = NO_MASKING;
2546 if (game.use_masked_elements)
2548 int lx = LEVELX(x), ly = LEVELY(y);
2550 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2552 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2554 mask_mode = USE_MASKING;
2558 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2559 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2562 void DrawLevelElement(int x, int y, int element)
2564 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2565 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2568 void DrawScreenField(int x, int y)
2570 int lx = LEVELX(x), ly = LEVELY(y);
2571 int element, content;
2573 if (!IN_LEV_FIELD(lx, ly))
2575 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2578 element = getBorderElement(lx, ly);
2580 DrawScreenElement(x, y, element);
2585 element = Tile[lx][ly];
2586 content = Store[lx][ly];
2588 if (IS_MOVING(lx, ly))
2590 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2591 boolean cut_mode = NO_CUTTING;
2593 if (element == EL_QUICKSAND_EMPTYING ||
2594 element == EL_QUICKSAND_FAST_EMPTYING ||
2595 element == EL_MAGIC_WALL_EMPTYING ||
2596 element == EL_BD_MAGIC_WALL_EMPTYING ||
2597 element == EL_DC_MAGIC_WALL_EMPTYING ||
2598 element == EL_AMOEBA_DROPPING)
2599 cut_mode = CUT_ABOVE;
2600 else if (element == EL_QUICKSAND_FILLING ||
2601 element == EL_QUICKSAND_FAST_FILLING ||
2602 element == EL_MAGIC_WALL_FILLING ||
2603 element == EL_BD_MAGIC_WALL_FILLING ||
2604 element == EL_DC_MAGIC_WALL_FILLING)
2605 cut_mode = CUT_BELOW;
2607 if (cut_mode == CUT_ABOVE)
2608 DrawScreenElement(x, y, element);
2610 DrawScreenElement(x, y, EL_EMPTY);
2612 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2614 int dir = MovDir[lx][ly];
2615 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2616 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2618 if (IN_SCR_FIELD(newx, newy))
2619 DrawScreenElement(newx, newy, EL_EMPTY);
2623 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2624 else if (cut_mode == NO_CUTTING)
2625 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2628 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2630 if (cut_mode == CUT_BELOW &&
2631 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2632 DrawLevelElement(lx, ly + 1, element);
2635 if (content == EL_ACID)
2637 int dir = MovDir[lx][ly];
2638 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2639 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2641 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2643 // prevent target field from being drawn again (but without masking)
2644 // (this would happen if target field is scanned after moving element)
2645 Stop[newlx][newly] = TRUE;
2648 else if (IS_BLOCKED(lx, ly))
2653 boolean cut_mode = NO_CUTTING;
2654 int element_old, content_old;
2656 Blocked2Moving(lx, ly, &oldx, &oldy);
2659 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2660 MovDir[oldx][oldy] == MV_RIGHT);
2662 element_old = Tile[oldx][oldy];
2663 content_old = Store[oldx][oldy];
2665 if (element_old == EL_QUICKSAND_EMPTYING ||
2666 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2667 element_old == EL_MAGIC_WALL_EMPTYING ||
2668 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2669 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2670 element_old == EL_AMOEBA_DROPPING)
2671 cut_mode = CUT_ABOVE;
2673 DrawScreenElement(x, y, EL_EMPTY);
2676 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2678 else if (cut_mode == NO_CUTTING)
2679 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2682 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2685 else if (IS_DRAWABLE(element))
2686 DrawScreenElement(x, y, element);
2688 DrawScreenElement(x, y, EL_EMPTY);
2691 void DrawLevelField(int x, int y)
2693 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2694 DrawScreenField(SCREENX(x), SCREENY(y));
2695 else if (IS_MOVING(x, y))
2699 Moving2Blocked(x, y, &newx, &newy);
2700 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2701 DrawScreenField(SCREENX(newx), SCREENY(newy));
2703 else if (IS_BLOCKED(x, y))
2707 Blocked2Moving(x, y, &oldx, &oldy);
2708 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2709 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2713 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2714 int (*el2img_function)(int), boolean masked,
2715 int element_bits_draw)
2717 int element_base = map_mm_wall_element(element);
2718 int element_bits = (IS_DF_WALL(element) ?
2719 element - EL_DF_WALL_START :
2720 IS_MM_WALL(element) ?
2721 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2722 int graphic = el2img_function(element_base);
2723 int tilesize_draw = tilesize / 2;
2728 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2730 for (i = 0; i < 4; i++)
2732 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2733 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2735 if (!(element_bits_draw & (1 << i)))
2738 if (element_bits & (1 << i))
2741 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2742 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2744 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2745 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2750 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2751 tilesize_draw, tilesize_draw);
2756 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2757 boolean masked, int element_bits_draw)
2759 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2760 element, tilesize, el2edimg, masked, element_bits_draw);
2763 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2764 int (*el2img_function)(int))
2766 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2770 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2773 if (IS_MM_WALL(element))
2775 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2776 element, tilesize, el2edimg, masked, 0x000f);
2780 int graphic = el2edimg(element);
2783 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2785 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2789 void DrawSizedElement(int x, int y, int element, int tilesize)
2791 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2794 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2796 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2799 void DrawMiniElement(int x, int y, int element)
2803 graphic = el2edimg(element);
2804 DrawMiniGraphic(x, y, graphic);
2807 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2810 int x = sx + scroll_x, y = sy + scroll_y;
2812 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2813 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2814 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2815 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2817 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2820 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2822 int x = sx + scroll_x, y = sy + scroll_y;
2824 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2825 DrawMiniElement(sx, sy, EL_EMPTY);
2826 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2827 DrawMiniElement(sx, sy, Tile[x][y]);
2829 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2832 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2833 int x, int y, int xsize, int ysize,
2834 int tile_width, int tile_height)
2838 int dst_x = startx + x * tile_width;
2839 int dst_y = starty + y * tile_height;
2840 int width = graphic_info[graphic].width;
2841 int height = graphic_info[graphic].height;
2842 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2843 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2844 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2845 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2846 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2847 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2848 boolean draw_masked = graphic_info[graphic].draw_masked;
2850 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2852 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2854 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2858 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2859 inner_sx + (x - 1) * tile_width % inner_width);
2860 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2861 inner_sy + (y - 1) * tile_height % inner_height);
2864 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2867 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2871 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2872 int x, int y, int xsize, int ysize,
2875 int font_width = getFontWidth(font_nr);
2876 int font_height = getFontHeight(font_nr);
2878 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2879 font_width, font_height);
2882 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2884 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2885 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2886 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2887 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2888 boolean no_delay = (tape.warp_forward);
2889 unsigned int anim_delay = 0;
2890 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2891 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2892 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2893 int font_width = getFontWidth(font_nr);
2894 int font_height = getFontHeight(font_nr);
2895 int max_xsize = level.envelope[envelope_nr].xsize;
2896 int max_ysize = level.envelope[envelope_nr].ysize;
2897 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2898 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2899 int xend = max_xsize;
2900 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2901 int xstep = (xstart < xend ? 1 : 0);
2902 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2904 int end = MAX(xend - xstart, yend - ystart);
2907 for (i = start; i <= end; i++)
2909 int last_frame = end; // last frame of this "for" loop
2910 int x = xstart + i * xstep;
2911 int y = ystart + i * ystep;
2912 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2913 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2914 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2915 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2918 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2920 BlitScreenToBitmap(backbuffer);
2922 SetDrawtoField(DRAW_TO_BACKBUFFER);
2924 for (yy = 0; yy < ysize; yy++)
2925 for (xx = 0; xx < xsize; xx++)
2926 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2928 DrawTextBuffer(sx + font_width, sy + font_height,
2929 level.envelope[envelope_nr].text, font_nr, max_xsize,
2930 xsize - 2, ysize - 2, 0, mask_mode,
2931 level.envelope[envelope_nr].autowrap,
2932 level.envelope[envelope_nr].centered, FALSE);
2934 redraw_mask |= REDRAW_FIELD;
2937 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2940 ClearAutoRepeatKeyEvents();
2943 void ShowEnvelope(int envelope_nr)
2945 int element = EL_ENVELOPE_1 + envelope_nr;
2946 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2947 int sound_opening = element_info[element].sound[ACTION_OPENING];
2948 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2949 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2950 boolean no_delay = (tape.warp_forward);
2951 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2952 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2953 int anim_mode = graphic_info[graphic].anim_mode;
2954 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2955 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2956 boolean overlay_enabled = GetOverlayEnabled();
2958 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2960 SetOverlayEnabled(FALSE);
2963 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2965 if (anim_mode == ANIM_DEFAULT)
2966 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2968 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2971 Delay_WithScreenUpdates(wait_delay_value);
2973 WaitForEventToContinue();
2976 SetOverlayEnabled(overlay_enabled);
2978 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2980 if (anim_mode != ANIM_NONE)
2981 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2983 if (anim_mode == ANIM_DEFAULT)
2984 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2986 game.envelope_active = FALSE;
2988 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2990 redraw_mask |= REDRAW_FIELD;
2994 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2995 int xsize, int ysize)
2997 if (!global.use_envelope_request ||
2998 request.sort_priority <= 0)
3001 if (request.bitmap == NULL ||
3002 xsize > request.xsize ||
3003 ysize > request.ysize)
3005 if (request.bitmap != NULL)
3006 FreeBitmap(request.bitmap);
3008 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3010 SDL_Surface *surface = request.bitmap->surface;
3012 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3013 Fail("SDLGetNativeSurface() failed");
3016 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3018 SDLFreeBitmapTextures(request.bitmap);
3019 SDLCreateBitmapTextures(request.bitmap);
3021 // set envelope request run-time values
3024 request.xsize = xsize;
3025 request.ysize = ysize;
3028 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3030 if (global.use_envelope_request &&
3031 game.request_active_or_moving &&
3032 request.sort_priority > 0 &&
3033 drawing_target == DRAW_TO_SCREEN &&
3034 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3036 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3037 request.sx, request.sy);
3041 static void setRequestBasePosition(int *x, int *y)
3043 int sx_base, sy_base;
3045 if (request.x != -1)
3046 sx_base = request.x;
3047 else if (request.align == ALIGN_LEFT)
3049 else if (request.align == ALIGN_RIGHT)
3050 sx_base = SX + SXSIZE;
3052 sx_base = SX + SXSIZE / 2;
3054 if (request.y != -1)
3055 sy_base = request.y;
3056 else if (request.valign == VALIGN_TOP)
3058 else if (request.valign == VALIGN_BOTTOM)
3059 sy_base = SY + SYSIZE;
3061 sy_base = SY + SYSIZE / 2;
3067 static void setRequestPositionExt(int *x, int *y, int width, int height,
3068 boolean add_border_size)
3070 int border_size = request.border_size;
3071 int sx_base, sy_base;
3074 setRequestBasePosition(&sx_base, &sy_base);
3076 if (request.align == ALIGN_LEFT)
3078 else if (request.align == ALIGN_RIGHT)
3079 sx = sx_base - width;
3081 sx = sx_base - width / 2;
3083 if (request.valign == VALIGN_TOP)
3085 else if (request.valign == VALIGN_BOTTOM)
3086 sy = sy_base - height;
3088 sy = sy_base - height / 2;
3090 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3091 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3093 if (add_border_size)
3103 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3105 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3108 static void DrawEnvelopeRequest(char *text)
3110 char *text_final = text;
3111 char *text_door_style = NULL;
3112 int graphic = IMG_BACKGROUND_REQUEST;
3113 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3114 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3115 int font_nr = FONT_REQUEST;
3116 int font_width = getFontWidth(font_nr);
3117 int font_height = getFontHeight(font_nr);
3118 int border_size = request.border_size;
3119 int line_spacing = request.line_spacing;
3120 int line_height = font_height + line_spacing;
3121 int max_text_width = request.width - 2 * border_size;
3122 int max_text_height = request.height - 2 * border_size;
3123 int line_length = max_text_width / font_width;
3124 int max_lines = max_text_height / line_height;
3125 int text_width = line_length * font_width;
3126 int width = request.width;
3127 int height = request.height;
3128 int tile_size = MAX(request.step_offset, 1);
3129 int x_steps = width / tile_size;
3130 int y_steps = height / tile_size;
3131 int sx_offset = border_size;
3132 int sy_offset = border_size;
3136 if (request.centered)
3137 sx_offset = (request.width - text_width) / 2;
3139 if (request.wrap_single_words && !request.autowrap)
3141 char *src_text_ptr, *dst_text_ptr;
3143 text_door_style = checked_malloc(2 * strlen(text) + 1);
3145 src_text_ptr = text;
3146 dst_text_ptr = text_door_style;
3148 while (*src_text_ptr)
3150 if (*src_text_ptr == ' ' ||
3151 *src_text_ptr == '?' ||
3152 *src_text_ptr == '!')
3153 *dst_text_ptr++ = '\n';
3155 if (*src_text_ptr != ' ')
3156 *dst_text_ptr++ = *src_text_ptr;
3161 *dst_text_ptr = '\0';
3163 text_final = text_door_style;
3166 setRequestPosition(&sx, &sy, FALSE);
3168 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3170 for (y = 0; y < y_steps; y++)
3171 for (x = 0; x < x_steps; x++)
3172 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3173 x, y, x_steps, y_steps,
3174 tile_size, tile_size);
3176 // force DOOR font inside door area
3177 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3179 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3180 line_length, -1, max_lines, line_spacing, mask_mode,
3181 request.autowrap, request.centered, FALSE);
3185 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3186 RedrawGadget(tool_gadget[i]);
3188 // store readily prepared envelope request for later use when animating
3189 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3191 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3193 if (text_door_style)
3194 free(text_door_style);
3197 static void AnimateEnvelopeRequest(int anim_mode, int action)
3199 int graphic = IMG_BACKGROUND_REQUEST;
3200 boolean draw_masked = graphic_info[graphic].draw_masked;
3201 int delay_value_normal = request.step_delay;
3202 int delay_value_fast = delay_value_normal / 2;
3203 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3204 boolean no_delay = (tape.warp_forward);
3205 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3206 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3207 unsigned int anim_delay = 0;
3209 int tile_size = MAX(request.step_offset, 1);
3210 int max_xsize = request.width / tile_size;
3211 int max_ysize = request.height / tile_size;
3212 int max_xsize_inner = max_xsize - 2;
3213 int max_ysize_inner = max_ysize - 2;
3215 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3216 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3217 int xend = max_xsize_inner;
3218 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3219 int xstep = (xstart < xend ? 1 : 0);
3220 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3222 int end = MAX(xend - xstart, yend - ystart);
3225 if (setup.quick_doors)
3232 for (i = start; i <= end; i++)
3234 int last_frame = end; // last frame of this "for" loop
3235 int x = xstart + i * xstep;
3236 int y = ystart + i * ystep;
3237 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3238 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3239 int xsize_size_left = (xsize - 1) * tile_size;
3240 int ysize_size_top = (ysize - 1) * tile_size;
3241 int max_xsize_pos = (max_xsize - 1) * tile_size;
3242 int max_ysize_pos = (max_ysize - 1) * tile_size;
3243 int width = xsize * tile_size;
3244 int height = ysize * tile_size;
3249 setRequestPosition(&src_x, &src_y, FALSE);
3250 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3252 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3254 for (yy = 0; yy < 2; yy++)
3256 for (xx = 0; xx < 2; xx++)
3258 int src_xx = src_x + xx * max_xsize_pos;
3259 int src_yy = src_y + yy * max_ysize_pos;
3260 int dst_xx = dst_x + xx * xsize_size_left;
3261 int dst_yy = dst_y + yy * ysize_size_top;
3262 int xx_size = (xx ? tile_size : xsize_size_left);
3263 int yy_size = (yy ? tile_size : ysize_size_top);
3266 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3267 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3269 BlitBitmap(bitmap_db_store_2, backbuffer,
3270 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3274 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3276 redraw_mask |= REDRAW_FIELD;
3280 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3283 ClearAutoRepeatKeyEvents();
3286 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3288 int graphic = IMG_BACKGROUND_REQUEST;
3289 int sound_opening = SND_REQUEST_OPENING;
3290 int sound_closing = SND_REQUEST_CLOSING;
3291 int anim_mode_1 = request.anim_mode; // (higher priority)
3292 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3293 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3294 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3295 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3297 if (game_status == GAME_MODE_PLAYING)
3298 BlitScreenToBitmap(backbuffer);
3300 SetDrawtoField(DRAW_TO_BACKBUFFER);
3302 // SetDrawBackgroundMask(REDRAW_NONE);
3304 if (action == ACTION_OPENING)
3306 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3308 if (req_state & REQ_ASK)
3310 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3311 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3312 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3313 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3315 else if (req_state & REQ_CONFIRM)
3317 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3318 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3320 else if (req_state & REQ_PLAYER)
3322 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3323 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3324 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3325 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3328 DrawEnvelopeRequest(text);
3331 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3333 if (action == ACTION_OPENING)
3335 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3337 if (anim_mode == ANIM_DEFAULT)
3338 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3340 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3344 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3346 if (anim_mode != ANIM_NONE)
3347 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3349 if (anim_mode == ANIM_DEFAULT)
3350 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3353 game.envelope_active = FALSE;
3355 if (action == ACTION_CLOSING)
3356 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3358 // SetDrawBackgroundMask(last_draw_background_mask);
3360 redraw_mask |= REDRAW_FIELD;
3364 if (action == ACTION_CLOSING &&
3365 game_status == GAME_MODE_PLAYING &&
3366 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3367 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3370 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3372 if (IS_MM_WALL(element))
3374 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3380 int graphic = el2preimg(element);
3382 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3383 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3388 void DrawLevel(int draw_background_mask)
3392 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3393 SetDrawBackgroundMask(draw_background_mask);
3397 for (x = BX1; x <= BX2; x++)
3398 for (y = BY1; y <= BY2; y++)
3399 DrawScreenField(x, y);
3401 redraw_mask |= REDRAW_FIELD;
3404 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3409 for (x = 0; x < size_x; x++)
3410 for (y = 0; y < size_y; y++)
3411 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3413 redraw_mask |= REDRAW_FIELD;
3416 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3420 for (x = 0; x < size_x; x++)
3421 for (y = 0; y < size_y; y++)
3422 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3424 redraw_mask |= REDRAW_FIELD;
3427 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3429 boolean show_level_border = (BorderElement != EL_EMPTY);
3430 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3431 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3432 int tile_size = preview.tile_size;
3433 int preview_width = preview.xsize * tile_size;
3434 int preview_height = preview.ysize * tile_size;
3435 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3436 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3437 int real_preview_width = real_preview_xsize * tile_size;
3438 int real_preview_height = real_preview_ysize * tile_size;
3439 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3440 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3443 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3446 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3448 dst_x += (preview_width - real_preview_width) / 2;
3449 dst_y += (preview_height - real_preview_height) / 2;
3451 for (x = 0; x < real_preview_xsize; x++)
3453 for (y = 0; y < real_preview_ysize; y++)
3455 int lx = from_x + x + (show_level_border ? -1 : 0);
3456 int ly = from_y + y + (show_level_border ? -1 : 0);
3457 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3458 getBorderElement(lx, ly));
3460 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3461 element, tile_size);
3465 redraw_mask |= REDRAW_FIELD;
3468 #define MICROLABEL_EMPTY 0
3469 #define MICROLABEL_LEVEL_NAME 1
3470 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3471 #define MICROLABEL_LEVEL_AUTHOR 3
3472 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3473 #define MICROLABEL_IMPORTED_FROM 5
3474 #define MICROLABEL_IMPORTED_BY_HEAD 6
3475 #define MICROLABEL_IMPORTED_BY 7
3477 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3479 int max_text_width = SXSIZE;
3480 int font_width = getFontWidth(font_nr);
3482 if (pos->align == ALIGN_CENTER)
3483 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3484 else if (pos->align == ALIGN_RIGHT)
3485 max_text_width = pos->x;
3487 max_text_width = SXSIZE - pos->x;
3489 return max_text_width / font_width;
3492 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3494 char label_text[MAX_OUTPUT_LINESIZE + 1];
3495 int max_len_label_text;
3496 int font_nr = pos->font;
3499 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3502 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3503 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3504 mode == MICROLABEL_IMPORTED_BY_HEAD)
3505 font_nr = pos->font_alt;
3507 max_len_label_text = getMaxTextLength(pos, font_nr);
3509 if (pos->size != -1)
3510 max_len_label_text = pos->size;
3512 for (i = 0; i < max_len_label_text; i++)
3513 label_text[i] = ' ';
3514 label_text[max_len_label_text] = '\0';
3516 if (strlen(label_text) > 0)
3517 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3520 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3521 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3522 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3523 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3524 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3525 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3526 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3527 max_len_label_text);
3528 label_text[max_len_label_text] = '\0';
3530 if (strlen(label_text) > 0)
3531 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3533 redraw_mask |= REDRAW_FIELD;
3536 static void DrawPreviewLevelLabel(int mode)
3538 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3541 static void DrawPreviewLevelInfo(int mode)
3543 if (mode == MICROLABEL_LEVEL_NAME)
3544 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3545 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3546 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3549 static void DrawPreviewLevelExt(boolean restart)
3551 static unsigned int scroll_delay = 0;
3552 static unsigned int label_delay = 0;
3553 static int from_x, from_y, scroll_direction;
3554 static int label_state, label_counter;
3555 unsigned int scroll_delay_value = preview.step_delay;
3556 boolean show_level_border = (BorderElement != EL_EMPTY);
3557 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3558 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3565 if (preview.anim_mode == ANIM_CENTERED)
3567 if (level_xsize > preview.xsize)
3568 from_x = (level_xsize - preview.xsize) / 2;
3569 if (level_ysize > preview.ysize)
3570 from_y = (level_ysize - preview.ysize) / 2;
3573 from_x += preview.xoffset;
3574 from_y += preview.yoffset;
3576 scroll_direction = MV_RIGHT;
3580 DrawPreviewLevelPlayfield(from_x, from_y);
3581 DrawPreviewLevelLabel(label_state);
3583 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3584 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3586 // initialize delay counters
3587 ResetDelayCounter(&scroll_delay);
3588 ResetDelayCounter(&label_delay);
3590 if (leveldir_current->name)
3592 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3593 char label_text[MAX_OUTPUT_LINESIZE + 1];
3594 int font_nr = pos->font;
3595 int max_len_label_text = getMaxTextLength(pos, font_nr);
3597 if (pos->size != -1)
3598 max_len_label_text = pos->size;
3600 strncpy(label_text, leveldir_current->name, max_len_label_text);
3601 label_text[max_len_label_text] = '\0';
3603 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3604 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3610 // scroll preview level, if needed
3611 if (preview.anim_mode != ANIM_NONE &&
3612 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3613 DelayReached(&scroll_delay, scroll_delay_value))
3615 switch (scroll_direction)
3620 from_x -= preview.step_offset;
3621 from_x = (from_x < 0 ? 0 : from_x);
3624 scroll_direction = MV_UP;
3628 if (from_x < level_xsize - preview.xsize)
3630 from_x += preview.step_offset;
3631 from_x = (from_x > level_xsize - preview.xsize ?
3632 level_xsize - preview.xsize : from_x);
3635 scroll_direction = MV_DOWN;
3641 from_y -= preview.step_offset;
3642 from_y = (from_y < 0 ? 0 : from_y);
3645 scroll_direction = MV_RIGHT;
3649 if (from_y < level_ysize - preview.ysize)
3651 from_y += preview.step_offset;
3652 from_y = (from_y > level_ysize - preview.ysize ?
3653 level_ysize - preview.ysize : from_y);
3656 scroll_direction = MV_LEFT;
3663 DrawPreviewLevelPlayfield(from_x, from_y);
3666 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3667 // redraw micro level label, if needed
3668 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3669 !strEqual(level.author, ANONYMOUS_NAME) &&
3670 !strEqual(level.author, leveldir_current->name) &&
3671 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3673 int max_label_counter = 23;
3675 if (leveldir_current->imported_from != NULL &&
3676 strlen(leveldir_current->imported_from) > 0)
3677 max_label_counter += 14;
3678 if (leveldir_current->imported_by != NULL &&
3679 strlen(leveldir_current->imported_by) > 0)
3680 max_label_counter += 14;
3682 label_counter = (label_counter + 1) % max_label_counter;
3683 label_state = (label_counter >= 0 && label_counter <= 7 ?
3684 MICROLABEL_LEVEL_NAME :
3685 label_counter >= 9 && label_counter <= 12 ?
3686 MICROLABEL_LEVEL_AUTHOR_HEAD :
3687 label_counter >= 14 && label_counter <= 21 ?
3688 MICROLABEL_LEVEL_AUTHOR :
3689 label_counter >= 23 && label_counter <= 26 ?
3690 MICROLABEL_IMPORTED_FROM_HEAD :
3691 label_counter >= 28 && label_counter <= 35 ?
3692 MICROLABEL_IMPORTED_FROM :
3693 label_counter >= 37 && label_counter <= 40 ?
3694 MICROLABEL_IMPORTED_BY_HEAD :
3695 label_counter >= 42 && label_counter <= 49 ?
3696 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3698 if (leveldir_current->imported_from == NULL &&
3699 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3700 label_state == MICROLABEL_IMPORTED_FROM))
3701 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3702 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3704 DrawPreviewLevelLabel(label_state);
3708 void DrawPreviewPlayers(void)
3710 if (game_status != GAME_MODE_MAIN)
3713 // do not draw preview players if level preview redefined, but players aren't
3714 if (preview.redefined && !menu.main.preview_players.redefined)
3717 boolean player_found[MAX_PLAYERS];
3718 int num_players = 0;
3721 for (i = 0; i < MAX_PLAYERS; i++)
3722 player_found[i] = FALSE;
3724 // check which players can be found in the level (simple approach)
3725 for (x = 0; x < lev_fieldx; x++)
3727 for (y = 0; y < lev_fieldy; y++)
3729 int element = level.field[x][y];
3731 if (IS_PLAYER_ELEMENT(element))
3733 int player_nr = GET_PLAYER_NR(element);
3735 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3737 if (!player_found[player_nr])
3740 player_found[player_nr] = TRUE;
3745 struct TextPosInfo *pos = &menu.main.preview_players;
3746 int tile_size = pos->tile_size;
3747 int border_size = pos->border_size;
3748 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3749 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3750 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3751 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3752 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3753 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3754 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3755 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3756 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3757 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3758 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3759 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3761 // clear area in which the players will be drawn
3762 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3763 max_players_width, max_players_height);
3765 if (!network.enabled && !setup.team_mode)
3768 // only draw players if level is suited for team mode
3769 if (num_players < 2)
3772 // draw all players that were found in the level
3773 for (i = 0; i < MAX_PLAYERS; i++)
3775 if (player_found[i])
3777 int graphic = el2img(EL_PLAYER_1 + i);
3779 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3781 xpos += player_xoffset;
3782 ypos += player_yoffset;
3787 void DrawPreviewLevelInitial(void)
3789 DrawPreviewLevelExt(TRUE);
3790 DrawPreviewPlayers();
3793 void DrawPreviewLevelAnimation(void)
3795 DrawPreviewLevelExt(FALSE);
3798 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3799 int border_size, int font_nr)
3801 int graphic = el2img(EL_PLAYER_1 + player_nr);
3802 int font_height = getFontHeight(font_nr);
3803 int player_height = MAX(tile_size, font_height);
3804 int xoffset_text = tile_size + border_size;
3805 int yoffset_text = (player_height - font_height) / 2;
3806 int yoffset_graphic = (player_height - tile_size) / 2;
3807 char *player_name = getNetworkPlayerName(player_nr + 1);
3809 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3811 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3814 static void DrawNetworkPlayersExt(boolean force)
3816 if (game_status != GAME_MODE_MAIN)
3819 if (!network.connected && !force)
3822 // do not draw network players if level preview redefined, but players aren't
3823 if (preview.redefined && !menu.main.network_players.redefined)
3826 int num_players = 0;
3829 for (i = 0; i < MAX_PLAYERS; i++)
3830 if (stored_player[i].connected_network)
3833 struct TextPosInfo *pos = &menu.main.network_players;
3834 int tile_size = pos->tile_size;
3835 int border_size = pos->border_size;
3836 int xoffset_text = tile_size + border_size;
3837 int font_nr = pos->font;
3838 int font_width = getFontWidth(font_nr);
3839 int font_height = getFontHeight(font_nr);
3840 int player_height = MAX(tile_size, font_height);
3841 int player_yoffset = player_height + border_size;
3842 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3843 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3844 int all_players_height = num_players * player_yoffset - border_size;
3845 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3846 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3847 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3849 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3850 max_players_width, max_players_height);
3852 // first draw local network player ...
3853 for (i = 0; i < MAX_PLAYERS; i++)
3855 if (stored_player[i].connected_network &&
3856 stored_player[i].connected_locally)
3858 char *player_name = getNetworkPlayerName(i + 1);
3859 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3860 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3862 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3864 ypos += player_yoffset;
3868 // ... then draw all other network players
3869 for (i = 0; i < MAX_PLAYERS; i++)
3871 if (stored_player[i].connected_network &&
3872 !stored_player[i].connected_locally)
3874 char *player_name = getNetworkPlayerName(i + 1);
3875 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3876 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3878 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3880 ypos += player_yoffset;
3885 void DrawNetworkPlayers(void)
3887 DrawNetworkPlayersExt(FALSE);
3890 void ClearNetworkPlayers(void)
3892 DrawNetworkPlayersExt(TRUE);
3895 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3896 int graphic, int lx, int ly,
3899 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3901 if (mask_mode == USE_MASKING)
3902 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3904 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3907 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3908 int graphic, int sync_frame, int mask_mode)
3910 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3912 if (mask_mode == USE_MASKING)
3913 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3915 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3918 static void DrawGraphicAnimation(int x, int y, int graphic)
3920 int lx = LEVELX(x), ly = LEVELY(y);
3921 int mask_mode = NO_MASKING;
3923 if (!IN_SCR_FIELD(x, y))
3926 if (game.use_masked_elements)
3928 if (Tile[lx][ly] != EL_EMPTY)
3930 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3932 mask_mode = USE_MASKING;
3936 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3937 graphic, lx, ly, mask_mode);
3939 MarkTileDirty(x, y);
3942 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3944 int lx = LEVELX(x), ly = LEVELY(y);
3945 int mask_mode = NO_MASKING;
3947 if (!IN_SCR_FIELD(x, y))
3950 if (game.use_masked_elements)
3952 if (Tile[lx][ly] != EL_EMPTY)
3954 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3956 mask_mode = USE_MASKING;
3960 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3961 graphic, lx, ly, mask_mode);
3963 MarkTileDirty(x, y);
3966 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3968 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3971 void DrawLevelElementAnimation(int x, int y, int element)
3973 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3975 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3978 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3980 int sx = SCREENX(x), sy = SCREENY(y);
3982 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3985 if (Tile[x][y] == EL_EMPTY)
3986 graphic = el2img(GfxElementEmpty[x][y]);
3988 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3991 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3994 DrawGraphicAnimation(sx, sy, graphic);
3997 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3998 DrawLevelFieldCrumbled(x, y);
4000 if (GFX_CRUMBLED(Tile[x][y]))
4001 DrawLevelFieldCrumbled(x, y);
4005 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4007 int sx = SCREENX(x), sy = SCREENY(y);
4010 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4013 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4015 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4018 DrawGraphicAnimation(sx, sy, graphic);
4020 if (GFX_CRUMBLED(element))
4021 DrawLevelFieldCrumbled(x, y);
4024 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4026 if (player->use_murphy)
4028 // this works only because currently only one player can be "murphy" ...
4029 static int last_horizontal_dir = MV_LEFT;
4030 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4032 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4033 last_horizontal_dir = move_dir;
4035 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4037 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4039 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4045 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4048 static boolean equalGraphics(int graphic1, int graphic2)
4050 struct GraphicInfo *g1 = &graphic_info[graphic1];
4051 struct GraphicInfo *g2 = &graphic_info[graphic2];
4053 return (g1->bitmap == g2->bitmap &&
4054 g1->src_x == g2->src_x &&
4055 g1->src_y == g2->src_y &&
4056 g1->anim_frames == g2->anim_frames &&
4057 g1->anim_delay == g2->anim_delay &&
4058 g1->anim_mode == g2->anim_mode);
4061 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4065 DRAW_PLAYER_STAGE_INIT = 0,
4066 DRAW_PLAYER_STAGE_LAST_FIELD,
4067 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4068 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4069 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4070 DRAW_PLAYER_STAGE_PLAYER,
4072 DRAW_PLAYER_STAGE_PLAYER,
4073 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4075 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4076 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4078 NUM_DRAW_PLAYER_STAGES
4081 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4083 static int static_last_player_graphic[MAX_PLAYERS];
4084 static int static_last_player_frame[MAX_PLAYERS];
4085 static boolean static_player_is_opaque[MAX_PLAYERS];
4086 static boolean draw_player[MAX_PLAYERS];
4087 int pnr = player->index_nr;
4089 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4091 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4092 static_last_player_frame[pnr] = player->Frame;
4093 static_player_is_opaque[pnr] = FALSE;
4095 draw_player[pnr] = TRUE;
4098 if (!draw_player[pnr])
4102 if (!IN_LEV_FIELD(player->jx, player->jy))
4104 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4105 Debug("draw:DrawPlayerExt", "This should never happen!");
4107 draw_player[pnr] = FALSE;
4113 int last_player_graphic = static_last_player_graphic[pnr];
4114 int last_player_frame = static_last_player_frame[pnr];
4115 boolean player_is_opaque = static_player_is_opaque[pnr];
4117 int jx = player->jx;
4118 int jy = player->jy;
4119 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4120 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4121 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4122 int last_jx = (player->is_moving ? jx - dx : jx);
4123 int last_jy = (player->is_moving ? jy - dy : jy);
4124 int next_jx = jx + dx;
4125 int next_jy = jy + dy;
4126 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4127 int sx = SCREENX(jx);
4128 int sy = SCREENY(jy);
4129 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4130 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4131 int element = Tile[jx][jy];
4132 int last_element = Tile[last_jx][last_jy];
4133 int action = (player->is_pushing ? ACTION_PUSHING :
4134 player->is_digging ? ACTION_DIGGING :
4135 player->is_collecting ? ACTION_COLLECTING :
4136 player->is_moving ? ACTION_MOVING :
4137 player->is_snapping ? ACTION_SNAPPING :
4138 player->is_dropping ? ACTION_DROPPING :
4139 player->is_waiting ? player->action_waiting :
4142 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4144 // ------------------------------------------------------------------------
4145 // initialize drawing the player
4146 // ------------------------------------------------------------------------
4148 draw_player[pnr] = FALSE;
4150 // GfxElement[][] is set to the element the player is digging or collecting;
4151 // remove also for off-screen player if the player is not moving anymore
4152 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4153 GfxElement[jx][jy] = EL_UNDEFINED;
4155 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4158 if (element == EL_EXPLOSION)
4161 InitPlayerGfxAnimation(player, action, move_dir);
4163 draw_player[pnr] = TRUE;
4165 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4167 // ------------------------------------------------------------------------
4168 // draw things in the field the player is leaving, if needed
4169 // ------------------------------------------------------------------------
4171 if (!IN_SCR_FIELD(sx, sy))
4172 draw_player[pnr] = FALSE;
4174 if (!player->is_moving)
4177 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4179 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4181 if (last_element == EL_DYNAMITE_ACTIVE ||
4182 last_element == EL_EM_DYNAMITE_ACTIVE ||
4183 last_element == EL_SP_DISK_RED_ACTIVE)
4184 DrawDynamite(last_jx, last_jy);
4186 DrawLevelFieldThruMask(last_jx, last_jy);
4188 else if (last_element == EL_DYNAMITE_ACTIVE ||
4189 last_element == EL_EM_DYNAMITE_ACTIVE ||
4190 last_element == EL_SP_DISK_RED_ACTIVE)
4191 DrawDynamite(last_jx, last_jy);
4193 DrawLevelField(last_jx, last_jy);
4195 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4196 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4198 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4200 // ------------------------------------------------------------------------
4201 // draw things behind the player, if needed
4202 // ------------------------------------------------------------------------
4206 DrawLevelElement(jx, jy, Back[jx][jy]);
4211 if (IS_ACTIVE_BOMB(element))
4213 DrawLevelElement(jx, jy, EL_EMPTY);
4218 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4220 int old_element = GfxElement[jx][jy];
4221 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4222 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4224 if (GFX_CRUMBLED(old_element))
4225 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4227 DrawScreenGraphic(sx, sy, old_graphic, frame);
4229 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4230 static_player_is_opaque[pnr] = TRUE;
4234 GfxElement[jx][jy] = EL_UNDEFINED;
4236 // make sure that pushed elements are drawn with correct frame rate
4237 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4239 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4240 GfxFrame[jx][jy] = player->StepFrame;
4242 DrawLevelField(jx, jy);
4245 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4247 // ------------------------------------------------------------------------
4248 // draw things the player is pushing, if needed
4249 // ------------------------------------------------------------------------
4251 if (!player->is_pushing || !player->is_moving)
4254 int gfx_frame = GfxFrame[jx][jy];
4256 if (!IS_MOVING(jx, jy)) // push movement already finished
4258 element = Tile[next_jx][next_jy];
4259 gfx_frame = GfxFrame[next_jx][next_jy];
4262 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4263 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4264 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4266 // draw background element under pushed element (like the Sokoban field)
4267 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4269 // this allows transparent pushing animation over non-black background
4272 DrawLevelElement(jx, jy, Back[jx][jy]);
4274 DrawLevelElement(jx, jy, EL_EMPTY);
4276 if (Back[next_jx][next_jy])
4277 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4279 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4281 else if (Back[next_jx][next_jy])
4282 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4284 int px = SCREENX(jx), py = SCREENY(jy);
4285 int pxx = (TILEX - ABS(sxx)) * dx;
4286 int pyy = (TILEY - ABS(syy)) * dy;
4289 // do not draw (EM style) pushing animation when pushing is finished
4290 // (two-tile animations usually do not contain start and end frame)
4291 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4292 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4294 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4296 // masked drawing is needed for EMC style (double) movement graphics
4297 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4298 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4301 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4303 // ------------------------------------------------------------------------
4304 // draw player himself
4305 // ------------------------------------------------------------------------
4307 int graphic = getPlayerGraphic(player, move_dir);
4309 // in the case of changed player action or direction, prevent the current
4310 // animation frame from being restarted for identical animations
4311 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4312 player->Frame = last_player_frame;
4314 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4316 if (player_is_opaque)
4317 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4319 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4321 if (SHIELD_ON(player))
4323 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4324 IMG_SHIELD_NORMAL_ACTIVE);
4325 frame = getGraphicAnimationFrame(graphic, -1);
4327 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4330 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4332 // ------------------------------------------------------------------------
4333 // draw things in front of player (active dynamite or dynabombs)
4334 // ------------------------------------------------------------------------
4336 if (IS_ACTIVE_BOMB(element))
4338 int graphic = el2img(element);
4339 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4341 if (game.emulation == EMU_SUPAPLEX)
4342 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4344 DrawGraphicThruMask(sx, sy, graphic, frame);
4347 if (player_is_moving && last_element == EL_EXPLOSION)
4349 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4350 GfxElement[last_jx][last_jy] : EL_EMPTY);
4351 int graphic = el_act2img(element, ACTION_EXPLODING);
4352 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4353 int phase = ExplodePhase[last_jx][last_jy] - 1;
4354 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4357 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4360 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4362 // ------------------------------------------------------------------------
4363 // draw elements the player is just walking/passing through/under
4364 // ------------------------------------------------------------------------
4366 if (player_is_moving)
4368 // handle the field the player is leaving ...
4369 if (IS_ACCESSIBLE_INSIDE(last_element))
4370 DrawLevelField(last_jx, last_jy);
4371 else if (IS_ACCESSIBLE_UNDER(last_element))
4372 DrawLevelFieldThruMask(last_jx, last_jy);
4375 // do not redraw accessible elements if the player is just pushing them
4376 if (!player_is_moving || !player->is_pushing)
4378 // ... and the field the player is entering
4379 if (IS_ACCESSIBLE_INSIDE(element))
4380 DrawLevelField(jx, jy);
4381 else if (IS_ACCESSIBLE_UNDER(element))
4382 DrawLevelFieldThruMask(jx, jy);
4385 MarkTileDirty(sx, sy);
4389 void DrawPlayer(struct PlayerInfo *player)
4393 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4394 DrawPlayerExt(player, i);
4397 void DrawAllPlayers(void)
4401 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4402 for (j = 0; j < MAX_PLAYERS; j++)
4403 if (stored_player[j].active)
4404 DrawPlayerExt(&stored_player[j], i);
4407 void DrawPlayerField(int x, int y)
4409 if (!IS_PLAYER(x, y))
4412 DrawPlayer(PLAYERINFO(x, y));
4415 // ----------------------------------------------------------------------------
4417 void WaitForEventToContinue(void)
4419 boolean first_wait = TRUE;
4420 boolean still_wait = TRUE;
4422 if (program.headless)
4425 // simulate releasing mouse button over last gadget, if still pressed
4427 HandleGadgets(-1, -1, 0);
4429 button_status = MB_RELEASED;
4432 ClearPlayerAction();
4438 if (NextValidEvent(&event))
4442 case EVENT_BUTTONPRESS:
4443 case EVENT_FINGERPRESS:
4447 case EVENT_BUTTONRELEASE:
4448 case EVENT_FINGERRELEASE:
4449 still_wait = first_wait;
4452 case EVENT_KEYPRESS:
4453 case SDL_CONTROLLERBUTTONDOWN:
4454 case SDL_JOYBUTTONDOWN:
4459 HandleOtherEvents(&event);
4463 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4468 if (!PendingEvent())
4473 #define MAX_REQUEST_LINES 13
4474 #define MAX_REQUEST_LINE_FONT1_LEN 7
4475 #define MAX_REQUEST_LINE_FONT2_LEN 10
4477 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4479 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4481 int draw_buffer_last = GetDrawtoField();
4482 int width = request.width;
4483 int height = request.height;
4487 // when showing request dialog after game ended, deactivate game panel
4488 if (game_just_ended)
4489 game.panel.active = FALSE;
4491 game.request_active = TRUE;
4493 setRequestPosition(&sx, &sy, FALSE);
4495 button_status = MB_RELEASED;
4497 request_gadget_id = -1;
4502 boolean event_handled = FALSE;
4504 if (game_just_ended)
4506 SetDrawtoField(draw_buffer_game);
4508 HandleGameActions();
4510 SetDrawtoField(DRAW_TO_BACKBUFFER);
4512 if (global.use_envelope_request)
4514 // copy current state of request area to middle of playfield area
4515 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4523 while (NextValidEvent(&event))
4525 event_handled = TRUE;
4529 case EVENT_BUTTONPRESS:
4530 case EVENT_BUTTONRELEASE:
4531 case EVENT_MOTIONNOTIFY:
4535 if (event.type == EVENT_MOTIONNOTIFY)
4540 motion_status = TRUE;
4541 mx = ((MotionEvent *) &event)->x;
4542 my = ((MotionEvent *) &event)->y;
4546 motion_status = FALSE;
4547 mx = ((ButtonEvent *) &event)->x;
4548 my = ((ButtonEvent *) &event)->y;
4549 if (event.type == EVENT_BUTTONPRESS)
4550 button_status = ((ButtonEvent *) &event)->button;
4552 button_status = MB_RELEASED;
4555 // this sets 'request_gadget_id'
4556 HandleGadgets(mx, my, button_status);
4558 switch (request_gadget_id)
4560 case TOOL_CTRL_ID_YES:
4561 case TOOL_CTRL_ID_TOUCH_YES:
4564 case TOOL_CTRL_ID_NO:
4565 case TOOL_CTRL_ID_TOUCH_NO:
4568 case TOOL_CTRL_ID_CONFIRM:
4569 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4570 result = TRUE | FALSE;
4573 case TOOL_CTRL_ID_PLAYER_1:
4576 case TOOL_CTRL_ID_PLAYER_2:
4579 case TOOL_CTRL_ID_PLAYER_3:
4582 case TOOL_CTRL_ID_PLAYER_4:
4587 // only check clickable animations if no request gadget clicked
4588 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4595 case SDL_WINDOWEVENT:
4596 HandleWindowEvent((WindowEvent *) &event);
4599 case SDL_APP_WILLENTERBACKGROUND:
4600 case SDL_APP_DIDENTERBACKGROUND:
4601 case SDL_APP_WILLENTERFOREGROUND:
4602 case SDL_APP_DIDENTERFOREGROUND:
4603 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4606 case EVENT_KEYPRESS:
4608 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4613 if (req_state & REQ_CONFIRM)
4622 #if defined(KSYM_Rewind)
4623 case KSYM_Rewind: // for Amazon Fire TV remote
4632 #if defined(KSYM_FastForward)
4633 case KSYM_FastForward: // for Amazon Fire TV remote
4639 HandleKeysDebug(key, KEY_PRESSED);
4643 if (req_state & REQ_PLAYER)
4645 int old_player_nr = setup.network_player_nr;
4648 result = old_player_nr + 1;
4653 result = old_player_nr + 1;
4684 case EVENT_FINGERRELEASE:
4685 case EVENT_KEYRELEASE:
4686 ClearPlayerAction();
4689 case SDL_CONTROLLERBUTTONDOWN:
4690 switch (event.cbutton.button)
4692 case SDL_CONTROLLER_BUTTON_A:
4693 case SDL_CONTROLLER_BUTTON_X:
4694 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4695 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4699 case SDL_CONTROLLER_BUTTON_B:
4700 case SDL_CONTROLLER_BUTTON_Y:
4701 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4702 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4703 case SDL_CONTROLLER_BUTTON_BACK:
4708 if (req_state & REQ_PLAYER)
4710 int old_player_nr = setup.network_player_nr;
4713 result = old_player_nr + 1;
4715 switch (event.cbutton.button)
4717 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4718 case SDL_CONTROLLER_BUTTON_Y:
4722 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4723 case SDL_CONTROLLER_BUTTON_B:
4727 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4728 case SDL_CONTROLLER_BUTTON_A:
4732 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4733 case SDL_CONTROLLER_BUTTON_X:
4744 case SDL_CONTROLLERBUTTONUP:
4745 HandleJoystickEvent(&event);
4746 ClearPlayerAction();
4750 HandleOtherEvents(&event);
4755 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4757 int joy = AnyJoystick();
4759 if (joy & JOY_BUTTON_1)
4761 else if (joy & JOY_BUTTON_2)
4764 else if (AnyJoystick())
4766 int joy = AnyJoystick();
4768 if (req_state & REQ_PLAYER)
4772 else if (joy & JOY_RIGHT)
4774 else if (joy & JOY_DOWN)
4776 else if (joy & JOY_LEFT)
4783 if (game_just_ended)
4785 if (global.use_envelope_request)
4787 // copy back current state of pressed buttons inside request area
4788 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4792 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4798 SetDrawtoField(draw_buffer_last);
4800 game.request_active = FALSE;
4805 static boolean RequestDoor(char *text, unsigned int req_state)
4807 int draw_buffer_last = GetDrawtoField();
4808 unsigned int old_door_state;
4809 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4810 int font_nr = FONT_TEXT_2;
4815 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4817 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4818 font_nr = FONT_TEXT_1;
4821 if (game_status == GAME_MODE_PLAYING)
4822 BlitScreenToBitmap(backbuffer);
4824 // disable deactivated drawing when quick-loading level tape recording
4825 if (tape.playing && tape.deactivate_display)
4826 TapeDeactivateDisplayOff(TRUE);
4828 SetMouseCursor(CURSOR_DEFAULT);
4830 // pause network game while waiting for request to answer
4831 if (network.enabled &&
4832 game_status == GAME_MODE_PLAYING &&
4833 !game.all_players_gone &&
4834 req_state & REQUEST_WAIT_FOR_INPUT)
4835 SendToServer_PausePlaying();
4837 old_door_state = GetDoorState();
4839 // simulate releasing mouse button over last gadget, if still pressed
4841 HandleGadgets(-1, -1, 0);
4845 // draw released gadget before proceeding
4848 if (old_door_state & DOOR_OPEN_1)
4850 CloseDoor(DOOR_CLOSE_1);
4852 // save old door content
4853 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4854 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4857 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4858 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4860 // clear door drawing field
4861 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4863 // force DOOR font inside door area
4864 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4866 // write text for request
4867 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4869 char text_line[max_request_line_len + 1];
4875 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4877 tc = *(text_ptr + tx);
4878 // if (!tc || tc == ' ')
4879 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4883 if ((tc == '?' || tc == '!') && tl == 0)
4893 strncpy(text_line, text_ptr, tl);
4896 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4897 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4898 text_line, font_nr);
4900 text_ptr += tl + (tc == ' ' ? 1 : 0);
4901 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4906 if (req_state & REQ_ASK)
4908 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4909 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4910 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4911 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4913 else if (req_state & REQ_CONFIRM)
4915 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4916 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4918 else if (req_state & REQ_PLAYER)
4920 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4921 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4922 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4923 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4926 // copy request gadgets to door backbuffer
4927 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4929 OpenDoor(DOOR_OPEN_1);
4931 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4933 if (game_status == GAME_MODE_PLAYING)
4935 SetPanelBackground();
4936 SetDrawBackgroundMask(REDRAW_DOOR_1);
4940 SetDrawBackgroundMask(REDRAW_FIELD);
4946 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4948 // ---------- handle request buttons ----------
4949 result = RequestHandleEvents(req_state, draw_buffer_last);
4953 if (!(req_state & REQ_STAY_OPEN))
4955 CloseDoor(DOOR_CLOSE_1);
4957 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4958 (req_state & REQ_REOPEN))
4959 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4964 if (game_status == GAME_MODE_PLAYING)
4966 SetPanelBackground();
4967 SetDrawBackgroundMask(REDRAW_DOOR_1);
4971 SetDrawBackgroundMask(REDRAW_FIELD);
4974 // continue network game after request
4975 if (network.enabled &&
4976 game_status == GAME_MODE_PLAYING &&
4977 !game.all_players_gone &&
4978 req_state & REQUEST_WAIT_FOR_INPUT)
4979 SendToServer_ContinuePlaying();
4981 // restore deactivated drawing when quick-loading level tape recording
4982 if (tape.playing && tape.deactivate_display)
4983 TapeDeactivateDisplayOn();
4988 static boolean RequestEnvelope(char *text, unsigned int req_state)
4990 int draw_buffer_last = GetDrawtoField();
4993 if (game_status == GAME_MODE_PLAYING)
4994 BlitScreenToBitmap(backbuffer);
4996 // disable deactivated drawing when quick-loading level tape recording
4997 if (tape.playing && tape.deactivate_display)
4998 TapeDeactivateDisplayOff(TRUE);
5000 SetMouseCursor(CURSOR_DEFAULT);
5002 // pause network game while waiting for request to answer
5003 if (network.enabled &&
5004 game_status == GAME_MODE_PLAYING &&
5005 !game.all_players_gone &&
5006 req_state & REQUEST_WAIT_FOR_INPUT)
5007 SendToServer_PausePlaying();
5009 // simulate releasing mouse button over last gadget, if still pressed
5011 HandleGadgets(-1, -1, 0);
5015 // (replace with setting corresponding request background)
5016 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5017 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5019 // clear door drawing field
5020 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5022 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5024 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5026 if (game_status == GAME_MODE_PLAYING)
5028 SetPanelBackground();
5029 SetDrawBackgroundMask(REDRAW_DOOR_1);
5033 SetDrawBackgroundMask(REDRAW_FIELD);
5039 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5041 // ---------- handle request buttons ----------
5042 result = RequestHandleEvents(req_state, draw_buffer_last);
5046 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5050 if (game_status == GAME_MODE_PLAYING)
5052 SetPanelBackground();
5053 SetDrawBackgroundMask(REDRAW_DOOR_1);
5057 SetDrawBackgroundMask(REDRAW_FIELD);
5060 // continue network game after request
5061 if (network.enabled &&
5062 game_status == GAME_MODE_PLAYING &&
5063 !game.all_players_gone &&
5064 req_state & REQUEST_WAIT_FOR_INPUT)
5065 SendToServer_ContinuePlaying();
5067 // restore deactivated drawing when quick-loading level tape recording
5068 if (tape.playing && tape.deactivate_display)
5069 TapeDeactivateDisplayOn();
5074 boolean Request(char *text, unsigned int req_state)
5076 boolean overlay_enabled = GetOverlayEnabled();
5079 game.request_active_or_moving = TRUE;
5081 SetOverlayEnabled(FALSE);
5083 if (global.use_envelope_request)
5084 result = RequestEnvelope(text, req_state);
5086 result = RequestDoor(text, req_state);
5088 SetOverlayEnabled(overlay_enabled);
5090 game.request_active_or_moving = FALSE;
5095 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5097 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5098 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5101 if (dpo1->sort_priority != dpo2->sort_priority)
5102 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5104 compare_result = dpo1->nr - dpo2->nr;
5106 return compare_result;
5109 void InitGraphicCompatibilityInfo_Doors(void)
5115 struct DoorInfo *door;
5119 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5120 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5122 { -1, -1, -1, NULL }
5124 struct Rect door_rect_list[] =
5126 { DX, DY, DXSIZE, DYSIZE },
5127 { VX, VY, VXSIZE, VYSIZE }
5131 for (i = 0; doors[i].door_token != -1; i++)
5133 int door_token = doors[i].door_token;
5134 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5135 int part_1 = doors[i].part_1;
5136 int part_8 = doors[i].part_8;
5137 int part_2 = part_1 + 1;
5138 int part_3 = part_1 + 2;
5139 struct DoorInfo *door = doors[i].door;
5140 struct Rect *door_rect = &door_rect_list[door_index];
5141 boolean door_gfx_redefined = FALSE;
5143 // check if any door part graphic definitions have been redefined
5145 for (j = 0; door_part_controls[j].door_token != -1; j++)
5147 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5148 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5150 if (dpc->door_token == door_token && fi->redefined)
5151 door_gfx_redefined = TRUE;
5154 // check for old-style door graphic/animation modifications
5156 if (!door_gfx_redefined)
5158 if (door->anim_mode & ANIM_STATIC_PANEL)
5160 door->panel.step_xoffset = 0;
5161 door->panel.step_yoffset = 0;
5164 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5166 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5167 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5168 int num_door_steps, num_panel_steps;
5170 // remove door part graphics other than the two default wings
5172 for (j = 0; door_part_controls[j].door_token != -1; j++)
5174 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5175 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5177 if (dpc->graphic >= part_3 &&
5178 dpc->graphic <= part_8)
5182 // set graphics and screen positions of the default wings
5184 g_part_1->width = door_rect->width;
5185 g_part_1->height = door_rect->height;
5186 g_part_2->width = door_rect->width;
5187 g_part_2->height = door_rect->height;
5188 g_part_2->src_x = door_rect->width;
5189 g_part_2->src_y = g_part_1->src_y;
5191 door->part_2.x = door->part_1.x;
5192 door->part_2.y = door->part_1.y;
5194 if (door->width != -1)
5196 g_part_1->width = door->width;
5197 g_part_2->width = door->width;
5199 // special treatment for graphics and screen position of right wing
5200 g_part_2->src_x += door_rect->width - door->width;
5201 door->part_2.x += door_rect->width - door->width;
5204 if (door->height != -1)
5206 g_part_1->height = door->height;
5207 g_part_2->height = door->height;
5209 // special treatment for graphics and screen position of bottom wing
5210 g_part_2->src_y += door_rect->height - door->height;
5211 door->part_2.y += door_rect->height - door->height;
5214 // set animation delays for the default wings and panels
5216 door->part_1.step_delay = door->step_delay;
5217 door->part_2.step_delay = door->step_delay;
5218 door->panel.step_delay = door->step_delay;
5220 // set animation draw order for the default wings
5222 door->part_1.sort_priority = 2; // draw left wing over ...
5223 door->part_2.sort_priority = 1; // ... right wing
5225 // set animation draw offset for the default wings
5227 if (door->anim_mode & ANIM_HORIZONTAL)
5229 door->part_1.step_xoffset = door->step_offset;
5230 door->part_1.step_yoffset = 0;
5231 door->part_2.step_xoffset = door->step_offset * -1;
5232 door->part_2.step_yoffset = 0;
5234 num_door_steps = g_part_1->width / door->step_offset;
5236 else // ANIM_VERTICAL
5238 door->part_1.step_xoffset = 0;
5239 door->part_1.step_yoffset = door->step_offset;
5240 door->part_2.step_xoffset = 0;
5241 door->part_2.step_yoffset = door->step_offset * -1;
5243 num_door_steps = g_part_1->height / door->step_offset;
5246 // set animation draw offset for the default panels
5248 if (door->step_offset > 1)
5250 num_panel_steps = 2 * door_rect->height / door->step_offset;
5251 door->panel.start_step = num_panel_steps - num_door_steps;
5252 door->panel.start_step_closing = door->panel.start_step;
5256 num_panel_steps = door_rect->height / door->step_offset;
5257 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5258 door->panel.start_step_closing = door->panel.start_step;
5259 door->panel.step_delay *= 2;
5266 void InitDoors(void)
5270 for (i = 0; door_part_controls[i].door_token != -1; i++)
5272 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5273 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5275 // initialize "start_step_opening" and "start_step_closing", if needed
5276 if (dpc->pos->start_step_opening == 0 &&
5277 dpc->pos->start_step_closing == 0)
5279 // dpc->pos->start_step_opening = dpc->pos->start_step;
5280 dpc->pos->start_step_closing = dpc->pos->start_step;
5283 // fill structure for door part draw order (sorted below)
5285 dpo->sort_priority = dpc->pos->sort_priority;
5288 // sort door part controls according to sort_priority and graphic number
5289 qsort(door_part_order, MAX_DOOR_PARTS,
5290 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5293 unsigned int OpenDoor(unsigned int door_state)
5295 if (door_state & DOOR_COPY_BACK)
5297 if (door_state & DOOR_OPEN_1)
5298 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5299 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5301 if (door_state & DOOR_OPEN_2)
5302 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5303 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5305 door_state &= ~DOOR_COPY_BACK;
5308 return MoveDoor(door_state);
5311 unsigned int CloseDoor(unsigned int door_state)
5313 unsigned int old_door_state = GetDoorState();
5315 if (!(door_state & DOOR_NO_COPY_BACK))
5317 if (old_door_state & DOOR_OPEN_1)
5318 BlitBitmap(backbuffer, bitmap_db_door_1,
5319 DX, DY, DXSIZE, DYSIZE, 0, 0);
5321 if (old_door_state & DOOR_OPEN_2)
5322 BlitBitmap(backbuffer, bitmap_db_door_2,
5323 VX, VY, VXSIZE, VYSIZE, 0, 0);
5325 door_state &= ~DOOR_NO_COPY_BACK;
5328 return MoveDoor(door_state);
5331 unsigned int GetDoorState(void)
5333 return MoveDoor(DOOR_GET_STATE);
5336 unsigned int SetDoorState(unsigned int door_state)
5338 return MoveDoor(door_state | DOOR_SET_STATE);
5341 static int euclid(int a, int b)
5343 return (b ? euclid(b, a % b) : a);
5346 unsigned int MoveDoor(unsigned int door_state)
5348 struct Rect door_rect_list[] =
5350 { DX, DY, DXSIZE, DYSIZE },
5351 { VX, VY, VXSIZE, VYSIZE }
5353 static int door1 = DOOR_CLOSE_1;
5354 static int door2 = DOOR_CLOSE_2;
5355 unsigned int door_delay = 0;
5356 unsigned int door_delay_value;
5359 if (door_state == DOOR_GET_STATE)
5360 return (door1 | door2);
5362 if (door_state & DOOR_SET_STATE)
5364 if (door_state & DOOR_ACTION_1)
5365 door1 = door_state & DOOR_ACTION_1;
5366 if (door_state & DOOR_ACTION_2)
5367 door2 = door_state & DOOR_ACTION_2;
5369 return (door1 | door2);
5372 if (!(door_state & DOOR_FORCE_REDRAW))
5374 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5375 door_state &= ~DOOR_OPEN_1;
5376 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5377 door_state &= ~DOOR_CLOSE_1;
5378 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5379 door_state &= ~DOOR_OPEN_2;
5380 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5381 door_state &= ~DOOR_CLOSE_2;
5384 if (global.autoplay_leveldir)
5386 door_state |= DOOR_NO_DELAY;
5387 door_state &= ~DOOR_CLOSE_ALL;
5390 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5391 door_state |= DOOR_NO_DELAY;
5393 if (door_state & DOOR_ACTION)
5395 boolean door_panel_drawn[NUM_DOORS];
5396 boolean panel_has_doors[NUM_DOORS];
5397 boolean door_part_skip[MAX_DOOR_PARTS];
5398 boolean door_part_done[MAX_DOOR_PARTS];
5399 boolean door_part_done_all;
5400 int num_steps[MAX_DOOR_PARTS];
5401 int max_move_delay = 0; // delay for complete animations of all doors
5402 int max_step_delay = 0; // delay (ms) between two animation frames
5403 int num_move_steps = 0; // number of animation steps for all doors
5404 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5405 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5409 for (i = 0; i < NUM_DOORS; i++)
5410 panel_has_doors[i] = FALSE;
5412 for (i = 0; i < MAX_DOOR_PARTS; i++)
5414 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5415 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5416 int door_token = dpc->door_token;
5418 door_part_done[i] = FALSE;
5419 door_part_skip[i] = (!(door_state & door_token) ||
5423 for (i = 0; i < MAX_DOOR_PARTS; i++)
5425 int nr = door_part_order[i].nr;
5426 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5427 struct DoorPartPosInfo *pos = dpc->pos;
5428 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5429 int door_token = dpc->door_token;
5430 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5431 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5432 int step_xoffset = ABS(pos->step_xoffset);
5433 int step_yoffset = ABS(pos->step_yoffset);
5434 int step_delay = pos->step_delay;
5435 int current_door_state = door_state & door_token;
5436 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5437 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5438 boolean part_opening = (is_panel ? door_closing : door_opening);
5439 int start_step = (part_opening ? pos->start_step_opening :
5440 pos->start_step_closing);
5441 float move_xsize = (step_xoffset ? g->width : 0);
5442 float move_ysize = (step_yoffset ? g->height : 0);
5443 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5444 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5445 int move_steps = (move_xsteps && move_ysteps ?
5446 MIN(move_xsteps, move_ysteps) :
5447 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5448 int move_delay = move_steps * step_delay;
5450 if (door_part_skip[nr])
5453 max_move_delay = MAX(max_move_delay, move_delay);
5454 max_step_delay = (max_step_delay == 0 ? step_delay :
5455 euclid(max_step_delay, step_delay));
5456 num_steps[nr] = move_steps;
5460 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5462 panel_has_doors[door_index] = TRUE;
5466 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5468 num_move_steps = max_move_delay / max_step_delay;
5469 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5471 door_delay_value = max_step_delay;
5473 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5475 start = num_move_steps - 1;
5479 // opening door sound has priority over simultaneously closing door
5480 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5482 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5484 if (door_state & DOOR_OPEN_1)
5485 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5486 if (door_state & DOOR_OPEN_2)
5487 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5489 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5491 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5493 if (door_state & DOOR_CLOSE_1)
5494 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5495 if (door_state & DOOR_CLOSE_2)
5496 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5500 for (k = start; k < num_move_steps; k++)
5502 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5504 door_part_done_all = TRUE;
5506 for (i = 0; i < NUM_DOORS; i++)
5507 door_panel_drawn[i] = FALSE;
5509 for (i = 0; i < MAX_DOOR_PARTS; i++)
5511 int nr = door_part_order[i].nr;
5512 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5513 struct DoorPartPosInfo *pos = dpc->pos;
5514 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5515 int door_token = dpc->door_token;
5516 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5517 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5518 boolean is_panel_and_door_has_closed = FALSE;
5519 struct Rect *door_rect = &door_rect_list[door_index];
5520 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5522 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5523 int current_door_state = door_state & door_token;
5524 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5525 boolean door_closing = !door_opening;
5526 boolean part_opening = (is_panel ? door_closing : door_opening);
5527 boolean part_closing = !part_opening;
5528 int start_step = (part_opening ? pos->start_step_opening :
5529 pos->start_step_closing);
5530 int step_delay = pos->step_delay;
5531 int step_factor = step_delay / max_step_delay;
5532 int k1 = (step_factor ? k / step_factor + 1 : k);
5533 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5534 int kk = MAX(0, k2);
5537 int src_x, src_y, src_xx, src_yy;
5538 int dst_x, dst_y, dst_xx, dst_yy;
5541 if (door_part_skip[nr])
5544 if (!(door_state & door_token))
5552 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5553 int kk_door = MAX(0, k2_door);
5554 int sync_frame = kk_door * door_delay_value;
5555 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5557 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5558 &g_src_x, &g_src_y);
5563 if (!door_panel_drawn[door_index])
5565 ClearRectangle(drawto, door_rect->x, door_rect->y,
5566 door_rect->width, door_rect->height);
5568 door_panel_drawn[door_index] = TRUE;
5571 // draw opening or closing door parts
5573 if (pos->step_xoffset < 0) // door part on right side
5576 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5579 if (dst_xx + width > door_rect->width)
5580 width = door_rect->width - dst_xx;
5582 else // door part on left side
5585 dst_xx = pos->x - kk * pos->step_xoffset;
5589 src_xx = ABS(dst_xx);
5593 width = g->width - src_xx;
5595 if (width > door_rect->width)
5596 width = door_rect->width;
5598 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5601 if (pos->step_yoffset < 0) // door part on bottom side
5604 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5607 if (dst_yy + height > door_rect->height)
5608 height = door_rect->height - dst_yy;
5610 else // door part on top side
5613 dst_yy = pos->y - kk * pos->step_yoffset;
5617 src_yy = ABS(dst_yy);
5621 height = g->height - src_yy;
5624 src_x = g_src_x + src_xx;
5625 src_y = g_src_y + src_yy;
5627 dst_x = door_rect->x + dst_xx;
5628 dst_y = door_rect->y + dst_yy;
5630 is_panel_and_door_has_closed =
5633 panel_has_doors[door_index] &&
5634 k >= num_move_steps_doors_only - 1);
5636 if (width >= 0 && width <= g->width &&
5637 height >= 0 && height <= g->height &&
5638 !is_panel_and_door_has_closed)
5640 if (is_panel || !pos->draw_masked)
5641 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5644 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5648 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5650 if ((part_opening && (width < 0 || height < 0)) ||
5651 (part_closing && (width >= g->width && height >= g->height)))
5652 door_part_done[nr] = TRUE;
5654 // continue door part animations, but not panel after door has closed
5655 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5656 door_part_done_all = FALSE;
5659 if (!(door_state & DOOR_NO_DELAY))
5663 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5665 // prevent OS (Windows) from complaining about program not responding
5669 if (door_part_done_all)
5673 if (!(door_state & DOOR_NO_DELAY))
5675 // wait for specified door action post delay
5676 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5677 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5678 else if (door_state & DOOR_ACTION_1)
5679 door_delay_value = door_1.post_delay;
5680 else if (door_state & DOOR_ACTION_2)
5681 door_delay_value = door_2.post_delay;
5683 while (!DelayReached(&door_delay, door_delay_value))
5688 if (door_state & DOOR_ACTION_1)
5689 door1 = door_state & DOOR_ACTION_1;
5690 if (door_state & DOOR_ACTION_2)
5691 door2 = door_state & DOOR_ACTION_2;
5693 // draw masked border over door area
5694 DrawMaskedBorder(REDRAW_DOOR_1);
5695 DrawMaskedBorder(REDRAW_DOOR_2);
5697 ClearAutoRepeatKeyEvents();
5699 return (door1 | door2);
5702 static boolean useSpecialEditorDoor(void)
5704 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5705 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5707 // do not draw special editor door if editor border defined or redefined
5708 if (graphic_info[graphic].bitmap != NULL || redefined)
5711 // do not draw special editor door if global border defined to be empty
5712 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5715 // do not draw special editor door if viewport definitions do not match
5719 EY + EYSIZE != VY + VYSIZE)
5725 void DrawSpecialEditorDoor(void)
5727 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5728 int top_border_width = gfx1->width;
5729 int top_border_height = gfx1->height;
5730 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5731 int ex = EX - outer_border;
5732 int ey = EY - outer_border;
5733 int vy = VY - outer_border;
5734 int exsize = EXSIZE + 2 * outer_border;
5736 if (!useSpecialEditorDoor())
5739 // draw bigger level editor toolbox window
5740 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5741 top_border_width, top_border_height, ex, ey - top_border_height);
5742 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5743 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5745 redraw_mask |= REDRAW_ALL;
5748 void UndrawSpecialEditorDoor(void)
5750 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5751 int top_border_width = gfx1->width;
5752 int top_border_height = gfx1->height;
5753 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5754 int ex = EX - outer_border;
5755 int ey = EY - outer_border;
5756 int ey_top = ey - top_border_height;
5757 int exsize = EXSIZE + 2 * outer_border;
5758 int eysize = EYSIZE + 2 * outer_border;
5760 if (!useSpecialEditorDoor())
5763 // draw normal tape recorder window
5764 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5766 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5767 ex, ey_top, top_border_width, top_border_height,
5769 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5770 ex, ey, exsize, eysize, ex, ey);
5774 // if screen background is set to "[NONE]", clear editor toolbox window
5775 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5776 ClearRectangle(drawto, ex, ey, exsize, eysize);
5779 redraw_mask |= REDRAW_ALL;
5783 // ---------- new tool button stuff -------------------------------------------
5788 struct TextPosInfo *pos;
5790 boolean is_touch_button;
5792 } toolbutton_info[NUM_TOOL_BUTTONS] =
5795 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5796 TOOL_CTRL_ID_YES, FALSE, "yes"
5799 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5800 TOOL_CTRL_ID_NO, FALSE, "no"
5803 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5804 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5807 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5808 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5811 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5812 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5815 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5816 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5819 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5820 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5823 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5824 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5827 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5828 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5831 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5832 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5836 void CreateToolButtons(void)
5840 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5842 int graphic = toolbutton_info[i].graphic;
5843 struct GraphicInfo *gfx = &graphic_info[graphic];
5844 struct TextPosInfo *pos = toolbutton_info[i].pos;
5845 struct GadgetInfo *gi;
5846 Bitmap *deco_bitmap = None;
5847 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5848 unsigned int event_mask = GD_EVENT_RELEASED;
5849 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5850 int base_x = (is_touch_button ? 0 : DX);
5851 int base_y = (is_touch_button ? 0 : DY);
5852 int gd_x = gfx->src_x;
5853 int gd_y = gfx->src_y;
5854 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5855 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5860 if (global.use_envelope_request && !is_touch_button)
5862 setRequestPosition(&base_x, &base_y, TRUE);
5864 // check if request buttons are outside of envelope and fix, if needed
5865 if (x < 0 || x + gfx->width > request.width ||
5866 y < 0 || y + gfx->height > request.height)
5868 if (id == TOOL_CTRL_ID_YES)
5871 y = request.height - 2 * request.border_size - gfx->height;
5873 else if (id == TOOL_CTRL_ID_NO)
5875 x = request.width - 2 * request.border_size - gfx->width;
5876 y = request.height - 2 * request.border_size - gfx->height;
5878 else if (id == TOOL_CTRL_ID_CONFIRM)
5880 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5881 y = request.height - 2 * request.border_size - gfx->height;
5883 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5885 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5887 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5888 y = request.height - 2 * request.border_size - gfx->height * 2;
5890 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5891 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5896 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5898 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5900 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5901 pos->size, &deco_bitmap, &deco_x, &deco_y);
5902 deco_xpos = (gfx->width - pos->size) / 2;
5903 deco_ypos = (gfx->height - pos->size) / 2;
5906 gi = CreateGadget(GDI_CUSTOM_ID, id,
5907 GDI_IMAGE_ID, graphic,
5908 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5911 GDI_WIDTH, gfx->width,
5912 GDI_HEIGHT, gfx->height,
5913 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5914 GDI_STATE, GD_BUTTON_UNPRESSED,
5915 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5916 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5917 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5918 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5919 GDI_DECORATION_SIZE, pos->size, pos->size,
5920 GDI_DECORATION_SHIFTING, 1, 1,
5921 GDI_DIRECT_DRAW, FALSE,
5922 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5923 GDI_EVENT_MASK, event_mask,
5924 GDI_CALLBACK_ACTION, HandleToolButtons,
5928 Fail("cannot create gadget");
5930 tool_gadget[id] = gi;
5934 void FreeToolButtons(void)
5938 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5939 FreeGadget(tool_gadget[i]);
5942 static void UnmapToolButtons(void)
5946 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5947 UnmapGadget(tool_gadget[i]);
5950 static void HandleToolButtons(struct GadgetInfo *gi)
5952 request_gadget_id = gi->custom_id;
5955 static struct Mapping_EM_to_RND_object
5958 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5959 boolean is_backside; // backside of moving element
5965 em_object_mapping_list[GAME_TILE_MAX + 1] =
5968 Zborder, FALSE, FALSE,
5972 Zplayer, FALSE, FALSE,
5981 Ztank, FALSE, FALSE,
5985 Zeater, FALSE, FALSE,
5989 Zdynamite, FALSE, FALSE,
5993 Zboom, FALSE, FALSE,
5998 Xchain, FALSE, FALSE,
5999 EL_DEFAULT, ACTION_EXPLODING, -1
6002 Xboom_bug, FALSE, FALSE,
6003 EL_BUG, ACTION_EXPLODING, -1
6006 Xboom_tank, FALSE, FALSE,
6007 EL_SPACESHIP, ACTION_EXPLODING, -1
6010 Xboom_android, FALSE, FALSE,
6011 EL_EMC_ANDROID, ACTION_OTHER, -1
6014 Xboom_1, FALSE, FALSE,
6015 EL_DEFAULT, ACTION_EXPLODING, -1
6018 Xboom_2, FALSE, FALSE,
6019 EL_DEFAULT, ACTION_EXPLODING, -1
6023 Xblank, TRUE, FALSE,
6028 Xsplash_e, FALSE, FALSE,
6029 EL_ACID_SPLASH_RIGHT, -1, -1
6032 Xsplash_w, FALSE, FALSE,
6033 EL_ACID_SPLASH_LEFT, -1, -1
6037 Xplant, TRUE, FALSE,
6038 EL_EMC_PLANT, -1, -1
6041 Yplant, FALSE, FALSE,
6042 EL_EMC_PLANT, -1, -1
6046 Xacid_1, TRUE, FALSE,
6050 Xacid_2, FALSE, FALSE,
6054 Xacid_3, FALSE, FALSE,
6058 Xacid_4, FALSE, FALSE,
6062 Xacid_5, FALSE, FALSE,
6066 Xacid_6, FALSE, FALSE,
6070 Xacid_7, FALSE, FALSE,
6074 Xacid_8, FALSE, FALSE,
6079 Xfake_acid_1, TRUE, FALSE,
6080 EL_EMC_FAKE_ACID, -1, -1
6083 Xfake_acid_2, FALSE, FALSE,
6084 EL_EMC_FAKE_ACID, -1, -1
6087 Xfake_acid_3, FALSE, FALSE,
6088 EL_EMC_FAKE_ACID, -1, -1
6091 Xfake_acid_4, FALSE, FALSE,
6092 EL_EMC_FAKE_ACID, -1, -1
6095 Xfake_acid_5, FALSE, FALSE,
6096 EL_EMC_FAKE_ACID, -1, -1
6099 Xfake_acid_6, FALSE, FALSE,
6100 EL_EMC_FAKE_ACID, -1, -1
6103 Xfake_acid_7, FALSE, FALSE,
6104 EL_EMC_FAKE_ACID, -1, -1
6107 Xfake_acid_8, FALSE, FALSE,
6108 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_1_player, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_2_player, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_3_player, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6124 Xfake_acid_4_player, FALSE, FALSE,
6125 EL_EMC_FAKE_ACID, -1, -1
6128 Xfake_acid_5_player, FALSE, FALSE,
6129 EL_EMC_FAKE_ACID, -1, -1
6132 Xfake_acid_6_player, FALSE, FALSE,
6133 EL_EMC_FAKE_ACID, -1, -1
6136 Xfake_acid_7_player, FALSE, FALSE,
6137 EL_EMC_FAKE_ACID, -1, -1
6140 Xfake_acid_8_player, FALSE, FALSE,
6141 EL_EMC_FAKE_ACID, -1, -1
6145 Xgrass, TRUE, FALSE,
6146 EL_EMC_GRASS, -1, -1
6149 Ygrass_nB, FALSE, FALSE,
6150 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6153 Ygrass_eB, FALSE, FALSE,
6154 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6157 Ygrass_sB, FALSE, FALSE,
6158 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6161 Ygrass_wB, FALSE, FALSE,
6162 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6170 Ydirt_nB, FALSE, FALSE,
6171 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6174 Ydirt_eB, FALSE, FALSE,
6175 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6178 Ydirt_sB, FALSE, FALSE,
6179 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6182 Ydirt_wB, FALSE, FALSE,
6183 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6187 Xandroid, TRUE, FALSE,
6188 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6191 Xandroid_1_n, FALSE, FALSE,
6192 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6195 Xandroid_2_n, FALSE, FALSE,
6196 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6199 Xandroid_1_e, FALSE, FALSE,
6200 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6203 Xandroid_2_e, FALSE, FALSE,
6204 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6207 Xandroid_1_w, FALSE, FALSE,
6208 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6211 Xandroid_2_w, FALSE, FALSE,
6212 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6215 Xandroid_1_s, FALSE, FALSE,
6216 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6219 Xandroid_2_s, FALSE, FALSE,
6220 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6223 Yandroid_n, FALSE, FALSE,
6224 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6227 Yandroid_nB, FALSE, TRUE,
6228 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6231 Yandroid_ne, FALSE, FALSE,
6232 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6235 Yandroid_neB, FALSE, TRUE,
6236 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6239 Yandroid_e, FALSE, FALSE,
6240 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6243 Yandroid_eB, FALSE, TRUE,
6244 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6247 Yandroid_se, FALSE, FALSE,
6248 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6251 Yandroid_seB, FALSE, TRUE,
6252 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6255 Yandroid_s, FALSE, FALSE,
6256 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6259 Yandroid_sB, FALSE, TRUE,
6260 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6263 Yandroid_sw, FALSE, FALSE,
6264 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6267 Yandroid_swB, FALSE, TRUE,
6268 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6271 Yandroid_w, FALSE, FALSE,
6272 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6275 Yandroid_wB, FALSE, TRUE,
6276 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6279 Yandroid_nw, FALSE, FALSE,
6280 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6283 Yandroid_nwB, FALSE, TRUE,
6284 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6288 Xeater_n, TRUE, FALSE,
6289 EL_YAMYAM_UP, -1, -1
6292 Xeater_e, TRUE, FALSE,
6293 EL_YAMYAM_RIGHT, -1, -1
6296 Xeater_w, TRUE, FALSE,
6297 EL_YAMYAM_LEFT, -1, -1
6300 Xeater_s, TRUE, FALSE,
6301 EL_YAMYAM_DOWN, -1, -1
6304 Yeater_n, FALSE, FALSE,
6305 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6308 Yeater_nB, FALSE, TRUE,
6309 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6312 Yeater_e, FALSE, FALSE,
6313 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6316 Yeater_eB, FALSE, TRUE,
6317 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6320 Yeater_s, FALSE, FALSE,
6321 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6324 Yeater_sB, FALSE, TRUE,
6325 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6328 Yeater_w, FALSE, FALSE,
6329 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6332 Yeater_wB, FALSE, TRUE,
6333 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6336 Yeater_stone, FALSE, FALSE,
6337 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6340 Yeater_spring, FALSE, FALSE,
6341 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6345 Xalien, TRUE, FALSE,
6349 Xalien_pause, FALSE, FALSE,
6353 Yalien_n, FALSE, FALSE,
6354 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6357 Yalien_nB, FALSE, TRUE,
6358 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6361 Yalien_e, FALSE, FALSE,
6362 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6365 Yalien_eB, FALSE, TRUE,
6366 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6369 Yalien_s, FALSE, FALSE,
6370 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6373 Yalien_sB, FALSE, TRUE,
6374 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6377 Yalien_w, FALSE, FALSE,
6378 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6381 Yalien_wB, FALSE, TRUE,
6382 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6385 Yalien_stone, FALSE, FALSE,
6386 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6389 Yalien_spring, FALSE, FALSE,
6390 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6394 Xbug_1_n, TRUE, FALSE,
6398 Xbug_1_e, TRUE, FALSE,
6399 EL_BUG_RIGHT, -1, -1
6402 Xbug_1_s, TRUE, FALSE,
6406 Xbug_1_w, TRUE, FALSE,
6410 Xbug_2_n, FALSE, FALSE,
6414 Xbug_2_e, FALSE, FALSE,
6415 EL_BUG_RIGHT, -1, -1
6418 Xbug_2_s, FALSE, FALSE,
6422 Xbug_2_w, FALSE, FALSE,
6426 Ybug_n, FALSE, FALSE,
6427 EL_BUG, ACTION_MOVING, MV_BIT_UP
6430 Ybug_nB, FALSE, TRUE,
6431 EL_BUG, ACTION_MOVING, MV_BIT_UP
6434 Ybug_e, FALSE, FALSE,
6435 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6438 Ybug_eB, FALSE, TRUE,
6439 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6442 Ybug_s, FALSE, FALSE,
6443 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6446 Ybug_sB, FALSE, TRUE,
6447 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6450 Ybug_w, FALSE, FALSE,
6451 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6454 Ybug_wB, FALSE, TRUE,
6455 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6458 Ybug_w_n, FALSE, FALSE,
6459 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6462 Ybug_n_e, FALSE, FALSE,
6463 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6466 Ybug_e_s, FALSE, FALSE,
6467 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6470 Ybug_s_w, FALSE, FALSE,
6471 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6474 Ybug_e_n, FALSE, FALSE,
6475 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6478 Ybug_s_e, FALSE, FALSE,
6479 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6482 Ybug_w_s, FALSE, FALSE,
6483 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6486 Ybug_n_w, FALSE, FALSE,
6487 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6490 Ybug_stone, FALSE, FALSE,
6491 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6494 Ybug_spring, FALSE, FALSE,
6495 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6499 Xtank_1_n, TRUE, FALSE,
6500 EL_SPACESHIP_UP, -1, -1
6503 Xtank_1_e, TRUE, FALSE,
6504 EL_SPACESHIP_RIGHT, -1, -1
6507 Xtank_1_s, TRUE, FALSE,
6508 EL_SPACESHIP_DOWN, -1, -1
6511 Xtank_1_w, TRUE, FALSE,
6512 EL_SPACESHIP_LEFT, -1, -1
6515 Xtank_2_n, FALSE, FALSE,
6516 EL_SPACESHIP_UP, -1, -1
6519 Xtank_2_e, FALSE, FALSE,
6520 EL_SPACESHIP_RIGHT, -1, -1
6523 Xtank_2_s, FALSE, FALSE,
6524 EL_SPACESHIP_DOWN, -1, -1
6527 Xtank_2_w, FALSE, FALSE,
6528 EL_SPACESHIP_LEFT, -1, -1
6531 Ytank_n, FALSE, FALSE,
6532 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6535 Ytank_nB, FALSE, TRUE,
6536 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6539 Ytank_e, FALSE, FALSE,
6540 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6543 Ytank_eB, FALSE, TRUE,
6544 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6547 Ytank_s, FALSE, FALSE,
6548 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6551 Ytank_sB, FALSE, TRUE,
6552 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6555 Ytank_w, FALSE, FALSE,
6556 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6559 Ytank_wB, FALSE, TRUE,
6560 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6563 Ytank_w_n, FALSE, FALSE,
6564 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6567 Ytank_n_e, FALSE, FALSE,
6568 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6571 Ytank_e_s, FALSE, FALSE,
6572 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6575 Ytank_s_w, FALSE, FALSE,
6576 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6579 Ytank_e_n, FALSE, FALSE,
6580 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6583 Ytank_s_e, FALSE, FALSE,
6584 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6587 Ytank_w_s, FALSE, FALSE,
6588 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6591 Ytank_n_w, FALSE, FALSE,
6592 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6595 Ytank_stone, FALSE, FALSE,
6596 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6599 Ytank_spring, FALSE, FALSE,
6600 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6604 Xemerald, TRUE, FALSE,
6608 Xemerald_pause, FALSE, FALSE,
6612 Xemerald_fall, FALSE, FALSE,
6616 Xemerald_shine, FALSE, FALSE,
6617 EL_EMERALD, ACTION_TWINKLING, -1
6620 Yemerald_s, FALSE, FALSE,
6621 EL_EMERALD, ACTION_FALLING, -1
6624 Yemerald_sB, FALSE, TRUE,
6625 EL_EMERALD, ACTION_FALLING, -1
6628 Yemerald_e, FALSE, FALSE,
6629 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6632 Yemerald_eB, FALSE, TRUE,
6633 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6636 Yemerald_w, FALSE, FALSE,
6637 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6640 Yemerald_wB, FALSE, TRUE,
6641 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6644 Yemerald_blank, FALSE, FALSE,
6645 EL_EMERALD, ACTION_COLLECTING, -1
6649 Xdiamond, TRUE, FALSE,
6653 Xdiamond_pause, FALSE, FALSE,
6657 Xdiamond_fall, FALSE, FALSE,
6661 Xdiamond_shine, FALSE, FALSE,
6662 EL_DIAMOND, ACTION_TWINKLING, -1
6665 Ydiamond_s, FALSE, FALSE,
6666 EL_DIAMOND, ACTION_FALLING, -1
6669 Ydiamond_sB, FALSE, TRUE,
6670 EL_DIAMOND, ACTION_FALLING, -1
6673 Ydiamond_e, FALSE, FALSE,
6674 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6677 Ydiamond_eB, FALSE, TRUE,
6678 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6681 Ydiamond_w, FALSE, FALSE,
6682 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6685 Ydiamond_wB, FALSE, TRUE,
6686 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6689 Ydiamond_blank, FALSE, FALSE,
6690 EL_DIAMOND, ACTION_COLLECTING, -1
6693 Ydiamond_stone, FALSE, FALSE,
6694 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6698 Xstone, TRUE, FALSE,
6702 Xstone_pause, FALSE, FALSE,
6706 Xstone_fall, FALSE, FALSE,
6710 Ystone_s, FALSE, FALSE,
6711 EL_ROCK, ACTION_FALLING, -1
6714 Ystone_sB, FALSE, TRUE,
6715 EL_ROCK, ACTION_FALLING, -1
6718 Ystone_e, FALSE, FALSE,
6719 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6722 Ystone_eB, FALSE, TRUE,
6723 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6726 Ystone_w, FALSE, FALSE,
6727 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6730 Ystone_wB, FALSE, TRUE,
6731 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6739 Xbomb_pause, FALSE, FALSE,
6743 Xbomb_fall, FALSE, FALSE,
6747 Ybomb_s, FALSE, FALSE,
6748 EL_BOMB, ACTION_FALLING, -1
6751 Ybomb_sB, FALSE, TRUE,
6752 EL_BOMB, ACTION_FALLING, -1
6755 Ybomb_e, FALSE, FALSE,
6756 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6759 Ybomb_eB, FALSE, TRUE,
6760 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6763 Ybomb_w, FALSE, FALSE,
6764 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6767 Ybomb_wB, FALSE, TRUE,
6768 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6771 Ybomb_blank, FALSE, FALSE,
6772 EL_BOMB, ACTION_ACTIVATING, -1
6780 Xnut_pause, FALSE, FALSE,
6784 Xnut_fall, FALSE, FALSE,
6788 Ynut_s, FALSE, FALSE,
6789 EL_NUT, ACTION_FALLING, -1
6792 Ynut_sB, FALSE, TRUE,
6793 EL_NUT, ACTION_FALLING, -1
6796 Ynut_e, FALSE, FALSE,
6797 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6800 Ynut_eB, FALSE, TRUE,
6801 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6804 Ynut_w, FALSE, FALSE,
6805 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6808 Ynut_wB, FALSE, TRUE,
6809 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6812 Ynut_stone, FALSE, FALSE,
6813 EL_NUT, ACTION_BREAKING, -1
6817 Xspring, TRUE, FALSE,
6821 Xspring_pause, FALSE, FALSE,
6825 Xspring_e, TRUE, FALSE,
6826 EL_SPRING_RIGHT, -1, -1
6829 Xspring_w, TRUE, FALSE,
6830 EL_SPRING_LEFT, -1, -1
6833 Xspring_fall, FALSE, FALSE,
6837 Yspring_s, FALSE, FALSE,
6838 EL_SPRING, ACTION_FALLING, -1
6841 Yspring_sB, FALSE, TRUE,
6842 EL_SPRING, ACTION_FALLING, -1
6845 Yspring_e, FALSE, FALSE,
6846 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6849 Yspring_eB, FALSE, TRUE,
6850 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6853 Yspring_w, FALSE, FALSE,
6854 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6857 Yspring_wB, FALSE, TRUE,
6858 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6861 Yspring_alien_e, FALSE, FALSE,
6862 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6865 Yspring_alien_eB, FALSE, TRUE,
6866 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6869 Yspring_alien_w, FALSE, FALSE,
6870 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6873 Yspring_alien_wB, FALSE, TRUE,
6874 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6878 Xpush_emerald_e, FALSE, FALSE,
6879 EL_EMERALD, -1, MV_BIT_RIGHT
6882 Xpush_emerald_w, FALSE, FALSE,
6883 EL_EMERALD, -1, MV_BIT_LEFT
6886 Xpush_diamond_e, FALSE, FALSE,
6887 EL_DIAMOND, -1, MV_BIT_RIGHT
6890 Xpush_diamond_w, FALSE, FALSE,
6891 EL_DIAMOND, -1, MV_BIT_LEFT
6894 Xpush_stone_e, FALSE, FALSE,
6895 EL_ROCK, -1, MV_BIT_RIGHT
6898 Xpush_stone_w, FALSE, FALSE,
6899 EL_ROCK, -1, MV_BIT_LEFT
6902 Xpush_bomb_e, FALSE, FALSE,
6903 EL_BOMB, -1, MV_BIT_RIGHT
6906 Xpush_bomb_w, FALSE, FALSE,
6907 EL_BOMB, -1, MV_BIT_LEFT
6910 Xpush_nut_e, FALSE, FALSE,
6911 EL_NUT, -1, MV_BIT_RIGHT
6914 Xpush_nut_w, FALSE, FALSE,
6915 EL_NUT, -1, MV_BIT_LEFT
6918 Xpush_spring_e, FALSE, FALSE,
6919 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6922 Xpush_spring_w, FALSE, FALSE,
6923 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6927 Xdynamite, TRUE, FALSE,
6928 EL_EM_DYNAMITE, -1, -1
6931 Ydynamite_blank, FALSE, FALSE,
6932 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6935 Xdynamite_1, TRUE, FALSE,
6936 EL_EM_DYNAMITE_ACTIVE, -1, -1
6939 Xdynamite_2, FALSE, FALSE,
6940 EL_EM_DYNAMITE_ACTIVE, -1, -1
6943 Xdynamite_3, FALSE, FALSE,
6944 EL_EM_DYNAMITE_ACTIVE, -1, -1
6947 Xdynamite_4, FALSE, FALSE,
6948 EL_EM_DYNAMITE_ACTIVE, -1, -1
6952 Xkey_1, TRUE, FALSE,
6956 Xkey_2, TRUE, FALSE,
6960 Xkey_3, TRUE, FALSE,
6964 Xkey_4, TRUE, FALSE,
6968 Xkey_5, TRUE, FALSE,
6969 EL_EMC_KEY_5, -1, -1
6972 Xkey_6, TRUE, FALSE,
6973 EL_EMC_KEY_6, -1, -1
6976 Xkey_7, TRUE, FALSE,
6977 EL_EMC_KEY_7, -1, -1
6980 Xkey_8, TRUE, FALSE,
6981 EL_EMC_KEY_8, -1, -1
6985 Xdoor_1, TRUE, FALSE,
6986 EL_EM_GATE_1, -1, -1
6989 Xdoor_2, TRUE, FALSE,
6990 EL_EM_GATE_2, -1, -1
6993 Xdoor_3, TRUE, FALSE,
6994 EL_EM_GATE_3, -1, -1
6997 Xdoor_4, TRUE, FALSE,
6998 EL_EM_GATE_4, -1, -1
7001 Xdoor_5, TRUE, FALSE,
7002 EL_EMC_GATE_5, -1, -1
7005 Xdoor_6, TRUE, FALSE,
7006 EL_EMC_GATE_6, -1, -1
7009 Xdoor_7, TRUE, FALSE,
7010 EL_EMC_GATE_7, -1, -1
7013 Xdoor_8, TRUE, FALSE,
7014 EL_EMC_GATE_8, -1, -1
7018 Xfake_door_1, TRUE, FALSE,
7019 EL_EM_GATE_1_GRAY, -1, -1
7022 Xfake_door_2, TRUE, FALSE,
7023 EL_EM_GATE_2_GRAY, -1, -1
7026 Xfake_door_3, TRUE, FALSE,
7027 EL_EM_GATE_3_GRAY, -1, -1
7030 Xfake_door_4, TRUE, FALSE,
7031 EL_EM_GATE_4_GRAY, -1, -1
7034 Xfake_door_5, TRUE, FALSE,
7035 EL_EMC_GATE_5_GRAY, -1, -1
7038 Xfake_door_6, TRUE, FALSE,
7039 EL_EMC_GATE_6_GRAY, -1, -1
7042 Xfake_door_7, TRUE, FALSE,
7043 EL_EMC_GATE_7_GRAY, -1, -1
7046 Xfake_door_8, TRUE, FALSE,
7047 EL_EMC_GATE_8_GRAY, -1, -1
7051 Xballoon, TRUE, FALSE,
7055 Yballoon_n, FALSE, FALSE,
7056 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7059 Yballoon_nB, FALSE, TRUE,
7060 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7063 Yballoon_e, FALSE, FALSE,
7064 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7067 Yballoon_eB, FALSE, TRUE,
7068 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7071 Yballoon_s, FALSE, FALSE,
7072 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7075 Yballoon_sB, FALSE, TRUE,
7076 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7079 Yballoon_w, FALSE, FALSE,
7080 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7083 Yballoon_wB, FALSE, TRUE,
7084 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7088 Xball_1, TRUE, FALSE,
7089 EL_EMC_MAGIC_BALL, -1, -1
7092 Yball_1, FALSE, FALSE,
7093 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7096 Xball_2, FALSE, FALSE,
7097 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7100 Yball_2, FALSE, FALSE,
7101 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7104 Yball_blank, FALSE, FALSE,
7105 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7109 Xamoeba_1, TRUE, FALSE,
7110 EL_AMOEBA_DRY, ACTION_OTHER, -1
7113 Xamoeba_2, FALSE, FALSE,
7114 EL_AMOEBA_DRY, ACTION_OTHER, -1
7117 Xamoeba_3, FALSE, FALSE,
7118 EL_AMOEBA_DRY, ACTION_OTHER, -1
7121 Xamoeba_4, FALSE, FALSE,
7122 EL_AMOEBA_DRY, ACTION_OTHER, -1
7125 Xamoeba_5, TRUE, FALSE,
7126 EL_AMOEBA_WET, ACTION_OTHER, -1
7129 Xamoeba_6, FALSE, FALSE,
7130 EL_AMOEBA_WET, ACTION_OTHER, -1
7133 Xamoeba_7, FALSE, FALSE,
7134 EL_AMOEBA_WET, ACTION_OTHER, -1
7137 Xamoeba_8, FALSE, FALSE,
7138 EL_AMOEBA_WET, ACTION_OTHER, -1
7143 EL_AMOEBA_DROP, ACTION_GROWING, -1
7146 Xdrip_fall, FALSE, FALSE,
7147 EL_AMOEBA_DROP, -1, -1
7150 Xdrip_stretch, FALSE, FALSE,
7151 EL_AMOEBA_DROP, ACTION_FALLING, -1
7154 Xdrip_stretchB, FALSE, TRUE,
7155 EL_AMOEBA_DROP, ACTION_FALLING, -1
7158 Ydrip_1_s, FALSE, FALSE,
7159 EL_AMOEBA_DROP, ACTION_FALLING, -1
7162 Ydrip_1_sB, FALSE, TRUE,
7163 EL_AMOEBA_DROP, ACTION_FALLING, -1
7166 Ydrip_2_s, FALSE, FALSE,
7167 EL_AMOEBA_DROP, ACTION_FALLING, -1
7170 Ydrip_2_sB, FALSE, TRUE,
7171 EL_AMOEBA_DROP, ACTION_FALLING, -1
7175 Xwonderwall, TRUE, FALSE,
7176 EL_MAGIC_WALL, -1, -1
7179 Ywonderwall, FALSE, FALSE,
7180 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7184 Xwheel, TRUE, FALSE,
7185 EL_ROBOT_WHEEL, -1, -1
7188 Ywheel, FALSE, FALSE,
7189 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7193 Xswitch, TRUE, FALSE,
7194 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7197 Yswitch, FALSE, FALSE,
7198 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7202 Xbumper, TRUE, FALSE,
7203 EL_EMC_SPRING_BUMPER, -1, -1
7206 Ybumper, FALSE, FALSE,
7207 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7211 Xacid_nw, TRUE, FALSE,
7212 EL_ACID_POOL_TOPLEFT, -1, -1
7215 Xacid_ne, TRUE, FALSE,
7216 EL_ACID_POOL_TOPRIGHT, -1, -1
7219 Xacid_sw, TRUE, FALSE,
7220 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7223 Xacid_s, TRUE, FALSE,
7224 EL_ACID_POOL_BOTTOM, -1, -1
7227 Xacid_se, TRUE, FALSE,
7228 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7232 Xfake_blank, TRUE, FALSE,
7233 EL_INVISIBLE_WALL, -1, -1
7236 Yfake_blank, FALSE, FALSE,
7237 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7241 Xfake_grass, TRUE, FALSE,
7242 EL_EMC_FAKE_GRASS, -1, -1
7245 Yfake_grass, FALSE, FALSE,
7246 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7250 Xfake_amoeba, TRUE, FALSE,
7251 EL_EMC_DRIPPER, -1, -1
7254 Yfake_amoeba, FALSE, FALSE,
7255 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7259 Xlenses, TRUE, FALSE,
7260 EL_EMC_LENSES, -1, -1
7264 Xmagnify, TRUE, FALSE,
7265 EL_EMC_MAGNIFIER, -1, -1
7270 EL_QUICKSAND_EMPTY, -1, -1
7273 Xsand_stone, TRUE, FALSE,
7274 EL_QUICKSAND_FULL, -1, -1
7277 Xsand_stonein_1, FALSE, TRUE,
7278 EL_ROCK, ACTION_FILLING, -1
7281 Xsand_stonein_2, FALSE, TRUE,
7282 EL_ROCK, ACTION_FILLING, -1
7285 Xsand_stonein_3, FALSE, TRUE,
7286 EL_ROCK, ACTION_FILLING, -1
7289 Xsand_stonein_4, FALSE, TRUE,
7290 EL_ROCK, ACTION_FILLING, -1
7293 Xsand_sandstone_1, FALSE, FALSE,
7294 EL_QUICKSAND_FILLING, -1, -1
7297 Xsand_sandstone_2, FALSE, FALSE,
7298 EL_QUICKSAND_FILLING, -1, -1
7301 Xsand_sandstone_3, FALSE, FALSE,
7302 EL_QUICKSAND_FILLING, -1, -1
7305 Xsand_sandstone_4, FALSE, FALSE,
7306 EL_QUICKSAND_FILLING, -1, -1
7309 Xsand_stonesand_1, FALSE, FALSE,
7310 EL_QUICKSAND_EMPTYING, -1, -1
7313 Xsand_stonesand_2, FALSE, FALSE,
7314 EL_QUICKSAND_EMPTYING, -1, -1
7317 Xsand_stonesand_3, FALSE, FALSE,
7318 EL_QUICKSAND_EMPTYING, -1, -1
7321 Xsand_stonesand_4, FALSE, FALSE,
7322 EL_QUICKSAND_EMPTYING, -1, -1
7325 Xsand_stoneout_1, FALSE, FALSE,
7326 EL_ROCK, ACTION_EMPTYING, -1
7329 Xsand_stoneout_2, FALSE, FALSE,
7330 EL_ROCK, ACTION_EMPTYING, -1
7333 Xsand_stonesand_quickout_1, FALSE, FALSE,
7334 EL_QUICKSAND_EMPTYING, -1, -1
7337 Xsand_stonesand_quickout_2, FALSE, FALSE,
7338 EL_QUICKSAND_EMPTYING, -1, -1
7342 Xslide_ns, TRUE, FALSE,
7343 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7346 Yslide_ns_blank, FALSE, FALSE,
7347 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7350 Xslide_ew, TRUE, FALSE,
7351 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7354 Yslide_ew_blank, FALSE, FALSE,
7355 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7359 Xwind_n, TRUE, FALSE,
7360 EL_BALLOON_SWITCH_UP, -1, -1
7363 Xwind_e, TRUE, FALSE,
7364 EL_BALLOON_SWITCH_RIGHT, -1, -1
7367 Xwind_s, TRUE, FALSE,
7368 EL_BALLOON_SWITCH_DOWN, -1, -1
7371 Xwind_w, TRUE, FALSE,
7372 EL_BALLOON_SWITCH_LEFT, -1, -1
7375 Xwind_any, TRUE, FALSE,
7376 EL_BALLOON_SWITCH_ANY, -1, -1
7379 Xwind_stop, TRUE, FALSE,
7380 EL_BALLOON_SWITCH_NONE, -1, -1
7385 EL_EM_EXIT_CLOSED, -1, -1
7388 Xexit_1, TRUE, FALSE,
7389 EL_EM_EXIT_OPEN, -1, -1
7392 Xexit_2, FALSE, FALSE,
7393 EL_EM_EXIT_OPEN, -1, -1
7396 Xexit_3, FALSE, FALSE,
7397 EL_EM_EXIT_OPEN, -1, -1
7401 Xpause, FALSE, FALSE,
7406 Xwall_1, TRUE, FALSE,
7410 Xwall_2, TRUE, FALSE,
7411 EL_EMC_WALL_14, -1, -1
7414 Xwall_3, TRUE, FALSE,
7415 EL_EMC_WALL_15, -1, -1
7418 Xwall_4, TRUE, FALSE,
7419 EL_EMC_WALL_16, -1, -1
7423 Xroundwall_1, TRUE, FALSE,
7424 EL_WALL_SLIPPERY, -1, -1
7427 Xroundwall_2, TRUE, FALSE,
7428 EL_EMC_WALL_SLIPPERY_2, -1, -1
7431 Xroundwall_3, TRUE, FALSE,
7432 EL_EMC_WALL_SLIPPERY_3, -1, -1
7435 Xroundwall_4, TRUE, FALSE,
7436 EL_EMC_WALL_SLIPPERY_4, -1, -1
7440 Xsteel_1, TRUE, FALSE,
7441 EL_STEELWALL, -1, -1
7444 Xsteel_2, TRUE, FALSE,
7445 EL_EMC_STEELWALL_2, -1, -1
7448 Xsteel_3, TRUE, FALSE,
7449 EL_EMC_STEELWALL_3, -1, -1
7452 Xsteel_4, TRUE, FALSE,
7453 EL_EMC_STEELWALL_4, -1, -1
7457 Xdecor_1, TRUE, FALSE,
7458 EL_EMC_WALL_8, -1, -1
7461 Xdecor_2, TRUE, FALSE,
7462 EL_EMC_WALL_6, -1, -1
7465 Xdecor_3, TRUE, FALSE,
7466 EL_EMC_WALL_4, -1, -1
7469 Xdecor_4, TRUE, FALSE,
7470 EL_EMC_WALL_7, -1, -1
7473 Xdecor_5, TRUE, FALSE,
7474 EL_EMC_WALL_5, -1, -1
7477 Xdecor_6, TRUE, FALSE,
7478 EL_EMC_WALL_9, -1, -1
7481 Xdecor_7, TRUE, FALSE,
7482 EL_EMC_WALL_10, -1, -1
7485 Xdecor_8, TRUE, FALSE,
7486 EL_EMC_WALL_1, -1, -1
7489 Xdecor_9, TRUE, FALSE,
7490 EL_EMC_WALL_2, -1, -1
7493 Xdecor_10, TRUE, FALSE,
7494 EL_EMC_WALL_3, -1, -1
7497 Xdecor_11, TRUE, FALSE,
7498 EL_EMC_WALL_11, -1, -1
7501 Xdecor_12, TRUE, FALSE,
7502 EL_EMC_WALL_12, -1, -1
7506 Xalpha_0, TRUE, FALSE,
7507 EL_CHAR('0'), -1, -1
7510 Xalpha_1, TRUE, FALSE,
7511 EL_CHAR('1'), -1, -1
7514 Xalpha_2, TRUE, FALSE,
7515 EL_CHAR('2'), -1, -1
7518 Xalpha_3, TRUE, FALSE,
7519 EL_CHAR('3'), -1, -1
7522 Xalpha_4, TRUE, FALSE,
7523 EL_CHAR('4'), -1, -1
7526 Xalpha_5, TRUE, FALSE,
7527 EL_CHAR('5'), -1, -1
7530 Xalpha_6, TRUE, FALSE,
7531 EL_CHAR('6'), -1, -1
7534 Xalpha_7, TRUE, FALSE,
7535 EL_CHAR('7'), -1, -1
7538 Xalpha_8, TRUE, FALSE,
7539 EL_CHAR('8'), -1, -1
7542 Xalpha_9, TRUE, FALSE,
7543 EL_CHAR('9'), -1, -1
7546 Xalpha_excla, TRUE, FALSE,
7547 EL_CHAR('!'), -1, -1
7550 Xalpha_apost, TRUE, FALSE,
7551 EL_CHAR('\''), -1, -1
7554 Xalpha_comma, TRUE, FALSE,
7555 EL_CHAR(','), -1, -1
7558 Xalpha_minus, TRUE, FALSE,
7559 EL_CHAR('-'), -1, -1
7562 Xalpha_perio, TRUE, FALSE,
7563 EL_CHAR('.'), -1, -1
7566 Xalpha_colon, TRUE, FALSE,
7567 EL_CHAR(':'), -1, -1
7570 Xalpha_quest, TRUE, FALSE,
7571 EL_CHAR('?'), -1, -1
7574 Xalpha_a, TRUE, FALSE,
7575 EL_CHAR('A'), -1, -1
7578 Xalpha_b, TRUE, FALSE,
7579 EL_CHAR('B'), -1, -1
7582 Xalpha_c, TRUE, FALSE,
7583 EL_CHAR('C'), -1, -1
7586 Xalpha_d, TRUE, FALSE,
7587 EL_CHAR('D'), -1, -1
7590 Xalpha_e, TRUE, FALSE,
7591 EL_CHAR('E'), -1, -1
7594 Xalpha_f, TRUE, FALSE,
7595 EL_CHAR('F'), -1, -1
7598 Xalpha_g, TRUE, FALSE,
7599 EL_CHAR('G'), -1, -1
7602 Xalpha_h, TRUE, FALSE,
7603 EL_CHAR('H'), -1, -1
7606 Xalpha_i, TRUE, FALSE,
7607 EL_CHAR('I'), -1, -1
7610 Xalpha_j, TRUE, FALSE,
7611 EL_CHAR('J'), -1, -1
7614 Xalpha_k, TRUE, FALSE,
7615 EL_CHAR('K'), -1, -1
7618 Xalpha_l, TRUE, FALSE,
7619 EL_CHAR('L'), -1, -1
7622 Xalpha_m, TRUE, FALSE,
7623 EL_CHAR('M'), -1, -1
7626 Xalpha_n, TRUE, FALSE,
7627 EL_CHAR('N'), -1, -1
7630 Xalpha_o, TRUE, FALSE,
7631 EL_CHAR('O'), -1, -1
7634 Xalpha_p, TRUE, FALSE,
7635 EL_CHAR('P'), -1, -1
7638 Xalpha_q, TRUE, FALSE,
7639 EL_CHAR('Q'), -1, -1
7642 Xalpha_r, TRUE, FALSE,
7643 EL_CHAR('R'), -1, -1
7646 Xalpha_s, TRUE, FALSE,
7647 EL_CHAR('S'), -1, -1
7650 Xalpha_t, TRUE, FALSE,
7651 EL_CHAR('T'), -1, -1
7654 Xalpha_u, TRUE, FALSE,
7655 EL_CHAR('U'), -1, -1
7658 Xalpha_v, TRUE, FALSE,
7659 EL_CHAR('V'), -1, -1
7662 Xalpha_w, TRUE, FALSE,
7663 EL_CHAR('W'), -1, -1
7666 Xalpha_x, TRUE, FALSE,
7667 EL_CHAR('X'), -1, -1
7670 Xalpha_y, TRUE, FALSE,
7671 EL_CHAR('Y'), -1, -1
7674 Xalpha_z, TRUE, FALSE,
7675 EL_CHAR('Z'), -1, -1
7678 Xalpha_arrow_e, TRUE, FALSE,
7679 EL_CHAR('>'), -1, -1
7682 Xalpha_arrow_w, TRUE, FALSE,
7683 EL_CHAR('<'), -1, -1
7686 Xalpha_copyr, TRUE, FALSE,
7687 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7691 Ykey_1_blank, FALSE, FALSE,
7692 EL_EM_KEY_1, ACTION_COLLECTING, -1
7695 Ykey_2_blank, FALSE, FALSE,
7696 EL_EM_KEY_2, ACTION_COLLECTING, -1
7699 Ykey_3_blank, FALSE, FALSE,
7700 EL_EM_KEY_3, ACTION_COLLECTING, -1
7703 Ykey_4_blank, FALSE, FALSE,
7704 EL_EM_KEY_4, ACTION_COLLECTING, -1
7707 Ykey_5_blank, FALSE, FALSE,
7708 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7711 Ykey_6_blank, FALSE, FALSE,
7712 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7715 Ykey_7_blank, FALSE, FALSE,
7716 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7719 Ykey_8_blank, FALSE, FALSE,
7720 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7723 Ylenses_blank, FALSE, FALSE,
7724 EL_EMC_LENSES, ACTION_COLLECTING, -1
7727 Ymagnify_blank, FALSE, FALSE,
7728 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7731 Ygrass_blank, FALSE, FALSE,
7732 EL_EMC_GRASS, ACTION_SNAPPING, -1
7735 Ydirt_blank, FALSE, FALSE,
7736 EL_SAND, ACTION_SNAPPING, -1
7745 static struct Mapping_EM_to_RND_player
7754 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7758 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7762 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7766 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7770 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7774 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7778 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7782 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7786 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7790 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7794 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7798 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7802 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7806 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7810 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7814 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7818 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7822 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7826 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7830 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7834 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7838 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7842 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7846 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7850 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7854 EL_PLAYER_1, ACTION_DEFAULT, -1,
7858 EL_PLAYER_2, ACTION_DEFAULT, -1,
7862 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7866 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7870 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7874 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7878 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7882 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7886 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7890 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7894 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7898 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7902 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7906 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7910 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7914 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7918 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7922 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7926 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7930 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7934 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7938 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7942 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7946 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7950 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7954 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7958 EL_PLAYER_3, ACTION_DEFAULT, -1,
7962 EL_PLAYER_4, ACTION_DEFAULT, -1,
7971 int map_element_RND_to_EM_cave(int element_rnd)
7973 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7974 static boolean mapping_initialized = FALSE;
7976 if (!mapping_initialized)
7980 // return "Xalpha_quest" for all undefined elements in mapping array
7981 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7982 mapping_RND_to_EM[i] = Xalpha_quest;
7984 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7985 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7986 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7987 em_object_mapping_list[i].element_em;
7989 mapping_initialized = TRUE;
7992 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7994 Warn("invalid RND level element %d", element_rnd);
7999 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8002 int map_element_EM_to_RND_cave(int element_em_cave)
8004 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8005 static boolean mapping_initialized = FALSE;
8007 if (!mapping_initialized)
8011 // return "EL_UNKNOWN" for all undefined elements in mapping array
8012 for (i = 0; i < GAME_TILE_MAX; i++)
8013 mapping_EM_to_RND[i] = EL_UNKNOWN;
8015 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8016 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8017 em_object_mapping_list[i].element_rnd;
8019 mapping_initialized = TRUE;
8022 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8024 Warn("invalid EM cave element %d", element_em_cave);
8029 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8032 int map_element_EM_to_RND_game(int element_em_game)
8034 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8035 static boolean mapping_initialized = FALSE;
8037 if (!mapping_initialized)
8041 // return "EL_UNKNOWN" for all undefined elements in mapping array
8042 for (i = 0; i < GAME_TILE_MAX; i++)
8043 mapping_EM_to_RND[i] = EL_UNKNOWN;
8045 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8046 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8047 em_object_mapping_list[i].element_rnd;
8049 mapping_initialized = TRUE;
8052 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8054 Warn("invalid EM game element %d", element_em_game);
8059 return mapping_EM_to_RND[element_em_game];
8062 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8064 struct LevelInfo_EM *level_em = level->native_em_level;
8065 struct CAVE *cav = level_em->cav;
8068 for (i = 0; i < GAME_TILE_MAX; i++)
8069 cav->android_array[i] = Cblank;
8071 for (i = 0; i < level->num_android_clone_elements; i++)
8073 int element_rnd = level->android_clone_element[i];
8074 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8076 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8077 if (em_object_mapping_list[j].element_rnd == element_rnd)
8078 cav->android_array[em_object_mapping_list[j].element_em] =
8083 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8085 struct LevelInfo_EM *level_em = level->native_em_level;
8086 struct CAVE *cav = level_em->cav;
8089 level->num_android_clone_elements = 0;
8091 for (i = 0; i < GAME_TILE_MAX; i++)
8093 int element_em_cave = cav->android_array[i];
8095 boolean element_found = FALSE;
8097 if (element_em_cave == Cblank)
8100 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8102 for (j = 0; j < level->num_android_clone_elements; j++)
8103 if (level->android_clone_element[j] == element_rnd)
8104 element_found = TRUE;
8108 level->android_clone_element[level->num_android_clone_elements++] =
8111 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8116 if (level->num_android_clone_elements == 0)
8118 level->num_android_clone_elements = 1;
8119 level->android_clone_element[0] = EL_EMPTY;
8123 int map_direction_RND_to_EM(int direction)
8125 return (direction == MV_UP ? 0 :
8126 direction == MV_RIGHT ? 1 :
8127 direction == MV_DOWN ? 2 :
8128 direction == MV_LEFT ? 3 :
8132 int map_direction_EM_to_RND(int direction)
8134 return (direction == 0 ? MV_UP :
8135 direction == 1 ? MV_RIGHT :
8136 direction == 2 ? MV_DOWN :
8137 direction == 3 ? MV_LEFT :
8141 int map_element_RND_to_SP(int element_rnd)
8143 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8145 if (element_rnd >= EL_SP_START &&
8146 element_rnd <= EL_SP_END)
8147 element_sp = element_rnd - EL_SP_START;
8148 else if (element_rnd == EL_EMPTY_SPACE)
8150 else if (element_rnd == EL_INVISIBLE_WALL)
8156 int map_element_SP_to_RND(int element_sp)
8158 int element_rnd = EL_UNKNOWN;
8160 if (element_sp >= 0x00 &&
8162 element_rnd = EL_SP_START + element_sp;
8163 else if (element_sp == 0x28)
8164 element_rnd = EL_INVISIBLE_WALL;
8169 int map_action_SP_to_RND(int action_sp)
8173 case actActive: return ACTION_ACTIVE;
8174 case actImpact: return ACTION_IMPACT;
8175 case actExploding: return ACTION_EXPLODING;
8176 case actDigging: return ACTION_DIGGING;
8177 case actSnapping: return ACTION_SNAPPING;
8178 case actCollecting: return ACTION_COLLECTING;
8179 case actPassing: return ACTION_PASSING;
8180 case actPushing: return ACTION_PUSHING;
8181 case actDropping: return ACTION_DROPPING;
8183 default: return ACTION_DEFAULT;
8187 int map_element_RND_to_MM(int element_rnd)
8189 return (element_rnd >= EL_MM_START_1 &&
8190 element_rnd <= EL_MM_END_1 ?
8191 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8193 element_rnd >= EL_MM_START_2 &&
8194 element_rnd <= EL_MM_END_2 ?
8195 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8197 element_rnd >= EL_CHAR_START &&
8198 element_rnd <= EL_CHAR_END ?
8199 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8201 element_rnd >= EL_MM_RUNTIME_START &&
8202 element_rnd <= EL_MM_RUNTIME_END ?
8203 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8205 element_rnd >= EL_MM_DUMMY_START &&
8206 element_rnd <= EL_MM_DUMMY_END ?
8207 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8209 EL_MM_EMPTY_NATIVE);
8212 int map_element_MM_to_RND(int element_mm)
8214 return (element_mm == EL_MM_EMPTY_NATIVE ||
8215 element_mm == EL_DF_EMPTY_NATIVE ?
8218 element_mm >= EL_MM_START_1_NATIVE &&
8219 element_mm <= EL_MM_END_1_NATIVE ?
8220 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8222 element_mm >= EL_MM_START_2_NATIVE &&
8223 element_mm <= EL_MM_END_2_NATIVE ?
8224 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8226 element_mm >= EL_MM_CHAR_START_NATIVE &&
8227 element_mm <= EL_MM_CHAR_END_NATIVE ?
8228 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8230 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8231 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8232 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8234 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8235 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8236 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8241 int map_action_MM_to_RND(int action_mm)
8243 // all MM actions are defined to exactly match their RND counterparts
8247 int map_sound_MM_to_RND(int sound_mm)
8251 case SND_MM_GAME_LEVELTIME_CHARGING:
8252 return SND_GAME_LEVELTIME_CHARGING;
8254 case SND_MM_GAME_HEALTH_CHARGING:
8255 return SND_GAME_HEALTH_CHARGING;
8258 return SND_UNDEFINED;
8262 int map_mm_wall_element(int element)
8264 return (element >= EL_MM_STEEL_WALL_START &&
8265 element <= EL_MM_STEEL_WALL_END ?
8268 element >= EL_MM_WOODEN_WALL_START &&
8269 element <= EL_MM_WOODEN_WALL_END ?
8272 element >= EL_MM_ICE_WALL_START &&
8273 element <= EL_MM_ICE_WALL_END ?
8276 element >= EL_MM_AMOEBA_WALL_START &&
8277 element <= EL_MM_AMOEBA_WALL_END ?
8280 element >= EL_DF_STEEL_WALL_START &&
8281 element <= EL_DF_STEEL_WALL_END ?
8284 element >= EL_DF_WOODEN_WALL_START &&
8285 element <= EL_DF_WOODEN_WALL_END ?
8291 int map_mm_wall_element_editor(int element)
8295 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8296 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8297 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8298 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8299 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8300 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8302 default: return element;
8306 int get_next_element(int element)
8310 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8311 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8312 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8313 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8314 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8315 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8316 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8317 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8318 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8319 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8320 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8322 default: return element;
8326 int el2img_mm(int element_mm)
8328 return el2img(map_element_MM_to_RND(element_mm));
8331 int el_act_dir2img(int element, int action, int direction)
8333 element = GFX_ELEMENT(element);
8334 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8336 // direction_graphic[][] == graphic[] for undefined direction graphics
8337 return element_info[element].direction_graphic[action][direction];
8340 static int el_act_dir2crm(int element, int action, int direction)
8342 element = GFX_ELEMENT(element);
8343 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8345 // direction_graphic[][] == graphic[] for undefined direction graphics
8346 return element_info[element].direction_crumbled[action][direction];
8349 int el_act2img(int element, int action)
8351 element = GFX_ELEMENT(element);
8353 return element_info[element].graphic[action];
8356 int el_act2crm(int element, int action)
8358 element = GFX_ELEMENT(element);
8360 return element_info[element].crumbled[action];
8363 int el_dir2img(int element, int direction)
8365 element = GFX_ELEMENT(element);
8367 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8370 int el2baseimg(int element)
8372 return element_info[element].graphic[ACTION_DEFAULT];
8375 int el2img(int element)
8377 element = GFX_ELEMENT(element);
8379 return element_info[element].graphic[ACTION_DEFAULT];
8382 int el2edimg(int element)
8384 element = GFX_ELEMENT(element);
8386 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8389 int el2preimg(int element)
8391 element = GFX_ELEMENT(element);
8393 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8396 int el2panelimg(int element)
8398 element = GFX_ELEMENT(element);
8400 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8403 int font2baseimg(int font_nr)
8405 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8408 int getBeltNrFromBeltElement(int element)
8410 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8411 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8412 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8415 int getBeltNrFromBeltActiveElement(int element)
8417 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8418 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8419 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8422 int getBeltNrFromBeltSwitchElement(int element)
8424 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8425 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8426 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8429 int getBeltDirNrFromBeltElement(int element)
8431 static int belt_base_element[4] =
8433 EL_CONVEYOR_BELT_1_LEFT,
8434 EL_CONVEYOR_BELT_2_LEFT,
8435 EL_CONVEYOR_BELT_3_LEFT,
8436 EL_CONVEYOR_BELT_4_LEFT
8439 int belt_nr = getBeltNrFromBeltElement(element);
8440 int belt_dir_nr = element - belt_base_element[belt_nr];
8442 return (belt_dir_nr % 3);
8445 int getBeltDirNrFromBeltSwitchElement(int element)
8447 static int belt_base_element[4] =
8449 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8450 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8451 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8452 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8455 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8456 int belt_dir_nr = element - belt_base_element[belt_nr];
8458 return (belt_dir_nr % 3);
8461 int getBeltDirFromBeltElement(int element)
8463 static int belt_move_dir[3] =
8470 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8472 return belt_move_dir[belt_dir_nr];
8475 int getBeltDirFromBeltSwitchElement(int element)
8477 static int belt_move_dir[3] =
8484 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8486 return belt_move_dir[belt_dir_nr];
8489 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8491 static int belt_base_element[4] =
8493 EL_CONVEYOR_BELT_1_LEFT,
8494 EL_CONVEYOR_BELT_2_LEFT,
8495 EL_CONVEYOR_BELT_3_LEFT,
8496 EL_CONVEYOR_BELT_4_LEFT
8499 return belt_base_element[belt_nr] + belt_dir_nr;
8502 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8504 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8506 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8509 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8511 static int belt_base_element[4] =
8513 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8514 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8515 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8516 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8519 return belt_base_element[belt_nr] + belt_dir_nr;
8522 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8524 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8526 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8529 boolean swapTiles_EM(boolean is_pre_emc_cave)
8531 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8534 boolean getTeamMode_EM(void)
8536 return game.team_mode || network_playing;
8539 boolean isActivePlayer_EM(int player_nr)
8541 return stored_player[player_nr].active;
8544 unsigned int InitRND(int seed)
8546 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8547 return InitEngineRandom_EM(seed);
8548 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8549 return InitEngineRandom_SP(seed);
8550 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8551 return InitEngineRandom_MM(seed);
8553 return InitEngineRandom_RND(seed);
8556 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8557 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8559 static int get_effective_element_EM(int tile, int frame_em)
8561 int element = object_mapping[tile].element_rnd;
8562 int action = object_mapping[tile].action;
8563 boolean is_backside = object_mapping[tile].is_backside;
8564 boolean action_removing = (action == ACTION_DIGGING ||
8565 action == ACTION_SNAPPING ||
8566 action == ACTION_COLLECTING);
8574 return (frame_em > 5 ? EL_EMPTY : element);
8580 else // frame_em == 7
8591 case Ydiamond_stone:
8595 case Xdrip_stretchB:
8611 case Ymagnify_blank:
8614 case Xsand_stonein_1:
8615 case Xsand_stonein_2:
8616 case Xsand_stonein_3:
8617 case Xsand_stonein_4:
8621 return (is_backside || action_removing ? EL_EMPTY : element);
8626 static boolean check_linear_animation_EM(int tile)
8630 case Xsand_stonesand_1:
8631 case Xsand_stonesand_quickout_1:
8632 case Xsand_sandstone_1:
8633 case Xsand_stonein_1:
8634 case Xsand_stoneout_1:
8662 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8663 boolean has_crumbled_graphics,
8664 int crumbled, int sync_frame)
8666 // if element can be crumbled, but certain action graphics are just empty
8667 // space (like instantly snapping sand to empty space in 1 frame), do not
8668 // treat these empty space graphics as crumbled graphics in EMC engine
8669 if (crumbled == IMG_EMPTY_SPACE)
8670 has_crumbled_graphics = FALSE;
8672 if (has_crumbled_graphics)
8674 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8675 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8676 g_crumbled->anim_delay,
8677 g_crumbled->anim_mode,
8678 g_crumbled->anim_start_frame,
8681 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8682 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8684 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8685 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8687 g_em->has_crumbled_graphics = TRUE;
8691 g_em->crumbled_bitmap = NULL;
8692 g_em->crumbled_src_x = 0;
8693 g_em->crumbled_src_y = 0;
8694 g_em->crumbled_border_size = 0;
8695 g_em->crumbled_tile_size = 0;
8697 g_em->has_crumbled_graphics = FALSE;
8702 void ResetGfxAnimation_EM(int x, int y, int tile)
8708 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8709 int tile, int frame_em, int x, int y)
8711 int action = object_mapping[tile].action;
8712 int direction = object_mapping[tile].direction;
8713 int effective_element = get_effective_element_EM(tile, frame_em);
8714 int graphic = (direction == MV_NONE ?
8715 el_act2img(effective_element, action) :
8716 el_act_dir2img(effective_element, action, direction));
8717 struct GraphicInfo *g = &graphic_info[graphic];
8719 boolean action_removing = (action == ACTION_DIGGING ||
8720 action == ACTION_SNAPPING ||
8721 action == ACTION_COLLECTING);
8722 boolean action_moving = (action == ACTION_FALLING ||
8723 action == ACTION_MOVING ||
8724 action == ACTION_PUSHING ||
8725 action == ACTION_EATING ||
8726 action == ACTION_FILLING ||
8727 action == ACTION_EMPTYING);
8728 boolean action_falling = (action == ACTION_FALLING ||
8729 action == ACTION_FILLING ||
8730 action == ACTION_EMPTYING);
8732 // special case: graphic uses "2nd movement tile" and has defined
8733 // 7 frames for movement animation (or less) => use default graphic
8734 // for last (8th) frame which ends the movement animation
8735 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8737 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8738 graphic = (direction == MV_NONE ?
8739 el_act2img(effective_element, action) :
8740 el_act_dir2img(effective_element, action, direction));
8742 g = &graphic_info[graphic];
8745 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8749 else if (action_moving)
8751 boolean is_backside = object_mapping[tile].is_backside;
8755 int direction = object_mapping[tile].direction;
8756 int move_dir = (action_falling ? MV_DOWN : direction);
8761 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8762 if (g->double_movement && frame_em == 0)
8766 if (move_dir == MV_LEFT)
8767 GfxFrame[x - 1][y] = GfxFrame[x][y];
8768 else if (move_dir == MV_RIGHT)
8769 GfxFrame[x + 1][y] = GfxFrame[x][y];
8770 else if (move_dir == MV_UP)
8771 GfxFrame[x][y - 1] = GfxFrame[x][y];
8772 else if (move_dir == MV_DOWN)
8773 GfxFrame[x][y + 1] = GfxFrame[x][y];
8780 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8781 if (tile == Xsand_stonesand_quickout_1 ||
8782 tile == Xsand_stonesand_quickout_2)
8786 if (graphic_info[graphic].anim_global_sync)
8787 sync_frame = FrameCounter;
8788 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8789 sync_frame = GfxFrame[x][y];
8791 sync_frame = 0; // playfield border (pseudo steel)
8793 SetRandomAnimationValue(x, y);
8795 int frame = getAnimationFrame(g->anim_frames,
8798 g->anim_start_frame,
8801 g_em->unique_identifier =
8802 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8805 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8806 int tile, int frame_em, int x, int y)
8808 int action = object_mapping[tile].action;
8809 int direction = object_mapping[tile].direction;
8810 boolean is_backside = object_mapping[tile].is_backside;
8811 int effective_element = get_effective_element_EM(tile, frame_em);
8812 int effective_action = action;
8813 int graphic = (direction == MV_NONE ?
8814 el_act2img(effective_element, effective_action) :
8815 el_act_dir2img(effective_element, effective_action,
8817 int crumbled = (direction == MV_NONE ?
8818 el_act2crm(effective_element, effective_action) :
8819 el_act_dir2crm(effective_element, effective_action,
8821 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8822 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8823 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8824 struct GraphicInfo *g = &graphic_info[graphic];
8827 // special case: graphic uses "2nd movement tile" and has defined
8828 // 7 frames for movement animation (or less) => use default graphic
8829 // for last (8th) frame which ends the movement animation
8830 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8832 effective_action = ACTION_DEFAULT;
8833 graphic = (direction == MV_NONE ?
8834 el_act2img(effective_element, effective_action) :
8835 el_act_dir2img(effective_element, effective_action,
8837 crumbled = (direction == MV_NONE ?
8838 el_act2crm(effective_element, effective_action) :
8839 el_act_dir2crm(effective_element, effective_action,
8842 g = &graphic_info[graphic];
8845 if (graphic_info[graphic].anim_global_sync)
8846 sync_frame = FrameCounter;
8847 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8848 sync_frame = GfxFrame[x][y];
8850 sync_frame = 0; // playfield border (pseudo steel)
8852 SetRandomAnimationValue(x, y);
8854 int frame = getAnimationFrame(g->anim_frames,
8857 g->anim_start_frame,
8860 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8861 g->double_movement && is_backside);
8863 // (updating the "crumbled" graphic definitions is probably not really needed,
8864 // as animations for crumbled graphics can't be longer than one EMC cycle)
8865 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8869 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8870 int player_nr, int anim, int frame_em)
8872 int element = player_mapping[player_nr][anim].element_rnd;
8873 int action = player_mapping[player_nr][anim].action;
8874 int direction = player_mapping[player_nr][anim].direction;
8875 int graphic = (direction == MV_NONE ?
8876 el_act2img(element, action) :
8877 el_act_dir2img(element, action, direction));
8878 struct GraphicInfo *g = &graphic_info[graphic];
8881 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8883 stored_player[player_nr].StepFrame = frame_em;
8885 sync_frame = stored_player[player_nr].Frame;
8887 int frame = getAnimationFrame(g->anim_frames,
8890 g->anim_start_frame,
8893 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8894 &g_em->src_x, &g_em->src_y, FALSE);
8897 void InitGraphicInfo_EM(void)
8901 // always start with reliable default values
8902 for (i = 0; i < GAME_TILE_MAX; i++)
8904 object_mapping[i].element_rnd = EL_UNKNOWN;
8905 object_mapping[i].is_backside = FALSE;
8906 object_mapping[i].action = ACTION_DEFAULT;
8907 object_mapping[i].direction = MV_NONE;
8910 // always start with reliable default values
8911 for (p = 0; p < MAX_PLAYERS; p++)
8913 for (i = 0; i < PLY_MAX; i++)
8915 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8916 player_mapping[p][i].action = ACTION_DEFAULT;
8917 player_mapping[p][i].direction = MV_NONE;
8921 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8923 int e = em_object_mapping_list[i].element_em;
8925 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8926 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8928 if (em_object_mapping_list[i].action != -1)
8929 object_mapping[e].action = em_object_mapping_list[i].action;
8931 if (em_object_mapping_list[i].direction != -1)
8932 object_mapping[e].direction =
8933 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8936 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8938 int a = em_player_mapping_list[i].action_em;
8939 int p = em_player_mapping_list[i].player_nr;
8941 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8943 if (em_player_mapping_list[i].action != -1)
8944 player_mapping[p][a].action = em_player_mapping_list[i].action;
8946 if (em_player_mapping_list[i].direction != -1)
8947 player_mapping[p][a].direction =
8948 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8951 for (i = 0; i < GAME_TILE_MAX; i++)
8953 int element = object_mapping[i].element_rnd;
8954 int action = object_mapping[i].action;
8955 int direction = object_mapping[i].direction;
8956 boolean is_backside = object_mapping[i].is_backside;
8957 boolean action_exploding = ((action == ACTION_EXPLODING ||
8958 action == ACTION_SMASHED_BY_ROCK ||
8959 action == ACTION_SMASHED_BY_SPRING) &&
8960 element != EL_DIAMOND);
8961 boolean action_active = (action == ACTION_ACTIVE);
8962 boolean action_other = (action == ACTION_OTHER);
8964 for (j = 0; j < 8; j++)
8966 int effective_element = get_effective_element_EM(i, j);
8967 int effective_action = (j < 7 ? action :
8968 i == Xdrip_stretch ? action :
8969 i == Xdrip_stretchB ? action :
8970 i == Ydrip_1_s ? action :
8971 i == Ydrip_1_sB ? action :
8972 i == Yball_1 ? action :
8973 i == Xball_2 ? action :
8974 i == Yball_2 ? action :
8975 i == Yball_blank ? action :
8976 i == Ykey_1_blank ? action :
8977 i == Ykey_2_blank ? action :
8978 i == Ykey_3_blank ? action :
8979 i == Ykey_4_blank ? action :
8980 i == Ykey_5_blank ? action :
8981 i == Ykey_6_blank ? action :
8982 i == Ykey_7_blank ? action :
8983 i == Ykey_8_blank ? action :
8984 i == Ylenses_blank ? action :
8985 i == Ymagnify_blank ? action :
8986 i == Ygrass_blank ? action :
8987 i == Ydirt_blank ? action :
8988 i == Xsand_stonein_1 ? action :
8989 i == Xsand_stonein_2 ? action :
8990 i == Xsand_stonein_3 ? action :
8991 i == Xsand_stonein_4 ? action :
8992 i == Xsand_stoneout_1 ? action :
8993 i == Xsand_stoneout_2 ? action :
8994 i == Xboom_android ? ACTION_EXPLODING :
8995 action_exploding ? ACTION_EXPLODING :
8996 action_active ? action :
8997 action_other ? action :
8999 int graphic = (el_act_dir2img(effective_element, effective_action,
9001 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9003 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9004 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9005 boolean has_action_graphics = (graphic != base_graphic);
9006 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9007 struct GraphicInfo *g = &graphic_info[graphic];
9008 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9011 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9012 boolean special_animation = (action != ACTION_DEFAULT &&
9013 g->anim_frames == 3 &&
9014 g->anim_delay == 2 &&
9015 g->anim_mode & ANIM_LINEAR);
9016 int sync_frame = (i == Xdrip_stretch ? 7 :
9017 i == Xdrip_stretchB ? 7 :
9018 i == Ydrip_2_s ? j + 8 :
9019 i == Ydrip_2_sB ? j + 8 :
9028 i == Xfake_acid_1 ? 0 :
9029 i == Xfake_acid_2 ? 10 :
9030 i == Xfake_acid_3 ? 20 :
9031 i == Xfake_acid_4 ? 30 :
9032 i == Xfake_acid_5 ? 40 :
9033 i == Xfake_acid_6 ? 50 :
9034 i == Xfake_acid_7 ? 60 :
9035 i == Xfake_acid_8 ? 70 :
9036 i == Xfake_acid_1_player ? 0 :
9037 i == Xfake_acid_2_player ? 10 :
9038 i == Xfake_acid_3_player ? 20 :
9039 i == Xfake_acid_4_player ? 30 :
9040 i == Xfake_acid_5_player ? 40 :
9041 i == Xfake_acid_6_player ? 50 :
9042 i == Xfake_acid_7_player ? 60 :
9043 i == Xfake_acid_8_player ? 70 :
9045 i == Yball_2 ? j + 8 :
9046 i == Yball_blank ? j + 1 :
9047 i == Ykey_1_blank ? j + 1 :
9048 i == Ykey_2_blank ? j + 1 :
9049 i == Ykey_3_blank ? j + 1 :
9050 i == Ykey_4_blank ? j + 1 :
9051 i == Ykey_5_blank ? j + 1 :
9052 i == Ykey_6_blank ? j + 1 :
9053 i == Ykey_7_blank ? j + 1 :
9054 i == Ykey_8_blank ? j + 1 :
9055 i == Ylenses_blank ? j + 1 :
9056 i == Ymagnify_blank ? j + 1 :
9057 i == Ygrass_blank ? j + 1 :
9058 i == Ydirt_blank ? j + 1 :
9059 i == Xamoeba_1 ? 0 :
9060 i == Xamoeba_2 ? 1 :
9061 i == Xamoeba_3 ? 2 :
9062 i == Xamoeba_4 ? 3 :
9063 i == Xamoeba_5 ? 0 :
9064 i == Xamoeba_6 ? 1 :
9065 i == Xamoeba_7 ? 2 :
9066 i == Xamoeba_8 ? 3 :
9067 i == Xexit_2 ? j + 8 :
9068 i == Xexit_3 ? j + 16 :
9069 i == Xdynamite_1 ? 0 :
9070 i == Xdynamite_2 ? 8 :
9071 i == Xdynamite_3 ? 16 :
9072 i == Xdynamite_4 ? 24 :
9073 i == Xsand_stonein_1 ? j + 1 :
9074 i == Xsand_stonein_2 ? j + 9 :
9075 i == Xsand_stonein_3 ? j + 17 :
9076 i == Xsand_stonein_4 ? j + 25 :
9077 i == Xsand_stoneout_1 && j == 0 ? 0 :
9078 i == Xsand_stoneout_1 && j == 1 ? 0 :
9079 i == Xsand_stoneout_1 && j == 2 ? 1 :
9080 i == Xsand_stoneout_1 && j == 3 ? 2 :
9081 i == Xsand_stoneout_1 && j == 4 ? 2 :
9082 i == Xsand_stoneout_1 && j == 5 ? 3 :
9083 i == Xsand_stoneout_1 && j == 6 ? 4 :
9084 i == Xsand_stoneout_1 && j == 7 ? 4 :
9085 i == Xsand_stoneout_2 && j == 0 ? 5 :
9086 i == Xsand_stoneout_2 && j == 1 ? 6 :
9087 i == Xsand_stoneout_2 && j == 2 ? 7 :
9088 i == Xsand_stoneout_2 && j == 3 ? 8 :
9089 i == Xsand_stoneout_2 && j == 4 ? 9 :
9090 i == Xsand_stoneout_2 && j == 5 ? 11 :
9091 i == Xsand_stoneout_2 && j == 6 ? 13 :
9092 i == Xsand_stoneout_2 && j == 7 ? 15 :
9093 i == Xboom_bug && j == 1 ? 2 :
9094 i == Xboom_bug && j == 2 ? 2 :
9095 i == Xboom_bug && j == 3 ? 4 :
9096 i == Xboom_bug && j == 4 ? 4 :
9097 i == Xboom_bug && j == 5 ? 2 :
9098 i == Xboom_bug && j == 6 ? 2 :
9099 i == Xboom_bug && j == 7 ? 0 :
9100 i == Xboom_tank && j == 1 ? 2 :
9101 i == Xboom_tank && j == 2 ? 2 :
9102 i == Xboom_tank && j == 3 ? 4 :
9103 i == Xboom_tank && j == 4 ? 4 :
9104 i == Xboom_tank && j == 5 ? 2 :
9105 i == Xboom_tank && j == 6 ? 2 :
9106 i == Xboom_tank && j == 7 ? 0 :
9107 i == Xboom_android && j == 7 ? 6 :
9108 i == Xboom_1 && j == 1 ? 2 :
9109 i == Xboom_1 && j == 2 ? 2 :
9110 i == Xboom_1 && j == 3 ? 4 :
9111 i == Xboom_1 && j == 4 ? 4 :
9112 i == Xboom_1 && j == 5 ? 6 :
9113 i == Xboom_1 && j == 6 ? 6 :
9114 i == Xboom_1 && j == 7 ? 8 :
9115 i == Xboom_2 && j == 0 ? 8 :
9116 i == Xboom_2 && j == 1 ? 8 :
9117 i == Xboom_2 && j == 2 ? 10 :
9118 i == Xboom_2 && j == 3 ? 10 :
9119 i == Xboom_2 && j == 4 ? 10 :
9120 i == Xboom_2 && j == 5 ? 12 :
9121 i == Xboom_2 && j == 6 ? 12 :
9122 i == Xboom_2 && j == 7 ? 12 :
9123 special_animation && j == 4 ? 3 :
9124 effective_action != action ? 0 :
9126 int frame = getAnimationFrame(g->anim_frames,
9129 g->anim_start_frame,
9132 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9133 g->double_movement && is_backside);
9135 g_em->bitmap = src_bitmap;
9136 g_em->src_x = src_x;
9137 g_em->src_y = src_y;
9138 g_em->src_offset_x = 0;
9139 g_em->src_offset_y = 0;
9140 g_em->dst_offset_x = 0;
9141 g_em->dst_offset_y = 0;
9142 g_em->width = TILEX;
9143 g_em->height = TILEY;
9145 g_em->preserve_background = FALSE;
9147 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9150 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9151 effective_action == ACTION_MOVING ||
9152 effective_action == ACTION_PUSHING ||
9153 effective_action == ACTION_EATING)) ||
9154 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9155 effective_action == ACTION_EMPTYING)))
9158 (effective_action == ACTION_FALLING ||
9159 effective_action == ACTION_FILLING ||
9160 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9161 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9162 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9163 int num_steps = (i == Ydrip_1_s ? 16 :
9164 i == Ydrip_1_sB ? 16 :
9165 i == Ydrip_2_s ? 16 :
9166 i == Ydrip_2_sB ? 16 :
9167 i == Xsand_stonein_1 ? 32 :
9168 i == Xsand_stonein_2 ? 32 :
9169 i == Xsand_stonein_3 ? 32 :
9170 i == Xsand_stonein_4 ? 32 :
9171 i == Xsand_stoneout_1 ? 16 :
9172 i == Xsand_stoneout_2 ? 16 : 8);
9173 int cx = ABS(dx) * (TILEX / num_steps);
9174 int cy = ABS(dy) * (TILEY / num_steps);
9175 int step_frame = (i == Ydrip_2_s ? j + 8 :
9176 i == Ydrip_2_sB ? j + 8 :
9177 i == Xsand_stonein_2 ? j + 8 :
9178 i == Xsand_stonein_3 ? j + 16 :
9179 i == Xsand_stonein_4 ? j + 24 :
9180 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9181 int step = (is_backside ? step_frame : num_steps - step_frame);
9183 if (is_backside) // tile where movement starts
9185 if (dx < 0 || dy < 0)
9187 g_em->src_offset_x = cx * step;
9188 g_em->src_offset_y = cy * step;
9192 g_em->dst_offset_x = cx * step;
9193 g_em->dst_offset_y = cy * step;
9196 else // tile where movement ends
9198 if (dx < 0 || dy < 0)
9200 g_em->dst_offset_x = cx * step;
9201 g_em->dst_offset_y = cy * step;
9205 g_em->src_offset_x = cx * step;
9206 g_em->src_offset_y = cy * step;
9210 g_em->width = TILEX - cx * step;
9211 g_em->height = TILEY - cy * step;
9214 // create unique graphic identifier to decide if tile must be redrawn
9215 /* bit 31 - 16 (16 bit): EM style graphic
9216 bit 15 - 12 ( 4 bit): EM style frame
9217 bit 11 - 6 ( 6 bit): graphic width
9218 bit 5 - 0 ( 6 bit): graphic height */
9219 g_em->unique_identifier =
9220 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9224 for (i = 0; i < GAME_TILE_MAX; i++)
9226 for (j = 0; j < 8; j++)
9228 int element = object_mapping[i].element_rnd;
9229 int action = object_mapping[i].action;
9230 int direction = object_mapping[i].direction;
9231 boolean is_backside = object_mapping[i].is_backside;
9232 int graphic_action = el_act_dir2img(element, action, direction);
9233 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9235 if ((action == ACTION_SMASHED_BY_ROCK ||
9236 action == ACTION_SMASHED_BY_SPRING ||
9237 action == ACTION_EATING) &&
9238 graphic_action == graphic_default)
9240 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9241 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9242 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9243 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9246 // no separate animation for "smashed by rock" -- use rock instead
9247 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9248 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9250 g_em->bitmap = g_xx->bitmap;
9251 g_em->src_x = g_xx->src_x;
9252 g_em->src_y = g_xx->src_y;
9253 g_em->src_offset_x = g_xx->src_offset_x;
9254 g_em->src_offset_y = g_xx->src_offset_y;
9255 g_em->dst_offset_x = g_xx->dst_offset_x;
9256 g_em->dst_offset_y = g_xx->dst_offset_y;
9257 g_em->width = g_xx->width;
9258 g_em->height = g_xx->height;
9259 g_em->unique_identifier = g_xx->unique_identifier;
9262 g_em->preserve_background = TRUE;
9267 for (p = 0; p < MAX_PLAYERS; p++)
9269 for (i = 0; i < PLY_MAX; i++)
9271 int element = player_mapping[p][i].element_rnd;
9272 int action = player_mapping[p][i].action;
9273 int direction = player_mapping[p][i].direction;
9275 for (j = 0; j < 8; j++)
9277 int effective_element = element;
9278 int effective_action = action;
9279 int graphic = (direction == MV_NONE ?
9280 el_act2img(effective_element, effective_action) :
9281 el_act_dir2img(effective_element, effective_action,
9283 struct GraphicInfo *g = &graphic_info[graphic];
9284 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9288 int frame = getAnimationFrame(g->anim_frames,
9291 g->anim_start_frame,
9294 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9296 g_em->bitmap = src_bitmap;
9297 g_em->src_x = src_x;
9298 g_em->src_y = src_y;
9299 g_em->src_offset_x = 0;
9300 g_em->src_offset_y = 0;
9301 g_em->dst_offset_x = 0;
9302 g_em->dst_offset_y = 0;
9303 g_em->width = TILEX;
9304 g_em->height = TILEY;
9310 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9311 boolean any_player_moving,
9312 boolean any_player_snapping,
9313 boolean any_player_dropping)
9315 if (frame == 7 && !any_player_dropping)
9317 if (!local_player->was_waiting)
9319 if (!CheckSaveEngineSnapshotToList())
9322 local_player->was_waiting = TRUE;
9325 else if (any_player_moving || any_player_snapping || any_player_dropping)
9327 local_player->was_waiting = FALSE;
9331 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9332 boolean murphy_is_dropping)
9334 if (murphy_is_waiting)
9336 if (!local_player->was_waiting)
9338 if (!CheckSaveEngineSnapshotToList())
9341 local_player->was_waiting = TRUE;
9346 local_player->was_waiting = FALSE;
9350 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9351 boolean button_released)
9353 if (button_released)
9355 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9356 CheckSaveEngineSnapshotToList();
9358 else if (element_clicked)
9360 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9361 CheckSaveEngineSnapshotToList();
9363 game.snapshot.changed_action = TRUE;
9367 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9368 boolean any_player_moving,
9369 boolean any_player_snapping,
9370 boolean any_player_dropping)
9372 if (tape.single_step && tape.recording && !tape.pausing)
9373 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9374 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9376 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9377 any_player_snapping, any_player_dropping);
9379 return tape.pausing;
9382 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9383 boolean murphy_is_dropping)
9385 boolean murphy_starts_dropping = FALSE;
9388 for (i = 0; i < MAX_PLAYERS; i++)
9389 if (stored_player[i].force_dropping)
9390 murphy_starts_dropping = TRUE;
9392 if (tape.single_step && tape.recording && !tape.pausing)
9393 if (murphy_is_waiting && !murphy_starts_dropping)
9394 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9396 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9399 void CheckSingleStepMode_MM(boolean element_clicked,
9400 boolean button_released)
9402 if (tape.single_step && tape.recording && !tape.pausing)
9403 if (button_released)
9404 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9406 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9409 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9410 int graphic, int sync_frame, int x, int y)
9412 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9414 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9417 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9419 return (IS_NEXT_FRAME(sync_frame, graphic));
9422 int getGraphicInfo_Delay(int graphic)
9424 return graphic_info[graphic].anim_delay;
9427 void PlayMenuSoundExt(int sound)
9429 if (sound == SND_UNDEFINED)
9432 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9433 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9436 if (IS_LOOP_SOUND(sound))
9437 PlaySoundLoop(sound);
9442 void PlayMenuSound(void)
9444 PlayMenuSoundExt(menu.sound[game_status]);
9447 void PlayMenuSoundStereo(int sound, int stereo_position)
9449 if (sound == SND_UNDEFINED)
9452 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9453 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9456 if (IS_LOOP_SOUND(sound))
9457 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9459 PlaySoundStereo(sound, stereo_position);
9462 void PlayMenuSoundIfLoopExt(int sound)
9464 if (sound == SND_UNDEFINED)
9467 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9468 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9471 if (IS_LOOP_SOUND(sound))
9472 PlaySoundLoop(sound);
9475 void PlayMenuSoundIfLoop(void)
9477 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9480 void PlayMenuMusicExt(int music)
9482 if (music == MUS_UNDEFINED)
9485 if (!setup.sound_music)
9488 if (IS_LOOP_MUSIC(music))
9489 PlayMusicLoop(music);
9494 void PlayMenuMusic(void)
9496 char *curr_music = getCurrentlyPlayingMusicFilename();
9497 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9499 if (!strEqual(curr_music, next_music))
9500 PlayMenuMusicExt(menu.music[game_status]);
9503 void PlayMenuSoundsAndMusic(void)
9509 static void FadeMenuSounds(void)
9514 static void FadeMenuMusic(void)
9516 char *curr_music = getCurrentlyPlayingMusicFilename();
9517 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9519 if (!strEqual(curr_music, next_music))
9523 void FadeMenuSoundsAndMusic(void)
9529 void PlaySoundActivating(void)
9532 PlaySound(SND_MENU_ITEM_ACTIVATING);
9536 void PlaySoundSelecting(void)
9539 PlaySound(SND_MENU_ITEM_SELECTING);
9543 void ToggleFullscreenIfNeeded(void)
9545 // if setup and video fullscreen state are already matching, nothing do do
9546 if (setup.fullscreen == video.fullscreen_enabled ||
9547 !video.fullscreen_available)
9550 SDLSetWindowFullscreen(setup.fullscreen);
9552 // set setup value according to successfully changed fullscreen mode
9553 setup.fullscreen = video.fullscreen_enabled;
9556 void ChangeWindowScalingIfNeeded(void)
9558 // if setup and video window scaling are already matching, nothing do do
9559 if (setup.window_scaling_percent == video.window_scaling_percent ||
9560 video.fullscreen_enabled)
9563 SDLSetWindowScaling(setup.window_scaling_percent);
9565 // set setup value according to successfully changed window scaling
9566 setup.window_scaling_percent = video.window_scaling_percent;
9569 void ChangeVsyncModeIfNeeded(void)
9571 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9572 int video_vsync_mode = video.vsync_mode;
9574 // if setup and video vsync mode are already matching, nothing do do
9575 if (setup_vsync_mode == video_vsync_mode)
9578 // if renderer is using OpenGL, vsync mode can directly be changed
9579 SDLSetScreenVsyncMode(setup.vsync_mode);
9581 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9582 if (video.vsync_mode == video_vsync_mode)
9584 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9586 // save backbuffer content which gets lost when re-creating screen
9587 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9589 // force re-creating screen and renderer to set new vsync mode
9590 video.fullscreen_enabled = !setup.fullscreen;
9592 // when creating new renderer, destroy textures linked to old renderer
9593 FreeAllImageTextures(); // needs old renderer to free the textures
9595 // re-create screen and renderer (including change of vsync mode)
9596 ChangeVideoModeIfNeeded(setup.fullscreen);
9598 // set setup value according to successfully changed fullscreen mode
9599 setup.fullscreen = video.fullscreen_enabled;
9601 // restore backbuffer content from temporary backbuffer backup bitmap
9602 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9603 FreeBitmap(tmp_backbuffer);
9605 // update visible window/screen
9606 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9608 // when changing vsync mode, re-create textures for new renderer
9609 InitImageTextures();
9612 // set setup value according to successfully changed vsync mode
9613 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9616 static void JoinRectangles(int *x, int *y, int *width, int *height,
9617 int x2, int y2, int width2, int height2)
9619 // do not join with "off-screen" rectangle
9620 if (x2 == -1 || y2 == -1)
9625 *width = MAX(*width, width2);
9626 *height = MAX(*height, height2);
9629 void SetAnimStatus(int anim_status_new)
9631 if (anim_status_new == GAME_MODE_MAIN)
9632 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9633 else if (anim_status_new == GAME_MODE_NAMES)
9634 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9635 else if (anim_status_new == GAME_MODE_SCORES)
9636 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9638 global.anim_status_next = anim_status_new;
9640 // directly set screen modes that are entered without fading
9641 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9642 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9643 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9644 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9645 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9646 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9647 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9648 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9649 global.anim_status = global.anim_status_next;
9652 void SetGameStatus(int game_status_new)
9654 if (game_status_new != game_status)
9655 game_status_last_screen = game_status;
9657 game_status = game_status_new;
9659 SetAnimStatus(game_status_new);
9662 void SetFontStatus(int game_status_new)
9664 static int last_game_status = -1;
9666 if (game_status_new != -1)
9668 // set game status for font use after storing last game status
9669 last_game_status = game_status;
9670 game_status = game_status_new;
9674 // reset game status after font use from last stored game status
9675 game_status = last_game_status;
9679 void ResetFontStatus(void)
9684 void SetLevelSetInfo(char *identifier, int level_nr)
9686 setString(&levelset.identifier, identifier);
9688 levelset.level_nr = level_nr;
9691 boolean CheckIfAllViewportsHaveChanged(void)
9693 // if game status has not changed, viewports have not changed either
9694 if (game_status == game_status_last)
9697 // check if all viewports have changed with current game status
9699 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9700 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9701 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9702 int new_real_sx = vp_playfield->x;
9703 int new_real_sy = vp_playfield->y;
9704 int new_full_sxsize = vp_playfield->width;
9705 int new_full_sysize = vp_playfield->height;
9706 int new_dx = vp_door_1->x;
9707 int new_dy = vp_door_1->y;
9708 int new_dxsize = vp_door_1->width;
9709 int new_dysize = vp_door_1->height;
9710 int new_vx = vp_door_2->x;
9711 int new_vy = vp_door_2->y;
9712 int new_vxsize = vp_door_2->width;
9713 int new_vysize = vp_door_2->height;
9715 boolean playfield_viewport_has_changed =
9716 (new_real_sx != REAL_SX ||
9717 new_real_sy != REAL_SY ||
9718 new_full_sxsize != FULL_SXSIZE ||
9719 new_full_sysize != FULL_SYSIZE);
9721 boolean door_1_viewport_has_changed =
9724 new_dxsize != DXSIZE ||
9725 new_dysize != DYSIZE);
9727 boolean door_2_viewport_has_changed =
9730 new_vxsize != VXSIZE ||
9731 new_vysize != VYSIZE ||
9732 game_status_last == GAME_MODE_EDITOR);
9734 return (playfield_viewport_has_changed &&
9735 door_1_viewport_has_changed &&
9736 door_2_viewport_has_changed);
9739 boolean CheckFadeAll(void)
9741 return (CheckIfGlobalBorderHasChanged() ||
9742 CheckIfAllViewportsHaveChanged());
9745 void ChangeViewportPropertiesIfNeeded(void)
9747 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9748 FALSE : setup.small_game_graphics);
9749 int gfx_game_mode = getGlobalGameStatus(game_status);
9750 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9752 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9753 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9754 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9755 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9756 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9757 int new_win_xsize = vp_window->width;
9758 int new_win_ysize = vp_window->height;
9759 int border_left = vp_playfield->border_left;
9760 int border_right = vp_playfield->border_right;
9761 int border_top = vp_playfield->border_top;
9762 int border_bottom = vp_playfield->border_bottom;
9763 int new_sx = vp_playfield->x + border_left;
9764 int new_sy = vp_playfield->y + border_top;
9765 int new_sxsize = vp_playfield->width - border_left - border_right;
9766 int new_sysize = vp_playfield->height - border_top - border_bottom;
9767 int new_real_sx = vp_playfield->x;
9768 int new_real_sy = vp_playfield->y;
9769 int new_full_sxsize = vp_playfield->width;
9770 int new_full_sysize = vp_playfield->height;
9771 int new_dx = vp_door_1->x;
9772 int new_dy = vp_door_1->y;
9773 int new_dxsize = vp_door_1->width;
9774 int new_dysize = vp_door_1->height;
9775 int new_vx = vp_door_2->x;
9776 int new_vy = vp_door_2->y;
9777 int new_vxsize = vp_door_2->width;
9778 int new_vysize = vp_door_2->height;
9779 int new_ex = vp_door_3->x;
9780 int new_ey = vp_door_3->y;
9781 int new_exsize = vp_door_3->width;
9782 int new_eysize = vp_door_3->height;
9783 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9784 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9785 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9786 int new_scr_fieldx = new_sxsize / tilesize;
9787 int new_scr_fieldy = new_sysize / tilesize;
9788 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9789 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9790 boolean init_gfx_buffers = FALSE;
9791 boolean init_video_buffer = FALSE;
9792 boolean init_gadgets_and_anims = FALSE;
9793 boolean init_em_graphics = FALSE;
9795 if (new_win_xsize != WIN_XSIZE ||
9796 new_win_ysize != WIN_YSIZE)
9798 WIN_XSIZE = new_win_xsize;
9799 WIN_YSIZE = new_win_ysize;
9801 init_video_buffer = TRUE;
9802 init_gfx_buffers = TRUE;
9803 init_gadgets_and_anims = TRUE;
9805 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9808 if (new_scr_fieldx != SCR_FIELDX ||
9809 new_scr_fieldy != SCR_FIELDY)
9811 // this always toggles between MAIN and GAME when using small tile size
9813 SCR_FIELDX = new_scr_fieldx;
9814 SCR_FIELDY = new_scr_fieldy;
9816 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9827 new_sxsize != SXSIZE ||
9828 new_sysize != SYSIZE ||
9829 new_dxsize != DXSIZE ||
9830 new_dysize != DYSIZE ||
9831 new_vxsize != VXSIZE ||
9832 new_vysize != VYSIZE ||
9833 new_exsize != EXSIZE ||
9834 new_eysize != EYSIZE ||
9835 new_real_sx != REAL_SX ||
9836 new_real_sy != REAL_SY ||
9837 new_full_sxsize != FULL_SXSIZE ||
9838 new_full_sysize != FULL_SYSIZE ||
9839 new_tilesize_var != TILESIZE_VAR
9842 // ------------------------------------------------------------------------
9843 // determine next fading area for changed viewport definitions
9844 // ------------------------------------------------------------------------
9846 // start with current playfield area (default fading area)
9849 FADE_SXSIZE = FULL_SXSIZE;
9850 FADE_SYSIZE = FULL_SYSIZE;
9852 // add new playfield area if position or size has changed
9853 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9854 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9856 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9857 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9860 // add current and new door 1 area if position or size has changed
9861 if (new_dx != DX || new_dy != DY ||
9862 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9864 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9865 DX, DY, DXSIZE, DYSIZE);
9866 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9867 new_dx, new_dy, new_dxsize, new_dysize);
9870 // add current and new door 2 area if position or size has changed
9871 if (new_vx != VX || new_vy != VY ||
9872 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9874 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9875 VX, VY, VXSIZE, VYSIZE);
9876 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9877 new_vx, new_vy, new_vxsize, new_vysize);
9880 // ------------------------------------------------------------------------
9881 // handle changed tile size
9882 // ------------------------------------------------------------------------
9884 if (new_tilesize_var != TILESIZE_VAR)
9886 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9888 // changing tile size invalidates scroll values of engine snapshots
9889 FreeEngineSnapshotSingle();
9891 // changing tile size requires update of graphic mapping for EM engine
9892 init_em_graphics = TRUE;
9903 SXSIZE = new_sxsize;
9904 SYSIZE = new_sysize;
9905 DXSIZE = new_dxsize;
9906 DYSIZE = new_dysize;
9907 VXSIZE = new_vxsize;
9908 VYSIZE = new_vysize;
9909 EXSIZE = new_exsize;
9910 EYSIZE = new_eysize;
9911 REAL_SX = new_real_sx;
9912 REAL_SY = new_real_sy;
9913 FULL_SXSIZE = new_full_sxsize;
9914 FULL_SYSIZE = new_full_sysize;
9915 TILESIZE_VAR = new_tilesize_var;
9917 init_gfx_buffers = TRUE;
9918 init_gadgets_and_anims = TRUE;
9920 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9921 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9924 if (init_gfx_buffers)
9926 // Debug("tools:viewport", "init_gfx_buffers");
9928 SCR_FIELDX = new_scr_fieldx_buffers;
9929 SCR_FIELDY = new_scr_fieldy_buffers;
9933 SCR_FIELDX = new_scr_fieldx;
9934 SCR_FIELDY = new_scr_fieldy;
9936 SetDrawDeactivationMask(REDRAW_NONE);
9937 SetDrawBackgroundMask(REDRAW_FIELD);
9940 if (init_video_buffer)
9942 // Debug("tools:viewport", "init_video_buffer");
9944 FreeAllImageTextures(); // needs old renderer to free the textures
9946 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9947 InitImageTextures();
9950 if (init_gadgets_and_anims)
9952 // Debug("tools:viewport", "init_gadgets_and_anims");
9955 InitGlobalAnimations();
9958 if (init_em_graphics)
9960 InitGraphicInfo_EM();
9964 void OpenURL(char *url)
9969 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9971 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9975 // ============================================================================
9977 // ============================================================================
9979 #if defined(PLATFORM_WIN32)
9980 /* FILETIME of Jan 1 1970 00:00:00. */
9981 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9984 * timezone information is stored outside the kernel so tzp isn't used anymore.
9986 * Note: this function is not for Win32 high precision timing purpose. See
9989 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9992 SYSTEMTIME system_time;
9993 ULARGE_INTEGER ularge;
9995 GetSystemTime(&system_time);
9996 SystemTimeToFileTime(&system_time, &file_time);
9997 ularge.LowPart = file_time.dwLowDateTime;
9998 ularge.HighPart = file_time.dwHighDateTime;
10000 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10001 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10007 static char *test_init_uuid_random_function_simple(void)
10009 static char seed_text[100];
10010 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10012 sprintf(seed_text, "%d", seed);
10017 static char *test_init_uuid_random_function_better(void)
10019 static char seed_text[100];
10020 struct timeval current_time;
10022 gettimeofday(¤t_time, NULL);
10024 prng_seed_bytes(¤t_time, sizeof(current_time));
10026 sprintf(seed_text, "%ld.%ld",
10027 (long)current_time.tv_sec,
10028 (long)current_time.tv_usec);
10033 #if defined(PLATFORM_WIN32)
10034 static char *test_init_uuid_random_function_better_windows(void)
10036 static char seed_text[100];
10037 struct timeval current_time;
10039 gettimeofday_windows(¤t_time, NULL);
10041 prng_seed_bytes(¤t_time, sizeof(current_time));
10043 sprintf(seed_text, "%ld.%ld",
10044 (long)current_time.tv_sec,
10045 (long)current_time.tv_usec);
10051 static unsigned int test_uuid_random_function_simple(int max)
10053 return GetSimpleRandom(max);
10056 static unsigned int test_uuid_random_function_better(int max)
10058 return (max > 0 ? prng_get_uint() % max : 0);
10061 #if defined(PLATFORM_WIN32)
10062 #define NUM_UUID_TESTS 3
10064 #define NUM_UUID_TESTS 2
10067 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10069 struct hashtable *hash_seeds =
10070 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10071 struct hashtable *hash_uuids =
10072 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10073 static char message[100];
10076 char *random_name = (nr == 0 ? "simple" : "better");
10077 char *random_type = (always_seed ? "always" : "only once");
10078 char *(*init_random_function)(void) =
10080 test_init_uuid_random_function_simple :
10081 test_init_uuid_random_function_better);
10082 unsigned int (*random_function)(int) =
10084 test_uuid_random_function_simple :
10085 test_uuid_random_function_better);
10088 #if defined(PLATFORM_WIN32)
10091 random_name = "windows";
10092 init_random_function = test_init_uuid_random_function_better_windows;
10098 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10099 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10101 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10102 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10103 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10105 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10109 // always initialize random number generator at least once
10110 init_random_function();
10112 unsigned int time_start = SDL_GetTicks();
10114 for (i = 0; i < num_uuids; i++)
10118 char *seed = getStringCopy(init_random_function());
10120 hashtable_remove(hash_seeds, seed);
10121 hashtable_insert(hash_seeds, seed, "1");
10124 char *uuid = getStringCopy(getUUIDExt(random_function));
10126 hashtable_remove(hash_uuids, uuid);
10127 hashtable_insert(hash_uuids, uuid, "1");
10130 int num_unique_seeds = hashtable_count(hash_seeds);
10131 int num_unique_uuids = hashtable_count(hash_uuids);
10133 unsigned int time_needed = SDL_GetTicks() - time_start;
10135 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10137 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10140 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10142 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10143 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10145 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10147 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10149 Request(message, REQ_CONFIRM);
10151 hashtable_destroy(hash_seeds, 0);
10152 hashtable_destroy(hash_uuids, 0);
10155 void TestGeneratingUUIDs(void)
10157 int num_uuids = 1000000;
10160 for (i = 0; i < NUM_UUID_TESTS; i++)
10161 for (j = 0; j < 2; j++)
10162 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10164 CloseAllAndExit(0);