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 ||
1126 status == GAME_MODE_SCOREINFO ? IMG_GLOBAL_BORDER_SCORES :
1127 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1128 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1131 return getGlobalBorderBitmap(graphic);
1134 void SetWindowBackgroundImageIfDefined(int graphic)
1136 if (graphic_info[graphic].bitmap)
1137 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1140 void SetMainBackgroundImageIfDefined(int graphic)
1142 if (graphic_info[graphic].bitmap)
1143 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1146 void SetDoorBackgroundImageIfDefined(int graphic)
1148 if (graphic_info[graphic].bitmap)
1149 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1152 void SetWindowBackgroundImage(int graphic)
1154 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetMainBackgroundImage(int graphic)
1159 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetDoorBackgroundImage(int graphic)
1164 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1167 void SetPanelBackground(void)
1169 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1171 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1172 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1174 SetDoorBackgroundBitmap(bitmap_db_panel);
1177 void DrawBackground(int x, int y, int width, int height)
1179 // "drawto" might still point to playfield buffer here (hall of fame)
1180 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1182 if (IN_GFX_FIELD_FULL(x, y))
1183 redraw_mask |= REDRAW_FIELD;
1184 else if (IN_GFX_DOOR_1(x, y))
1185 redraw_mask |= REDRAW_DOOR_1;
1186 else if (IN_GFX_DOOR_2(x, y))
1187 redraw_mask |= REDRAW_DOOR_2;
1188 else if (IN_GFX_DOOR_3(x, y))
1189 redraw_mask |= REDRAW_DOOR_3;
1192 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1194 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1196 if (font->bitmap == NULL)
1199 DrawBackground(x, y, width, height);
1202 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1204 struct GraphicInfo *g = &graphic_info[graphic];
1206 if (g->bitmap == NULL)
1209 DrawBackground(x, y, width, height);
1212 static int game_status_last = -1;
1213 static Bitmap *global_border_bitmap_last = NULL;
1214 static Bitmap *global_border_bitmap = NULL;
1215 static int real_sx_last = -1, real_sy_last = -1;
1216 static int full_sxsize_last = -1, full_sysize_last = -1;
1217 static int dx_last = -1, dy_last = -1;
1218 static int dxsize_last = -1, dysize_last = -1;
1219 static int vx_last = -1, vy_last = -1;
1220 static int vxsize_last = -1, vysize_last = -1;
1221 static int ex_last = -1, ey_last = -1;
1222 static int exsize_last = -1, eysize_last = -1;
1224 boolean CheckIfGlobalBorderHasChanged(void)
1226 // if game status has not changed, global border has not changed either
1227 if (game_status == game_status_last)
1230 // determine and store new global border bitmap for current game status
1231 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1233 return (global_border_bitmap_last != global_border_bitmap);
1236 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1238 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1239 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1241 // if game status has not changed, nothing has to be redrawn
1242 if (game_status == game_status_last)
1245 // redraw if last screen was title screen
1246 if (game_status_last == GAME_MODE_TITLE)
1249 // redraw if global screen border has changed
1250 if (CheckIfGlobalBorderHasChanged())
1253 // redraw if position or size of playfield area has changed
1254 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1255 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1258 // redraw if position or size of door area has changed
1259 if (dx_last != DX || dy_last != DY ||
1260 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1263 // redraw if position or size of tape area has changed
1264 if (vx_last != VX || vy_last != VY ||
1265 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1268 // redraw if position or size of editor area has changed
1269 if (ex_last != EX || ey_last != EY ||
1270 exsize_last != EXSIZE || eysize_last != EYSIZE)
1277 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1280 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1282 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1285 void RedrawGlobalBorder(void)
1287 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1289 RedrawGlobalBorderFromBitmap(bitmap);
1291 redraw_mask = REDRAW_ALL;
1294 static void RedrawGlobalBorderIfNeeded(void)
1296 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1297 if (game_status == game_status_last)
1301 // copy current draw buffer to later copy back areas that have not changed
1302 if (game_status_last != GAME_MODE_TITLE)
1303 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1305 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1306 if (CheckIfGlobalBorderRedrawIsNeeded())
1308 // determine and store new global border bitmap for current game status
1309 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1312 // redraw global screen border (or clear, if defined to be empty)
1313 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1315 if (game_status == GAME_MODE_EDITOR)
1316 DrawSpecialEditorDoor();
1318 // copy previous playfield and door areas, if they are defined on both
1319 // previous and current screen and if they still have the same size
1321 if (real_sx_last != -1 && real_sy_last != -1 &&
1322 REAL_SX != -1 && REAL_SY != -1 &&
1323 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1324 BlitBitmap(bitmap_db_store_1, backbuffer,
1325 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1328 if (dx_last != -1 && dy_last != -1 &&
1329 DX != -1 && DY != -1 &&
1330 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1331 BlitBitmap(bitmap_db_store_1, backbuffer,
1332 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1334 if (game_status != GAME_MODE_EDITOR)
1336 if (vx_last != -1 && vy_last != -1 &&
1337 VX != -1 && VY != -1 &&
1338 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1339 BlitBitmap(bitmap_db_store_1, backbuffer,
1340 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1344 if (ex_last != -1 && ey_last != -1 &&
1345 EX != -1 && EY != -1 &&
1346 exsize_last == EXSIZE && eysize_last == EYSIZE)
1347 BlitBitmap(bitmap_db_store_1, backbuffer,
1348 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1351 redraw_mask = REDRAW_ALL;
1354 game_status_last = game_status;
1356 global_border_bitmap_last = global_border_bitmap;
1358 real_sx_last = REAL_SX;
1359 real_sy_last = REAL_SY;
1360 full_sxsize_last = FULL_SXSIZE;
1361 full_sysize_last = FULL_SYSIZE;
1364 dxsize_last = DXSIZE;
1365 dysize_last = DYSIZE;
1368 vxsize_last = VXSIZE;
1369 vysize_last = VYSIZE;
1372 exsize_last = EXSIZE;
1373 eysize_last = EYSIZE;
1376 void ClearField(void)
1378 RedrawGlobalBorderIfNeeded();
1380 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1381 // (when entering hall of fame after playing)
1382 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1384 // !!! maybe this should be done before clearing the background !!!
1385 if (game_status == GAME_MODE_PLAYING)
1387 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1388 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1392 SetDrawtoField(DRAW_TO_BACKBUFFER);
1396 void MarkTileDirty(int x, int y)
1398 redraw_mask |= REDRAW_FIELD;
1401 void SetBorderElement(void)
1405 BorderElement = EL_EMPTY;
1407 // only the R'n'D game engine may use an additional steelwall border
1408 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1411 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1413 for (x = 0; x < lev_fieldx; x++)
1415 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1416 BorderElement = EL_STEELWALL;
1418 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1424 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1425 int max_array_fieldx, int max_array_fieldy,
1426 short field[max_array_fieldx][max_array_fieldy],
1427 int max_fieldx, int max_fieldy)
1429 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1430 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1431 int old_element = field[start_x][start_y];
1434 // do nothing if start field already has the desired content
1435 if (old_element == fill_element)
1438 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1440 while (stack_pos > 0)
1442 struct XY current = stack_buffer[--stack_pos];
1445 field[current.x][current.y] = fill_element;
1447 for (i = 0; i < 4; i++)
1449 int x = current.x + check[i].x;
1450 int y = current.y + check[i].y;
1452 // check for stack buffer overflow (should not happen)
1453 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1454 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1456 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1457 stack_buffer[stack_pos++] = (struct XY){ x, y };
1462 void FloodFillLevel(int from_x, int from_y, int fill_element,
1463 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1464 int max_fieldx, int max_fieldy)
1466 FloodFillLevelExt(from_x, from_y, fill_element,
1467 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1468 max_fieldx, max_fieldy);
1471 void SetRandomAnimationValue(int x, int y)
1473 gfx.anim_random_frame = GfxRandom[x][y];
1476 int getGraphicAnimationFrame(int graphic, int sync_frame)
1478 // animation synchronized with global frame counter, not move position
1479 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1480 sync_frame = FrameCounter;
1482 return getAnimationFrame(graphic_info[graphic].anim_frames,
1483 graphic_info[graphic].anim_delay,
1484 graphic_info[graphic].anim_mode,
1485 graphic_info[graphic].anim_start_frame,
1489 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1491 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1493 struct GraphicInfo *g = &graphic_info[graphic];
1494 int xsize = MAX(1, g->anim_frames_per_line);
1495 int ysize = MAX(1, g->anim_frames / xsize);
1496 int xoffset = g->anim_start_frame % xsize;
1497 int yoffset = g->anim_start_frame % ysize;
1498 // may be needed if screen field is significantly larger than playfield
1499 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1500 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1501 int sync_frame = y * xsize + x;
1503 return sync_frame % g->anim_frames;
1505 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1507 struct GraphicInfo *g = &graphic_info[graphic];
1508 // may be needed if screen field is significantly larger than playfield
1509 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1510 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1511 int sync_frame = GfxRandomStatic[x][y];
1513 return sync_frame % g->anim_frames;
1517 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1519 return getGraphicAnimationFrame(graphic, sync_frame);
1523 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1525 struct GraphicInfo *g = &graphic_info[graphic];
1526 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1528 if (tilesize == gfx.standard_tile_size)
1529 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1530 else if (tilesize == game.tile_size)
1531 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1533 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1536 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1537 boolean get_backside)
1539 struct GraphicInfo *g = &graphic_info[graphic];
1540 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1541 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1543 if (g->offset_y == 0) // frames are ordered horizontally
1545 int max_width = g->anim_frames_per_line * g->width;
1546 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1548 *x = pos % max_width;
1549 *y = src_y % g->height + pos / max_width * g->height;
1551 else if (g->offset_x == 0) // frames are ordered vertically
1553 int max_height = g->anim_frames_per_line * g->height;
1554 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1556 *x = src_x % g->width + pos / max_height * g->width;
1557 *y = pos % max_height;
1559 else // frames are ordered diagonally
1561 *x = src_x + frame * g->offset_x;
1562 *y = src_y + frame * g->offset_y;
1566 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1567 Bitmap **bitmap, int *x, int *y,
1568 boolean get_backside)
1570 struct GraphicInfo *g = &graphic_info[graphic];
1572 // if no graphics defined at all, use fallback graphics
1573 if (g->bitmaps == NULL)
1574 *g = graphic_info[IMG_CHAR_EXCLAM];
1576 // if no in-game graphics defined, always use standard graphic size
1577 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1578 tilesize = TILESIZE;
1580 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1581 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1583 *x = *x * tilesize / g->tile_size;
1584 *y = *y * tilesize / g->tile_size;
1587 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1588 Bitmap **bitmap, int *x, int *y)
1590 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1593 void getFixedGraphicSource(int graphic, int frame,
1594 Bitmap **bitmap, int *x, int *y)
1596 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1599 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1601 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1604 void getGlobalAnimGraphicSource(int graphic, int frame,
1605 Bitmap **bitmap, int *x, int *y)
1607 struct GraphicInfo *g = &graphic_info[graphic];
1609 // if no graphics defined at all, use fallback graphics
1610 if (g->bitmaps == NULL)
1611 *g = graphic_info[IMG_CHAR_EXCLAM];
1613 // use original size graphics, if existing, else use standard size graphics
1614 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1615 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1617 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1619 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1622 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1623 int *x, int *y, boolean get_backside)
1625 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1629 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1631 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1634 void DrawGraphic(int x, int y, int graphic, int frame)
1637 if (!IN_SCR_FIELD(x, y))
1639 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1640 Debug("draw:DrawGraphic", "This should never happen!");
1646 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1649 MarkTileDirty(x, y);
1652 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1655 if (!IN_SCR_FIELD(x, y))
1657 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1659 Debug("draw:DrawFixedGraphic", "This should never happen!");
1665 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1667 MarkTileDirty(x, y);
1670 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1676 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1678 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1681 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1687 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1688 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1691 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1694 if (!IN_SCR_FIELD(x, y))
1696 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1698 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1704 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1707 MarkTileDirty(x, y);
1710 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1713 if (!IN_SCR_FIELD(x, y))
1715 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1717 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1723 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1725 MarkTileDirty(x, y);
1728 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1734 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1736 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1740 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1741 int graphic, int frame)
1746 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1748 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1752 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1754 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1756 MarkTileDirty(x / tilesize, y / tilesize);
1759 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1762 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1763 graphic, frame, tilesize);
1764 MarkTileDirty(x / tilesize, y / tilesize);
1767 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1773 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1774 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1777 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1778 int frame, int tilesize)
1783 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1784 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1787 void DrawMiniGraphic(int x, int y, int graphic)
1789 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1790 MarkTileDirty(x / 2, y / 2);
1793 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1798 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1799 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1802 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1803 int graphic, int frame,
1804 int cut_mode, int mask_mode)
1809 int width = TILEX, height = TILEY;
1812 if (dx || dy) // shifted graphic
1814 if (x < BX1) // object enters playfield from the left
1821 else if (x > BX2) // object enters playfield from the right
1827 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1833 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1835 else if (dx) // general horizontal movement
1836 MarkTileDirty(x + SIGN(dx), y);
1838 if (y < BY1) // object enters playfield from the top
1840 if (cut_mode == CUT_BELOW) // object completely above top border
1848 else if (y > BY2) // object enters playfield from the bottom
1854 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1860 else if (dy > 0 && cut_mode == CUT_ABOVE)
1862 if (y == BY2) // object completely above bottom border
1868 MarkTileDirty(x, y + 1);
1869 } // object leaves playfield to the bottom
1870 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1872 else if (dy) // general vertical movement
1873 MarkTileDirty(x, y + SIGN(dy));
1877 if (!IN_SCR_FIELD(x, y))
1879 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1881 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1887 width = width * TILESIZE_VAR / TILESIZE;
1888 height = height * TILESIZE_VAR / TILESIZE;
1889 cx = cx * TILESIZE_VAR / TILESIZE;
1890 cy = cy * TILESIZE_VAR / TILESIZE;
1891 dx = dx * TILESIZE_VAR / TILESIZE;
1892 dy = dy * TILESIZE_VAR / TILESIZE;
1894 if (width > 0 && height > 0)
1896 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1901 dst_x = FX + x * TILEX_VAR + dx;
1902 dst_y = FY + y * TILEY_VAR + dy;
1904 if (mask_mode == USE_MASKING)
1905 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1908 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1911 MarkTileDirty(x, y);
1915 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1916 int graphic, int frame,
1917 int cut_mode, int mask_mode)
1922 int width = TILEX_VAR, height = TILEY_VAR;
1925 int x2 = x + SIGN(dx);
1926 int y2 = y + SIGN(dy);
1928 // movement with two-tile animations must be sync'ed with movement position,
1929 // not with current GfxFrame (which can be higher when using slow movement)
1930 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1931 int anim_frames = graphic_info[graphic].anim_frames;
1933 // (we also need anim_delay here for movement animations with less frames)
1934 int anim_delay = graphic_info[graphic].anim_delay;
1935 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1937 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1938 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1940 // re-calculate animation frame for two-tile movement animation
1941 frame = getGraphicAnimationFrame(graphic, sync_frame);
1943 // check if movement start graphic inside screen area and should be drawn
1944 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1946 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1948 dst_x = FX + x1 * TILEX_VAR;
1949 dst_y = FY + y1 * TILEY_VAR;
1951 if (mask_mode == USE_MASKING)
1952 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1955 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1958 MarkTileDirty(x1, y1);
1961 // check if movement end graphic inside screen area and should be drawn
1962 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1964 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1966 dst_x = FX + x2 * TILEX_VAR;
1967 dst_y = FY + y2 * TILEY_VAR;
1969 if (mask_mode == USE_MASKING)
1970 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1973 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1976 MarkTileDirty(x2, y2);
1980 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1981 int graphic, int frame,
1982 int cut_mode, int mask_mode)
1986 DrawGraphic(x, y, graphic, frame);
1991 if (graphic_info[graphic].double_movement) // EM style movement images
1992 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1994 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1997 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1998 int graphic, int frame, int cut_mode)
2000 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2003 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2004 int cut_mode, int mask_mode)
2006 int lx = LEVELX(x), ly = LEVELY(y);
2010 if (IN_LEV_FIELD(lx, ly))
2012 if (element == EL_EMPTY)
2013 element = GfxElementEmpty[lx][ly];
2015 SetRandomAnimationValue(lx, ly);
2017 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2018 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2020 // do not use double (EM style) movement graphic when not moving
2021 if (graphic_info[graphic].double_movement && !dx && !dy)
2023 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2024 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2027 if (game.use_masked_elements && (dx || dy))
2028 mask_mode = USE_MASKING;
2030 else // border element
2032 graphic = el2img(element);
2033 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2036 if (element == EL_EXPANDABLE_WALL)
2038 boolean left_stopped = FALSE, right_stopped = FALSE;
2040 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2041 left_stopped = TRUE;
2042 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2043 right_stopped = TRUE;
2045 if (left_stopped && right_stopped)
2047 else if (left_stopped)
2049 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2050 frame = graphic_info[graphic].anim_frames - 1;
2052 else if (right_stopped)
2054 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2055 frame = graphic_info[graphic].anim_frames - 1;
2060 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2061 else if (mask_mode == USE_MASKING)
2062 DrawGraphicThruMask(x, y, graphic, frame);
2064 DrawGraphic(x, y, graphic, frame);
2067 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2068 int cut_mode, int mask_mode)
2070 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2071 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2072 cut_mode, mask_mode);
2075 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2078 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2081 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2084 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2087 void DrawLevelElementThruMask(int x, int y, int element)
2089 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2092 void DrawLevelFieldThruMask(int x, int y)
2094 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2097 // !!! implementation of quicksand is totally broken !!!
2098 #define IS_CRUMBLED_TILE(x, y, e) \
2099 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2100 !IS_MOVING(x, y) || \
2101 (e) == EL_QUICKSAND_EMPTYING || \
2102 (e) == EL_QUICKSAND_FAST_EMPTYING))
2104 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2109 int width, height, cx, cy;
2110 int sx = SCREENX(x), sy = SCREENY(y);
2111 int crumbled_border_size = graphic_info[graphic].border_size;
2112 int crumbled_tile_size = graphic_info[graphic].tile_size;
2113 int crumbled_border_size_var =
2114 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2117 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2119 for (i = 1; i < 4; i++)
2121 int dxx = (i & 1 ? dx : 0);
2122 int dyy = (i & 2 ? dy : 0);
2125 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2128 // check if neighbour field is of same crumble type
2129 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2130 graphic_info[graphic].class ==
2131 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2133 // return if check prevents inner corner
2134 if (same == (dxx == dx && dyy == dy))
2138 // if we reach this point, we have an inner corner
2140 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2142 width = crumbled_border_size_var;
2143 height = crumbled_border_size_var;
2144 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2145 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2147 if (game.use_masked_elements)
2149 int graphic0 = el2img(EL_EMPTY);
2150 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2151 Bitmap *src_bitmap0;
2154 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2156 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2158 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2160 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2162 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2165 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2167 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2170 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2175 int width, height, bx, by, cx, cy;
2176 int sx = SCREENX(x), sy = SCREENY(y);
2177 int crumbled_border_size = graphic_info[graphic].border_size;
2178 int crumbled_tile_size = graphic_info[graphic].tile_size;
2179 int crumbled_border_size_var =
2180 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2181 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2184 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2186 // only needed when using masked elements
2187 int graphic0 = el2img(EL_EMPTY);
2188 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2189 Bitmap *src_bitmap0;
2192 if (game.use_masked_elements)
2193 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2195 // draw simple, sloppy, non-corner-accurate crumbled border
2197 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2198 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2199 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2200 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2202 if (game.use_masked_elements)
2204 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2206 FX + sx * TILEX_VAR + cx,
2207 FY + sy * TILEY_VAR + cy);
2209 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2211 FX + sx * TILEX_VAR + cx,
2212 FY + sy * TILEY_VAR + cy);
2215 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2217 FX + sx * TILEX_VAR + cx,
2218 FY + sy * TILEY_VAR + cy);
2220 // (remaining middle border part must be at least as big as corner part)
2221 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2222 crumbled_border_size_var >= TILESIZE_VAR / 3)
2225 // correct corners of crumbled border, if needed
2227 for (i = -1; i <= 1; i += 2)
2229 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2230 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2231 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2234 // check if neighbour field is of same crumble type
2235 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2236 graphic_info[graphic].class ==
2237 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2239 // no crumbled corner, but continued crumbled border
2241 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2242 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2243 int b1 = (i == 1 ? crumbled_border_size_var :
2244 TILESIZE_VAR - 2 * crumbled_border_size_var);
2246 width = crumbled_border_size_var;
2247 height = crumbled_border_size_var;
2249 if (dir == 1 || dir == 2)
2264 if (game.use_masked_elements)
2266 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2268 FX + sx * TILEX_VAR + cx,
2269 FY + sy * TILEY_VAR + cy);
2271 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2273 FX + sx * TILEX_VAR + cx,
2274 FY + sy * TILEY_VAR + cy);
2277 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2279 FX + sx * TILEX_VAR + cx,
2280 FY + sy * TILEY_VAR + cy);
2285 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2287 int sx = SCREENX(x), sy = SCREENY(y);
2290 static int xy[4][2] =
2298 if (!IN_LEV_FIELD(x, y))
2301 element = TILE_GFX_ELEMENT(x, y);
2303 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2305 if (!IN_SCR_FIELD(sx, sy))
2308 // crumble field borders towards direct neighbour fields
2309 for (i = 0; i < 4; i++)
2311 int xx = x + xy[i][0];
2312 int yy = y + xy[i][1];
2314 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2317 // check if neighbour field is of same crumble type
2318 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2319 graphic_info[graphic].class ==
2320 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2323 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2326 // crumble inner field corners towards corner neighbour fields
2327 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2328 graphic_info[graphic].anim_frames == 2)
2330 for (i = 0; i < 4; i++)
2332 int dx = (i & 1 ? +1 : -1);
2333 int dy = (i & 2 ? +1 : -1);
2335 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2339 MarkTileDirty(sx, sy);
2341 else // center field is not crumbled -- crumble neighbour fields
2343 // crumble field borders of direct neighbour fields
2344 for (i = 0; i < 4; i++)
2346 int xx = x + xy[i][0];
2347 int yy = y + xy[i][1];
2348 int sxx = sx + xy[i][0];
2349 int syy = sy + xy[i][1];
2351 if (!IN_LEV_FIELD(xx, yy) ||
2352 !IN_SCR_FIELD(sxx, syy))
2355 // do not crumble fields that are being digged or snapped
2356 if (Tile[xx][yy] == EL_EMPTY ||
2357 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2360 element = TILE_GFX_ELEMENT(xx, yy);
2362 if (!IS_CRUMBLED_TILE(xx, yy, element))
2365 graphic = el_act2crm(element, ACTION_DEFAULT);
2367 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2369 MarkTileDirty(sxx, syy);
2372 // crumble inner field corners of corner neighbour fields
2373 for (i = 0; i < 4; i++)
2375 int dx = (i & 1 ? +1 : -1);
2376 int dy = (i & 2 ? +1 : -1);
2382 if (!IN_LEV_FIELD(xx, yy) ||
2383 !IN_SCR_FIELD(sxx, syy))
2386 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2389 element = TILE_GFX_ELEMENT(xx, yy);
2391 if (!IS_CRUMBLED_TILE(xx, yy, element))
2394 graphic = el_act2crm(element, ACTION_DEFAULT);
2396 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2397 graphic_info[graphic].anim_frames == 2)
2398 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2400 MarkTileDirty(sxx, syy);
2405 void DrawLevelFieldCrumbled(int x, int y)
2409 if (!IN_LEV_FIELD(x, y))
2412 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2413 GfxElement[x][y] != EL_UNDEFINED &&
2414 GFX_CRUMBLED(GfxElement[x][y]))
2416 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2421 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2423 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2426 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2429 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2430 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2431 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2432 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2433 int sx = SCREENX(x), sy = SCREENY(y);
2435 DrawScreenGraphic(sx, sy, graphic1, frame1);
2436 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2439 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2441 int sx = SCREENX(x), sy = SCREENY(y);
2442 static int xy[4][2] =
2451 // crumble direct neighbour fields (required for field borders)
2452 for (i = 0; i < 4; i++)
2454 int xx = x + xy[i][0];
2455 int yy = y + xy[i][1];
2456 int sxx = sx + xy[i][0];
2457 int syy = sy + xy[i][1];
2459 if (!IN_LEV_FIELD(xx, yy) ||
2460 !IN_SCR_FIELD(sxx, syy) ||
2461 !GFX_CRUMBLED(Tile[xx][yy]) ||
2465 DrawLevelField(xx, yy);
2468 // crumble corner neighbour fields (required for inner field corners)
2469 for (i = 0; i < 4; i++)
2471 int dx = (i & 1 ? +1 : -1);
2472 int dy = (i & 2 ? +1 : -1);
2478 if (!IN_LEV_FIELD(xx, yy) ||
2479 !IN_SCR_FIELD(sxx, syy) ||
2480 !GFX_CRUMBLED(Tile[xx][yy]) ||
2484 int element = TILE_GFX_ELEMENT(xx, yy);
2485 int graphic = el_act2crm(element, ACTION_DEFAULT);
2487 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2488 graphic_info[graphic].anim_frames == 2)
2489 DrawLevelField(xx, yy);
2493 static int getBorderElement(int x, int y)
2497 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2498 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2499 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2500 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2501 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2502 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2503 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2505 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2506 int steel_position = (x == -1 && y == -1 ? 0 :
2507 x == lev_fieldx && y == -1 ? 1 :
2508 x == -1 && y == lev_fieldy ? 2 :
2509 x == lev_fieldx && y == lev_fieldy ? 3 :
2510 x == -1 || x == lev_fieldx ? 4 :
2511 y == -1 || y == lev_fieldy ? 5 : 6);
2513 return border[steel_position][steel_type];
2516 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2518 if (game.use_masked_elements)
2520 if (graphic != el2img(EL_EMPTY))
2521 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2523 DrawGraphicThruMask(x, y, graphic, frame);
2527 DrawGraphic(x, y, graphic, frame);
2531 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2533 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2536 void DrawScreenElement(int x, int y, int element)
2538 int mask_mode = NO_MASKING;
2540 if (game.use_masked_elements)
2542 int lx = LEVELX(x), ly = LEVELY(y);
2544 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2546 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2548 mask_mode = USE_MASKING;
2552 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2553 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2556 void DrawLevelElement(int x, int y, int element)
2558 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2559 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2562 void DrawScreenField(int x, int y)
2564 int lx = LEVELX(x), ly = LEVELY(y);
2565 int element, content;
2567 if (!IN_LEV_FIELD(lx, ly))
2569 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2572 element = getBorderElement(lx, ly);
2574 DrawScreenElement(x, y, element);
2579 element = Tile[lx][ly];
2580 content = Store[lx][ly];
2582 if (IS_MOVING(lx, ly))
2584 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2585 boolean cut_mode = NO_CUTTING;
2587 if (element == EL_QUICKSAND_EMPTYING ||
2588 element == EL_QUICKSAND_FAST_EMPTYING ||
2589 element == EL_MAGIC_WALL_EMPTYING ||
2590 element == EL_BD_MAGIC_WALL_EMPTYING ||
2591 element == EL_DC_MAGIC_WALL_EMPTYING ||
2592 element == EL_AMOEBA_DROPPING)
2593 cut_mode = CUT_ABOVE;
2594 else if (element == EL_QUICKSAND_FILLING ||
2595 element == EL_QUICKSAND_FAST_FILLING ||
2596 element == EL_MAGIC_WALL_FILLING ||
2597 element == EL_BD_MAGIC_WALL_FILLING ||
2598 element == EL_DC_MAGIC_WALL_FILLING)
2599 cut_mode = CUT_BELOW;
2601 if (cut_mode == CUT_ABOVE)
2602 DrawScreenElement(x, y, element);
2604 DrawScreenElement(x, y, EL_EMPTY);
2606 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2608 int dir = MovDir[lx][ly];
2609 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2610 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2612 if (IN_SCR_FIELD(newx, newy))
2613 DrawScreenElement(newx, newy, EL_EMPTY);
2617 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2618 else if (cut_mode == NO_CUTTING)
2619 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2622 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2624 if (cut_mode == CUT_BELOW &&
2625 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2626 DrawLevelElement(lx, ly + 1, element);
2629 if (content == EL_ACID)
2631 int dir = MovDir[lx][ly];
2632 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2633 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2635 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2637 // prevent target field from being drawn again (but without masking)
2638 // (this would happen if target field is scanned after moving element)
2639 Stop[newlx][newly] = TRUE;
2642 else if (IS_BLOCKED(lx, ly))
2647 boolean cut_mode = NO_CUTTING;
2648 int element_old, content_old;
2650 Blocked2Moving(lx, ly, &oldx, &oldy);
2653 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2654 MovDir[oldx][oldy] == MV_RIGHT);
2656 element_old = Tile[oldx][oldy];
2657 content_old = Store[oldx][oldy];
2659 if (element_old == EL_QUICKSAND_EMPTYING ||
2660 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2661 element_old == EL_MAGIC_WALL_EMPTYING ||
2662 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2663 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2664 element_old == EL_AMOEBA_DROPPING)
2665 cut_mode = CUT_ABOVE;
2667 DrawScreenElement(x, y, EL_EMPTY);
2670 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2672 else if (cut_mode == NO_CUTTING)
2673 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2676 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2679 else if (IS_DRAWABLE(element))
2680 DrawScreenElement(x, y, element);
2682 DrawScreenElement(x, y, EL_EMPTY);
2685 void DrawLevelField(int x, int y)
2687 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2688 DrawScreenField(SCREENX(x), SCREENY(y));
2689 else if (IS_MOVING(x, y))
2693 Moving2Blocked(x, y, &newx, &newy);
2694 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2695 DrawScreenField(SCREENX(newx), SCREENY(newy));
2697 else if (IS_BLOCKED(x, y))
2701 Blocked2Moving(x, y, &oldx, &oldy);
2702 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2703 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2707 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2708 int (*el2img_function)(int), boolean masked,
2709 int element_bits_draw)
2711 int element_base = map_mm_wall_element(element);
2712 int element_bits = (IS_DF_WALL(element) ?
2713 element - EL_DF_WALL_START :
2714 IS_MM_WALL(element) ?
2715 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2716 int graphic = el2img_function(element_base);
2717 int tilesize_draw = tilesize / 2;
2722 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2724 for (i = 0; i < 4; i++)
2726 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2727 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2729 if (!(element_bits_draw & (1 << i)))
2732 if (element_bits & (1 << i))
2735 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2736 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2738 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2739 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2744 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2745 tilesize_draw, tilesize_draw);
2750 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2751 boolean masked, int element_bits_draw)
2753 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2754 element, tilesize, el2edimg, masked, element_bits_draw);
2757 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2758 int (*el2img_function)(int))
2760 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2764 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2767 if (IS_MM_WALL(element))
2769 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2770 element, tilesize, el2edimg, masked, 0x000f);
2774 int graphic = el2edimg(element);
2777 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2779 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2783 void DrawSizedElement(int x, int y, int element, int tilesize)
2785 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2788 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2790 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2793 void DrawMiniElement(int x, int y, int element)
2797 graphic = el2edimg(element);
2798 DrawMiniGraphic(x, y, graphic);
2801 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2804 int x = sx + scroll_x, y = sy + scroll_y;
2806 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2807 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2808 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2809 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2811 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2814 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2816 int x = sx + scroll_x, y = sy + scroll_y;
2818 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2819 DrawMiniElement(sx, sy, EL_EMPTY);
2820 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2821 DrawMiniElement(sx, sy, Tile[x][y]);
2823 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2826 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2827 int x, int y, int xsize, int ysize,
2828 int tile_width, int tile_height)
2832 int dst_x = startx + x * tile_width;
2833 int dst_y = starty + y * tile_height;
2834 int width = graphic_info[graphic].width;
2835 int height = graphic_info[graphic].height;
2836 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2837 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2838 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2839 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2840 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2841 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2842 boolean draw_masked = graphic_info[graphic].draw_masked;
2844 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2846 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2848 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2852 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2853 inner_sx + (x - 1) * tile_width % inner_width);
2854 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2855 inner_sy + (y - 1) * tile_height % inner_height);
2858 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2861 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2865 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2866 int x, int y, int xsize, int ysize,
2869 int font_width = getFontWidth(font_nr);
2870 int font_height = getFontHeight(font_nr);
2872 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2873 font_width, font_height);
2876 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2878 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2879 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2880 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2881 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2882 boolean no_delay = (tape.warp_forward);
2883 unsigned int anim_delay = 0;
2884 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2885 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2886 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2887 int font_width = getFontWidth(font_nr);
2888 int font_height = getFontHeight(font_nr);
2889 int max_xsize = level.envelope[envelope_nr].xsize;
2890 int max_ysize = level.envelope[envelope_nr].ysize;
2891 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2892 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2893 int xend = max_xsize;
2894 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2895 int xstep = (xstart < xend ? 1 : 0);
2896 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2898 int end = MAX(xend - xstart, yend - ystart);
2901 for (i = start; i <= end; i++)
2903 int last_frame = end; // last frame of this "for" loop
2904 int x = xstart + i * xstep;
2905 int y = ystart + i * ystep;
2906 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2907 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2908 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2909 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2912 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2914 BlitScreenToBitmap(backbuffer);
2916 SetDrawtoField(DRAW_TO_BACKBUFFER);
2918 for (yy = 0; yy < ysize; yy++)
2919 for (xx = 0; xx < xsize; xx++)
2920 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2922 DrawTextBuffer(sx + font_width, sy + font_height,
2923 level.envelope[envelope_nr].text, font_nr, max_xsize,
2924 xsize - 2, ysize - 2, 0, mask_mode,
2925 level.envelope[envelope_nr].autowrap,
2926 level.envelope[envelope_nr].centered, FALSE);
2928 redraw_mask |= REDRAW_FIELD;
2931 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2934 ClearAutoRepeatKeyEvents();
2937 void ShowEnvelope(int envelope_nr)
2939 int element = EL_ENVELOPE_1 + envelope_nr;
2940 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2941 int sound_opening = element_info[element].sound[ACTION_OPENING];
2942 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2943 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2944 boolean no_delay = (tape.warp_forward);
2945 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2946 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2947 int anim_mode = graphic_info[graphic].anim_mode;
2948 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2949 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2950 boolean overlay_enabled = GetOverlayEnabled();
2952 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2954 SetOverlayEnabled(FALSE);
2957 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2959 if (anim_mode == ANIM_DEFAULT)
2960 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2962 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2965 Delay_WithScreenUpdates(wait_delay_value);
2967 WaitForEventToContinue();
2970 SetOverlayEnabled(overlay_enabled);
2972 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2974 if (anim_mode != ANIM_NONE)
2975 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2977 if (anim_mode == ANIM_DEFAULT)
2978 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2980 game.envelope_active = FALSE;
2982 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2984 redraw_mask |= REDRAW_FIELD;
2988 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2989 int xsize, int ysize)
2991 if (!global.use_envelope_request ||
2992 request.sort_priority <= 0)
2995 if (request.bitmap == NULL ||
2996 xsize > request.xsize ||
2997 ysize > request.ysize)
2999 if (request.bitmap != NULL)
3000 FreeBitmap(request.bitmap);
3002 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3004 SDL_Surface *surface = request.bitmap->surface;
3006 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3007 Fail("SDLGetNativeSurface() failed");
3010 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3012 SDLFreeBitmapTextures(request.bitmap);
3013 SDLCreateBitmapTextures(request.bitmap);
3015 // set envelope request run-time values
3018 request.xsize = xsize;
3019 request.ysize = ysize;
3022 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3024 if (global.use_envelope_request &&
3025 game.request_active_or_moving &&
3026 request.sort_priority > 0 &&
3027 drawing_target == DRAW_TO_SCREEN &&
3028 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3030 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3031 request.sx, request.sy);
3035 static void setRequestBasePosition(int *x, int *y)
3037 int sx_base, sy_base;
3039 if (request.x != -1)
3040 sx_base = request.x;
3041 else if (request.align == ALIGN_LEFT)
3043 else if (request.align == ALIGN_RIGHT)
3044 sx_base = SX + SXSIZE;
3046 sx_base = SX + SXSIZE / 2;
3048 if (request.y != -1)
3049 sy_base = request.y;
3050 else if (request.valign == VALIGN_TOP)
3052 else if (request.valign == VALIGN_BOTTOM)
3053 sy_base = SY + SYSIZE;
3055 sy_base = SY + SYSIZE / 2;
3061 static void setRequestPositionExt(int *x, int *y, int width, int height,
3062 boolean add_border_size)
3064 int border_size = request.border_size;
3065 int sx_base, sy_base;
3068 setRequestBasePosition(&sx_base, &sy_base);
3070 if (request.align == ALIGN_LEFT)
3072 else if (request.align == ALIGN_RIGHT)
3073 sx = sx_base - width;
3075 sx = sx_base - width / 2;
3077 if (request.valign == VALIGN_TOP)
3079 else if (request.valign == VALIGN_BOTTOM)
3080 sy = sy_base - height;
3082 sy = sy_base - height / 2;
3084 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3085 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3087 if (add_border_size)
3097 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3099 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3102 static void DrawEnvelopeRequest(char *text)
3104 char *text_final = text;
3105 char *text_door_style = NULL;
3106 int graphic = IMG_BACKGROUND_REQUEST;
3107 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3108 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3109 int font_nr = FONT_REQUEST;
3110 int font_width = getFontWidth(font_nr);
3111 int font_height = getFontHeight(font_nr);
3112 int border_size = request.border_size;
3113 int line_spacing = request.line_spacing;
3114 int line_height = font_height + line_spacing;
3115 int max_text_width = request.width - 2 * border_size;
3116 int max_text_height = request.height - 2 * border_size;
3117 int line_length = max_text_width / font_width;
3118 int max_lines = max_text_height / line_height;
3119 int text_width = line_length * font_width;
3120 int width = request.width;
3121 int height = request.height;
3122 int tile_size = MAX(request.step_offset, 1);
3123 int x_steps = width / tile_size;
3124 int y_steps = height / tile_size;
3125 int sx_offset = border_size;
3126 int sy_offset = border_size;
3130 if (request.centered)
3131 sx_offset = (request.width - text_width) / 2;
3133 if (request.wrap_single_words && !request.autowrap)
3135 char *src_text_ptr, *dst_text_ptr;
3137 text_door_style = checked_malloc(2 * strlen(text) + 1);
3139 src_text_ptr = text;
3140 dst_text_ptr = text_door_style;
3142 while (*src_text_ptr)
3144 if (*src_text_ptr == ' ' ||
3145 *src_text_ptr == '?' ||
3146 *src_text_ptr == '!')
3147 *dst_text_ptr++ = '\n';
3149 if (*src_text_ptr != ' ')
3150 *dst_text_ptr++ = *src_text_ptr;
3155 *dst_text_ptr = '\0';
3157 text_final = text_door_style;
3160 setRequestPosition(&sx, &sy, FALSE);
3162 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3164 for (y = 0; y < y_steps; y++)
3165 for (x = 0; x < x_steps; x++)
3166 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3167 x, y, x_steps, y_steps,
3168 tile_size, tile_size);
3170 // force DOOR font inside door area
3171 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3173 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3174 line_length, -1, max_lines, line_spacing, mask_mode,
3175 request.autowrap, request.centered, FALSE);
3179 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3180 RedrawGadget(tool_gadget[i]);
3182 // store readily prepared envelope request for later use when animating
3183 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3185 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3187 if (text_door_style)
3188 free(text_door_style);
3191 static void AnimateEnvelopeRequest(int anim_mode, int action)
3193 int graphic = IMG_BACKGROUND_REQUEST;
3194 boolean draw_masked = graphic_info[graphic].draw_masked;
3195 int delay_value_normal = request.step_delay;
3196 int delay_value_fast = delay_value_normal / 2;
3197 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3198 boolean no_delay = (tape.warp_forward);
3199 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3200 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3201 unsigned int anim_delay = 0;
3203 int tile_size = MAX(request.step_offset, 1);
3204 int max_xsize = request.width / tile_size;
3205 int max_ysize = request.height / tile_size;
3206 int max_xsize_inner = max_xsize - 2;
3207 int max_ysize_inner = max_ysize - 2;
3209 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3210 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3211 int xend = max_xsize_inner;
3212 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3213 int xstep = (xstart < xend ? 1 : 0);
3214 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3216 int end = MAX(xend - xstart, yend - ystart);
3219 if (setup.quick_doors)
3226 for (i = start; i <= end; i++)
3228 int last_frame = end; // last frame of this "for" loop
3229 int x = xstart + i * xstep;
3230 int y = ystart + i * ystep;
3231 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3232 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3233 int xsize_size_left = (xsize - 1) * tile_size;
3234 int ysize_size_top = (ysize - 1) * tile_size;
3235 int max_xsize_pos = (max_xsize - 1) * tile_size;
3236 int max_ysize_pos = (max_ysize - 1) * tile_size;
3237 int width = xsize * tile_size;
3238 int height = ysize * tile_size;
3243 setRequestPosition(&src_x, &src_y, FALSE);
3244 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3246 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3248 for (yy = 0; yy < 2; yy++)
3250 for (xx = 0; xx < 2; xx++)
3252 int src_xx = src_x + xx * max_xsize_pos;
3253 int src_yy = src_y + yy * max_ysize_pos;
3254 int dst_xx = dst_x + xx * xsize_size_left;
3255 int dst_yy = dst_y + yy * ysize_size_top;
3256 int xx_size = (xx ? tile_size : xsize_size_left);
3257 int yy_size = (yy ? tile_size : ysize_size_top);
3260 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3261 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3263 BlitBitmap(bitmap_db_store_2, backbuffer,
3264 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3268 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3270 redraw_mask |= REDRAW_FIELD;
3274 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3277 ClearAutoRepeatKeyEvents();
3280 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3282 int graphic = IMG_BACKGROUND_REQUEST;
3283 int sound_opening = SND_REQUEST_OPENING;
3284 int sound_closing = SND_REQUEST_CLOSING;
3285 int anim_mode_1 = request.anim_mode; // (higher priority)
3286 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3287 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3288 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3289 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3291 if (game_status == GAME_MODE_PLAYING)
3292 BlitScreenToBitmap(backbuffer);
3294 SetDrawtoField(DRAW_TO_BACKBUFFER);
3296 // SetDrawBackgroundMask(REDRAW_NONE);
3298 if (action == ACTION_OPENING)
3300 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3302 if (req_state & REQ_ASK)
3304 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3305 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3306 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3307 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3309 else if (req_state & REQ_CONFIRM)
3311 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3312 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3314 else if (req_state & REQ_PLAYER)
3316 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3317 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3318 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3319 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3322 DrawEnvelopeRequest(text);
3325 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3327 if (action == ACTION_OPENING)
3329 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3331 if (anim_mode == ANIM_DEFAULT)
3332 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3334 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3338 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3340 if (anim_mode != ANIM_NONE)
3341 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3343 if (anim_mode == ANIM_DEFAULT)
3344 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3347 game.envelope_active = FALSE;
3349 if (action == ACTION_CLOSING)
3350 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3352 // SetDrawBackgroundMask(last_draw_background_mask);
3354 redraw_mask |= REDRAW_FIELD;
3358 if (action == ACTION_CLOSING &&
3359 game_status == GAME_MODE_PLAYING &&
3360 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3361 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3364 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3366 if (IS_MM_WALL(element))
3368 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3374 int graphic = el2preimg(element);
3376 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3377 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3382 void DrawLevel(int draw_background_mask)
3386 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3387 SetDrawBackgroundMask(draw_background_mask);
3391 for (x = BX1; x <= BX2; x++)
3392 for (y = BY1; y <= BY2; y++)
3393 DrawScreenField(x, y);
3395 redraw_mask |= REDRAW_FIELD;
3398 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3403 for (x = 0; x < size_x; x++)
3404 for (y = 0; y < size_y; y++)
3405 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3407 redraw_mask |= REDRAW_FIELD;
3410 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3414 for (x = 0; x < size_x; x++)
3415 for (y = 0; y < size_y; y++)
3416 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3418 redraw_mask |= REDRAW_FIELD;
3421 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3423 boolean show_level_border = (BorderElement != EL_EMPTY);
3424 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3425 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3426 int tile_size = preview.tile_size;
3427 int preview_width = preview.xsize * tile_size;
3428 int preview_height = preview.ysize * tile_size;
3429 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3430 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3431 int real_preview_width = real_preview_xsize * tile_size;
3432 int real_preview_height = real_preview_ysize * tile_size;
3433 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3434 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3437 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3440 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3442 dst_x += (preview_width - real_preview_width) / 2;
3443 dst_y += (preview_height - real_preview_height) / 2;
3445 for (x = 0; x < real_preview_xsize; x++)
3447 for (y = 0; y < real_preview_ysize; y++)
3449 int lx = from_x + x + (show_level_border ? -1 : 0);
3450 int ly = from_y + y + (show_level_border ? -1 : 0);
3451 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3452 getBorderElement(lx, ly));
3454 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3455 element, tile_size);
3459 redraw_mask |= REDRAW_FIELD;
3462 #define MICROLABEL_EMPTY 0
3463 #define MICROLABEL_LEVEL_NAME 1
3464 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3465 #define MICROLABEL_LEVEL_AUTHOR 3
3466 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3467 #define MICROLABEL_IMPORTED_FROM 5
3468 #define MICROLABEL_IMPORTED_BY_HEAD 6
3469 #define MICROLABEL_IMPORTED_BY 7
3471 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3473 int max_text_width = SXSIZE;
3474 int font_width = getFontWidth(font_nr);
3476 if (pos->align == ALIGN_CENTER)
3477 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3478 else if (pos->align == ALIGN_RIGHT)
3479 max_text_width = pos->x;
3481 max_text_width = SXSIZE - pos->x;
3483 return max_text_width / font_width;
3486 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3488 char label_text[MAX_OUTPUT_LINESIZE + 1];
3489 int max_len_label_text;
3490 int font_nr = pos->font;
3493 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3496 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3497 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3498 mode == MICROLABEL_IMPORTED_BY_HEAD)
3499 font_nr = pos->font_alt;
3501 max_len_label_text = getMaxTextLength(pos, font_nr);
3503 if (pos->size != -1)
3504 max_len_label_text = pos->size;
3506 for (i = 0; i < max_len_label_text; i++)
3507 label_text[i] = ' ';
3508 label_text[max_len_label_text] = '\0';
3510 if (strlen(label_text) > 0)
3511 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3514 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3515 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3516 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3517 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3518 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3519 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3520 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3521 max_len_label_text);
3522 label_text[max_len_label_text] = '\0';
3524 if (strlen(label_text) > 0)
3525 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3527 redraw_mask |= REDRAW_FIELD;
3530 static void DrawPreviewLevelLabel(int mode)
3532 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3535 static void DrawPreviewLevelInfo(int mode)
3537 if (mode == MICROLABEL_LEVEL_NAME)
3538 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3539 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3540 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3543 static void DrawPreviewLevelExt(boolean restart)
3545 static unsigned int scroll_delay = 0;
3546 static unsigned int label_delay = 0;
3547 static int from_x, from_y, scroll_direction;
3548 static int label_state, label_counter;
3549 unsigned int scroll_delay_value = preview.step_delay;
3550 boolean show_level_border = (BorderElement != EL_EMPTY);
3551 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3552 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3559 if (preview.anim_mode == ANIM_CENTERED)
3561 if (level_xsize > preview.xsize)
3562 from_x = (level_xsize - preview.xsize) / 2;
3563 if (level_ysize > preview.ysize)
3564 from_y = (level_ysize - preview.ysize) / 2;
3567 from_x += preview.xoffset;
3568 from_y += preview.yoffset;
3570 scroll_direction = MV_RIGHT;
3574 DrawPreviewLevelPlayfield(from_x, from_y);
3575 DrawPreviewLevelLabel(label_state);
3577 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3578 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3580 // initialize delay counters
3581 ResetDelayCounter(&scroll_delay);
3582 ResetDelayCounter(&label_delay);
3584 if (leveldir_current->name)
3586 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3587 char label_text[MAX_OUTPUT_LINESIZE + 1];
3588 int font_nr = pos->font;
3589 int max_len_label_text = getMaxTextLength(pos, font_nr);
3591 if (pos->size != -1)
3592 max_len_label_text = pos->size;
3594 strncpy(label_text, leveldir_current->name, max_len_label_text);
3595 label_text[max_len_label_text] = '\0';
3597 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3598 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3604 // scroll preview level, if needed
3605 if (preview.anim_mode != ANIM_NONE &&
3606 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3607 DelayReached(&scroll_delay, scroll_delay_value))
3609 switch (scroll_direction)
3614 from_x -= preview.step_offset;
3615 from_x = (from_x < 0 ? 0 : from_x);
3618 scroll_direction = MV_UP;
3622 if (from_x < level_xsize - preview.xsize)
3624 from_x += preview.step_offset;
3625 from_x = (from_x > level_xsize - preview.xsize ?
3626 level_xsize - preview.xsize : from_x);
3629 scroll_direction = MV_DOWN;
3635 from_y -= preview.step_offset;
3636 from_y = (from_y < 0 ? 0 : from_y);
3639 scroll_direction = MV_RIGHT;
3643 if (from_y < level_ysize - preview.ysize)
3645 from_y += preview.step_offset;
3646 from_y = (from_y > level_ysize - preview.ysize ?
3647 level_ysize - preview.ysize : from_y);
3650 scroll_direction = MV_LEFT;
3657 DrawPreviewLevelPlayfield(from_x, from_y);
3660 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3661 // redraw micro level label, if needed
3662 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3663 !strEqual(level.author, ANONYMOUS_NAME) &&
3664 !strEqual(level.author, leveldir_current->name) &&
3665 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3667 int max_label_counter = 23;
3669 if (leveldir_current->imported_from != NULL &&
3670 strlen(leveldir_current->imported_from) > 0)
3671 max_label_counter += 14;
3672 if (leveldir_current->imported_by != NULL &&
3673 strlen(leveldir_current->imported_by) > 0)
3674 max_label_counter += 14;
3676 label_counter = (label_counter + 1) % max_label_counter;
3677 label_state = (label_counter >= 0 && label_counter <= 7 ?
3678 MICROLABEL_LEVEL_NAME :
3679 label_counter >= 9 && label_counter <= 12 ?
3680 MICROLABEL_LEVEL_AUTHOR_HEAD :
3681 label_counter >= 14 && label_counter <= 21 ?
3682 MICROLABEL_LEVEL_AUTHOR :
3683 label_counter >= 23 && label_counter <= 26 ?
3684 MICROLABEL_IMPORTED_FROM_HEAD :
3685 label_counter >= 28 && label_counter <= 35 ?
3686 MICROLABEL_IMPORTED_FROM :
3687 label_counter >= 37 && label_counter <= 40 ?
3688 MICROLABEL_IMPORTED_BY_HEAD :
3689 label_counter >= 42 && label_counter <= 49 ?
3690 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3692 if (leveldir_current->imported_from == NULL &&
3693 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3694 label_state == MICROLABEL_IMPORTED_FROM))
3695 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3696 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3698 DrawPreviewLevelLabel(label_state);
3702 void DrawPreviewPlayers(void)
3704 if (game_status != GAME_MODE_MAIN)
3707 // do not draw preview players if level preview redefined, but players aren't
3708 if (preview.redefined && !menu.main.preview_players.redefined)
3711 boolean player_found[MAX_PLAYERS];
3712 int num_players = 0;
3715 for (i = 0; i < MAX_PLAYERS; i++)
3716 player_found[i] = FALSE;
3718 // check which players can be found in the level (simple approach)
3719 for (x = 0; x < lev_fieldx; x++)
3721 for (y = 0; y < lev_fieldy; y++)
3723 int element = level.field[x][y];
3725 if (IS_PLAYER_ELEMENT(element))
3727 int player_nr = GET_PLAYER_NR(element);
3729 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3731 if (!player_found[player_nr])
3734 player_found[player_nr] = TRUE;
3739 struct TextPosInfo *pos = &menu.main.preview_players;
3740 int tile_size = pos->tile_size;
3741 int border_size = pos->border_size;
3742 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3743 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3744 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3745 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3746 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3747 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3748 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3749 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3750 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3751 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3752 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3753 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3755 // clear area in which the players will be drawn
3756 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3757 max_players_width, max_players_height);
3759 if (!network.enabled && !setup.team_mode)
3762 // only draw players if level is suited for team mode
3763 if (num_players < 2)
3766 // draw all players that were found in the level
3767 for (i = 0; i < MAX_PLAYERS; i++)
3769 if (player_found[i])
3771 int graphic = el2img(EL_PLAYER_1 + i);
3773 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3775 xpos += player_xoffset;
3776 ypos += player_yoffset;
3781 void DrawPreviewLevelInitial(void)
3783 DrawPreviewLevelExt(TRUE);
3784 DrawPreviewPlayers();
3787 void DrawPreviewLevelAnimation(void)
3789 DrawPreviewLevelExt(FALSE);
3792 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3793 int border_size, int font_nr)
3795 int graphic = el2img(EL_PLAYER_1 + player_nr);
3796 int font_height = getFontHeight(font_nr);
3797 int player_height = MAX(tile_size, font_height);
3798 int xoffset_text = tile_size + border_size;
3799 int yoffset_text = (player_height - font_height) / 2;
3800 int yoffset_graphic = (player_height - tile_size) / 2;
3801 char *player_name = getNetworkPlayerName(player_nr + 1);
3803 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3805 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3808 static void DrawNetworkPlayersExt(boolean force)
3810 if (game_status != GAME_MODE_MAIN)
3813 if (!network.connected && !force)
3816 // do not draw network players if level preview redefined, but players aren't
3817 if (preview.redefined && !menu.main.network_players.redefined)
3820 int num_players = 0;
3823 for (i = 0; i < MAX_PLAYERS; i++)
3824 if (stored_player[i].connected_network)
3827 struct TextPosInfo *pos = &menu.main.network_players;
3828 int tile_size = pos->tile_size;
3829 int border_size = pos->border_size;
3830 int xoffset_text = tile_size + border_size;
3831 int font_nr = pos->font;
3832 int font_width = getFontWidth(font_nr);
3833 int font_height = getFontHeight(font_nr);
3834 int player_height = MAX(tile_size, font_height);
3835 int player_yoffset = player_height + border_size;
3836 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3837 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3838 int all_players_height = num_players * player_yoffset - border_size;
3839 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3840 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3841 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3843 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3844 max_players_width, max_players_height);
3846 // first draw local network player ...
3847 for (i = 0; i < MAX_PLAYERS; i++)
3849 if (stored_player[i].connected_network &&
3850 stored_player[i].connected_locally)
3852 char *player_name = getNetworkPlayerName(i + 1);
3853 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3854 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3856 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3858 ypos += player_yoffset;
3862 // ... then draw all other network players
3863 for (i = 0; i < MAX_PLAYERS; i++)
3865 if (stored_player[i].connected_network &&
3866 !stored_player[i].connected_locally)
3868 char *player_name = getNetworkPlayerName(i + 1);
3869 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3870 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3872 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3874 ypos += player_yoffset;
3879 void DrawNetworkPlayers(void)
3881 DrawNetworkPlayersExt(FALSE);
3884 void ClearNetworkPlayers(void)
3886 DrawNetworkPlayersExt(TRUE);
3889 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3890 int graphic, int lx, int ly,
3893 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3895 if (mask_mode == USE_MASKING)
3896 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3898 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3901 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3902 int graphic, int sync_frame, int mask_mode)
3904 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3906 if (mask_mode == USE_MASKING)
3907 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3909 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3912 static void DrawGraphicAnimation(int x, int y, int graphic)
3914 int lx = LEVELX(x), ly = LEVELY(y);
3915 int mask_mode = NO_MASKING;
3917 if (!IN_SCR_FIELD(x, y))
3920 if (game.use_masked_elements)
3922 if (Tile[lx][ly] != EL_EMPTY)
3924 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3926 mask_mode = USE_MASKING;
3930 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3931 graphic, lx, ly, mask_mode);
3933 MarkTileDirty(x, y);
3936 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3938 int lx = LEVELX(x), ly = LEVELY(y);
3939 int mask_mode = NO_MASKING;
3941 if (!IN_SCR_FIELD(x, y))
3944 if (game.use_masked_elements)
3946 if (Tile[lx][ly] != EL_EMPTY)
3948 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3950 mask_mode = USE_MASKING;
3954 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3955 graphic, lx, ly, mask_mode);
3957 MarkTileDirty(x, y);
3960 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3962 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3965 void DrawLevelElementAnimation(int x, int y, int element)
3967 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3969 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3972 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3974 int sx = SCREENX(x), sy = SCREENY(y);
3976 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3979 if (Tile[x][y] == EL_EMPTY)
3980 graphic = el2img(GfxElementEmpty[x][y]);
3982 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3985 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3988 DrawGraphicAnimation(sx, sy, graphic);
3991 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3992 DrawLevelFieldCrumbled(x, y);
3994 if (GFX_CRUMBLED(Tile[x][y]))
3995 DrawLevelFieldCrumbled(x, y);
3999 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4001 int sx = SCREENX(x), sy = SCREENY(y);
4004 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4007 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4009 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4012 DrawGraphicAnimation(sx, sy, graphic);
4014 if (GFX_CRUMBLED(element))
4015 DrawLevelFieldCrumbled(x, y);
4018 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4020 if (player->use_murphy)
4022 // this works only because currently only one player can be "murphy" ...
4023 static int last_horizontal_dir = MV_LEFT;
4024 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4026 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4027 last_horizontal_dir = move_dir;
4029 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4031 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4033 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4039 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4042 static boolean equalGraphics(int graphic1, int graphic2)
4044 struct GraphicInfo *g1 = &graphic_info[graphic1];
4045 struct GraphicInfo *g2 = &graphic_info[graphic2];
4047 return (g1->bitmap == g2->bitmap &&
4048 g1->src_x == g2->src_x &&
4049 g1->src_y == g2->src_y &&
4050 g1->anim_frames == g2->anim_frames &&
4051 g1->anim_delay == g2->anim_delay &&
4052 g1->anim_mode == g2->anim_mode);
4055 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4059 DRAW_PLAYER_STAGE_INIT = 0,
4060 DRAW_PLAYER_STAGE_LAST_FIELD,
4061 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4062 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4063 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4064 DRAW_PLAYER_STAGE_PLAYER,
4066 DRAW_PLAYER_STAGE_PLAYER,
4067 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4069 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4070 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4072 NUM_DRAW_PLAYER_STAGES
4075 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4077 static int static_last_player_graphic[MAX_PLAYERS];
4078 static int static_last_player_frame[MAX_PLAYERS];
4079 static boolean static_player_is_opaque[MAX_PLAYERS];
4080 static boolean draw_player[MAX_PLAYERS];
4081 int pnr = player->index_nr;
4083 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4085 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4086 static_last_player_frame[pnr] = player->Frame;
4087 static_player_is_opaque[pnr] = FALSE;
4089 draw_player[pnr] = TRUE;
4092 if (!draw_player[pnr])
4096 if (!IN_LEV_FIELD(player->jx, player->jy))
4098 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4099 Debug("draw:DrawPlayerExt", "This should never happen!");
4101 draw_player[pnr] = FALSE;
4107 int last_player_graphic = static_last_player_graphic[pnr];
4108 int last_player_frame = static_last_player_frame[pnr];
4109 boolean player_is_opaque = static_player_is_opaque[pnr];
4111 int jx = player->jx;
4112 int jy = player->jy;
4113 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4114 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4115 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4116 int last_jx = (player->is_moving ? jx - dx : jx);
4117 int last_jy = (player->is_moving ? jy - dy : jy);
4118 int next_jx = jx + dx;
4119 int next_jy = jy + dy;
4120 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4121 int sx = SCREENX(jx);
4122 int sy = SCREENY(jy);
4123 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4124 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4125 int element = Tile[jx][jy];
4126 int last_element = Tile[last_jx][last_jy];
4127 int action = (player->is_pushing ? ACTION_PUSHING :
4128 player->is_digging ? ACTION_DIGGING :
4129 player->is_collecting ? ACTION_COLLECTING :
4130 player->is_moving ? ACTION_MOVING :
4131 player->is_snapping ? ACTION_SNAPPING :
4132 player->is_dropping ? ACTION_DROPPING :
4133 player->is_waiting ? player->action_waiting :
4136 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4138 // ------------------------------------------------------------------------
4139 // initialize drawing the player
4140 // ------------------------------------------------------------------------
4142 draw_player[pnr] = FALSE;
4144 // GfxElement[][] is set to the element the player is digging or collecting;
4145 // remove also for off-screen player if the player is not moving anymore
4146 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4147 GfxElement[jx][jy] = EL_UNDEFINED;
4149 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4152 if (element == EL_EXPLOSION)
4155 InitPlayerGfxAnimation(player, action, move_dir);
4157 draw_player[pnr] = TRUE;
4159 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4161 // ------------------------------------------------------------------------
4162 // draw things in the field the player is leaving, if needed
4163 // ------------------------------------------------------------------------
4165 if (!IN_SCR_FIELD(sx, sy))
4166 draw_player[pnr] = FALSE;
4168 if (!player->is_moving)
4171 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4173 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4175 if (last_element == EL_DYNAMITE_ACTIVE ||
4176 last_element == EL_EM_DYNAMITE_ACTIVE ||
4177 last_element == EL_SP_DISK_RED_ACTIVE)
4178 DrawDynamite(last_jx, last_jy);
4180 DrawLevelFieldThruMask(last_jx, last_jy);
4182 else if (last_element == EL_DYNAMITE_ACTIVE ||
4183 last_element == EL_EM_DYNAMITE_ACTIVE ||
4184 last_element == EL_SP_DISK_RED_ACTIVE)
4185 DrawDynamite(last_jx, last_jy);
4187 DrawLevelField(last_jx, last_jy);
4189 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4190 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4192 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4194 // ------------------------------------------------------------------------
4195 // draw things behind the player, if needed
4196 // ------------------------------------------------------------------------
4200 DrawLevelElement(jx, jy, Back[jx][jy]);
4205 if (IS_ACTIVE_BOMB(element))
4207 DrawLevelElement(jx, jy, EL_EMPTY);
4212 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4214 int old_element = GfxElement[jx][jy];
4215 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4216 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4218 if (GFX_CRUMBLED(old_element))
4219 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4221 DrawScreenGraphic(sx, sy, old_graphic, frame);
4223 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4224 static_player_is_opaque[pnr] = TRUE;
4228 GfxElement[jx][jy] = EL_UNDEFINED;
4230 // make sure that pushed elements are drawn with correct frame rate
4231 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4233 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4234 GfxFrame[jx][jy] = player->StepFrame;
4236 DrawLevelField(jx, jy);
4239 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4241 // ------------------------------------------------------------------------
4242 // draw things the player is pushing, if needed
4243 // ------------------------------------------------------------------------
4245 if (!player->is_pushing || !player->is_moving)
4248 int gfx_frame = GfxFrame[jx][jy];
4250 if (!IS_MOVING(jx, jy)) // push movement already finished
4252 element = Tile[next_jx][next_jy];
4253 gfx_frame = GfxFrame[next_jx][next_jy];
4256 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4257 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4258 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4260 // draw background element under pushed element (like the Sokoban field)
4261 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4263 // this allows transparent pushing animation over non-black background
4266 DrawLevelElement(jx, jy, Back[jx][jy]);
4268 DrawLevelElement(jx, jy, EL_EMPTY);
4270 if (Back[next_jx][next_jy])
4271 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4273 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4275 else if (Back[next_jx][next_jy])
4276 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4278 int px = SCREENX(jx), py = SCREENY(jy);
4279 int pxx = (TILEX - ABS(sxx)) * dx;
4280 int pyy = (TILEY - ABS(syy)) * dy;
4283 // do not draw (EM style) pushing animation when pushing is finished
4284 // (two-tile animations usually do not contain start and end frame)
4285 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4286 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4288 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4290 // masked drawing is needed for EMC style (double) movement graphics
4291 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4292 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4295 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4297 // ------------------------------------------------------------------------
4298 // draw player himself
4299 // ------------------------------------------------------------------------
4301 int graphic = getPlayerGraphic(player, move_dir);
4303 // in the case of changed player action or direction, prevent the current
4304 // animation frame from being restarted for identical animations
4305 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4306 player->Frame = last_player_frame;
4308 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4310 if (player_is_opaque)
4311 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4313 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4315 if (SHIELD_ON(player))
4317 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4318 IMG_SHIELD_NORMAL_ACTIVE);
4319 frame = getGraphicAnimationFrame(graphic, -1);
4321 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4324 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4326 // ------------------------------------------------------------------------
4327 // draw things in front of player (active dynamite or dynabombs)
4328 // ------------------------------------------------------------------------
4330 if (IS_ACTIVE_BOMB(element))
4332 int graphic = el2img(element);
4333 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4335 if (game.emulation == EMU_SUPAPLEX)
4336 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4338 DrawGraphicThruMask(sx, sy, graphic, frame);
4341 if (player_is_moving && last_element == EL_EXPLOSION)
4343 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4344 GfxElement[last_jx][last_jy] : EL_EMPTY);
4345 int graphic = el_act2img(element, ACTION_EXPLODING);
4346 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4347 int phase = ExplodePhase[last_jx][last_jy] - 1;
4348 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4351 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4354 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4356 // ------------------------------------------------------------------------
4357 // draw elements the player is just walking/passing through/under
4358 // ------------------------------------------------------------------------
4360 if (player_is_moving)
4362 // handle the field the player is leaving ...
4363 if (IS_ACCESSIBLE_INSIDE(last_element))
4364 DrawLevelField(last_jx, last_jy);
4365 else if (IS_ACCESSIBLE_UNDER(last_element))
4366 DrawLevelFieldThruMask(last_jx, last_jy);
4369 // do not redraw accessible elements if the player is just pushing them
4370 if (!player_is_moving || !player->is_pushing)
4372 // ... and the field the player is entering
4373 if (IS_ACCESSIBLE_INSIDE(element))
4374 DrawLevelField(jx, jy);
4375 else if (IS_ACCESSIBLE_UNDER(element))
4376 DrawLevelFieldThruMask(jx, jy);
4379 MarkTileDirty(sx, sy);
4383 void DrawPlayer(struct PlayerInfo *player)
4387 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4388 DrawPlayerExt(player, i);
4391 void DrawAllPlayers(void)
4395 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4396 for (j = 0; j < MAX_PLAYERS; j++)
4397 if (stored_player[j].active)
4398 DrawPlayerExt(&stored_player[j], i);
4401 void DrawPlayerField(int x, int y)
4403 if (!IS_PLAYER(x, y))
4406 DrawPlayer(PLAYERINFO(x, y));
4409 // ----------------------------------------------------------------------------
4411 void WaitForEventToContinue(void)
4413 boolean first_wait = TRUE;
4414 boolean still_wait = TRUE;
4416 if (program.headless)
4419 // simulate releasing mouse button over last gadget, if still pressed
4421 HandleGadgets(-1, -1, 0);
4423 button_status = MB_RELEASED;
4426 ClearPlayerAction();
4432 if (NextValidEvent(&event))
4436 case EVENT_BUTTONPRESS:
4437 case EVENT_FINGERPRESS:
4441 case EVENT_BUTTONRELEASE:
4442 case EVENT_FINGERRELEASE:
4443 still_wait = first_wait;
4446 case EVENT_KEYPRESS:
4447 case SDL_CONTROLLERBUTTONDOWN:
4448 case SDL_JOYBUTTONDOWN:
4453 HandleOtherEvents(&event);
4457 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4462 if (!PendingEvent())
4467 #define MAX_REQUEST_LINES 13
4468 #define MAX_REQUEST_LINE_FONT1_LEN 7
4469 #define MAX_REQUEST_LINE_FONT2_LEN 10
4471 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4473 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4475 int draw_buffer_last = GetDrawtoField();
4476 int width = request.width;
4477 int height = request.height;
4481 // when showing request dialog after game ended, deactivate game panel
4482 if (game_just_ended)
4483 game.panel.active = FALSE;
4485 game.request_active = TRUE;
4487 setRequestPosition(&sx, &sy, FALSE);
4489 button_status = MB_RELEASED;
4491 request_gadget_id = -1;
4496 boolean event_handled = FALSE;
4498 if (game_just_ended)
4500 SetDrawtoField(draw_buffer_game);
4502 HandleGameActions();
4504 SetDrawtoField(DRAW_TO_BACKBUFFER);
4506 if (global.use_envelope_request)
4508 // copy current state of request area to middle of playfield area
4509 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4517 while (NextValidEvent(&event))
4519 event_handled = TRUE;
4523 case EVENT_BUTTONPRESS:
4524 case EVENT_BUTTONRELEASE:
4525 case EVENT_MOTIONNOTIFY:
4529 if (event.type == EVENT_MOTIONNOTIFY)
4534 motion_status = TRUE;
4535 mx = ((MotionEvent *) &event)->x;
4536 my = ((MotionEvent *) &event)->y;
4540 motion_status = FALSE;
4541 mx = ((ButtonEvent *) &event)->x;
4542 my = ((ButtonEvent *) &event)->y;
4543 if (event.type == EVENT_BUTTONPRESS)
4544 button_status = ((ButtonEvent *) &event)->button;
4546 button_status = MB_RELEASED;
4549 // this sets 'request_gadget_id'
4550 HandleGadgets(mx, my, button_status);
4552 switch (request_gadget_id)
4554 case TOOL_CTRL_ID_YES:
4555 case TOOL_CTRL_ID_TOUCH_YES:
4558 case TOOL_CTRL_ID_NO:
4559 case TOOL_CTRL_ID_TOUCH_NO:
4562 case TOOL_CTRL_ID_CONFIRM:
4563 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4564 result = TRUE | FALSE;
4567 case TOOL_CTRL_ID_PLAYER_1:
4570 case TOOL_CTRL_ID_PLAYER_2:
4573 case TOOL_CTRL_ID_PLAYER_3:
4576 case TOOL_CTRL_ID_PLAYER_4:
4581 // only check clickable animations if no request gadget clicked
4582 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4589 case SDL_WINDOWEVENT:
4590 HandleWindowEvent((WindowEvent *) &event);
4593 case SDL_APP_WILLENTERBACKGROUND:
4594 case SDL_APP_DIDENTERBACKGROUND:
4595 case SDL_APP_WILLENTERFOREGROUND:
4596 case SDL_APP_DIDENTERFOREGROUND:
4597 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4600 case EVENT_KEYPRESS:
4602 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4607 if (req_state & REQ_CONFIRM)
4616 #if defined(KSYM_Rewind)
4617 case KSYM_Rewind: // for Amazon Fire TV remote
4626 #if defined(KSYM_FastForward)
4627 case KSYM_FastForward: // for Amazon Fire TV remote
4633 HandleKeysDebug(key, KEY_PRESSED);
4637 if (req_state & REQ_PLAYER)
4639 int old_player_nr = setup.network_player_nr;
4642 result = old_player_nr + 1;
4647 result = old_player_nr + 1;
4678 case EVENT_FINGERRELEASE:
4679 case EVENT_KEYRELEASE:
4680 ClearPlayerAction();
4683 case SDL_CONTROLLERBUTTONDOWN:
4684 switch (event.cbutton.button)
4686 case SDL_CONTROLLER_BUTTON_A:
4687 case SDL_CONTROLLER_BUTTON_X:
4688 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4689 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4693 case SDL_CONTROLLER_BUTTON_B:
4694 case SDL_CONTROLLER_BUTTON_Y:
4695 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4696 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4697 case SDL_CONTROLLER_BUTTON_BACK:
4702 if (req_state & REQ_PLAYER)
4704 int old_player_nr = setup.network_player_nr;
4707 result = old_player_nr + 1;
4709 switch (event.cbutton.button)
4711 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4712 case SDL_CONTROLLER_BUTTON_Y:
4716 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4717 case SDL_CONTROLLER_BUTTON_B:
4721 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4722 case SDL_CONTROLLER_BUTTON_A:
4726 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4727 case SDL_CONTROLLER_BUTTON_X:
4738 case SDL_CONTROLLERBUTTONUP:
4739 HandleJoystickEvent(&event);
4740 ClearPlayerAction();
4744 HandleOtherEvents(&event);
4749 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4751 int joy = AnyJoystick();
4753 if (joy & JOY_BUTTON_1)
4755 else if (joy & JOY_BUTTON_2)
4758 else if (AnyJoystick())
4760 int joy = AnyJoystick();
4762 if (req_state & REQ_PLAYER)
4766 else if (joy & JOY_RIGHT)
4768 else if (joy & JOY_DOWN)
4770 else if (joy & JOY_LEFT)
4777 if (game_just_ended)
4779 if (global.use_envelope_request)
4781 // copy back current state of pressed buttons inside request area
4782 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4786 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4792 SetDrawtoField(draw_buffer_last);
4794 game.request_active = FALSE;
4799 static boolean RequestDoor(char *text, unsigned int req_state)
4801 int draw_buffer_last = GetDrawtoField();
4802 unsigned int old_door_state;
4803 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4804 int font_nr = FONT_TEXT_2;
4809 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4811 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4812 font_nr = FONT_TEXT_1;
4815 if (game_status == GAME_MODE_PLAYING)
4816 BlitScreenToBitmap(backbuffer);
4818 // disable deactivated drawing when quick-loading level tape recording
4819 if (tape.playing && tape.deactivate_display)
4820 TapeDeactivateDisplayOff(TRUE);
4822 SetMouseCursor(CURSOR_DEFAULT);
4824 // pause network game while waiting for request to answer
4825 if (network.enabled &&
4826 game_status == GAME_MODE_PLAYING &&
4827 !game.all_players_gone &&
4828 req_state & REQUEST_WAIT_FOR_INPUT)
4829 SendToServer_PausePlaying();
4831 old_door_state = GetDoorState();
4833 // simulate releasing mouse button over last gadget, if still pressed
4835 HandleGadgets(-1, -1, 0);
4839 // draw released gadget before proceeding
4842 if (old_door_state & DOOR_OPEN_1)
4844 CloseDoor(DOOR_CLOSE_1);
4846 // save old door content
4847 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4848 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4851 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4852 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4854 // clear door drawing field
4855 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4857 // force DOOR font inside door area
4858 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4860 // write text for request
4861 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4863 char text_line[max_request_line_len + 1];
4869 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4871 tc = *(text_ptr + tx);
4872 // if (!tc || tc == ' ')
4873 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4877 if ((tc == '?' || tc == '!') && tl == 0)
4887 strncpy(text_line, text_ptr, tl);
4890 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4891 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4892 text_line, font_nr);
4894 text_ptr += tl + (tc == ' ' ? 1 : 0);
4895 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4900 if (req_state & REQ_ASK)
4902 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4903 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4904 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4905 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4907 else if (req_state & REQ_CONFIRM)
4909 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4910 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4912 else if (req_state & REQ_PLAYER)
4914 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4915 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4916 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4917 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4920 // copy request gadgets to door backbuffer
4921 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4923 OpenDoor(DOOR_OPEN_1);
4925 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4927 if (game_status == GAME_MODE_PLAYING)
4929 SetPanelBackground();
4930 SetDrawBackgroundMask(REDRAW_DOOR_1);
4934 SetDrawBackgroundMask(REDRAW_FIELD);
4940 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4942 // ---------- handle request buttons ----------
4943 result = RequestHandleEvents(req_state, draw_buffer_last);
4947 if (!(req_state & REQ_STAY_OPEN))
4949 CloseDoor(DOOR_CLOSE_1);
4951 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4952 (req_state & REQ_REOPEN))
4953 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4958 if (game_status == GAME_MODE_PLAYING)
4960 SetPanelBackground();
4961 SetDrawBackgroundMask(REDRAW_DOOR_1);
4965 SetDrawBackgroundMask(REDRAW_FIELD);
4968 // continue network game after request
4969 if (network.enabled &&
4970 game_status == GAME_MODE_PLAYING &&
4971 !game.all_players_gone &&
4972 req_state & REQUEST_WAIT_FOR_INPUT)
4973 SendToServer_ContinuePlaying();
4975 // restore deactivated drawing when quick-loading level tape recording
4976 if (tape.playing && tape.deactivate_display)
4977 TapeDeactivateDisplayOn();
4982 static boolean RequestEnvelope(char *text, unsigned int req_state)
4984 int draw_buffer_last = GetDrawtoField();
4987 if (game_status == GAME_MODE_PLAYING)
4988 BlitScreenToBitmap(backbuffer);
4990 // disable deactivated drawing when quick-loading level tape recording
4991 if (tape.playing && tape.deactivate_display)
4992 TapeDeactivateDisplayOff(TRUE);
4994 SetMouseCursor(CURSOR_DEFAULT);
4996 // pause network game while waiting for request to answer
4997 if (network.enabled &&
4998 game_status == GAME_MODE_PLAYING &&
4999 !game.all_players_gone &&
5000 req_state & REQUEST_WAIT_FOR_INPUT)
5001 SendToServer_PausePlaying();
5003 // simulate releasing mouse button over last gadget, if still pressed
5005 HandleGadgets(-1, -1, 0);
5009 // (replace with setting corresponding request background)
5010 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5011 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5013 // clear door drawing field
5014 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5016 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5018 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5020 if (game_status == GAME_MODE_PLAYING)
5022 SetPanelBackground();
5023 SetDrawBackgroundMask(REDRAW_DOOR_1);
5027 SetDrawBackgroundMask(REDRAW_FIELD);
5033 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5035 // ---------- handle request buttons ----------
5036 result = RequestHandleEvents(req_state, draw_buffer_last);
5040 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5044 if (game_status == GAME_MODE_PLAYING)
5046 SetPanelBackground();
5047 SetDrawBackgroundMask(REDRAW_DOOR_1);
5051 SetDrawBackgroundMask(REDRAW_FIELD);
5054 // continue network game after request
5055 if (network.enabled &&
5056 game_status == GAME_MODE_PLAYING &&
5057 !game.all_players_gone &&
5058 req_state & REQUEST_WAIT_FOR_INPUT)
5059 SendToServer_ContinuePlaying();
5061 // restore deactivated drawing when quick-loading level tape recording
5062 if (tape.playing && tape.deactivate_display)
5063 TapeDeactivateDisplayOn();
5068 boolean Request(char *text, unsigned int req_state)
5070 boolean overlay_enabled = GetOverlayEnabled();
5073 game.request_active_or_moving = TRUE;
5075 SetOverlayEnabled(FALSE);
5077 if (global.use_envelope_request)
5078 result = RequestEnvelope(text, req_state);
5080 result = RequestDoor(text, req_state);
5082 SetOverlayEnabled(overlay_enabled);
5084 game.request_active_or_moving = FALSE;
5089 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5091 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5092 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5095 if (dpo1->sort_priority != dpo2->sort_priority)
5096 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5098 compare_result = dpo1->nr - dpo2->nr;
5100 return compare_result;
5103 void InitGraphicCompatibilityInfo_Doors(void)
5109 struct DoorInfo *door;
5113 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5114 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5116 { -1, -1, -1, NULL }
5118 struct Rect door_rect_list[] =
5120 { DX, DY, DXSIZE, DYSIZE },
5121 { VX, VY, VXSIZE, VYSIZE }
5125 for (i = 0; doors[i].door_token != -1; i++)
5127 int door_token = doors[i].door_token;
5128 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5129 int part_1 = doors[i].part_1;
5130 int part_8 = doors[i].part_8;
5131 int part_2 = part_1 + 1;
5132 int part_3 = part_1 + 2;
5133 struct DoorInfo *door = doors[i].door;
5134 struct Rect *door_rect = &door_rect_list[door_index];
5135 boolean door_gfx_redefined = FALSE;
5137 // check if any door part graphic definitions have been redefined
5139 for (j = 0; door_part_controls[j].door_token != -1; j++)
5141 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5142 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5144 if (dpc->door_token == door_token && fi->redefined)
5145 door_gfx_redefined = TRUE;
5148 // check for old-style door graphic/animation modifications
5150 if (!door_gfx_redefined)
5152 if (door->anim_mode & ANIM_STATIC_PANEL)
5154 door->panel.step_xoffset = 0;
5155 door->panel.step_yoffset = 0;
5158 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5160 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5161 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5162 int num_door_steps, num_panel_steps;
5164 // remove door part graphics other than the two default wings
5166 for (j = 0; door_part_controls[j].door_token != -1; j++)
5168 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5169 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5171 if (dpc->graphic >= part_3 &&
5172 dpc->graphic <= part_8)
5176 // set graphics and screen positions of the default wings
5178 g_part_1->width = door_rect->width;
5179 g_part_1->height = door_rect->height;
5180 g_part_2->width = door_rect->width;
5181 g_part_2->height = door_rect->height;
5182 g_part_2->src_x = door_rect->width;
5183 g_part_2->src_y = g_part_1->src_y;
5185 door->part_2.x = door->part_1.x;
5186 door->part_2.y = door->part_1.y;
5188 if (door->width != -1)
5190 g_part_1->width = door->width;
5191 g_part_2->width = door->width;
5193 // special treatment for graphics and screen position of right wing
5194 g_part_2->src_x += door_rect->width - door->width;
5195 door->part_2.x += door_rect->width - door->width;
5198 if (door->height != -1)
5200 g_part_1->height = door->height;
5201 g_part_2->height = door->height;
5203 // special treatment for graphics and screen position of bottom wing
5204 g_part_2->src_y += door_rect->height - door->height;
5205 door->part_2.y += door_rect->height - door->height;
5208 // set animation delays for the default wings and panels
5210 door->part_1.step_delay = door->step_delay;
5211 door->part_2.step_delay = door->step_delay;
5212 door->panel.step_delay = door->step_delay;
5214 // set animation draw order for the default wings
5216 door->part_1.sort_priority = 2; // draw left wing over ...
5217 door->part_2.sort_priority = 1; // ... right wing
5219 // set animation draw offset for the default wings
5221 if (door->anim_mode & ANIM_HORIZONTAL)
5223 door->part_1.step_xoffset = door->step_offset;
5224 door->part_1.step_yoffset = 0;
5225 door->part_2.step_xoffset = door->step_offset * -1;
5226 door->part_2.step_yoffset = 0;
5228 num_door_steps = g_part_1->width / door->step_offset;
5230 else // ANIM_VERTICAL
5232 door->part_1.step_xoffset = 0;
5233 door->part_1.step_yoffset = door->step_offset;
5234 door->part_2.step_xoffset = 0;
5235 door->part_2.step_yoffset = door->step_offset * -1;
5237 num_door_steps = g_part_1->height / door->step_offset;
5240 // set animation draw offset for the default panels
5242 if (door->step_offset > 1)
5244 num_panel_steps = 2 * door_rect->height / door->step_offset;
5245 door->panel.start_step = num_panel_steps - num_door_steps;
5246 door->panel.start_step_closing = door->panel.start_step;
5250 num_panel_steps = door_rect->height / door->step_offset;
5251 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5252 door->panel.start_step_closing = door->panel.start_step;
5253 door->panel.step_delay *= 2;
5260 void InitDoors(void)
5264 for (i = 0; door_part_controls[i].door_token != -1; i++)
5266 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5267 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5269 // initialize "start_step_opening" and "start_step_closing", if needed
5270 if (dpc->pos->start_step_opening == 0 &&
5271 dpc->pos->start_step_closing == 0)
5273 // dpc->pos->start_step_opening = dpc->pos->start_step;
5274 dpc->pos->start_step_closing = dpc->pos->start_step;
5277 // fill structure for door part draw order (sorted below)
5279 dpo->sort_priority = dpc->pos->sort_priority;
5282 // sort door part controls according to sort_priority and graphic number
5283 qsort(door_part_order, MAX_DOOR_PARTS,
5284 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5287 unsigned int OpenDoor(unsigned int door_state)
5289 if (door_state & DOOR_COPY_BACK)
5291 if (door_state & DOOR_OPEN_1)
5292 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5293 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5295 if (door_state & DOOR_OPEN_2)
5296 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5297 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5299 door_state &= ~DOOR_COPY_BACK;
5302 return MoveDoor(door_state);
5305 unsigned int CloseDoor(unsigned int door_state)
5307 unsigned int old_door_state = GetDoorState();
5309 if (!(door_state & DOOR_NO_COPY_BACK))
5311 if (old_door_state & DOOR_OPEN_1)
5312 BlitBitmap(backbuffer, bitmap_db_door_1,
5313 DX, DY, DXSIZE, DYSIZE, 0, 0);
5315 if (old_door_state & DOOR_OPEN_2)
5316 BlitBitmap(backbuffer, bitmap_db_door_2,
5317 VX, VY, VXSIZE, VYSIZE, 0, 0);
5319 door_state &= ~DOOR_NO_COPY_BACK;
5322 return MoveDoor(door_state);
5325 unsigned int GetDoorState(void)
5327 return MoveDoor(DOOR_GET_STATE);
5330 unsigned int SetDoorState(unsigned int door_state)
5332 return MoveDoor(door_state | DOOR_SET_STATE);
5335 static int euclid(int a, int b)
5337 return (b ? euclid(b, a % b) : a);
5340 unsigned int MoveDoor(unsigned int door_state)
5342 struct Rect door_rect_list[] =
5344 { DX, DY, DXSIZE, DYSIZE },
5345 { VX, VY, VXSIZE, VYSIZE }
5347 static int door1 = DOOR_CLOSE_1;
5348 static int door2 = DOOR_CLOSE_2;
5349 unsigned int door_delay = 0;
5350 unsigned int door_delay_value;
5353 if (door_state == DOOR_GET_STATE)
5354 return (door1 | door2);
5356 if (door_state & DOOR_SET_STATE)
5358 if (door_state & DOOR_ACTION_1)
5359 door1 = door_state & DOOR_ACTION_1;
5360 if (door_state & DOOR_ACTION_2)
5361 door2 = door_state & DOOR_ACTION_2;
5363 return (door1 | door2);
5366 if (!(door_state & DOOR_FORCE_REDRAW))
5368 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5369 door_state &= ~DOOR_OPEN_1;
5370 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5371 door_state &= ~DOOR_CLOSE_1;
5372 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5373 door_state &= ~DOOR_OPEN_2;
5374 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5375 door_state &= ~DOOR_CLOSE_2;
5378 if (global.autoplay_leveldir)
5380 door_state |= DOOR_NO_DELAY;
5381 door_state &= ~DOOR_CLOSE_ALL;
5384 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5385 door_state |= DOOR_NO_DELAY;
5387 if (door_state & DOOR_ACTION)
5389 boolean door_panel_drawn[NUM_DOORS];
5390 boolean panel_has_doors[NUM_DOORS];
5391 boolean door_part_skip[MAX_DOOR_PARTS];
5392 boolean door_part_done[MAX_DOOR_PARTS];
5393 boolean door_part_done_all;
5394 int num_steps[MAX_DOOR_PARTS];
5395 int max_move_delay = 0; // delay for complete animations of all doors
5396 int max_step_delay = 0; // delay (ms) between two animation frames
5397 int num_move_steps = 0; // number of animation steps for all doors
5398 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5399 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5403 for (i = 0; i < NUM_DOORS; i++)
5404 panel_has_doors[i] = FALSE;
5406 for (i = 0; i < MAX_DOOR_PARTS; i++)
5408 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5409 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5410 int door_token = dpc->door_token;
5412 door_part_done[i] = FALSE;
5413 door_part_skip[i] = (!(door_state & door_token) ||
5417 for (i = 0; i < MAX_DOOR_PARTS; i++)
5419 int nr = door_part_order[i].nr;
5420 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5421 struct DoorPartPosInfo *pos = dpc->pos;
5422 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5423 int door_token = dpc->door_token;
5424 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5425 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5426 int step_xoffset = ABS(pos->step_xoffset);
5427 int step_yoffset = ABS(pos->step_yoffset);
5428 int step_delay = pos->step_delay;
5429 int current_door_state = door_state & door_token;
5430 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5431 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5432 boolean part_opening = (is_panel ? door_closing : door_opening);
5433 int start_step = (part_opening ? pos->start_step_opening :
5434 pos->start_step_closing);
5435 float move_xsize = (step_xoffset ? g->width : 0);
5436 float move_ysize = (step_yoffset ? g->height : 0);
5437 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5438 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5439 int move_steps = (move_xsteps && move_ysteps ?
5440 MIN(move_xsteps, move_ysteps) :
5441 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5442 int move_delay = move_steps * step_delay;
5444 if (door_part_skip[nr])
5447 max_move_delay = MAX(max_move_delay, move_delay);
5448 max_step_delay = (max_step_delay == 0 ? step_delay :
5449 euclid(max_step_delay, step_delay));
5450 num_steps[nr] = move_steps;
5454 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5456 panel_has_doors[door_index] = TRUE;
5460 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5462 num_move_steps = max_move_delay / max_step_delay;
5463 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5465 door_delay_value = max_step_delay;
5467 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5469 start = num_move_steps - 1;
5473 // opening door sound has priority over simultaneously closing door
5474 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5476 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5478 if (door_state & DOOR_OPEN_1)
5479 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5480 if (door_state & DOOR_OPEN_2)
5481 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5483 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5485 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5487 if (door_state & DOOR_CLOSE_1)
5488 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5489 if (door_state & DOOR_CLOSE_2)
5490 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5494 for (k = start; k < num_move_steps; k++)
5496 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5498 door_part_done_all = TRUE;
5500 for (i = 0; i < NUM_DOORS; i++)
5501 door_panel_drawn[i] = FALSE;
5503 for (i = 0; i < MAX_DOOR_PARTS; i++)
5505 int nr = door_part_order[i].nr;
5506 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5507 struct DoorPartPosInfo *pos = dpc->pos;
5508 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5509 int door_token = dpc->door_token;
5510 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5511 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5512 boolean is_panel_and_door_has_closed = FALSE;
5513 struct Rect *door_rect = &door_rect_list[door_index];
5514 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5516 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5517 int current_door_state = door_state & door_token;
5518 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5519 boolean door_closing = !door_opening;
5520 boolean part_opening = (is_panel ? door_closing : door_opening);
5521 boolean part_closing = !part_opening;
5522 int start_step = (part_opening ? pos->start_step_opening :
5523 pos->start_step_closing);
5524 int step_delay = pos->step_delay;
5525 int step_factor = step_delay / max_step_delay;
5526 int k1 = (step_factor ? k / step_factor + 1 : k);
5527 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5528 int kk = MAX(0, k2);
5531 int src_x, src_y, src_xx, src_yy;
5532 int dst_x, dst_y, dst_xx, dst_yy;
5535 if (door_part_skip[nr])
5538 if (!(door_state & door_token))
5546 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5547 int kk_door = MAX(0, k2_door);
5548 int sync_frame = kk_door * door_delay_value;
5549 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5551 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5552 &g_src_x, &g_src_y);
5557 if (!door_panel_drawn[door_index])
5559 ClearRectangle(drawto, door_rect->x, door_rect->y,
5560 door_rect->width, door_rect->height);
5562 door_panel_drawn[door_index] = TRUE;
5565 // draw opening or closing door parts
5567 if (pos->step_xoffset < 0) // door part on right side
5570 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5573 if (dst_xx + width > door_rect->width)
5574 width = door_rect->width - dst_xx;
5576 else // door part on left side
5579 dst_xx = pos->x - kk * pos->step_xoffset;
5583 src_xx = ABS(dst_xx);
5587 width = g->width - src_xx;
5589 if (width > door_rect->width)
5590 width = door_rect->width;
5592 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5595 if (pos->step_yoffset < 0) // door part on bottom side
5598 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5601 if (dst_yy + height > door_rect->height)
5602 height = door_rect->height - dst_yy;
5604 else // door part on top side
5607 dst_yy = pos->y - kk * pos->step_yoffset;
5611 src_yy = ABS(dst_yy);
5615 height = g->height - src_yy;
5618 src_x = g_src_x + src_xx;
5619 src_y = g_src_y + src_yy;
5621 dst_x = door_rect->x + dst_xx;
5622 dst_y = door_rect->y + dst_yy;
5624 is_panel_and_door_has_closed =
5627 panel_has_doors[door_index] &&
5628 k >= num_move_steps_doors_only - 1);
5630 if (width >= 0 && width <= g->width &&
5631 height >= 0 && height <= g->height &&
5632 !is_panel_and_door_has_closed)
5634 if (is_panel || !pos->draw_masked)
5635 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5638 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5642 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5644 if ((part_opening && (width < 0 || height < 0)) ||
5645 (part_closing && (width >= g->width && height >= g->height)))
5646 door_part_done[nr] = TRUE;
5648 // continue door part animations, but not panel after door has closed
5649 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5650 door_part_done_all = FALSE;
5653 if (!(door_state & DOOR_NO_DELAY))
5657 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5659 // prevent OS (Windows) from complaining about program not responding
5663 if (door_part_done_all)
5667 if (!(door_state & DOOR_NO_DELAY))
5669 // wait for specified door action post delay
5670 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5671 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5672 else if (door_state & DOOR_ACTION_1)
5673 door_delay_value = door_1.post_delay;
5674 else if (door_state & DOOR_ACTION_2)
5675 door_delay_value = door_2.post_delay;
5677 while (!DelayReached(&door_delay, door_delay_value))
5682 if (door_state & DOOR_ACTION_1)
5683 door1 = door_state & DOOR_ACTION_1;
5684 if (door_state & DOOR_ACTION_2)
5685 door2 = door_state & DOOR_ACTION_2;
5687 // draw masked border over door area
5688 DrawMaskedBorder(REDRAW_DOOR_1);
5689 DrawMaskedBorder(REDRAW_DOOR_2);
5691 ClearAutoRepeatKeyEvents();
5693 return (door1 | door2);
5696 static boolean useSpecialEditorDoor(void)
5698 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5699 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5701 // do not draw special editor door if editor border defined or redefined
5702 if (graphic_info[graphic].bitmap != NULL || redefined)
5705 // do not draw special editor door if global border defined to be empty
5706 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5709 // do not draw special editor door if viewport definitions do not match
5713 EY + EYSIZE != VY + VYSIZE)
5719 void DrawSpecialEditorDoor(void)
5721 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5722 int top_border_width = gfx1->width;
5723 int top_border_height = gfx1->height;
5724 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5725 int ex = EX - outer_border;
5726 int ey = EY - outer_border;
5727 int vy = VY - outer_border;
5728 int exsize = EXSIZE + 2 * outer_border;
5730 if (!useSpecialEditorDoor())
5733 // draw bigger level editor toolbox window
5734 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5735 top_border_width, top_border_height, ex, ey - top_border_height);
5736 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5737 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5739 redraw_mask |= REDRAW_ALL;
5742 void UndrawSpecialEditorDoor(void)
5744 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5745 int top_border_width = gfx1->width;
5746 int top_border_height = gfx1->height;
5747 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5748 int ex = EX - outer_border;
5749 int ey = EY - outer_border;
5750 int ey_top = ey - top_border_height;
5751 int exsize = EXSIZE + 2 * outer_border;
5752 int eysize = EYSIZE + 2 * outer_border;
5754 if (!useSpecialEditorDoor())
5757 // draw normal tape recorder window
5758 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5760 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5761 ex, ey_top, top_border_width, top_border_height,
5763 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5764 ex, ey, exsize, eysize, ex, ey);
5768 // if screen background is set to "[NONE]", clear editor toolbox window
5769 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5770 ClearRectangle(drawto, ex, ey, exsize, eysize);
5773 redraw_mask |= REDRAW_ALL;
5777 // ---------- new tool button stuff -------------------------------------------
5782 struct TextPosInfo *pos;
5784 boolean is_touch_button;
5786 } toolbutton_info[NUM_TOOL_BUTTONS] =
5789 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5790 TOOL_CTRL_ID_YES, FALSE, "yes"
5793 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5794 TOOL_CTRL_ID_NO, FALSE, "no"
5797 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5798 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5801 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5802 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5805 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5806 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5809 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5810 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5813 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5814 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5817 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5818 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5821 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5822 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5825 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5826 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5830 void CreateToolButtons(void)
5834 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5836 int graphic = toolbutton_info[i].graphic;
5837 struct GraphicInfo *gfx = &graphic_info[graphic];
5838 struct TextPosInfo *pos = toolbutton_info[i].pos;
5839 struct GadgetInfo *gi;
5840 Bitmap *deco_bitmap = None;
5841 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5842 unsigned int event_mask = GD_EVENT_RELEASED;
5843 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5844 int base_x = (is_touch_button ? 0 : DX);
5845 int base_y = (is_touch_button ? 0 : DY);
5846 int gd_x = gfx->src_x;
5847 int gd_y = gfx->src_y;
5848 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5849 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5854 if (global.use_envelope_request && !is_touch_button)
5856 setRequestPosition(&base_x, &base_y, TRUE);
5858 // check if request buttons are outside of envelope and fix, if needed
5859 if (x < 0 || x + gfx->width > request.width ||
5860 y < 0 || y + gfx->height > request.height)
5862 if (id == TOOL_CTRL_ID_YES)
5865 y = request.height - 2 * request.border_size - gfx->height;
5867 else if (id == TOOL_CTRL_ID_NO)
5869 x = request.width - 2 * request.border_size - gfx->width;
5870 y = request.height - 2 * request.border_size - gfx->height;
5872 else if (id == TOOL_CTRL_ID_CONFIRM)
5874 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5875 y = request.height - 2 * request.border_size - gfx->height;
5877 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5879 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5881 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5882 y = request.height - 2 * request.border_size - gfx->height * 2;
5884 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5885 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5890 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5892 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5894 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5895 pos->size, &deco_bitmap, &deco_x, &deco_y);
5896 deco_xpos = (gfx->width - pos->size) / 2;
5897 deco_ypos = (gfx->height - pos->size) / 2;
5900 gi = CreateGadget(GDI_CUSTOM_ID, id,
5901 GDI_IMAGE_ID, graphic,
5902 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5905 GDI_WIDTH, gfx->width,
5906 GDI_HEIGHT, gfx->height,
5907 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5908 GDI_STATE, GD_BUTTON_UNPRESSED,
5909 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5910 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5911 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5912 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5913 GDI_DECORATION_SIZE, pos->size, pos->size,
5914 GDI_DECORATION_SHIFTING, 1, 1,
5915 GDI_DIRECT_DRAW, FALSE,
5916 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5917 GDI_EVENT_MASK, event_mask,
5918 GDI_CALLBACK_ACTION, HandleToolButtons,
5922 Fail("cannot create gadget");
5924 tool_gadget[id] = gi;
5928 void FreeToolButtons(void)
5932 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5933 FreeGadget(tool_gadget[i]);
5936 static void UnmapToolButtons(void)
5940 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5941 UnmapGadget(tool_gadget[i]);
5944 static void HandleToolButtons(struct GadgetInfo *gi)
5946 request_gadget_id = gi->custom_id;
5949 static struct Mapping_EM_to_RND_object
5952 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5953 boolean is_backside; // backside of moving element
5959 em_object_mapping_list[GAME_TILE_MAX + 1] =
5962 Zborder, FALSE, FALSE,
5966 Zplayer, FALSE, FALSE,
5975 Ztank, FALSE, FALSE,
5979 Zeater, FALSE, FALSE,
5983 Zdynamite, FALSE, FALSE,
5987 Zboom, FALSE, FALSE,
5992 Xchain, FALSE, FALSE,
5993 EL_DEFAULT, ACTION_EXPLODING, -1
5996 Xboom_bug, FALSE, FALSE,
5997 EL_BUG, ACTION_EXPLODING, -1
6000 Xboom_tank, FALSE, FALSE,
6001 EL_SPACESHIP, ACTION_EXPLODING, -1
6004 Xboom_android, FALSE, FALSE,
6005 EL_EMC_ANDROID, ACTION_OTHER, -1
6008 Xboom_1, FALSE, FALSE,
6009 EL_DEFAULT, ACTION_EXPLODING, -1
6012 Xboom_2, FALSE, FALSE,
6013 EL_DEFAULT, ACTION_EXPLODING, -1
6017 Xblank, TRUE, FALSE,
6022 Xsplash_e, FALSE, FALSE,
6023 EL_ACID_SPLASH_RIGHT, -1, -1
6026 Xsplash_w, FALSE, FALSE,
6027 EL_ACID_SPLASH_LEFT, -1, -1
6031 Xplant, TRUE, FALSE,
6032 EL_EMC_PLANT, -1, -1
6035 Yplant, FALSE, FALSE,
6036 EL_EMC_PLANT, -1, -1
6040 Xacid_1, TRUE, FALSE,
6044 Xacid_2, FALSE, FALSE,
6048 Xacid_3, FALSE, FALSE,
6052 Xacid_4, FALSE, FALSE,
6056 Xacid_5, FALSE, FALSE,
6060 Xacid_6, FALSE, FALSE,
6064 Xacid_7, FALSE, FALSE,
6068 Xacid_8, FALSE, FALSE,
6073 Xfake_acid_1, TRUE, FALSE,
6074 EL_EMC_FAKE_ACID, -1, -1
6077 Xfake_acid_2, FALSE, FALSE,
6078 EL_EMC_FAKE_ACID, -1, -1
6081 Xfake_acid_3, FALSE, FALSE,
6082 EL_EMC_FAKE_ACID, -1, -1
6085 Xfake_acid_4, FALSE, FALSE,
6086 EL_EMC_FAKE_ACID, -1, -1
6089 Xfake_acid_5, FALSE, FALSE,
6090 EL_EMC_FAKE_ACID, -1, -1
6093 Xfake_acid_6, FALSE, FALSE,
6094 EL_EMC_FAKE_ACID, -1, -1
6097 Xfake_acid_7, FALSE, FALSE,
6098 EL_EMC_FAKE_ACID, -1, -1
6101 Xfake_acid_8, FALSE, FALSE,
6102 EL_EMC_FAKE_ACID, -1, -1
6106 Xfake_acid_1_player, FALSE, FALSE,
6107 EL_EMC_FAKE_ACID, -1, -1
6110 Xfake_acid_2_player, FALSE, FALSE,
6111 EL_EMC_FAKE_ACID, -1, -1
6114 Xfake_acid_3_player, FALSE, FALSE,
6115 EL_EMC_FAKE_ACID, -1, -1
6118 Xfake_acid_4_player, FALSE, FALSE,
6119 EL_EMC_FAKE_ACID, -1, -1
6122 Xfake_acid_5_player, FALSE, FALSE,
6123 EL_EMC_FAKE_ACID, -1, -1
6126 Xfake_acid_6_player, FALSE, FALSE,
6127 EL_EMC_FAKE_ACID, -1, -1
6130 Xfake_acid_7_player, FALSE, FALSE,
6131 EL_EMC_FAKE_ACID, -1, -1
6134 Xfake_acid_8_player, FALSE, FALSE,
6135 EL_EMC_FAKE_ACID, -1, -1
6139 Xgrass, TRUE, FALSE,
6140 EL_EMC_GRASS, -1, -1
6143 Ygrass_nB, FALSE, FALSE,
6144 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6147 Ygrass_eB, FALSE, FALSE,
6148 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6151 Ygrass_sB, FALSE, FALSE,
6152 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6155 Ygrass_wB, FALSE, FALSE,
6156 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6164 Ydirt_nB, FALSE, FALSE,
6165 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6168 Ydirt_eB, FALSE, FALSE,
6169 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6172 Ydirt_sB, FALSE, FALSE,
6173 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6176 Ydirt_wB, FALSE, FALSE,
6177 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6181 Xandroid, TRUE, FALSE,
6182 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6185 Xandroid_1_n, FALSE, FALSE,
6186 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6189 Xandroid_2_n, FALSE, FALSE,
6190 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6193 Xandroid_1_e, FALSE, FALSE,
6194 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6197 Xandroid_2_e, FALSE, FALSE,
6198 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6201 Xandroid_1_w, FALSE, FALSE,
6202 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6205 Xandroid_2_w, FALSE, FALSE,
6206 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6209 Xandroid_1_s, FALSE, FALSE,
6210 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6213 Xandroid_2_s, FALSE, FALSE,
6214 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6217 Yandroid_n, FALSE, FALSE,
6218 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6221 Yandroid_nB, FALSE, TRUE,
6222 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6225 Yandroid_ne, FALSE, FALSE,
6226 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6229 Yandroid_neB, FALSE, TRUE,
6230 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6233 Yandroid_e, FALSE, FALSE,
6234 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6237 Yandroid_eB, FALSE, TRUE,
6238 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6241 Yandroid_se, FALSE, FALSE,
6242 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6245 Yandroid_seB, FALSE, TRUE,
6246 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6249 Yandroid_s, FALSE, FALSE,
6250 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6253 Yandroid_sB, FALSE, TRUE,
6254 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6257 Yandroid_sw, FALSE, FALSE,
6258 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6261 Yandroid_swB, FALSE, TRUE,
6262 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6265 Yandroid_w, FALSE, FALSE,
6266 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6269 Yandroid_wB, FALSE, TRUE,
6270 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6273 Yandroid_nw, FALSE, FALSE,
6274 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6277 Yandroid_nwB, FALSE, TRUE,
6278 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6282 Xeater_n, TRUE, FALSE,
6283 EL_YAMYAM_UP, -1, -1
6286 Xeater_e, TRUE, FALSE,
6287 EL_YAMYAM_RIGHT, -1, -1
6290 Xeater_w, TRUE, FALSE,
6291 EL_YAMYAM_LEFT, -1, -1
6294 Xeater_s, TRUE, FALSE,
6295 EL_YAMYAM_DOWN, -1, -1
6298 Yeater_n, FALSE, FALSE,
6299 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6302 Yeater_nB, FALSE, TRUE,
6303 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6306 Yeater_e, FALSE, FALSE,
6307 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6310 Yeater_eB, FALSE, TRUE,
6311 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6314 Yeater_s, FALSE, FALSE,
6315 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6318 Yeater_sB, FALSE, TRUE,
6319 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6322 Yeater_w, FALSE, FALSE,
6323 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6326 Yeater_wB, FALSE, TRUE,
6327 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6330 Yeater_stone, FALSE, FALSE,
6331 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6334 Yeater_spring, FALSE, FALSE,
6335 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6339 Xalien, TRUE, FALSE,
6343 Xalien_pause, FALSE, FALSE,
6347 Yalien_n, FALSE, FALSE,
6348 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6351 Yalien_nB, FALSE, TRUE,
6352 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6355 Yalien_e, FALSE, FALSE,
6356 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6359 Yalien_eB, FALSE, TRUE,
6360 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6363 Yalien_s, FALSE, FALSE,
6364 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6367 Yalien_sB, FALSE, TRUE,
6368 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6371 Yalien_w, FALSE, FALSE,
6372 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6375 Yalien_wB, FALSE, TRUE,
6376 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6379 Yalien_stone, FALSE, FALSE,
6380 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6383 Yalien_spring, FALSE, FALSE,
6384 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6388 Xbug_1_n, TRUE, FALSE,
6392 Xbug_1_e, TRUE, FALSE,
6393 EL_BUG_RIGHT, -1, -1
6396 Xbug_1_s, TRUE, FALSE,
6400 Xbug_1_w, TRUE, FALSE,
6404 Xbug_2_n, FALSE, FALSE,
6408 Xbug_2_e, FALSE, FALSE,
6409 EL_BUG_RIGHT, -1, -1
6412 Xbug_2_s, FALSE, FALSE,
6416 Xbug_2_w, FALSE, FALSE,
6420 Ybug_n, FALSE, FALSE,
6421 EL_BUG, ACTION_MOVING, MV_BIT_UP
6424 Ybug_nB, FALSE, TRUE,
6425 EL_BUG, ACTION_MOVING, MV_BIT_UP
6428 Ybug_e, FALSE, FALSE,
6429 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6432 Ybug_eB, FALSE, TRUE,
6433 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6436 Ybug_s, FALSE, FALSE,
6437 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6440 Ybug_sB, FALSE, TRUE,
6441 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6444 Ybug_w, FALSE, FALSE,
6445 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6448 Ybug_wB, FALSE, TRUE,
6449 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6452 Ybug_w_n, FALSE, FALSE,
6453 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6456 Ybug_n_e, FALSE, FALSE,
6457 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6460 Ybug_e_s, FALSE, FALSE,
6461 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6464 Ybug_s_w, FALSE, FALSE,
6465 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6468 Ybug_e_n, FALSE, FALSE,
6469 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6472 Ybug_s_e, FALSE, FALSE,
6473 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6476 Ybug_w_s, FALSE, FALSE,
6477 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6480 Ybug_n_w, FALSE, FALSE,
6481 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6484 Ybug_stone, FALSE, FALSE,
6485 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6488 Ybug_spring, FALSE, FALSE,
6489 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6493 Xtank_1_n, TRUE, FALSE,
6494 EL_SPACESHIP_UP, -1, -1
6497 Xtank_1_e, TRUE, FALSE,
6498 EL_SPACESHIP_RIGHT, -1, -1
6501 Xtank_1_s, TRUE, FALSE,
6502 EL_SPACESHIP_DOWN, -1, -1
6505 Xtank_1_w, TRUE, FALSE,
6506 EL_SPACESHIP_LEFT, -1, -1
6509 Xtank_2_n, FALSE, FALSE,
6510 EL_SPACESHIP_UP, -1, -1
6513 Xtank_2_e, FALSE, FALSE,
6514 EL_SPACESHIP_RIGHT, -1, -1
6517 Xtank_2_s, FALSE, FALSE,
6518 EL_SPACESHIP_DOWN, -1, -1
6521 Xtank_2_w, FALSE, FALSE,
6522 EL_SPACESHIP_LEFT, -1, -1
6525 Ytank_n, FALSE, FALSE,
6526 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6529 Ytank_nB, FALSE, TRUE,
6530 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6533 Ytank_e, FALSE, FALSE,
6534 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6537 Ytank_eB, FALSE, TRUE,
6538 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6541 Ytank_s, FALSE, FALSE,
6542 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6545 Ytank_sB, FALSE, TRUE,
6546 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6549 Ytank_w, FALSE, FALSE,
6550 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6553 Ytank_wB, FALSE, TRUE,
6554 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6557 Ytank_w_n, FALSE, FALSE,
6558 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6561 Ytank_n_e, FALSE, FALSE,
6562 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6565 Ytank_e_s, FALSE, FALSE,
6566 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6569 Ytank_s_w, FALSE, FALSE,
6570 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6573 Ytank_e_n, FALSE, FALSE,
6574 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6577 Ytank_s_e, FALSE, FALSE,
6578 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6581 Ytank_w_s, FALSE, FALSE,
6582 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6585 Ytank_n_w, FALSE, FALSE,
6586 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6589 Ytank_stone, FALSE, FALSE,
6590 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6593 Ytank_spring, FALSE, FALSE,
6594 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6598 Xemerald, TRUE, FALSE,
6602 Xemerald_pause, FALSE, FALSE,
6606 Xemerald_fall, FALSE, FALSE,
6610 Xemerald_shine, FALSE, FALSE,
6611 EL_EMERALD, ACTION_TWINKLING, -1
6614 Yemerald_s, FALSE, FALSE,
6615 EL_EMERALD, ACTION_FALLING, -1
6618 Yemerald_sB, FALSE, TRUE,
6619 EL_EMERALD, ACTION_FALLING, -1
6622 Yemerald_e, FALSE, FALSE,
6623 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6626 Yemerald_eB, FALSE, TRUE,
6627 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6630 Yemerald_w, FALSE, FALSE,
6631 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6634 Yemerald_wB, FALSE, TRUE,
6635 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6638 Yemerald_blank, FALSE, FALSE,
6639 EL_EMERALD, ACTION_COLLECTING, -1
6643 Xdiamond, TRUE, FALSE,
6647 Xdiamond_pause, FALSE, FALSE,
6651 Xdiamond_fall, FALSE, FALSE,
6655 Xdiamond_shine, FALSE, FALSE,
6656 EL_DIAMOND, ACTION_TWINKLING, -1
6659 Ydiamond_s, FALSE, FALSE,
6660 EL_DIAMOND, ACTION_FALLING, -1
6663 Ydiamond_sB, FALSE, TRUE,
6664 EL_DIAMOND, ACTION_FALLING, -1
6667 Ydiamond_e, FALSE, FALSE,
6668 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6671 Ydiamond_eB, FALSE, TRUE,
6672 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6675 Ydiamond_w, FALSE, FALSE,
6676 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6679 Ydiamond_wB, FALSE, TRUE,
6680 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6683 Ydiamond_blank, FALSE, FALSE,
6684 EL_DIAMOND, ACTION_COLLECTING, -1
6687 Ydiamond_stone, FALSE, FALSE,
6688 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6692 Xstone, TRUE, FALSE,
6696 Xstone_pause, FALSE, FALSE,
6700 Xstone_fall, FALSE, FALSE,
6704 Ystone_s, FALSE, FALSE,
6705 EL_ROCK, ACTION_FALLING, -1
6708 Ystone_sB, FALSE, TRUE,
6709 EL_ROCK, ACTION_FALLING, -1
6712 Ystone_e, FALSE, FALSE,
6713 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6716 Ystone_eB, FALSE, TRUE,
6717 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6720 Ystone_w, FALSE, FALSE,
6721 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6724 Ystone_wB, FALSE, TRUE,
6725 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6733 Xbomb_pause, FALSE, FALSE,
6737 Xbomb_fall, FALSE, FALSE,
6741 Ybomb_s, FALSE, FALSE,
6742 EL_BOMB, ACTION_FALLING, -1
6745 Ybomb_sB, FALSE, TRUE,
6746 EL_BOMB, ACTION_FALLING, -1
6749 Ybomb_e, FALSE, FALSE,
6750 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6753 Ybomb_eB, FALSE, TRUE,
6754 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6757 Ybomb_w, FALSE, FALSE,
6758 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6761 Ybomb_wB, FALSE, TRUE,
6762 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6765 Ybomb_blank, FALSE, FALSE,
6766 EL_BOMB, ACTION_ACTIVATING, -1
6774 Xnut_pause, FALSE, FALSE,
6778 Xnut_fall, FALSE, FALSE,
6782 Ynut_s, FALSE, FALSE,
6783 EL_NUT, ACTION_FALLING, -1
6786 Ynut_sB, FALSE, TRUE,
6787 EL_NUT, ACTION_FALLING, -1
6790 Ynut_e, FALSE, FALSE,
6791 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6794 Ynut_eB, FALSE, TRUE,
6795 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6798 Ynut_w, FALSE, FALSE,
6799 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6802 Ynut_wB, FALSE, TRUE,
6803 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6806 Ynut_stone, FALSE, FALSE,
6807 EL_NUT, ACTION_BREAKING, -1
6811 Xspring, TRUE, FALSE,
6815 Xspring_pause, FALSE, FALSE,
6819 Xspring_e, TRUE, FALSE,
6820 EL_SPRING_RIGHT, -1, -1
6823 Xspring_w, TRUE, FALSE,
6824 EL_SPRING_LEFT, -1, -1
6827 Xspring_fall, FALSE, FALSE,
6831 Yspring_s, FALSE, FALSE,
6832 EL_SPRING, ACTION_FALLING, -1
6835 Yspring_sB, FALSE, TRUE,
6836 EL_SPRING, ACTION_FALLING, -1
6839 Yspring_e, FALSE, FALSE,
6840 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6843 Yspring_eB, FALSE, TRUE,
6844 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6847 Yspring_w, FALSE, FALSE,
6848 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6851 Yspring_wB, FALSE, TRUE,
6852 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6855 Yspring_alien_e, FALSE, FALSE,
6856 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6859 Yspring_alien_eB, FALSE, TRUE,
6860 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6863 Yspring_alien_w, FALSE, FALSE,
6864 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6867 Yspring_alien_wB, FALSE, TRUE,
6868 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6872 Xpush_emerald_e, FALSE, FALSE,
6873 EL_EMERALD, -1, MV_BIT_RIGHT
6876 Xpush_emerald_w, FALSE, FALSE,
6877 EL_EMERALD, -1, MV_BIT_LEFT
6880 Xpush_diamond_e, FALSE, FALSE,
6881 EL_DIAMOND, -1, MV_BIT_RIGHT
6884 Xpush_diamond_w, FALSE, FALSE,
6885 EL_DIAMOND, -1, MV_BIT_LEFT
6888 Xpush_stone_e, FALSE, FALSE,
6889 EL_ROCK, -1, MV_BIT_RIGHT
6892 Xpush_stone_w, FALSE, FALSE,
6893 EL_ROCK, -1, MV_BIT_LEFT
6896 Xpush_bomb_e, FALSE, FALSE,
6897 EL_BOMB, -1, MV_BIT_RIGHT
6900 Xpush_bomb_w, FALSE, FALSE,
6901 EL_BOMB, -1, MV_BIT_LEFT
6904 Xpush_nut_e, FALSE, FALSE,
6905 EL_NUT, -1, MV_BIT_RIGHT
6908 Xpush_nut_w, FALSE, FALSE,
6909 EL_NUT, -1, MV_BIT_LEFT
6912 Xpush_spring_e, FALSE, FALSE,
6913 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6916 Xpush_spring_w, FALSE, FALSE,
6917 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6921 Xdynamite, TRUE, FALSE,
6922 EL_EM_DYNAMITE, -1, -1
6925 Ydynamite_blank, FALSE, FALSE,
6926 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6929 Xdynamite_1, TRUE, FALSE,
6930 EL_EM_DYNAMITE_ACTIVE, -1, -1
6933 Xdynamite_2, FALSE, FALSE,
6934 EL_EM_DYNAMITE_ACTIVE, -1, -1
6937 Xdynamite_3, FALSE, FALSE,
6938 EL_EM_DYNAMITE_ACTIVE, -1, -1
6941 Xdynamite_4, FALSE, FALSE,
6942 EL_EM_DYNAMITE_ACTIVE, -1, -1
6946 Xkey_1, TRUE, FALSE,
6950 Xkey_2, TRUE, FALSE,
6954 Xkey_3, TRUE, FALSE,
6958 Xkey_4, TRUE, FALSE,
6962 Xkey_5, TRUE, FALSE,
6963 EL_EMC_KEY_5, -1, -1
6966 Xkey_6, TRUE, FALSE,
6967 EL_EMC_KEY_6, -1, -1
6970 Xkey_7, TRUE, FALSE,
6971 EL_EMC_KEY_7, -1, -1
6974 Xkey_8, TRUE, FALSE,
6975 EL_EMC_KEY_8, -1, -1
6979 Xdoor_1, TRUE, FALSE,
6980 EL_EM_GATE_1, -1, -1
6983 Xdoor_2, TRUE, FALSE,
6984 EL_EM_GATE_2, -1, -1
6987 Xdoor_3, TRUE, FALSE,
6988 EL_EM_GATE_3, -1, -1
6991 Xdoor_4, TRUE, FALSE,
6992 EL_EM_GATE_4, -1, -1
6995 Xdoor_5, TRUE, FALSE,
6996 EL_EMC_GATE_5, -1, -1
6999 Xdoor_6, TRUE, FALSE,
7000 EL_EMC_GATE_6, -1, -1
7003 Xdoor_7, TRUE, FALSE,
7004 EL_EMC_GATE_7, -1, -1
7007 Xdoor_8, TRUE, FALSE,
7008 EL_EMC_GATE_8, -1, -1
7012 Xfake_door_1, TRUE, FALSE,
7013 EL_EM_GATE_1_GRAY, -1, -1
7016 Xfake_door_2, TRUE, FALSE,
7017 EL_EM_GATE_2_GRAY, -1, -1
7020 Xfake_door_3, TRUE, FALSE,
7021 EL_EM_GATE_3_GRAY, -1, -1
7024 Xfake_door_4, TRUE, FALSE,
7025 EL_EM_GATE_4_GRAY, -1, -1
7028 Xfake_door_5, TRUE, FALSE,
7029 EL_EMC_GATE_5_GRAY, -1, -1
7032 Xfake_door_6, TRUE, FALSE,
7033 EL_EMC_GATE_6_GRAY, -1, -1
7036 Xfake_door_7, TRUE, FALSE,
7037 EL_EMC_GATE_7_GRAY, -1, -1
7040 Xfake_door_8, TRUE, FALSE,
7041 EL_EMC_GATE_8_GRAY, -1, -1
7045 Xballoon, TRUE, FALSE,
7049 Yballoon_n, FALSE, FALSE,
7050 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7053 Yballoon_nB, FALSE, TRUE,
7054 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7057 Yballoon_e, FALSE, FALSE,
7058 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7061 Yballoon_eB, FALSE, TRUE,
7062 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7065 Yballoon_s, FALSE, FALSE,
7066 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7069 Yballoon_sB, FALSE, TRUE,
7070 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7073 Yballoon_w, FALSE, FALSE,
7074 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7077 Yballoon_wB, FALSE, TRUE,
7078 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7082 Xball_1, TRUE, FALSE,
7083 EL_EMC_MAGIC_BALL, -1, -1
7086 Yball_1, FALSE, FALSE,
7087 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7090 Xball_2, FALSE, FALSE,
7091 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7094 Yball_2, FALSE, FALSE,
7095 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7098 Yball_blank, FALSE, FALSE,
7099 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7103 Xamoeba_1, TRUE, FALSE,
7104 EL_AMOEBA_DRY, ACTION_OTHER, -1
7107 Xamoeba_2, FALSE, FALSE,
7108 EL_AMOEBA_DRY, ACTION_OTHER, -1
7111 Xamoeba_3, FALSE, FALSE,
7112 EL_AMOEBA_DRY, ACTION_OTHER, -1
7115 Xamoeba_4, FALSE, FALSE,
7116 EL_AMOEBA_DRY, ACTION_OTHER, -1
7119 Xamoeba_5, TRUE, FALSE,
7120 EL_AMOEBA_WET, ACTION_OTHER, -1
7123 Xamoeba_6, FALSE, FALSE,
7124 EL_AMOEBA_WET, ACTION_OTHER, -1
7127 Xamoeba_7, FALSE, FALSE,
7128 EL_AMOEBA_WET, ACTION_OTHER, -1
7131 Xamoeba_8, FALSE, FALSE,
7132 EL_AMOEBA_WET, ACTION_OTHER, -1
7137 EL_AMOEBA_DROP, ACTION_GROWING, -1
7140 Xdrip_fall, FALSE, FALSE,
7141 EL_AMOEBA_DROP, -1, -1
7144 Xdrip_stretch, FALSE, FALSE,
7145 EL_AMOEBA_DROP, ACTION_FALLING, -1
7148 Xdrip_stretchB, FALSE, TRUE,
7149 EL_AMOEBA_DROP, ACTION_FALLING, -1
7152 Ydrip_1_s, FALSE, FALSE,
7153 EL_AMOEBA_DROP, ACTION_FALLING, -1
7156 Ydrip_1_sB, FALSE, TRUE,
7157 EL_AMOEBA_DROP, ACTION_FALLING, -1
7160 Ydrip_2_s, FALSE, FALSE,
7161 EL_AMOEBA_DROP, ACTION_FALLING, -1
7164 Ydrip_2_sB, FALSE, TRUE,
7165 EL_AMOEBA_DROP, ACTION_FALLING, -1
7169 Xwonderwall, TRUE, FALSE,
7170 EL_MAGIC_WALL, -1, -1
7173 Ywonderwall, FALSE, FALSE,
7174 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7178 Xwheel, TRUE, FALSE,
7179 EL_ROBOT_WHEEL, -1, -1
7182 Ywheel, FALSE, FALSE,
7183 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7187 Xswitch, TRUE, FALSE,
7188 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7191 Yswitch, FALSE, FALSE,
7192 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7196 Xbumper, TRUE, FALSE,
7197 EL_EMC_SPRING_BUMPER, -1, -1
7200 Ybumper, FALSE, FALSE,
7201 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7205 Xacid_nw, TRUE, FALSE,
7206 EL_ACID_POOL_TOPLEFT, -1, -1
7209 Xacid_ne, TRUE, FALSE,
7210 EL_ACID_POOL_TOPRIGHT, -1, -1
7213 Xacid_sw, TRUE, FALSE,
7214 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7217 Xacid_s, TRUE, FALSE,
7218 EL_ACID_POOL_BOTTOM, -1, -1
7221 Xacid_se, TRUE, FALSE,
7222 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7226 Xfake_blank, TRUE, FALSE,
7227 EL_INVISIBLE_WALL, -1, -1
7230 Yfake_blank, FALSE, FALSE,
7231 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7235 Xfake_grass, TRUE, FALSE,
7236 EL_EMC_FAKE_GRASS, -1, -1
7239 Yfake_grass, FALSE, FALSE,
7240 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7244 Xfake_amoeba, TRUE, FALSE,
7245 EL_EMC_DRIPPER, -1, -1
7248 Yfake_amoeba, FALSE, FALSE,
7249 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7253 Xlenses, TRUE, FALSE,
7254 EL_EMC_LENSES, -1, -1
7258 Xmagnify, TRUE, FALSE,
7259 EL_EMC_MAGNIFIER, -1, -1
7264 EL_QUICKSAND_EMPTY, -1, -1
7267 Xsand_stone, TRUE, FALSE,
7268 EL_QUICKSAND_FULL, -1, -1
7271 Xsand_stonein_1, FALSE, TRUE,
7272 EL_ROCK, ACTION_FILLING, -1
7275 Xsand_stonein_2, FALSE, TRUE,
7276 EL_ROCK, ACTION_FILLING, -1
7279 Xsand_stonein_3, FALSE, TRUE,
7280 EL_ROCK, ACTION_FILLING, -1
7283 Xsand_stonein_4, FALSE, TRUE,
7284 EL_ROCK, ACTION_FILLING, -1
7287 Xsand_sandstone_1, FALSE, FALSE,
7288 EL_QUICKSAND_FILLING, -1, -1
7291 Xsand_sandstone_2, FALSE, FALSE,
7292 EL_QUICKSAND_FILLING, -1, -1
7295 Xsand_sandstone_3, FALSE, FALSE,
7296 EL_QUICKSAND_FILLING, -1, -1
7299 Xsand_sandstone_4, FALSE, FALSE,
7300 EL_QUICKSAND_FILLING, -1, -1
7303 Xsand_stonesand_1, FALSE, FALSE,
7304 EL_QUICKSAND_EMPTYING, -1, -1
7307 Xsand_stonesand_2, FALSE, FALSE,
7308 EL_QUICKSAND_EMPTYING, -1, -1
7311 Xsand_stonesand_3, FALSE, FALSE,
7312 EL_QUICKSAND_EMPTYING, -1, -1
7315 Xsand_stonesand_4, FALSE, FALSE,
7316 EL_QUICKSAND_EMPTYING, -1, -1
7319 Xsand_stoneout_1, FALSE, FALSE,
7320 EL_ROCK, ACTION_EMPTYING, -1
7323 Xsand_stoneout_2, FALSE, FALSE,
7324 EL_ROCK, ACTION_EMPTYING, -1
7327 Xsand_stonesand_quickout_1, FALSE, FALSE,
7328 EL_QUICKSAND_EMPTYING, -1, -1
7331 Xsand_stonesand_quickout_2, FALSE, FALSE,
7332 EL_QUICKSAND_EMPTYING, -1, -1
7336 Xslide_ns, TRUE, FALSE,
7337 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7340 Yslide_ns_blank, FALSE, FALSE,
7341 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7344 Xslide_ew, TRUE, FALSE,
7345 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7348 Yslide_ew_blank, FALSE, FALSE,
7349 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7353 Xwind_n, TRUE, FALSE,
7354 EL_BALLOON_SWITCH_UP, -1, -1
7357 Xwind_e, TRUE, FALSE,
7358 EL_BALLOON_SWITCH_RIGHT, -1, -1
7361 Xwind_s, TRUE, FALSE,
7362 EL_BALLOON_SWITCH_DOWN, -1, -1
7365 Xwind_w, TRUE, FALSE,
7366 EL_BALLOON_SWITCH_LEFT, -1, -1
7369 Xwind_any, TRUE, FALSE,
7370 EL_BALLOON_SWITCH_ANY, -1, -1
7373 Xwind_stop, TRUE, FALSE,
7374 EL_BALLOON_SWITCH_NONE, -1, -1
7379 EL_EM_EXIT_CLOSED, -1, -1
7382 Xexit_1, TRUE, FALSE,
7383 EL_EM_EXIT_OPEN, -1, -1
7386 Xexit_2, FALSE, FALSE,
7387 EL_EM_EXIT_OPEN, -1, -1
7390 Xexit_3, FALSE, FALSE,
7391 EL_EM_EXIT_OPEN, -1, -1
7395 Xpause, FALSE, FALSE,
7400 Xwall_1, TRUE, FALSE,
7404 Xwall_2, TRUE, FALSE,
7405 EL_EMC_WALL_14, -1, -1
7408 Xwall_3, TRUE, FALSE,
7409 EL_EMC_WALL_15, -1, -1
7412 Xwall_4, TRUE, FALSE,
7413 EL_EMC_WALL_16, -1, -1
7417 Xroundwall_1, TRUE, FALSE,
7418 EL_WALL_SLIPPERY, -1, -1
7421 Xroundwall_2, TRUE, FALSE,
7422 EL_EMC_WALL_SLIPPERY_2, -1, -1
7425 Xroundwall_3, TRUE, FALSE,
7426 EL_EMC_WALL_SLIPPERY_3, -1, -1
7429 Xroundwall_4, TRUE, FALSE,
7430 EL_EMC_WALL_SLIPPERY_4, -1, -1
7434 Xsteel_1, TRUE, FALSE,
7435 EL_STEELWALL, -1, -1
7438 Xsteel_2, TRUE, FALSE,
7439 EL_EMC_STEELWALL_2, -1, -1
7442 Xsteel_3, TRUE, FALSE,
7443 EL_EMC_STEELWALL_3, -1, -1
7446 Xsteel_4, TRUE, FALSE,
7447 EL_EMC_STEELWALL_4, -1, -1
7451 Xdecor_1, TRUE, FALSE,
7452 EL_EMC_WALL_8, -1, -1
7455 Xdecor_2, TRUE, FALSE,
7456 EL_EMC_WALL_6, -1, -1
7459 Xdecor_3, TRUE, FALSE,
7460 EL_EMC_WALL_4, -1, -1
7463 Xdecor_4, TRUE, FALSE,
7464 EL_EMC_WALL_7, -1, -1
7467 Xdecor_5, TRUE, FALSE,
7468 EL_EMC_WALL_5, -1, -1
7471 Xdecor_6, TRUE, FALSE,
7472 EL_EMC_WALL_9, -1, -1
7475 Xdecor_7, TRUE, FALSE,
7476 EL_EMC_WALL_10, -1, -1
7479 Xdecor_8, TRUE, FALSE,
7480 EL_EMC_WALL_1, -1, -1
7483 Xdecor_9, TRUE, FALSE,
7484 EL_EMC_WALL_2, -1, -1
7487 Xdecor_10, TRUE, FALSE,
7488 EL_EMC_WALL_3, -1, -1
7491 Xdecor_11, TRUE, FALSE,
7492 EL_EMC_WALL_11, -1, -1
7495 Xdecor_12, TRUE, FALSE,
7496 EL_EMC_WALL_12, -1, -1
7500 Xalpha_0, TRUE, FALSE,
7501 EL_CHAR('0'), -1, -1
7504 Xalpha_1, TRUE, FALSE,
7505 EL_CHAR('1'), -1, -1
7508 Xalpha_2, TRUE, FALSE,
7509 EL_CHAR('2'), -1, -1
7512 Xalpha_3, TRUE, FALSE,
7513 EL_CHAR('3'), -1, -1
7516 Xalpha_4, TRUE, FALSE,
7517 EL_CHAR('4'), -1, -1
7520 Xalpha_5, TRUE, FALSE,
7521 EL_CHAR('5'), -1, -1
7524 Xalpha_6, TRUE, FALSE,
7525 EL_CHAR('6'), -1, -1
7528 Xalpha_7, TRUE, FALSE,
7529 EL_CHAR('7'), -1, -1
7532 Xalpha_8, TRUE, FALSE,
7533 EL_CHAR('8'), -1, -1
7536 Xalpha_9, TRUE, FALSE,
7537 EL_CHAR('9'), -1, -1
7540 Xalpha_excla, TRUE, FALSE,
7541 EL_CHAR('!'), -1, -1
7544 Xalpha_apost, TRUE, FALSE,
7545 EL_CHAR('\''), -1, -1
7548 Xalpha_comma, TRUE, FALSE,
7549 EL_CHAR(','), -1, -1
7552 Xalpha_minus, TRUE, FALSE,
7553 EL_CHAR('-'), -1, -1
7556 Xalpha_perio, TRUE, FALSE,
7557 EL_CHAR('.'), -1, -1
7560 Xalpha_colon, TRUE, FALSE,
7561 EL_CHAR(':'), -1, -1
7564 Xalpha_quest, TRUE, FALSE,
7565 EL_CHAR('?'), -1, -1
7568 Xalpha_a, TRUE, FALSE,
7569 EL_CHAR('A'), -1, -1
7572 Xalpha_b, TRUE, FALSE,
7573 EL_CHAR('B'), -1, -1
7576 Xalpha_c, TRUE, FALSE,
7577 EL_CHAR('C'), -1, -1
7580 Xalpha_d, TRUE, FALSE,
7581 EL_CHAR('D'), -1, -1
7584 Xalpha_e, TRUE, FALSE,
7585 EL_CHAR('E'), -1, -1
7588 Xalpha_f, TRUE, FALSE,
7589 EL_CHAR('F'), -1, -1
7592 Xalpha_g, TRUE, FALSE,
7593 EL_CHAR('G'), -1, -1
7596 Xalpha_h, TRUE, FALSE,
7597 EL_CHAR('H'), -1, -1
7600 Xalpha_i, TRUE, FALSE,
7601 EL_CHAR('I'), -1, -1
7604 Xalpha_j, TRUE, FALSE,
7605 EL_CHAR('J'), -1, -1
7608 Xalpha_k, TRUE, FALSE,
7609 EL_CHAR('K'), -1, -1
7612 Xalpha_l, TRUE, FALSE,
7613 EL_CHAR('L'), -1, -1
7616 Xalpha_m, TRUE, FALSE,
7617 EL_CHAR('M'), -1, -1
7620 Xalpha_n, TRUE, FALSE,
7621 EL_CHAR('N'), -1, -1
7624 Xalpha_o, TRUE, FALSE,
7625 EL_CHAR('O'), -1, -1
7628 Xalpha_p, TRUE, FALSE,
7629 EL_CHAR('P'), -1, -1
7632 Xalpha_q, TRUE, FALSE,
7633 EL_CHAR('Q'), -1, -1
7636 Xalpha_r, TRUE, FALSE,
7637 EL_CHAR('R'), -1, -1
7640 Xalpha_s, TRUE, FALSE,
7641 EL_CHAR('S'), -1, -1
7644 Xalpha_t, TRUE, FALSE,
7645 EL_CHAR('T'), -1, -1
7648 Xalpha_u, TRUE, FALSE,
7649 EL_CHAR('U'), -1, -1
7652 Xalpha_v, TRUE, FALSE,
7653 EL_CHAR('V'), -1, -1
7656 Xalpha_w, TRUE, FALSE,
7657 EL_CHAR('W'), -1, -1
7660 Xalpha_x, TRUE, FALSE,
7661 EL_CHAR('X'), -1, -1
7664 Xalpha_y, TRUE, FALSE,
7665 EL_CHAR('Y'), -1, -1
7668 Xalpha_z, TRUE, FALSE,
7669 EL_CHAR('Z'), -1, -1
7672 Xalpha_arrow_e, TRUE, FALSE,
7673 EL_CHAR('>'), -1, -1
7676 Xalpha_arrow_w, TRUE, FALSE,
7677 EL_CHAR('<'), -1, -1
7680 Xalpha_copyr, TRUE, FALSE,
7681 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7685 Ykey_1_blank, FALSE, FALSE,
7686 EL_EM_KEY_1, ACTION_COLLECTING, -1
7689 Ykey_2_blank, FALSE, FALSE,
7690 EL_EM_KEY_2, ACTION_COLLECTING, -1
7693 Ykey_3_blank, FALSE, FALSE,
7694 EL_EM_KEY_3, ACTION_COLLECTING, -1
7697 Ykey_4_blank, FALSE, FALSE,
7698 EL_EM_KEY_4, ACTION_COLLECTING, -1
7701 Ykey_5_blank, FALSE, FALSE,
7702 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7705 Ykey_6_blank, FALSE, FALSE,
7706 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7709 Ykey_7_blank, FALSE, FALSE,
7710 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7713 Ykey_8_blank, FALSE, FALSE,
7714 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7717 Ylenses_blank, FALSE, FALSE,
7718 EL_EMC_LENSES, ACTION_COLLECTING, -1
7721 Ymagnify_blank, FALSE, FALSE,
7722 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7725 Ygrass_blank, FALSE, FALSE,
7726 EL_EMC_GRASS, ACTION_SNAPPING, -1
7729 Ydirt_blank, FALSE, FALSE,
7730 EL_SAND, ACTION_SNAPPING, -1
7739 static struct Mapping_EM_to_RND_player
7748 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7752 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7756 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7760 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7764 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7768 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7772 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7776 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7780 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7784 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7788 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7792 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7796 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7800 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7804 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7808 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7812 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7816 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7820 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7824 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7828 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7832 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7836 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7840 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7844 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7848 EL_PLAYER_1, ACTION_DEFAULT, -1,
7852 EL_PLAYER_2, ACTION_DEFAULT, -1,
7856 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7860 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7864 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7868 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7872 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7876 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7880 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7884 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7888 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7892 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7896 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7900 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7904 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7908 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7912 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7916 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7920 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7924 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7928 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7932 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7936 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7940 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7944 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7948 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7952 EL_PLAYER_3, ACTION_DEFAULT, -1,
7956 EL_PLAYER_4, ACTION_DEFAULT, -1,
7965 int map_element_RND_to_EM_cave(int element_rnd)
7967 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7968 static boolean mapping_initialized = FALSE;
7970 if (!mapping_initialized)
7974 // return "Xalpha_quest" for all undefined elements in mapping array
7975 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7976 mapping_RND_to_EM[i] = Xalpha_quest;
7978 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7979 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7980 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7981 em_object_mapping_list[i].element_em;
7983 mapping_initialized = TRUE;
7986 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7988 Warn("invalid RND level element %d", element_rnd);
7993 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7996 int map_element_EM_to_RND_cave(int element_em_cave)
7998 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7999 static boolean mapping_initialized = FALSE;
8001 if (!mapping_initialized)
8005 // return "EL_UNKNOWN" for all undefined elements in mapping array
8006 for (i = 0; i < GAME_TILE_MAX; i++)
8007 mapping_EM_to_RND[i] = EL_UNKNOWN;
8009 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8010 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8011 em_object_mapping_list[i].element_rnd;
8013 mapping_initialized = TRUE;
8016 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8018 Warn("invalid EM cave element %d", element_em_cave);
8023 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8026 int map_element_EM_to_RND_game(int element_em_game)
8028 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8029 static boolean mapping_initialized = FALSE;
8031 if (!mapping_initialized)
8035 // return "EL_UNKNOWN" for all undefined elements in mapping array
8036 for (i = 0; i < GAME_TILE_MAX; i++)
8037 mapping_EM_to_RND[i] = EL_UNKNOWN;
8039 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8040 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8041 em_object_mapping_list[i].element_rnd;
8043 mapping_initialized = TRUE;
8046 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8048 Warn("invalid EM game element %d", element_em_game);
8053 return mapping_EM_to_RND[element_em_game];
8056 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8058 struct LevelInfo_EM *level_em = level->native_em_level;
8059 struct CAVE *cav = level_em->cav;
8062 for (i = 0; i < GAME_TILE_MAX; i++)
8063 cav->android_array[i] = Cblank;
8065 for (i = 0; i < level->num_android_clone_elements; i++)
8067 int element_rnd = level->android_clone_element[i];
8068 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8070 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8071 if (em_object_mapping_list[j].element_rnd == element_rnd)
8072 cav->android_array[em_object_mapping_list[j].element_em] =
8077 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8079 struct LevelInfo_EM *level_em = level->native_em_level;
8080 struct CAVE *cav = level_em->cav;
8083 level->num_android_clone_elements = 0;
8085 for (i = 0; i < GAME_TILE_MAX; i++)
8087 int element_em_cave = cav->android_array[i];
8089 boolean element_found = FALSE;
8091 if (element_em_cave == Cblank)
8094 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8096 for (j = 0; j < level->num_android_clone_elements; j++)
8097 if (level->android_clone_element[j] == element_rnd)
8098 element_found = TRUE;
8102 level->android_clone_element[level->num_android_clone_elements++] =
8105 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8110 if (level->num_android_clone_elements == 0)
8112 level->num_android_clone_elements = 1;
8113 level->android_clone_element[0] = EL_EMPTY;
8117 int map_direction_RND_to_EM(int direction)
8119 return (direction == MV_UP ? 0 :
8120 direction == MV_RIGHT ? 1 :
8121 direction == MV_DOWN ? 2 :
8122 direction == MV_LEFT ? 3 :
8126 int map_direction_EM_to_RND(int direction)
8128 return (direction == 0 ? MV_UP :
8129 direction == 1 ? MV_RIGHT :
8130 direction == 2 ? MV_DOWN :
8131 direction == 3 ? MV_LEFT :
8135 int map_element_RND_to_SP(int element_rnd)
8137 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8139 if (element_rnd >= EL_SP_START &&
8140 element_rnd <= EL_SP_END)
8141 element_sp = element_rnd - EL_SP_START;
8142 else if (element_rnd == EL_EMPTY_SPACE)
8144 else if (element_rnd == EL_INVISIBLE_WALL)
8150 int map_element_SP_to_RND(int element_sp)
8152 int element_rnd = EL_UNKNOWN;
8154 if (element_sp >= 0x00 &&
8156 element_rnd = EL_SP_START + element_sp;
8157 else if (element_sp == 0x28)
8158 element_rnd = EL_INVISIBLE_WALL;
8163 int map_action_SP_to_RND(int action_sp)
8167 case actActive: return ACTION_ACTIVE;
8168 case actImpact: return ACTION_IMPACT;
8169 case actExploding: return ACTION_EXPLODING;
8170 case actDigging: return ACTION_DIGGING;
8171 case actSnapping: return ACTION_SNAPPING;
8172 case actCollecting: return ACTION_COLLECTING;
8173 case actPassing: return ACTION_PASSING;
8174 case actPushing: return ACTION_PUSHING;
8175 case actDropping: return ACTION_DROPPING;
8177 default: return ACTION_DEFAULT;
8181 int map_element_RND_to_MM(int element_rnd)
8183 return (element_rnd >= EL_MM_START_1 &&
8184 element_rnd <= EL_MM_END_1 ?
8185 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8187 element_rnd >= EL_MM_START_2 &&
8188 element_rnd <= EL_MM_END_2 ?
8189 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8191 element_rnd >= EL_CHAR_START &&
8192 element_rnd <= EL_CHAR_END ?
8193 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8195 element_rnd >= EL_MM_RUNTIME_START &&
8196 element_rnd <= EL_MM_RUNTIME_END ?
8197 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8199 element_rnd >= EL_MM_DUMMY_START &&
8200 element_rnd <= EL_MM_DUMMY_END ?
8201 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8203 EL_MM_EMPTY_NATIVE);
8206 int map_element_MM_to_RND(int element_mm)
8208 return (element_mm == EL_MM_EMPTY_NATIVE ||
8209 element_mm == EL_DF_EMPTY_NATIVE ?
8212 element_mm >= EL_MM_START_1_NATIVE &&
8213 element_mm <= EL_MM_END_1_NATIVE ?
8214 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8216 element_mm >= EL_MM_START_2_NATIVE &&
8217 element_mm <= EL_MM_END_2_NATIVE ?
8218 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8220 element_mm >= EL_MM_CHAR_START_NATIVE &&
8221 element_mm <= EL_MM_CHAR_END_NATIVE ?
8222 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8224 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8225 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8226 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8228 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8229 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8230 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8235 int map_action_MM_to_RND(int action_mm)
8237 // all MM actions are defined to exactly match their RND counterparts
8241 int map_sound_MM_to_RND(int sound_mm)
8245 case SND_MM_GAME_LEVELTIME_CHARGING:
8246 return SND_GAME_LEVELTIME_CHARGING;
8248 case SND_MM_GAME_HEALTH_CHARGING:
8249 return SND_GAME_HEALTH_CHARGING;
8252 return SND_UNDEFINED;
8256 int map_mm_wall_element(int element)
8258 return (element >= EL_MM_STEEL_WALL_START &&
8259 element <= EL_MM_STEEL_WALL_END ?
8262 element >= EL_MM_WOODEN_WALL_START &&
8263 element <= EL_MM_WOODEN_WALL_END ?
8266 element >= EL_MM_ICE_WALL_START &&
8267 element <= EL_MM_ICE_WALL_END ?
8270 element >= EL_MM_AMOEBA_WALL_START &&
8271 element <= EL_MM_AMOEBA_WALL_END ?
8274 element >= EL_DF_STEEL_WALL_START &&
8275 element <= EL_DF_STEEL_WALL_END ?
8278 element >= EL_DF_WOODEN_WALL_START &&
8279 element <= EL_DF_WOODEN_WALL_END ?
8285 int map_mm_wall_element_editor(int element)
8289 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8290 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8291 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8292 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8293 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8294 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8296 default: return element;
8300 int get_next_element(int element)
8304 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8305 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8306 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8307 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8308 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8309 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8310 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8311 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8312 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8313 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8314 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8316 default: return element;
8320 int el2img_mm(int element_mm)
8322 return el2img(map_element_MM_to_RND(element_mm));
8325 int el_act_dir2img(int element, int action, int direction)
8327 element = GFX_ELEMENT(element);
8328 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8330 // direction_graphic[][] == graphic[] for undefined direction graphics
8331 return element_info[element].direction_graphic[action][direction];
8334 static int el_act_dir2crm(int element, int action, int direction)
8336 element = GFX_ELEMENT(element);
8337 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8339 // direction_graphic[][] == graphic[] for undefined direction graphics
8340 return element_info[element].direction_crumbled[action][direction];
8343 int el_act2img(int element, int action)
8345 element = GFX_ELEMENT(element);
8347 return element_info[element].graphic[action];
8350 int el_act2crm(int element, int action)
8352 element = GFX_ELEMENT(element);
8354 return element_info[element].crumbled[action];
8357 int el_dir2img(int element, int direction)
8359 element = GFX_ELEMENT(element);
8361 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8364 int el2baseimg(int element)
8366 return element_info[element].graphic[ACTION_DEFAULT];
8369 int el2img(int element)
8371 element = GFX_ELEMENT(element);
8373 return element_info[element].graphic[ACTION_DEFAULT];
8376 int el2edimg(int element)
8378 element = GFX_ELEMENT(element);
8380 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8383 int el2preimg(int element)
8385 element = GFX_ELEMENT(element);
8387 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8390 int el2panelimg(int element)
8392 element = GFX_ELEMENT(element);
8394 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8397 int font2baseimg(int font_nr)
8399 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8402 int getBeltNrFromBeltElement(int element)
8404 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8405 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8406 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8409 int getBeltNrFromBeltActiveElement(int element)
8411 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8412 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8413 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8416 int getBeltNrFromBeltSwitchElement(int element)
8418 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8419 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8420 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8423 int getBeltDirNrFromBeltElement(int element)
8425 static int belt_base_element[4] =
8427 EL_CONVEYOR_BELT_1_LEFT,
8428 EL_CONVEYOR_BELT_2_LEFT,
8429 EL_CONVEYOR_BELT_3_LEFT,
8430 EL_CONVEYOR_BELT_4_LEFT
8433 int belt_nr = getBeltNrFromBeltElement(element);
8434 int belt_dir_nr = element - belt_base_element[belt_nr];
8436 return (belt_dir_nr % 3);
8439 int getBeltDirNrFromBeltSwitchElement(int element)
8441 static int belt_base_element[4] =
8443 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8444 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8445 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8446 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8449 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8450 int belt_dir_nr = element - belt_base_element[belt_nr];
8452 return (belt_dir_nr % 3);
8455 int getBeltDirFromBeltElement(int element)
8457 static int belt_move_dir[3] =
8464 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8466 return belt_move_dir[belt_dir_nr];
8469 int getBeltDirFromBeltSwitchElement(int element)
8471 static int belt_move_dir[3] =
8478 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8480 return belt_move_dir[belt_dir_nr];
8483 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8485 static int belt_base_element[4] =
8487 EL_CONVEYOR_BELT_1_LEFT,
8488 EL_CONVEYOR_BELT_2_LEFT,
8489 EL_CONVEYOR_BELT_3_LEFT,
8490 EL_CONVEYOR_BELT_4_LEFT
8493 return belt_base_element[belt_nr] + belt_dir_nr;
8496 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8498 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8500 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8503 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8505 static int belt_base_element[4] =
8507 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8508 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8509 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8510 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8513 return belt_base_element[belt_nr] + belt_dir_nr;
8516 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8518 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8520 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8523 boolean swapTiles_EM(boolean is_pre_emc_cave)
8525 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8528 boolean getTeamMode_EM(void)
8530 return game.team_mode || network_playing;
8533 boolean isActivePlayer_EM(int player_nr)
8535 return stored_player[player_nr].active;
8538 unsigned int InitRND(int seed)
8540 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8541 return InitEngineRandom_EM(seed);
8542 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8543 return InitEngineRandom_SP(seed);
8544 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8545 return InitEngineRandom_MM(seed);
8547 return InitEngineRandom_RND(seed);
8550 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8551 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8553 static int get_effective_element_EM(int tile, int frame_em)
8555 int element = object_mapping[tile].element_rnd;
8556 int action = object_mapping[tile].action;
8557 boolean is_backside = object_mapping[tile].is_backside;
8558 boolean action_removing = (action == ACTION_DIGGING ||
8559 action == ACTION_SNAPPING ||
8560 action == ACTION_COLLECTING);
8568 return (frame_em > 5 ? EL_EMPTY : element);
8574 else // frame_em == 7
8585 case Ydiamond_stone:
8589 case Xdrip_stretchB:
8605 case Ymagnify_blank:
8608 case Xsand_stonein_1:
8609 case Xsand_stonein_2:
8610 case Xsand_stonein_3:
8611 case Xsand_stonein_4:
8615 return (is_backside || action_removing ? EL_EMPTY : element);
8620 static boolean check_linear_animation_EM(int tile)
8624 case Xsand_stonesand_1:
8625 case Xsand_stonesand_quickout_1:
8626 case Xsand_sandstone_1:
8627 case Xsand_stonein_1:
8628 case Xsand_stoneout_1:
8656 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8657 boolean has_crumbled_graphics,
8658 int crumbled, int sync_frame)
8660 // if element can be crumbled, but certain action graphics are just empty
8661 // space (like instantly snapping sand to empty space in 1 frame), do not
8662 // treat these empty space graphics as crumbled graphics in EMC engine
8663 if (crumbled == IMG_EMPTY_SPACE)
8664 has_crumbled_graphics = FALSE;
8666 if (has_crumbled_graphics)
8668 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8669 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8670 g_crumbled->anim_delay,
8671 g_crumbled->anim_mode,
8672 g_crumbled->anim_start_frame,
8675 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8676 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8678 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8679 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8681 g_em->has_crumbled_graphics = TRUE;
8685 g_em->crumbled_bitmap = NULL;
8686 g_em->crumbled_src_x = 0;
8687 g_em->crumbled_src_y = 0;
8688 g_em->crumbled_border_size = 0;
8689 g_em->crumbled_tile_size = 0;
8691 g_em->has_crumbled_graphics = FALSE;
8696 void ResetGfxAnimation_EM(int x, int y, int tile)
8702 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8703 int tile, int frame_em, int x, int y)
8705 int action = object_mapping[tile].action;
8706 int direction = object_mapping[tile].direction;
8707 int effective_element = get_effective_element_EM(tile, frame_em);
8708 int graphic = (direction == MV_NONE ?
8709 el_act2img(effective_element, action) :
8710 el_act_dir2img(effective_element, action, direction));
8711 struct GraphicInfo *g = &graphic_info[graphic];
8713 boolean action_removing = (action == ACTION_DIGGING ||
8714 action == ACTION_SNAPPING ||
8715 action == ACTION_COLLECTING);
8716 boolean action_moving = (action == ACTION_FALLING ||
8717 action == ACTION_MOVING ||
8718 action == ACTION_PUSHING ||
8719 action == ACTION_EATING ||
8720 action == ACTION_FILLING ||
8721 action == ACTION_EMPTYING);
8722 boolean action_falling = (action == ACTION_FALLING ||
8723 action == ACTION_FILLING ||
8724 action == ACTION_EMPTYING);
8726 // special case: graphic uses "2nd movement tile" and has defined
8727 // 7 frames for movement animation (or less) => use default graphic
8728 // for last (8th) frame which ends the movement animation
8729 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8731 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8732 graphic = (direction == MV_NONE ?
8733 el_act2img(effective_element, action) :
8734 el_act_dir2img(effective_element, action, direction));
8736 g = &graphic_info[graphic];
8739 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8743 else if (action_moving)
8745 boolean is_backside = object_mapping[tile].is_backside;
8749 int direction = object_mapping[tile].direction;
8750 int move_dir = (action_falling ? MV_DOWN : direction);
8755 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8756 if (g->double_movement && frame_em == 0)
8760 if (move_dir == MV_LEFT)
8761 GfxFrame[x - 1][y] = GfxFrame[x][y];
8762 else if (move_dir == MV_RIGHT)
8763 GfxFrame[x + 1][y] = GfxFrame[x][y];
8764 else if (move_dir == MV_UP)
8765 GfxFrame[x][y - 1] = GfxFrame[x][y];
8766 else if (move_dir == MV_DOWN)
8767 GfxFrame[x][y + 1] = GfxFrame[x][y];
8774 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8775 if (tile == Xsand_stonesand_quickout_1 ||
8776 tile == Xsand_stonesand_quickout_2)
8780 if (graphic_info[graphic].anim_global_sync)
8781 sync_frame = FrameCounter;
8782 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8783 sync_frame = GfxFrame[x][y];
8785 sync_frame = 0; // playfield border (pseudo steel)
8787 SetRandomAnimationValue(x, y);
8789 int frame = getAnimationFrame(g->anim_frames,
8792 g->anim_start_frame,
8795 g_em->unique_identifier =
8796 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8799 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8800 int tile, int frame_em, int x, int y)
8802 int action = object_mapping[tile].action;
8803 int direction = object_mapping[tile].direction;
8804 boolean is_backside = object_mapping[tile].is_backside;
8805 int effective_element = get_effective_element_EM(tile, frame_em);
8806 int effective_action = action;
8807 int graphic = (direction == MV_NONE ?
8808 el_act2img(effective_element, effective_action) :
8809 el_act_dir2img(effective_element, effective_action,
8811 int crumbled = (direction == MV_NONE ?
8812 el_act2crm(effective_element, effective_action) :
8813 el_act_dir2crm(effective_element, effective_action,
8815 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8816 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8817 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8818 struct GraphicInfo *g = &graphic_info[graphic];
8821 // special case: graphic uses "2nd movement tile" and has defined
8822 // 7 frames for movement animation (or less) => use default graphic
8823 // for last (8th) frame which ends the movement animation
8824 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8826 effective_action = ACTION_DEFAULT;
8827 graphic = (direction == MV_NONE ?
8828 el_act2img(effective_element, effective_action) :
8829 el_act_dir2img(effective_element, effective_action,
8831 crumbled = (direction == MV_NONE ?
8832 el_act2crm(effective_element, effective_action) :
8833 el_act_dir2crm(effective_element, effective_action,
8836 g = &graphic_info[graphic];
8839 if (graphic_info[graphic].anim_global_sync)
8840 sync_frame = FrameCounter;
8841 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8842 sync_frame = GfxFrame[x][y];
8844 sync_frame = 0; // playfield border (pseudo steel)
8846 SetRandomAnimationValue(x, y);
8848 int frame = getAnimationFrame(g->anim_frames,
8851 g->anim_start_frame,
8854 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8855 g->double_movement && is_backside);
8857 // (updating the "crumbled" graphic definitions is probably not really needed,
8858 // as animations for crumbled graphics can't be longer than one EMC cycle)
8859 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8863 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8864 int player_nr, int anim, int frame_em)
8866 int element = player_mapping[player_nr][anim].element_rnd;
8867 int action = player_mapping[player_nr][anim].action;
8868 int direction = player_mapping[player_nr][anim].direction;
8869 int graphic = (direction == MV_NONE ?
8870 el_act2img(element, action) :
8871 el_act_dir2img(element, action, direction));
8872 struct GraphicInfo *g = &graphic_info[graphic];
8875 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8877 stored_player[player_nr].StepFrame = frame_em;
8879 sync_frame = stored_player[player_nr].Frame;
8881 int frame = getAnimationFrame(g->anim_frames,
8884 g->anim_start_frame,
8887 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8888 &g_em->src_x, &g_em->src_y, FALSE);
8891 void InitGraphicInfo_EM(void)
8895 // always start with reliable default values
8896 for (i = 0; i < GAME_TILE_MAX; i++)
8898 object_mapping[i].element_rnd = EL_UNKNOWN;
8899 object_mapping[i].is_backside = FALSE;
8900 object_mapping[i].action = ACTION_DEFAULT;
8901 object_mapping[i].direction = MV_NONE;
8904 // always start with reliable default values
8905 for (p = 0; p < MAX_PLAYERS; p++)
8907 for (i = 0; i < PLY_MAX; i++)
8909 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8910 player_mapping[p][i].action = ACTION_DEFAULT;
8911 player_mapping[p][i].direction = MV_NONE;
8915 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8917 int e = em_object_mapping_list[i].element_em;
8919 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8920 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8922 if (em_object_mapping_list[i].action != -1)
8923 object_mapping[e].action = em_object_mapping_list[i].action;
8925 if (em_object_mapping_list[i].direction != -1)
8926 object_mapping[e].direction =
8927 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8930 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8932 int a = em_player_mapping_list[i].action_em;
8933 int p = em_player_mapping_list[i].player_nr;
8935 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8937 if (em_player_mapping_list[i].action != -1)
8938 player_mapping[p][a].action = em_player_mapping_list[i].action;
8940 if (em_player_mapping_list[i].direction != -1)
8941 player_mapping[p][a].direction =
8942 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8945 for (i = 0; i < GAME_TILE_MAX; i++)
8947 int element = object_mapping[i].element_rnd;
8948 int action = object_mapping[i].action;
8949 int direction = object_mapping[i].direction;
8950 boolean is_backside = object_mapping[i].is_backside;
8951 boolean action_exploding = ((action == ACTION_EXPLODING ||
8952 action == ACTION_SMASHED_BY_ROCK ||
8953 action == ACTION_SMASHED_BY_SPRING) &&
8954 element != EL_DIAMOND);
8955 boolean action_active = (action == ACTION_ACTIVE);
8956 boolean action_other = (action == ACTION_OTHER);
8958 for (j = 0; j < 8; j++)
8960 int effective_element = get_effective_element_EM(i, j);
8961 int effective_action = (j < 7 ? action :
8962 i == Xdrip_stretch ? action :
8963 i == Xdrip_stretchB ? action :
8964 i == Ydrip_1_s ? action :
8965 i == Ydrip_1_sB ? action :
8966 i == Yball_1 ? action :
8967 i == Xball_2 ? action :
8968 i == Yball_2 ? action :
8969 i == Yball_blank ? action :
8970 i == Ykey_1_blank ? action :
8971 i == Ykey_2_blank ? action :
8972 i == Ykey_3_blank ? action :
8973 i == Ykey_4_blank ? action :
8974 i == Ykey_5_blank ? action :
8975 i == Ykey_6_blank ? action :
8976 i == Ykey_7_blank ? action :
8977 i == Ykey_8_blank ? action :
8978 i == Ylenses_blank ? action :
8979 i == Ymagnify_blank ? action :
8980 i == Ygrass_blank ? action :
8981 i == Ydirt_blank ? action :
8982 i == Xsand_stonein_1 ? action :
8983 i == Xsand_stonein_2 ? action :
8984 i == Xsand_stonein_3 ? action :
8985 i == Xsand_stonein_4 ? action :
8986 i == Xsand_stoneout_1 ? action :
8987 i == Xsand_stoneout_2 ? action :
8988 i == Xboom_android ? ACTION_EXPLODING :
8989 action_exploding ? ACTION_EXPLODING :
8990 action_active ? action :
8991 action_other ? action :
8993 int graphic = (el_act_dir2img(effective_element, effective_action,
8995 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8997 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8998 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8999 boolean has_action_graphics = (graphic != base_graphic);
9000 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9001 struct GraphicInfo *g = &graphic_info[graphic];
9002 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9005 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9006 boolean special_animation = (action != ACTION_DEFAULT &&
9007 g->anim_frames == 3 &&
9008 g->anim_delay == 2 &&
9009 g->anim_mode & ANIM_LINEAR);
9010 int sync_frame = (i == Xdrip_stretch ? 7 :
9011 i == Xdrip_stretchB ? 7 :
9012 i == Ydrip_2_s ? j + 8 :
9013 i == Ydrip_2_sB ? j + 8 :
9022 i == Xfake_acid_1 ? 0 :
9023 i == Xfake_acid_2 ? 10 :
9024 i == Xfake_acid_3 ? 20 :
9025 i == Xfake_acid_4 ? 30 :
9026 i == Xfake_acid_5 ? 40 :
9027 i == Xfake_acid_6 ? 50 :
9028 i == Xfake_acid_7 ? 60 :
9029 i == Xfake_acid_8 ? 70 :
9030 i == Xfake_acid_1_player ? 0 :
9031 i == Xfake_acid_2_player ? 10 :
9032 i == Xfake_acid_3_player ? 20 :
9033 i == Xfake_acid_4_player ? 30 :
9034 i == Xfake_acid_5_player ? 40 :
9035 i == Xfake_acid_6_player ? 50 :
9036 i == Xfake_acid_7_player ? 60 :
9037 i == Xfake_acid_8_player ? 70 :
9039 i == Yball_2 ? j + 8 :
9040 i == Yball_blank ? j + 1 :
9041 i == Ykey_1_blank ? j + 1 :
9042 i == Ykey_2_blank ? j + 1 :
9043 i == Ykey_3_blank ? j + 1 :
9044 i == Ykey_4_blank ? j + 1 :
9045 i == Ykey_5_blank ? j + 1 :
9046 i == Ykey_6_blank ? j + 1 :
9047 i == Ykey_7_blank ? j + 1 :
9048 i == Ykey_8_blank ? j + 1 :
9049 i == Ylenses_blank ? j + 1 :
9050 i == Ymagnify_blank ? j + 1 :
9051 i == Ygrass_blank ? j + 1 :
9052 i == Ydirt_blank ? j + 1 :
9053 i == Xamoeba_1 ? 0 :
9054 i == Xamoeba_2 ? 1 :
9055 i == Xamoeba_3 ? 2 :
9056 i == Xamoeba_4 ? 3 :
9057 i == Xamoeba_5 ? 0 :
9058 i == Xamoeba_6 ? 1 :
9059 i == Xamoeba_7 ? 2 :
9060 i == Xamoeba_8 ? 3 :
9061 i == Xexit_2 ? j + 8 :
9062 i == Xexit_3 ? j + 16 :
9063 i == Xdynamite_1 ? 0 :
9064 i == Xdynamite_2 ? 8 :
9065 i == Xdynamite_3 ? 16 :
9066 i == Xdynamite_4 ? 24 :
9067 i == Xsand_stonein_1 ? j + 1 :
9068 i == Xsand_stonein_2 ? j + 9 :
9069 i == Xsand_stonein_3 ? j + 17 :
9070 i == Xsand_stonein_4 ? j + 25 :
9071 i == Xsand_stoneout_1 && j == 0 ? 0 :
9072 i == Xsand_stoneout_1 && j == 1 ? 0 :
9073 i == Xsand_stoneout_1 && j == 2 ? 1 :
9074 i == Xsand_stoneout_1 && j == 3 ? 2 :
9075 i == Xsand_stoneout_1 && j == 4 ? 2 :
9076 i == Xsand_stoneout_1 && j == 5 ? 3 :
9077 i == Xsand_stoneout_1 && j == 6 ? 4 :
9078 i == Xsand_stoneout_1 && j == 7 ? 4 :
9079 i == Xsand_stoneout_2 && j == 0 ? 5 :
9080 i == Xsand_stoneout_2 && j == 1 ? 6 :
9081 i == Xsand_stoneout_2 && j == 2 ? 7 :
9082 i == Xsand_stoneout_2 && j == 3 ? 8 :
9083 i == Xsand_stoneout_2 && j == 4 ? 9 :
9084 i == Xsand_stoneout_2 && j == 5 ? 11 :
9085 i == Xsand_stoneout_2 && j == 6 ? 13 :
9086 i == Xsand_stoneout_2 && j == 7 ? 15 :
9087 i == Xboom_bug && j == 1 ? 2 :
9088 i == Xboom_bug && j == 2 ? 2 :
9089 i == Xboom_bug && j == 3 ? 4 :
9090 i == Xboom_bug && j == 4 ? 4 :
9091 i == Xboom_bug && j == 5 ? 2 :
9092 i == Xboom_bug && j == 6 ? 2 :
9093 i == Xboom_bug && j == 7 ? 0 :
9094 i == Xboom_tank && j == 1 ? 2 :
9095 i == Xboom_tank && j == 2 ? 2 :
9096 i == Xboom_tank && j == 3 ? 4 :
9097 i == Xboom_tank && j == 4 ? 4 :
9098 i == Xboom_tank && j == 5 ? 2 :
9099 i == Xboom_tank && j == 6 ? 2 :
9100 i == Xboom_tank && j == 7 ? 0 :
9101 i == Xboom_android && j == 7 ? 6 :
9102 i == Xboom_1 && j == 1 ? 2 :
9103 i == Xboom_1 && j == 2 ? 2 :
9104 i == Xboom_1 && j == 3 ? 4 :
9105 i == Xboom_1 && j == 4 ? 4 :
9106 i == Xboom_1 && j == 5 ? 6 :
9107 i == Xboom_1 && j == 6 ? 6 :
9108 i == Xboom_1 && j == 7 ? 8 :
9109 i == Xboom_2 && j == 0 ? 8 :
9110 i == Xboom_2 && j == 1 ? 8 :
9111 i == Xboom_2 && j == 2 ? 10 :
9112 i == Xboom_2 && j == 3 ? 10 :
9113 i == Xboom_2 && j == 4 ? 10 :
9114 i == Xboom_2 && j == 5 ? 12 :
9115 i == Xboom_2 && j == 6 ? 12 :
9116 i == Xboom_2 && j == 7 ? 12 :
9117 special_animation && j == 4 ? 3 :
9118 effective_action != action ? 0 :
9120 int frame = getAnimationFrame(g->anim_frames,
9123 g->anim_start_frame,
9126 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9127 g->double_movement && is_backside);
9129 g_em->bitmap = src_bitmap;
9130 g_em->src_x = src_x;
9131 g_em->src_y = src_y;
9132 g_em->src_offset_x = 0;
9133 g_em->src_offset_y = 0;
9134 g_em->dst_offset_x = 0;
9135 g_em->dst_offset_y = 0;
9136 g_em->width = TILEX;
9137 g_em->height = TILEY;
9139 g_em->preserve_background = FALSE;
9141 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9144 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9145 effective_action == ACTION_MOVING ||
9146 effective_action == ACTION_PUSHING ||
9147 effective_action == ACTION_EATING)) ||
9148 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9149 effective_action == ACTION_EMPTYING)))
9152 (effective_action == ACTION_FALLING ||
9153 effective_action == ACTION_FILLING ||
9154 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9155 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9156 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9157 int num_steps = (i == Ydrip_1_s ? 16 :
9158 i == Ydrip_1_sB ? 16 :
9159 i == Ydrip_2_s ? 16 :
9160 i == Ydrip_2_sB ? 16 :
9161 i == Xsand_stonein_1 ? 32 :
9162 i == Xsand_stonein_2 ? 32 :
9163 i == Xsand_stonein_3 ? 32 :
9164 i == Xsand_stonein_4 ? 32 :
9165 i == Xsand_stoneout_1 ? 16 :
9166 i == Xsand_stoneout_2 ? 16 : 8);
9167 int cx = ABS(dx) * (TILEX / num_steps);
9168 int cy = ABS(dy) * (TILEY / num_steps);
9169 int step_frame = (i == Ydrip_2_s ? j + 8 :
9170 i == Ydrip_2_sB ? j + 8 :
9171 i == Xsand_stonein_2 ? j + 8 :
9172 i == Xsand_stonein_3 ? j + 16 :
9173 i == Xsand_stonein_4 ? j + 24 :
9174 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9175 int step = (is_backside ? step_frame : num_steps - step_frame);
9177 if (is_backside) // tile where movement starts
9179 if (dx < 0 || dy < 0)
9181 g_em->src_offset_x = cx * step;
9182 g_em->src_offset_y = cy * step;
9186 g_em->dst_offset_x = cx * step;
9187 g_em->dst_offset_y = cy * step;
9190 else // tile where movement ends
9192 if (dx < 0 || dy < 0)
9194 g_em->dst_offset_x = cx * step;
9195 g_em->dst_offset_y = cy * step;
9199 g_em->src_offset_x = cx * step;
9200 g_em->src_offset_y = cy * step;
9204 g_em->width = TILEX - cx * step;
9205 g_em->height = TILEY - cy * step;
9208 // create unique graphic identifier to decide if tile must be redrawn
9209 /* bit 31 - 16 (16 bit): EM style graphic
9210 bit 15 - 12 ( 4 bit): EM style frame
9211 bit 11 - 6 ( 6 bit): graphic width
9212 bit 5 - 0 ( 6 bit): graphic height */
9213 g_em->unique_identifier =
9214 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9218 for (i = 0; i < GAME_TILE_MAX; i++)
9220 for (j = 0; j < 8; j++)
9222 int element = object_mapping[i].element_rnd;
9223 int action = object_mapping[i].action;
9224 int direction = object_mapping[i].direction;
9225 boolean is_backside = object_mapping[i].is_backside;
9226 int graphic_action = el_act_dir2img(element, action, direction);
9227 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9229 if ((action == ACTION_SMASHED_BY_ROCK ||
9230 action == ACTION_SMASHED_BY_SPRING ||
9231 action == ACTION_EATING) &&
9232 graphic_action == graphic_default)
9234 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9235 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9236 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9237 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9240 // no separate animation for "smashed by rock" -- use rock instead
9241 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9242 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9244 g_em->bitmap = g_xx->bitmap;
9245 g_em->src_x = g_xx->src_x;
9246 g_em->src_y = g_xx->src_y;
9247 g_em->src_offset_x = g_xx->src_offset_x;
9248 g_em->src_offset_y = g_xx->src_offset_y;
9249 g_em->dst_offset_x = g_xx->dst_offset_x;
9250 g_em->dst_offset_y = g_xx->dst_offset_y;
9251 g_em->width = g_xx->width;
9252 g_em->height = g_xx->height;
9253 g_em->unique_identifier = g_xx->unique_identifier;
9256 g_em->preserve_background = TRUE;
9261 for (p = 0; p < MAX_PLAYERS; p++)
9263 for (i = 0; i < PLY_MAX; i++)
9265 int element = player_mapping[p][i].element_rnd;
9266 int action = player_mapping[p][i].action;
9267 int direction = player_mapping[p][i].direction;
9269 for (j = 0; j < 8; j++)
9271 int effective_element = element;
9272 int effective_action = action;
9273 int graphic = (direction == MV_NONE ?
9274 el_act2img(effective_element, effective_action) :
9275 el_act_dir2img(effective_element, effective_action,
9277 struct GraphicInfo *g = &graphic_info[graphic];
9278 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9282 int frame = getAnimationFrame(g->anim_frames,
9285 g->anim_start_frame,
9288 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9290 g_em->bitmap = src_bitmap;
9291 g_em->src_x = src_x;
9292 g_em->src_y = src_y;
9293 g_em->src_offset_x = 0;
9294 g_em->src_offset_y = 0;
9295 g_em->dst_offset_x = 0;
9296 g_em->dst_offset_y = 0;
9297 g_em->width = TILEX;
9298 g_em->height = TILEY;
9304 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9305 boolean any_player_moving,
9306 boolean any_player_snapping,
9307 boolean any_player_dropping)
9309 if (frame == 7 && !any_player_dropping)
9311 if (!local_player->was_waiting)
9313 if (!CheckSaveEngineSnapshotToList())
9316 local_player->was_waiting = TRUE;
9319 else if (any_player_moving || any_player_snapping || any_player_dropping)
9321 local_player->was_waiting = FALSE;
9325 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9326 boolean murphy_is_dropping)
9328 if (murphy_is_waiting)
9330 if (!local_player->was_waiting)
9332 if (!CheckSaveEngineSnapshotToList())
9335 local_player->was_waiting = TRUE;
9340 local_player->was_waiting = FALSE;
9344 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9345 boolean button_released)
9347 if (button_released)
9349 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9350 CheckSaveEngineSnapshotToList();
9352 else if (element_clicked)
9354 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9355 CheckSaveEngineSnapshotToList();
9357 game.snapshot.changed_action = TRUE;
9361 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9362 boolean any_player_moving,
9363 boolean any_player_snapping,
9364 boolean any_player_dropping)
9366 if (tape.single_step && tape.recording && !tape.pausing)
9367 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9368 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9370 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9371 any_player_snapping, any_player_dropping);
9373 return tape.pausing;
9376 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9377 boolean murphy_is_dropping)
9379 boolean murphy_starts_dropping = FALSE;
9382 for (i = 0; i < MAX_PLAYERS; i++)
9383 if (stored_player[i].force_dropping)
9384 murphy_starts_dropping = TRUE;
9386 if (tape.single_step && tape.recording && !tape.pausing)
9387 if (murphy_is_waiting && !murphy_starts_dropping)
9388 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9390 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9393 void CheckSingleStepMode_MM(boolean element_clicked,
9394 boolean button_released)
9396 if (tape.single_step && tape.recording && !tape.pausing)
9397 if (button_released)
9398 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9400 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9403 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9404 int graphic, int sync_frame, int x, int y)
9406 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9408 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9411 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9413 return (IS_NEXT_FRAME(sync_frame, graphic));
9416 int getGraphicInfo_Delay(int graphic)
9418 return graphic_info[graphic].anim_delay;
9421 void PlayMenuSoundExt(int sound)
9423 if (sound == SND_UNDEFINED)
9426 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9427 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9430 if (IS_LOOP_SOUND(sound))
9431 PlaySoundLoop(sound);
9436 void PlayMenuSound(void)
9438 PlayMenuSoundExt(menu.sound[game_status]);
9441 void PlayMenuSoundStereo(int sound, int stereo_position)
9443 if (sound == SND_UNDEFINED)
9446 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9447 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9450 if (IS_LOOP_SOUND(sound))
9451 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9453 PlaySoundStereo(sound, stereo_position);
9456 void PlayMenuSoundIfLoopExt(int sound)
9458 if (sound == SND_UNDEFINED)
9461 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9462 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9465 if (IS_LOOP_SOUND(sound))
9466 PlaySoundLoop(sound);
9469 void PlayMenuSoundIfLoop(void)
9471 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9474 void PlayMenuMusicExt(int music)
9476 if (music == MUS_UNDEFINED)
9479 if (!setup.sound_music)
9482 if (IS_LOOP_MUSIC(music))
9483 PlayMusicLoop(music);
9488 void PlayMenuMusic(void)
9490 char *curr_music = getCurrentlyPlayingMusicFilename();
9491 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9493 if (!strEqual(curr_music, next_music))
9494 PlayMenuMusicExt(menu.music[game_status]);
9497 void PlayMenuSoundsAndMusic(void)
9503 static void FadeMenuSounds(void)
9508 static void FadeMenuMusic(void)
9510 char *curr_music = getCurrentlyPlayingMusicFilename();
9511 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9513 if (!strEqual(curr_music, next_music))
9517 void FadeMenuSoundsAndMusic(void)
9523 void PlaySoundActivating(void)
9526 PlaySound(SND_MENU_ITEM_ACTIVATING);
9530 void PlaySoundSelecting(void)
9533 PlaySound(SND_MENU_ITEM_SELECTING);
9537 void ToggleFullscreenIfNeeded(void)
9539 // if setup and video fullscreen state are already matching, nothing do do
9540 if (setup.fullscreen == video.fullscreen_enabled ||
9541 !video.fullscreen_available)
9544 SDLSetWindowFullscreen(setup.fullscreen);
9546 // set setup value according to successfully changed fullscreen mode
9547 setup.fullscreen = video.fullscreen_enabled;
9550 void ChangeWindowScalingIfNeeded(void)
9552 // if setup and video window scaling are already matching, nothing do do
9553 if (setup.window_scaling_percent == video.window_scaling_percent ||
9554 video.fullscreen_enabled)
9557 SDLSetWindowScaling(setup.window_scaling_percent);
9559 // set setup value according to successfully changed window scaling
9560 setup.window_scaling_percent = video.window_scaling_percent;
9563 void ChangeVsyncModeIfNeeded(void)
9565 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9566 int video_vsync_mode = video.vsync_mode;
9568 // if setup and video vsync mode are already matching, nothing do do
9569 if (setup_vsync_mode == video_vsync_mode)
9572 // if renderer is using OpenGL, vsync mode can directly be changed
9573 SDLSetScreenVsyncMode(setup.vsync_mode);
9575 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9576 if (video.vsync_mode == video_vsync_mode)
9578 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9580 // save backbuffer content which gets lost when re-creating screen
9581 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9583 // force re-creating screen and renderer to set new vsync mode
9584 video.fullscreen_enabled = !setup.fullscreen;
9586 // when creating new renderer, destroy textures linked to old renderer
9587 FreeAllImageTextures(); // needs old renderer to free the textures
9589 // re-create screen and renderer (including change of vsync mode)
9590 ChangeVideoModeIfNeeded(setup.fullscreen);
9592 // set setup value according to successfully changed fullscreen mode
9593 setup.fullscreen = video.fullscreen_enabled;
9595 // restore backbuffer content from temporary backbuffer backup bitmap
9596 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9597 FreeBitmap(tmp_backbuffer);
9599 // update visible window/screen
9600 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9602 // when changing vsync mode, re-create textures for new renderer
9603 InitImageTextures();
9606 // set setup value according to successfully changed vsync mode
9607 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9610 static void JoinRectangles(int *x, int *y, int *width, int *height,
9611 int x2, int y2, int width2, int height2)
9613 // do not join with "off-screen" rectangle
9614 if (x2 == -1 || y2 == -1)
9619 *width = MAX(*width, width2);
9620 *height = MAX(*height, height2);
9623 void SetAnimStatus(int anim_status_new)
9625 if (anim_status_new == GAME_MODE_MAIN)
9626 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9627 else if (anim_status_new == GAME_MODE_NAMES)
9628 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9629 else if (anim_status_new == GAME_MODE_SCORES)
9630 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9632 global.anim_status_next = anim_status_new;
9634 // directly set screen modes that are entered without fading
9635 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9636 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9637 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9638 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9639 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9640 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9641 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9642 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9643 global.anim_status = global.anim_status_next;
9646 void SetGameStatus(int game_status_new)
9648 if (game_status_new != game_status)
9649 game_status_last_screen = game_status;
9651 game_status = game_status_new;
9653 SetAnimStatus(game_status_new);
9656 void SetFontStatus(int game_status_new)
9658 static int last_game_status = -1;
9660 if (game_status_new != -1)
9662 // set game status for font use after storing last game status
9663 last_game_status = game_status;
9664 game_status = game_status_new;
9668 // reset game status after font use from last stored game status
9669 game_status = last_game_status;
9673 void ResetFontStatus(void)
9678 void SetLevelSetInfo(char *identifier, int level_nr)
9680 setString(&levelset.identifier, identifier);
9682 levelset.level_nr = level_nr;
9685 boolean CheckIfAllViewportsHaveChanged(void)
9687 // if game status has not changed, viewports have not changed either
9688 if (game_status == game_status_last)
9691 // check if all viewports have changed with current game status
9693 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9694 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9695 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9696 int new_real_sx = vp_playfield->x;
9697 int new_real_sy = vp_playfield->y;
9698 int new_full_sxsize = vp_playfield->width;
9699 int new_full_sysize = vp_playfield->height;
9700 int new_dx = vp_door_1->x;
9701 int new_dy = vp_door_1->y;
9702 int new_dxsize = vp_door_1->width;
9703 int new_dysize = vp_door_1->height;
9704 int new_vx = vp_door_2->x;
9705 int new_vy = vp_door_2->y;
9706 int new_vxsize = vp_door_2->width;
9707 int new_vysize = vp_door_2->height;
9709 boolean playfield_viewport_has_changed =
9710 (new_real_sx != REAL_SX ||
9711 new_real_sy != REAL_SY ||
9712 new_full_sxsize != FULL_SXSIZE ||
9713 new_full_sysize != FULL_SYSIZE);
9715 boolean door_1_viewport_has_changed =
9718 new_dxsize != DXSIZE ||
9719 new_dysize != DYSIZE);
9721 boolean door_2_viewport_has_changed =
9724 new_vxsize != VXSIZE ||
9725 new_vysize != VYSIZE ||
9726 game_status_last == GAME_MODE_EDITOR);
9728 return (playfield_viewport_has_changed &&
9729 door_1_viewport_has_changed &&
9730 door_2_viewport_has_changed);
9733 boolean CheckFadeAll(void)
9735 return (CheckIfGlobalBorderHasChanged() ||
9736 CheckIfAllViewportsHaveChanged());
9739 void ChangeViewportPropertiesIfNeeded(void)
9741 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9742 FALSE : setup.small_game_graphics);
9743 int gfx_game_mode = (game_status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
9745 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9747 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9748 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9749 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9750 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9751 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9752 int new_win_xsize = vp_window->width;
9753 int new_win_ysize = vp_window->height;
9754 int border_left = vp_playfield->border_left;
9755 int border_right = vp_playfield->border_right;
9756 int border_top = vp_playfield->border_top;
9757 int border_bottom = vp_playfield->border_bottom;
9758 int new_sx = vp_playfield->x + border_left;
9759 int new_sy = vp_playfield->y + border_top;
9760 int new_sxsize = vp_playfield->width - border_left - border_right;
9761 int new_sysize = vp_playfield->height - border_top - border_bottom;
9762 int new_real_sx = vp_playfield->x;
9763 int new_real_sy = vp_playfield->y;
9764 int new_full_sxsize = vp_playfield->width;
9765 int new_full_sysize = vp_playfield->height;
9766 int new_dx = vp_door_1->x;
9767 int new_dy = vp_door_1->y;
9768 int new_dxsize = vp_door_1->width;
9769 int new_dysize = vp_door_1->height;
9770 int new_vx = vp_door_2->x;
9771 int new_vy = vp_door_2->y;
9772 int new_vxsize = vp_door_2->width;
9773 int new_vysize = vp_door_2->height;
9774 int new_ex = vp_door_3->x;
9775 int new_ey = vp_door_3->y;
9776 int new_exsize = vp_door_3->width;
9777 int new_eysize = vp_door_3->height;
9778 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9779 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9780 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9781 int new_scr_fieldx = new_sxsize / tilesize;
9782 int new_scr_fieldy = new_sysize / tilesize;
9783 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9784 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9785 boolean init_gfx_buffers = FALSE;
9786 boolean init_video_buffer = FALSE;
9787 boolean init_gadgets_and_anims = FALSE;
9788 boolean init_em_graphics = FALSE;
9790 if (new_win_xsize != WIN_XSIZE ||
9791 new_win_ysize != WIN_YSIZE)
9793 WIN_XSIZE = new_win_xsize;
9794 WIN_YSIZE = new_win_ysize;
9796 init_video_buffer = TRUE;
9797 init_gfx_buffers = TRUE;
9798 init_gadgets_and_anims = TRUE;
9800 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9803 if (new_scr_fieldx != SCR_FIELDX ||
9804 new_scr_fieldy != SCR_FIELDY)
9806 // this always toggles between MAIN and GAME when using small tile size
9808 SCR_FIELDX = new_scr_fieldx;
9809 SCR_FIELDY = new_scr_fieldy;
9811 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9822 new_sxsize != SXSIZE ||
9823 new_sysize != SYSIZE ||
9824 new_dxsize != DXSIZE ||
9825 new_dysize != DYSIZE ||
9826 new_vxsize != VXSIZE ||
9827 new_vysize != VYSIZE ||
9828 new_exsize != EXSIZE ||
9829 new_eysize != EYSIZE ||
9830 new_real_sx != REAL_SX ||
9831 new_real_sy != REAL_SY ||
9832 new_full_sxsize != FULL_SXSIZE ||
9833 new_full_sysize != FULL_SYSIZE ||
9834 new_tilesize_var != TILESIZE_VAR
9837 // ------------------------------------------------------------------------
9838 // determine next fading area for changed viewport definitions
9839 // ------------------------------------------------------------------------
9841 // start with current playfield area (default fading area)
9844 FADE_SXSIZE = FULL_SXSIZE;
9845 FADE_SYSIZE = FULL_SYSIZE;
9847 // add new playfield area if position or size has changed
9848 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9849 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9851 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9852 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9855 // add current and new door 1 area if position or size has changed
9856 if (new_dx != DX || new_dy != DY ||
9857 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9859 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9860 DX, DY, DXSIZE, DYSIZE);
9861 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9862 new_dx, new_dy, new_dxsize, new_dysize);
9865 // add current and new door 2 area if position or size has changed
9866 if (new_vx != VX || new_vy != VY ||
9867 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9869 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9870 VX, VY, VXSIZE, VYSIZE);
9871 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9872 new_vx, new_vy, new_vxsize, new_vysize);
9875 // ------------------------------------------------------------------------
9876 // handle changed tile size
9877 // ------------------------------------------------------------------------
9879 if (new_tilesize_var != TILESIZE_VAR)
9881 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9883 // changing tile size invalidates scroll values of engine snapshots
9884 FreeEngineSnapshotSingle();
9886 // changing tile size requires update of graphic mapping for EM engine
9887 init_em_graphics = TRUE;
9898 SXSIZE = new_sxsize;
9899 SYSIZE = new_sysize;
9900 DXSIZE = new_dxsize;
9901 DYSIZE = new_dysize;
9902 VXSIZE = new_vxsize;
9903 VYSIZE = new_vysize;
9904 EXSIZE = new_exsize;
9905 EYSIZE = new_eysize;
9906 REAL_SX = new_real_sx;
9907 REAL_SY = new_real_sy;
9908 FULL_SXSIZE = new_full_sxsize;
9909 FULL_SYSIZE = new_full_sysize;
9910 TILESIZE_VAR = new_tilesize_var;
9912 init_gfx_buffers = TRUE;
9913 init_gadgets_and_anims = TRUE;
9915 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9916 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9919 if (init_gfx_buffers)
9921 // Debug("tools:viewport", "init_gfx_buffers");
9923 SCR_FIELDX = new_scr_fieldx_buffers;
9924 SCR_FIELDY = new_scr_fieldy_buffers;
9928 SCR_FIELDX = new_scr_fieldx;
9929 SCR_FIELDY = new_scr_fieldy;
9931 SetDrawDeactivationMask(REDRAW_NONE);
9932 SetDrawBackgroundMask(REDRAW_FIELD);
9935 if (init_video_buffer)
9937 // Debug("tools:viewport", "init_video_buffer");
9939 FreeAllImageTextures(); // needs old renderer to free the textures
9941 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9942 InitImageTextures();
9945 if (init_gadgets_and_anims)
9947 // Debug("tools:viewport", "init_gadgets_and_anims");
9950 InitGlobalAnimations();
9953 if (init_em_graphics)
9955 InitGraphicInfo_EM();
9959 void OpenURL(char *url)
9964 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9966 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9970 // ============================================================================
9972 // ============================================================================
9974 #if defined(PLATFORM_WIN32)
9975 /* FILETIME of Jan 1 1970 00:00:00. */
9976 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9979 * timezone information is stored outside the kernel so tzp isn't used anymore.
9981 * Note: this function is not for Win32 high precision timing purpose. See
9984 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9987 SYSTEMTIME system_time;
9988 ULARGE_INTEGER ularge;
9990 GetSystemTime(&system_time);
9991 SystemTimeToFileTime(&system_time, &file_time);
9992 ularge.LowPart = file_time.dwLowDateTime;
9993 ularge.HighPart = file_time.dwHighDateTime;
9995 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9996 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10002 static char *test_init_uuid_random_function_simple(void)
10004 static char seed_text[100];
10005 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10007 sprintf(seed_text, "%d", seed);
10012 static char *test_init_uuid_random_function_better(void)
10014 static char seed_text[100];
10015 struct timeval current_time;
10017 gettimeofday(¤t_time, NULL);
10019 prng_seed_bytes(¤t_time, sizeof(current_time));
10021 sprintf(seed_text, "%ld.%ld",
10022 (long)current_time.tv_sec,
10023 (long)current_time.tv_usec);
10028 #if defined(PLATFORM_WIN32)
10029 static char *test_init_uuid_random_function_better_windows(void)
10031 static char seed_text[100];
10032 struct timeval current_time;
10034 gettimeofday_windows(¤t_time, NULL);
10036 prng_seed_bytes(¤t_time, sizeof(current_time));
10038 sprintf(seed_text, "%ld.%ld",
10039 (long)current_time.tv_sec,
10040 (long)current_time.tv_usec);
10046 static unsigned int test_uuid_random_function_simple(int max)
10048 return GetSimpleRandom(max);
10051 static unsigned int test_uuid_random_function_better(int max)
10053 return (max > 0 ? prng_get_uint() % max : 0);
10056 #if defined(PLATFORM_WIN32)
10057 #define NUM_UUID_TESTS 3
10059 #define NUM_UUID_TESTS 2
10062 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10064 struct hashtable *hash_seeds =
10065 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10066 struct hashtable *hash_uuids =
10067 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10068 static char message[100];
10071 char *random_name = (nr == 0 ? "simple" : "better");
10072 char *random_type = (always_seed ? "always" : "only once");
10073 char *(*init_random_function)(void) =
10075 test_init_uuid_random_function_simple :
10076 test_init_uuid_random_function_better);
10077 unsigned int (*random_function)(int) =
10079 test_uuid_random_function_simple :
10080 test_uuid_random_function_better);
10083 #if defined(PLATFORM_WIN32)
10086 random_name = "windows";
10087 init_random_function = test_init_uuid_random_function_better_windows;
10093 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10094 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10096 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10097 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10098 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10100 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10104 // always initialize random number generator at least once
10105 init_random_function();
10107 unsigned int time_start = SDL_GetTicks();
10109 for (i = 0; i < num_uuids; i++)
10113 char *seed = getStringCopy(init_random_function());
10115 hashtable_remove(hash_seeds, seed);
10116 hashtable_insert(hash_seeds, seed, "1");
10119 char *uuid = getStringCopy(getUUIDExt(random_function));
10121 hashtable_remove(hash_uuids, uuid);
10122 hashtable_insert(hash_uuids, uuid, "1");
10125 int num_unique_seeds = hashtable_count(hash_seeds);
10126 int num_unique_uuids = hashtable_count(hash_uuids);
10128 unsigned int time_needed = SDL_GetTicks() - time_start;
10130 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10132 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10135 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10137 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10138 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10140 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10142 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10144 Request(message, REQ_CONFIRM);
10146 hashtable_destroy(hash_seeds, 0);
10147 hashtable_destroy(hash_uuids, 0);
10150 void TestGeneratingUUIDs(void)
10152 int num_uuids = 1000000;
10155 for (i = 0; i < NUM_UUID_TESTS; i++)
10156 for (j = 0; j < 2; j++)
10157 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10159 CloseAllAndExit(0);