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 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1100 if (graphic == IMG_UNDEFINED)
1103 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1105 return (graphic_info[graphic].bitmap != NULL || redefined ?
1106 graphic_info[graphic].bitmap :
1107 graphic_info[default_graphic].bitmap);
1110 static Bitmap *getBackgroundBitmap(int graphic)
1112 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1115 static Bitmap *getGlobalBorderBitmap(int graphic)
1117 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1120 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1123 (status == GAME_MODE_MAIN ||
1124 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1125 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1126 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1127 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1130 return getGlobalBorderBitmap(graphic);
1133 void SetWindowBackgroundImageIfDefined(int graphic)
1135 if (graphic_info[graphic].bitmap)
1136 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1139 void SetMainBackgroundImageIfDefined(int graphic)
1141 if (graphic_info[graphic].bitmap)
1142 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1145 void SetDoorBackgroundImageIfDefined(int graphic)
1147 if (graphic_info[graphic].bitmap)
1148 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1151 void SetWindowBackgroundImage(int graphic)
1153 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1156 void SetMainBackgroundImage(int graphic)
1158 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1161 void SetDoorBackgroundImage(int graphic)
1163 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1166 void SetPanelBackground(void)
1168 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1170 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1171 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1173 SetDoorBackgroundBitmap(bitmap_db_panel);
1176 void DrawBackground(int x, int y, int width, int height)
1178 // "drawto" might still point to playfield buffer here (hall of fame)
1179 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1181 if (IN_GFX_FIELD_FULL(x, y))
1182 redraw_mask |= REDRAW_FIELD;
1183 else if (IN_GFX_DOOR_1(x, y))
1184 redraw_mask |= REDRAW_DOOR_1;
1185 else if (IN_GFX_DOOR_2(x, y))
1186 redraw_mask |= REDRAW_DOOR_2;
1187 else if (IN_GFX_DOOR_3(x, y))
1188 redraw_mask |= REDRAW_DOOR_3;
1191 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1193 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1195 if (font->bitmap == NULL)
1198 DrawBackground(x, y, width, height);
1201 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1203 struct GraphicInfo *g = &graphic_info[graphic];
1205 if (g->bitmap == NULL)
1208 DrawBackground(x, y, width, height);
1211 static int game_status_last = -1;
1212 static Bitmap *global_border_bitmap_last = NULL;
1213 static Bitmap *global_border_bitmap = NULL;
1214 static int real_sx_last = -1, real_sy_last = -1;
1215 static int full_sxsize_last = -1, full_sysize_last = -1;
1216 static int dx_last = -1, dy_last = -1;
1217 static int dxsize_last = -1, dysize_last = -1;
1218 static int vx_last = -1, vy_last = -1;
1219 static int vxsize_last = -1, vysize_last = -1;
1220 static int ex_last = -1, ey_last = -1;
1221 static int exsize_last = -1, eysize_last = -1;
1223 boolean CheckIfGlobalBorderHasChanged(void)
1225 // if game status has not changed, global border has not changed either
1226 if (game_status == game_status_last)
1229 // determine and store new global border bitmap for current game status
1230 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1232 return (global_border_bitmap_last != global_border_bitmap);
1235 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1237 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1238 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1240 // if game status has not changed, nothing has to be redrawn
1241 if (game_status == game_status_last)
1244 // redraw if last screen was title screen
1245 if (game_status_last == GAME_MODE_TITLE)
1248 // redraw if global screen border has changed
1249 if (CheckIfGlobalBorderHasChanged())
1252 // redraw if position or size of playfield area has changed
1253 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1254 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1257 // redraw if position or size of door area has changed
1258 if (dx_last != DX || dy_last != DY ||
1259 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1262 // redraw if position or size of tape area has changed
1263 if (vx_last != VX || vy_last != VY ||
1264 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1267 // redraw if position or size of editor area has changed
1268 if (ex_last != EX || ey_last != EY ||
1269 exsize_last != EXSIZE || eysize_last != EYSIZE)
1276 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1279 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1281 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1284 void RedrawGlobalBorder(void)
1286 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1288 RedrawGlobalBorderFromBitmap(bitmap);
1290 redraw_mask = REDRAW_ALL;
1293 static void RedrawGlobalBorderIfNeeded(void)
1295 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1296 if (game_status == game_status_last)
1300 // copy current draw buffer to later copy back areas that have not changed
1301 if (game_status_last != GAME_MODE_TITLE)
1302 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1304 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1305 if (CheckIfGlobalBorderRedrawIsNeeded())
1307 // determine and store new global border bitmap for current game status
1308 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1311 // redraw global screen border (or clear, if defined to be empty)
1312 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1314 if (game_status == GAME_MODE_EDITOR)
1315 DrawSpecialEditorDoor();
1317 // copy previous playfield and door areas, if they are defined on both
1318 // previous and current screen and if they still have the same size
1320 if (real_sx_last != -1 && real_sy_last != -1 &&
1321 REAL_SX != -1 && REAL_SY != -1 &&
1322 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1323 BlitBitmap(bitmap_db_store_1, backbuffer,
1324 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1327 if (dx_last != -1 && dy_last != -1 &&
1328 DX != -1 && DY != -1 &&
1329 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1330 BlitBitmap(bitmap_db_store_1, backbuffer,
1331 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1333 if (game_status != GAME_MODE_EDITOR)
1335 if (vx_last != -1 && vy_last != -1 &&
1336 VX != -1 && VY != -1 &&
1337 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1338 BlitBitmap(bitmap_db_store_1, backbuffer,
1339 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1343 if (ex_last != -1 && ey_last != -1 &&
1344 EX != -1 && EY != -1 &&
1345 exsize_last == EXSIZE && eysize_last == EYSIZE)
1346 BlitBitmap(bitmap_db_store_1, backbuffer,
1347 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1350 redraw_mask = REDRAW_ALL;
1353 game_status_last = game_status;
1355 global_border_bitmap_last = global_border_bitmap;
1357 real_sx_last = REAL_SX;
1358 real_sy_last = REAL_SY;
1359 full_sxsize_last = FULL_SXSIZE;
1360 full_sysize_last = FULL_SYSIZE;
1363 dxsize_last = DXSIZE;
1364 dysize_last = DYSIZE;
1367 vxsize_last = VXSIZE;
1368 vysize_last = VYSIZE;
1371 exsize_last = EXSIZE;
1372 eysize_last = EYSIZE;
1375 void ClearField(void)
1377 RedrawGlobalBorderIfNeeded();
1379 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1380 // (when entering hall of fame after playing)
1381 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1383 // !!! maybe this should be done before clearing the background !!!
1384 if (game_status == GAME_MODE_PLAYING)
1386 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1387 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1391 SetDrawtoField(DRAW_TO_BACKBUFFER);
1395 void MarkTileDirty(int x, int y)
1397 redraw_mask |= REDRAW_FIELD;
1400 void SetBorderElement(void)
1404 BorderElement = EL_EMPTY;
1406 // only the R'n'D game engine may use an additional steelwall border
1407 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1410 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1412 for (x = 0; x < lev_fieldx; x++)
1414 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1415 BorderElement = EL_STEELWALL;
1417 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1423 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1424 int max_array_fieldx, int max_array_fieldy,
1425 short field[max_array_fieldx][max_array_fieldy],
1426 int max_fieldx, int max_fieldy)
1428 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1429 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1430 int old_element = field[start_x][start_y];
1433 // do nothing if start field already has the desired content
1434 if (old_element == fill_element)
1437 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1439 while (stack_pos > 0)
1441 struct XY current = stack_buffer[--stack_pos];
1444 field[current.x][current.y] = fill_element;
1446 for (i = 0; i < 4; i++)
1448 int x = current.x + check[i].x;
1449 int y = current.y + check[i].y;
1451 // check for stack buffer overflow (should not happen)
1452 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1453 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1455 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1456 stack_buffer[stack_pos++] = (struct XY){ x, y };
1461 void FloodFillLevel(int from_x, int from_y, int fill_element,
1462 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1463 int max_fieldx, int max_fieldy)
1465 FloodFillLevelExt(from_x, from_y, fill_element,
1466 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1467 max_fieldx, max_fieldy);
1470 void SetRandomAnimationValue(int x, int y)
1472 gfx.anim_random_frame = GfxRandom[x][y];
1475 int getGraphicAnimationFrame(int graphic, int sync_frame)
1477 // animation synchronized with global frame counter, not move position
1478 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1479 sync_frame = FrameCounter;
1481 return getAnimationFrame(graphic_info[graphic].anim_frames,
1482 graphic_info[graphic].anim_delay,
1483 graphic_info[graphic].anim_mode,
1484 graphic_info[graphic].anim_start_frame,
1488 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1490 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1492 struct GraphicInfo *g = &graphic_info[graphic];
1493 int xsize = MAX(1, g->anim_frames_per_line);
1494 int ysize = MAX(1, g->anim_frames / xsize);
1495 int xoffset = g->anim_start_frame % xsize;
1496 int yoffset = g->anim_start_frame % ysize;
1497 // may be needed if screen field is significantly larger than playfield
1498 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1499 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1500 int sync_frame = y * xsize + x;
1502 return sync_frame % g->anim_frames;
1504 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1506 struct GraphicInfo *g = &graphic_info[graphic];
1507 // may be needed if screen field is significantly larger than playfield
1508 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1509 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1510 int sync_frame = GfxRandomStatic[x][y];
1512 return sync_frame % g->anim_frames;
1516 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1518 return getGraphicAnimationFrame(graphic, sync_frame);
1522 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1524 struct GraphicInfo *g = &graphic_info[graphic];
1525 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1527 if (tilesize == gfx.standard_tile_size)
1528 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1529 else if (tilesize == game.tile_size)
1530 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1532 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1535 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1536 boolean get_backside)
1538 struct GraphicInfo *g = &graphic_info[graphic];
1539 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1540 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1542 if (g->offset_y == 0) // frames are ordered horizontally
1544 int max_width = g->anim_frames_per_line * g->width;
1545 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1547 *x = pos % max_width;
1548 *y = src_y % g->height + pos / max_width * g->height;
1550 else if (g->offset_x == 0) // frames are ordered vertically
1552 int max_height = g->anim_frames_per_line * g->height;
1553 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1555 *x = src_x % g->width + pos / max_height * g->width;
1556 *y = pos % max_height;
1558 else // frames are ordered diagonally
1560 *x = src_x + frame * g->offset_x;
1561 *y = src_y + frame * g->offset_y;
1565 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1566 Bitmap **bitmap, int *x, int *y,
1567 boolean get_backside)
1569 struct GraphicInfo *g = &graphic_info[graphic];
1571 // if no graphics defined at all, use fallback graphics
1572 if (g->bitmaps == NULL)
1573 *g = graphic_info[IMG_CHAR_EXCLAM];
1575 // if no in-game graphics defined, always use standard graphic size
1576 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1577 tilesize = TILESIZE;
1579 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1580 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1582 *x = *x * tilesize / g->tile_size;
1583 *y = *y * tilesize / g->tile_size;
1586 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1587 Bitmap **bitmap, int *x, int *y)
1589 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1592 void getFixedGraphicSource(int graphic, int frame,
1593 Bitmap **bitmap, int *x, int *y)
1595 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1598 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1600 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1603 void getGlobalAnimGraphicSource(int graphic, int frame,
1604 Bitmap **bitmap, int *x, int *y)
1606 struct GraphicInfo *g = &graphic_info[graphic];
1608 // if no graphics defined at all, use fallback graphics
1609 if (g->bitmaps == NULL)
1610 *g = graphic_info[IMG_CHAR_EXCLAM];
1612 // use original size graphics, if existing, else use standard size graphics
1613 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1614 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1616 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1618 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1621 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1622 int *x, int *y, boolean get_backside)
1624 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1628 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1630 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1633 void DrawGraphic(int x, int y, int graphic, int frame)
1636 if (!IN_SCR_FIELD(x, y))
1638 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1639 Debug("draw:DrawGraphic", "This should never happen!");
1645 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1648 MarkTileDirty(x, y);
1651 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1654 if (!IN_SCR_FIELD(x, y))
1656 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1658 Debug("draw:DrawFixedGraphic", "This should never happen!");
1664 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1666 MarkTileDirty(x, y);
1669 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1675 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1680 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1686 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1687 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1690 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1693 if (!IN_SCR_FIELD(x, y))
1695 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1697 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1703 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1706 MarkTileDirty(x, y);
1709 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1712 if (!IN_SCR_FIELD(x, y))
1714 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1716 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1722 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1724 MarkTileDirty(x, y);
1727 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1733 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1735 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1739 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1740 int graphic, int frame)
1745 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1747 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1751 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1753 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1755 MarkTileDirty(x / tilesize, y / tilesize);
1758 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1761 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1762 graphic, frame, tilesize);
1763 MarkTileDirty(x / tilesize, y / tilesize);
1766 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1772 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1773 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1776 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1777 int frame, int tilesize)
1782 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1783 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1786 void DrawMiniGraphic(int x, int y, int graphic)
1788 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1789 MarkTileDirty(x / 2, y / 2);
1792 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1797 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1798 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1801 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1802 int graphic, int frame,
1803 int cut_mode, int mask_mode)
1808 int width = TILEX, height = TILEY;
1811 if (dx || dy) // shifted graphic
1813 if (x < BX1) // object enters playfield from the left
1820 else if (x > BX2) // object enters playfield from the right
1826 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1832 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1834 else if (dx) // general horizontal movement
1835 MarkTileDirty(x + SIGN(dx), y);
1837 if (y < BY1) // object enters playfield from the top
1839 if (cut_mode == CUT_BELOW) // object completely above top border
1847 else if (y > BY2) // object enters playfield from the bottom
1853 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1859 else if (dy > 0 && cut_mode == CUT_ABOVE)
1861 if (y == BY2) // object completely above bottom border
1867 MarkTileDirty(x, y + 1);
1868 } // object leaves playfield to the bottom
1869 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1871 else if (dy) // general vertical movement
1872 MarkTileDirty(x, y + SIGN(dy));
1876 if (!IN_SCR_FIELD(x, y))
1878 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1880 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1886 width = width * TILESIZE_VAR / TILESIZE;
1887 height = height * TILESIZE_VAR / TILESIZE;
1888 cx = cx * TILESIZE_VAR / TILESIZE;
1889 cy = cy * TILESIZE_VAR / TILESIZE;
1890 dx = dx * TILESIZE_VAR / TILESIZE;
1891 dy = dy * TILESIZE_VAR / TILESIZE;
1893 if (width > 0 && height > 0)
1895 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1900 dst_x = FX + x * TILEX_VAR + dx;
1901 dst_y = FY + y * TILEY_VAR + dy;
1903 if (mask_mode == USE_MASKING)
1904 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1907 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1910 MarkTileDirty(x, y);
1914 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1915 int graphic, int frame,
1916 int cut_mode, int mask_mode)
1921 int width = TILEX_VAR, height = TILEY_VAR;
1924 int x2 = x + SIGN(dx);
1925 int y2 = y + SIGN(dy);
1927 // movement with two-tile animations must be sync'ed with movement position,
1928 // not with current GfxFrame (which can be higher when using slow movement)
1929 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1930 int anim_frames = graphic_info[graphic].anim_frames;
1932 // (we also need anim_delay here for movement animations with less frames)
1933 int anim_delay = graphic_info[graphic].anim_delay;
1934 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1936 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1937 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1939 // re-calculate animation frame for two-tile movement animation
1940 frame = getGraphicAnimationFrame(graphic, sync_frame);
1942 // check if movement start graphic inside screen area and should be drawn
1943 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1945 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1947 dst_x = FX + x1 * TILEX_VAR;
1948 dst_y = FY + y1 * TILEY_VAR;
1950 if (mask_mode == USE_MASKING)
1951 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1957 MarkTileDirty(x1, y1);
1960 // check if movement end graphic inside screen area and should be drawn
1961 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1963 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1965 dst_x = FX + x2 * TILEX_VAR;
1966 dst_y = FY + y2 * TILEY_VAR;
1968 if (mask_mode == USE_MASKING)
1969 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1972 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1975 MarkTileDirty(x2, y2);
1979 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1980 int graphic, int frame,
1981 int cut_mode, int mask_mode)
1985 DrawGraphic(x, y, graphic, frame);
1990 if (graphic_info[graphic].double_movement) // EM style movement images
1991 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1993 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1996 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1997 int graphic, int frame, int cut_mode)
1999 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2002 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2003 int cut_mode, int mask_mode)
2005 int lx = LEVELX(x), ly = LEVELY(y);
2009 if (IN_LEV_FIELD(lx, ly))
2011 if (element == EL_EMPTY)
2012 element = GfxElementEmpty[lx][ly];
2014 SetRandomAnimationValue(lx, ly);
2016 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2017 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2019 // do not use double (EM style) movement graphic when not moving
2020 if (graphic_info[graphic].double_movement && !dx && !dy)
2022 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2023 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2026 if (game.use_masked_elements && (dx || dy))
2027 mask_mode = USE_MASKING;
2029 else // border element
2031 graphic = el2img(element);
2032 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2035 if (element == EL_EXPANDABLE_WALL)
2037 boolean left_stopped = FALSE, right_stopped = FALSE;
2039 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2040 left_stopped = TRUE;
2041 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2042 right_stopped = TRUE;
2044 if (left_stopped && right_stopped)
2046 else if (left_stopped)
2048 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2049 frame = graphic_info[graphic].anim_frames - 1;
2051 else if (right_stopped)
2053 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2054 frame = graphic_info[graphic].anim_frames - 1;
2059 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2060 else if (mask_mode == USE_MASKING)
2061 DrawGraphicThruMask(x, y, graphic, frame);
2063 DrawGraphic(x, y, graphic, frame);
2066 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2067 int cut_mode, int mask_mode)
2069 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2070 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2071 cut_mode, mask_mode);
2074 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2077 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2080 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2083 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2086 void DrawLevelElementThruMask(int x, int y, int element)
2088 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2091 void DrawLevelFieldThruMask(int x, int y)
2093 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2096 // !!! implementation of quicksand is totally broken !!!
2097 #define IS_CRUMBLED_TILE(x, y, e) \
2098 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2099 !IS_MOVING(x, y) || \
2100 (e) == EL_QUICKSAND_EMPTYING || \
2101 (e) == EL_QUICKSAND_FAST_EMPTYING))
2103 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2108 int width, height, cx, cy;
2109 int sx = SCREENX(x), sy = SCREENY(y);
2110 int crumbled_border_size = graphic_info[graphic].border_size;
2111 int crumbled_tile_size = graphic_info[graphic].tile_size;
2112 int crumbled_border_size_var =
2113 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2116 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2118 for (i = 1; i < 4; i++)
2120 int dxx = (i & 1 ? dx : 0);
2121 int dyy = (i & 2 ? dy : 0);
2124 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2127 // check if neighbour field is of same crumble type
2128 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2129 graphic_info[graphic].class ==
2130 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2132 // return if check prevents inner corner
2133 if (same == (dxx == dx && dyy == dy))
2137 // if we reach this point, we have an inner corner
2139 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2141 width = crumbled_border_size_var;
2142 height = crumbled_border_size_var;
2143 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2144 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2146 if (game.use_masked_elements)
2148 int graphic0 = el2img(EL_EMPTY);
2149 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2150 Bitmap *src_bitmap0;
2153 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2155 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2157 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2159 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2161 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2164 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2166 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2169 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2174 int width, height, bx, by, cx, cy;
2175 int sx = SCREENX(x), sy = SCREENY(y);
2176 int crumbled_border_size = graphic_info[graphic].border_size;
2177 int crumbled_tile_size = graphic_info[graphic].tile_size;
2178 int crumbled_border_size_var =
2179 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2180 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2183 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2185 // only needed when using masked elements
2186 int graphic0 = el2img(EL_EMPTY);
2187 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2188 Bitmap *src_bitmap0;
2191 if (game.use_masked_elements)
2192 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2194 // draw simple, sloppy, non-corner-accurate crumbled border
2196 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2197 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2198 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2199 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2201 if (game.use_masked_elements)
2203 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2205 FX + sx * TILEX_VAR + cx,
2206 FY + sy * TILEY_VAR + cy);
2208 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2210 FX + sx * TILEX_VAR + cx,
2211 FY + sy * TILEY_VAR + cy);
2214 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2216 FX + sx * TILEX_VAR + cx,
2217 FY + sy * TILEY_VAR + cy);
2219 // (remaining middle border part must be at least as big as corner part)
2220 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2221 crumbled_border_size_var >= TILESIZE_VAR / 3)
2224 // correct corners of crumbled border, if needed
2226 for (i = -1; i <= 1; i += 2)
2228 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2229 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2230 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2233 // check if neighbour field is of same crumble type
2234 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2235 graphic_info[graphic].class ==
2236 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2238 // no crumbled corner, but continued crumbled border
2240 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2241 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2242 int b1 = (i == 1 ? crumbled_border_size_var :
2243 TILESIZE_VAR - 2 * crumbled_border_size_var);
2245 width = crumbled_border_size_var;
2246 height = crumbled_border_size_var;
2248 if (dir == 1 || dir == 2)
2263 if (game.use_masked_elements)
2265 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2267 FX + sx * TILEX_VAR + cx,
2268 FY + sy * TILEY_VAR + cy);
2270 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2272 FX + sx * TILEX_VAR + cx,
2273 FY + sy * TILEY_VAR + cy);
2276 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2278 FX + sx * TILEX_VAR + cx,
2279 FY + sy * TILEY_VAR + cy);
2284 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2286 int sx = SCREENX(x), sy = SCREENY(y);
2289 static int xy[4][2] =
2297 if (!IN_LEV_FIELD(x, y))
2300 element = TILE_GFX_ELEMENT(x, y);
2302 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2304 if (!IN_SCR_FIELD(sx, sy))
2307 // crumble field borders towards direct neighbour fields
2308 for (i = 0; i < 4; i++)
2310 int xx = x + xy[i][0];
2311 int yy = y + xy[i][1];
2313 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2316 // check if neighbour field is of same crumble type
2317 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2318 graphic_info[graphic].class ==
2319 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2322 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2325 // crumble inner field corners towards corner neighbour fields
2326 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2327 graphic_info[graphic].anim_frames == 2)
2329 for (i = 0; i < 4; i++)
2331 int dx = (i & 1 ? +1 : -1);
2332 int dy = (i & 2 ? +1 : -1);
2334 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2338 MarkTileDirty(sx, sy);
2340 else // center field is not crumbled -- crumble neighbour fields
2342 // crumble field borders of direct neighbour fields
2343 for (i = 0; i < 4; i++)
2345 int xx = x + xy[i][0];
2346 int yy = y + xy[i][1];
2347 int sxx = sx + xy[i][0];
2348 int syy = sy + xy[i][1];
2350 if (!IN_LEV_FIELD(xx, yy) ||
2351 !IN_SCR_FIELD(sxx, syy))
2354 // do not crumble fields that are being digged or snapped
2355 if (Tile[xx][yy] == EL_EMPTY ||
2356 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2359 element = TILE_GFX_ELEMENT(xx, yy);
2361 if (!IS_CRUMBLED_TILE(xx, yy, element))
2364 graphic = el_act2crm(element, ACTION_DEFAULT);
2366 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2368 MarkTileDirty(sxx, syy);
2371 // crumble inner field corners of corner neighbour fields
2372 for (i = 0; i < 4; i++)
2374 int dx = (i & 1 ? +1 : -1);
2375 int dy = (i & 2 ? +1 : -1);
2381 if (!IN_LEV_FIELD(xx, yy) ||
2382 !IN_SCR_FIELD(sxx, syy))
2385 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2388 element = TILE_GFX_ELEMENT(xx, yy);
2390 if (!IS_CRUMBLED_TILE(xx, yy, element))
2393 graphic = el_act2crm(element, ACTION_DEFAULT);
2395 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2396 graphic_info[graphic].anim_frames == 2)
2397 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2399 MarkTileDirty(sxx, syy);
2404 void DrawLevelFieldCrumbled(int x, int y)
2408 if (!IN_LEV_FIELD(x, y))
2411 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2412 GfxElement[x][y] != EL_UNDEFINED &&
2413 GFX_CRUMBLED(GfxElement[x][y]))
2415 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2420 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2422 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2425 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2428 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2429 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2430 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2431 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2432 int sx = SCREENX(x), sy = SCREENY(y);
2434 DrawScreenGraphic(sx, sy, graphic1, frame1);
2435 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2438 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2440 int sx = SCREENX(x), sy = SCREENY(y);
2441 static int xy[4][2] =
2450 // crumble direct neighbour fields (required for field borders)
2451 for (i = 0; i < 4; i++)
2453 int xx = x + xy[i][0];
2454 int yy = y + xy[i][1];
2455 int sxx = sx + xy[i][0];
2456 int syy = sy + xy[i][1];
2458 if (!IN_LEV_FIELD(xx, yy) ||
2459 !IN_SCR_FIELD(sxx, syy) ||
2460 !GFX_CRUMBLED(Tile[xx][yy]) ||
2464 DrawLevelField(xx, yy);
2467 // crumble corner neighbour fields (required for inner field corners)
2468 for (i = 0; i < 4; i++)
2470 int dx = (i & 1 ? +1 : -1);
2471 int dy = (i & 2 ? +1 : -1);
2477 if (!IN_LEV_FIELD(xx, yy) ||
2478 !IN_SCR_FIELD(sxx, syy) ||
2479 !GFX_CRUMBLED(Tile[xx][yy]) ||
2483 int element = TILE_GFX_ELEMENT(xx, yy);
2484 int graphic = el_act2crm(element, ACTION_DEFAULT);
2486 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2487 graphic_info[graphic].anim_frames == 2)
2488 DrawLevelField(xx, yy);
2492 static int getBorderElement(int x, int y)
2496 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2497 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2498 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2499 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2500 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2501 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2502 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2504 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2505 int steel_position = (x == -1 && y == -1 ? 0 :
2506 x == lev_fieldx && y == -1 ? 1 :
2507 x == -1 && y == lev_fieldy ? 2 :
2508 x == lev_fieldx && y == lev_fieldy ? 3 :
2509 x == -1 || x == lev_fieldx ? 4 :
2510 y == -1 || y == lev_fieldy ? 5 : 6);
2512 return border[steel_position][steel_type];
2515 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2517 if (game.use_masked_elements)
2519 if (graphic != el2img(EL_EMPTY))
2520 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2522 DrawGraphicThruMask(x, y, graphic, frame);
2526 DrawGraphic(x, y, graphic, frame);
2530 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2532 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2535 void DrawScreenElement(int x, int y, int element)
2537 int mask_mode = NO_MASKING;
2539 if (game.use_masked_elements)
2541 int lx = LEVELX(x), ly = LEVELY(y);
2543 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2545 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2547 mask_mode = USE_MASKING;
2551 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2552 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2555 void DrawLevelElement(int x, int y, int element)
2557 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2558 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2561 void DrawScreenField(int x, int y)
2563 int lx = LEVELX(x), ly = LEVELY(y);
2564 int element, content;
2566 if (!IN_LEV_FIELD(lx, ly))
2568 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2571 element = getBorderElement(lx, ly);
2573 DrawScreenElement(x, y, element);
2578 element = Tile[lx][ly];
2579 content = Store[lx][ly];
2581 if (IS_MOVING(lx, ly))
2583 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2584 boolean cut_mode = NO_CUTTING;
2586 if (element == EL_QUICKSAND_EMPTYING ||
2587 element == EL_QUICKSAND_FAST_EMPTYING ||
2588 element == EL_MAGIC_WALL_EMPTYING ||
2589 element == EL_BD_MAGIC_WALL_EMPTYING ||
2590 element == EL_DC_MAGIC_WALL_EMPTYING ||
2591 element == EL_AMOEBA_DROPPING)
2592 cut_mode = CUT_ABOVE;
2593 else if (element == EL_QUICKSAND_FILLING ||
2594 element == EL_QUICKSAND_FAST_FILLING ||
2595 element == EL_MAGIC_WALL_FILLING ||
2596 element == EL_BD_MAGIC_WALL_FILLING ||
2597 element == EL_DC_MAGIC_WALL_FILLING)
2598 cut_mode = CUT_BELOW;
2600 if (cut_mode == CUT_ABOVE)
2601 DrawScreenElement(x, y, element);
2603 DrawScreenElement(x, y, EL_EMPTY);
2605 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2607 int dir = MovDir[lx][ly];
2608 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2609 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2611 if (IN_SCR_FIELD(newx, newy))
2612 DrawScreenElement(newx, newy, EL_EMPTY);
2616 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2617 else if (cut_mode == NO_CUTTING)
2618 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2621 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2623 if (cut_mode == CUT_BELOW &&
2624 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2625 DrawLevelElement(lx, ly + 1, element);
2628 if (content == EL_ACID)
2630 int dir = MovDir[lx][ly];
2631 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2632 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2634 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2636 // prevent target field from being drawn again (but without masking)
2637 // (this would happen if target field is scanned after moving element)
2638 Stop[newlx][newly] = TRUE;
2641 else if (IS_BLOCKED(lx, ly))
2646 boolean cut_mode = NO_CUTTING;
2647 int element_old, content_old;
2649 Blocked2Moving(lx, ly, &oldx, &oldy);
2652 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2653 MovDir[oldx][oldy] == MV_RIGHT);
2655 element_old = Tile[oldx][oldy];
2656 content_old = Store[oldx][oldy];
2658 if (element_old == EL_QUICKSAND_EMPTYING ||
2659 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2660 element_old == EL_MAGIC_WALL_EMPTYING ||
2661 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2662 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2663 element_old == EL_AMOEBA_DROPPING)
2664 cut_mode = CUT_ABOVE;
2666 DrawScreenElement(x, y, EL_EMPTY);
2669 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2671 else if (cut_mode == NO_CUTTING)
2672 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2675 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2678 else if (IS_DRAWABLE(element))
2679 DrawScreenElement(x, y, element);
2681 DrawScreenElement(x, y, EL_EMPTY);
2684 void DrawLevelField(int x, int y)
2686 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2687 DrawScreenField(SCREENX(x), SCREENY(y));
2688 else if (IS_MOVING(x, y))
2692 Moving2Blocked(x, y, &newx, &newy);
2693 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2694 DrawScreenField(SCREENX(newx), SCREENY(newy));
2696 else if (IS_BLOCKED(x, y))
2700 Blocked2Moving(x, y, &oldx, &oldy);
2701 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2702 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2706 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2707 int (*el2img_function)(int), boolean masked,
2708 int element_bits_draw)
2710 int element_base = map_mm_wall_element(element);
2711 int element_bits = (IS_DF_WALL(element) ?
2712 element - EL_DF_WALL_START :
2713 IS_MM_WALL(element) ?
2714 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2715 int graphic = el2img_function(element_base);
2716 int tilesize_draw = tilesize / 2;
2721 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2723 for (i = 0; i < 4; i++)
2725 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2726 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2728 if (!(element_bits_draw & (1 << i)))
2731 if (element_bits & (1 << i))
2734 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2735 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2737 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2738 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2743 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2744 tilesize_draw, tilesize_draw);
2749 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2750 boolean masked, int element_bits_draw)
2752 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2753 element, tilesize, el2edimg, masked, element_bits_draw);
2756 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2757 int (*el2img_function)(int))
2759 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2763 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2766 if (IS_MM_WALL(element))
2768 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2769 element, tilesize, el2edimg, masked, 0x000f);
2773 int graphic = el2edimg(element);
2776 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2778 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2782 void DrawSizedElement(int x, int y, int element, int tilesize)
2784 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2787 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2789 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2792 void DrawMiniElement(int x, int y, int element)
2796 graphic = el2edimg(element);
2797 DrawMiniGraphic(x, y, graphic);
2800 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2803 int x = sx + scroll_x, y = sy + scroll_y;
2805 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2806 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2807 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2808 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2810 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2813 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2815 int x = sx + scroll_x, y = sy + scroll_y;
2817 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2818 DrawMiniElement(sx, sy, EL_EMPTY);
2819 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2820 DrawMiniElement(sx, sy, Tile[x][y]);
2822 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2825 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2826 int x, int y, int xsize, int ysize,
2827 int tile_width, int tile_height)
2831 int dst_x = startx + x * tile_width;
2832 int dst_y = starty + y * tile_height;
2833 int width = graphic_info[graphic].width;
2834 int height = graphic_info[graphic].height;
2835 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2836 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2837 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2838 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2839 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2840 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2841 boolean draw_masked = graphic_info[graphic].draw_masked;
2843 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2845 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2847 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2851 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2852 inner_sx + (x - 1) * tile_width % inner_width);
2853 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2854 inner_sy + (y - 1) * tile_height % inner_height);
2857 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2860 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2864 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2865 int x, int y, int xsize, int ysize,
2868 int font_width = getFontWidth(font_nr);
2869 int font_height = getFontHeight(font_nr);
2871 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2872 font_width, font_height);
2875 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2877 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2878 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2879 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2880 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2881 boolean no_delay = (tape.warp_forward);
2882 unsigned int anim_delay = 0;
2883 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2884 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2885 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2886 int font_width = getFontWidth(font_nr);
2887 int font_height = getFontHeight(font_nr);
2888 int max_xsize = level.envelope[envelope_nr].xsize;
2889 int max_ysize = level.envelope[envelope_nr].ysize;
2890 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2891 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2892 int xend = max_xsize;
2893 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2894 int xstep = (xstart < xend ? 1 : 0);
2895 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2897 int end = MAX(xend - xstart, yend - ystart);
2900 for (i = start; i <= end; i++)
2902 int last_frame = end; // last frame of this "for" loop
2903 int x = xstart + i * xstep;
2904 int y = ystart + i * ystep;
2905 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2906 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2907 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2908 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2911 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2913 BlitScreenToBitmap(backbuffer);
2915 SetDrawtoField(DRAW_TO_BACKBUFFER);
2917 for (yy = 0; yy < ysize; yy++)
2918 for (xx = 0; xx < xsize; xx++)
2919 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2921 DrawTextBuffer(sx + font_width, sy + font_height,
2922 level.envelope[envelope_nr].text, font_nr, max_xsize,
2923 xsize - 2, ysize - 2, 0, mask_mode,
2924 level.envelope[envelope_nr].autowrap,
2925 level.envelope[envelope_nr].centered, FALSE);
2927 redraw_mask |= REDRAW_FIELD;
2930 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2933 ClearAutoRepeatKeyEvents();
2936 void ShowEnvelope(int envelope_nr)
2938 int element = EL_ENVELOPE_1 + envelope_nr;
2939 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2940 int sound_opening = element_info[element].sound[ACTION_OPENING];
2941 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2942 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2943 boolean no_delay = (tape.warp_forward);
2944 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2945 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2946 int anim_mode = graphic_info[graphic].anim_mode;
2947 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2948 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2949 boolean overlay_enabled = GetOverlayEnabled();
2951 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2953 SetOverlayEnabled(FALSE);
2956 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2958 if (anim_mode == ANIM_DEFAULT)
2959 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2961 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2964 Delay_WithScreenUpdates(wait_delay_value);
2966 WaitForEventToContinue();
2969 SetOverlayEnabled(overlay_enabled);
2971 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2973 if (anim_mode != ANIM_NONE)
2974 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2976 if (anim_mode == ANIM_DEFAULT)
2977 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2979 game.envelope_active = FALSE;
2981 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2983 redraw_mask |= REDRAW_FIELD;
2987 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2988 int xsize, int ysize)
2990 if (!global.use_envelope_request ||
2991 request.sort_priority <= 0)
2994 if (request.bitmap == NULL ||
2995 xsize > request.xsize ||
2996 ysize > request.ysize)
2998 if (request.bitmap != NULL)
2999 FreeBitmap(request.bitmap);
3001 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3003 SDL_Surface *surface = request.bitmap->surface;
3005 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3006 Fail("SDLGetNativeSurface() failed");
3009 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3011 SDLFreeBitmapTextures(request.bitmap);
3012 SDLCreateBitmapTextures(request.bitmap);
3014 // set envelope request run-time values
3017 request.xsize = xsize;
3018 request.ysize = ysize;
3021 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3023 if (global.use_envelope_request &&
3024 game.request_active_or_moving &&
3025 request.sort_priority > 0 &&
3026 drawing_target == DRAW_TO_SCREEN &&
3027 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3029 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3030 request.sx, request.sy);
3034 static void setRequestBasePosition(int *x, int *y)
3036 int sx_base, sy_base;
3038 if (request.x != -1)
3039 sx_base = request.x;
3040 else if (request.align == ALIGN_LEFT)
3042 else if (request.align == ALIGN_RIGHT)
3043 sx_base = SX + SXSIZE;
3045 sx_base = SX + SXSIZE / 2;
3047 if (request.y != -1)
3048 sy_base = request.y;
3049 else if (request.valign == VALIGN_TOP)
3051 else if (request.valign == VALIGN_BOTTOM)
3052 sy_base = SY + SYSIZE;
3054 sy_base = SY + SYSIZE / 2;
3060 static void setRequestPositionExt(int *x, int *y, int width, int height,
3061 boolean add_border_size)
3063 int border_size = request.border_size;
3064 int sx_base, sy_base;
3067 setRequestBasePosition(&sx_base, &sy_base);
3069 if (request.align == ALIGN_LEFT)
3071 else if (request.align == ALIGN_RIGHT)
3072 sx = sx_base - width;
3074 sx = sx_base - width / 2;
3076 if (request.valign == VALIGN_TOP)
3078 else if (request.valign == VALIGN_BOTTOM)
3079 sy = sy_base - height;
3081 sy = sy_base - height / 2;
3083 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3084 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3086 if (add_border_size)
3096 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3098 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3101 static void DrawEnvelopeRequest(char *text)
3103 char *text_final = text;
3104 char *text_door_style = NULL;
3105 int graphic = IMG_BACKGROUND_REQUEST;
3106 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3107 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3108 int font_nr = FONT_REQUEST;
3109 int font_width = getFontWidth(font_nr);
3110 int font_height = getFontHeight(font_nr);
3111 int border_size = request.border_size;
3112 int line_spacing = request.line_spacing;
3113 int line_height = font_height + line_spacing;
3114 int max_text_width = request.width - 2 * border_size;
3115 int max_text_height = request.height - 2 * border_size;
3116 int line_length = max_text_width / font_width;
3117 int max_lines = max_text_height / line_height;
3118 int text_width = line_length * font_width;
3119 int width = request.width;
3120 int height = request.height;
3121 int tile_size = MAX(request.step_offset, 1);
3122 int x_steps = width / tile_size;
3123 int y_steps = height / tile_size;
3124 int sx_offset = border_size;
3125 int sy_offset = border_size;
3129 if (request.centered)
3130 sx_offset = (request.width - text_width) / 2;
3132 if (request.wrap_single_words && !request.autowrap)
3134 char *src_text_ptr, *dst_text_ptr;
3136 text_door_style = checked_malloc(2 * strlen(text) + 1);
3138 src_text_ptr = text;
3139 dst_text_ptr = text_door_style;
3141 while (*src_text_ptr)
3143 if (*src_text_ptr == ' ' ||
3144 *src_text_ptr == '?' ||
3145 *src_text_ptr == '!')
3146 *dst_text_ptr++ = '\n';
3148 if (*src_text_ptr != ' ')
3149 *dst_text_ptr++ = *src_text_ptr;
3154 *dst_text_ptr = '\0';
3156 text_final = text_door_style;
3159 setRequestPosition(&sx, &sy, FALSE);
3161 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3163 for (y = 0; y < y_steps; y++)
3164 for (x = 0; x < x_steps; x++)
3165 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3166 x, y, x_steps, y_steps,
3167 tile_size, tile_size);
3169 // force DOOR font inside door area
3170 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3172 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3173 line_length, -1, max_lines, line_spacing, mask_mode,
3174 request.autowrap, request.centered, FALSE);
3178 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3179 RedrawGadget(tool_gadget[i]);
3181 // store readily prepared envelope request for later use when animating
3182 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3184 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3186 if (text_door_style)
3187 free(text_door_style);
3190 static void AnimateEnvelopeRequest(int anim_mode, int action)
3192 int graphic = IMG_BACKGROUND_REQUEST;
3193 boolean draw_masked = graphic_info[graphic].draw_masked;
3194 int delay_value_normal = request.step_delay;
3195 int delay_value_fast = delay_value_normal / 2;
3196 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3197 boolean no_delay = (tape.warp_forward);
3198 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3199 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3200 unsigned int anim_delay = 0;
3202 int tile_size = MAX(request.step_offset, 1);
3203 int max_xsize = request.width / tile_size;
3204 int max_ysize = request.height / tile_size;
3205 int max_xsize_inner = max_xsize - 2;
3206 int max_ysize_inner = max_ysize - 2;
3208 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3209 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3210 int xend = max_xsize_inner;
3211 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3212 int xstep = (xstart < xend ? 1 : 0);
3213 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3215 int end = MAX(xend - xstart, yend - ystart);
3218 if (setup.quick_doors)
3225 for (i = start; i <= end; i++)
3227 int last_frame = end; // last frame of this "for" loop
3228 int x = xstart + i * xstep;
3229 int y = ystart + i * ystep;
3230 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3231 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3232 int xsize_size_left = (xsize - 1) * tile_size;
3233 int ysize_size_top = (ysize - 1) * tile_size;
3234 int max_xsize_pos = (max_xsize - 1) * tile_size;
3235 int max_ysize_pos = (max_ysize - 1) * tile_size;
3236 int width = xsize * tile_size;
3237 int height = ysize * tile_size;
3242 setRequestPosition(&src_x, &src_y, FALSE);
3243 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3245 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3247 for (yy = 0; yy < 2; yy++)
3249 for (xx = 0; xx < 2; xx++)
3251 int src_xx = src_x + xx * max_xsize_pos;
3252 int src_yy = src_y + yy * max_ysize_pos;
3253 int dst_xx = dst_x + xx * xsize_size_left;
3254 int dst_yy = dst_y + yy * ysize_size_top;
3255 int xx_size = (xx ? tile_size : xsize_size_left);
3256 int yy_size = (yy ? tile_size : ysize_size_top);
3259 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3260 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3262 BlitBitmap(bitmap_db_store_2, backbuffer,
3263 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3267 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3269 redraw_mask |= REDRAW_FIELD;
3273 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3276 ClearAutoRepeatKeyEvents();
3279 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3281 int graphic = IMG_BACKGROUND_REQUEST;
3282 int sound_opening = SND_REQUEST_OPENING;
3283 int sound_closing = SND_REQUEST_CLOSING;
3284 int anim_mode_1 = request.anim_mode; // (higher priority)
3285 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3286 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3287 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3288 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3290 if (game_status == GAME_MODE_PLAYING)
3291 BlitScreenToBitmap(backbuffer);
3293 SetDrawtoField(DRAW_TO_BACKBUFFER);
3295 // SetDrawBackgroundMask(REDRAW_NONE);
3297 if (action == ACTION_OPENING)
3299 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3301 if (req_state & REQ_ASK)
3303 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3304 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3305 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3306 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3308 else if (req_state & REQ_CONFIRM)
3310 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3311 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3313 else if (req_state & REQ_PLAYER)
3315 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3316 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3317 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3318 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3321 DrawEnvelopeRequest(text);
3324 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3326 if (action == ACTION_OPENING)
3328 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3330 if (anim_mode == ANIM_DEFAULT)
3331 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3333 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3337 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3339 if (anim_mode != ANIM_NONE)
3340 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3342 if (anim_mode == ANIM_DEFAULT)
3343 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3346 game.envelope_active = FALSE;
3348 if (action == ACTION_CLOSING)
3349 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3351 // SetDrawBackgroundMask(last_draw_background_mask);
3353 redraw_mask |= REDRAW_FIELD;
3357 if (action == ACTION_CLOSING &&
3358 game_status == GAME_MODE_PLAYING &&
3359 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3360 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3363 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3365 if (IS_MM_WALL(element))
3367 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3373 int graphic = el2preimg(element);
3375 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3376 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3381 void DrawLevel(int draw_background_mask)
3385 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3386 SetDrawBackgroundMask(draw_background_mask);
3390 for (x = BX1; x <= BX2; x++)
3391 for (y = BY1; y <= BY2; y++)
3392 DrawScreenField(x, y);
3394 redraw_mask |= REDRAW_FIELD;
3397 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3402 for (x = 0; x < size_x; x++)
3403 for (y = 0; y < size_y; y++)
3404 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3406 redraw_mask |= REDRAW_FIELD;
3409 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3413 for (x = 0; x < size_x; x++)
3414 for (y = 0; y < size_y; y++)
3415 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3417 redraw_mask |= REDRAW_FIELD;
3420 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3422 boolean show_level_border = (BorderElement != EL_EMPTY);
3423 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3424 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3425 int tile_size = preview.tile_size;
3426 int preview_width = preview.xsize * tile_size;
3427 int preview_height = preview.ysize * tile_size;
3428 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3429 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3430 int real_preview_width = real_preview_xsize * tile_size;
3431 int real_preview_height = real_preview_ysize * tile_size;
3432 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3433 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3436 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3439 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3441 dst_x += (preview_width - real_preview_width) / 2;
3442 dst_y += (preview_height - real_preview_height) / 2;
3444 for (x = 0; x < real_preview_xsize; x++)
3446 for (y = 0; y < real_preview_ysize; y++)
3448 int lx = from_x + x + (show_level_border ? -1 : 0);
3449 int ly = from_y + y + (show_level_border ? -1 : 0);
3450 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3451 getBorderElement(lx, ly));
3453 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3454 element, tile_size);
3458 redraw_mask |= REDRAW_FIELD;
3461 #define MICROLABEL_EMPTY 0
3462 #define MICROLABEL_LEVEL_NAME 1
3463 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3464 #define MICROLABEL_LEVEL_AUTHOR 3
3465 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3466 #define MICROLABEL_IMPORTED_FROM 5
3467 #define MICROLABEL_IMPORTED_BY_HEAD 6
3468 #define MICROLABEL_IMPORTED_BY 7
3470 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3472 int max_text_width = SXSIZE;
3473 int font_width = getFontWidth(font_nr);
3475 if (pos->align == ALIGN_CENTER)
3476 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3477 else if (pos->align == ALIGN_RIGHT)
3478 max_text_width = pos->x;
3480 max_text_width = SXSIZE - pos->x;
3482 return max_text_width / font_width;
3485 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3487 char label_text[MAX_OUTPUT_LINESIZE + 1];
3488 int max_len_label_text;
3489 int font_nr = pos->font;
3492 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3495 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3496 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3497 mode == MICROLABEL_IMPORTED_BY_HEAD)
3498 font_nr = pos->font_alt;
3500 max_len_label_text = getMaxTextLength(pos, font_nr);
3502 if (pos->size != -1)
3503 max_len_label_text = pos->size;
3505 for (i = 0; i < max_len_label_text; i++)
3506 label_text[i] = ' ';
3507 label_text[max_len_label_text] = '\0';
3509 if (strlen(label_text) > 0)
3510 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3513 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3514 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3515 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3516 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3517 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3518 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3519 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3520 max_len_label_text);
3521 label_text[max_len_label_text] = '\0';
3523 if (strlen(label_text) > 0)
3524 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3526 redraw_mask |= REDRAW_FIELD;
3529 static void DrawPreviewLevelLabel(int mode)
3531 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3534 static void DrawPreviewLevelInfo(int mode)
3536 if (mode == MICROLABEL_LEVEL_NAME)
3537 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3538 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3539 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3542 static void DrawPreviewLevelExt(boolean restart)
3544 static unsigned int scroll_delay = 0;
3545 static unsigned int label_delay = 0;
3546 static int from_x, from_y, scroll_direction;
3547 static int label_state, label_counter;
3548 unsigned int scroll_delay_value = preview.step_delay;
3549 boolean show_level_border = (BorderElement != EL_EMPTY);
3550 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3551 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3558 if (preview.anim_mode == ANIM_CENTERED)
3560 if (level_xsize > preview.xsize)
3561 from_x = (level_xsize - preview.xsize) / 2;
3562 if (level_ysize > preview.ysize)
3563 from_y = (level_ysize - preview.ysize) / 2;
3566 from_x += preview.xoffset;
3567 from_y += preview.yoffset;
3569 scroll_direction = MV_RIGHT;
3573 DrawPreviewLevelPlayfield(from_x, from_y);
3574 DrawPreviewLevelLabel(label_state);
3576 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3577 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3579 // initialize delay counters
3580 ResetDelayCounter(&scroll_delay);
3581 ResetDelayCounter(&label_delay);
3583 if (leveldir_current->name)
3585 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3586 char label_text[MAX_OUTPUT_LINESIZE + 1];
3587 int font_nr = pos->font;
3588 int max_len_label_text = getMaxTextLength(pos, font_nr);
3590 if (pos->size != -1)
3591 max_len_label_text = pos->size;
3593 strncpy(label_text, leveldir_current->name, max_len_label_text);
3594 label_text[max_len_label_text] = '\0';
3596 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3597 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3603 // scroll preview level, if needed
3604 if (preview.anim_mode != ANIM_NONE &&
3605 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3606 DelayReached(&scroll_delay, scroll_delay_value))
3608 switch (scroll_direction)
3613 from_x -= preview.step_offset;
3614 from_x = (from_x < 0 ? 0 : from_x);
3617 scroll_direction = MV_UP;
3621 if (from_x < level_xsize - preview.xsize)
3623 from_x += preview.step_offset;
3624 from_x = (from_x > level_xsize - preview.xsize ?
3625 level_xsize - preview.xsize : from_x);
3628 scroll_direction = MV_DOWN;
3634 from_y -= preview.step_offset;
3635 from_y = (from_y < 0 ? 0 : from_y);
3638 scroll_direction = MV_RIGHT;
3642 if (from_y < level_ysize - preview.ysize)
3644 from_y += preview.step_offset;
3645 from_y = (from_y > level_ysize - preview.ysize ?
3646 level_ysize - preview.ysize : from_y);
3649 scroll_direction = MV_LEFT;
3656 DrawPreviewLevelPlayfield(from_x, from_y);
3659 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3660 // redraw micro level label, if needed
3661 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3662 !strEqual(level.author, ANONYMOUS_NAME) &&
3663 !strEqual(level.author, leveldir_current->name) &&
3664 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3666 int max_label_counter = 23;
3668 if (leveldir_current->imported_from != NULL &&
3669 strlen(leveldir_current->imported_from) > 0)
3670 max_label_counter += 14;
3671 if (leveldir_current->imported_by != NULL &&
3672 strlen(leveldir_current->imported_by) > 0)
3673 max_label_counter += 14;
3675 label_counter = (label_counter + 1) % max_label_counter;
3676 label_state = (label_counter >= 0 && label_counter <= 7 ?
3677 MICROLABEL_LEVEL_NAME :
3678 label_counter >= 9 && label_counter <= 12 ?
3679 MICROLABEL_LEVEL_AUTHOR_HEAD :
3680 label_counter >= 14 && label_counter <= 21 ?
3681 MICROLABEL_LEVEL_AUTHOR :
3682 label_counter >= 23 && label_counter <= 26 ?
3683 MICROLABEL_IMPORTED_FROM_HEAD :
3684 label_counter >= 28 && label_counter <= 35 ?
3685 MICROLABEL_IMPORTED_FROM :
3686 label_counter >= 37 && label_counter <= 40 ?
3687 MICROLABEL_IMPORTED_BY_HEAD :
3688 label_counter >= 42 && label_counter <= 49 ?
3689 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3691 if (leveldir_current->imported_from == NULL &&
3692 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3693 label_state == MICROLABEL_IMPORTED_FROM))
3694 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3695 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3697 DrawPreviewLevelLabel(label_state);
3701 void DrawPreviewPlayers(void)
3703 if (game_status != GAME_MODE_MAIN)
3706 // do not draw preview players if level preview redefined, but players aren't
3707 if (preview.redefined && !menu.main.preview_players.redefined)
3710 boolean player_found[MAX_PLAYERS];
3711 int num_players = 0;
3714 for (i = 0; i < MAX_PLAYERS; i++)
3715 player_found[i] = FALSE;
3717 // check which players can be found in the level (simple approach)
3718 for (x = 0; x < lev_fieldx; x++)
3720 for (y = 0; y < lev_fieldy; y++)
3722 int element = level.field[x][y];
3724 if (IS_PLAYER_ELEMENT(element))
3726 int player_nr = GET_PLAYER_NR(element);
3728 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3730 if (!player_found[player_nr])
3733 player_found[player_nr] = TRUE;
3738 struct TextPosInfo *pos = &menu.main.preview_players;
3739 int tile_size = pos->tile_size;
3740 int border_size = pos->border_size;
3741 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3742 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3743 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3744 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3745 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3746 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3747 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3748 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3749 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3750 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3751 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3752 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3754 // clear area in which the players will be drawn
3755 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3756 max_players_width, max_players_height);
3758 if (!network.enabled && !setup.team_mode)
3761 // only draw players if level is suited for team mode
3762 if (num_players < 2)
3765 // draw all players that were found in the level
3766 for (i = 0; i < MAX_PLAYERS; i++)
3768 if (player_found[i])
3770 int graphic = el2img(EL_PLAYER_1 + i);
3772 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3774 xpos += player_xoffset;
3775 ypos += player_yoffset;
3780 void DrawPreviewLevelInitial(void)
3782 DrawPreviewLevelExt(TRUE);
3783 DrawPreviewPlayers();
3786 void DrawPreviewLevelAnimation(void)
3788 DrawPreviewLevelExt(FALSE);
3791 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3792 int border_size, int font_nr)
3794 int graphic = el2img(EL_PLAYER_1 + player_nr);
3795 int font_height = getFontHeight(font_nr);
3796 int player_height = MAX(tile_size, font_height);
3797 int xoffset_text = tile_size + border_size;
3798 int yoffset_text = (player_height - font_height) / 2;
3799 int yoffset_graphic = (player_height - tile_size) / 2;
3800 char *player_name = getNetworkPlayerName(player_nr + 1);
3802 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3804 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3807 static void DrawNetworkPlayersExt(boolean force)
3809 if (game_status != GAME_MODE_MAIN)
3812 if (!network.connected && !force)
3815 // do not draw network players if level preview redefined, but players aren't
3816 if (preview.redefined && !menu.main.network_players.redefined)
3819 int num_players = 0;
3822 for (i = 0; i < MAX_PLAYERS; i++)
3823 if (stored_player[i].connected_network)
3826 struct TextPosInfo *pos = &menu.main.network_players;
3827 int tile_size = pos->tile_size;
3828 int border_size = pos->border_size;
3829 int xoffset_text = tile_size + border_size;
3830 int font_nr = pos->font;
3831 int font_width = getFontWidth(font_nr);
3832 int font_height = getFontHeight(font_nr);
3833 int player_height = MAX(tile_size, font_height);
3834 int player_yoffset = player_height + border_size;
3835 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3836 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3837 int all_players_height = num_players * player_yoffset - border_size;
3838 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3839 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3840 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3842 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3843 max_players_width, max_players_height);
3845 // first draw local network player ...
3846 for (i = 0; i < MAX_PLAYERS; i++)
3848 if (stored_player[i].connected_network &&
3849 stored_player[i].connected_locally)
3851 char *player_name = getNetworkPlayerName(i + 1);
3852 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3853 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3855 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3857 ypos += player_yoffset;
3861 // ... then draw all other network players
3862 for (i = 0; i < MAX_PLAYERS; i++)
3864 if (stored_player[i].connected_network &&
3865 !stored_player[i].connected_locally)
3867 char *player_name = getNetworkPlayerName(i + 1);
3868 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3869 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3871 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3873 ypos += player_yoffset;
3878 void DrawNetworkPlayers(void)
3880 DrawNetworkPlayersExt(FALSE);
3883 void ClearNetworkPlayers(void)
3885 DrawNetworkPlayersExt(TRUE);
3888 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3889 int graphic, int lx, int ly,
3892 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3894 if (mask_mode == USE_MASKING)
3895 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3897 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3900 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3901 int graphic, int sync_frame, int mask_mode)
3903 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3905 if (mask_mode == USE_MASKING)
3906 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3908 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3911 static void DrawGraphicAnimation(int x, int y, int graphic)
3913 int lx = LEVELX(x), ly = LEVELY(y);
3914 int mask_mode = NO_MASKING;
3916 if (!IN_SCR_FIELD(x, y))
3919 if (game.use_masked_elements)
3921 if (Tile[lx][ly] != EL_EMPTY)
3923 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3925 mask_mode = USE_MASKING;
3929 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3930 graphic, lx, ly, mask_mode);
3932 MarkTileDirty(x, y);
3935 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3937 int lx = LEVELX(x), ly = LEVELY(y);
3938 int mask_mode = NO_MASKING;
3940 if (!IN_SCR_FIELD(x, y))
3943 if (game.use_masked_elements)
3945 if (Tile[lx][ly] != EL_EMPTY)
3947 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3949 mask_mode = USE_MASKING;
3953 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3954 graphic, lx, ly, mask_mode);
3956 MarkTileDirty(x, y);
3959 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3961 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3964 void DrawLevelElementAnimation(int x, int y, int element)
3966 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3968 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3971 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3973 int sx = SCREENX(x), sy = SCREENY(y);
3975 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3978 if (Tile[x][y] == EL_EMPTY)
3979 graphic = el2img(GfxElementEmpty[x][y]);
3981 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3984 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3987 DrawGraphicAnimation(sx, sy, graphic);
3990 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3991 DrawLevelFieldCrumbled(x, y);
3993 if (GFX_CRUMBLED(Tile[x][y]))
3994 DrawLevelFieldCrumbled(x, y);
3998 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4000 int sx = SCREENX(x), sy = SCREENY(y);
4003 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4006 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4008 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4011 DrawGraphicAnimation(sx, sy, graphic);
4013 if (GFX_CRUMBLED(element))
4014 DrawLevelFieldCrumbled(x, y);
4017 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4019 if (player->use_murphy)
4021 // this works only because currently only one player can be "murphy" ...
4022 static int last_horizontal_dir = MV_LEFT;
4023 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4025 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4026 last_horizontal_dir = move_dir;
4028 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4030 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4032 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4038 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4041 static boolean equalGraphics(int graphic1, int graphic2)
4043 struct GraphicInfo *g1 = &graphic_info[graphic1];
4044 struct GraphicInfo *g2 = &graphic_info[graphic2];
4046 return (g1->bitmap == g2->bitmap &&
4047 g1->src_x == g2->src_x &&
4048 g1->src_y == g2->src_y &&
4049 g1->anim_frames == g2->anim_frames &&
4050 g1->anim_delay == g2->anim_delay &&
4051 g1->anim_mode == g2->anim_mode);
4054 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4058 DRAW_PLAYER_STAGE_INIT = 0,
4059 DRAW_PLAYER_STAGE_LAST_FIELD,
4060 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4061 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4062 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4063 DRAW_PLAYER_STAGE_PLAYER,
4065 DRAW_PLAYER_STAGE_PLAYER,
4066 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4068 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4069 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4071 NUM_DRAW_PLAYER_STAGES
4074 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4076 static int static_last_player_graphic[MAX_PLAYERS];
4077 static int static_last_player_frame[MAX_PLAYERS];
4078 static boolean static_player_is_opaque[MAX_PLAYERS];
4079 static boolean draw_player[MAX_PLAYERS];
4080 int pnr = player->index_nr;
4082 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4084 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4085 static_last_player_frame[pnr] = player->Frame;
4086 static_player_is_opaque[pnr] = FALSE;
4088 draw_player[pnr] = TRUE;
4091 if (!draw_player[pnr])
4095 if (!IN_LEV_FIELD(player->jx, player->jy))
4097 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4098 Debug("draw:DrawPlayerExt", "This should never happen!");
4100 draw_player[pnr] = FALSE;
4106 int last_player_graphic = static_last_player_graphic[pnr];
4107 int last_player_frame = static_last_player_frame[pnr];
4108 boolean player_is_opaque = static_player_is_opaque[pnr];
4110 int jx = player->jx;
4111 int jy = player->jy;
4112 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4113 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4114 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4115 int last_jx = (player->is_moving ? jx - dx : jx);
4116 int last_jy = (player->is_moving ? jy - dy : jy);
4117 int next_jx = jx + dx;
4118 int next_jy = jy + dy;
4119 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4120 int sx = SCREENX(jx);
4121 int sy = SCREENY(jy);
4122 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4123 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4124 int element = Tile[jx][jy];
4125 int last_element = Tile[last_jx][last_jy];
4126 int action = (player->is_pushing ? ACTION_PUSHING :
4127 player->is_digging ? ACTION_DIGGING :
4128 player->is_collecting ? ACTION_COLLECTING :
4129 player->is_moving ? ACTION_MOVING :
4130 player->is_snapping ? ACTION_SNAPPING :
4131 player->is_dropping ? ACTION_DROPPING :
4132 player->is_waiting ? player->action_waiting :
4135 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4137 // ------------------------------------------------------------------------
4138 // initialize drawing the player
4139 // ------------------------------------------------------------------------
4141 draw_player[pnr] = FALSE;
4143 // GfxElement[][] is set to the element the player is digging or collecting;
4144 // remove also for off-screen player if the player is not moving anymore
4145 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4146 GfxElement[jx][jy] = EL_UNDEFINED;
4148 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4151 if (element == EL_EXPLOSION)
4154 InitPlayerGfxAnimation(player, action, move_dir);
4156 draw_player[pnr] = TRUE;
4158 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4160 // ------------------------------------------------------------------------
4161 // draw things in the field the player is leaving, if needed
4162 // ------------------------------------------------------------------------
4164 if (!IN_SCR_FIELD(sx, sy))
4165 draw_player[pnr] = FALSE;
4167 if (!player->is_moving)
4170 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4172 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4174 if (last_element == EL_DYNAMITE_ACTIVE ||
4175 last_element == EL_EM_DYNAMITE_ACTIVE ||
4176 last_element == EL_SP_DISK_RED_ACTIVE)
4177 DrawDynamite(last_jx, last_jy);
4179 DrawLevelFieldThruMask(last_jx, last_jy);
4181 else 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 DrawLevelField(last_jx, last_jy);
4188 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4189 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4191 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4193 // ------------------------------------------------------------------------
4194 // draw things behind the player, if needed
4195 // ------------------------------------------------------------------------
4199 DrawLevelElement(jx, jy, Back[jx][jy]);
4204 if (IS_ACTIVE_BOMB(element))
4206 DrawLevelElement(jx, jy, EL_EMPTY);
4211 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4213 int old_element = GfxElement[jx][jy];
4214 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4215 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4217 if (GFX_CRUMBLED(old_element))
4218 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4220 DrawScreenGraphic(sx, sy, old_graphic, frame);
4222 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4223 static_player_is_opaque[pnr] = TRUE;
4227 GfxElement[jx][jy] = EL_UNDEFINED;
4229 // make sure that pushed elements are drawn with correct frame rate
4230 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4232 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4233 GfxFrame[jx][jy] = player->StepFrame;
4235 DrawLevelField(jx, jy);
4238 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4240 // ------------------------------------------------------------------------
4241 // draw things the player is pushing, if needed
4242 // ------------------------------------------------------------------------
4244 if (!player->is_pushing || !player->is_moving)
4247 int gfx_frame = GfxFrame[jx][jy];
4249 if (!IS_MOVING(jx, jy)) // push movement already finished
4251 element = Tile[next_jx][next_jy];
4252 gfx_frame = GfxFrame[next_jx][next_jy];
4255 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4256 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4257 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4259 // draw background element under pushed element (like the Sokoban field)
4260 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4262 // this allows transparent pushing animation over non-black background
4265 DrawLevelElement(jx, jy, Back[jx][jy]);
4267 DrawLevelElement(jx, jy, EL_EMPTY);
4269 if (Back[next_jx][next_jy])
4270 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4272 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4274 else if (Back[next_jx][next_jy])
4275 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4277 int px = SCREENX(jx), py = SCREENY(jy);
4278 int pxx = (TILEX - ABS(sxx)) * dx;
4279 int pyy = (TILEY - ABS(syy)) * dy;
4282 // do not draw (EM style) pushing animation when pushing is finished
4283 // (two-tile animations usually do not contain start and end frame)
4284 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4285 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4287 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4289 // masked drawing is needed for EMC style (double) movement graphics
4290 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4291 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4294 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4296 // ------------------------------------------------------------------------
4297 // draw player himself
4298 // ------------------------------------------------------------------------
4300 int graphic = getPlayerGraphic(player, move_dir);
4302 // in the case of changed player action or direction, prevent the current
4303 // animation frame from being restarted for identical animations
4304 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4305 player->Frame = last_player_frame;
4307 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4309 if (player_is_opaque)
4310 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4312 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4314 if (SHIELD_ON(player))
4316 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4317 IMG_SHIELD_NORMAL_ACTIVE);
4318 frame = getGraphicAnimationFrame(graphic, -1);
4320 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4323 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4325 // ------------------------------------------------------------------------
4326 // draw things in front of player (active dynamite or dynabombs)
4327 // ------------------------------------------------------------------------
4329 if (IS_ACTIVE_BOMB(element))
4331 int graphic = el2img(element);
4332 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4334 if (game.emulation == EMU_SUPAPLEX)
4335 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4337 DrawGraphicThruMask(sx, sy, graphic, frame);
4340 if (player_is_moving && last_element == EL_EXPLOSION)
4342 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4343 GfxElement[last_jx][last_jy] : EL_EMPTY);
4344 int graphic = el_act2img(element, ACTION_EXPLODING);
4345 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4346 int phase = ExplodePhase[last_jx][last_jy] - 1;
4347 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4350 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4353 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4355 // ------------------------------------------------------------------------
4356 // draw elements the player is just walking/passing through/under
4357 // ------------------------------------------------------------------------
4359 if (player_is_moving)
4361 // handle the field the player is leaving ...
4362 if (IS_ACCESSIBLE_INSIDE(last_element))
4363 DrawLevelField(last_jx, last_jy);
4364 else if (IS_ACCESSIBLE_UNDER(last_element))
4365 DrawLevelFieldThruMask(last_jx, last_jy);
4368 // do not redraw accessible elements if the player is just pushing them
4369 if (!player_is_moving || !player->is_pushing)
4371 // ... and the field the player is entering
4372 if (IS_ACCESSIBLE_INSIDE(element))
4373 DrawLevelField(jx, jy);
4374 else if (IS_ACCESSIBLE_UNDER(element))
4375 DrawLevelFieldThruMask(jx, jy);
4378 MarkTileDirty(sx, sy);
4382 void DrawPlayer(struct PlayerInfo *player)
4386 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4387 DrawPlayerExt(player, i);
4390 void DrawAllPlayers(void)
4394 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4395 for (j = 0; j < MAX_PLAYERS; j++)
4396 if (stored_player[j].active)
4397 DrawPlayerExt(&stored_player[j], i);
4400 void DrawPlayerField(int x, int y)
4402 if (!IS_PLAYER(x, y))
4405 DrawPlayer(PLAYERINFO(x, y));
4408 // ----------------------------------------------------------------------------
4410 void WaitForEventToContinue(void)
4412 boolean first_wait = TRUE;
4413 boolean still_wait = TRUE;
4415 if (program.headless)
4418 // simulate releasing mouse button over last gadget, if still pressed
4420 HandleGadgets(-1, -1, 0);
4422 button_status = MB_RELEASED;
4425 ClearPlayerAction();
4431 if (NextValidEvent(&event))
4435 case EVENT_BUTTONPRESS:
4436 case EVENT_FINGERPRESS:
4440 case EVENT_BUTTONRELEASE:
4441 case EVENT_FINGERRELEASE:
4442 still_wait = first_wait;
4445 case EVENT_KEYPRESS:
4446 case SDL_CONTROLLERBUTTONDOWN:
4447 case SDL_JOYBUTTONDOWN:
4452 HandleOtherEvents(&event);
4456 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4461 if (!PendingEvent())
4466 #define MAX_REQUEST_LINES 13
4467 #define MAX_REQUEST_LINE_FONT1_LEN 7
4468 #define MAX_REQUEST_LINE_FONT2_LEN 10
4470 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4472 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4474 int draw_buffer_last = GetDrawtoField();
4475 int width = request.width;
4476 int height = request.height;
4480 // when showing request dialog after game ended, deactivate game panel
4481 if (game_just_ended)
4482 game.panel.active = FALSE;
4484 game.request_active = TRUE;
4486 setRequestPosition(&sx, &sy, FALSE);
4488 button_status = MB_RELEASED;
4490 request_gadget_id = -1;
4495 boolean event_handled = FALSE;
4497 if (game_just_ended)
4499 SetDrawtoField(draw_buffer_game);
4501 HandleGameActions();
4503 SetDrawtoField(DRAW_TO_BACKBUFFER);
4505 if (global.use_envelope_request)
4507 // copy current state of request area to middle of playfield area
4508 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4516 while (NextValidEvent(&event))
4518 event_handled = TRUE;
4522 case EVENT_BUTTONPRESS:
4523 case EVENT_BUTTONRELEASE:
4524 case EVENT_MOTIONNOTIFY:
4528 if (event.type == EVENT_MOTIONNOTIFY)
4533 motion_status = TRUE;
4534 mx = ((MotionEvent *) &event)->x;
4535 my = ((MotionEvent *) &event)->y;
4539 motion_status = FALSE;
4540 mx = ((ButtonEvent *) &event)->x;
4541 my = ((ButtonEvent *) &event)->y;
4542 if (event.type == EVENT_BUTTONPRESS)
4543 button_status = ((ButtonEvent *) &event)->button;
4545 button_status = MB_RELEASED;
4548 // this sets 'request_gadget_id'
4549 HandleGadgets(mx, my, button_status);
4551 switch (request_gadget_id)
4553 case TOOL_CTRL_ID_YES:
4554 case TOOL_CTRL_ID_TOUCH_YES:
4557 case TOOL_CTRL_ID_NO:
4558 case TOOL_CTRL_ID_TOUCH_NO:
4561 case TOOL_CTRL_ID_CONFIRM:
4562 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4563 result = TRUE | FALSE;
4566 case TOOL_CTRL_ID_PLAYER_1:
4569 case TOOL_CTRL_ID_PLAYER_2:
4572 case TOOL_CTRL_ID_PLAYER_3:
4575 case TOOL_CTRL_ID_PLAYER_4:
4580 // only check clickable animations if no request gadget clicked
4581 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4588 case SDL_WINDOWEVENT:
4589 HandleWindowEvent((WindowEvent *) &event);
4592 case SDL_APP_WILLENTERBACKGROUND:
4593 case SDL_APP_DIDENTERBACKGROUND:
4594 case SDL_APP_WILLENTERFOREGROUND:
4595 case SDL_APP_DIDENTERFOREGROUND:
4596 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4599 case EVENT_KEYPRESS:
4601 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4606 if (req_state & REQ_CONFIRM)
4615 #if defined(KSYM_Rewind)
4616 case KSYM_Rewind: // for Amazon Fire TV remote
4625 #if defined(KSYM_FastForward)
4626 case KSYM_FastForward: // for Amazon Fire TV remote
4632 HandleKeysDebug(key, KEY_PRESSED);
4636 if (req_state & REQ_PLAYER)
4638 int old_player_nr = setup.network_player_nr;
4641 result = old_player_nr + 1;
4646 result = old_player_nr + 1;
4677 case EVENT_FINGERRELEASE:
4678 case EVENT_KEYRELEASE:
4679 ClearPlayerAction();
4682 case SDL_CONTROLLERBUTTONDOWN:
4683 switch (event.cbutton.button)
4685 case SDL_CONTROLLER_BUTTON_A:
4686 case SDL_CONTROLLER_BUTTON_X:
4687 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4688 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4692 case SDL_CONTROLLER_BUTTON_B:
4693 case SDL_CONTROLLER_BUTTON_Y:
4694 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4695 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4696 case SDL_CONTROLLER_BUTTON_BACK:
4701 if (req_state & REQ_PLAYER)
4703 int old_player_nr = setup.network_player_nr;
4706 result = old_player_nr + 1;
4708 switch (event.cbutton.button)
4710 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4711 case SDL_CONTROLLER_BUTTON_Y:
4715 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4716 case SDL_CONTROLLER_BUTTON_B:
4720 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4721 case SDL_CONTROLLER_BUTTON_A:
4725 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4726 case SDL_CONTROLLER_BUTTON_X:
4737 case SDL_CONTROLLERBUTTONUP:
4738 HandleJoystickEvent(&event);
4739 ClearPlayerAction();
4743 HandleOtherEvents(&event);
4748 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4750 int joy = AnyJoystick();
4752 if (joy & JOY_BUTTON_1)
4754 else if (joy & JOY_BUTTON_2)
4757 else if (AnyJoystick())
4759 int joy = AnyJoystick();
4761 if (req_state & REQ_PLAYER)
4765 else if (joy & JOY_RIGHT)
4767 else if (joy & JOY_DOWN)
4769 else if (joy & JOY_LEFT)
4776 if (game_just_ended)
4778 if (global.use_envelope_request)
4780 // copy back current state of pressed buttons inside request area
4781 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4785 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4791 SetDrawtoField(draw_buffer_last);
4793 game.request_active = FALSE;
4798 static boolean RequestDoor(char *text, unsigned int req_state)
4800 int draw_buffer_last = GetDrawtoField();
4801 unsigned int old_door_state;
4802 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4803 int font_nr = FONT_TEXT_2;
4808 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4810 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4811 font_nr = FONT_TEXT_1;
4814 if (game_status == GAME_MODE_PLAYING)
4815 BlitScreenToBitmap(backbuffer);
4817 // disable deactivated drawing when quick-loading level tape recording
4818 if (tape.playing && tape.deactivate_display)
4819 TapeDeactivateDisplayOff(TRUE);
4821 SetMouseCursor(CURSOR_DEFAULT);
4823 // pause network game while waiting for request to answer
4824 if (network.enabled &&
4825 game_status == GAME_MODE_PLAYING &&
4826 !game.all_players_gone &&
4827 req_state & REQUEST_WAIT_FOR_INPUT)
4828 SendToServer_PausePlaying();
4830 old_door_state = GetDoorState();
4832 // simulate releasing mouse button over last gadget, if still pressed
4834 HandleGadgets(-1, -1, 0);
4838 // draw released gadget before proceeding
4841 if (old_door_state & DOOR_OPEN_1)
4843 CloseDoor(DOOR_CLOSE_1);
4845 // save old door content
4846 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4847 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4850 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4851 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4853 // clear door drawing field
4854 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4856 // force DOOR font inside door area
4857 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4859 // write text for request
4860 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4862 char text_line[max_request_line_len + 1];
4868 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4870 tc = *(text_ptr + tx);
4871 // if (!tc || tc == ' ')
4872 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4876 if ((tc == '?' || tc == '!') && tl == 0)
4886 strncpy(text_line, text_ptr, tl);
4889 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4890 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4891 text_line, font_nr);
4893 text_ptr += tl + (tc == ' ' ? 1 : 0);
4894 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4899 if (req_state & REQ_ASK)
4901 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4902 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4903 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4904 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4906 else if (req_state & REQ_CONFIRM)
4908 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4909 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4911 else if (req_state & REQ_PLAYER)
4913 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4914 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4915 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4916 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4919 // copy request gadgets to door backbuffer
4920 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4922 OpenDoor(DOOR_OPEN_1);
4924 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4926 if (game_status == GAME_MODE_PLAYING)
4928 SetPanelBackground();
4929 SetDrawBackgroundMask(REDRAW_DOOR_1);
4933 SetDrawBackgroundMask(REDRAW_FIELD);
4939 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4941 // ---------- handle request buttons ----------
4942 result = RequestHandleEvents(req_state, draw_buffer_last);
4946 if (!(req_state & REQ_STAY_OPEN))
4948 CloseDoor(DOOR_CLOSE_1);
4950 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4951 (req_state & REQ_REOPEN))
4952 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4957 if (game_status == GAME_MODE_PLAYING)
4959 SetPanelBackground();
4960 SetDrawBackgroundMask(REDRAW_DOOR_1);
4964 SetDrawBackgroundMask(REDRAW_FIELD);
4967 // continue network game after request
4968 if (network.enabled &&
4969 game_status == GAME_MODE_PLAYING &&
4970 !game.all_players_gone &&
4971 req_state & REQUEST_WAIT_FOR_INPUT)
4972 SendToServer_ContinuePlaying();
4974 // restore deactivated drawing when quick-loading level tape recording
4975 if (tape.playing && tape.deactivate_display)
4976 TapeDeactivateDisplayOn();
4981 static boolean RequestEnvelope(char *text, unsigned int req_state)
4983 int draw_buffer_last = GetDrawtoField();
4986 if (game_status == GAME_MODE_PLAYING)
4987 BlitScreenToBitmap(backbuffer);
4989 // disable deactivated drawing when quick-loading level tape recording
4990 if (tape.playing && tape.deactivate_display)
4991 TapeDeactivateDisplayOff(TRUE);
4993 SetMouseCursor(CURSOR_DEFAULT);
4995 // pause network game while waiting for request to answer
4996 if (network.enabled &&
4997 game_status == GAME_MODE_PLAYING &&
4998 !game.all_players_gone &&
4999 req_state & REQUEST_WAIT_FOR_INPUT)
5000 SendToServer_PausePlaying();
5002 // simulate releasing mouse button over last gadget, if still pressed
5004 HandleGadgets(-1, -1, 0);
5008 // (replace with setting corresponding request background)
5009 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5010 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5012 // clear door drawing field
5013 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5015 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5017 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5019 if (game_status == GAME_MODE_PLAYING)
5021 SetPanelBackground();
5022 SetDrawBackgroundMask(REDRAW_DOOR_1);
5026 SetDrawBackgroundMask(REDRAW_FIELD);
5032 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5034 // ---------- handle request buttons ----------
5035 result = RequestHandleEvents(req_state, draw_buffer_last);
5039 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5043 if (game_status == GAME_MODE_PLAYING)
5045 SetPanelBackground();
5046 SetDrawBackgroundMask(REDRAW_DOOR_1);
5050 SetDrawBackgroundMask(REDRAW_FIELD);
5053 // continue network game after request
5054 if (network.enabled &&
5055 game_status == GAME_MODE_PLAYING &&
5056 !game.all_players_gone &&
5057 req_state & REQUEST_WAIT_FOR_INPUT)
5058 SendToServer_ContinuePlaying();
5060 // restore deactivated drawing when quick-loading level tape recording
5061 if (tape.playing && tape.deactivate_display)
5062 TapeDeactivateDisplayOn();
5067 boolean Request(char *text, unsigned int req_state)
5069 boolean overlay_enabled = GetOverlayEnabled();
5072 game.request_active_or_moving = TRUE;
5074 SetOverlayEnabled(FALSE);
5076 if (global.use_envelope_request)
5077 result = RequestEnvelope(text, req_state);
5079 result = RequestDoor(text, req_state);
5081 SetOverlayEnabled(overlay_enabled);
5083 game.request_active_or_moving = FALSE;
5088 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5090 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5091 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5094 if (dpo1->sort_priority != dpo2->sort_priority)
5095 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5097 compare_result = dpo1->nr - dpo2->nr;
5099 return compare_result;
5102 void InitGraphicCompatibilityInfo_Doors(void)
5108 struct DoorInfo *door;
5112 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5113 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5115 { -1, -1, -1, NULL }
5117 struct Rect door_rect_list[] =
5119 { DX, DY, DXSIZE, DYSIZE },
5120 { VX, VY, VXSIZE, VYSIZE }
5124 for (i = 0; doors[i].door_token != -1; i++)
5126 int door_token = doors[i].door_token;
5127 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5128 int part_1 = doors[i].part_1;
5129 int part_8 = doors[i].part_8;
5130 int part_2 = part_1 + 1;
5131 int part_3 = part_1 + 2;
5132 struct DoorInfo *door = doors[i].door;
5133 struct Rect *door_rect = &door_rect_list[door_index];
5134 boolean door_gfx_redefined = FALSE;
5136 // check if any door part graphic definitions have been redefined
5138 for (j = 0; door_part_controls[j].door_token != -1; j++)
5140 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5141 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5143 if (dpc->door_token == door_token && fi->redefined)
5144 door_gfx_redefined = TRUE;
5147 // check for old-style door graphic/animation modifications
5149 if (!door_gfx_redefined)
5151 if (door->anim_mode & ANIM_STATIC_PANEL)
5153 door->panel.step_xoffset = 0;
5154 door->panel.step_yoffset = 0;
5157 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5159 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5160 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5161 int num_door_steps, num_panel_steps;
5163 // remove door part graphics other than the two default wings
5165 for (j = 0; door_part_controls[j].door_token != -1; j++)
5167 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5168 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5170 if (dpc->graphic >= part_3 &&
5171 dpc->graphic <= part_8)
5175 // set graphics and screen positions of the default wings
5177 g_part_1->width = door_rect->width;
5178 g_part_1->height = door_rect->height;
5179 g_part_2->width = door_rect->width;
5180 g_part_2->height = door_rect->height;
5181 g_part_2->src_x = door_rect->width;
5182 g_part_2->src_y = g_part_1->src_y;
5184 door->part_2.x = door->part_1.x;
5185 door->part_2.y = door->part_1.y;
5187 if (door->width != -1)
5189 g_part_1->width = door->width;
5190 g_part_2->width = door->width;
5192 // special treatment for graphics and screen position of right wing
5193 g_part_2->src_x += door_rect->width - door->width;
5194 door->part_2.x += door_rect->width - door->width;
5197 if (door->height != -1)
5199 g_part_1->height = door->height;
5200 g_part_2->height = door->height;
5202 // special treatment for graphics and screen position of bottom wing
5203 g_part_2->src_y += door_rect->height - door->height;
5204 door->part_2.y += door_rect->height - door->height;
5207 // set animation delays for the default wings and panels
5209 door->part_1.step_delay = door->step_delay;
5210 door->part_2.step_delay = door->step_delay;
5211 door->panel.step_delay = door->step_delay;
5213 // set animation draw order for the default wings
5215 door->part_1.sort_priority = 2; // draw left wing over ...
5216 door->part_2.sort_priority = 1; // ... right wing
5218 // set animation draw offset for the default wings
5220 if (door->anim_mode & ANIM_HORIZONTAL)
5222 door->part_1.step_xoffset = door->step_offset;
5223 door->part_1.step_yoffset = 0;
5224 door->part_2.step_xoffset = door->step_offset * -1;
5225 door->part_2.step_yoffset = 0;
5227 num_door_steps = g_part_1->width / door->step_offset;
5229 else // ANIM_VERTICAL
5231 door->part_1.step_xoffset = 0;
5232 door->part_1.step_yoffset = door->step_offset;
5233 door->part_2.step_xoffset = 0;
5234 door->part_2.step_yoffset = door->step_offset * -1;
5236 num_door_steps = g_part_1->height / door->step_offset;
5239 // set animation draw offset for the default panels
5241 if (door->step_offset > 1)
5243 num_panel_steps = 2 * door_rect->height / door->step_offset;
5244 door->panel.start_step = num_panel_steps - num_door_steps;
5245 door->panel.start_step_closing = door->panel.start_step;
5249 num_panel_steps = door_rect->height / door->step_offset;
5250 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5251 door->panel.start_step_closing = door->panel.start_step;
5252 door->panel.step_delay *= 2;
5259 void InitDoors(void)
5263 for (i = 0; door_part_controls[i].door_token != -1; i++)
5265 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5266 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5268 // initialize "start_step_opening" and "start_step_closing", if needed
5269 if (dpc->pos->start_step_opening == 0 &&
5270 dpc->pos->start_step_closing == 0)
5272 // dpc->pos->start_step_opening = dpc->pos->start_step;
5273 dpc->pos->start_step_closing = dpc->pos->start_step;
5276 // fill structure for door part draw order (sorted below)
5278 dpo->sort_priority = dpc->pos->sort_priority;
5281 // sort door part controls according to sort_priority and graphic number
5282 qsort(door_part_order, MAX_DOOR_PARTS,
5283 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5286 unsigned int OpenDoor(unsigned int door_state)
5288 if (door_state & DOOR_COPY_BACK)
5290 if (door_state & DOOR_OPEN_1)
5291 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5292 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5294 if (door_state & DOOR_OPEN_2)
5295 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5296 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5298 door_state &= ~DOOR_COPY_BACK;
5301 return MoveDoor(door_state);
5304 unsigned int CloseDoor(unsigned int door_state)
5306 unsigned int old_door_state = GetDoorState();
5308 if (!(door_state & DOOR_NO_COPY_BACK))
5310 if (old_door_state & DOOR_OPEN_1)
5311 BlitBitmap(backbuffer, bitmap_db_door_1,
5312 DX, DY, DXSIZE, DYSIZE, 0, 0);
5314 if (old_door_state & DOOR_OPEN_2)
5315 BlitBitmap(backbuffer, bitmap_db_door_2,
5316 VX, VY, VXSIZE, VYSIZE, 0, 0);
5318 door_state &= ~DOOR_NO_COPY_BACK;
5321 return MoveDoor(door_state);
5324 unsigned int GetDoorState(void)
5326 return MoveDoor(DOOR_GET_STATE);
5329 unsigned int SetDoorState(unsigned int door_state)
5331 return MoveDoor(door_state | DOOR_SET_STATE);
5334 static int euclid(int a, int b)
5336 return (b ? euclid(b, a % b) : a);
5339 unsigned int MoveDoor(unsigned int door_state)
5341 struct Rect door_rect_list[] =
5343 { DX, DY, DXSIZE, DYSIZE },
5344 { VX, VY, VXSIZE, VYSIZE }
5346 static int door1 = DOOR_CLOSE_1;
5347 static int door2 = DOOR_CLOSE_2;
5348 unsigned int door_delay = 0;
5349 unsigned int door_delay_value;
5352 if (door_state == DOOR_GET_STATE)
5353 return (door1 | door2);
5355 if (door_state & DOOR_SET_STATE)
5357 if (door_state & DOOR_ACTION_1)
5358 door1 = door_state & DOOR_ACTION_1;
5359 if (door_state & DOOR_ACTION_2)
5360 door2 = door_state & DOOR_ACTION_2;
5362 return (door1 | door2);
5365 if (!(door_state & DOOR_FORCE_REDRAW))
5367 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5368 door_state &= ~DOOR_OPEN_1;
5369 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5370 door_state &= ~DOOR_CLOSE_1;
5371 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5372 door_state &= ~DOOR_OPEN_2;
5373 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5374 door_state &= ~DOOR_CLOSE_2;
5377 if (global.autoplay_leveldir)
5379 door_state |= DOOR_NO_DELAY;
5380 door_state &= ~DOOR_CLOSE_ALL;
5383 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5384 door_state |= DOOR_NO_DELAY;
5386 if (door_state & DOOR_ACTION)
5388 boolean door_panel_drawn[NUM_DOORS];
5389 boolean panel_has_doors[NUM_DOORS];
5390 boolean door_part_skip[MAX_DOOR_PARTS];
5391 boolean door_part_done[MAX_DOOR_PARTS];
5392 boolean door_part_done_all;
5393 int num_steps[MAX_DOOR_PARTS];
5394 int max_move_delay = 0; // delay for complete animations of all doors
5395 int max_step_delay = 0; // delay (ms) between two animation frames
5396 int num_move_steps = 0; // number of animation steps for all doors
5397 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5398 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5402 for (i = 0; i < NUM_DOORS; i++)
5403 panel_has_doors[i] = FALSE;
5405 for (i = 0; i < MAX_DOOR_PARTS; i++)
5407 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5408 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5409 int door_token = dpc->door_token;
5411 door_part_done[i] = FALSE;
5412 door_part_skip[i] = (!(door_state & door_token) ||
5416 for (i = 0; i < MAX_DOOR_PARTS; i++)
5418 int nr = door_part_order[i].nr;
5419 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5420 struct DoorPartPosInfo *pos = dpc->pos;
5421 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5422 int door_token = dpc->door_token;
5423 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5424 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5425 int step_xoffset = ABS(pos->step_xoffset);
5426 int step_yoffset = ABS(pos->step_yoffset);
5427 int step_delay = pos->step_delay;
5428 int current_door_state = door_state & door_token;
5429 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5430 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5431 boolean part_opening = (is_panel ? door_closing : door_opening);
5432 int start_step = (part_opening ? pos->start_step_opening :
5433 pos->start_step_closing);
5434 float move_xsize = (step_xoffset ? g->width : 0);
5435 float move_ysize = (step_yoffset ? g->height : 0);
5436 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5437 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5438 int move_steps = (move_xsteps && move_ysteps ?
5439 MIN(move_xsteps, move_ysteps) :
5440 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5441 int move_delay = move_steps * step_delay;
5443 if (door_part_skip[nr])
5446 max_move_delay = MAX(max_move_delay, move_delay);
5447 max_step_delay = (max_step_delay == 0 ? step_delay :
5448 euclid(max_step_delay, step_delay));
5449 num_steps[nr] = move_steps;
5453 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5455 panel_has_doors[door_index] = TRUE;
5459 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5461 num_move_steps = max_move_delay / max_step_delay;
5462 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5464 door_delay_value = max_step_delay;
5466 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5468 start = num_move_steps - 1;
5472 // opening door sound has priority over simultaneously closing door
5473 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5475 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5477 if (door_state & DOOR_OPEN_1)
5478 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5479 if (door_state & DOOR_OPEN_2)
5480 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5482 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5484 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5486 if (door_state & DOOR_CLOSE_1)
5487 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5488 if (door_state & DOOR_CLOSE_2)
5489 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5493 for (k = start; k < num_move_steps; k++)
5495 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5497 door_part_done_all = TRUE;
5499 for (i = 0; i < NUM_DOORS; i++)
5500 door_panel_drawn[i] = FALSE;
5502 for (i = 0; i < MAX_DOOR_PARTS; i++)
5504 int nr = door_part_order[i].nr;
5505 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5506 struct DoorPartPosInfo *pos = dpc->pos;
5507 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5508 int door_token = dpc->door_token;
5509 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5510 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5511 boolean is_panel_and_door_has_closed = FALSE;
5512 struct Rect *door_rect = &door_rect_list[door_index];
5513 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5515 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5516 int current_door_state = door_state & door_token;
5517 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5518 boolean door_closing = !door_opening;
5519 boolean part_opening = (is_panel ? door_closing : door_opening);
5520 boolean part_closing = !part_opening;
5521 int start_step = (part_opening ? pos->start_step_opening :
5522 pos->start_step_closing);
5523 int step_delay = pos->step_delay;
5524 int step_factor = step_delay / max_step_delay;
5525 int k1 = (step_factor ? k / step_factor + 1 : k);
5526 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5527 int kk = MAX(0, k2);
5530 int src_x, src_y, src_xx, src_yy;
5531 int dst_x, dst_y, dst_xx, dst_yy;
5534 if (door_part_skip[nr])
5537 if (!(door_state & door_token))
5545 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5546 int kk_door = MAX(0, k2_door);
5547 int sync_frame = kk_door * door_delay_value;
5548 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5550 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5551 &g_src_x, &g_src_y);
5556 if (!door_panel_drawn[door_index])
5558 ClearRectangle(drawto, door_rect->x, door_rect->y,
5559 door_rect->width, door_rect->height);
5561 door_panel_drawn[door_index] = TRUE;
5564 // draw opening or closing door parts
5566 if (pos->step_xoffset < 0) // door part on right side
5569 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5572 if (dst_xx + width > door_rect->width)
5573 width = door_rect->width - dst_xx;
5575 else // door part on left side
5578 dst_xx = pos->x - kk * pos->step_xoffset;
5582 src_xx = ABS(dst_xx);
5586 width = g->width - src_xx;
5588 if (width > door_rect->width)
5589 width = door_rect->width;
5591 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5594 if (pos->step_yoffset < 0) // door part on bottom side
5597 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5600 if (dst_yy + height > door_rect->height)
5601 height = door_rect->height - dst_yy;
5603 else // door part on top side
5606 dst_yy = pos->y - kk * pos->step_yoffset;
5610 src_yy = ABS(dst_yy);
5614 height = g->height - src_yy;
5617 src_x = g_src_x + src_xx;
5618 src_y = g_src_y + src_yy;
5620 dst_x = door_rect->x + dst_xx;
5621 dst_y = door_rect->y + dst_yy;
5623 is_panel_and_door_has_closed =
5626 panel_has_doors[door_index] &&
5627 k >= num_move_steps_doors_only - 1);
5629 if (width >= 0 && width <= g->width &&
5630 height >= 0 && height <= g->height &&
5631 !is_panel_and_door_has_closed)
5633 if (is_panel || !pos->draw_masked)
5634 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5637 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5641 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5643 if ((part_opening && (width < 0 || height < 0)) ||
5644 (part_closing && (width >= g->width && height >= g->height)))
5645 door_part_done[nr] = TRUE;
5647 // continue door part animations, but not panel after door has closed
5648 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5649 door_part_done_all = FALSE;
5652 if (!(door_state & DOOR_NO_DELAY))
5656 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5658 // prevent OS (Windows) from complaining about program not responding
5662 if (door_part_done_all)
5666 if (!(door_state & DOOR_NO_DELAY))
5668 // wait for specified door action post delay
5669 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5670 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5671 else if (door_state & DOOR_ACTION_1)
5672 door_delay_value = door_1.post_delay;
5673 else if (door_state & DOOR_ACTION_2)
5674 door_delay_value = door_2.post_delay;
5676 while (!DelayReached(&door_delay, door_delay_value))
5681 if (door_state & DOOR_ACTION_1)
5682 door1 = door_state & DOOR_ACTION_1;
5683 if (door_state & DOOR_ACTION_2)
5684 door2 = door_state & DOOR_ACTION_2;
5686 // draw masked border over door area
5687 DrawMaskedBorder(REDRAW_DOOR_1);
5688 DrawMaskedBorder(REDRAW_DOOR_2);
5690 ClearAutoRepeatKeyEvents();
5692 return (door1 | door2);
5695 static boolean useSpecialEditorDoor(void)
5697 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5698 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5700 // do not draw special editor door if editor border defined or redefined
5701 if (graphic_info[graphic].bitmap != NULL || redefined)
5704 // do not draw special editor door if global border defined to be empty
5705 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5708 // do not draw special editor door if viewport definitions do not match
5712 EY + EYSIZE != VY + VYSIZE)
5718 void DrawSpecialEditorDoor(void)
5720 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5721 int top_border_width = gfx1->width;
5722 int top_border_height = gfx1->height;
5723 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5724 int ex = EX - outer_border;
5725 int ey = EY - outer_border;
5726 int vy = VY - outer_border;
5727 int exsize = EXSIZE + 2 * outer_border;
5729 if (!useSpecialEditorDoor())
5732 // draw bigger level editor toolbox window
5733 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5734 top_border_width, top_border_height, ex, ey - top_border_height);
5735 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5736 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5738 redraw_mask |= REDRAW_ALL;
5741 void UndrawSpecialEditorDoor(void)
5743 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5744 int top_border_width = gfx1->width;
5745 int top_border_height = gfx1->height;
5746 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5747 int ex = EX - outer_border;
5748 int ey = EY - outer_border;
5749 int ey_top = ey - top_border_height;
5750 int exsize = EXSIZE + 2 * outer_border;
5751 int eysize = EYSIZE + 2 * outer_border;
5753 if (!useSpecialEditorDoor())
5756 // draw normal tape recorder window
5757 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5759 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5760 ex, ey_top, top_border_width, top_border_height,
5762 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5763 ex, ey, exsize, eysize, ex, ey);
5767 // if screen background is set to "[NONE]", clear editor toolbox window
5768 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5769 ClearRectangle(drawto, ex, ey, exsize, eysize);
5772 redraw_mask |= REDRAW_ALL;
5776 // ---------- new tool button stuff -------------------------------------------
5781 struct TextPosInfo *pos;
5783 boolean is_touch_button;
5785 } toolbutton_info[NUM_TOOL_BUTTONS] =
5788 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5789 TOOL_CTRL_ID_YES, FALSE, "yes"
5792 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5793 TOOL_CTRL_ID_NO, FALSE, "no"
5796 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5797 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5800 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5801 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5804 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5805 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5808 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5809 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5812 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5813 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5816 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5817 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5820 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5821 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5824 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5825 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5829 void CreateToolButtons(void)
5833 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5835 int graphic = toolbutton_info[i].graphic;
5836 struct GraphicInfo *gfx = &graphic_info[graphic];
5837 struct TextPosInfo *pos = toolbutton_info[i].pos;
5838 struct GadgetInfo *gi;
5839 Bitmap *deco_bitmap = None;
5840 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5841 unsigned int event_mask = GD_EVENT_RELEASED;
5842 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5843 int base_x = (is_touch_button ? 0 : DX);
5844 int base_y = (is_touch_button ? 0 : DY);
5845 int gd_x = gfx->src_x;
5846 int gd_y = gfx->src_y;
5847 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5848 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5853 if (global.use_envelope_request && !is_touch_button)
5855 setRequestPosition(&base_x, &base_y, TRUE);
5857 // check if request buttons are outside of envelope and fix, if needed
5858 if (x < 0 || x + gfx->width > request.width ||
5859 y < 0 || y + gfx->height > request.height)
5861 if (id == TOOL_CTRL_ID_YES)
5864 y = request.height - 2 * request.border_size - gfx->height;
5866 else if (id == TOOL_CTRL_ID_NO)
5868 x = request.width - 2 * request.border_size - gfx->width;
5869 y = request.height - 2 * request.border_size - gfx->height;
5871 else if (id == TOOL_CTRL_ID_CONFIRM)
5873 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5874 y = request.height - 2 * request.border_size - gfx->height;
5876 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5878 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5880 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5881 y = request.height - 2 * request.border_size - gfx->height * 2;
5883 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5884 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5889 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5891 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5893 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5894 pos->size, &deco_bitmap, &deco_x, &deco_y);
5895 deco_xpos = (gfx->width - pos->size) / 2;
5896 deco_ypos = (gfx->height - pos->size) / 2;
5899 gi = CreateGadget(GDI_CUSTOM_ID, id,
5900 GDI_IMAGE_ID, graphic,
5901 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5904 GDI_WIDTH, gfx->width,
5905 GDI_HEIGHT, gfx->height,
5906 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5907 GDI_STATE, GD_BUTTON_UNPRESSED,
5908 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5909 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5910 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5911 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5912 GDI_DECORATION_SIZE, pos->size, pos->size,
5913 GDI_DECORATION_SHIFTING, 1, 1,
5914 GDI_DIRECT_DRAW, FALSE,
5915 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5916 GDI_EVENT_MASK, event_mask,
5917 GDI_CALLBACK_ACTION, HandleToolButtons,
5921 Fail("cannot create gadget");
5923 tool_gadget[id] = gi;
5927 void FreeToolButtons(void)
5931 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5932 FreeGadget(tool_gadget[i]);
5935 static void UnmapToolButtons(void)
5939 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5940 UnmapGadget(tool_gadget[i]);
5943 static void HandleToolButtons(struct GadgetInfo *gi)
5945 request_gadget_id = gi->custom_id;
5948 static struct Mapping_EM_to_RND_object
5951 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5952 boolean is_backside; // backside of moving element
5958 em_object_mapping_list[GAME_TILE_MAX + 1] =
5961 Zborder, FALSE, FALSE,
5965 Zplayer, FALSE, FALSE,
5974 Ztank, FALSE, FALSE,
5978 Zeater, FALSE, FALSE,
5982 Zdynamite, FALSE, FALSE,
5986 Zboom, FALSE, FALSE,
5991 Xchain, FALSE, FALSE,
5992 EL_DEFAULT, ACTION_EXPLODING, -1
5995 Xboom_bug, FALSE, FALSE,
5996 EL_BUG, ACTION_EXPLODING, -1
5999 Xboom_tank, FALSE, FALSE,
6000 EL_SPACESHIP, ACTION_EXPLODING, -1
6003 Xboom_android, FALSE, FALSE,
6004 EL_EMC_ANDROID, ACTION_OTHER, -1
6007 Xboom_1, FALSE, FALSE,
6008 EL_DEFAULT, ACTION_EXPLODING, -1
6011 Xboom_2, FALSE, FALSE,
6012 EL_DEFAULT, ACTION_EXPLODING, -1
6016 Xblank, TRUE, FALSE,
6021 Xsplash_e, FALSE, FALSE,
6022 EL_ACID_SPLASH_RIGHT, -1, -1
6025 Xsplash_w, FALSE, FALSE,
6026 EL_ACID_SPLASH_LEFT, -1, -1
6030 Xplant, TRUE, FALSE,
6031 EL_EMC_PLANT, -1, -1
6034 Yplant, FALSE, FALSE,
6035 EL_EMC_PLANT, -1, -1
6039 Xacid_1, TRUE, FALSE,
6043 Xacid_2, FALSE, FALSE,
6047 Xacid_3, FALSE, FALSE,
6051 Xacid_4, FALSE, FALSE,
6055 Xacid_5, FALSE, FALSE,
6059 Xacid_6, FALSE, FALSE,
6063 Xacid_7, FALSE, FALSE,
6067 Xacid_8, FALSE, FALSE,
6072 Xfake_acid_1, TRUE, FALSE,
6073 EL_EMC_FAKE_ACID, -1, -1
6076 Xfake_acid_2, FALSE, FALSE,
6077 EL_EMC_FAKE_ACID, -1, -1
6080 Xfake_acid_3, FALSE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_4, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_5, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6092 Xfake_acid_6, FALSE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_7, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_8, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6105 Xfake_acid_1_player, FALSE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_2_player, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_3_player, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_4_player, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_5_player, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_6_player, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_7_player, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_8_player, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6138 Xgrass, TRUE, FALSE,
6139 EL_EMC_GRASS, -1, -1
6142 Ygrass_nB, FALSE, FALSE,
6143 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6146 Ygrass_eB, FALSE, FALSE,
6147 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6150 Ygrass_sB, FALSE, FALSE,
6151 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6154 Ygrass_wB, FALSE, FALSE,
6155 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6163 Ydirt_nB, FALSE, FALSE,
6164 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6167 Ydirt_eB, FALSE, FALSE,
6168 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6171 Ydirt_sB, FALSE, FALSE,
6172 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6175 Ydirt_wB, FALSE, FALSE,
6176 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6180 Xandroid, TRUE, FALSE,
6181 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6184 Xandroid_1_n, FALSE, FALSE,
6185 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6188 Xandroid_2_n, FALSE, FALSE,
6189 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6192 Xandroid_1_e, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6196 Xandroid_2_e, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6200 Xandroid_1_w, FALSE, FALSE,
6201 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6204 Xandroid_2_w, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6208 Xandroid_1_s, FALSE, FALSE,
6209 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6212 Xandroid_2_s, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6216 Yandroid_n, FALSE, FALSE,
6217 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6220 Yandroid_nB, FALSE, TRUE,
6221 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6224 Yandroid_ne, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6228 Yandroid_neB, FALSE, TRUE,
6229 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6232 Yandroid_e, FALSE, FALSE,
6233 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6236 Yandroid_eB, FALSE, TRUE,
6237 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6240 Yandroid_se, FALSE, FALSE,
6241 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6244 Yandroid_seB, FALSE, TRUE,
6245 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6248 Yandroid_s, FALSE, FALSE,
6249 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6252 Yandroid_sB, FALSE, TRUE,
6253 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6256 Yandroid_sw, FALSE, FALSE,
6257 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6260 Yandroid_swB, FALSE, TRUE,
6261 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6264 Yandroid_w, FALSE, FALSE,
6265 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6268 Yandroid_wB, FALSE, TRUE,
6269 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6272 Yandroid_nw, FALSE, FALSE,
6273 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6276 Yandroid_nwB, FALSE, TRUE,
6277 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6281 Xeater_n, TRUE, FALSE,
6282 EL_YAMYAM_UP, -1, -1
6285 Xeater_e, TRUE, FALSE,
6286 EL_YAMYAM_RIGHT, -1, -1
6289 Xeater_w, TRUE, FALSE,
6290 EL_YAMYAM_LEFT, -1, -1
6293 Xeater_s, TRUE, FALSE,
6294 EL_YAMYAM_DOWN, -1, -1
6297 Yeater_n, FALSE, FALSE,
6298 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6301 Yeater_nB, FALSE, TRUE,
6302 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6305 Yeater_e, FALSE, FALSE,
6306 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6309 Yeater_eB, FALSE, TRUE,
6310 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6313 Yeater_s, FALSE, FALSE,
6314 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6317 Yeater_sB, FALSE, TRUE,
6318 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6321 Yeater_w, FALSE, FALSE,
6322 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6325 Yeater_wB, FALSE, TRUE,
6326 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6329 Yeater_stone, FALSE, FALSE,
6330 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6333 Yeater_spring, FALSE, FALSE,
6334 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6338 Xalien, TRUE, FALSE,
6342 Xalien_pause, FALSE, FALSE,
6346 Yalien_n, FALSE, FALSE,
6347 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6350 Yalien_nB, FALSE, TRUE,
6351 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6354 Yalien_e, FALSE, FALSE,
6355 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6358 Yalien_eB, FALSE, TRUE,
6359 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6362 Yalien_s, FALSE, FALSE,
6363 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6366 Yalien_sB, FALSE, TRUE,
6367 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6370 Yalien_w, FALSE, FALSE,
6371 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6374 Yalien_wB, FALSE, TRUE,
6375 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6378 Yalien_stone, FALSE, FALSE,
6379 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6382 Yalien_spring, FALSE, FALSE,
6383 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6387 Xbug_1_n, TRUE, FALSE,
6391 Xbug_1_e, TRUE, FALSE,
6392 EL_BUG_RIGHT, -1, -1
6395 Xbug_1_s, TRUE, FALSE,
6399 Xbug_1_w, TRUE, FALSE,
6403 Xbug_2_n, FALSE, FALSE,
6407 Xbug_2_e, FALSE, FALSE,
6408 EL_BUG_RIGHT, -1, -1
6411 Xbug_2_s, FALSE, FALSE,
6415 Xbug_2_w, FALSE, FALSE,
6419 Ybug_n, FALSE, FALSE,
6420 EL_BUG, ACTION_MOVING, MV_BIT_UP
6423 Ybug_nB, FALSE, TRUE,
6424 EL_BUG, ACTION_MOVING, MV_BIT_UP
6427 Ybug_e, FALSE, FALSE,
6428 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6431 Ybug_eB, FALSE, TRUE,
6432 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6435 Ybug_s, FALSE, FALSE,
6436 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6439 Ybug_sB, FALSE, TRUE,
6440 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6443 Ybug_w, FALSE, FALSE,
6444 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6447 Ybug_wB, FALSE, TRUE,
6448 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6451 Ybug_w_n, FALSE, FALSE,
6452 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6455 Ybug_n_e, FALSE, FALSE,
6456 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6459 Ybug_e_s, FALSE, FALSE,
6460 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6463 Ybug_s_w, FALSE, FALSE,
6464 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6467 Ybug_e_n, FALSE, FALSE,
6468 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6471 Ybug_s_e, FALSE, FALSE,
6472 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6475 Ybug_w_s, FALSE, FALSE,
6476 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6479 Ybug_n_w, FALSE, FALSE,
6480 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6483 Ybug_stone, FALSE, FALSE,
6484 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6487 Ybug_spring, FALSE, FALSE,
6488 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6492 Xtank_1_n, TRUE, FALSE,
6493 EL_SPACESHIP_UP, -1, -1
6496 Xtank_1_e, TRUE, FALSE,
6497 EL_SPACESHIP_RIGHT, -1, -1
6500 Xtank_1_s, TRUE, FALSE,
6501 EL_SPACESHIP_DOWN, -1, -1
6504 Xtank_1_w, TRUE, FALSE,
6505 EL_SPACESHIP_LEFT, -1, -1
6508 Xtank_2_n, FALSE, FALSE,
6509 EL_SPACESHIP_UP, -1, -1
6512 Xtank_2_e, FALSE, FALSE,
6513 EL_SPACESHIP_RIGHT, -1, -1
6516 Xtank_2_s, FALSE, FALSE,
6517 EL_SPACESHIP_DOWN, -1, -1
6520 Xtank_2_w, FALSE, FALSE,
6521 EL_SPACESHIP_LEFT, -1, -1
6524 Ytank_n, FALSE, FALSE,
6525 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6528 Ytank_nB, FALSE, TRUE,
6529 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6532 Ytank_e, FALSE, FALSE,
6533 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6536 Ytank_eB, FALSE, TRUE,
6537 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6540 Ytank_s, FALSE, FALSE,
6541 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6544 Ytank_sB, FALSE, TRUE,
6545 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6548 Ytank_w, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6552 Ytank_wB, FALSE, TRUE,
6553 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6556 Ytank_w_n, FALSE, FALSE,
6557 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6560 Ytank_n_e, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6564 Ytank_e_s, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6568 Ytank_s_w, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6572 Ytank_e_n, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6576 Ytank_s_e, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6580 Ytank_w_s, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6584 Ytank_n_w, FALSE, FALSE,
6585 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6588 Ytank_stone, FALSE, FALSE,
6589 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6592 Ytank_spring, FALSE, FALSE,
6593 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6597 Xemerald, TRUE, FALSE,
6601 Xemerald_pause, FALSE, FALSE,
6605 Xemerald_fall, FALSE, FALSE,
6609 Xemerald_shine, FALSE, FALSE,
6610 EL_EMERALD, ACTION_TWINKLING, -1
6613 Yemerald_s, FALSE, FALSE,
6614 EL_EMERALD, ACTION_FALLING, -1
6617 Yemerald_sB, FALSE, TRUE,
6618 EL_EMERALD, ACTION_FALLING, -1
6621 Yemerald_e, FALSE, FALSE,
6622 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6625 Yemerald_eB, FALSE, TRUE,
6626 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6629 Yemerald_w, FALSE, FALSE,
6630 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6633 Yemerald_wB, FALSE, TRUE,
6634 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6637 Yemerald_blank, FALSE, FALSE,
6638 EL_EMERALD, ACTION_COLLECTING, -1
6642 Xdiamond, TRUE, FALSE,
6646 Xdiamond_pause, FALSE, FALSE,
6650 Xdiamond_fall, FALSE, FALSE,
6654 Xdiamond_shine, FALSE, FALSE,
6655 EL_DIAMOND, ACTION_TWINKLING, -1
6658 Ydiamond_s, FALSE, FALSE,
6659 EL_DIAMOND, ACTION_FALLING, -1
6662 Ydiamond_sB, FALSE, TRUE,
6663 EL_DIAMOND, ACTION_FALLING, -1
6666 Ydiamond_e, FALSE, FALSE,
6667 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6670 Ydiamond_eB, FALSE, TRUE,
6671 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6674 Ydiamond_w, FALSE, FALSE,
6675 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6678 Ydiamond_wB, FALSE, TRUE,
6679 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6682 Ydiamond_blank, FALSE, FALSE,
6683 EL_DIAMOND, ACTION_COLLECTING, -1
6686 Ydiamond_stone, FALSE, FALSE,
6687 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6691 Xstone, TRUE, FALSE,
6695 Xstone_pause, FALSE, FALSE,
6699 Xstone_fall, FALSE, FALSE,
6703 Ystone_s, FALSE, FALSE,
6704 EL_ROCK, ACTION_FALLING, -1
6707 Ystone_sB, FALSE, TRUE,
6708 EL_ROCK, ACTION_FALLING, -1
6711 Ystone_e, FALSE, FALSE,
6712 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6715 Ystone_eB, FALSE, TRUE,
6716 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6719 Ystone_w, FALSE, FALSE,
6720 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6723 Ystone_wB, FALSE, TRUE,
6724 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6732 Xbomb_pause, FALSE, FALSE,
6736 Xbomb_fall, FALSE, FALSE,
6740 Ybomb_s, FALSE, FALSE,
6741 EL_BOMB, ACTION_FALLING, -1
6744 Ybomb_sB, FALSE, TRUE,
6745 EL_BOMB, ACTION_FALLING, -1
6748 Ybomb_e, FALSE, FALSE,
6749 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6752 Ybomb_eB, FALSE, TRUE,
6753 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6756 Ybomb_w, FALSE, FALSE,
6757 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6760 Ybomb_wB, FALSE, TRUE,
6761 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6764 Ybomb_blank, FALSE, FALSE,
6765 EL_BOMB, ACTION_ACTIVATING, -1
6773 Xnut_pause, FALSE, FALSE,
6777 Xnut_fall, FALSE, FALSE,
6781 Ynut_s, FALSE, FALSE,
6782 EL_NUT, ACTION_FALLING, -1
6785 Ynut_sB, FALSE, TRUE,
6786 EL_NUT, ACTION_FALLING, -1
6789 Ynut_e, FALSE, FALSE,
6790 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6793 Ynut_eB, FALSE, TRUE,
6794 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6797 Ynut_w, FALSE, FALSE,
6798 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6801 Ynut_wB, FALSE, TRUE,
6802 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6805 Ynut_stone, FALSE, FALSE,
6806 EL_NUT, ACTION_BREAKING, -1
6810 Xspring, TRUE, FALSE,
6814 Xspring_pause, FALSE, FALSE,
6818 Xspring_e, TRUE, FALSE,
6819 EL_SPRING_RIGHT, -1, -1
6822 Xspring_w, TRUE, FALSE,
6823 EL_SPRING_LEFT, -1, -1
6826 Xspring_fall, FALSE, FALSE,
6830 Yspring_s, FALSE, FALSE,
6831 EL_SPRING, ACTION_FALLING, -1
6834 Yspring_sB, FALSE, TRUE,
6835 EL_SPRING, ACTION_FALLING, -1
6838 Yspring_e, FALSE, FALSE,
6839 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6842 Yspring_eB, FALSE, TRUE,
6843 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6846 Yspring_w, FALSE, FALSE,
6847 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6850 Yspring_wB, FALSE, TRUE,
6851 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6854 Yspring_alien_e, FALSE, FALSE,
6855 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6858 Yspring_alien_eB, FALSE, TRUE,
6859 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6862 Yspring_alien_w, FALSE, FALSE,
6863 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6866 Yspring_alien_wB, FALSE, TRUE,
6867 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6871 Xpush_emerald_e, FALSE, FALSE,
6872 EL_EMERALD, -1, MV_BIT_RIGHT
6875 Xpush_emerald_w, FALSE, FALSE,
6876 EL_EMERALD, -1, MV_BIT_LEFT
6879 Xpush_diamond_e, FALSE, FALSE,
6880 EL_DIAMOND, -1, MV_BIT_RIGHT
6883 Xpush_diamond_w, FALSE, FALSE,
6884 EL_DIAMOND, -1, MV_BIT_LEFT
6887 Xpush_stone_e, FALSE, FALSE,
6888 EL_ROCK, -1, MV_BIT_RIGHT
6891 Xpush_stone_w, FALSE, FALSE,
6892 EL_ROCK, -1, MV_BIT_LEFT
6895 Xpush_bomb_e, FALSE, FALSE,
6896 EL_BOMB, -1, MV_BIT_RIGHT
6899 Xpush_bomb_w, FALSE, FALSE,
6900 EL_BOMB, -1, MV_BIT_LEFT
6903 Xpush_nut_e, FALSE, FALSE,
6904 EL_NUT, -1, MV_BIT_RIGHT
6907 Xpush_nut_w, FALSE, FALSE,
6908 EL_NUT, -1, MV_BIT_LEFT
6911 Xpush_spring_e, FALSE, FALSE,
6912 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6915 Xpush_spring_w, FALSE, FALSE,
6916 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6920 Xdynamite, TRUE, FALSE,
6921 EL_EM_DYNAMITE, -1, -1
6924 Ydynamite_blank, FALSE, FALSE,
6925 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6928 Xdynamite_1, TRUE, FALSE,
6929 EL_EM_DYNAMITE_ACTIVE, -1, -1
6932 Xdynamite_2, FALSE, FALSE,
6933 EL_EM_DYNAMITE_ACTIVE, -1, -1
6936 Xdynamite_3, FALSE, FALSE,
6937 EL_EM_DYNAMITE_ACTIVE, -1, -1
6940 Xdynamite_4, FALSE, FALSE,
6941 EL_EM_DYNAMITE_ACTIVE, -1, -1
6945 Xkey_1, TRUE, FALSE,
6949 Xkey_2, TRUE, FALSE,
6953 Xkey_3, TRUE, FALSE,
6957 Xkey_4, TRUE, FALSE,
6961 Xkey_5, TRUE, FALSE,
6962 EL_EMC_KEY_5, -1, -1
6965 Xkey_6, TRUE, FALSE,
6966 EL_EMC_KEY_6, -1, -1
6969 Xkey_7, TRUE, FALSE,
6970 EL_EMC_KEY_7, -1, -1
6973 Xkey_8, TRUE, FALSE,
6974 EL_EMC_KEY_8, -1, -1
6978 Xdoor_1, TRUE, FALSE,
6979 EL_EM_GATE_1, -1, -1
6982 Xdoor_2, TRUE, FALSE,
6983 EL_EM_GATE_2, -1, -1
6986 Xdoor_3, TRUE, FALSE,
6987 EL_EM_GATE_3, -1, -1
6990 Xdoor_4, TRUE, FALSE,
6991 EL_EM_GATE_4, -1, -1
6994 Xdoor_5, TRUE, FALSE,
6995 EL_EMC_GATE_5, -1, -1
6998 Xdoor_6, TRUE, FALSE,
6999 EL_EMC_GATE_6, -1, -1
7002 Xdoor_7, TRUE, FALSE,
7003 EL_EMC_GATE_7, -1, -1
7006 Xdoor_8, TRUE, FALSE,
7007 EL_EMC_GATE_8, -1, -1
7011 Xfake_door_1, TRUE, FALSE,
7012 EL_EM_GATE_1_GRAY, -1, -1
7015 Xfake_door_2, TRUE, FALSE,
7016 EL_EM_GATE_2_GRAY, -1, -1
7019 Xfake_door_3, TRUE, FALSE,
7020 EL_EM_GATE_3_GRAY, -1, -1
7023 Xfake_door_4, TRUE, FALSE,
7024 EL_EM_GATE_4_GRAY, -1, -1
7027 Xfake_door_5, TRUE, FALSE,
7028 EL_EMC_GATE_5_GRAY, -1, -1
7031 Xfake_door_6, TRUE, FALSE,
7032 EL_EMC_GATE_6_GRAY, -1, -1
7035 Xfake_door_7, TRUE, FALSE,
7036 EL_EMC_GATE_7_GRAY, -1, -1
7039 Xfake_door_8, TRUE, FALSE,
7040 EL_EMC_GATE_8_GRAY, -1, -1
7044 Xballoon, TRUE, FALSE,
7048 Yballoon_n, FALSE, FALSE,
7049 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7052 Yballoon_nB, FALSE, TRUE,
7053 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7056 Yballoon_e, FALSE, FALSE,
7057 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7060 Yballoon_eB, FALSE, TRUE,
7061 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7064 Yballoon_s, FALSE, FALSE,
7065 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7068 Yballoon_sB, FALSE, TRUE,
7069 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7072 Yballoon_w, FALSE, FALSE,
7073 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7076 Yballoon_wB, FALSE, TRUE,
7077 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7081 Xball_1, TRUE, FALSE,
7082 EL_EMC_MAGIC_BALL, -1, -1
7085 Yball_1, FALSE, FALSE,
7086 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7089 Xball_2, FALSE, FALSE,
7090 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7093 Yball_2, FALSE, FALSE,
7094 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7097 Yball_blank, FALSE, FALSE,
7098 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7102 Xamoeba_1, TRUE, FALSE,
7103 EL_AMOEBA_DRY, ACTION_OTHER, -1
7106 Xamoeba_2, FALSE, FALSE,
7107 EL_AMOEBA_DRY, ACTION_OTHER, -1
7110 Xamoeba_3, FALSE, FALSE,
7111 EL_AMOEBA_DRY, ACTION_OTHER, -1
7114 Xamoeba_4, FALSE, FALSE,
7115 EL_AMOEBA_DRY, ACTION_OTHER, -1
7118 Xamoeba_5, TRUE, FALSE,
7119 EL_AMOEBA_WET, ACTION_OTHER, -1
7122 Xamoeba_6, FALSE, FALSE,
7123 EL_AMOEBA_WET, ACTION_OTHER, -1
7126 Xamoeba_7, FALSE, FALSE,
7127 EL_AMOEBA_WET, ACTION_OTHER, -1
7130 Xamoeba_8, FALSE, FALSE,
7131 EL_AMOEBA_WET, ACTION_OTHER, -1
7136 EL_AMOEBA_DROP, ACTION_GROWING, -1
7139 Xdrip_fall, FALSE, FALSE,
7140 EL_AMOEBA_DROP, -1, -1
7143 Xdrip_stretch, FALSE, FALSE,
7144 EL_AMOEBA_DROP, ACTION_FALLING, -1
7147 Xdrip_stretchB, FALSE, TRUE,
7148 EL_AMOEBA_DROP, ACTION_FALLING, -1
7151 Ydrip_1_s, FALSE, FALSE,
7152 EL_AMOEBA_DROP, ACTION_FALLING, -1
7155 Ydrip_1_sB, FALSE, TRUE,
7156 EL_AMOEBA_DROP, ACTION_FALLING, -1
7159 Ydrip_2_s, FALSE, FALSE,
7160 EL_AMOEBA_DROP, ACTION_FALLING, -1
7163 Ydrip_2_sB, FALSE, TRUE,
7164 EL_AMOEBA_DROP, ACTION_FALLING, -1
7168 Xwonderwall, TRUE, FALSE,
7169 EL_MAGIC_WALL, -1, -1
7172 Ywonderwall, FALSE, FALSE,
7173 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7177 Xwheel, TRUE, FALSE,
7178 EL_ROBOT_WHEEL, -1, -1
7181 Ywheel, FALSE, FALSE,
7182 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7186 Xswitch, TRUE, FALSE,
7187 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7190 Yswitch, FALSE, FALSE,
7191 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7195 Xbumper, TRUE, FALSE,
7196 EL_EMC_SPRING_BUMPER, -1, -1
7199 Ybumper, FALSE, FALSE,
7200 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7204 Xacid_nw, TRUE, FALSE,
7205 EL_ACID_POOL_TOPLEFT, -1, -1
7208 Xacid_ne, TRUE, FALSE,
7209 EL_ACID_POOL_TOPRIGHT, -1, -1
7212 Xacid_sw, TRUE, FALSE,
7213 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7216 Xacid_s, TRUE, FALSE,
7217 EL_ACID_POOL_BOTTOM, -1, -1
7220 Xacid_se, TRUE, FALSE,
7221 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7225 Xfake_blank, TRUE, FALSE,
7226 EL_INVISIBLE_WALL, -1, -1
7229 Yfake_blank, FALSE, FALSE,
7230 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7234 Xfake_grass, TRUE, FALSE,
7235 EL_EMC_FAKE_GRASS, -1, -1
7238 Yfake_grass, FALSE, FALSE,
7239 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7243 Xfake_amoeba, TRUE, FALSE,
7244 EL_EMC_DRIPPER, -1, -1
7247 Yfake_amoeba, FALSE, FALSE,
7248 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7252 Xlenses, TRUE, FALSE,
7253 EL_EMC_LENSES, -1, -1
7257 Xmagnify, TRUE, FALSE,
7258 EL_EMC_MAGNIFIER, -1, -1
7263 EL_QUICKSAND_EMPTY, -1, -1
7266 Xsand_stone, TRUE, FALSE,
7267 EL_QUICKSAND_FULL, -1, -1
7270 Xsand_stonein_1, FALSE, TRUE,
7271 EL_ROCK, ACTION_FILLING, -1
7274 Xsand_stonein_2, FALSE, TRUE,
7275 EL_ROCK, ACTION_FILLING, -1
7278 Xsand_stonein_3, FALSE, TRUE,
7279 EL_ROCK, ACTION_FILLING, -1
7282 Xsand_stonein_4, FALSE, TRUE,
7283 EL_ROCK, ACTION_FILLING, -1
7286 Xsand_sandstone_1, FALSE, FALSE,
7287 EL_QUICKSAND_FILLING, -1, -1
7290 Xsand_sandstone_2, FALSE, FALSE,
7291 EL_QUICKSAND_FILLING, -1, -1
7294 Xsand_sandstone_3, FALSE, FALSE,
7295 EL_QUICKSAND_FILLING, -1, -1
7298 Xsand_sandstone_4, FALSE, FALSE,
7299 EL_QUICKSAND_FILLING, -1, -1
7302 Xsand_stonesand_1, FALSE, FALSE,
7303 EL_QUICKSAND_EMPTYING, -1, -1
7306 Xsand_stonesand_2, FALSE, FALSE,
7307 EL_QUICKSAND_EMPTYING, -1, -1
7310 Xsand_stonesand_3, FALSE, FALSE,
7311 EL_QUICKSAND_EMPTYING, -1, -1
7314 Xsand_stonesand_4, FALSE, FALSE,
7315 EL_QUICKSAND_EMPTYING, -1, -1
7318 Xsand_stoneout_1, FALSE, FALSE,
7319 EL_ROCK, ACTION_EMPTYING, -1
7322 Xsand_stoneout_2, FALSE, FALSE,
7323 EL_ROCK, ACTION_EMPTYING, -1
7326 Xsand_stonesand_quickout_1, FALSE, FALSE,
7327 EL_QUICKSAND_EMPTYING, -1, -1
7330 Xsand_stonesand_quickout_2, FALSE, FALSE,
7331 EL_QUICKSAND_EMPTYING, -1, -1
7335 Xslide_ns, TRUE, FALSE,
7336 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7339 Yslide_ns_blank, FALSE, FALSE,
7340 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7343 Xslide_ew, TRUE, FALSE,
7344 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7347 Yslide_ew_blank, FALSE, FALSE,
7348 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7352 Xwind_n, TRUE, FALSE,
7353 EL_BALLOON_SWITCH_UP, -1, -1
7356 Xwind_e, TRUE, FALSE,
7357 EL_BALLOON_SWITCH_RIGHT, -1, -1
7360 Xwind_s, TRUE, FALSE,
7361 EL_BALLOON_SWITCH_DOWN, -1, -1
7364 Xwind_w, TRUE, FALSE,
7365 EL_BALLOON_SWITCH_LEFT, -1, -1
7368 Xwind_any, TRUE, FALSE,
7369 EL_BALLOON_SWITCH_ANY, -1, -1
7372 Xwind_stop, TRUE, FALSE,
7373 EL_BALLOON_SWITCH_NONE, -1, -1
7378 EL_EM_EXIT_CLOSED, -1, -1
7381 Xexit_1, TRUE, FALSE,
7382 EL_EM_EXIT_OPEN, -1, -1
7385 Xexit_2, FALSE, FALSE,
7386 EL_EM_EXIT_OPEN, -1, -1
7389 Xexit_3, FALSE, FALSE,
7390 EL_EM_EXIT_OPEN, -1, -1
7394 Xpause, FALSE, FALSE,
7399 Xwall_1, TRUE, FALSE,
7403 Xwall_2, TRUE, FALSE,
7404 EL_EMC_WALL_14, -1, -1
7407 Xwall_3, TRUE, FALSE,
7408 EL_EMC_WALL_15, -1, -1
7411 Xwall_4, TRUE, FALSE,
7412 EL_EMC_WALL_16, -1, -1
7416 Xroundwall_1, TRUE, FALSE,
7417 EL_WALL_SLIPPERY, -1, -1
7420 Xroundwall_2, TRUE, FALSE,
7421 EL_EMC_WALL_SLIPPERY_2, -1, -1
7424 Xroundwall_3, TRUE, FALSE,
7425 EL_EMC_WALL_SLIPPERY_3, -1, -1
7428 Xroundwall_4, TRUE, FALSE,
7429 EL_EMC_WALL_SLIPPERY_4, -1, -1
7433 Xsteel_1, TRUE, FALSE,
7434 EL_STEELWALL, -1, -1
7437 Xsteel_2, TRUE, FALSE,
7438 EL_EMC_STEELWALL_2, -1, -1
7441 Xsteel_3, TRUE, FALSE,
7442 EL_EMC_STEELWALL_3, -1, -1
7445 Xsteel_4, TRUE, FALSE,
7446 EL_EMC_STEELWALL_4, -1, -1
7450 Xdecor_1, TRUE, FALSE,
7451 EL_EMC_WALL_8, -1, -1
7454 Xdecor_2, TRUE, FALSE,
7455 EL_EMC_WALL_6, -1, -1
7458 Xdecor_3, TRUE, FALSE,
7459 EL_EMC_WALL_4, -1, -1
7462 Xdecor_4, TRUE, FALSE,
7463 EL_EMC_WALL_7, -1, -1
7466 Xdecor_5, TRUE, FALSE,
7467 EL_EMC_WALL_5, -1, -1
7470 Xdecor_6, TRUE, FALSE,
7471 EL_EMC_WALL_9, -1, -1
7474 Xdecor_7, TRUE, FALSE,
7475 EL_EMC_WALL_10, -1, -1
7478 Xdecor_8, TRUE, FALSE,
7479 EL_EMC_WALL_1, -1, -1
7482 Xdecor_9, TRUE, FALSE,
7483 EL_EMC_WALL_2, -1, -1
7486 Xdecor_10, TRUE, FALSE,
7487 EL_EMC_WALL_3, -1, -1
7490 Xdecor_11, TRUE, FALSE,
7491 EL_EMC_WALL_11, -1, -1
7494 Xdecor_12, TRUE, FALSE,
7495 EL_EMC_WALL_12, -1, -1
7499 Xalpha_0, TRUE, FALSE,
7500 EL_CHAR('0'), -1, -1
7503 Xalpha_1, TRUE, FALSE,
7504 EL_CHAR('1'), -1, -1
7507 Xalpha_2, TRUE, FALSE,
7508 EL_CHAR('2'), -1, -1
7511 Xalpha_3, TRUE, FALSE,
7512 EL_CHAR('3'), -1, -1
7515 Xalpha_4, TRUE, FALSE,
7516 EL_CHAR('4'), -1, -1
7519 Xalpha_5, TRUE, FALSE,
7520 EL_CHAR('5'), -1, -1
7523 Xalpha_6, TRUE, FALSE,
7524 EL_CHAR('6'), -1, -1
7527 Xalpha_7, TRUE, FALSE,
7528 EL_CHAR('7'), -1, -1
7531 Xalpha_8, TRUE, FALSE,
7532 EL_CHAR('8'), -1, -1
7535 Xalpha_9, TRUE, FALSE,
7536 EL_CHAR('9'), -1, -1
7539 Xalpha_excla, TRUE, FALSE,
7540 EL_CHAR('!'), -1, -1
7543 Xalpha_apost, TRUE, FALSE,
7544 EL_CHAR('\''), -1, -1
7547 Xalpha_comma, TRUE, FALSE,
7548 EL_CHAR(','), -1, -1
7551 Xalpha_minus, TRUE, FALSE,
7552 EL_CHAR('-'), -1, -1
7555 Xalpha_perio, TRUE, FALSE,
7556 EL_CHAR('.'), -1, -1
7559 Xalpha_colon, TRUE, FALSE,
7560 EL_CHAR(':'), -1, -1
7563 Xalpha_quest, TRUE, FALSE,
7564 EL_CHAR('?'), -1, -1
7567 Xalpha_a, TRUE, FALSE,
7568 EL_CHAR('A'), -1, -1
7571 Xalpha_b, TRUE, FALSE,
7572 EL_CHAR('B'), -1, -1
7575 Xalpha_c, TRUE, FALSE,
7576 EL_CHAR('C'), -1, -1
7579 Xalpha_d, TRUE, FALSE,
7580 EL_CHAR('D'), -1, -1
7583 Xalpha_e, TRUE, FALSE,
7584 EL_CHAR('E'), -1, -1
7587 Xalpha_f, TRUE, FALSE,
7588 EL_CHAR('F'), -1, -1
7591 Xalpha_g, TRUE, FALSE,
7592 EL_CHAR('G'), -1, -1
7595 Xalpha_h, TRUE, FALSE,
7596 EL_CHAR('H'), -1, -1
7599 Xalpha_i, TRUE, FALSE,
7600 EL_CHAR('I'), -1, -1
7603 Xalpha_j, TRUE, FALSE,
7604 EL_CHAR('J'), -1, -1
7607 Xalpha_k, TRUE, FALSE,
7608 EL_CHAR('K'), -1, -1
7611 Xalpha_l, TRUE, FALSE,
7612 EL_CHAR('L'), -1, -1
7615 Xalpha_m, TRUE, FALSE,
7616 EL_CHAR('M'), -1, -1
7619 Xalpha_n, TRUE, FALSE,
7620 EL_CHAR('N'), -1, -1
7623 Xalpha_o, TRUE, FALSE,
7624 EL_CHAR('O'), -1, -1
7627 Xalpha_p, TRUE, FALSE,
7628 EL_CHAR('P'), -1, -1
7631 Xalpha_q, TRUE, FALSE,
7632 EL_CHAR('Q'), -1, -1
7635 Xalpha_r, TRUE, FALSE,
7636 EL_CHAR('R'), -1, -1
7639 Xalpha_s, TRUE, FALSE,
7640 EL_CHAR('S'), -1, -1
7643 Xalpha_t, TRUE, FALSE,
7644 EL_CHAR('T'), -1, -1
7647 Xalpha_u, TRUE, FALSE,
7648 EL_CHAR('U'), -1, -1
7651 Xalpha_v, TRUE, FALSE,
7652 EL_CHAR('V'), -1, -1
7655 Xalpha_w, TRUE, FALSE,
7656 EL_CHAR('W'), -1, -1
7659 Xalpha_x, TRUE, FALSE,
7660 EL_CHAR('X'), -1, -1
7663 Xalpha_y, TRUE, FALSE,
7664 EL_CHAR('Y'), -1, -1
7667 Xalpha_z, TRUE, FALSE,
7668 EL_CHAR('Z'), -1, -1
7671 Xalpha_arrow_e, TRUE, FALSE,
7672 EL_CHAR('>'), -1, -1
7675 Xalpha_arrow_w, TRUE, FALSE,
7676 EL_CHAR('<'), -1, -1
7679 Xalpha_copyr, TRUE, FALSE,
7680 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7684 Ykey_1_blank, FALSE, FALSE,
7685 EL_EM_KEY_1, ACTION_COLLECTING, -1
7688 Ykey_2_blank, FALSE, FALSE,
7689 EL_EM_KEY_2, ACTION_COLLECTING, -1
7692 Ykey_3_blank, FALSE, FALSE,
7693 EL_EM_KEY_3, ACTION_COLLECTING, -1
7696 Ykey_4_blank, FALSE, FALSE,
7697 EL_EM_KEY_4, ACTION_COLLECTING, -1
7700 Ykey_5_blank, FALSE, FALSE,
7701 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7704 Ykey_6_blank, FALSE, FALSE,
7705 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7708 Ykey_7_blank, FALSE, FALSE,
7709 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7712 Ykey_8_blank, FALSE, FALSE,
7713 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7716 Ylenses_blank, FALSE, FALSE,
7717 EL_EMC_LENSES, ACTION_COLLECTING, -1
7720 Ymagnify_blank, FALSE, FALSE,
7721 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7724 Ygrass_blank, FALSE, FALSE,
7725 EL_EMC_GRASS, ACTION_SNAPPING, -1
7728 Ydirt_blank, FALSE, FALSE,
7729 EL_SAND, ACTION_SNAPPING, -1
7738 static struct Mapping_EM_to_RND_player
7747 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7751 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7755 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7759 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7763 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7767 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7771 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7775 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7779 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7783 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7787 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7791 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7795 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7799 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7803 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7807 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7811 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7815 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7819 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7823 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7827 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7831 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7835 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7839 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7843 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7847 EL_PLAYER_1, ACTION_DEFAULT, -1,
7851 EL_PLAYER_2, ACTION_DEFAULT, -1,
7855 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7859 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7863 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7867 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7871 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7875 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7879 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7883 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7887 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7891 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7895 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7899 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7903 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7907 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7911 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7915 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7919 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7923 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7927 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7931 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7935 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7939 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7943 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7947 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7951 EL_PLAYER_3, ACTION_DEFAULT, -1,
7955 EL_PLAYER_4, ACTION_DEFAULT, -1,
7964 int map_element_RND_to_EM_cave(int element_rnd)
7966 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7967 static boolean mapping_initialized = FALSE;
7969 if (!mapping_initialized)
7973 // return "Xalpha_quest" for all undefined elements in mapping array
7974 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7975 mapping_RND_to_EM[i] = Xalpha_quest;
7977 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7978 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7979 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7980 em_object_mapping_list[i].element_em;
7982 mapping_initialized = TRUE;
7985 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7987 Warn("invalid RND level element %d", element_rnd);
7992 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7995 int map_element_EM_to_RND_cave(int element_em_cave)
7997 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7998 static boolean mapping_initialized = FALSE;
8000 if (!mapping_initialized)
8004 // return "EL_UNKNOWN" for all undefined elements in mapping array
8005 for (i = 0; i < GAME_TILE_MAX; i++)
8006 mapping_EM_to_RND[i] = EL_UNKNOWN;
8008 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8009 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8010 em_object_mapping_list[i].element_rnd;
8012 mapping_initialized = TRUE;
8015 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8017 Warn("invalid EM cave element %d", element_em_cave);
8022 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8025 int map_element_EM_to_RND_game(int element_em_game)
8027 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8028 static boolean mapping_initialized = FALSE;
8030 if (!mapping_initialized)
8034 // return "EL_UNKNOWN" for all undefined elements in mapping array
8035 for (i = 0; i < GAME_TILE_MAX; i++)
8036 mapping_EM_to_RND[i] = EL_UNKNOWN;
8038 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8039 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8040 em_object_mapping_list[i].element_rnd;
8042 mapping_initialized = TRUE;
8045 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8047 Warn("invalid EM game element %d", element_em_game);
8052 return mapping_EM_to_RND[element_em_game];
8055 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8057 struct LevelInfo_EM *level_em = level->native_em_level;
8058 struct CAVE *cav = level_em->cav;
8061 for (i = 0; i < GAME_TILE_MAX; i++)
8062 cav->android_array[i] = Cblank;
8064 for (i = 0; i < level->num_android_clone_elements; i++)
8066 int element_rnd = level->android_clone_element[i];
8067 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8069 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8070 if (em_object_mapping_list[j].element_rnd == element_rnd)
8071 cav->android_array[em_object_mapping_list[j].element_em] =
8076 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8078 struct LevelInfo_EM *level_em = level->native_em_level;
8079 struct CAVE *cav = level_em->cav;
8082 level->num_android_clone_elements = 0;
8084 for (i = 0; i < GAME_TILE_MAX; i++)
8086 int element_em_cave = cav->android_array[i];
8088 boolean element_found = FALSE;
8090 if (element_em_cave == Cblank)
8093 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8095 for (j = 0; j < level->num_android_clone_elements; j++)
8096 if (level->android_clone_element[j] == element_rnd)
8097 element_found = TRUE;
8101 level->android_clone_element[level->num_android_clone_elements++] =
8104 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8109 if (level->num_android_clone_elements == 0)
8111 level->num_android_clone_elements = 1;
8112 level->android_clone_element[0] = EL_EMPTY;
8116 int map_direction_RND_to_EM(int direction)
8118 return (direction == MV_UP ? 0 :
8119 direction == MV_RIGHT ? 1 :
8120 direction == MV_DOWN ? 2 :
8121 direction == MV_LEFT ? 3 :
8125 int map_direction_EM_to_RND(int direction)
8127 return (direction == 0 ? MV_UP :
8128 direction == 1 ? MV_RIGHT :
8129 direction == 2 ? MV_DOWN :
8130 direction == 3 ? MV_LEFT :
8134 int map_element_RND_to_SP(int element_rnd)
8136 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8138 if (element_rnd >= EL_SP_START &&
8139 element_rnd <= EL_SP_END)
8140 element_sp = element_rnd - EL_SP_START;
8141 else if (element_rnd == EL_EMPTY_SPACE)
8143 else if (element_rnd == EL_INVISIBLE_WALL)
8149 int map_element_SP_to_RND(int element_sp)
8151 int element_rnd = EL_UNKNOWN;
8153 if (element_sp >= 0x00 &&
8155 element_rnd = EL_SP_START + element_sp;
8156 else if (element_sp == 0x28)
8157 element_rnd = EL_INVISIBLE_WALL;
8162 int map_action_SP_to_RND(int action_sp)
8166 case actActive: return ACTION_ACTIVE;
8167 case actImpact: return ACTION_IMPACT;
8168 case actExploding: return ACTION_EXPLODING;
8169 case actDigging: return ACTION_DIGGING;
8170 case actSnapping: return ACTION_SNAPPING;
8171 case actCollecting: return ACTION_COLLECTING;
8172 case actPassing: return ACTION_PASSING;
8173 case actPushing: return ACTION_PUSHING;
8174 case actDropping: return ACTION_DROPPING;
8176 default: return ACTION_DEFAULT;
8180 int map_element_RND_to_MM(int element_rnd)
8182 return (element_rnd >= EL_MM_START_1 &&
8183 element_rnd <= EL_MM_END_1 ?
8184 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8186 element_rnd >= EL_MM_START_2 &&
8187 element_rnd <= EL_MM_END_2 ?
8188 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8190 element_rnd >= EL_CHAR_START &&
8191 element_rnd <= EL_CHAR_END ?
8192 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8194 element_rnd >= EL_MM_RUNTIME_START &&
8195 element_rnd <= EL_MM_RUNTIME_END ?
8196 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8198 element_rnd >= EL_MM_DUMMY_START &&
8199 element_rnd <= EL_MM_DUMMY_END ?
8200 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8202 EL_MM_EMPTY_NATIVE);
8205 int map_element_MM_to_RND(int element_mm)
8207 return (element_mm == EL_MM_EMPTY_NATIVE ||
8208 element_mm == EL_DF_EMPTY_NATIVE ?
8211 element_mm >= EL_MM_START_1_NATIVE &&
8212 element_mm <= EL_MM_END_1_NATIVE ?
8213 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8215 element_mm >= EL_MM_START_2_NATIVE &&
8216 element_mm <= EL_MM_END_2_NATIVE ?
8217 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8219 element_mm >= EL_MM_CHAR_START_NATIVE &&
8220 element_mm <= EL_MM_CHAR_END_NATIVE ?
8221 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8223 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8224 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8225 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8227 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8228 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8229 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8234 int map_action_MM_to_RND(int action_mm)
8236 // all MM actions are defined to exactly match their RND counterparts
8240 int map_sound_MM_to_RND(int sound_mm)
8244 case SND_MM_GAME_LEVELTIME_CHARGING:
8245 return SND_GAME_LEVELTIME_CHARGING;
8247 case SND_MM_GAME_HEALTH_CHARGING:
8248 return SND_GAME_HEALTH_CHARGING;
8251 return SND_UNDEFINED;
8255 int map_mm_wall_element(int element)
8257 return (element >= EL_MM_STEEL_WALL_START &&
8258 element <= EL_MM_STEEL_WALL_END ?
8261 element >= EL_MM_WOODEN_WALL_START &&
8262 element <= EL_MM_WOODEN_WALL_END ?
8265 element >= EL_MM_ICE_WALL_START &&
8266 element <= EL_MM_ICE_WALL_END ?
8269 element >= EL_MM_AMOEBA_WALL_START &&
8270 element <= EL_MM_AMOEBA_WALL_END ?
8273 element >= EL_DF_STEEL_WALL_START &&
8274 element <= EL_DF_STEEL_WALL_END ?
8277 element >= EL_DF_WOODEN_WALL_START &&
8278 element <= EL_DF_WOODEN_WALL_END ?
8284 int map_mm_wall_element_editor(int element)
8288 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8289 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8290 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8291 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8292 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8293 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8295 default: return element;
8299 int get_next_element(int element)
8303 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8304 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8305 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8306 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8307 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8308 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8309 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8310 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8311 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8312 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8313 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8315 default: return element;
8319 int el2img_mm(int element_mm)
8321 return el2img(map_element_MM_to_RND(element_mm));
8324 int el_act_dir2img(int element, int action, int direction)
8326 element = GFX_ELEMENT(element);
8327 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8329 // direction_graphic[][] == graphic[] for undefined direction graphics
8330 return element_info[element].direction_graphic[action][direction];
8333 static int el_act_dir2crm(int element, int action, int direction)
8335 element = GFX_ELEMENT(element);
8336 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8338 // direction_graphic[][] == graphic[] for undefined direction graphics
8339 return element_info[element].direction_crumbled[action][direction];
8342 int el_act2img(int element, int action)
8344 element = GFX_ELEMENT(element);
8346 return element_info[element].graphic[action];
8349 int el_act2crm(int element, int action)
8351 element = GFX_ELEMENT(element);
8353 return element_info[element].crumbled[action];
8356 int el_dir2img(int element, int direction)
8358 element = GFX_ELEMENT(element);
8360 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8363 int el2baseimg(int element)
8365 return element_info[element].graphic[ACTION_DEFAULT];
8368 int el2img(int element)
8370 element = GFX_ELEMENT(element);
8372 return element_info[element].graphic[ACTION_DEFAULT];
8375 int el2edimg(int element)
8377 element = GFX_ELEMENT(element);
8379 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8382 int el2preimg(int element)
8384 element = GFX_ELEMENT(element);
8386 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8389 int el2panelimg(int element)
8391 element = GFX_ELEMENT(element);
8393 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8396 int font2baseimg(int font_nr)
8398 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8401 int getBeltNrFromBeltElement(int element)
8403 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8404 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8405 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8408 int getBeltNrFromBeltActiveElement(int element)
8410 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8411 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8412 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8415 int getBeltNrFromBeltSwitchElement(int element)
8417 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8418 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8419 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8422 int getBeltDirNrFromBeltElement(int element)
8424 static int belt_base_element[4] =
8426 EL_CONVEYOR_BELT_1_LEFT,
8427 EL_CONVEYOR_BELT_2_LEFT,
8428 EL_CONVEYOR_BELT_3_LEFT,
8429 EL_CONVEYOR_BELT_4_LEFT
8432 int belt_nr = getBeltNrFromBeltElement(element);
8433 int belt_dir_nr = element - belt_base_element[belt_nr];
8435 return (belt_dir_nr % 3);
8438 int getBeltDirNrFromBeltSwitchElement(int element)
8440 static int belt_base_element[4] =
8442 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8443 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8444 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8445 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8448 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8449 int belt_dir_nr = element - belt_base_element[belt_nr];
8451 return (belt_dir_nr % 3);
8454 int getBeltDirFromBeltElement(int element)
8456 static int belt_move_dir[3] =
8463 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8465 return belt_move_dir[belt_dir_nr];
8468 int getBeltDirFromBeltSwitchElement(int element)
8470 static int belt_move_dir[3] =
8477 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8479 return belt_move_dir[belt_dir_nr];
8482 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8484 static int belt_base_element[4] =
8486 EL_CONVEYOR_BELT_1_LEFT,
8487 EL_CONVEYOR_BELT_2_LEFT,
8488 EL_CONVEYOR_BELT_3_LEFT,
8489 EL_CONVEYOR_BELT_4_LEFT
8492 return belt_base_element[belt_nr] + belt_dir_nr;
8495 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8497 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8499 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8502 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8504 static int belt_base_element[4] =
8506 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8507 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8508 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8509 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8512 return belt_base_element[belt_nr] + belt_dir_nr;
8515 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8517 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8519 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8522 boolean swapTiles_EM(boolean is_pre_emc_cave)
8524 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8527 boolean getTeamMode_EM(void)
8529 return game.team_mode || network_playing;
8532 boolean isActivePlayer_EM(int player_nr)
8534 return stored_player[player_nr].active;
8537 unsigned int InitRND(int seed)
8539 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8540 return InitEngineRandom_EM(seed);
8541 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8542 return InitEngineRandom_SP(seed);
8543 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8544 return InitEngineRandom_MM(seed);
8546 return InitEngineRandom_RND(seed);
8549 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8550 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8552 static int get_effective_element_EM(int tile, int frame_em)
8554 int element = object_mapping[tile].element_rnd;
8555 int action = object_mapping[tile].action;
8556 boolean is_backside = object_mapping[tile].is_backside;
8557 boolean action_removing = (action == ACTION_DIGGING ||
8558 action == ACTION_SNAPPING ||
8559 action == ACTION_COLLECTING);
8567 return (frame_em > 5 ? EL_EMPTY : element);
8573 else // frame_em == 7
8584 case Ydiamond_stone:
8588 case Xdrip_stretchB:
8604 case Ymagnify_blank:
8607 case Xsand_stonein_1:
8608 case Xsand_stonein_2:
8609 case Xsand_stonein_3:
8610 case Xsand_stonein_4:
8614 return (is_backside || action_removing ? EL_EMPTY : element);
8619 static boolean check_linear_animation_EM(int tile)
8623 case Xsand_stonesand_1:
8624 case Xsand_stonesand_quickout_1:
8625 case Xsand_sandstone_1:
8626 case Xsand_stonein_1:
8627 case Xsand_stoneout_1:
8655 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8656 boolean has_crumbled_graphics,
8657 int crumbled, int sync_frame)
8659 // if element can be crumbled, but certain action graphics are just empty
8660 // space (like instantly snapping sand to empty space in 1 frame), do not
8661 // treat these empty space graphics as crumbled graphics in EMC engine
8662 if (crumbled == IMG_EMPTY_SPACE)
8663 has_crumbled_graphics = FALSE;
8665 if (has_crumbled_graphics)
8667 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8668 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8669 g_crumbled->anim_delay,
8670 g_crumbled->anim_mode,
8671 g_crumbled->anim_start_frame,
8674 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8675 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8677 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8678 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8680 g_em->has_crumbled_graphics = TRUE;
8684 g_em->crumbled_bitmap = NULL;
8685 g_em->crumbled_src_x = 0;
8686 g_em->crumbled_src_y = 0;
8687 g_em->crumbled_border_size = 0;
8688 g_em->crumbled_tile_size = 0;
8690 g_em->has_crumbled_graphics = FALSE;
8695 void ResetGfxAnimation_EM(int x, int y, int tile)
8701 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8702 int tile, int frame_em, int x, int y)
8704 int action = object_mapping[tile].action;
8705 int direction = object_mapping[tile].direction;
8706 int effective_element = get_effective_element_EM(tile, frame_em);
8707 int graphic = (direction == MV_NONE ?
8708 el_act2img(effective_element, action) :
8709 el_act_dir2img(effective_element, action, direction));
8710 struct GraphicInfo *g = &graphic_info[graphic];
8712 boolean action_removing = (action == ACTION_DIGGING ||
8713 action == ACTION_SNAPPING ||
8714 action == ACTION_COLLECTING);
8715 boolean action_moving = (action == ACTION_FALLING ||
8716 action == ACTION_MOVING ||
8717 action == ACTION_PUSHING ||
8718 action == ACTION_EATING ||
8719 action == ACTION_FILLING ||
8720 action == ACTION_EMPTYING);
8721 boolean action_falling = (action == ACTION_FALLING ||
8722 action == ACTION_FILLING ||
8723 action == ACTION_EMPTYING);
8725 // special case: graphic uses "2nd movement tile" and has defined
8726 // 7 frames for movement animation (or less) => use default graphic
8727 // for last (8th) frame which ends the movement animation
8728 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8730 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8731 graphic = (direction == MV_NONE ?
8732 el_act2img(effective_element, action) :
8733 el_act_dir2img(effective_element, action, direction));
8735 g = &graphic_info[graphic];
8738 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8742 else if (action_moving)
8744 boolean is_backside = object_mapping[tile].is_backside;
8748 int direction = object_mapping[tile].direction;
8749 int move_dir = (action_falling ? MV_DOWN : direction);
8754 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8755 if (g->double_movement && frame_em == 0)
8759 if (move_dir == MV_LEFT)
8760 GfxFrame[x - 1][y] = GfxFrame[x][y];
8761 else if (move_dir == MV_RIGHT)
8762 GfxFrame[x + 1][y] = GfxFrame[x][y];
8763 else if (move_dir == MV_UP)
8764 GfxFrame[x][y - 1] = GfxFrame[x][y];
8765 else if (move_dir == MV_DOWN)
8766 GfxFrame[x][y + 1] = GfxFrame[x][y];
8773 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8774 if (tile == Xsand_stonesand_quickout_1 ||
8775 tile == Xsand_stonesand_quickout_2)
8779 if (graphic_info[graphic].anim_global_sync)
8780 sync_frame = FrameCounter;
8781 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8782 sync_frame = GfxFrame[x][y];
8784 sync_frame = 0; // playfield border (pseudo steel)
8786 SetRandomAnimationValue(x, y);
8788 int frame = getAnimationFrame(g->anim_frames,
8791 g->anim_start_frame,
8794 g_em->unique_identifier =
8795 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8798 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8799 int tile, int frame_em, int x, int y)
8801 int action = object_mapping[tile].action;
8802 int direction = object_mapping[tile].direction;
8803 boolean is_backside = object_mapping[tile].is_backside;
8804 int effective_element = get_effective_element_EM(tile, frame_em);
8805 int effective_action = action;
8806 int graphic = (direction == MV_NONE ?
8807 el_act2img(effective_element, effective_action) :
8808 el_act_dir2img(effective_element, effective_action,
8810 int crumbled = (direction == MV_NONE ?
8811 el_act2crm(effective_element, effective_action) :
8812 el_act_dir2crm(effective_element, effective_action,
8814 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8815 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8816 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8817 struct GraphicInfo *g = &graphic_info[graphic];
8820 // special case: graphic uses "2nd movement tile" and has defined
8821 // 7 frames for movement animation (or less) => use default graphic
8822 // for last (8th) frame which ends the movement animation
8823 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8825 effective_action = ACTION_DEFAULT;
8826 graphic = (direction == MV_NONE ?
8827 el_act2img(effective_element, effective_action) :
8828 el_act_dir2img(effective_element, effective_action,
8830 crumbled = (direction == MV_NONE ?
8831 el_act2crm(effective_element, effective_action) :
8832 el_act_dir2crm(effective_element, effective_action,
8835 g = &graphic_info[graphic];
8838 if (graphic_info[graphic].anim_global_sync)
8839 sync_frame = FrameCounter;
8840 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8841 sync_frame = GfxFrame[x][y];
8843 sync_frame = 0; // playfield border (pseudo steel)
8845 SetRandomAnimationValue(x, y);
8847 int frame = getAnimationFrame(g->anim_frames,
8850 g->anim_start_frame,
8853 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8854 g->double_movement && is_backside);
8856 // (updating the "crumbled" graphic definitions is probably not really needed,
8857 // as animations for crumbled graphics can't be longer than one EMC cycle)
8858 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8862 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8863 int player_nr, int anim, int frame_em)
8865 int element = player_mapping[player_nr][anim].element_rnd;
8866 int action = player_mapping[player_nr][anim].action;
8867 int direction = player_mapping[player_nr][anim].direction;
8868 int graphic = (direction == MV_NONE ?
8869 el_act2img(element, action) :
8870 el_act_dir2img(element, action, direction));
8871 struct GraphicInfo *g = &graphic_info[graphic];
8874 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8876 stored_player[player_nr].StepFrame = frame_em;
8878 sync_frame = stored_player[player_nr].Frame;
8880 int frame = getAnimationFrame(g->anim_frames,
8883 g->anim_start_frame,
8886 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8887 &g_em->src_x, &g_em->src_y, FALSE);
8890 void InitGraphicInfo_EM(void)
8894 // always start with reliable default values
8895 for (i = 0; i < GAME_TILE_MAX; i++)
8897 object_mapping[i].element_rnd = EL_UNKNOWN;
8898 object_mapping[i].is_backside = FALSE;
8899 object_mapping[i].action = ACTION_DEFAULT;
8900 object_mapping[i].direction = MV_NONE;
8903 // always start with reliable default values
8904 for (p = 0; p < MAX_PLAYERS; p++)
8906 for (i = 0; i < PLY_MAX; i++)
8908 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8909 player_mapping[p][i].action = ACTION_DEFAULT;
8910 player_mapping[p][i].direction = MV_NONE;
8914 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8916 int e = em_object_mapping_list[i].element_em;
8918 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8919 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8921 if (em_object_mapping_list[i].action != -1)
8922 object_mapping[e].action = em_object_mapping_list[i].action;
8924 if (em_object_mapping_list[i].direction != -1)
8925 object_mapping[e].direction =
8926 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8929 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8931 int a = em_player_mapping_list[i].action_em;
8932 int p = em_player_mapping_list[i].player_nr;
8934 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8936 if (em_player_mapping_list[i].action != -1)
8937 player_mapping[p][a].action = em_player_mapping_list[i].action;
8939 if (em_player_mapping_list[i].direction != -1)
8940 player_mapping[p][a].direction =
8941 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8944 for (i = 0; i < GAME_TILE_MAX; i++)
8946 int element = object_mapping[i].element_rnd;
8947 int action = object_mapping[i].action;
8948 int direction = object_mapping[i].direction;
8949 boolean is_backside = object_mapping[i].is_backside;
8950 boolean action_exploding = ((action == ACTION_EXPLODING ||
8951 action == ACTION_SMASHED_BY_ROCK ||
8952 action == ACTION_SMASHED_BY_SPRING) &&
8953 element != EL_DIAMOND);
8954 boolean action_active = (action == ACTION_ACTIVE);
8955 boolean action_other = (action == ACTION_OTHER);
8957 for (j = 0; j < 8; j++)
8959 int effective_element = get_effective_element_EM(i, j);
8960 int effective_action = (j < 7 ? action :
8961 i == Xdrip_stretch ? action :
8962 i == Xdrip_stretchB ? action :
8963 i == Ydrip_1_s ? action :
8964 i == Ydrip_1_sB ? action :
8965 i == Yball_1 ? action :
8966 i == Xball_2 ? action :
8967 i == Yball_2 ? action :
8968 i == Yball_blank ? action :
8969 i == Ykey_1_blank ? action :
8970 i == Ykey_2_blank ? action :
8971 i == Ykey_3_blank ? action :
8972 i == Ykey_4_blank ? action :
8973 i == Ykey_5_blank ? action :
8974 i == Ykey_6_blank ? action :
8975 i == Ykey_7_blank ? action :
8976 i == Ykey_8_blank ? action :
8977 i == Ylenses_blank ? action :
8978 i == Ymagnify_blank ? action :
8979 i == Ygrass_blank ? action :
8980 i == Ydirt_blank ? action :
8981 i == Xsand_stonein_1 ? action :
8982 i == Xsand_stonein_2 ? action :
8983 i == Xsand_stonein_3 ? action :
8984 i == Xsand_stonein_4 ? action :
8985 i == Xsand_stoneout_1 ? action :
8986 i == Xsand_stoneout_2 ? action :
8987 i == Xboom_android ? ACTION_EXPLODING :
8988 action_exploding ? ACTION_EXPLODING :
8989 action_active ? action :
8990 action_other ? action :
8992 int graphic = (el_act_dir2img(effective_element, effective_action,
8994 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8996 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8997 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8998 boolean has_action_graphics = (graphic != base_graphic);
8999 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9000 struct GraphicInfo *g = &graphic_info[graphic];
9001 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9004 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9005 boolean special_animation = (action != ACTION_DEFAULT &&
9006 g->anim_frames == 3 &&
9007 g->anim_delay == 2 &&
9008 g->anim_mode & ANIM_LINEAR);
9009 int sync_frame = (i == Xdrip_stretch ? 7 :
9010 i == Xdrip_stretchB ? 7 :
9011 i == Ydrip_2_s ? j + 8 :
9012 i == Ydrip_2_sB ? j + 8 :
9021 i == Xfake_acid_1 ? 0 :
9022 i == Xfake_acid_2 ? 10 :
9023 i == Xfake_acid_3 ? 20 :
9024 i == Xfake_acid_4 ? 30 :
9025 i == Xfake_acid_5 ? 40 :
9026 i == Xfake_acid_6 ? 50 :
9027 i == Xfake_acid_7 ? 60 :
9028 i == Xfake_acid_8 ? 70 :
9029 i == Xfake_acid_1_player ? 0 :
9030 i == Xfake_acid_2_player ? 10 :
9031 i == Xfake_acid_3_player ? 20 :
9032 i == Xfake_acid_4_player ? 30 :
9033 i == Xfake_acid_5_player ? 40 :
9034 i == Xfake_acid_6_player ? 50 :
9035 i == Xfake_acid_7_player ? 60 :
9036 i == Xfake_acid_8_player ? 70 :
9038 i == Yball_2 ? j + 8 :
9039 i == Yball_blank ? j + 1 :
9040 i == Ykey_1_blank ? j + 1 :
9041 i == Ykey_2_blank ? j + 1 :
9042 i == Ykey_3_blank ? j + 1 :
9043 i == Ykey_4_blank ? j + 1 :
9044 i == Ykey_5_blank ? j + 1 :
9045 i == Ykey_6_blank ? j + 1 :
9046 i == Ykey_7_blank ? j + 1 :
9047 i == Ykey_8_blank ? j + 1 :
9048 i == Ylenses_blank ? j + 1 :
9049 i == Ymagnify_blank ? j + 1 :
9050 i == Ygrass_blank ? j + 1 :
9051 i == Ydirt_blank ? j + 1 :
9052 i == Xamoeba_1 ? 0 :
9053 i == Xamoeba_2 ? 1 :
9054 i == Xamoeba_3 ? 2 :
9055 i == Xamoeba_4 ? 3 :
9056 i == Xamoeba_5 ? 0 :
9057 i == Xamoeba_6 ? 1 :
9058 i == Xamoeba_7 ? 2 :
9059 i == Xamoeba_8 ? 3 :
9060 i == Xexit_2 ? j + 8 :
9061 i == Xexit_3 ? j + 16 :
9062 i == Xdynamite_1 ? 0 :
9063 i == Xdynamite_2 ? 8 :
9064 i == Xdynamite_3 ? 16 :
9065 i == Xdynamite_4 ? 24 :
9066 i == Xsand_stonein_1 ? j + 1 :
9067 i == Xsand_stonein_2 ? j + 9 :
9068 i == Xsand_stonein_3 ? j + 17 :
9069 i == Xsand_stonein_4 ? j + 25 :
9070 i == Xsand_stoneout_1 && j == 0 ? 0 :
9071 i == Xsand_stoneout_1 && j == 1 ? 0 :
9072 i == Xsand_stoneout_1 && j == 2 ? 1 :
9073 i == Xsand_stoneout_1 && j == 3 ? 2 :
9074 i == Xsand_stoneout_1 && j == 4 ? 2 :
9075 i == Xsand_stoneout_1 && j == 5 ? 3 :
9076 i == Xsand_stoneout_1 && j == 6 ? 4 :
9077 i == Xsand_stoneout_1 && j == 7 ? 4 :
9078 i == Xsand_stoneout_2 && j == 0 ? 5 :
9079 i == Xsand_stoneout_2 && j == 1 ? 6 :
9080 i == Xsand_stoneout_2 && j == 2 ? 7 :
9081 i == Xsand_stoneout_2 && j == 3 ? 8 :
9082 i == Xsand_stoneout_2 && j == 4 ? 9 :
9083 i == Xsand_stoneout_2 && j == 5 ? 11 :
9084 i == Xsand_stoneout_2 && j == 6 ? 13 :
9085 i == Xsand_stoneout_2 && j == 7 ? 15 :
9086 i == Xboom_bug && j == 1 ? 2 :
9087 i == Xboom_bug && j == 2 ? 2 :
9088 i == Xboom_bug && j == 3 ? 4 :
9089 i == Xboom_bug && j == 4 ? 4 :
9090 i == Xboom_bug && j == 5 ? 2 :
9091 i == Xboom_bug && j == 6 ? 2 :
9092 i == Xboom_bug && j == 7 ? 0 :
9093 i == Xboom_tank && j == 1 ? 2 :
9094 i == Xboom_tank && j == 2 ? 2 :
9095 i == Xboom_tank && j == 3 ? 4 :
9096 i == Xboom_tank && j == 4 ? 4 :
9097 i == Xboom_tank && j == 5 ? 2 :
9098 i == Xboom_tank && j == 6 ? 2 :
9099 i == Xboom_tank && j == 7 ? 0 :
9100 i == Xboom_android && j == 7 ? 6 :
9101 i == Xboom_1 && j == 1 ? 2 :
9102 i == Xboom_1 && j == 2 ? 2 :
9103 i == Xboom_1 && j == 3 ? 4 :
9104 i == Xboom_1 && j == 4 ? 4 :
9105 i == Xboom_1 && j == 5 ? 6 :
9106 i == Xboom_1 && j == 6 ? 6 :
9107 i == Xboom_1 && j == 7 ? 8 :
9108 i == Xboom_2 && j == 0 ? 8 :
9109 i == Xboom_2 && j == 1 ? 8 :
9110 i == Xboom_2 && j == 2 ? 10 :
9111 i == Xboom_2 && j == 3 ? 10 :
9112 i == Xboom_2 && j == 4 ? 10 :
9113 i == Xboom_2 && j == 5 ? 12 :
9114 i == Xboom_2 && j == 6 ? 12 :
9115 i == Xboom_2 && j == 7 ? 12 :
9116 special_animation && j == 4 ? 3 :
9117 effective_action != action ? 0 :
9119 int frame = getAnimationFrame(g->anim_frames,
9122 g->anim_start_frame,
9125 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9126 g->double_movement && is_backside);
9128 g_em->bitmap = src_bitmap;
9129 g_em->src_x = src_x;
9130 g_em->src_y = src_y;
9131 g_em->src_offset_x = 0;
9132 g_em->src_offset_y = 0;
9133 g_em->dst_offset_x = 0;
9134 g_em->dst_offset_y = 0;
9135 g_em->width = TILEX;
9136 g_em->height = TILEY;
9138 g_em->preserve_background = FALSE;
9140 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9143 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9144 effective_action == ACTION_MOVING ||
9145 effective_action == ACTION_PUSHING ||
9146 effective_action == ACTION_EATING)) ||
9147 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9148 effective_action == ACTION_EMPTYING)))
9151 (effective_action == ACTION_FALLING ||
9152 effective_action == ACTION_FILLING ||
9153 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9154 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9155 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9156 int num_steps = (i == Ydrip_1_s ? 16 :
9157 i == Ydrip_1_sB ? 16 :
9158 i == Ydrip_2_s ? 16 :
9159 i == Ydrip_2_sB ? 16 :
9160 i == Xsand_stonein_1 ? 32 :
9161 i == Xsand_stonein_2 ? 32 :
9162 i == Xsand_stonein_3 ? 32 :
9163 i == Xsand_stonein_4 ? 32 :
9164 i == Xsand_stoneout_1 ? 16 :
9165 i == Xsand_stoneout_2 ? 16 : 8);
9166 int cx = ABS(dx) * (TILEX / num_steps);
9167 int cy = ABS(dy) * (TILEY / num_steps);
9168 int step_frame = (i == Ydrip_2_s ? j + 8 :
9169 i == Ydrip_2_sB ? j + 8 :
9170 i == Xsand_stonein_2 ? j + 8 :
9171 i == Xsand_stonein_3 ? j + 16 :
9172 i == Xsand_stonein_4 ? j + 24 :
9173 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9174 int step = (is_backside ? step_frame : num_steps - step_frame);
9176 if (is_backside) // tile where movement starts
9178 if (dx < 0 || dy < 0)
9180 g_em->src_offset_x = cx * step;
9181 g_em->src_offset_y = cy * step;
9185 g_em->dst_offset_x = cx * step;
9186 g_em->dst_offset_y = cy * step;
9189 else // tile where movement ends
9191 if (dx < 0 || dy < 0)
9193 g_em->dst_offset_x = cx * step;
9194 g_em->dst_offset_y = cy * step;
9198 g_em->src_offset_x = cx * step;
9199 g_em->src_offset_y = cy * step;
9203 g_em->width = TILEX - cx * step;
9204 g_em->height = TILEY - cy * step;
9207 // create unique graphic identifier to decide if tile must be redrawn
9208 /* bit 31 - 16 (16 bit): EM style graphic
9209 bit 15 - 12 ( 4 bit): EM style frame
9210 bit 11 - 6 ( 6 bit): graphic width
9211 bit 5 - 0 ( 6 bit): graphic height */
9212 g_em->unique_identifier =
9213 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9217 for (i = 0; i < GAME_TILE_MAX; i++)
9219 for (j = 0; j < 8; j++)
9221 int element = object_mapping[i].element_rnd;
9222 int action = object_mapping[i].action;
9223 int direction = object_mapping[i].direction;
9224 boolean is_backside = object_mapping[i].is_backside;
9225 int graphic_action = el_act_dir2img(element, action, direction);
9226 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9228 if ((action == ACTION_SMASHED_BY_ROCK ||
9229 action == ACTION_SMASHED_BY_SPRING ||
9230 action == ACTION_EATING) &&
9231 graphic_action == graphic_default)
9233 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9234 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9235 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9236 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9239 // no separate animation for "smashed by rock" -- use rock instead
9240 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9241 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9243 g_em->bitmap = g_xx->bitmap;
9244 g_em->src_x = g_xx->src_x;
9245 g_em->src_y = g_xx->src_y;
9246 g_em->src_offset_x = g_xx->src_offset_x;
9247 g_em->src_offset_y = g_xx->src_offset_y;
9248 g_em->dst_offset_x = g_xx->dst_offset_x;
9249 g_em->dst_offset_y = g_xx->dst_offset_y;
9250 g_em->width = g_xx->width;
9251 g_em->height = g_xx->height;
9252 g_em->unique_identifier = g_xx->unique_identifier;
9255 g_em->preserve_background = TRUE;
9260 for (p = 0; p < MAX_PLAYERS; p++)
9262 for (i = 0; i < PLY_MAX; i++)
9264 int element = player_mapping[p][i].element_rnd;
9265 int action = player_mapping[p][i].action;
9266 int direction = player_mapping[p][i].direction;
9268 for (j = 0; j < 8; j++)
9270 int effective_element = element;
9271 int effective_action = action;
9272 int graphic = (direction == MV_NONE ?
9273 el_act2img(effective_element, effective_action) :
9274 el_act_dir2img(effective_element, effective_action,
9276 struct GraphicInfo *g = &graphic_info[graphic];
9277 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9281 int frame = getAnimationFrame(g->anim_frames,
9284 g->anim_start_frame,
9287 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9289 g_em->bitmap = src_bitmap;
9290 g_em->src_x = src_x;
9291 g_em->src_y = src_y;
9292 g_em->src_offset_x = 0;
9293 g_em->src_offset_y = 0;
9294 g_em->dst_offset_x = 0;
9295 g_em->dst_offset_y = 0;
9296 g_em->width = TILEX;
9297 g_em->height = TILEY;
9303 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9304 boolean any_player_moving,
9305 boolean any_player_snapping,
9306 boolean any_player_dropping)
9308 if (frame == 7 && !any_player_dropping)
9310 if (!local_player->was_waiting)
9312 if (!CheckSaveEngineSnapshotToList())
9315 local_player->was_waiting = TRUE;
9318 else if (any_player_moving || any_player_snapping || any_player_dropping)
9320 local_player->was_waiting = FALSE;
9324 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9325 boolean murphy_is_dropping)
9327 if (murphy_is_waiting)
9329 if (!local_player->was_waiting)
9331 if (!CheckSaveEngineSnapshotToList())
9334 local_player->was_waiting = TRUE;
9339 local_player->was_waiting = FALSE;
9343 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9344 boolean button_released)
9346 if (button_released)
9348 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9349 CheckSaveEngineSnapshotToList();
9351 else if (element_clicked)
9353 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9354 CheckSaveEngineSnapshotToList();
9356 game.snapshot.changed_action = TRUE;
9360 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9361 boolean any_player_moving,
9362 boolean any_player_snapping,
9363 boolean any_player_dropping)
9365 if (tape.single_step && tape.recording && !tape.pausing)
9366 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9367 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9369 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9370 any_player_snapping, any_player_dropping);
9372 return tape.pausing;
9375 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9376 boolean murphy_is_dropping)
9378 boolean murphy_starts_dropping = FALSE;
9381 for (i = 0; i < MAX_PLAYERS; i++)
9382 if (stored_player[i].force_dropping)
9383 murphy_starts_dropping = TRUE;
9385 if (tape.single_step && tape.recording && !tape.pausing)
9386 if (murphy_is_waiting && !murphy_starts_dropping)
9387 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9389 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9392 void CheckSingleStepMode_MM(boolean element_clicked,
9393 boolean button_released)
9395 if (tape.single_step && tape.recording && !tape.pausing)
9396 if (button_released)
9397 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9399 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9402 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9403 int graphic, int sync_frame, int x, int y)
9405 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9407 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9410 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9412 return (IS_NEXT_FRAME(sync_frame, graphic));
9415 int getGraphicInfo_Delay(int graphic)
9417 return graphic_info[graphic].anim_delay;
9420 void PlayMenuSoundExt(int sound)
9422 if (sound == SND_UNDEFINED)
9425 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9426 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9429 if (IS_LOOP_SOUND(sound))
9430 PlaySoundLoop(sound);
9435 void PlayMenuSound(void)
9437 PlayMenuSoundExt(menu.sound[game_status]);
9440 void PlayMenuSoundStereo(int sound, int stereo_position)
9442 if (sound == SND_UNDEFINED)
9445 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9446 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9449 if (IS_LOOP_SOUND(sound))
9450 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9452 PlaySoundStereo(sound, stereo_position);
9455 void PlayMenuSoundIfLoopExt(int sound)
9457 if (sound == SND_UNDEFINED)
9460 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9461 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9464 if (IS_LOOP_SOUND(sound))
9465 PlaySoundLoop(sound);
9468 void PlayMenuSoundIfLoop(void)
9470 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9473 void PlayMenuMusicExt(int music)
9475 if (music == MUS_UNDEFINED)
9478 if (!setup.sound_music)
9481 if (IS_LOOP_MUSIC(music))
9482 PlayMusicLoop(music);
9487 void PlayMenuMusic(void)
9489 char *curr_music = getCurrentlyPlayingMusicFilename();
9490 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9492 if (!strEqual(curr_music, next_music))
9493 PlayMenuMusicExt(menu.music[game_status]);
9496 void PlayMenuSoundsAndMusic(void)
9502 static void FadeMenuSounds(void)
9507 static void FadeMenuMusic(void)
9509 char *curr_music = getCurrentlyPlayingMusicFilename();
9510 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9512 if (!strEqual(curr_music, next_music))
9516 void FadeMenuSoundsAndMusic(void)
9522 void PlaySoundActivating(void)
9525 PlaySound(SND_MENU_ITEM_ACTIVATING);
9529 void PlaySoundSelecting(void)
9532 PlaySound(SND_MENU_ITEM_SELECTING);
9536 void ToggleFullscreenIfNeeded(void)
9538 // if setup and video fullscreen state are already matching, nothing do do
9539 if (setup.fullscreen == video.fullscreen_enabled ||
9540 !video.fullscreen_available)
9543 SDLSetWindowFullscreen(setup.fullscreen);
9545 // set setup value according to successfully changed fullscreen mode
9546 setup.fullscreen = video.fullscreen_enabled;
9549 void ChangeWindowScalingIfNeeded(void)
9551 // if setup and video window scaling are already matching, nothing do do
9552 if (setup.window_scaling_percent == video.window_scaling_percent ||
9553 video.fullscreen_enabled)
9556 SDLSetWindowScaling(setup.window_scaling_percent);
9558 // set setup value according to successfully changed window scaling
9559 setup.window_scaling_percent = video.window_scaling_percent;
9562 void ChangeVsyncModeIfNeeded(void)
9564 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9565 int video_vsync_mode = video.vsync_mode;
9567 // if setup and video vsync mode are already matching, nothing do do
9568 if (setup_vsync_mode == video_vsync_mode)
9571 // if renderer is using OpenGL, vsync mode can directly be changed
9572 SDLSetScreenVsyncMode(setup.vsync_mode);
9574 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9575 if (video.vsync_mode == video_vsync_mode)
9577 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9579 // save backbuffer content which gets lost when re-creating screen
9580 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9582 // force re-creating screen and renderer to set new vsync mode
9583 video.fullscreen_enabled = !setup.fullscreen;
9585 // when creating new renderer, destroy textures linked to old renderer
9586 FreeAllImageTextures(); // needs old renderer to free the textures
9588 // re-create screen and renderer (including change of vsync mode)
9589 ChangeVideoModeIfNeeded(setup.fullscreen);
9591 // set setup value according to successfully changed fullscreen mode
9592 setup.fullscreen = video.fullscreen_enabled;
9594 // restore backbuffer content from temporary backbuffer backup bitmap
9595 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9596 FreeBitmap(tmp_backbuffer);
9598 // update visible window/screen
9599 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9601 // when changing vsync mode, re-create textures for new renderer
9602 InitImageTextures();
9605 // set setup value according to successfully changed vsync mode
9606 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9609 static void JoinRectangles(int *x, int *y, int *width, int *height,
9610 int x2, int y2, int width2, int height2)
9612 // do not join with "off-screen" rectangle
9613 if (x2 == -1 || y2 == -1)
9618 *width = MAX(*width, width2);
9619 *height = MAX(*height, height2);
9622 void SetAnimStatus(int anim_status_new)
9624 if (anim_status_new == GAME_MODE_MAIN)
9625 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9626 else if (anim_status_new == GAME_MODE_NAMES)
9627 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9628 else if (anim_status_new == GAME_MODE_SCORES)
9629 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9631 global.anim_status_next = anim_status_new;
9633 // directly set screen modes that are entered without fading
9634 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9635 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9636 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9637 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9638 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9639 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9640 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9641 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9642 global.anim_status = global.anim_status_next;
9645 void SetGameStatus(int game_status_new)
9647 if (game_status_new != game_status)
9648 game_status_last_screen = game_status;
9650 game_status = game_status_new;
9652 SetAnimStatus(game_status_new);
9655 void SetFontStatus(int game_status_new)
9657 static int last_game_status = -1;
9659 if (game_status_new != -1)
9661 // set game status for font use after storing last game status
9662 last_game_status = game_status;
9663 game_status = game_status_new;
9667 // reset game status after font use from last stored game status
9668 game_status = last_game_status;
9672 void ResetFontStatus(void)
9677 void SetLevelSetInfo(char *identifier, int level_nr)
9679 setString(&levelset.identifier, identifier);
9681 levelset.level_nr = level_nr;
9684 boolean CheckIfAllViewportsHaveChanged(void)
9686 // if game status has not changed, viewports have not changed either
9687 if (game_status == game_status_last)
9690 // check if all viewports have changed with current game status
9692 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9693 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9694 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9695 int new_real_sx = vp_playfield->x;
9696 int new_real_sy = vp_playfield->y;
9697 int new_full_sxsize = vp_playfield->width;
9698 int new_full_sysize = vp_playfield->height;
9699 int new_dx = vp_door_1->x;
9700 int new_dy = vp_door_1->y;
9701 int new_dxsize = vp_door_1->width;
9702 int new_dysize = vp_door_1->height;
9703 int new_vx = vp_door_2->x;
9704 int new_vy = vp_door_2->y;
9705 int new_vxsize = vp_door_2->width;
9706 int new_vysize = vp_door_2->height;
9708 boolean playfield_viewport_has_changed =
9709 (new_real_sx != REAL_SX ||
9710 new_real_sy != REAL_SY ||
9711 new_full_sxsize != FULL_SXSIZE ||
9712 new_full_sysize != FULL_SYSIZE);
9714 boolean door_1_viewport_has_changed =
9717 new_dxsize != DXSIZE ||
9718 new_dysize != DYSIZE);
9720 boolean door_2_viewport_has_changed =
9723 new_vxsize != VXSIZE ||
9724 new_vysize != VYSIZE ||
9725 game_status_last == GAME_MODE_EDITOR);
9727 return (playfield_viewport_has_changed &&
9728 door_1_viewport_has_changed &&
9729 door_2_viewport_has_changed);
9732 boolean CheckFadeAll(void)
9734 return (CheckIfGlobalBorderHasChanged() ||
9735 CheckIfAllViewportsHaveChanged());
9738 void ChangeViewportPropertiesIfNeeded(void)
9740 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9741 FALSE : setup.small_game_graphics);
9742 int gfx_game_mode = game_status;
9743 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9745 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9746 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9747 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9748 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9749 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9750 int new_win_xsize = vp_window->width;
9751 int new_win_ysize = vp_window->height;
9752 int border_left = vp_playfield->border_left;
9753 int border_right = vp_playfield->border_right;
9754 int border_top = vp_playfield->border_top;
9755 int border_bottom = vp_playfield->border_bottom;
9756 int new_sx = vp_playfield->x + border_left;
9757 int new_sy = vp_playfield->y + border_top;
9758 int new_sxsize = vp_playfield->width - border_left - border_right;
9759 int new_sysize = vp_playfield->height - border_top - border_bottom;
9760 int new_real_sx = vp_playfield->x;
9761 int new_real_sy = vp_playfield->y;
9762 int new_full_sxsize = vp_playfield->width;
9763 int new_full_sysize = vp_playfield->height;
9764 int new_dx = vp_door_1->x;
9765 int new_dy = vp_door_1->y;
9766 int new_dxsize = vp_door_1->width;
9767 int new_dysize = vp_door_1->height;
9768 int new_vx = vp_door_2->x;
9769 int new_vy = vp_door_2->y;
9770 int new_vxsize = vp_door_2->width;
9771 int new_vysize = vp_door_2->height;
9772 int new_ex = vp_door_3->x;
9773 int new_ey = vp_door_3->y;
9774 int new_exsize = vp_door_3->width;
9775 int new_eysize = vp_door_3->height;
9776 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9777 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9778 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9779 int new_scr_fieldx = new_sxsize / tilesize;
9780 int new_scr_fieldy = new_sysize / tilesize;
9781 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9782 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9783 boolean init_gfx_buffers = FALSE;
9784 boolean init_video_buffer = FALSE;
9785 boolean init_gadgets_and_anims = FALSE;
9786 boolean init_em_graphics = FALSE;
9788 if (new_win_xsize != WIN_XSIZE ||
9789 new_win_ysize != WIN_YSIZE)
9791 WIN_XSIZE = new_win_xsize;
9792 WIN_YSIZE = new_win_ysize;
9794 init_video_buffer = TRUE;
9795 init_gfx_buffers = TRUE;
9796 init_gadgets_and_anims = TRUE;
9798 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9801 if (new_scr_fieldx != SCR_FIELDX ||
9802 new_scr_fieldy != SCR_FIELDY)
9804 // this always toggles between MAIN and GAME when using small tile size
9806 SCR_FIELDX = new_scr_fieldx;
9807 SCR_FIELDY = new_scr_fieldy;
9809 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9820 new_sxsize != SXSIZE ||
9821 new_sysize != SYSIZE ||
9822 new_dxsize != DXSIZE ||
9823 new_dysize != DYSIZE ||
9824 new_vxsize != VXSIZE ||
9825 new_vysize != VYSIZE ||
9826 new_exsize != EXSIZE ||
9827 new_eysize != EYSIZE ||
9828 new_real_sx != REAL_SX ||
9829 new_real_sy != REAL_SY ||
9830 new_full_sxsize != FULL_SXSIZE ||
9831 new_full_sysize != FULL_SYSIZE ||
9832 new_tilesize_var != TILESIZE_VAR
9835 // ------------------------------------------------------------------------
9836 // determine next fading area for changed viewport definitions
9837 // ------------------------------------------------------------------------
9839 // start with current playfield area (default fading area)
9842 FADE_SXSIZE = FULL_SXSIZE;
9843 FADE_SYSIZE = FULL_SYSIZE;
9845 // add new playfield area if position or size has changed
9846 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9847 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9849 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9850 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9853 // add current and new door 1 area if position or size has changed
9854 if (new_dx != DX || new_dy != DY ||
9855 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9857 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9858 DX, DY, DXSIZE, DYSIZE);
9859 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9860 new_dx, new_dy, new_dxsize, new_dysize);
9863 // add current and new door 2 area if position or size has changed
9864 if (new_vx != VX || new_vy != VY ||
9865 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9867 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9868 VX, VY, VXSIZE, VYSIZE);
9869 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9870 new_vx, new_vy, new_vxsize, new_vysize);
9873 // ------------------------------------------------------------------------
9874 // handle changed tile size
9875 // ------------------------------------------------------------------------
9877 if (new_tilesize_var != TILESIZE_VAR)
9879 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9881 // changing tile size invalidates scroll values of engine snapshots
9882 FreeEngineSnapshotSingle();
9884 // changing tile size requires update of graphic mapping for EM engine
9885 init_em_graphics = TRUE;
9896 SXSIZE = new_sxsize;
9897 SYSIZE = new_sysize;
9898 DXSIZE = new_dxsize;
9899 DYSIZE = new_dysize;
9900 VXSIZE = new_vxsize;
9901 VYSIZE = new_vysize;
9902 EXSIZE = new_exsize;
9903 EYSIZE = new_eysize;
9904 REAL_SX = new_real_sx;
9905 REAL_SY = new_real_sy;
9906 FULL_SXSIZE = new_full_sxsize;
9907 FULL_SYSIZE = new_full_sysize;
9908 TILESIZE_VAR = new_tilesize_var;
9910 init_gfx_buffers = TRUE;
9911 init_gadgets_and_anims = TRUE;
9913 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9914 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9917 if (init_gfx_buffers)
9919 // Debug("tools:viewport", "init_gfx_buffers");
9921 SCR_FIELDX = new_scr_fieldx_buffers;
9922 SCR_FIELDY = new_scr_fieldy_buffers;
9926 SCR_FIELDX = new_scr_fieldx;
9927 SCR_FIELDY = new_scr_fieldy;
9929 SetDrawDeactivationMask(REDRAW_NONE);
9930 SetDrawBackgroundMask(REDRAW_FIELD);
9933 if (init_video_buffer)
9935 // Debug("tools:viewport", "init_video_buffer");
9937 FreeAllImageTextures(); // needs old renderer to free the textures
9939 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9940 InitImageTextures();
9943 if (init_gadgets_and_anims)
9945 // Debug("tools:viewport", "init_gadgets_and_anims");
9948 InitGlobalAnimations();
9951 if (init_em_graphics)
9953 InitGraphicInfo_EM();
9957 void OpenURL(char *url)
9962 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9964 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9968 // ============================================================================
9970 // ============================================================================
9972 #if defined(PLATFORM_WIN32)
9973 /* FILETIME of Jan 1 1970 00:00:00. */
9974 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9977 * timezone information is stored outside the kernel so tzp isn't used anymore.
9979 * Note: this function is not for Win32 high precision timing purpose. See
9982 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9985 SYSTEMTIME system_time;
9986 ULARGE_INTEGER ularge;
9988 GetSystemTime(&system_time);
9989 SystemTimeToFileTime(&system_time, &file_time);
9990 ularge.LowPart = file_time.dwLowDateTime;
9991 ularge.HighPart = file_time.dwHighDateTime;
9993 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9994 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10000 static char *test_init_uuid_random_function_simple(void)
10002 static char seed_text[100];
10003 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10005 sprintf(seed_text, "%d", seed);
10010 static char *test_init_uuid_random_function_better(void)
10012 static char seed_text[100];
10013 struct timeval current_time;
10015 gettimeofday(¤t_time, NULL);
10017 prng_seed_bytes(¤t_time, sizeof(current_time));
10019 sprintf(seed_text, "%ld.%ld",
10020 (long)current_time.tv_sec,
10021 (long)current_time.tv_usec);
10026 #if defined(PLATFORM_WIN32)
10027 static char *test_init_uuid_random_function_better_windows(void)
10029 static char seed_text[100];
10030 struct timeval current_time;
10032 gettimeofday_windows(¤t_time, NULL);
10034 prng_seed_bytes(¤t_time, sizeof(current_time));
10036 sprintf(seed_text, "%ld.%ld",
10037 (long)current_time.tv_sec,
10038 (long)current_time.tv_usec);
10044 static unsigned int test_uuid_random_function_simple(int max)
10046 return GetSimpleRandom(max);
10049 static unsigned int test_uuid_random_function_better(int max)
10051 return (max > 0 ? prng_get_uint() % max : 0);
10054 #if defined(PLATFORM_WIN32)
10055 #define NUM_UUID_TESTS 3
10057 #define NUM_UUID_TESTS 2
10060 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10062 struct hashtable *hash_seeds =
10063 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10064 struct hashtable *hash_uuids =
10065 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10066 static char message[100];
10069 char *random_name = (nr == 0 ? "simple" : "better");
10070 char *random_type = (always_seed ? "always" : "only once");
10071 char *(*init_random_function)(void) =
10073 test_init_uuid_random_function_simple :
10074 test_init_uuid_random_function_better);
10075 unsigned int (*random_function)(int) =
10077 test_uuid_random_function_simple :
10078 test_uuid_random_function_better);
10081 #if defined(PLATFORM_WIN32)
10084 random_name = "windows";
10085 init_random_function = test_init_uuid_random_function_better_windows;
10091 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10092 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10094 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10095 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10096 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10098 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10102 // always initialize random number generator at least once
10103 init_random_function();
10105 unsigned int time_start = SDL_GetTicks();
10107 for (i = 0; i < num_uuids; i++)
10111 char *seed = getStringCopy(init_random_function());
10113 hashtable_remove(hash_seeds, seed);
10114 hashtable_insert(hash_seeds, seed, "1");
10117 char *uuid = getStringCopy(getUUIDExt(random_function));
10119 hashtable_remove(hash_uuids, uuid);
10120 hashtable_insert(hash_uuids, uuid, "1");
10123 int num_unique_seeds = hashtable_count(hash_seeds);
10124 int num_unique_uuids = hashtable_count(hash_uuids);
10126 unsigned int time_needed = SDL_GetTicks() - time_start;
10128 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10130 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10133 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10135 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10136 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10138 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10140 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10142 Request(message, REQ_CONFIRM);
10144 hashtable_destroy(hash_seeds, 0);
10145 hashtable_destroy(hash_uuids, 0);
10148 void TestGeneratingUUIDs(void)
10150 int num_uuids = 1000000;
10153 for (i = 0; i < NUM_UUID_TESTS; i++)
10154 for (j = 0; j < 2; j++)
10155 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10157 CloseAllAndExit(0);