1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 // may happen for "border.draw_masked.*" with undefined "global.border.*"
516 if (src_bitmap == NULL)
519 if (x == -1 && y == -1)
522 if (draw_target == DRAW_TO_SCREEN)
523 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
525 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
528 static void DrawMaskedBorderExt_FIELD(int draw_target)
530 if (global.border_status >= GAME_MODE_MAIN &&
531 global.border_status <= GAME_MODE_PLAYING &&
532 border.draw_masked[global.border_status])
533 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
537 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
539 // when drawing to backbuffer, never draw border over open doors
540 if (draw_target == DRAW_TO_BACKBUFFER &&
541 (GetDoorState() & DOOR_OPEN_1))
544 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
545 (global.border_status != GAME_MODE_EDITOR ||
546 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
547 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
550 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
552 // when drawing to backbuffer, never draw border over open doors
553 if (draw_target == DRAW_TO_BACKBUFFER &&
554 (GetDoorState() & DOOR_OPEN_2))
557 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
558 global.border_status != GAME_MODE_EDITOR)
559 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
562 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
564 // currently not available
567 static void DrawMaskedBorderExt_ALL(int draw_target)
569 DrawMaskedBorderExt_FIELD(draw_target);
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 DrawMaskedBorderExt_DOOR_2(draw_target);
572 DrawMaskedBorderExt_DOOR_3(draw_target);
575 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
577 // never draw masked screen borders on borderless screens
578 if (global.border_status == GAME_MODE_LOADING ||
579 global.border_status == GAME_MODE_TITLE)
582 if (redraw_mask & REDRAW_ALL)
583 DrawMaskedBorderExt_ALL(draw_target);
586 if (redraw_mask & REDRAW_FIELD)
587 DrawMaskedBorderExt_FIELD(draw_target);
588 if (redraw_mask & REDRAW_DOOR_1)
589 DrawMaskedBorderExt_DOOR_1(draw_target);
590 if (redraw_mask & REDRAW_DOOR_2)
591 DrawMaskedBorderExt_DOOR_2(draw_target);
592 if (redraw_mask & REDRAW_DOOR_3)
593 DrawMaskedBorderExt_DOOR_3(draw_target);
597 void DrawMaskedBorder_FIELD(void)
599 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
602 void DrawMaskedBorder(int redraw_mask)
604 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
607 void DrawMaskedBorderToTarget(int draw_target)
609 if (draw_target == DRAW_TO_BACKBUFFER ||
610 draw_target == DRAW_TO_SCREEN)
612 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
616 int last_border_status = global.border_status;
618 if (draw_target == DRAW_TO_FADE_SOURCE)
620 global.border_status = gfx.fade_border_source_status;
621 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
623 else if (draw_target == DRAW_TO_FADE_TARGET)
625 global.border_status = gfx.fade_border_target_status;
626 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
629 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
631 global.border_status = last_border_status;
632 gfx.masked_border_bitmap_ptr = backbuffer;
636 void DrawTileCursor(int draw_target)
638 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
641 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
643 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
646 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
648 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
649 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
651 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
654 void BlitScreenToBitmap(Bitmap *target_bitmap)
656 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
657 BlitScreenToBitmap_EM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
659 BlitScreenToBitmap_SP(target_bitmap);
660 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
661 BlitScreenToBitmap_MM(target_bitmap);
662 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
663 BlitScreenToBitmap_RND(target_bitmap);
665 redraw_mask |= REDRAW_FIELD;
668 static void DrawFramesPerSecond(void)
671 int font_nr = FONT_TEXT_2;
672 int font_width = getFontWidth(font_nr);
673 int draw_deactivation_mask = GetDrawDeactivationMask();
674 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
676 // draw FPS with leading space (needed if field buffer deactivated)
677 sprintf(text, " %04.1f fps", global.frames_per_second);
679 // override draw deactivation mask (required for invisible warp mode)
680 SetDrawDeactivationMask(REDRAW_NONE);
682 // draw opaque FPS if field buffer deactivated, else draw masked FPS
683 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
684 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
686 // set draw deactivation mask to previous value
687 SetDrawDeactivationMask(draw_deactivation_mask);
689 // force full-screen redraw in this frame
690 redraw_mask = REDRAW_ALL;
694 static void PrintFrameTimeDebugging(void)
696 static unsigned int last_counter = 0;
697 unsigned int counter = Counter();
698 int diff_1 = counter - last_counter;
699 int diff_2 = diff_1 - GAME_FRAME_DELAY;
701 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
702 char diff_bar[2 * diff_2_max + 5];
706 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
708 for (i = 0; i < diff_2_max; i++)
709 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
710 i >= diff_2_max - diff_2_cut ? '-' : ' ');
712 diff_bar[pos++] = '|';
714 for (i = 0; i < diff_2_max; i++)
715 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
717 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
719 diff_bar[pos++] = '\0';
721 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
724 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
727 last_counter = counter;
731 static int unifiedRedrawMask(int mask)
733 if (mask & REDRAW_ALL)
736 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
742 static boolean equalRedrawMasks(int mask_1, int mask_2)
744 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
747 void BackToFront(void)
749 static int last_redraw_mask = REDRAW_NONE;
751 // force screen redraw in every frame to continue drawing global animations
752 // (but always use the last redraw mask to prevent unwanted side effects)
753 if (redraw_mask == REDRAW_NONE)
754 redraw_mask = last_redraw_mask;
756 last_redraw_mask = redraw_mask;
759 // masked border now drawn immediately when blitting backbuffer to window
761 // draw masked border to all viewports, if defined
762 DrawMaskedBorder(redraw_mask);
765 // draw frames per second (only if debug mode is enabled)
766 if (redraw_mask & REDRAW_FPS)
767 DrawFramesPerSecond();
769 // remove playfield redraw before potentially merging with doors redraw
770 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
771 redraw_mask &= ~REDRAW_FIELD;
773 // redraw complete window if both playfield and (some) doors need redraw
774 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
775 redraw_mask = REDRAW_ALL;
777 /* although redrawing the whole window would be fine for normal gameplay,
778 being able to only redraw the playfield is required for deactivating
779 certain drawing areas (mainly playfield) to work, which is needed for
780 warp-forward to be fast enough (by skipping redraw of most frames) */
782 if (redraw_mask & REDRAW_ALL)
784 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
786 else if (redraw_mask & REDRAW_FIELD)
788 BlitBitmap(backbuffer, window,
789 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
791 else if (redraw_mask & REDRAW_DOORS)
793 // merge door areas to prevent calling screen redraw more than once
799 if (redraw_mask & REDRAW_DOOR_1)
803 x2 = MAX(x2, DX + DXSIZE);
804 y2 = MAX(y2, DY + DYSIZE);
807 if (redraw_mask & REDRAW_DOOR_2)
811 x2 = MAX(x2, VX + VXSIZE);
812 y2 = MAX(y2, VY + VYSIZE);
815 if (redraw_mask & REDRAW_DOOR_3)
819 x2 = MAX(x2, EX + EXSIZE);
820 y2 = MAX(y2, EY + EYSIZE);
823 // make sure that at least one pixel is blitted, and inside the screen
824 // (else nothing is blitted, causing the animations not to be updated)
825 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
826 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
827 x2 = MIN(MAX(1, x2), WIN_XSIZE);
828 y2 = MIN(MAX(1, y2), WIN_YSIZE);
830 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
833 redraw_mask = REDRAW_NONE;
836 PrintFrameTimeDebugging();
840 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
842 unsigned int frame_delay_value_old = GetVideoFrameDelay();
844 SetVideoFrameDelay(frame_delay_value);
848 SetVideoFrameDelay(frame_delay_value_old);
851 static int fade_type_skip = FADE_TYPE_NONE;
853 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
855 void (*draw_border_function)(void) = NULL;
856 int x, y, width, height;
857 int fade_delay, post_delay;
859 if (fade_type == FADE_TYPE_FADE_OUT)
861 if (fade_type_skip != FADE_TYPE_NONE)
863 // skip all fade operations until specified fade operation
864 if (fade_type & fade_type_skip)
865 fade_type_skip = FADE_TYPE_NONE;
870 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
874 redraw_mask |= fade_mask;
876 if (fade_type == FADE_TYPE_SKIP)
878 fade_type_skip = fade_mode;
883 fade_delay = fading.fade_delay;
884 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
886 if (fade_type_skip != FADE_TYPE_NONE)
888 // skip all fade operations until specified fade operation
889 if (fade_type & fade_type_skip)
890 fade_type_skip = FADE_TYPE_NONE;
895 if (global.autoplay_leveldir)
900 if (fade_mask == REDRAW_FIELD)
905 height = FADE_SYSIZE;
907 if (border.draw_masked_when_fading)
908 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
910 DrawMaskedBorder_FIELD(); // draw once
920 // when switching screens without fading, set fade delay to zero
921 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
924 // do not display black frame when fading out without fade delay
925 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
928 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
929 draw_border_function);
931 redraw_mask &= ~fade_mask;
933 ClearAutoRepeatKeyEvents();
936 static void SetScreenStates_BeforeFadingIn(void)
938 // temporarily set screen mode for animations to screen after fading in
939 global.anim_status = global.anim_status_next;
941 // store backbuffer with all animations that will be started after fading in
942 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
944 // set screen mode for animations back to fading
945 global.anim_status = GAME_MODE_PSEUDO_FADING;
948 static void SetScreenStates_AfterFadingIn(void)
950 // store new source screen (to use correct masked border for fading)
951 gfx.fade_border_source_status = global.border_status;
953 global.anim_status = global.anim_status_next;
956 static void SetScreenStates_BeforeFadingOut(void)
958 // store new target screen (to use correct masked border for fading)
959 gfx.fade_border_target_status = game_status;
961 // set screen mode for animations to fading
962 global.anim_status = GAME_MODE_PSEUDO_FADING;
964 // store backbuffer with all animations that will be stopped for fading out
965 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
968 static void SetScreenStates_AfterFadingOut(void)
970 global.border_status = game_status;
973 void FadeIn(int fade_mask)
975 SetScreenStates_BeforeFadingIn();
978 DrawMaskedBorder(REDRAW_ALL);
981 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
982 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
984 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
988 FADE_SXSIZE = FULL_SXSIZE;
989 FADE_SYSIZE = FULL_SYSIZE;
991 // activate virtual buttons depending on upcoming game status
992 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
993 game_status == GAME_MODE_PLAYING && !tape.playing)
994 SetOverlayActive(TRUE);
996 SetScreenStates_AfterFadingIn();
998 // force update of global animation status in case of rapid screen changes
999 redraw_mask = REDRAW_ALL;
1003 void FadeOut(int fade_mask)
1005 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1006 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1007 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1010 SetScreenStates_BeforeFadingOut();
1012 SetTileCursorActive(FALSE);
1013 SetOverlayActive(FALSE);
1016 DrawMaskedBorder(REDRAW_ALL);
1019 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1020 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1022 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1024 SetScreenStates_AfterFadingOut();
1027 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1029 static struct TitleFadingInfo fading_leave_stored;
1032 fading_leave_stored = fading_leave;
1034 fading = fading_leave_stored;
1037 void FadeSetEnterMenu(void)
1039 fading = menu.enter_menu;
1041 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1044 void FadeSetLeaveMenu(void)
1046 fading = menu.leave_menu;
1048 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1051 void FadeSetEnterScreen(void)
1053 fading = menu.enter_screen[game_status];
1055 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1058 void FadeSetNextScreen(void)
1060 fading = menu.next_screen[game_status];
1062 // (do not overwrite fade mode set by FadeSetEnterScreen)
1063 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1066 void FadeSetLeaveScreen(void)
1068 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1071 void FadeSetFromType(int type)
1073 if (type & TYPE_ENTER_SCREEN)
1074 FadeSetEnterScreen();
1075 else if (type & TYPE_ENTER)
1077 else if (type & TYPE_LEAVE)
1081 void FadeSetDisabled(void)
1083 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1085 fading = fading_none;
1088 void FadeSkipNextFadeIn(void)
1090 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1093 void FadeSkipNextFadeOut(void)
1095 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1098 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1100 if (graphic == IMG_UNDEFINED)
1103 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1105 return (graphic_info[graphic].bitmap != NULL || redefined ?
1106 graphic_info[graphic].bitmap :
1107 graphic_info[default_graphic].bitmap);
1110 static Bitmap *getBackgroundBitmap(int graphic)
1112 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1115 static Bitmap *getGlobalBorderBitmap(int graphic)
1117 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1120 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1123 (status == GAME_MODE_MAIN ||
1124 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1125 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1126 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1127 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1130 return getGlobalBorderBitmap(graphic);
1133 void SetWindowBackgroundImageIfDefined(int graphic)
1135 if (graphic_info[graphic].bitmap)
1136 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1139 void SetMainBackgroundImageIfDefined(int graphic)
1141 if (graphic_info[graphic].bitmap)
1142 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1145 void SetDoorBackgroundImageIfDefined(int graphic)
1147 if (graphic_info[graphic].bitmap)
1148 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1151 void SetWindowBackgroundImage(int graphic)
1153 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1156 void SetMainBackgroundImage(int graphic)
1158 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1161 void SetDoorBackgroundImage(int graphic)
1163 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1166 void SetPanelBackground(void)
1168 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1170 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1171 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1173 SetDoorBackgroundBitmap(bitmap_db_panel);
1176 void DrawBackground(int x, int y, int width, int height)
1178 // "drawto" might still point to playfield buffer here (hall of fame)
1179 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1181 if (IN_GFX_FIELD_FULL(x, y))
1182 redraw_mask |= REDRAW_FIELD;
1183 else if (IN_GFX_DOOR_1(x, y))
1184 redraw_mask |= REDRAW_DOOR_1;
1185 else if (IN_GFX_DOOR_2(x, y))
1186 redraw_mask |= REDRAW_DOOR_2;
1187 else if (IN_GFX_DOOR_3(x, y))
1188 redraw_mask |= REDRAW_DOOR_3;
1191 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1193 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1195 if (font->bitmap == NULL)
1198 DrawBackground(x, y, width, height);
1201 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1203 struct GraphicInfo *g = &graphic_info[graphic];
1205 if (g->bitmap == NULL)
1208 DrawBackground(x, y, width, height);
1211 static int game_status_last = -1;
1212 static Bitmap *global_border_bitmap_last = NULL;
1213 static Bitmap *global_border_bitmap = NULL;
1214 static int real_sx_last = -1, real_sy_last = -1;
1215 static int full_sxsize_last = -1, full_sysize_last = -1;
1216 static int dx_last = -1, dy_last = -1;
1217 static int dxsize_last = -1, dysize_last = -1;
1218 static int vx_last = -1, vy_last = -1;
1219 static int vxsize_last = -1, vysize_last = -1;
1220 static int ex_last = -1, ey_last = -1;
1221 static int exsize_last = -1, eysize_last = -1;
1223 boolean CheckIfGlobalBorderHasChanged(void)
1225 // if game status has not changed, global border has not changed either
1226 if (game_status == game_status_last)
1229 // determine and store new global border bitmap for current game status
1230 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1232 return (global_border_bitmap_last != global_border_bitmap);
1235 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1237 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1238 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1240 // if game status has not changed, nothing has to be redrawn
1241 if (game_status == game_status_last)
1244 // redraw if last screen was title screen
1245 if (game_status_last == GAME_MODE_TITLE)
1248 // redraw if global screen border has changed
1249 if (CheckIfGlobalBorderHasChanged())
1252 // redraw if position or size of playfield area has changed
1253 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1254 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1257 // redraw if position or size of door area has changed
1258 if (dx_last != DX || dy_last != DY ||
1259 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1262 // redraw if position or size of tape area has changed
1263 if (vx_last != VX || vy_last != VY ||
1264 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1267 // redraw if position or size of editor area has changed
1268 if (ex_last != EX || ey_last != EY ||
1269 exsize_last != EXSIZE || eysize_last != EYSIZE)
1276 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1279 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1281 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1284 void RedrawGlobalBorder(void)
1286 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1288 RedrawGlobalBorderFromBitmap(bitmap);
1290 redraw_mask = REDRAW_ALL;
1293 static void RedrawGlobalBorderIfNeeded(void)
1295 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1296 if (game_status == game_status_last)
1300 // copy current draw buffer to later copy back areas that have not changed
1301 if (game_status_last != GAME_MODE_TITLE)
1302 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1304 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1305 if (CheckIfGlobalBorderRedrawIsNeeded())
1307 // determine and store new global border bitmap for current game status
1308 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1311 // redraw global screen border (or clear, if defined to be empty)
1312 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1314 if (game_status == GAME_MODE_EDITOR)
1315 DrawSpecialEditorDoor();
1317 // copy previous playfield and door areas, if they are defined on both
1318 // previous and current screen and if they still have the same size
1320 if (real_sx_last != -1 && real_sy_last != -1 &&
1321 REAL_SX != -1 && REAL_SY != -1 &&
1322 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1323 BlitBitmap(bitmap_db_store_1, backbuffer,
1324 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1327 if (dx_last != -1 && dy_last != -1 &&
1328 DX != -1 && DY != -1 &&
1329 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1330 BlitBitmap(bitmap_db_store_1, backbuffer,
1331 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1333 if (game_status != GAME_MODE_EDITOR)
1335 if (vx_last != -1 && vy_last != -1 &&
1336 VX != -1 && VY != -1 &&
1337 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1338 BlitBitmap(bitmap_db_store_1, backbuffer,
1339 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1343 if (ex_last != -1 && ey_last != -1 &&
1344 EX != -1 && EY != -1 &&
1345 exsize_last == EXSIZE && eysize_last == EYSIZE)
1346 BlitBitmap(bitmap_db_store_1, backbuffer,
1347 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1350 redraw_mask = REDRAW_ALL;
1353 game_status_last = game_status;
1355 global_border_bitmap_last = global_border_bitmap;
1357 real_sx_last = REAL_SX;
1358 real_sy_last = REAL_SY;
1359 full_sxsize_last = FULL_SXSIZE;
1360 full_sysize_last = FULL_SYSIZE;
1363 dxsize_last = DXSIZE;
1364 dysize_last = DYSIZE;
1367 vxsize_last = VXSIZE;
1368 vysize_last = VYSIZE;
1371 exsize_last = EXSIZE;
1372 eysize_last = EYSIZE;
1375 void ClearField(void)
1377 RedrawGlobalBorderIfNeeded();
1379 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1380 // (when entering hall of fame after playing)
1381 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1383 // !!! maybe this should be done before clearing the background !!!
1384 if (game_status == GAME_MODE_PLAYING)
1386 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1387 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1391 SetDrawtoField(DRAW_TO_BACKBUFFER);
1395 void MarkTileDirty(int x, int y)
1397 redraw_mask |= REDRAW_FIELD;
1400 void SetBorderElement(void)
1404 BorderElement = EL_EMPTY;
1406 // only the R'n'D game engine may use an additional steelwall border
1407 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1410 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1412 for (x = 0; x < lev_fieldx; x++)
1414 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1415 BorderElement = EL_STEELWALL;
1417 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1423 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1424 int max_array_fieldx, int max_array_fieldy,
1425 short field[max_array_fieldx][max_array_fieldy],
1426 int max_fieldx, int max_fieldy)
1428 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1429 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1430 int old_element = field[start_x][start_y];
1433 // do nothing if start field already has the desired content
1434 if (old_element == fill_element)
1437 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1439 while (stack_pos > 0)
1441 struct XY current = stack_buffer[--stack_pos];
1444 field[current.x][current.y] = fill_element;
1446 for (i = 0; i < 4; i++)
1448 int x = current.x + check[i].x;
1449 int y = current.y + check[i].y;
1451 // check for stack buffer overflow (should not happen)
1452 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1453 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1455 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1456 stack_buffer[stack_pos++] = (struct XY){ x, y };
1461 void FloodFillLevel(int from_x, int from_y, int fill_element,
1462 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1463 int max_fieldx, int max_fieldy)
1465 FloodFillLevelExt(from_x, from_y, fill_element,
1466 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1467 max_fieldx, max_fieldy);
1470 void SetRandomAnimationValue(int x, int y)
1472 gfx.anim_random_frame = GfxRandom[x][y];
1475 int getGraphicAnimationFrame(int graphic, int sync_frame)
1477 // animation synchronized with global frame counter, not move position
1478 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1479 sync_frame = FrameCounter;
1481 return getAnimationFrame(graphic_info[graphic].anim_frames,
1482 graphic_info[graphic].anim_delay,
1483 graphic_info[graphic].anim_mode,
1484 graphic_info[graphic].anim_start_frame,
1488 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1490 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1492 struct GraphicInfo *g = &graphic_info[graphic];
1493 int xsize = MAX(1, g->anim_frames_per_line);
1494 int ysize = MAX(1, g->anim_frames / xsize);
1495 int xoffset = g->anim_start_frame % xsize;
1496 int yoffset = g->anim_start_frame % ysize;
1497 // may be needed if screen field is significantly larger than playfield
1498 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1499 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1500 int sync_frame = y * xsize + x;
1502 return sync_frame % g->anim_frames;
1504 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1506 struct GraphicInfo *g = &graphic_info[graphic];
1507 // may be needed if screen field is significantly larger than playfield
1508 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1509 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1510 int sync_frame = GfxRandomStatic[x][y];
1512 return sync_frame % g->anim_frames;
1516 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1518 return getGraphicAnimationFrame(graphic, sync_frame);
1522 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1524 struct GraphicInfo *g = &graphic_info[graphic];
1525 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1527 if (tilesize == gfx.standard_tile_size)
1528 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1529 else if (tilesize == game.tile_size)
1530 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1532 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1535 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1536 boolean get_backside)
1538 struct GraphicInfo *g = &graphic_info[graphic];
1539 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1540 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1542 if (g->offset_y == 0) // frames are ordered horizontally
1544 int max_width = g->anim_frames_per_line * g->width;
1545 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1547 *x = pos % max_width;
1548 *y = src_y % g->height + pos / max_width * g->height;
1550 else if (g->offset_x == 0) // frames are ordered vertically
1552 int max_height = g->anim_frames_per_line * g->height;
1553 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1555 *x = src_x % g->width + pos / max_height * g->width;
1556 *y = pos % max_height;
1558 else // frames are ordered diagonally
1560 *x = src_x + frame * g->offset_x;
1561 *y = src_y + frame * g->offset_y;
1565 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1566 Bitmap **bitmap, int *x, int *y,
1567 boolean get_backside)
1569 struct GraphicInfo *g = &graphic_info[graphic];
1571 // if no graphics defined at all, use fallback graphics
1572 if (g->bitmaps == NULL)
1573 *g = graphic_info[IMG_CHAR_EXCLAM];
1575 // if no in-game graphics defined, always use standard graphic size
1576 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1577 tilesize = TILESIZE;
1579 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1580 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1582 *x = *x * tilesize / g->tile_size;
1583 *y = *y * tilesize / g->tile_size;
1586 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1587 Bitmap **bitmap, int *x, int *y)
1589 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1592 void getFixedGraphicSource(int graphic, int frame,
1593 Bitmap **bitmap, int *x, int *y)
1595 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1598 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1600 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1603 void getGlobalAnimGraphicSource(int graphic, int frame,
1604 Bitmap **bitmap, int *x, int *y)
1606 struct GraphicInfo *g = &graphic_info[graphic];
1608 // if no graphics defined at all, use fallback graphics
1609 if (g->bitmaps == NULL)
1610 *g = graphic_info[IMG_CHAR_EXCLAM];
1612 // use original size graphics, if existing, else use standard size graphics
1613 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1614 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1616 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1618 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1621 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1622 int *x, int *y, boolean get_backside)
1624 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1628 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1630 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1633 void DrawGraphic(int x, int y, int graphic, int frame)
1636 if (!IN_SCR_FIELD(x, y))
1638 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1639 Debug("draw:DrawGraphic", "This should never happen!");
1645 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1648 MarkTileDirty(x, y);
1651 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1654 if (!IN_SCR_FIELD(x, y))
1656 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1658 Debug("draw:DrawFixedGraphic", "This should never happen!");
1664 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1666 MarkTileDirty(x, y);
1669 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1675 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1680 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1686 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1687 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1690 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1693 if (!IN_SCR_FIELD(x, y))
1695 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1697 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1703 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1706 MarkTileDirty(x, y);
1709 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1712 if (!IN_SCR_FIELD(x, y))
1714 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1716 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1722 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1724 MarkTileDirty(x, y);
1727 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1733 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1735 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1739 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1740 int graphic, int frame)
1745 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1747 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1751 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1753 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1755 MarkTileDirty(x / tilesize, y / tilesize);
1758 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1761 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1762 graphic, frame, tilesize);
1763 MarkTileDirty(x / tilesize, y / tilesize);
1766 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1772 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1773 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1776 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1777 int frame, int tilesize)
1782 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1783 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1786 void DrawMiniGraphic(int x, int y, int graphic)
1788 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1789 MarkTileDirty(x / 2, y / 2);
1792 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1797 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1798 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1801 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1802 int graphic, int frame,
1803 int cut_mode, int mask_mode)
1808 int width = TILEX, height = TILEY;
1811 if (dx || dy) // shifted graphic
1813 if (x < BX1) // object enters playfield from the left
1820 else if (x > BX2) // object enters playfield from the right
1826 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1832 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1834 else if (dx) // general horizontal movement
1835 MarkTileDirty(x + SIGN(dx), y);
1837 if (y < BY1) // object enters playfield from the top
1839 if (cut_mode == CUT_BELOW) // object completely above top border
1847 else if (y > BY2) // object enters playfield from the bottom
1853 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1859 else if (dy > 0 && cut_mode == CUT_ABOVE)
1861 if (y == BY2) // object completely above bottom border
1867 MarkTileDirty(x, y + 1);
1868 } // object leaves playfield to the bottom
1869 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1871 else if (dy) // general vertical movement
1872 MarkTileDirty(x, y + SIGN(dy));
1876 if (!IN_SCR_FIELD(x, y))
1878 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1880 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1886 width = width * TILESIZE_VAR / TILESIZE;
1887 height = height * TILESIZE_VAR / TILESIZE;
1888 cx = cx * TILESIZE_VAR / TILESIZE;
1889 cy = cy * TILESIZE_VAR / TILESIZE;
1890 dx = dx * TILESIZE_VAR / TILESIZE;
1891 dy = dy * TILESIZE_VAR / TILESIZE;
1893 if (width > 0 && height > 0)
1895 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1900 dst_x = FX + x * TILEX_VAR + dx;
1901 dst_y = FY + y * TILEY_VAR + dy;
1903 if (mask_mode == USE_MASKING)
1904 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1907 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1910 MarkTileDirty(x, y);
1914 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1915 int graphic, int frame,
1916 int cut_mode, int mask_mode)
1921 int width = TILEX_VAR, height = TILEY_VAR;
1924 int x2 = x + SIGN(dx);
1925 int y2 = y + SIGN(dy);
1927 // movement with two-tile animations must be sync'ed with movement position,
1928 // not with current GfxFrame (which can be higher when using slow movement)
1929 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1930 int anim_frames = graphic_info[graphic].anim_frames;
1932 // (we also need anim_delay here for movement animations with less frames)
1933 int anim_delay = graphic_info[graphic].anim_delay;
1934 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1936 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1937 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1939 // re-calculate animation frame for two-tile movement animation
1940 frame = getGraphicAnimationFrame(graphic, sync_frame);
1942 // check if movement start graphic inside screen area and should be drawn
1943 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1945 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1947 dst_x = FX + x1 * TILEX_VAR;
1948 dst_y = FY + y1 * TILEY_VAR;
1950 if (mask_mode == USE_MASKING)
1951 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1957 MarkTileDirty(x1, y1);
1960 // check if movement end graphic inside screen area and should be drawn
1961 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1963 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1965 dst_x = FX + x2 * TILEX_VAR;
1966 dst_y = FY + y2 * TILEY_VAR;
1968 if (mask_mode == USE_MASKING)
1969 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1972 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1975 MarkTileDirty(x2, y2);
1979 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1980 int graphic, int frame,
1981 int cut_mode, int mask_mode)
1985 DrawGraphic(x, y, graphic, frame);
1990 if (graphic_info[graphic].double_movement) // EM style movement images
1991 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1993 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1996 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1997 int graphic, int frame, int cut_mode)
1999 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2002 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2003 int cut_mode, int mask_mode)
2005 int lx = LEVELX(x), ly = LEVELY(y);
2009 if (IN_LEV_FIELD(lx, ly))
2011 if (element == EL_EMPTY)
2012 element = GfxElementEmpty[lx][ly];
2014 SetRandomAnimationValue(lx, ly);
2016 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2017 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2019 // do not use double (EM style) movement graphic when not moving
2020 if (graphic_info[graphic].double_movement && !dx && !dy)
2022 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2023 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2026 if (game.use_masked_elements && (dx || dy))
2027 mask_mode = USE_MASKING;
2029 else // border element
2031 graphic = el2img(element);
2032 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2035 if (element == EL_EXPANDABLE_WALL)
2037 boolean left_stopped = FALSE, right_stopped = FALSE;
2039 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2040 left_stopped = TRUE;
2041 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2042 right_stopped = TRUE;
2044 if (left_stopped && right_stopped)
2046 else if (left_stopped)
2048 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2049 frame = graphic_info[graphic].anim_frames - 1;
2051 else if (right_stopped)
2053 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2054 frame = graphic_info[graphic].anim_frames - 1;
2059 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2060 else if (mask_mode == USE_MASKING)
2061 DrawGraphicThruMask(x, y, graphic, frame);
2063 DrawGraphic(x, y, graphic, frame);
2066 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2067 int cut_mode, int mask_mode)
2069 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2070 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2071 cut_mode, mask_mode);
2074 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2077 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2080 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2083 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2086 void DrawLevelElementThruMask(int x, int y, int element)
2088 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2091 void DrawLevelFieldThruMask(int x, int y)
2093 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2096 // !!! implementation of quicksand is totally broken !!!
2097 #define IS_CRUMBLED_TILE(x, y, e) \
2098 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2099 !IS_MOVING(x, y) || \
2100 (e) == EL_QUICKSAND_EMPTYING || \
2101 (e) == EL_QUICKSAND_FAST_EMPTYING))
2103 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2108 int width, height, cx, cy;
2109 int sx = SCREENX(x), sy = SCREENY(y);
2110 int crumbled_border_size = graphic_info[graphic].border_size;
2111 int crumbled_tile_size = graphic_info[graphic].tile_size;
2112 int crumbled_border_size_var =
2113 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2116 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2118 for (i = 1; i < 4; i++)
2120 int dxx = (i & 1 ? dx : 0);
2121 int dyy = (i & 2 ? dy : 0);
2124 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2127 // check if neighbour field is of same crumble type
2128 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2129 graphic_info[graphic].class ==
2130 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2132 // return if check prevents inner corner
2133 if (same == (dxx == dx && dyy == dy))
2137 // if we reach this point, we have an inner corner
2139 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2141 width = crumbled_border_size_var;
2142 height = crumbled_border_size_var;
2143 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2144 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2146 if (game.use_masked_elements)
2148 int graphic0 = el2img(EL_EMPTY);
2149 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2150 Bitmap *src_bitmap0;
2153 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2155 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2157 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2159 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2161 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2164 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2166 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2169 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2174 int width, height, bx, by, cx, cy;
2175 int sx = SCREENX(x), sy = SCREENY(y);
2176 int crumbled_border_size = graphic_info[graphic].border_size;
2177 int crumbled_tile_size = graphic_info[graphic].tile_size;
2178 int crumbled_border_size_var =
2179 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2180 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2183 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2185 // only needed when using masked elements
2186 int graphic0 = el2img(EL_EMPTY);
2187 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2188 Bitmap *src_bitmap0;
2191 if (game.use_masked_elements)
2192 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2194 // draw simple, sloppy, non-corner-accurate crumbled border
2196 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2197 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2198 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2199 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2201 if (game.use_masked_elements)
2203 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2205 FX + sx * TILEX_VAR + cx,
2206 FY + sy * TILEY_VAR + cy);
2208 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2210 FX + sx * TILEX_VAR + cx,
2211 FY + sy * TILEY_VAR + cy);
2214 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2216 FX + sx * TILEX_VAR + cx,
2217 FY + sy * TILEY_VAR + cy);
2219 // (remaining middle border part must be at least as big as corner part)
2220 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2221 crumbled_border_size_var >= TILESIZE_VAR / 3)
2224 // correct corners of crumbled border, if needed
2226 for (i = -1; i <= 1; i += 2)
2228 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2229 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2230 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2233 // check if neighbour field is of same crumble type
2234 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2235 graphic_info[graphic].class ==
2236 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2238 // no crumbled corner, but continued crumbled border
2240 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2241 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2242 int b1 = (i == 1 ? crumbled_border_size_var :
2243 TILESIZE_VAR - 2 * crumbled_border_size_var);
2245 width = crumbled_border_size_var;
2246 height = crumbled_border_size_var;
2248 if (dir == 1 || dir == 2)
2263 if (game.use_masked_elements)
2265 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2267 FX + sx * TILEX_VAR + cx,
2268 FY + sy * TILEY_VAR + cy);
2270 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2272 FX + sx * TILEX_VAR + cx,
2273 FY + sy * TILEY_VAR + cy);
2276 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2278 FX + sx * TILEX_VAR + cx,
2279 FY + sy * TILEY_VAR + cy);
2284 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2286 int sx = SCREENX(x), sy = SCREENY(y);
2289 static int xy[4][2] =
2297 if (!IN_LEV_FIELD(x, y))
2300 element = TILE_GFX_ELEMENT(x, y);
2302 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2304 if (!IN_SCR_FIELD(sx, sy))
2307 // crumble field borders towards direct neighbour fields
2308 for (i = 0; i < 4; i++)
2310 int xx = x + xy[i][0];
2311 int yy = y + xy[i][1];
2313 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2316 // check if neighbour field is of same crumble type
2317 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2318 graphic_info[graphic].class ==
2319 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2322 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2325 // crumble inner field corners towards corner neighbour fields
2326 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2327 graphic_info[graphic].anim_frames == 2)
2329 for (i = 0; i < 4; i++)
2331 int dx = (i & 1 ? +1 : -1);
2332 int dy = (i & 2 ? +1 : -1);
2334 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2338 MarkTileDirty(sx, sy);
2340 else // center field is not crumbled -- crumble neighbour fields
2342 // crumble field borders of direct neighbour fields
2343 for (i = 0; i < 4; i++)
2345 int xx = x + xy[i][0];
2346 int yy = y + xy[i][1];
2347 int sxx = sx + xy[i][0];
2348 int syy = sy + xy[i][1];
2350 if (!IN_LEV_FIELD(xx, yy) ||
2351 !IN_SCR_FIELD(sxx, syy))
2354 // do not crumble fields that are being digged or snapped
2355 if (Tile[xx][yy] == EL_EMPTY ||
2356 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2359 element = TILE_GFX_ELEMENT(xx, yy);
2361 if (!IS_CRUMBLED_TILE(xx, yy, element))
2364 graphic = el_act2crm(element, ACTION_DEFAULT);
2366 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2368 MarkTileDirty(sxx, syy);
2371 // crumble inner field corners of corner neighbour fields
2372 for (i = 0; i < 4; i++)
2374 int dx = (i & 1 ? +1 : -1);
2375 int dy = (i & 2 ? +1 : -1);
2381 if (!IN_LEV_FIELD(xx, yy) ||
2382 !IN_SCR_FIELD(sxx, syy))
2385 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2388 element = TILE_GFX_ELEMENT(xx, yy);
2390 if (!IS_CRUMBLED_TILE(xx, yy, element))
2393 graphic = el_act2crm(element, ACTION_DEFAULT);
2395 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2396 graphic_info[graphic].anim_frames == 2)
2397 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2399 MarkTileDirty(sxx, syy);
2404 void DrawLevelFieldCrumbled(int x, int y)
2408 if (!IN_LEV_FIELD(x, y))
2411 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2412 GfxElement[x][y] != EL_UNDEFINED &&
2413 GFX_CRUMBLED(GfxElement[x][y]))
2415 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2420 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2422 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2425 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2428 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2429 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2430 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2431 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2432 int sx = SCREENX(x), sy = SCREENY(y);
2434 DrawScreenGraphic(sx, sy, graphic1, frame1);
2435 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2438 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2440 int sx = SCREENX(x), sy = SCREENY(y);
2441 static int xy[4][2] =
2450 // crumble direct neighbour fields (required for field borders)
2451 for (i = 0; i < 4; i++)
2453 int xx = x + xy[i][0];
2454 int yy = y + xy[i][1];
2455 int sxx = sx + xy[i][0];
2456 int syy = sy + xy[i][1];
2458 if (!IN_LEV_FIELD(xx, yy) ||
2459 !IN_SCR_FIELD(sxx, syy) ||
2460 !GFX_CRUMBLED(Tile[xx][yy]) ||
2464 DrawLevelField(xx, yy);
2467 // crumble corner neighbour fields (required for inner field corners)
2468 for (i = 0; i < 4; i++)
2470 int dx = (i & 1 ? +1 : -1);
2471 int dy = (i & 2 ? +1 : -1);
2477 if (!IN_LEV_FIELD(xx, yy) ||
2478 !IN_SCR_FIELD(sxx, syy) ||
2479 !GFX_CRUMBLED(Tile[xx][yy]) ||
2483 int element = TILE_GFX_ELEMENT(xx, yy);
2484 int graphic = el_act2crm(element, ACTION_DEFAULT);
2486 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2487 graphic_info[graphic].anim_frames == 2)
2488 DrawLevelField(xx, yy);
2492 static int getBorderElement(int x, int y)
2496 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2497 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2498 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2499 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2500 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2501 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2502 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2504 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2505 int steel_position = (x == -1 && y == -1 ? 0 :
2506 x == lev_fieldx && y == -1 ? 1 :
2507 x == -1 && y == lev_fieldy ? 2 :
2508 x == lev_fieldx && y == lev_fieldy ? 3 :
2509 x == -1 || x == lev_fieldx ? 4 :
2510 y == -1 || y == lev_fieldy ? 5 : 6);
2512 return border[steel_position][steel_type];
2515 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2517 if (game.use_masked_elements)
2519 if (graphic != el2img(EL_EMPTY))
2520 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2522 DrawGraphicThruMask(x, y, graphic, frame);
2526 DrawGraphic(x, y, graphic, frame);
2530 void DrawScreenElement(int x, int y, int element)
2532 int mask_mode = NO_MASKING;
2534 if (game.use_masked_elements)
2536 int lx = LEVELX(x), ly = LEVELY(y);
2538 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2540 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2542 mask_mode = USE_MASKING;
2546 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2547 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2550 void DrawLevelElement(int x, int y, int element)
2552 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2553 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2556 void DrawScreenField(int x, int y)
2558 int lx = LEVELX(x), ly = LEVELY(y);
2559 int element, content;
2561 if (!IN_LEV_FIELD(lx, ly))
2563 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2566 element = getBorderElement(lx, ly);
2568 DrawScreenElement(x, y, element);
2573 element = Tile[lx][ly];
2574 content = Store[lx][ly];
2576 if (IS_MOVING(lx, ly))
2578 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2579 boolean cut_mode = NO_CUTTING;
2581 if (element == EL_QUICKSAND_EMPTYING ||
2582 element == EL_QUICKSAND_FAST_EMPTYING ||
2583 element == EL_MAGIC_WALL_EMPTYING ||
2584 element == EL_BD_MAGIC_WALL_EMPTYING ||
2585 element == EL_DC_MAGIC_WALL_EMPTYING ||
2586 element == EL_AMOEBA_DROPPING)
2587 cut_mode = CUT_ABOVE;
2588 else if (element == EL_QUICKSAND_FILLING ||
2589 element == EL_QUICKSAND_FAST_FILLING ||
2590 element == EL_MAGIC_WALL_FILLING ||
2591 element == EL_BD_MAGIC_WALL_FILLING ||
2592 element == EL_DC_MAGIC_WALL_FILLING)
2593 cut_mode = CUT_BELOW;
2595 if (cut_mode == CUT_ABOVE)
2596 DrawScreenElement(x, y, element);
2598 DrawScreenElement(x, y, EL_EMPTY);
2600 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2602 int dir = MovDir[lx][ly];
2603 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2604 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2606 if (IN_SCR_FIELD(newx, newy))
2607 DrawScreenElement(newx, newy, EL_EMPTY);
2611 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2612 else if (cut_mode == NO_CUTTING)
2613 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2616 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2618 if (cut_mode == CUT_BELOW &&
2619 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2620 DrawLevelElement(lx, ly + 1, element);
2623 if (content == EL_ACID)
2625 int dir = MovDir[lx][ly];
2626 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2627 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2629 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2631 // prevent target field from being drawn again (but without masking)
2632 // (this would happen if target field is scanned after moving element)
2633 Stop[newlx][newly] = TRUE;
2636 else if (IS_BLOCKED(lx, ly))
2641 boolean cut_mode = NO_CUTTING;
2642 int element_old, content_old;
2644 Blocked2Moving(lx, ly, &oldx, &oldy);
2647 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2648 MovDir[oldx][oldy] == MV_RIGHT);
2650 element_old = Tile[oldx][oldy];
2651 content_old = Store[oldx][oldy];
2653 if (element_old == EL_QUICKSAND_EMPTYING ||
2654 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2655 element_old == EL_MAGIC_WALL_EMPTYING ||
2656 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2657 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2658 element_old == EL_AMOEBA_DROPPING)
2659 cut_mode = CUT_ABOVE;
2661 DrawScreenElement(x, y, EL_EMPTY);
2664 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2666 else if (cut_mode == NO_CUTTING)
2667 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2670 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2673 else if (IS_DRAWABLE(element))
2674 DrawScreenElement(x, y, element);
2676 DrawScreenElement(x, y, EL_EMPTY);
2679 void DrawLevelField(int x, int y)
2681 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2682 DrawScreenField(SCREENX(x), SCREENY(y));
2683 else if (IS_MOVING(x, y))
2687 Moving2Blocked(x, y, &newx, &newy);
2688 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2689 DrawScreenField(SCREENX(newx), SCREENY(newy));
2691 else if (IS_BLOCKED(x, y))
2695 Blocked2Moving(x, y, &oldx, &oldy);
2696 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2697 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2701 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2702 int (*el2img_function)(int), boolean masked,
2703 int element_bits_draw)
2705 int element_base = map_mm_wall_element(element);
2706 int element_bits = (IS_DF_WALL(element) ?
2707 element - EL_DF_WALL_START :
2708 IS_MM_WALL(element) ?
2709 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2710 int graphic = el2img_function(element_base);
2711 int tilesize_draw = tilesize / 2;
2716 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2718 for (i = 0; i < 4; i++)
2720 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2721 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2723 if (!(element_bits_draw & (1 << i)))
2726 if (element_bits & (1 << i))
2729 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2730 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2732 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2733 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2738 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2739 tilesize_draw, tilesize_draw);
2744 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2745 boolean masked, int element_bits_draw)
2747 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2748 element, tilesize, el2edimg, masked, element_bits_draw);
2751 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2752 int (*el2img_function)(int))
2754 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2758 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2761 if (IS_MM_WALL(element))
2763 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2764 element, tilesize, el2edimg, masked, 0x000f);
2768 int graphic = el2edimg(element);
2771 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2773 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2777 void DrawSizedElement(int x, int y, int element, int tilesize)
2779 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2782 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2784 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2787 void DrawMiniElement(int x, int y, int element)
2791 graphic = el2edimg(element);
2792 DrawMiniGraphic(x, y, graphic);
2795 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2798 int x = sx + scroll_x, y = sy + scroll_y;
2800 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2801 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2802 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2803 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2805 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2808 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2810 int x = sx + scroll_x, y = sy + scroll_y;
2812 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2813 DrawMiniElement(sx, sy, EL_EMPTY);
2814 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2815 DrawMiniElement(sx, sy, Tile[x][y]);
2817 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2820 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2821 int x, int y, int xsize, int ysize,
2822 int tile_width, int tile_height)
2826 int dst_x = startx + x * tile_width;
2827 int dst_y = starty + y * tile_height;
2828 int width = graphic_info[graphic].width;
2829 int height = graphic_info[graphic].height;
2830 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2831 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2832 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2833 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2834 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2835 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2836 boolean draw_masked = graphic_info[graphic].draw_masked;
2838 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2840 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2842 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2846 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2847 inner_sx + (x - 1) * tile_width % inner_width);
2848 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2849 inner_sy + (y - 1) * tile_height % inner_height);
2852 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2855 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2859 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2860 int x, int y, int xsize, int ysize,
2863 int font_width = getFontWidth(font_nr);
2864 int font_height = getFontHeight(font_nr);
2866 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2867 font_width, font_height);
2870 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2872 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2873 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2874 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2875 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2876 boolean no_delay = (tape.warp_forward);
2877 unsigned int anim_delay = 0;
2878 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2879 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2880 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2881 int font_width = getFontWidth(font_nr);
2882 int font_height = getFontHeight(font_nr);
2883 int max_xsize = level.envelope[envelope_nr].xsize;
2884 int max_ysize = level.envelope[envelope_nr].ysize;
2885 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2886 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2887 int xend = max_xsize;
2888 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2889 int xstep = (xstart < xend ? 1 : 0);
2890 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2892 int end = MAX(xend - xstart, yend - ystart);
2895 for (i = start; i <= end; i++)
2897 int last_frame = end; // last frame of this "for" loop
2898 int x = xstart + i * xstep;
2899 int y = ystart + i * ystep;
2900 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2901 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2902 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2903 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2906 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2908 BlitScreenToBitmap(backbuffer);
2910 SetDrawtoField(DRAW_TO_BACKBUFFER);
2912 for (yy = 0; yy < ysize; yy++)
2913 for (xx = 0; xx < xsize; xx++)
2914 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2916 DrawTextBuffer(sx + font_width, sy + font_height,
2917 level.envelope[envelope_nr].text, font_nr, max_xsize,
2918 xsize - 2, ysize - 2, 0, mask_mode,
2919 level.envelope[envelope_nr].autowrap,
2920 level.envelope[envelope_nr].centered, FALSE);
2922 redraw_mask |= REDRAW_FIELD;
2925 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2928 ClearAutoRepeatKeyEvents();
2931 void ShowEnvelope(int envelope_nr)
2933 int element = EL_ENVELOPE_1 + envelope_nr;
2934 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2935 int sound_opening = element_info[element].sound[ACTION_OPENING];
2936 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2937 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2938 boolean no_delay = (tape.warp_forward);
2939 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2940 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2941 int anim_mode = graphic_info[graphic].anim_mode;
2942 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2943 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2944 boolean overlay_enabled = GetOverlayEnabled();
2946 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2948 SetOverlayEnabled(FALSE);
2951 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2953 if (anim_mode == ANIM_DEFAULT)
2954 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2956 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2959 Delay_WithScreenUpdates(wait_delay_value);
2961 WaitForEventToContinue();
2964 SetOverlayEnabled(overlay_enabled);
2966 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2968 if (anim_mode != ANIM_NONE)
2969 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2971 if (anim_mode == ANIM_DEFAULT)
2972 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2974 game.envelope_active = FALSE;
2976 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2978 redraw_mask |= REDRAW_FIELD;
2982 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2983 int xsize, int ysize)
2985 if (!global.use_envelope_request ||
2986 request.sort_priority <= 0)
2989 if (request.bitmap == NULL ||
2990 xsize > request.xsize ||
2991 ysize > request.ysize)
2993 if (request.bitmap != NULL)
2994 FreeBitmap(request.bitmap);
2996 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2998 SDL_Surface *surface = request.bitmap->surface;
3000 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3001 Fail("SDLGetNativeSurface() failed");
3004 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3006 SDLFreeBitmapTextures(request.bitmap);
3007 SDLCreateBitmapTextures(request.bitmap);
3009 // set envelope request run-time values
3012 request.xsize = xsize;
3013 request.ysize = ysize;
3016 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3018 if (global.use_envelope_request &&
3019 game.request_active_or_moving &&
3020 request.sort_priority > 0 &&
3021 drawing_target == DRAW_TO_SCREEN &&
3022 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3024 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3025 request.sx, request.sy);
3029 static void setRequestBasePosition(int *x, int *y)
3031 int sx_base, sy_base;
3033 if (request.x != -1)
3034 sx_base = request.x;
3035 else if (request.align == ALIGN_LEFT)
3037 else if (request.align == ALIGN_RIGHT)
3038 sx_base = SX + SXSIZE;
3040 sx_base = SX + SXSIZE / 2;
3042 if (request.y != -1)
3043 sy_base = request.y;
3044 else if (request.valign == VALIGN_TOP)
3046 else if (request.valign == VALIGN_BOTTOM)
3047 sy_base = SY + SYSIZE;
3049 sy_base = SY + SYSIZE / 2;
3055 static void setRequestPositionExt(int *x, int *y, int width, int height,
3056 boolean add_border_size)
3058 int border_size = request.border_size;
3059 int sx_base, sy_base;
3062 setRequestBasePosition(&sx_base, &sy_base);
3064 if (request.align == ALIGN_LEFT)
3066 else if (request.align == ALIGN_RIGHT)
3067 sx = sx_base - width;
3069 sx = sx_base - width / 2;
3071 if (request.valign == VALIGN_TOP)
3073 else if (request.valign == VALIGN_BOTTOM)
3074 sy = sy_base - height;
3076 sy = sy_base - height / 2;
3078 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3079 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3081 if (add_border_size)
3091 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3093 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3096 static void DrawEnvelopeRequest(char *text)
3098 char *text_final = text;
3099 char *text_door_style = NULL;
3100 int graphic = IMG_BACKGROUND_REQUEST;
3101 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3102 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3103 int font_nr = FONT_REQUEST;
3104 int font_width = getFontWidth(font_nr);
3105 int font_height = getFontHeight(font_nr);
3106 int border_size = request.border_size;
3107 int line_spacing = request.line_spacing;
3108 int line_height = font_height + line_spacing;
3109 int max_text_width = request.width - 2 * border_size;
3110 int max_text_height = request.height - 2 * border_size;
3111 int line_length = max_text_width / font_width;
3112 int max_lines = max_text_height / line_height;
3113 int text_width = line_length * font_width;
3114 int width = request.width;
3115 int height = request.height;
3116 int tile_size = MAX(request.step_offset, 1);
3117 int x_steps = width / tile_size;
3118 int y_steps = height / tile_size;
3119 int sx_offset = border_size;
3120 int sy_offset = border_size;
3124 if (request.centered)
3125 sx_offset = (request.width - text_width) / 2;
3127 if (request.wrap_single_words && !request.autowrap)
3129 char *src_text_ptr, *dst_text_ptr;
3131 text_door_style = checked_malloc(2 * strlen(text) + 1);
3133 src_text_ptr = text;
3134 dst_text_ptr = text_door_style;
3136 while (*src_text_ptr)
3138 if (*src_text_ptr == ' ' ||
3139 *src_text_ptr == '?' ||
3140 *src_text_ptr == '!')
3141 *dst_text_ptr++ = '\n';
3143 if (*src_text_ptr != ' ')
3144 *dst_text_ptr++ = *src_text_ptr;
3149 *dst_text_ptr = '\0';
3151 text_final = text_door_style;
3154 setRequestPosition(&sx, &sy, FALSE);
3156 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3158 for (y = 0; y < y_steps; y++)
3159 for (x = 0; x < x_steps; x++)
3160 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3161 x, y, x_steps, y_steps,
3162 tile_size, tile_size);
3164 // force DOOR font inside door area
3165 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3167 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3168 line_length, -1, max_lines, line_spacing, mask_mode,
3169 request.autowrap, request.centered, FALSE);
3173 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3174 RedrawGadget(tool_gadget[i]);
3176 // store readily prepared envelope request for later use when animating
3177 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3179 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3181 if (text_door_style)
3182 free(text_door_style);
3185 static void AnimateEnvelopeRequest(int anim_mode, int action)
3187 int graphic = IMG_BACKGROUND_REQUEST;
3188 boolean draw_masked = graphic_info[graphic].draw_masked;
3189 int delay_value_normal = request.step_delay;
3190 int delay_value_fast = delay_value_normal / 2;
3191 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3192 boolean no_delay = (tape.warp_forward);
3193 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3194 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3195 unsigned int anim_delay = 0;
3197 int tile_size = MAX(request.step_offset, 1);
3198 int max_xsize = request.width / tile_size;
3199 int max_ysize = request.height / tile_size;
3200 int max_xsize_inner = max_xsize - 2;
3201 int max_ysize_inner = max_ysize - 2;
3203 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3204 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3205 int xend = max_xsize_inner;
3206 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3207 int xstep = (xstart < xend ? 1 : 0);
3208 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3210 int end = MAX(xend - xstart, yend - ystart);
3213 if (setup.quick_doors)
3220 for (i = start; i <= end; i++)
3222 int last_frame = end; // last frame of this "for" loop
3223 int x = xstart + i * xstep;
3224 int y = ystart + i * ystep;
3225 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3226 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3227 int xsize_size_left = (xsize - 1) * tile_size;
3228 int ysize_size_top = (ysize - 1) * tile_size;
3229 int max_xsize_pos = (max_xsize - 1) * tile_size;
3230 int max_ysize_pos = (max_ysize - 1) * tile_size;
3231 int width = xsize * tile_size;
3232 int height = ysize * tile_size;
3237 setRequestPosition(&src_x, &src_y, FALSE);
3238 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3240 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3242 for (yy = 0; yy < 2; yy++)
3244 for (xx = 0; xx < 2; xx++)
3246 int src_xx = src_x + xx * max_xsize_pos;
3247 int src_yy = src_y + yy * max_ysize_pos;
3248 int dst_xx = dst_x + xx * xsize_size_left;
3249 int dst_yy = dst_y + yy * ysize_size_top;
3250 int xx_size = (xx ? tile_size : xsize_size_left);
3251 int yy_size = (yy ? tile_size : ysize_size_top);
3254 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3255 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3257 BlitBitmap(bitmap_db_store_2, backbuffer,
3258 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3262 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3264 redraw_mask |= REDRAW_FIELD;
3268 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3271 ClearAutoRepeatKeyEvents();
3274 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3276 int graphic = IMG_BACKGROUND_REQUEST;
3277 int sound_opening = SND_REQUEST_OPENING;
3278 int sound_closing = SND_REQUEST_CLOSING;
3279 int anim_mode_1 = request.anim_mode; // (higher priority)
3280 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3281 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3282 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3283 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3285 if (game_status == GAME_MODE_PLAYING)
3286 BlitScreenToBitmap(backbuffer);
3288 SetDrawtoField(DRAW_TO_BACKBUFFER);
3290 // SetDrawBackgroundMask(REDRAW_NONE);
3292 if (action == ACTION_OPENING)
3294 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3296 if (req_state & REQ_ASK)
3298 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3299 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3300 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3301 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3303 else if (req_state & REQ_CONFIRM)
3305 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3306 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3308 else if (req_state & REQ_PLAYER)
3310 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3311 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3312 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3313 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3316 DrawEnvelopeRequest(text);
3319 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3321 if (action == ACTION_OPENING)
3323 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3325 if (anim_mode == ANIM_DEFAULT)
3326 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3328 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3332 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3334 if (anim_mode != ANIM_NONE)
3335 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3337 if (anim_mode == ANIM_DEFAULT)
3338 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3341 game.envelope_active = FALSE;
3343 if (action == ACTION_CLOSING)
3344 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3346 // SetDrawBackgroundMask(last_draw_background_mask);
3348 redraw_mask |= REDRAW_FIELD;
3352 if (action == ACTION_CLOSING &&
3353 game_status == GAME_MODE_PLAYING &&
3354 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3355 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3358 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3360 if (IS_MM_WALL(element))
3362 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3368 int graphic = el2preimg(element);
3370 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3371 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3376 void DrawLevel(int draw_background_mask)
3380 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3381 SetDrawBackgroundMask(draw_background_mask);
3385 for (x = BX1; x <= BX2; x++)
3386 for (y = BY1; y <= BY2; y++)
3387 DrawScreenField(x, y);
3389 redraw_mask |= REDRAW_FIELD;
3392 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3397 for (x = 0; x < size_x; x++)
3398 for (y = 0; y < size_y; y++)
3399 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3401 redraw_mask |= REDRAW_FIELD;
3404 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3408 for (x = 0; x < size_x; x++)
3409 for (y = 0; y < size_y; y++)
3410 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3412 redraw_mask |= REDRAW_FIELD;
3415 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3417 boolean show_level_border = (BorderElement != EL_EMPTY);
3418 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3419 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3420 int tile_size = preview.tile_size;
3421 int preview_width = preview.xsize * tile_size;
3422 int preview_height = preview.ysize * tile_size;
3423 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3424 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3425 int real_preview_width = real_preview_xsize * tile_size;
3426 int real_preview_height = real_preview_ysize * tile_size;
3427 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3428 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3431 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3434 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3436 dst_x += (preview_width - real_preview_width) / 2;
3437 dst_y += (preview_height - real_preview_height) / 2;
3439 for (x = 0; x < real_preview_xsize; x++)
3441 for (y = 0; y < real_preview_ysize; y++)
3443 int lx = from_x + x + (show_level_border ? -1 : 0);
3444 int ly = from_y + y + (show_level_border ? -1 : 0);
3445 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3446 getBorderElement(lx, ly));
3448 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3449 element, tile_size);
3453 redraw_mask |= REDRAW_FIELD;
3456 #define MICROLABEL_EMPTY 0
3457 #define MICROLABEL_LEVEL_NAME 1
3458 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3459 #define MICROLABEL_LEVEL_AUTHOR 3
3460 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3461 #define MICROLABEL_IMPORTED_FROM 5
3462 #define MICROLABEL_IMPORTED_BY_HEAD 6
3463 #define MICROLABEL_IMPORTED_BY 7
3465 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3467 int max_text_width = SXSIZE;
3468 int font_width = getFontWidth(font_nr);
3470 if (pos->align == ALIGN_CENTER)
3471 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3472 else if (pos->align == ALIGN_RIGHT)
3473 max_text_width = pos->x;
3475 max_text_width = SXSIZE - pos->x;
3477 return max_text_width / font_width;
3480 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3482 char label_text[MAX_OUTPUT_LINESIZE + 1];
3483 int max_len_label_text;
3484 int font_nr = pos->font;
3487 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3490 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3491 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3492 mode == MICROLABEL_IMPORTED_BY_HEAD)
3493 font_nr = pos->font_alt;
3495 max_len_label_text = getMaxTextLength(pos, font_nr);
3497 if (pos->size != -1)
3498 max_len_label_text = pos->size;
3500 for (i = 0; i < max_len_label_text; i++)
3501 label_text[i] = ' ';
3502 label_text[max_len_label_text] = '\0';
3504 if (strlen(label_text) > 0)
3505 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3508 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3509 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3510 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3511 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3512 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3513 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3514 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3515 max_len_label_text);
3516 label_text[max_len_label_text] = '\0';
3518 if (strlen(label_text) > 0)
3519 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3521 redraw_mask |= REDRAW_FIELD;
3524 static void DrawPreviewLevelLabel(int mode)
3526 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3529 static void DrawPreviewLevelInfo(int mode)
3531 if (mode == MICROLABEL_LEVEL_NAME)
3532 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3533 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3534 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3537 static void DrawPreviewLevelExt(boolean restart)
3539 static unsigned int scroll_delay = 0;
3540 static unsigned int label_delay = 0;
3541 static int from_x, from_y, scroll_direction;
3542 static int label_state, label_counter;
3543 unsigned int scroll_delay_value = preview.step_delay;
3544 boolean show_level_border = (BorderElement != EL_EMPTY);
3545 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3546 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3553 if (preview.anim_mode == ANIM_CENTERED)
3555 if (level_xsize > preview.xsize)
3556 from_x = (level_xsize - preview.xsize) / 2;
3557 if (level_ysize > preview.ysize)
3558 from_y = (level_ysize - preview.ysize) / 2;
3561 from_x += preview.xoffset;
3562 from_y += preview.yoffset;
3564 scroll_direction = MV_RIGHT;
3568 DrawPreviewLevelPlayfield(from_x, from_y);
3569 DrawPreviewLevelLabel(label_state);
3571 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3572 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3574 // initialize delay counters
3575 ResetDelayCounter(&scroll_delay);
3576 ResetDelayCounter(&label_delay);
3578 if (leveldir_current->name)
3580 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3581 char label_text[MAX_OUTPUT_LINESIZE + 1];
3582 int font_nr = pos->font;
3583 int max_len_label_text = getMaxTextLength(pos, font_nr);
3585 if (pos->size != -1)
3586 max_len_label_text = pos->size;
3588 strncpy(label_text, leveldir_current->name, max_len_label_text);
3589 label_text[max_len_label_text] = '\0';
3591 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3592 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3598 // scroll preview level, if needed
3599 if (preview.anim_mode != ANIM_NONE &&
3600 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3601 DelayReached(&scroll_delay, scroll_delay_value))
3603 switch (scroll_direction)
3608 from_x -= preview.step_offset;
3609 from_x = (from_x < 0 ? 0 : from_x);
3612 scroll_direction = MV_UP;
3616 if (from_x < level_xsize - preview.xsize)
3618 from_x += preview.step_offset;
3619 from_x = (from_x > level_xsize - preview.xsize ?
3620 level_xsize - preview.xsize : from_x);
3623 scroll_direction = MV_DOWN;
3629 from_y -= preview.step_offset;
3630 from_y = (from_y < 0 ? 0 : from_y);
3633 scroll_direction = MV_RIGHT;
3637 if (from_y < level_ysize - preview.ysize)
3639 from_y += preview.step_offset;
3640 from_y = (from_y > level_ysize - preview.ysize ?
3641 level_ysize - preview.ysize : from_y);
3644 scroll_direction = MV_LEFT;
3651 DrawPreviewLevelPlayfield(from_x, from_y);
3654 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3655 // redraw micro level label, if needed
3656 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3657 !strEqual(level.author, ANONYMOUS_NAME) &&
3658 !strEqual(level.author, leveldir_current->name) &&
3659 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3661 int max_label_counter = 23;
3663 if (leveldir_current->imported_from != NULL &&
3664 strlen(leveldir_current->imported_from) > 0)
3665 max_label_counter += 14;
3666 if (leveldir_current->imported_by != NULL &&
3667 strlen(leveldir_current->imported_by) > 0)
3668 max_label_counter += 14;
3670 label_counter = (label_counter + 1) % max_label_counter;
3671 label_state = (label_counter >= 0 && label_counter <= 7 ?
3672 MICROLABEL_LEVEL_NAME :
3673 label_counter >= 9 && label_counter <= 12 ?
3674 MICROLABEL_LEVEL_AUTHOR_HEAD :
3675 label_counter >= 14 && label_counter <= 21 ?
3676 MICROLABEL_LEVEL_AUTHOR :
3677 label_counter >= 23 && label_counter <= 26 ?
3678 MICROLABEL_IMPORTED_FROM_HEAD :
3679 label_counter >= 28 && label_counter <= 35 ?
3680 MICROLABEL_IMPORTED_FROM :
3681 label_counter >= 37 && label_counter <= 40 ?
3682 MICROLABEL_IMPORTED_BY_HEAD :
3683 label_counter >= 42 && label_counter <= 49 ?
3684 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3686 if (leveldir_current->imported_from == NULL &&
3687 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3688 label_state == MICROLABEL_IMPORTED_FROM))
3689 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3690 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3692 DrawPreviewLevelLabel(label_state);
3696 void DrawPreviewPlayers(void)
3698 if (game_status != GAME_MODE_MAIN)
3701 // do not draw preview players if level preview redefined, but players aren't
3702 if (preview.redefined && !menu.main.preview_players.redefined)
3705 boolean player_found[MAX_PLAYERS];
3706 int num_players = 0;
3709 for (i = 0; i < MAX_PLAYERS; i++)
3710 player_found[i] = FALSE;
3712 // check which players can be found in the level (simple approach)
3713 for (x = 0; x < lev_fieldx; x++)
3715 for (y = 0; y < lev_fieldy; y++)
3717 int element = level.field[x][y];
3719 if (IS_PLAYER_ELEMENT(element))
3721 int player_nr = GET_PLAYER_NR(element);
3723 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3725 if (!player_found[player_nr])
3728 player_found[player_nr] = TRUE;
3733 struct TextPosInfo *pos = &menu.main.preview_players;
3734 int tile_size = pos->tile_size;
3735 int border_size = pos->border_size;
3736 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3737 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3738 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3739 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3740 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3741 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3742 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3743 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3744 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3745 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3746 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3747 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3749 // clear area in which the players will be drawn
3750 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3751 max_players_width, max_players_height);
3753 if (!network.enabled && !setup.team_mode)
3756 // only draw players if level is suited for team mode
3757 if (num_players < 2)
3760 // draw all players that were found in the level
3761 for (i = 0; i < MAX_PLAYERS; i++)
3763 if (player_found[i])
3765 int graphic = el2img(EL_PLAYER_1 + i);
3767 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3769 xpos += player_xoffset;
3770 ypos += player_yoffset;
3775 void DrawPreviewLevelInitial(void)
3777 DrawPreviewLevelExt(TRUE);
3778 DrawPreviewPlayers();
3781 void DrawPreviewLevelAnimation(void)
3783 DrawPreviewLevelExt(FALSE);
3786 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3787 int border_size, int font_nr)
3789 int graphic = el2img(EL_PLAYER_1 + player_nr);
3790 int font_height = getFontHeight(font_nr);
3791 int player_height = MAX(tile_size, font_height);
3792 int xoffset_text = tile_size + border_size;
3793 int yoffset_text = (player_height - font_height) / 2;
3794 int yoffset_graphic = (player_height - tile_size) / 2;
3795 char *player_name = getNetworkPlayerName(player_nr + 1);
3797 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3799 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3802 static void DrawNetworkPlayersExt(boolean force)
3804 if (game_status != GAME_MODE_MAIN)
3807 if (!network.connected && !force)
3810 // do not draw network players if level preview redefined, but players aren't
3811 if (preview.redefined && !menu.main.network_players.redefined)
3814 int num_players = 0;
3817 for (i = 0; i < MAX_PLAYERS; i++)
3818 if (stored_player[i].connected_network)
3821 struct TextPosInfo *pos = &menu.main.network_players;
3822 int tile_size = pos->tile_size;
3823 int border_size = pos->border_size;
3824 int xoffset_text = tile_size + border_size;
3825 int font_nr = pos->font;
3826 int font_width = getFontWidth(font_nr);
3827 int font_height = getFontHeight(font_nr);
3828 int player_height = MAX(tile_size, font_height);
3829 int player_yoffset = player_height + border_size;
3830 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3831 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3832 int all_players_height = num_players * player_yoffset - border_size;
3833 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3834 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3835 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3837 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3838 max_players_width, max_players_height);
3840 // first draw local network player ...
3841 for (i = 0; i < MAX_PLAYERS; i++)
3843 if (stored_player[i].connected_network &&
3844 stored_player[i].connected_locally)
3846 char *player_name = getNetworkPlayerName(i + 1);
3847 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3848 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3850 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3852 ypos += player_yoffset;
3856 // ... then draw all other network players
3857 for (i = 0; i < MAX_PLAYERS; i++)
3859 if (stored_player[i].connected_network &&
3860 !stored_player[i].connected_locally)
3862 char *player_name = getNetworkPlayerName(i + 1);
3863 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3864 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3866 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3868 ypos += player_yoffset;
3873 void DrawNetworkPlayers(void)
3875 DrawNetworkPlayersExt(FALSE);
3878 void ClearNetworkPlayers(void)
3880 DrawNetworkPlayersExt(TRUE);
3883 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3884 int graphic, int lx, int ly,
3887 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3889 if (mask_mode == USE_MASKING)
3890 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3892 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3895 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3896 int graphic, int sync_frame, int mask_mode)
3898 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3900 if (mask_mode == USE_MASKING)
3901 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3903 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3906 static void DrawGraphicAnimation(int x, int y, int graphic)
3908 int lx = LEVELX(x), ly = LEVELY(y);
3909 int mask_mode = NO_MASKING;
3911 if (!IN_SCR_FIELD(x, y))
3914 if (game.use_masked_elements)
3916 if (Tile[lx][ly] != EL_EMPTY)
3918 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3920 mask_mode = USE_MASKING;
3924 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3925 graphic, lx, ly, mask_mode);
3927 MarkTileDirty(x, y);
3930 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3932 int lx = LEVELX(x), ly = LEVELY(y);
3933 int mask_mode = NO_MASKING;
3935 if (!IN_SCR_FIELD(x, y))
3938 if (game.use_masked_elements)
3940 if (Tile[lx][ly] != EL_EMPTY)
3942 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3944 mask_mode = USE_MASKING;
3948 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3949 graphic, lx, ly, mask_mode);
3951 MarkTileDirty(x, y);
3954 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3956 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3959 void DrawLevelElementAnimation(int x, int y, int element)
3961 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3963 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3966 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3968 int sx = SCREENX(x), sy = SCREENY(y);
3970 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3973 if (Tile[x][y] == EL_EMPTY)
3974 graphic = el2img(GfxElementEmpty[x][y]);
3976 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3979 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3982 DrawGraphicAnimation(sx, sy, graphic);
3985 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3986 DrawLevelFieldCrumbled(x, y);
3988 if (GFX_CRUMBLED(Tile[x][y]))
3989 DrawLevelFieldCrumbled(x, y);
3993 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3995 int sx = SCREENX(x), sy = SCREENY(y);
3998 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4001 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4003 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4006 DrawGraphicAnimation(sx, sy, graphic);
4008 if (GFX_CRUMBLED(element))
4009 DrawLevelFieldCrumbled(x, y);
4012 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4014 if (player->use_murphy)
4016 // this works only because currently only one player can be "murphy" ...
4017 static int last_horizontal_dir = MV_LEFT;
4018 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4020 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4021 last_horizontal_dir = move_dir;
4023 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4025 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4027 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4033 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4036 static boolean equalGraphics(int graphic1, int graphic2)
4038 struct GraphicInfo *g1 = &graphic_info[graphic1];
4039 struct GraphicInfo *g2 = &graphic_info[graphic2];
4041 return (g1->bitmap == g2->bitmap &&
4042 g1->src_x == g2->src_x &&
4043 g1->src_y == g2->src_y &&
4044 g1->anim_frames == g2->anim_frames &&
4045 g1->anim_delay == g2->anim_delay &&
4046 g1->anim_mode == g2->anim_mode);
4049 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4053 DRAW_PLAYER_STAGE_INIT = 0,
4054 DRAW_PLAYER_STAGE_LAST_FIELD,
4055 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4056 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4057 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4058 DRAW_PLAYER_STAGE_PLAYER,
4060 DRAW_PLAYER_STAGE_PLAYER,
4061 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4063 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4064 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4066 NUM_DRAW_PLAYER_STAGES
4069 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4071 static int static_last_player_graphic[MAX_PLAYERS];
4072 static int static_last_player_frame[MAX_PLAYERS];
4073 static boolean static_player_is_opaque[MAX_PLAYERS];
4074 static boolean draw_player[MAX_PLAYERS];
4075 int pnr = player->index_nr;
4077 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4079 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4080 static_last_player_frame[pnr] = player->Frame;
4081 static_player_is_opaque[pnr] = FALSE;
4083 draw_player[pnr] = TRUE;
4086 if (!draw_player[pnr])
4090 if (!IN_LEV_FIELD(player->jx, player->jy))
4092 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4093 Debug("draw:DrawPlayerExt", "This should never happen!");
4095 draw_player[pnr] = FALSE;
4101 int last_player_graphic = static_last_player_graphic[pnr];
4102 int last_player_frame = static_last_player_frame[pnr];
4103 boolean player_is_opaque = static_player_is_opaque[pnr];
4105 int jx = player->jx;
4106 int jy = player->jy;
4107 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4108 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4109 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4110 int last_jx = (player->is_moving ? jx - dx : jx);
4111 int last_jy = (player->is_moving ? jy - dy : jy);
4112 int next_jx = jx + dx;
4113 int next_jy = jy + dy;
4114 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4115 int sx = SCREENX(jx);
4116 int sy = SCREENY(jy);
4117 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4118 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4119 int element = Tile[jx][jy];
4120 int last_element = Tile[last_jx][last_jy];
4121 int action = (player->is_pushing ? ACTION_PUSHING :
4122 player->is_digging ? ACTION_DIGGING :
4123 player->is_collecting ? ACTION_COLLECTING :
4124 player->is_moving ? ACTION_MOVING :
4125 player->is_snapping ? ACTION_SNAPPING :
4126 player->is_dropping ? ACTION_DROPPING :
4127 player->is_waiting ? player->action_waiting :
4130 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4132 // ------------------------------------------------------------------------
4133 // initialize drawing the player
4134 // ------------------------------------------------------------------------
4136 draw_player[pnr] = FALSE;
4138 // GfxElement[][] is set to the element the player is digging or collecting;
4139 // remove also for off-screen player if the player is not moving anymore
4140 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4141 GfxElement[jx][jy] = EL_UNDEFINED;
4143 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4146 if (element == EL_EXPLOSION)
4149 InitPlayerGfxAnimation(player, action, move_dir);
4151 draw_player[pnr] = TRUE;
4153 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4155 // ------------------------------------------------------------------------
4156 // draw things in the field the player is leaving, if needed
4157 // ------------------------------------------------------------------------
4159 if (!IN_SCR_FIELD(sx, sy))
4160 draw_player[pnr] = FALSE;
4162 if (!player->is_moving)
4165 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4167 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4169 if (last_element == EL_DYNAMITE_ACTIVE ||
4170 last_element == EL_EM_DYNAMITE_ACTIVE ||
4171 last_element == EL_SP_DISK_RED_ACTIVE)
4172 DrawDynamite(last_jx, last_jy);
4174 DrawLevelFieldThruMask(last_jx, last_jy);
4176 else if (last_element == EL_DYNAMITE_ACTIVE ||
4177 last_element == EL_EM_DYNAMITE_ACTIVE ||
4178 last_element == EL_SP_DISK_RED_ACTIVE)
4179 DrawDynamite(last_jx, last_jy);
4181 DrawLevelField(last_jx, last_jy);
4183 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4184 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4186 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4188 // ------------------------------------------------------------------------
4189 // draw things behind the player, if needed
4190 // ------------------------------------------------------------------------
4194 DrawLevelElement(jx, jy, Back[jx][jy]);
4199 if (IS_ACTIVE_BOMB(element))
4201 DrawLevelElement(jx, jy, EL_EMPTY);
4206 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4208 int old_element = GfxElement[jx][jy];
4209 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4210 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4212 if (GFX_CRUMBLED(old_element))
4213 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4215 DrawScreenGraphic(sx, sy, old_graphic, frame);
4217 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4218 static_player_is_opaque[pnr] = TRUE;
4222 GfxElement[jx][jy] = EL_UNDEFINED;
4224 // make sure that pushed elements are drawn with correct frame rate
4225 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4227 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4228 GfxFrame[jx][jy] = player->StepFrame;
4230 DrawLevelField(jx, jy);
4233 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4235 // ------------------------------------------------------------------------
4236 // draw things the player is pushing, if needed
4237 // ------------------------------------------------------------------------
4239 if (!player->is_pushing || !player->is_moving)
4242 int gfx_frame = GfxFrame[jx][jy];
4244 if (!IS_MOVING(jx, jy)) // push movement already finished
4246 element = Tile[next_jx][next_jy];
4247 gfx_frame = GfxFrame[next_jx][next_jy];
4250 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4251 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4252 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4254 // draw background element under pushed element (like the Sokoban field)
4255 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4257 // this allows transparent pushing animation over non-black background
4260 DrawLevelElement(jx, jy, Back[jx][jy]);
4262 DrawLevelElement(jx, jy, EL_EMPTY);
4264 if (Back[next_jx][next_jy])
4265 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4267 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4269 else if (Back[next_jx][next_jy])
4270 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4272 int px = SCREENX(jx), py = SCREENY(jy);
4273 int pxx = (TILEX - ABS(sxx)) * dx;
4274 int pyy = (TILEY - ABS(syy)) * dy;
4277 // do not draw (EM style) pushing animation when pushing is finished
4278 // (two-tile animations usually do not contain start and end frame)
4279 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4280 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4282 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4284 // masked drawing is needed for EMC style (double) movement graphics
4285 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4286 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4289 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4291 // ------------------------------------------------------------------------
4292 // draw player himself
4293 // ------------------------------------------------------------------------
4295 int graphic = getPlayerGraphic(player, move_dir);
4297 // in the case of changed player action or direction, prevent the current
4298 // animation frame from being restarted for identical animations
4299 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4300 player->Frame = last_player_frame;
4302 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4304 if (player_is_opaque)
4305 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4307 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4309 if (SHIELD_ON(player))
4311 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4312 IMG_SHIELD_NORMAL_ACTIVE);
4313 frame = getGraphicAnimationFrame(graphic, -1);
4315 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4318 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4320 // ------------------------------------------------------------------------
4321 // draw things in front of player (active dynamite or dynabombs)
4322 // ------------------------------------------------------------------------
4324 if (IS_ACTIVE_BOMB(element))
4326 int graphic = el2img(element);
4327 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4329 if (game.emulation == EMU_SUPAPLEX)
4330 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4332 DrawGraphicThruMask(sx, sy, graphic, frame);
4335 if (player_is_moving && last_element == EL_EXPLOSION)
4337 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4338 GfxElement[last_jx][last_jy] : EL_EMPTY);
4339 int graphic = el_act2img(element, ACTION_EXPLODING);
4340 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4341 int phase = ExplodePhase[last_jx][last_jy] - 1;
4342 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4345 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4348 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4350 // ------------------------------------------------------------------------
4351 // draw elements the player is just walking/passing through/under
4352 // ------------------------------------------------------------------------
4354 if (player_is_moving)
4356 // handle the field the player is leaving ...
4357 if (IS_ACCESSIBLE_INSIDE(last_element))
4358 DrawLevelField(last_jx, last_jy);
4359 else if (IS_ACCESSIBLE_UNDER(last_element))
4360 DrawLevelFieldThruMask(last_jx, last_jy);
4363 // do not redraw accessible elements if the player is just pushing them
4364 if (!player_is_moving || !player->is_pushing)
4366 // ... and the field the player is entering
4367 if (IS_ACCESSIBLE_INSIDE(element))
4368 DrawLevelField(jx, jy);
4369 else if (IS_ACCESSIBLE_UNDER(element))
4370 DrawLevelFieldThruMask(jx, jy);
4373 MarkTileDirty(sx, sy);
4377 void DrawPlayer(struct PlayerInfo *player)
4381 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4382 DrawPlayerExt(player, i);
4385 void DrawAllPlayers(void)
4389 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4390 for (j = 0; j < MAX_PLAYERS; j++)
4391 if (stored_player[j].active)
4392 DrawPlayerExt(&stored_player[j], i);
4395 void DrawPlayerField(int x, int y)
4397 if (!IS_PLAYER(x, y))
4400 DrawPlayer(PLAYERINFO(x, y));
4403 // ----------------------------------------------------------------------------
4405 void WaitForEventToContinue(void)
4407 boolean first_wait = TRUE;
4408 boolean still_wait = TRUE;
4410 if (program.headless)
4413 // simulate releasing mouse button over last gadget, if still pressed
4415 HandleGadgets(-1, -1, 0);
4417 button_status = MB_RELEASED;
4420 ClearPlayerAction();
4426 if (NextValidEvent(&event))
4430 case EVENT_BUTTONPRESS:
4431 case EVENT_FINGERPRESS:
4435 case EVENT_BUTTONRELEASE:
4436 case EVENT_FINGERRELEASE:
4437 still_wait = first_wait;
4440 case EVENT_KEYPRESS:
4441 case SDL_CONTROLLERBUTTONDOWN:
4442 case SDL_JOYBUTTONDOWN:
4447 HandleOtherEvents(&event);
4451 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4456 if (!PendingEvent())
4461 #define MAX_REQUEST_LINES 13
4462 #define MAX_REQUEST_LINE_FONT1_LEN 7
4463 #define MAX_REQUEST_LINE_FONT2_LEN 10
4465 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4467 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4469 int draw_buffer_last = GetDrawtoField();
4470 int width = request.width;
4471 int height = request.height;
4475 // when showing request dialog after game ended, deactivate game panel
4476 if (game_just_ended)
4477 game.panel.active = FALSE;
4479 game.request_active = TRUE;
4481 setRequestPosition(&sx, &sy, FALSE);
4483 button_status = MB_RELEASED;
4485 request_gadget_id = -1;
4490 boolean event_handled = FALSE;
4492 if (game_just_ended)
4494 SetDrawtoField(draw_buffer_game);
4496 HandleGameActions();
4498 SetDrawtoField(DRAW_TO_BACKBUFFER);
4500 if (global.use_envelope_request)
4502 // copy current state of request area to middle of playfield area
4503 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4511 while (NextValidEvent(&event))
4513 event_handled = TRUE;
4517 case EVENT_BUTTONPRESS:
4518 case EVENT_BUTTONRELEASE:
4519 case EVENT_MOTIONNOTIFY:
4523 if (event.type == EVENT_MOTIONNOTIFY)
4528 motion_status = TRUE;
4529 mx = ((MotionEvent *) &event)->x;
4530 my = ((MotionEvent *) &event)->y;
4534 motion_status = FALSE;
4535 mx = ((ButtonEvent *) &event)->x;
4536 my = ((ButtonEvent *) &event)->y;
4537 if (event.type == EVENT_BUTTONPRESS)
4538 button_status = ((ButtonEvent *) &event)->button;
4540 button_status = MB_RELEASED;
4543 // this sets 'request_gadget_id'
4544 HandleGadgets(mx, my, button_status);
4546 switch (request_gadget_id)
4548 case TOOL_CTRL_ID_YES:
4549 case TOOL_CTRL_ID_TOUCH_YES:
4552 case TOOL_CTRL_ID_NO:
4553 case TOOL_CTRL_ID_TOUCH_NO:
4556 case TOOL_CTRL_ID_CONFIRM:
4557 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4558 result = TRUE | FALSE;
4561 case TOOL_CTRL_ID_PLAYER_1:
4564 case TOOL_CTRL_ID_PLAYER_2:
4567 case TOOL_CTRL_ID_PLAYER_3:
4570 case TOOL_CTRL_ID_PLAYER_4:
4575 // only check clickable animations if no request gadget clicked
4576 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4583 case SDL_WINDOWEVENT:
4584 HandleWindowEvent((WindowEvent *) &event);
4587 case SDL_APP_WILLENTERBACKGROUND:
4588 case SDL_APP_DIDENTERBACKGROUND:
4589 case SDL_APP_WILLENTERFOREGROUND:
4590 case SDL_APP_DIDENTERFOREGROUND:
4591 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4594 case EVENT_KEYPRESS:
4596 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4601 if (req_state & REQ_CONFIRM)
4610 #if defined(KSYM_Rewind)
4611 case KSYM_Rewind: // for Amazon Fire TV remote
4620 #if defined(KSYM_FastForward)
4621 case KSYM_FastForward: // for Amazon Fire TV remote
4627 HandleKeysDebug(key, KEY_PRESSED);
4631 if (req_state & REQ_PLAYER)
4633 int old_player_nr = setup.network_player_nr;
4636 result = old_player_nr + 1;
4641 result = old_player_nr + 1;
4672 case EVENT_FINGERRELEASE:
4673 case EVENT_KEYRELEASE:
4674 ClearPlayerAction();
4677 case SDL_CONTROLLERBUTTONDOWN:
4678 switch (event.cbutton.button)
4680 case SDL_CONTROLLER_BUTTON_A:
4681 case SDL_CONTROLLER_BUTTON_X:
4682 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4683 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4687 case SDL_CONTROLLER_BUTTON_B:
4688 case SDL_CONTROLLER_BUTTON_Y:
4689 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4690 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4691 case SDL_CONTROLLER_BUTTON_BACK:
4696 if (req_state & REQ_PLAYER)
4698 int old_player_nr = setup.network_player_nr;
4701 result = old_player_nr + 1;
4703 switch (event.cbutton.button)
4705 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4706 case SDL_CONTROLLER_BUTTON_Y:
4710 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4711 case SDL_CONTROLLER_BUTTON_B:
4715 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4716 case SDL_CONTROLLER_BUTTON_A:
4720 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4721 case SDL_CONTROLLER_BUTTON_X:
4732 case SDL_CONTROLLERBUTTONUP:
4733 HandleJoystickEvent(&event);
4734 ClearPlayerAction();
4738 HandleOtherEvents(&event);
4743 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4745 int joy = AnyJoystick();
4747 if (joy & JOY_BUTTON_1)
4749 else if (joy & JOY_BUTTON_2)
4752 else if (AnyJoystick())
4754 int joy = AnyJoystick();
4756 if (req_state & REQ_PLAYER)
4760 else if (joy & JOY_RIGHT)
4762 else if (joy & JOY_DOWN)
4764 else if (joy & JOY_LEFT)
4771 if (game_just_ended)
4773 if (global.use_envelope_request)
4775 // copy back current state of pressed buttons inside request area
4776 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4780 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4786 SetDrawtoField(draw_buffer_last);
4788 game.request_active = FALSE;
4793 static boolean RequestDoor(char *text, unsigned int req_state)
4795 int draw_buffer_last = GetDrawtoField();
4796 unsigned int old_door_state;
4797 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4798 int font_nr = FONT_TEXT_2;
4803 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4805 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4806 font_nr = FONT_TEXT_1;
4809 if (game_status == GAME_MODE_PLAYING)
4810 BlitScreenToBitmap(backbuffer);
4812 // disable deactivated drawing when quick-loading level tape recording
4813 if (tape.playing && tape.deactivate_display)
4814 TapeDeactivateDisplayOff(TRUE);
4816 SetMouseCursor(CURSOR_DEFAULT);
4818 // pause network game while waiting for request to answer
4819 if (network.enabled &&
4820 game_status == GAME_MODE_PLAYING &&
4821 !game.all_players_gone &&
4822 req_state & REQUEST_WAIT_FOR_INPUT)
4823 SendToServer_PausePlaying();
4825 old_door_state = GetDoorState();
4827 // simulate releasing mouse button over last gadget, if still pressed
4829 HandleGadgets(-1, -1, 0);
4833 // draw released gadget before proceeding
4836 if (old_door_state & DOOR_OPEN_1)
4838 CloseDoor(DOOR_CLOSE_1);
4840 // save old door content
4841 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4842 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4845 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4846 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4848 // clear door drawing field
4849 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4851 // force DOOR font inside door area
4852 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4854 // write text for request
4855 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4857 char text_line[max_request_line_len + 1];
4863 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4865 tc = *(text_ptr + tx);
4866 // if (!tc || tc == ' ')
4867 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4871 if ((tc == '?' || tc == '!') && tl == 0)
4881 strncpy(text_line, text_ptr, tl);
4884 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4885 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4886 text_line, font_nr);
4888 text_ptr += tl + (tc == ' ' ? 1 : 0);
4889 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4894 if (req_state & REQ_ASK)
4896 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4897 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4898 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4899 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4901 else if (req_state & REQ_CONFIRM)
4903 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4904 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4906 else if (req_state & REQ_PLAYER)
4908 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4909 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4910 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4911 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4914 // copy request gadgets to door backbuffer
4915 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4917 OpenDoor(DOOR_OPEN_1);
4919 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4921 if (game_status == GAME_MODE_PLAYING)
4923 SetPanelBackground();
4924 SetDrawBackgroundMask(REDRAW_DOOR_1);
4928 SetDrawBackgroundMask(REDRAW_FIELD);
4934 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4936 // ---------- handle request buttons ----------
4937 result = RequestHandleEvents(req_state, draw_buffer_last);
4941 if (!(req_state & REQ_STAY_OPEN))
4943 CloseDoor(DOOR_CLOSE_1);
4945 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4946 (req_state & REQ_REOPEN))
4947 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4952 if (game_status == GAME_MODE_PLAYING)
4954 SetPanelBackground();
4955 SetDrawBackgroundMask(REDRAW_DOOR_1);
4959 SetDrawBackgroundMask(REDRAW_FIELD);
4962 // continue network game after request
4963 if (network.enabled &&
4964 game_status == GAME_MODE_PLAYING &&
4965 !game.all_players_gone &&
4966 req_state & REQUEST_WAIT_FOR_INPUT)
4967 SendToServer_ContinuePlaying();
4969 // restore deactivated drawing when quick-loading level tape recording
4970 if (tape.playing && tape.deactivate_display)
4971 TapeDeactivateDisplayOn();
4976 static boolean RequestEnvelope(char *text, unsigned int req_state)
4978 int draw_buffer_last = GetDrawtoField();
4981 if (game_status == GAME_MODE_PLAYING)
4982 BlitScreenToBitmap(backbuffer);
4984 // disable deactivated drawing when quick-loading level tape recording
4985 if (tape.playing && tape.deactivate_display)
4986 TapeDeactivateDisplayOff(TRUE);
4988 SetMouseCursor(CURSOR_DEFAULT);
4990 // pause network game while waiting for request to answer
4991 if (network.enabled &&
4992 game_status == GAME_MODE_PLAYING &&
4993 !game.all_players_gone &&
4994 req_state & REQUEST_WAIT_FOR_INPUT)
4995 SendToServer_PausePlaying();
4997 // simulate releasing mouse button over last gadget, if still pressed
4999 HandleGadgets(-1, -1, 0);
5003 // (replace with setting corresponding request background)
5004 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5005 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5007 // clear door drawing field
5008 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5010 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5012 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5014 if (game_status == GAME_MODE_PLAYING)
5016 SetPanelBackground();
5017 SetDrawBackgroundMask(REDRAW_DOOR_1);
5021 SetDrawBackgroundMask(REDRAW_FIELD);
5027 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5029 // ---------- handle request buttons ----------
5030 result = RequestHandleEvents(req_state, draw_buffer_last);
5034 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5038 if (game_status == GAME_MODE_PLAYING)
5040 SetPanelBackground();
5041 SetDrawBackgroundMask(REDRAW_DOOR_1);
5045 SetDrawBackgroundMask(REDRAW_FIELD);
5048 // continue network game after request
5049 if (network.enabled &&
5050 game_status == GAME_MODE_PLAYING &&
5051 !game.all_players_gone &&
5052 req_state & REQUEST_WAIT_FOR_INPUT)
5053 SendToServer_ContinuePlaying();
5055 // restore deactivated drawing when quick-loading level tape recording
5056 if (tape.playing && tape.deactivate_display)
5057 TapeDeactivateDisplayOn();
5062 boolean Request(char *text, unsigned int req_state)
5064 boolean overlay_enabled = GetOverlayEnabled();
5067 game.request_active_or_moving = TRUE;
5069 SetOverlayEnabled(FALSE);
5071 if (global.use_envelope_request)
5072 result = RequestEnvelope(text, req_state);
5074 result = RequestDoor(text, req_state);
5076 SetOverlayEnabled(overlay_enabled);
5078 game.request_active_or_moving = FALSE;
5083 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5085 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5086 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5089 if (dpo1->sort_priority != dpo2->sort_priority)
5090 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5092 compare_result = dpo1->nr - dpo2->nr;
5094 return compare_result;
5097 void InitGraphicCompatibilityInfo_Doors(void)
5103 struct DoorInfo *door;
5107 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5108 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5110 { -1, -1, -1, NULL }
5112 struct Rect door_rect_list[] =
5114 { DX, DY, DXSIZE, DYSIZE },
5115 { VX, VY, VXSIZE, VYSIZE }
5119 for (i = 0; doors[i].door_token != -1; i++)
5121 int door_token = doors[i].door_token;
5122 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5123 int part_1 = doors[i].part_1;
5124 int part_8 = doors[i].part_8;
5125 int part_2 = part_1 + 1;
5126 int part_3 = part_1 + 2;
5127 struct DoorInfo *door = doors[i].door;
5128 struct Rect *door_rect = &door_rect_list[door_index];
5129 boolean door_gfx_redefined = FALSE;
5131 // check if any door part graphic definitions have been redefined
5133 for (j = 0; door_part_controls[j].door_token != -1; j++)
5135 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5136 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5138 if (dpc->door_token == door_token && fi->redefined)
5139 door_gfx_redefined = TRUE;
5142 // check for old-style door graphic/animation modifications
5144 if (!door_gfx_redefined)
5146 if (door->anim_mode & ANIM_STATIC_PANEL)
5148 door->panel.step_xoffset = 0;
5149 door->panel.step_yoffset = 0;
5152 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5154 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5155 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5156 int num_door_steps, num_panel_steps;
5158 // remove door part graphics other than the two default wings
5160 for (j = 0; door_part_controls[j].door_token != -1; j++)
5162 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5163 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5165 if (dpc->graphic >= part_3 &&
5166 dpc->graphic <= part_8)
5170 // set graphics and screen positions of the default wings
5172 g_part_1->width = door_rect->width;
5173 g_part_1->height = door_rect->height;
5174 g_part_2->width = door_rect->width;
5175 g_part_2->height = door_rect->height;
5176 g_part_2->src_x = door_rect->width;
5177 g_part_2->src_y = g_part_1->src_y;
5179 door->part_2.x = door->part_1.x;
5180 door->part_2.y = door->part_1.y;
5182 if (door->width != -1)
5184 g_part_1->width = door->width;
5185 g_part_2->width = door->width;
5187 // special treatment for graphics and screen position of right wing
5188 g_part_2->src_x += door_rect->width - door->width;
5189 door->part_2.x += door_rect->width - door->width;
5192 if (door->height != -1)
5194 g_part_1->height = door->height;
5195 g_part_2->height = door->height;
5197 // special treatment for graphics and screen position of bottom wing
5198 g_part_2->src_y += door_rect->height - door->height;
5199 door->part_2.y += door_rect->height - door->height;
5202 // set animation delays for the default wings and panels
5204 door->part_1.step_delay = door->step_delay;
5205 door->part_2.step_delay = door->step_delay;
5206 door->panel.step_delay = door->step_delay;
5208 // set animation draw order for the default wings
5210 door->part_1.sort_priority = 2; // draw left wing over ...
5211 door->part_2.sort_priority = 1; // ... right wing
5213 // set animation draw offset for the default wings
5215 if (door->anim_mode & ANIM_HORIZONTAL)
5217 door->part_1.step_xoffset = door->step_offset;
5218 door->part_1.step_yoffset = 0;
5219 door->part_2.step_xoffset = door->step_offset * -1;
5220 door->part_2.step_yoffset = 0;
5222 num_door_steps = g_part_1->width / door->step_offset;
5224 else // ANIM_VERTICAL
5226 door->part_1.step_xoffset = 0;
5227 door->part_1.step_yoffset = door->step_offset;
5228 door->part_2.step_xoffset = 0;
5229 door->part_2.step_yoffset = door->step_offset * -1;
5231 num_door_steps = g_part_1->height / door->step_offset;
5234 // set animation draw offset for the default panels
5236 if (door->step_offset > 1)
5238 num_panel_steps = 2 * door_rect->height / door->step_offset;
5239 door->panel.start_step = num_panel_steps - num_door_steps;
5240 door->panel.start_step_closing = door->panel.start_step;
5244 num_panel_steps = door_rect->height / door->step_offset;
5245 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5246 door->panel.start_step_closing = door->panel.start_step;
5247 door->panel.step_delay *= 2;
5254 void InitDoors(void)
5258 for (i = 0; door_part_controls[i].door_token != -1; i++)
5260 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5261 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5263 // initialize "start_step_opening" and "start_step_closing", if needed
5264 if (dpc->pos->start_step_opening == 0 &&
5265 dpc->pos->start_step_closing == 0)
5267 // dpc->pos->start_step_opening = dpc->pos->start_step;
5268 dpc->pos->start_step_closing = dpc->pos->start_step;
5271 // fill structure for door part draw order (sorted below)
5273 dpo->sort_priority = dpc->pos->sort_priority;
5276 // sort door part controls according to sort_priority and graphic number
5277 qsort(door_part_order, MAX_DOOR_PARTS,
5278 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5281 unsigned int OpenDoor(unsigned int door_state)
5283 if (door_state & DOOR_COPY_BACK)
5285 if (door_state & DOOR_OPEN_1)
5286 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5287 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5289 if (door_state & DOOR_OPEN_2)
5290 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5291 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5293 door_state &= ~DOOR_COPY_BACK;
5296 return MoveDoor(door_state);
5299 unsigned int CloseDoor(unsigned int door_state)
5301 unsigned int old_door_state = GetDoorState();
5303 if (!(door_state & DOOR_NO_COPY_BACK))
5305 if (old_door_state & DOOR_OPEN_1)
5306 BlitBitmap(backbuffer, bitmap_db_door_1,
5307 DX, DY, DXSIZE, DYSIZE, 0, 0);
5309 if (old_door_state & DOOR_OPEN_2)
5310 BlitBitmap(backbuffer, bitmap_db_door_2,
5311 VX, VY, VXSIZE, VYSIZE, 0, 0);
5313 door_state &= ~DOOR_NO_COPY_BACK;
5316 return MoveDoor(door_state);
5319 unsigned int GetDoorState(void)
5321 return MoveDoor(DOOR_GET_STATE);
5324 unsigned int SetDoorState(unsigned int door_state)
5326 return MoveDoor(door_state | DOOR_SET_STATE);
5329 static int euclid(int a, int b)
5331 return (b ? euclid(b, a % b) : a);
5334 unsigned int MoveDoor(unsigned int door_state)
5336 struct Rect door_rect_list[] =
5338 { DX, DY, DXSIZE, DYSIZE },
5339 { VX, VY, VXSIZE, VYSIZE }
5341 static int door1 = DOOR_CLOSE_1;
5342 static int door2 = DOOR_CLOSE_2;
5343 unsigned int door_delay = 0;
5344 unsigned int door_delay_value;
5347 if (door_state == DOOR_GET_STATE)
5348 return (door1 | door2);
5350 if (door_state & DOOR_SET_STATE)
5352 if (door_state & DOOR_ACTION_1)
5353 door1 = door_state & DOOR_ACTION_1;
5354 if (door_state & DOOR_ACTION_2)
5355 door2 = door_state & DOOR_ACTION_2;
5357 return (door1 | door2);
5360 if (!(door_state & DOOR_FORCE_REDRAW))
5362 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5363 door_state &= ~DOOR_OPEN_1;
5364 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5365 door_state &= ~DOOR_CLOSE_1;
5366 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5367 door_state &= ~DOOR_OPEN_2;
5368 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5369 door_state &= ~DOOR_CLOSE_2;
5372 if (global.autoplay_leveldir)
5374 door_state |= DOOR_NO_DELAY;
5375 door_state &= ~DOOR_CLOSE_ALL;
5378 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5379 door_state |= DOOR_NO_DELAY;
5381 if (door_state & DOOR_ACTION)
5383 boolean door_panel_drawn[NUM_DOORS];
5384 boolean panel_has_doors[NUM_DOORS];
5385 boolean door_part_skip[MAX_DOOR_PARTS];
5386 boolean door_part_done[MAX_DOOR_PARTS];
5387 boolean door_part_done_all;
5388 int num_steps[MAX_DOOR_PARTS];
5389 int max_move_delay = 0; // delay for complete animations of all doors
5390 int max_step_delay = 0; // delay (ms) between two animation frames
5391 int num_move_steps = 0; // number of animation steps for all doors
5392 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5393 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5397 for (i = 0; i < NUM_DOORS; i++)
5398 panel_has_doors[i] = FALSE;
5400 for (i = 0; i < MAX_DOOR_PARTS; i++)
5402 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5403 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5404 int door_token = dpc->door_token;
5406 door_part_done[i] = FALSE;
5407 door_part_skip[i] = (!(door_state & door_token) ||
5411 for (i = 0; i < MAX_DOOR_PARTS; i++)
5413 int nr = door_part_order[i].nr;
5414 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5415 struct DoorPartPosInfo *pos = dpc->pos;
5416 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5417 int door_token = dpc->door_token;
5418 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5419 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5420 int step_xoffset = ABS(pos->step_xoffset);
5421 int step_yoffset = ABS(pos->step_yoffset);
5422 int step_delay = pos->step_delay;
5423 int current_door_state = door_state & door_token;
5424 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5425 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5426 boolean part_opening = (is_panel ? door_closing : door_opening);
5427 int start_step = (part_opening ? pos->start_step_opening :
5428 pos->start_step_closing);
5429 float move_xsize = (step_xoffset ? g->width : 0);
5430 float move_ysize = (step_yoffset ? g->height : 0);
5431 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5432 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5433 int move_steps = (move_xsteps && move_ysteps ?
5434 MIN(move_xsteps, move_ysteps) :
5435 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5436 int move_delay = move_steps * step_delay;
5438 if (door_part_skip[nr])
5441 max_move_delay = MAX(max_move_delay, move_delay);
5442 max_step_delay = (max_step_delay == 0 ? step_delay :
5443 euclid(max_step_delay, step_delay));
5444 num_steps[nr] = move_steps;
5448 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5450 panel_has_doors[door_index] = TRUE;
5454 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5456 num_move_steps = max_move_delay / max_step_delay;
5457 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5459 door_delay_value = max_step_delay;
5461 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5463 start = num_move_steps - 1;
5467 // opening door sound has priority over simultaneously closing door
5468 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5470 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5472 if (door_state & DOOR_OPEN_1)
5473 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5474 if (door_state & DOOR_OPEN_2)
5475 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5477 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5479 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5481 if (door_state & DOOR_CLOSE_1)
5482 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5483 if (door_state & DOOR_CLOSE_2)
5484 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5488 for (k = start; k < num_move_steps; k++)
5490 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5492 door_part_done_all = TRUE;
5494 for (i = 0; i < NUM_DOORS; i++)
5495 door_panel_drawn[i] = FALSE;
5497 for (i = 0; i < MAX_DOOR_PARTS; i++)
5499 int nr = door_part_order[i].nr;
5500 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5501 struct DoorPartPosInfo *pos = dpc->pos;
5502 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5503 int door_token = dpc->door_token;
5504 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5505 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5506 boolean is_panel_and_door_has_closed = FALSE;
5507 struct Rect *door_rect = &door_rect_list[door_index];
5508 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5510 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5511 int current_door_state = door_state & door_token;
5512 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5513 boolean door_closing = !door_opening;
5514 boolean part_opening = (is_panel ? door_closing : door_opening);
5515 boolean part_closing = !part_opening;
5516 int start_step = (part_opening ? pos->start_step_opening :
5517 pos->start_step_closing);
5518 int step_delay = pos->step_delay;
5519 int step_factor = step_delay / max_step_delay;
5520 int k1 = (step_factor ? k / step_factor + 1 : k);
5521 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5522 int kk = MAX(0, k2);
5525 int src_x, src_y, src_xx, src_yy;
5526 int dst_x, dst_y, dst_xx, dst_yy;
5529 if (door_part_skip[nr])
5532 if (!(door_state & door_token))
5540 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5541 int kk_door = MAX(0, k2_door);
5542 int sync_frame = kk_door * door_delay_value;
5543 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5545 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5546 &g_src_x, &g_src_y);
5551 if (!door_panel_drawn[door_index])
5553 ClearRectangle(drawto, door_rect->x, door_rect->y,
5554 door_rect->width, door_rect->height);
5556 door_panel_drawn[door_index] = TRUE;
5559 // draw opening or closing door parts
5561 if (pos->step_xoffset < 0) // door part on right side
5564 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5567 if (dst_xx + width > door_rect->width)
5568 width = door_rect->width - dst_xx;
5570 else // door part on left side
5573 dst_xx = pos->x - kk * pos->step_xoffset;
5577 src_xx = ABS(dst_xx);
5581 width = g->width - src_xx;
5583 if (width > door_rect->width)
5584 width = door_rect->width;
5586 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5589 if (pos->step_yoffset < 0) // door part on bottom side
5592 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5595 if (dst_yy + height > door_rect->height)
5596 height = door_rect->height - dst_yy;
5598 else // door part on top side
5601 dst_yy = pos->y - kk * pos->step_yoffset;
5605 src_yy = ABS(dst_yy);
5609 height = g->height - src_yy;
5612 src_x = g_src_x + src_xx;
5613 src_y = g_src_y + src_yy;
5615 dst_x = door_rect->x + dst_xx;
5616 dst_y = door_rect->y + dst_yy;
5618 is_panel_and_door_has_closed =
5621 panel_has_doors[door_index] &&
5622 k >= num_move_steps_doors_only - 1);
5624 if (width >= 0 && width <= g->width &&
5625 height >= 0 && height <= g->height &&
5626 !is_panel_and_door_has_closed)
5628 if (is_panel || !pos->draw_masked)
5629 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5632 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5636 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5638 if ((part_opening && (width < 0 || height < 0)) ||
5639 (part_closing && (width >= g->width && height >= g->height)))
5640 door_part_done[nr] = TRUE;
5642 // continue door part animations, but not panel after door has closed
5643 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5644 door_part_done_all = FALSE;
5647 if (!(door_state & DOOR_NO_DELAY))
5651 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5653 // prevent OS (Windows) from complaining about program not responding
5657 if (door_part_done_all)
5661 if (!(door_state & DOOR_NO_DELAY))
5663 // wait for specified door action post delay
5664 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5665 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5666 else if (door_state & DOOR_ACTION_1)
5667 door_delay_value = door_1.post_delay;
5668 else if (door_state & DOOR_ACTION_2)
5669 door_delay_value = door_2.post_delay;
5671 while (!DelayReached(&door_delay, door_delay_value))
5676 if (door_state & DOOR_ACTION_1)
5677 door1 = door_state & DOOR_ACTION_1;
5678 if (door_state & DOOR_ACTION_2)
5679 door2 = door_state & DOOR_ACTION_2;
5681 // draw masked border over door area
5682 DrawMaskedBorder(REDRAW_DOOR_1);
5683 DrawMaskedBorder(REDRAW_DOOR_2);
5685 ClearAutoRepeatKeyEvents();
5687 return (door1 | door2);
5690 static boolean useSpecialEditorDoor(void)
5692 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5693 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5695 // do not draw special editor door if editor border defined or redefined
5696 if (graphic_info[graphic].bitmap != NULL || redefined)
5699 // do not draw special editor door if global border defined to be empty
5700 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5703 // do not draw special editor door if viewport definitions do not match
5707 EY + EYSIZE != VY + VYSIZE)
5713 void DrawSpecialEditorDoor(void)
5715 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5716 int top_border_width = gfx1->width;
5717 int top_border_height = gfx1->height;
5718 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5719 int ex = EX - outer_border;
5720 int ey = EY - outer_border;
5721 int vy = VY - outer_border;
5722 int exsize = EXSIZE + 2 * outer_border;
5724 if (!useSpecialEditorDoor())
5727 // draw bigger level editor toolbox window
5728 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5729 top_border_width, top_border_height, ex, ey - top_border_height);
5730 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5731 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5733 redraw_mask |= REDRAW_ALL;
5736 void UndrawSpecialEditorDoor(void)
5738 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5739 int top_border_width = gfx1->width;
5740 int top_border_height = gfx1->height;
5741 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5742 int ex = EX - outer_border;
5743 int ey = EY - outer_border;
5744 int ey_top = ey - top_border_height;
5745 int exsize = EXSIZE + 2 * outer_border;
5746 int eysize = EYSIZE + 2 * outer_border;
5748 if (!useSpecialEditorDoor())
5751 // draw normal tape recorder window
5752 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5754 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5755 ex, ey_top, top_border_width, top_border_height,
5757 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5758 ex, ey, exsize, eysize, ex, ey);
5762 // if screen background is set to "[NONE]", clear editor toolbox window
5763 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5764 ClearRectangle(drawto, ex, ey, exsize, eysize);
5767 redraw_mask |= REDRAW_ALL;
5771 // ---------- new tool button stuff -------------------------------------------
5776 struct TextPosInfo *pos;
5778 boolean is_touch_button;
5780 } toolbutton_info[NUM_TOOL_BUTTONS] =
5783 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5784 TOOL_CTRL_ID_YES, FALSE, "yes"
5787 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5788 TOOL_CTRL_ID_NO, FALSE, "no"
5791 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5792 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5795 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5796 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5799 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5800 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5803 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5804 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5807 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5808 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5811 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5812 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5815 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5816 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5819 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5820 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5824 void CreateToolButtons(void)
5828 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5830 int graphic = toolbutton_info[i].graphic;
5831 struct GraphicInfo *gfx = &graphic_info[graphic];
5832 struct TextPosInfo *pos = toolbutton_info[i].pos;
5833 struct GadgetInfo *gi;
5834 Bitmap *deco_bitmap = None;
5835 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5836 unsigned int event_mask = GD_EVENT_RELEASED;
5837 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5838 int base_x = (is_touch_button ? 0 : DX);
5839 int base_y = (is_touch_button ? 0 : DY);
5840 int gd_x = gfx->src_x;
5841 int gd_y = gfx->src_y;
5842 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5843 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5848 if (global.use_envelope_request && !is_touch_button)
5850 setRequestPosition(&base_x, &base_y, TRUE);
5852 // check if request buttons are outside of envelope and fix, if needed
5853 if (x < 0 || x + gfx->width > request.width ||
5854 y < 0 || y + gfx->height > request.height)
5856 if (id == TOOL_CTRL_ID_YES)
5859 y = request.height - 2 * request.border_size - gfx->height;
5861 else if (id == TOOL_CTRL_ID_NO)
5863 x = request.width - 2 * request.border_size - gfx->width;
5864 y = request.height - 2 * request.border_size - gfx->height;
5866 else if (id == TOOL_CTRL_ID_CONFIRM)
5868 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5869 y = request.height - 2 * request.border_size - gfx->height;
5871 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5873 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5875 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5876 y = request.height - 2 * request.border_size - gfx->height * 2;
5878 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5879 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5884 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5886 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5888 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5889 pos->size, &deco_bitmap, &deco_x, &deco_y);
5890 deco_xpos = (gfx->width - pos->size) / 2;
5891 deco_ypos = (gfx->height - pos->size) / 2;
5894 gi = CreateGadget(GDI_CUSTOM_ID, id,
5895 GDI_IMAGE_ID, graphic,
5896 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5899 GDI_WIDTH, gfx->width,
5900 GDI_HEIGHT, gfx->height,
5901 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5902 GDI_STATE, GD_BUTTON_UNPRESSED,
5903 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5904 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5905 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5906 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5907 GDI_DECORATION_SIZE, pos->size, pos->size,
5908 GDI_DECORATION_SHIFTING, 1, 1,
5909 GDI_DIRECT_DRAW, FALSE,
5910 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5911 GDI_EVENT_MASK, event_mask,
5912 GDI_CALLBACK_ACTION, HandleToolButtons,
5916 Fail("cannot create gadget");
5918 tool_gadget[id] = gi;
5922 void FreeToolButtons(void)
5926 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5927 FreeGadget(tool_gadget[i]);
5930 static void UnmapToolButtons(void)
5934 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5935 UnmapGadget(tool_gadget[i]);
5938 static void HandleToolButtons(struct GadgetInfo *gi)
5940 request_gadget_id = gi->custom_id;
5943 static struct Mapping_EM_to_RND_object
5946 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5947 boolean is_backside; // backside of moving element
5953 em_object_mapping_list[GAME_TILE_MAX + 1] =
5956 Zborder, FALSE, FALSE,
5960 Zplayer, FALSE, FALSE,
5969 Ztank, FALSE, FALSE,
5973 Zeater, FALSE, FALSE,
5977 Zdynamite, FALSE, FALSE,
5981 Zboom, FALSE, FALSE,
5986 Xchain, FALSE, FALSE,
5987 EL_DEFAULT, ACTION_EXPLODING, -1
5990 Xboom_bug, FALSE, FALSE,
5991 EL_BUG, ACTION_EXPLODING, -1
5994 Xboom_tank, FALSE, FALSE,
5995 EL_SPACESHIP, ACTION_EXPLODING, -1
5998 Xboom_android, FALSE, FALSE,
5999 EL_EMC_ANDROID, ACTION_OTHER, -1
6002 Xboom_1, FALSE, FALSE,
6003 EL_DEFAULT, ACTION_EXPLODING, -1
6006 Xboom_2, FALSE, FALSE,
6007 EL_DEFAULT, ACTION_EXPLODING, -1
6011 Xblank, TRUE, FALSE,
6016 Xsplash_e, FALSE, FALSE,
6017 EL_ACID_SPLASH_RIGHT, -1, -1
6020 Xsplash_w, FALSE, FALSE,
6021 EL_ACID_SPLASH_LEFT, -1, -1
6025 Xplant, TRUE, FALSE,
6026 EL_EMC_PLANT, -1, -1
6029 Yplant, FALSE, FALSE,
6030 EL_EMC_PLANT, -1, -1
6034 Xacid_1, TRUE, FALSE,
6038 Xacid_2, FALSE, FALSE,
6042 Xacid_3, FALSE, FALSE,
6046 Xacid_4, FALSE, FALSE,
6050 Xacid_5, FALSE, FALSE,
6054 Xacid_6, FALSE, FALSE,
6058 Xacid_7, FALSE, FALSE,
6062 Xacid_8, FALSE, FALSE,
6067 Xfake_acid_1, TRUE, FALSE,
6068 EL_EMC_FAKE_ACID, -1, -1
6071 Xfake_acid_2, FALSE, FALSE,
6072 EL_EMC_FAKE_ACID, -1, -1
6075 Xfake_acid_3, FALSE, FALSE,
6076 EL_EMC_FAKE_ACID, -1, -1
6079 Xfake_acid_4, FALSE, FALSE,
6080 EL_EMC_FAKE_ACID, -1, -1
6083 Xfake_acid_5, FALSE, FALSE,
6084 EL_EMC_FAKE_ACID, -1, -1
6087 Xfake_acid_6, FALSE, FALSE,
6088 EL_EMC_FAKE_ACID, -1, -1
6091 Xfake_acid_7, FALSE, FALSE,
6092 EL_EMC_FAKE_ACID, -1, -1
6095 Xfake_acid_8, FALSE, FALSE,
6096 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_1_player, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_2_player, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_3_player, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_4_player, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_5_player, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_6_player, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6124 Xfake_acid_7_player, FALSE, FALSE,
6125 EL_EMC_FAKE_ACID, -1, -1
6128 Xfake_acid_8_player, FALSE, FALSE,
6129 EL_EMC_FAKE_ACID, -1, -1
6133 Xgrass, TRUE, FALSE,
6134 EL_EMC_GRASS, -1, -1
6137 Ygrass_nB, FALSE, FALSE,
6138 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6141 Ygrass_eB, FALSE, FALSE,
6142 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6145 Ygrass_sB, FALSE, FALSE,
6146 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6149 Ygrass_wB, FALSE, FALSE,
6150 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6158 Ydirt_nB, FALSE, FALSE,
6159 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6162 Ydirt_eB, FALSE, FALSE,
6163 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6166 Ydirt_sB, FALSE, FALSE,
6167 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6170 Ydirt_wB, FALSE, FALSE,
6171 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6175 Xandroid, TRUE, FALSE,
6176 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6179 Xandroid_1_n, FALSE, FALSE,
6180 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6183 Xandroid_2_n, FALSE, FALSE,
6184 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6187 Xandroid_1_e, FALSE, FALSE,
6188 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6191 Xandroid_2_e, FALSE, FALSE,
6192 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6195 Xandroid_1_w, FALSE, FALSE,
6196 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6199 Xandroid_2_w, FALSE, FALSE,
6200 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6203 Xandroid_1_s, FALSE, FALSE,
6204 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6207 Xandroid_2_s, FALSE, FALSE,
6208 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6211 Yandroid_n, FALSE, FALSE,
6212 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6215 Yandroid_nB, FALSE, TRUE,
6216 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6219 Yandroid_ne, FALSE, FALSE,
6220 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6223 Yandroid_neB, FALSE, TRUE,
6224 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6227 Yandroid_e, FALSE, FALSE,
6228 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6231 Yandroid_eB, FALSE, TRUE,
6232 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6235 Yandroid_se, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6239 Yandroid_seB, FALSE, TRUE,
6240 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6243 Yandroid_s, FALSE, FALSE,
6244 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6247 Yandroid_sB, FALSE, TRUE,
6248 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6251 Yandroid_sw, FALSE, FALSE,
6252 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6255 Yandroid_swB, FALSE, TRUE,
6256 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6259 Yandroid_w, FALSE, FALSE,
6260 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6263 Yandroid_wB, FALSE, TRUE,
6264 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6267 Yandroid_nw, FALSE, FALSE,
6268 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6271 Yandroid_nwB, FALSE, TRUE,
6272 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6276 Xeater_n, TRUE, FALSE,
6277 EL_YAMYAM_UP, -1, -1
6280 Xeater_e, TRUE, FALSE,
6281 EL_YAMYAM_RIGHT, -1, -1
6284 Xeater_w, TRUE, FALSE,
6285 EL_YAMYAM_LEFT, -1, -1
6288 Xeater_s, TRUE, FALSE,
6289 EL_YAMYAM_DOWN, -1, -1
6292 Yeater_n, FALSE, FALSE,
6293 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6296 Yeater_nB, FALSE, TRUE,
6297 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6300 Yeater_e, FALSE, FALSE,
6301 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6304 Yeater_eB, FALSE, TRUE,
6305 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6308 Yeater_s, FALSE, FALSE,
6309 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6312 Yeater_sB, FALSE, TRUE,
6313 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6316 Yeater_w, FALSE, FALSE,
6317 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6320 Yeater_wB, FALSE, TRUE,
6321 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6324 Yeater_stone, FALSE, FALSE,
6325 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6328 Yeater_spring, FALSE, FALSE,
6329 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6333 Xalien, TRUE, FALSE,
6337 Xalien_pause, FALSE, FALSE,
6341 Yalien_n, FALSE, FALSE,
6342 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6345 Yalien_nB, FALSE, TRUE,
6346 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6349 Yalien_e, FALSE, FALSE,
6350 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6353 Yalien_eB, FALSE, TRUE,
6354 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6357 Yalien_s, FALSE, FALSE,
6358 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6361 Yalien_sB, FALSE, TRUE,
6362 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6365 Yalien_w, FALSE, FALSE,
6366 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6369 Yalien_wB, FALSE, TRUE,
6370 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6373 Yalien_stone, FALSE, FALSE,
6374 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6377 Yalien_spring, FALSE, FALSE,
6378 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6382 Xbug_1_n, TRUE, FALSE,
6386 Xbug_1_e, TRUE, FALSE,
6387 EL_BUG_RIGHT, -1, -1
6390 Xbug_1_s, TRUE, FALSE,
6394 Xbug_1_w, TRUE, FALSE,
6398 Xbug_2_n, FALSE, FALSE,
6402 Xbug_2_e, FALSE, FALSE,
6403 EL_BUG_RIGHT, -1, -1
6406 Xbug_2_s, FALSE, FALSE,
6410 Xbug_2_w, FALSE, FALSE,
6414 Ybug_n, FALSE, FALSE,
6415 EL_BUG, ACTION_MOVING, MV_BIT_UP
6418 Ybug_nB, FALSE, TRUE,
6419 EL_BUG, ACTION_MOVING, MV_BIT_UP
6422 Ybug_e, FALSE, FALSE,
6423 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6426 Ybug_eB, FALSE, TRUE,
6427 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6430 Ybug_s, FALSE, FALSE,
6431 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6434 Ybug_sB, FALSE, TRUE,
6435 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6438 Ybug_w, FALSE, FALSE,
6439 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6442 Ybug_wB, FALSE, TRUE,
6443 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6446 Ybug_w_n, FALSE, FALSE,
6447 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6450 Ybug_n_e, FALSE, FALSE,
6451 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6454 Ybug_e_s, FALSE, FALSE,
6455 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6458 Ybug_s_w, FALSE, FALSE,
6459 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6462 Ybug_e_n, FALSE, FALSE,
6463 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6466 Ybug_s_e, FALSE, FALSE,
6467 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6470 Ybug_w_s, FALSE, FALSE,
6471 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6474 Ybug_n_w, FALSE, FALSE,
6475 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6478 Ybug_stone, FALSE, FALSE,
6479 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6482 Ybug_spring, FALSE, FALSE,
6483 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6487 Xtank_1_n, TRUE, FALSE,
6488 EL_SPACESHIP_UP, -1, -1
6491 Xtank_1_e, TRUE, FALSE,
6492 EL_SPACESHIP_RIGHT, -1, -1
6495 Xtank_1_s, TRUE, FALSE,
6496 EL_SPACESHIP_DOWN, -1, -1
6499 Xtank_1_w, TRUE, FALSE,
6500 EL_SPACESHIP_LEFT, -1, -1
6503 Xtank_2_n, FALSE, FALSE,
6504 EL_SPACESHIP_UP, -1, -1
6507 Xtank_2_e, FALSE, FALSE,
6508 EL_SPACESHIP_RIGHT, -1, -1
6511 Xtank_2_s, FALSE, FALSE,
6512 EL_SPACESHIP_DOWN, -1, -1
6515 Xtank_2_w, FALSE, FALSE,
6516 EL_SPACESHIP_LEFT, -1, -1
6519 Ytank_n, FALSE, FALSE,
6520 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6523 Ytank_nB, FALSE, TRUE,
6524 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6527 Ytank_e, FALSE, FALSE,
6528 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6531 Ytank_eB, FALSE, TRUE,
6532 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6535 Ytank_s, FALSE, FALSE,
6536 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6539 Ytank_sB, FALSE, TRUE,
6540 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6543 Ytank_w, FALSE, FALSE,
6544 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6547 Ytank_wB, FALSE, TRUE,
6548 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6551 Ytank_w_n, FALSE, FALSE,
6552 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6555 Ytank_n_e, FALSE, FALSE,
6556 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6559 Ytank_e_s, FALSE, FALSE,
6560 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6563 Ytank_s_w, FALSE, FALSE,
6564 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6567 Ytank_e_n, FALSE, FALSE,
6568 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6571 Ytank_s_e, FALSE, FALSE,
6572 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6575 Ytank_w_s, FALSE, FALSE,
6576 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6579 Ytank_n_w, FALSE, FALSE,
6580 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6583 Ytank_stone, FALSE, FALSE,
6584 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6587 Ytank_spring, FALSE, FALSE,
6588 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6592 Xemerald, TRUE, FALSE,
6596 Xemerald_pause, FALSE, FALSE,
6600 Xemerald_fall, FALSE, FALSE,
6604 Xemerald_shine, FALSE, FALSE,
6605 EL_EMERALD, ACTION_TWINKLING, -1
6608 Yemerald_s, FALSE, FALSE,
6609 EL_EMERALD, ACTION_FALLING, -1
6612 Yemerald_sB, FALSE, TRUE,
6613 EL_EMERALD, ACTION_FALLING, -1
6616 Yemerald_e, FALSE, FALSE,
6617 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6620 Yemerald_eB, FALSE, TRUE,
6621 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6624 Yemerald_w, FALSE, FALSE,
6625 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6628 Yemerald_wB, FALSE, TRUE,
6629 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6632 Yemerald_blank, FALSE, FALSE,
6633 EL_EMERALD, ACTION_COLLECTING, -1
6637 Xdiamond, TRUE, FALSE,
6641 Xdiamond_pause, FALSE, FALSE,
6645 Xdiamond_fall, FALSE, FALSE,
6649 Xdiamond_shine, FALSE, FALSE,
6650 EL_DIAMOND, ACTION_TWINKLING, -1
6653 Ydiamond_s, FALSE, FALSE,
6654 EL_DIAMOND, ACTION_FALLING, -1
6657 Ydiamond_sB, FALSE, TRUE,
6658 EL_DIAMOND, ACTION_FALLING, -1
6661 Ydiamond_e, FALSE, FALSE,
6662 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6665 Ydiamond_eB, FALSE, TRUE,
6666 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6669 Ydiamond_w, FALSE, FALSE,
6670 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6673 Ydiamond_wB, FALSE, TRUE,
6674 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6677 Ydiamond_blank, FALSE, FALSE,
6678 EL_DIAMOND, ACTION_COLLECTING, -1
6681 Ydiamond_stone, FALSE, FALSE,
6682 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6686 Xstone, TRUE, FALSE,
6690 Xstone_pause, FALSE, FALSE,
6694 Xstone_fall, FALSE, FALSE,
6698 Ystone_s, FALSE, FALSE,
6699 EL_ROCK, ACTION_FALLING, -1
6702 Ystone_sB, FALSE, TRUE,
6703 EL_ROCK, ACTION_FALLING, -1
6706 Ystone_e, FALSE, FALSE,
6707 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6710 Ystone_eB, FALSE, TRUE,
6711 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6714 Ystone_w, FALSE, FALSE,
6715 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6718 Ystone_wB, FALSE, TRUE,
6719 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6727 Xbomb_pause, FALSE, FALSE,
6731 Xbomb_fall, FALSE, FALSE,
6735 Ybomb_s, FALSE, FALSE,
6736 EL_BOMB, ACTION_FALLING, -1
6739 Ybomb_sB, FALSE, TRUE,
6740 EL_BOMB, ACTION_FALLING, -1
6743 Ybomb_e, FALSE, FALSE,
6744 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6747 Ybomb_eB, FALSE, TRUE,
6748 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6751 Ybomb_w, FALSE, FALSE,
6752 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6755 Ybomb_wB, FALSE, TRUE,
6756 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6759 Ybomb_blank, FALSE, FALSE,
6760 EL_BOMB, ACTION_ACTIVATING, -1
6768 Xnut_pause, FALSE, FALSE,
6772 Xnut_fall, FALSE, FALSE,
6776 Ynut_s, FALSE, FALSE,
6777 EL_NUT, ACTION_FALLING, -1
6780 Ynut_sB, FALSE, TRUE,
6781 EL_NUT, ACTION_FALLING, -1
6784 Ynut_e, FALSE, FALSE,
6785 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6788 Ynut_eB, FALSE, TRUE,
6789 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6792 Ynut_w, FALSE, FALSE,
6793 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6796 Ynut_wB, FALSE, TRUE,
6797 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6800 Ynut_stone, FALSE, FALSE,
6801 EL_NUT, ACTION_BREAKING, -1
6805 Xspring, TRUE, FALSE,
6809 Xspring_pause, FALSE, FALSE,
6813 Xspring_e, TRUE, FALSE,
6814 EL_SPRING_RIGHT, -1, -1
6817 Xspring_w, TRUE, FALSE,
6818 EL_SPRING_LEFT, -1, -1
6821 Xspring_fall, FALSE, FALSE,
6825 Yspring_s, FALSE, FALSE,
6826 EL_SPRING, ACTION_FALLING, -1
6829 Yspring_sB, FALSE, TRUE,
6830 EL_SPRING, ACTION_FALLING, -1
6833 Yspring_e, FALSE, FALSE,
6834 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6837 Yspring_eB, FALSE, TRUE,
6838 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6841 Yspring_w, FALSE, FALSE,
6842 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6845 Yspring_wB, FALSE, TRUE,
6846 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6849 Yspring_alien_e, FALSE, FALSE,
6850 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6853 Yspring_alien_eB, FALSE, TRUE,
6854 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6857 Yspring_alien_w, FALSE, FALSE,
6858 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6861 Yspring_alien_wB, FALSE, TRUE,
6862 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6866 Xpush_emerald_e, FALSE, FALSE,
6867 EL_EMERALD, -1, MV_BIT_RIGHT
6870 Xpush_emerald_w, FALSE, FALSE,
6871 EL_EMERALD, -1, MV_BIT_LEFT
6874 Xpush_diamond_e, FALSE, FALSE,
6875 EL_DIAMOND, -1, MV_BIT_RIGHT
6878 Xpush_diamond_w, FALSE, FALSE,
6879 EL_DIAMOND, -1, MV_BIT_LEFT
6882 Xpush_stone_e, FALSE, FALSE,
6883 EL_ROCK, -1, MV_BIT_RIGHT
6886 Xpush_stone_w, FALSE, FALSE,
6887 EL_ROCK, -1, MV_BIT_LEFT
6890 Xpush_bomb_e, FALSE, FALSE,
6891 EL_BOMB, -1, MV_BIT_RIGHT
6894 Xpush_bomb_w, FALSE, FALSE,
6895 EL_BOMB, -1, MV_BIT_LEFT
6898 Xpush_nut_e, FALSE, FALSE,
6899 EL_NUT, -1, MV_BIT_RIGHT
6902 Xpush_nut_w, FALSE, FALSE,
6903 EL_NUT, -1, MV_BIT_LEFT
6906 Xpush_spring_e, FALSE, FALSE,
6907 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6910 Xpush_spring_w, FALSE, FALSE,
6911 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6915 Xdynamite, TRUE, FALSE,
6916 EL_EM_DYNAMITE, -1, -1
6919 Ydynamite_blank, FALSE, FALSE,
6920 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6923 Xdynamite_1, TRUE, FALSE,
6924 EL_EM_DYNAMITE_ACTIVE, -1, -1
6927 Xdynamite_2, FALSE, FALSE,
6928 EL_EM_DYNAMITE_ACTIVE, -1, -1
6931 Xdynamite_3, FALSE, FALSE,
6932 EL_EM_DYNAMITE_ACTIVE, -1, -1
6935 Xdynamite_4, FALSE, FALSE,
6936 EL_EM_DYNAMITE_ACTIVE, -1, -1
6940 Xkey_1, TRUE, FALSE,
6944 Xkey_2, TRUE, FALSE,
6948 Xkey_3, TRUE, FALSE,
6952 Xkey_4, TRUE, FALSE,
6956 Xkey_5, TRUE, FALSE,
6957 EL_EMC_KEY_5, -1, -1
6960 Xkey_6, TRUE, FALSE,
6961 EL_EMC_KEY_6, -1, -1
6964 Xkey_7, TRUE, FALSE,
6965 EL_EMC_KEY_7, -1, -1
6968 Xkey_8, TRUE, FALSE,
6969 EL_EMC_KEY_8, -1, -1
6973 Xdoor_1, TRUE, FALSE,
6974 EL_EM_GATE_1, -1, -1
6977 Xdoor_2, TRUE, FALSE,
6978 EL_EM_GATE_2, -1, -1
6981 Xdoor_3, TRUE, FALSE,
6982 EL_EM_GATE_3, -1, -1
6985 Xdoor_4, TRUE, FALSE,
6986 EL_EM_GATE_4, -1, -1
6989 Xdoor_5, TRUE, FALSE,
6990 EL_EMC_GATE_5, -1, -1
6993 Xdoor_6, TRUE, FALSE,
6994 EL_EMC_GATE_6, -1, -1
6997 Xdoor_7, TRUE, FALSE,
6998 EL_EMC_GATE_7, -1, -1
7001 Xdoor_8, TRUE, FALSE,
7002 EL_EMC_GATE_8, -1, -1
7006 Xfake_door_1, TRUE, FALSE,
7007 EL_EM_GATE_1_GRAY, -1, -1
7010 Xfake_door_2, TRUE, FALSE,
7011 EL_EM_GATE_2_GRAY, -1, -1
7014 Xfake_door_3, TRUE, FALSE,
7015 EL_EM_GATE_3_GRAY, -1, -1
7018 Xfake_door_4, TRUE, FALSE,
7019 EL_EM_GATE_4_GRAY, -1, -1
7022 Xfake_door_5, TRUE, FALSE,
7023 EL_EMC_GATE_5_GRAY, -1, -1
7026 Xfake_door_6, TRUE, FALSE,
7027 EL_EMC_GATE_6_GRAY, -1, -1
7030 Xfake_door_7, TRUE, FALSE,
7031 EL_EMC_GATE_7_GRAY, -1, -1
7034 Xfake_door_8, TRUE, FALSE,
7035 EL_EMC_GATE_8_GRAY, -1, -1
7039 Xballoon, TRUE, FALSE,
7043 Yballoon_n, FALSE, FALSE,
7044 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7047 Yballoon_nB, FALSE, TRUE,
7048 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7051 Yballoon_e, FALSE, FALSE,
7052 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7055 Yballoon_eB, FALSE, TRUE,
7056 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7059 Yballoon_s, FALSE, FALSE,
7060 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7063 Yballoon_sB, FALSE, TRUE,
7064 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7067 Yballoon_w, FALSE, FALSE,
7068 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7071 Yballoon_wB, FALSE, TRUE,
7072 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7076 Xball_1, TRUE, FALSE,
7077 EL_EMC_MAGIC_BALL, -1, -1
7080 Yball_1, FALSE, FALSE,
7081 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7084 Xball_2, FALSE, FALSE,
7085 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7088 Yball_2, FALSE, FALSE,
7089 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7092 Yball_blank, FALSE, FALSE,
7093 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7097 Xamoeba_1, TRUE, FALSE,
7098 EL_AMOEBA_DRY, ACTION_OTHER, -1
7101 Xamoeba_2, FALSE, FALSE,
7102 EL_AMOEBA_DRY, ACTION_OTHER, -1
7105 Xamoeba_3, FALSE, FALSE,
7106 EL_AMOEBA_DRY, ACTION_OTHER, -1
7109 Xamoeba_4, FALSE, FALSE,
7110 EL_AMOEBA_DRY, ACTION_OTHER, -1
7113 Xamoeba_5, TRUE, FALSE,
7114 EL_AMOEBA_WET, ACTION_OTHER, -1
7117 Xamoeba_6, FALSE, FALSE,
7118 EL_AMOEBA_WET, ACTION_OTHER, -1
7121 Xamoeba_7, FALSE, FALSE,
7122 EL_AMOEBA_WET, ACTION_OTHER, -1
7125 Xamoeba_8, FALSE, FALSE,
7126 EL_AMOEBA_WET, ACTION_OTHER, -1
7131 EL_AMOEBA_DROP, ACTION_GROWING, -1
7134 Xdrip_fall, FALSE, FALSE,
7135 EL_AMOEBA_DROP, -1, -1
7138 Xdrip_stretch, FALSE, FALSE,
7139 EL_AMOEBA_DROP, ACTION_FALLING, -1
7142 Xdrip_stretchB, FALSE, TRUE,
7143 EL_AMOEBA_DROP, ACTION_FALLING, -1
7146 Ydrip_1_s, FALSE, FALSE,
7147 EL_AMOEBA_DROP, ACTION_FALLING, -1
7150 Ydrip_1_sB, FALSE, TRUE,
7151 EL_AMOEBA_DROP, ACTION_FALLING, -1
7154 Ydrip_2_s, FALSE, FALSE,
7155 EL_AMOEBA_DROP, ACTION_FALLING, -1
7158 Ydrip_2_sB, FALSE, TRUE,
7159 EL_AMOEBA_DROP, ACTION_FALLING, -1
7163 Xwonderwall, TRUE, FALSE,
7164 EL_MAGIC_WALL, -1, -1
7167 Ywonderwall, FALSE, FALSE,
7168 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7172 Xwheel, TRUE, FALSE,
7173 EL_ROBOT_WHEEL, -1, -1
7176 Ywheel, FALSE, FALSE,
7177 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7181 Xswitch, TRUE, FALSE,
7182 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7185 Yswitch, FALSE, FALSE,
7186 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7190 Xbumper, TRUE, FALSE,
7191 EL_EMC_SPRING_BUMPER, -1, -1
7194 Ybumper, FALSE, FALSE,
7195 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7199 Xacid_nw, TRUE, FALSE,
7200 EL_ACID_POOL_TOPLEFT, -1, -1
7203 Xacid_ne, TRUE, FALSE,
7204 EL_ACID_POOL_TOPRIGHT, -1, -1
7207 Xacid_sw, TRUE, FALSE,
7208 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7211 Xacid_s, TRUE, FALSE,
7212 EL_ACID_POOL_BOTTOM, -1, -1
7215 Xacid_se, TRUE, FALSE,
7216 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7220 Xfake_blank, TRUE, FALSE,
7221 EL_INVISIBLE_WALL, -1, -1
7224 Yfake_blank, FALSE, FALSE,
7225 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7229 Xfake_grass, TRUE, FALSE,
7230 EL_EMC_FAKE_GRASS, -1, -1
7233 Yfake_grass, FALSE, FALSE,
7234 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7238 Xfake_amoeba, TRUE, FALSE,
7239 EL_EMC_DRIPPER, -1, -1
7242 Yfake_amoeba, FALSE, FALSE,
7243 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7247 Xlenses, TRUE, FALSE,
7248 EL_EMC_LENSES, -1, -1
7252 Xmagnify, TRUE, FALSE,
7253 EL_EMC_MAGNIFIER, -1, -1
7258 EL_QUICKSAND_EMPTY, -1, -1
7261 Xsand_stone, TRUE, FALSE,
7262 EL_QUICKSAND_FULL, -1, -1
7265 Xsand_stonein_1, FALSE, TRUE,
7266 EL_ROCK, ACTION_FILLING, -1
7269 Xsand_stonein_2, FALSE, TRUE,
7270 EL_ROCK, ACTION_FILLING, -1
7273 Xsand_stonein_3, FALSE, TRUE,
7274 EL_ROCK, ACTION_FILLING, -1
7277 Xsand_stonein_4, FALSE, TRUE,
7278 EL_ROCK, ACTION_FILLING, -1
7281 Xsand_sandstone_1, FALSE, FALSE,
7282 EL_QUICKSAND_FILLING, -1, -1
7285 Xsand_sandstone_2, FALSE, FALSE,
7286 EL_QUICKSAND_FILLING, -1, -1
7289 Xsand_sandstone_3, FALSE, FALSE,
7290 EL_QUICKSAND_FILLING, -1, -1
7293 Xsand_sandstone_4, FALSE, FALSE,
7294 EL_QUICKSAND_FILLING, -1, -1
7297 Xsand_stonesand_1, FALSE, FALSE,
7298 EL_QUICKSAND_EMPTYING, -1, -1
7301 Xsand_stonesand_2, FALSE, FALSE,
7302 EL_QUICKSAND_EMPTYING, -1, -1
7305 Xsand_stonesand_3, FALSE, FALSE,
7306 EL_QUICKSAND_EMPTYING, -1, -1
7309 Xsand_stonesand_4, FALSE, FALSE,
7310 EL_QUICKSAND_EMPTYING, -1, -1
7313 Xsand_stoneout_1, FALSE, FALSE,
7314 EL_ROCK, ACTION_EMPTYING, -1
7317 Xsand_stoneout_2, FALSE, FALSE,
7318 EL_ROCK, ACTION_EMPTYING, -1
7321 Xsand_stonesand_quickout_1, FALSE, FALSE,
7322 EL_QUICKSAND_EMPTYING, -1, -1
7325 Xsand_stonesand_quickout_2, FALSE, FALSE,
7326 EL_QUICKSAND_EMPTYING, -1, -1
7330 Xslide_ns, TRUE, FALSE,
7331 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7334 Yslide_ns_blank, FALSE, FALSE,
7335 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7338 Xslide_ew, TRUE, FALSE,
7339 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7342 Yslide_ew_blank, FALSE, FALSE,
7343 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7347 Xwind_n, TRUE, FALSE,
7348 EL_BALLOON_SWITCH_UP, -1, -1
7351 Xwind_e, TRUE, FALSE,
7352 EL_BALLOON_SWITCH_RIGHT, -1, -1
7355 Xwind_s, TRUE, FALSE,
7356 EL_BALLOON_SWITCH_DOWN, -1, -1
7359 Xwind_w, TRUE, FALSE,
7360 EL_BALLOON_SWITCH_LEFT, -1, -1
7363 Xwind_any, TRUE, FALSE,
7364 EL_BALLOON_SWITCH_ANY, -1, -1
7367 Xwind_stop, TRUE, FALSE,
7368 EL_BALLOON_SWITCH_NONE, -1, -1
7373 EL_EM_EXIT_CLOSED, -1, -1
7376 Xexit_1, TRUE, FALSE,
7377 EL_EM_EXIT_OPEN, -1, -1
7380 Xexit_2, FALSE, FALSE,
7381 EL_EM_EXIT_OPEN, -1, -1
7384 Xexit_3, FALSE, FALSE,
7385 EL_EM_EXIT_OPEN, -1, -1
7389 Xpause, FALSE, FALSE,
7394 Xwall_1, TRUE, FALSE,
7398 Xwall_2, TRUE, FALSE,
7399 EL_EMC_WALL_14, -1, -1
7402 Xwall_3, TRUE, FALSE,
7403 EL_EMC_WALL_15, -1, -1
7406 Xwall_4, TRUE, FALSE,
7407 EL_EMC_WALL_16, -1, -1
7411 Xroundwall_1, TRUE, FALSE,
7412 EL_WALL_SLIPPERY, -1, -1
7415 Xroundwall_2, TRUE, FALSE,
7416 EL_EMC_WALL_SLIPPERY_2, -1, -1
7419 Xroundwall_3, TRUE, FALSE,
7420 EL_EMC_WALL_SLIPPERY_3, -1, -1
7423 Xroundwall_4, TRUE, FALSE,
7424 EL_EMC_WALL_SLIPPERY_4, -1, -1
7428 Xsteel_1, TRUE, FALSE,
7429 EL_STEELWALL, -1, -1
7432 Xsteel_2, TRUE, FALSE,
7433 EL_EMC_STEELWALL_2, -1, -1
7436 Xsteel_3, TRUE, FALSE,
7437 EL_EMC_STEELWALL_3, -1, -1
7440 Xsteel_4, TRUE, FALSE,
7441 EL_EMC_STEELWALL_4, -1, -1
7445 Xdecor_1, TRUE, FALSE,
7446 EL_EMC_WALL_8, -1, -1
7449 Xdecor_2, TRUE, FALSE,
7450 EL_EMC_WALL_6, -1, -1
7453 Xdecor_3, TRUE, FALSE,
7454 EL_EMC_WALL_4, -1, -1
7457 Xdecor_4, TRUE, FALSE,
7458 EL_EMC_WALL_7, -1, -1
7461 Xdecor_5, TRUE, FALSE,
7462 EL_EMC_WALL_5, -1, -1
7465 Xdecor_6, TRUE, FALSE,
7466 EL_EMC_WALL_9, -1, -1
7469 Xdecor_7, TRUE, FALSE,
7470 EL_EMC_WALL_10, -1, -1
7473 Xdecor_8, TRUE, FALSE,
7474 EL_EMC_WALL_1, -1, -1
7477 Xdecor_9, TRUE, FALSE,
7478 EL_EMC_WALL_2, -1, -1
7481 Xdecor_10, TRUE, FALSE,
7482 EL_EMC_WALL_3, -1, -1
7485 Xdecor_11, TRUE, FALSE,
7486 EL_EMC_WALL_11, -1, -1
7489 Xdecor_12, TRUE, FALSE,
7490 EL_EMC_WALL_12, -1, -1
7494 Xalpha_0, TRUE, FALSE,
7495 EL_CHAR('0'), -1, -1
7498 Xalpha_1, TRUE, FALSE,
7499 EL_CHAR('1'), -1, -1
7502 Xalpha_2, TRUE, FALSE,
7503 EL_CHAR('2'), -1, -1
7506 Xalpha_3, TRUE, FALSE,
7507 EL_CHAR('3'), -1, -1
7510 Xalpha_4, TRUE, FALSE,
7511 EL_CHAR('4'), -1, -1
7514 Xalpha_5, TRUE, FALSE,
7515 EL_CHAR('5'), -1, -1
7518 Xalpha_6, TRUE, FALSE,
7519 EL_CHAR('6'), -1, -1
7522 Xalpha_7, TRUE, FALSE,
7523 EL_CHAR('7'), -1, -1
7526 Xalpha_8, TRUE, FALSE,
7527 EL_CHAR('8'), -1, -1
7530 Xalpha_9, TRUE, FALSE,
7531 EL_CHAR('9'), -1, -1
7534 Xalpha_excla, TRUE, FALSE,
7535 EL_CHAR('!'), -1, -1
7538 Xalpha_apost, TRUE, FALSE,
7539 EL_CHAR('\''), -1, -1
7542 Xalpha_comma, TRUE, FALSE,
7543 EL_CHAR(','), -1, -1
7546 Xalpha_minus, TRUE, FALSE,
7547 EL_CHAR('-'), -1, -1
7550 Xalpha_perio, TRUE, FALSE,
7551 EL_CHAR('.'), -1, -1
7554 Xalpha_colon, TRUE, FALSE,
7555 EL_CHAR(':'), -1, -1
7558 Xalpha_quest, TRUE, FALSE,
7559 EL_CHAR('?'), -1, -1
7562 Xalpha_a, TRUE, FALSE,
7563 EL_CHAR('A'), -1, -1
7566 Xalpha_b, TRUE, FALSE,
7567 EL_CHAR('B'), -1, -1
7570 Xalpha_c, TRUE, FALSE,
7571 EL_CHAR('C'), -1, -1
7574 Xalpha_d, TRUE, FALSE,
7575 EL_CHAR('D'), -1, -1
7578 Xalpha_e, TRUE, FALSE,
7579 EL_CHAR('E'), -1, -1
7582 Xalpha_f, TRUE, FALSE,
7583 EL_CHAR('F'), -1, -1
7586 Xalpha_g, TRUE, FALSE,
7587 EL_CHAR('G'), -1, -1
7590 Xalpha_h, TRUE, FALSE,
7591 EL_CHAR('H'), -1, -1
7594 Xalpha_i, TRUE, FALSE,
7595 EL_CHAR('I'), -1, -1
7598 Xalpha_j, TRUE, FALSE,
7599 EL_CHAR('J'), -1, -1
7602 Xalpha_k, TRUE, FALSE,
7603 EL_CHAR('K'), -1, -1
7606 Xalpha_l, TRUE, FALSE,
7607 EL_CHAR('L'), -1, -1
7610 Xalpha_m, TRUE, FALSE,
7611 EL_CHAR('M'), -1, -1
7614 Xalpha_n, TRUE, FALSE,
7615 EL_CHAR('N'), -1, -1
7618 Xalpha_o, TRUE, FALSE,
7619 EL_CHAR('O'), -1, -1
7622 Xalpha_p, TRUE, FALSE,
7623 EL_CHAR('P'), -1, -1
7626 Xalpha_q, TRUE, FALSE,
7627 EL_CHAR('Q'), -1, -1
7630 Xalpha_r, TRUE, FALSE,
7631 EL_CHAR('R'), -1, -1
7634 Xalpha_s, TRUE, FALSE,
7635 EL_CHAR('S'), -1, -1
7638 Xalpha_t, TRUE, FALSE,
7639 EL_CHAR('T'), -1, -1
7642 Xalpha_u, TRUE, FALSE,
7643 EL_CHAR('U'), -1, -1
7646 Xalpha_v, TRUE, FALSE,
7647 EL_CHAR('V'), -1, -1
7650 Xalpha_w, TRUE, FALSE,
7651 EL_CHAR('W'), -1, -1
7654 Xalpha_x, TRUE, FALSE,
7655 EL_CHAR('X'), -1, -1
7658 Xalpha_y, TRUE, FALSE,
7659 EL_CHAR('Y'), -1, -1
7662 Xalpha_z, TRUE, FALSE,
7663 EL_CHAR('Z'), -1, -1
7666 Xalpha_arrow_e, TRUE, FALSE,
7667 EL_CHAR('>'), -1, -1
7670 Xalpha_arrow_w, TRUE, FALSE,
7671 EL_CHAR('<'), -1, -1
7674 Xalpha_copyr, TRUE, FALSE,
7675 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7679 Ykey_1_blank, FALSE, FALSE,
7680 EL_EM_KEY_1, ACTION_COLLECTING, -1
7683 Ykey_2_blank, FALSE, FALSE,
7684 EL_EM_KEY_2, ACTION_COLLECTING, -1
7687 Ykey_3_blank, FALSE, FALSE,
7688 EL_EM_KEY_3, ACTION_COLLECTING, -1
7691 Ykey_4_blank, FALSE, FALSE,
7692 EL_EM_KEY_4, ACTION_COLLECTING, -1
7695 Ykey_5_blank, FALSE, FALSE,
7696 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7699 Ykey_6_blank, FALSE, FALSE,
7700 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7703 Ykey_7_blank, FALSE, FALSE,
7704 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7707 Ykey_8_blank, FALSE, FALSE,
7708 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7711 Ylenses_blank, FALSE, FALSE,
7712 EL_EMC_LENSES, ACTION_COLLECTING, -1
7715 Ymagnify_blank, FALSE, FALSE,
7716 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7719 Ygrass_blank, FALSE, FALSE,
7720 EL_EMC_GRASS, ACTION_SNAPPING, -1
7723 Ydirt_blank, FALSE, FALSE,
7724 EL_SAND, ACTION_SNAPPING, -1
7733 static struct Mapping_EM_to_RND_player
7742 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7746 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7750 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7754 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7758 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7762 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7766 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7770 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7774 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7778 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7782 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7786 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7790 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7794 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7798 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7802 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7806 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7810 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7814 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7818 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7822 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7826 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7830 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7834 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7838 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7842 EL_PLAYER_1, ACTION_DEFAULT, -1,
7846 EL_PLAYER_2, ACTION_DEFAULT, -1,
7850 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7854 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7858 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7862 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7866 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7870 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7874 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7878 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7882 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7886 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7890 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7894 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7898 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7902 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7906 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7910 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7914 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7918 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7922 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7926 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7930 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7934 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7938 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7942 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7946 EL_PLAYER_3, ACTION_DEFAULT, -1,
7950 EL_PLAYER_4, ACTION_DEFAULT, -1,
7959 int map_element_RND_to_EM_cave(int element_rnd)
7961 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7962 static boolean mapping_initialized = FALSE;
7964 if (!mapping_initialized)
7968 // return "Xalpha_quest" for all undefined elements in mapping array
7969 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7970 mapping_RND_to_EM[i] = Xalpha_quest;
7972 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7973 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7974 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7975 em_object_mapping_list[i].element_em;
7977 mapping_initialized = TRUE;
7980 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7982 Warn("invalid RND level element %d", element_rnd);
7987 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7990 int map_element_EM_to_RND_cave(int element_em_cave)
7992 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7993 static boolean mapping_initialized = FALSE;
7995 if (!mapping_initialized)
7999 // return "EL_UNKNOWN" for all undefined elements in mapping array
8000 for (i = 0; i < GAME_TILE_MAX; i++)
8001 mapping_EM_to_RND[i] = EL_UNKNOWN;
8003 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8004 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8005 em_object_mapping_list[i].element_rnd;
8007 mapping_initialized = TRUE;
8010 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8012 Warn("invalid EM cave element %d", element_em_cave);
8017 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8020 int map_element_EM_to_RND_game(int element_em_game)
8022 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8023 static boolean mapping_initialized = FALSE;
8025 if (!mapping_initialized)
8029 // return "EL_UNKNOWN" for all undefined elements in mapping array
8030 for (i = 0; i < GAME_TILE_MAX; i++)
8031 mapping_EM_to_RND[i] = EL_UNKNOWN;
8033 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8034 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8035 em_object_mapping_list[i].element_rnd;
8037 mapping_initialized = TRUE;
8040 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8042 Warn("invalid EM game element %d", element_em_game);
8047 return mapping_EM_to_RND[element_em_game];
8050 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8052 struct LevelInfo_EM *level_em = level->native_em_level;
8053 struct CAVE *cav = level_em->cav;
8056 for (i = 0; i < GAME_TILE_MAX; i++)
8057 cav->android_array[i] = Cblank;
8059 for (i = 0; i < level->num_android_clone_elements; i++)
8061 int element_rnd = level->android_clone_element[i];
8062 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8064 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8065 if (em_object_mapping_list[j].element_rnd == element_rnd)
8066 cav->android_array[em_object_mapping_list[j].element_em] =
8071 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8073 struct LevelInfo_EM *level_em = level->native_em_level;
8074 struct CAVE *cav = level_em->cav;
8077 level->num_android_clone_elements = 0;
8079 for (i = 0; i < GAME_TILE_MAX; i++)
8081 int element_em_cave = cav->android_array[i];
8083 boolean element_found = FALSE;
8085 if (element_em_cave == Cblank)
8088 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8090 for (j = 0; j < level->num_android_clone_elements; j++)
8091 if (level->android_clone_element[j] == element_rnd)
8092 element_found = TRUE;
8096 level->android_clone_element[level->num_android_clone_elements++] =
8099 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8104 if (level->num_android_clone_elements == 0)
8106 level->num_android_clone_elements = 1;
8107 level->android_clone_element[0] = EL_EMPTY;
8111 int map_direction_RND_to_EM(int direction)
8113 return (direction == MV_UP ? 0 :
8114 direction == MV_RIGHT ? 1 :
8115 direction == MV_DOWN ? 2 :
8116 direction == MV_LEFT ? 3 :
8120 int map_direction_EM_to_RND(int direction)
8122 return (direction == 0 ? MV_UP :
8123 direction == 1 ? MV_RIGHT :
8124 direction == 2 ? MV_DOWN :
8125 direction == 3 ? MV_LEFT :
8129 int map_element_RND_to_SP(int element_rnd)
8131 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8133 if (element_rnd >= EL_SP_START &&
8134 element_rnd <= EL_SP_END)
8135 element_sp = element_rnd - EL_SP_START;
8136 else if (element_rnd == EL_EMPTY_SPACE)
8138 else if (element_rnd == EL_INVISIBLE_WALL)
8144 int map_element_SP_to_RND(int element_sp)
8146 int element_rnd = EL_UNKNOWN;
8148 if (element_sp >= 0x00 &&
8150 element_rnd = EL_SP_START + element_sp;
8151 else if (element_sp == 0x28)
8152 element_rnd = EL_INVISIBLE_WALL;
8157 int map_action_SP_to_RND(int action_sp)
8161 case actActive: return ACTION_ACTIVE;
8162 case actImpact: return ACTION_IMPACT;
8163 case actExploding: return ACTION_EXPLODING;
8164 case actDigging: return ACTION_DIGGING;
8165 case actSnapping: return ACTION_SNAPPING;
8166 case actCollecting: return ACTION_COLLECTING;
8167 case actPassing: return ACTION_PASSING;
8168 case actPushing: return ACTION_PUSHING;
8169 case actDropping: return ACTION_DROPPING;
8171 default: return ACTION_DEFAULT;
8175 int map_element_RND_to_MM(int element_rnd)
8177 return (element_rnd >= EL_MM_START_1 &&
8178 element_rnd <= EL_MM_END_1 ?
8179 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8181 element_rnd >= EL_MM_START_2 &&
8182 element_rnd <= EL_MM_END_2 ?
8183 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8185 element_rnd >= EL_CHAR_START &&
8186 element_rnd <= EL_CHAR_END ?
8187 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8189 element_rnd >= EL_MM_RUNTIME_START &&
8190 element_rnd <= EL_MM_RUNTIME_END ?
8191 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8193 element_rnd >= EL_MM_DUMMY_START &&
8194 element_rnd <= EL_MM_DUMMY_END ?
8195 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8197 EL_MM_EMPTY_NATIVE);
8200 int map_element_MM_to_RND(int element_mm)
8202 return (element_mm == EL_MM_EMPTY_NATIVE ||
8203 element_mm == EL_DF_EMPTY_NATIVE ?
8206 element_mm >= EL_MM_START_1_NATIVE &&
8207 element_mm <= EL_MM_END_1_NATIVE ?
8208 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8210 element_mm >= EL_MM_START_2_NATIVE &&
8211 element_mm <= EL_MM_END_2_NATIVE ?
8212 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8214 element_mm >= EL_MM_CHAR_START_NATIVE &&
8215 element_mm <= EL_MM_CHAR_END_NATIVE ?
8216 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8218 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8219 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8220 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8222 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8223 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8224 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8229 int map_action_MM_to_RND(int action_mm)
8231 // all MM actions are defined to exactly match their RND counterparts
8235 int map_sound_MM_to_RND(int sound_mm)
8239 case SND_MM_GAME_LEVELTIME_CHARGING:
8240 return SND_GAME_LEVELTIME_CHARGING;
8242 case SND_MM_GAME_HEALTH_CHARGING:
8243 return SND_GAME_HEALTH_CHARGING;
8246 return SND_UNDEFINED;
8250 int map_mm_wall_element(int element)
8252 return (element >= EL_MM_STEEL_WALL_START &&
8253 element <= EL_MM_STEEL_WALL_END ?
8256 element >= EL_MM_WOODEN_WALL_START &&
8257 element <= EL_MM_WOODEN_WALL_END ?
8260 element >= EL_MM_ICE_WALL_START &&
8261 element <= EL_MM_ICE_WALL_END ?
8264 element >= EL_MM_AMOEBA_WALL_START &&
8265 element <= EL_MM_AMOEBA_WALL_END ?
8268 element >= EL_DF_STEEL_WALL_START &&
8269 element <= EL_DF_STEEL_WALL_END ?
8272 element >= EL_DF_WOODEN_WALL_START &&
8273 element <= EL_DF_WOODEN_WALL_END ?
8279 int map_mm_wall_element_editor(int element)
8283 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8284 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8285 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8286 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8287 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8288 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8290 default: return element;
8294 int get_next_element(int element)
8298 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8299 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8300 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8301 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8302 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8303 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8304 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8305 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8306 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8307 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8308 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8310 default: return element;
8314 int el2img_mm(int element_mm)
8316 return el2img(map_element_MM_to_RND(element_mm));
8319 int el_act_dir2img(int element, int action, int direction)
8321 element = GFX_ELEMENT(element);
8322 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8324 // direction_graphic[][] == graphic[] for undefined direction graphics
8325 return element_info[element].direction_graphic[action][direction];
8328 static int el_act_dir2crm(int element, int action, int direction)
8330 element = GFX_ELEMENT(element);
8331 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8333 // direction_graphic[][] == graphic[] for undefined direction graphics
8334 return element_info[element].direction_crumbled[action][direction];
8337 int el_act2img(int element, int action)
8339 element = GFX_ELEMENT(element);
8341 return element_info[element].graphic[action];
8344 int el_act2crm(int element, int action)
8346 element = GFX_ELEMENT(element);
8348 return element_info[element].crumbled[action];
8351 int el_dir2img(int element, int direction)
8353 element = GFX_ELEMENT(element);
8355 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8358 int el2baseimg(int element)
8360 return element_info[element].graphic[ACTION_DEFAULT];
8363 int el2img(int element)
8365 element = GFX_ELEMENT(element);
8367 return element_info[element].graphic[ACTION_DEFAULT];
8370 int el2edimg(int element)
8372 element = GFX_ELEMENT(element);
8374 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8377 int el2preimg(int element)
8379 element = GFX_ELEMENT(element);
8381 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8384 int el2panelimg(int element)
8386 element = GFX_ELEMENT(element);
8388 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8391 int font2baseimg(int font_nr)
8393 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8396 int getBeltNrFromBeltElement(int element)
8398 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8399 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8400 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8403 int getBeltNrFromBeltActiveElement(int element)
8405 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8406 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8407 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8410 int getBeltNrFromBeltSwitchElement(int element)
8412 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8413 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8414 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8417 int getBeltDirNrFromBeltElement(int element)
8419 static int belt_base_element[4] =
8421 EL_CONVEYOR_BELT_1_LEFT,
8422 EL_CONVEYOR_BELT_2_LEFT,
8423 EL_CONVEYOR_BELT_3_LEFT,
8424 EL_CONVEYOR_BELT_4_LEFT
8427 int belt_nr = getBeltNrFromBeltElement(element);
8428 int belt_dir_nr = element - belt_base_element[belt_nr];
8430 return (belt_dir_nr % 3);
8433 int getBeltDirNrFromBeltSwitchElement(int element)
8435 static int belt_base_element[4] =
8437 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8438 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8439 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8440 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8443 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8444 int belt_dir_nr = element - belt_base_element[belt_nr];
8446 return (belt_dir_nr % 3);
8449 int getBeltDirFromBeltElement(int element)
8451 static int belt_move_dir[3] =
8458 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8460 return belt_move_dir[belt_dir_nr];
8463 int getBeltDirFromBeltSwitchElement(int element)
8465 static int belt_move_dir[3] =
8472 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8474 return belt_move_dir[belt_dir_nr];
8477 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8479 static int belt_base_element[4] =
8481 EL_CONVEYOR_BELT_1_LEFT,
8482 EL_CONVEYOR_BELT_2_LEFT,
8483 EL_CONVEYOR_BELT_3_LEFT,
8484 EL_CONVEYOR_BELT_4_LEFT
8487 return belt_base_element[belt_nr] + belt_dir_nr;
8490 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8492 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8494 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8497 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8499 static int belt_base_element[4] =
8501 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8502 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8503 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8504 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8507 return belt_base_element[belt_nr] + belt_dir_nr;
8510 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8512 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8514 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8517 boolean swapTiles_EM(boolean is_pre_emc_cave)
8519 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8522 boolean getTeamMode_EM(void)
8524 return game.team_mode || network_playing;
8527 boolean isActivePlayer_EM(int player_nr)
8529 return stored_player[player_nr].active;
8532 unsigned int InitRND(int seed)
8534 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8535 return InitEngineRandom_EM(seed);
8536 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8537 return InitEngineRandom_SP(seed);
8538 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8539 return InitEngineRandom_MM(seed);
8541 return InitEngineRandom_RND(seed);
8544 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8545 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8547 static int get_effective_element_EM(int tile, int frame_em)
8549 int element = object_mapping[tile].element_rnd;
8550 int action = object_mapping[tile].action;
8551 boolean is_backside = object_mapping[tile].is_backside;
8552 boolean action_removing = (action == ACTION_DIGGING ||
8553 action == ACTION_SNAPPING ||
8554 action == ACTION_COLLECTING);
8562 return (frame_em > 5 ? EL_EMPTY : element);
8568 else // frame_em == 7
8579 case Ydiamond_stone:
8583 case Xdrip_stretchB:
8599 case Ymagnify_blank:
8602 case Xsand_stonein_1:
8603 case Xsand_stonein_2:
8604 case Xsand_stonein_3:
8605 case Xsand_stonein_4:
8609 return (is_backside || action_removing ? EL_EMPTY : element);
8614 static boolean check_linear_animation_EM(int tile)
8618 case Xsand_stonesand_1:
8619 case Xsand_stonesand_quickout_1:
8620 case Xsand_sandstone_1:
8621 case Xsand_stonein_1:
8622 case Xsand_stoneout_1:
8650 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8651 boolean has_crumbled_graphics,
8652 int crumbled, int sync_frame)
8654 // if element can be crumbled, but certain action graphics are just empty
8655 // space (like instantly snapping sand to empty space in 1 frame), do not
8656 // treat these empty space graphics as crumbled graphics in EMC engine
8657 if (crumbled == IMG_EMPTY_SPACE)
8658 has_crumbled_graphics = FALSE;
8660 if (has_crumbled_graphics)
8662 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8663 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8664 g_crumbled->anim_delay,
8665 g_crumbled->anim_mode,
8666 g_crumbled->anim_start_frame,
8669 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8670 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8672 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8673 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8675 g_em->has_crumbled_graphics = TRUE;
8679 g_em->crumbled_bitmap = NULL;
8680 g_em->crumbled_src_x = 0;
8681 g_em->crumbled_src_y = 0;
8682 g_em->crumbled_border_size = 0;
8683 g_em->crumbled_tile_size = 0;
8685 g_em->has_crumbled_graphics = FALSE;
8690 void ResetGfxAnimation_EM(int x, int y, int tile)
8696 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8697 int tile, int frame_em, int x, int y)
8699 int action = object_mapping[tile].action;
8700 int direction = object_mapping[tile].direction;
8701 int effective_element = get_effective_element_EM(tile, frame_em);
8702 int graphic = (direction == MV_NONE ?
8703 el_act2img(effective_element, action) :
8704 el_act_dir2img(effective_element, action, direction));
8705 struct GraphicInfo *g = &graphic_info[graphic];
8707 boolean action_removing = (action == ACTION_DIGGING ||
8708 action == ACTION_SNAPPING ||
8709 action == ACTION_COLLECTING);
8710 boolean action_moving = (action == ACTION_FALLING ||
8711 action == ACTION_MOVING ||
8712 action == ACTION_PUSHING ||
8713 action == ACTION_EATING ||
8714 action == ACTION_FILLING ||
8715 action == ACTION_EMPTYING);
8716 boolean action_falling = (action == ACTION_FALLING ||
8717 action == ACTION_FILLING ||
8718 action == ACTION_EMPTYING);
8720 // special case: graphic uses "2nd movement tile" and has defined
8721 // 7 frames for movement animation (or less) => use default graphic
8722 // for last (8th) frame which ends the movement animation
8723 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8725 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8726 graphic = (direction == MV_NONE ?
8727 el_act2img(effective_element, action) :
8728 el_act_dir2img(effective_element, action, direction));
8730 g = &graphic_info[graphic];
8733 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8737 else if (action_moving)
8739 boolean is_backside = object_mapping[tile].is_backside;
8743 int direction = object_mapping[tile].direction;
8744 int move_dir = (action_falling ? MV_DOWN : direction);
8749 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8750 if (g->double_movement && frame_em == 0)
8754 if (move_dir == MV_LEFT)
8755 GfxFrame[x - 1][y] = GfxFrame[x][y];
8756 else if (move_dir == MV_RIGHT)
8757 GfxFrame[x + 1][y] = GfxFrame[x][y];
8758 else if (move_dir == MV_UP)
8759 GfxFrame[x][y - 1] = GfxFrame[x][y];
8760 else if (move_dir == MV_DOWN)
8761 GfxFrame[x][y + 1] = GfxFrame[x][y];
8768 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8769 if (tile == Xsand_stonesand_quickout_1 ||
8770 tile == Xsand_stonesand_quickout_2)
8774 if (graphic_info[graphic].anim_global_sync)
8775 sync_frame = FrameCounter;
8776 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8777 sync_frame = GfxFrame[x][y];
8779 sync_frame = 0; // playfield border (pseudo steel)
8781 SetRandomAnimationValue(x, y);
8783 int frame = getAnimationFrame(g->anim_frames,
8786 g->anim_start_frame,
8789 g_em->unique_identifier =
8790 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8793 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8794 int tile, int frame_em, int x, int y)
8796 int action = object_mapping[tile].action;
8797 int direction = object_mapping[tile].direction;
8798 boolean is_backside = object_mapping[tile].is_backside;
8799 int effective_element = get_effective_element_EM(tile, frame_em);
8800 int effective_action = action;
8801 int graphic = (direction == MV_NONE ?
8802 el_act2img(effective_element, effective_action) :
8803 el_act_dir2img(effective_element, effective_action,
8805 int crumbled = (direction == MV_NONE ?
8806 el_act2crm(effective_element, effective_action) :
8807 el_act_dir2crm(effective_element, effective_action,
8809 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8810 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8811 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8812 struct GraphicInfo *g = &graphic_info[graphic];
8815 // special case: graphic uses "2nd movement tile" and has defined
8816 // 7 frames for movement animation (or less) => use default graphic
8817 // for last (8th) frame which ends the movement animation
8818 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8820 effective_action = ACTION_DEFAULT;
8821 graphic = (direction == MV_NONE ?
8822 el_act2img(effective_element, effective_action) :
8823 el_act_dir2img(effective_element, effective_action,
8825 crumbled = (direction == MV_NONE ?
8826 el_act2crm(effective_element, effective_action) :
8827 el_act_dir2crm(effective_element, effective_action,
8830 g = &graphic_info[graphic];
8833 if (graphic_info[graphic].anim_global_sync)
8834 sync_frame = FrameCounter;
8835 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8836 sync_frame = GfxFrame[x][y];
8838 sync_frame = 0; // playfield border (pseudo steel)
8840 SetRandomAnimationValue(x, y);
8842 int frame = getAnimationFrame(g->anim_frames,
8845 g->anim_start_frame,
8848 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8849 g->double_movement && is_backside);
8851 // (updating the "crumbled" graphic definitions is probably not really needed,
8852 // as animations for crumbled graphics can't be longer than one EMC cycle)
8853 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8857 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8858 int player_nr, int anim, int frame_em)
8860 int element = player_mapping[player_nr][anim].element_rnd;
8861 int action = player_mapping[player_nr][anim].action;
8862 int direction = player_mapping[player_nr][anim].direction;
8863 int graphic = (direction == MV_NONE ?
8864 el_act2img(element, action) :
8865 el_act_dir2img(element, action, direction));
8866 struct GraphicInfo *g = &graphic_info[graphic];
8869 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8871 stored_player[player_nr].StepFrame = frame_em;
8873 sync_frame = stored_player[player_nr].Frame;
8875 int frame = getAnimationFrame(g->anim_frames,
8878 g->anim_start_frame,
8881 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8882 &g_em->src_x, &g_em->src_y, FALSE);
8885 void InitGraphicInfo_EM(void)
8889 // always start with reliable default values
8890 for (i = 0; i < GAME_TILE_MAX; i++)
8892 object_mapping[i].element_rnd = EL_UNKNOWN;
8893 object_mapping[i].is_backside = FALSE;
8894 object_mapping[i].action = ACTION_DEFAULT;
8895 object_mapping[i].direction = MV_NONE;
8898 // always start with reliable default values
8899 for (p = 0; p < MAX_PLAYERS; p++)
8901 for (i = 0; i < PLY_MAX; i++)
8903 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8904 player_mapping[p][i].action = ACTION_DEFAULT;
8905 player_mapping[p][i].direction = MV_NONE;
8909 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8911 int e = em_object_mapping_list[i].element_em;
8913 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8914 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8916 if (em_object_mapping_list[i].action != -1)
8917 object_mapping[e].action = em_object_mapping_list[i].action;
8919 if (em_object_mapping_list[i].direction != -1)
8920 object_mapping[e].direction =
8921 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8924 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8926 int a = em_player_mapping_list[i].action_em;
8927 int p = em_player_mapping_list[i].player_nr;
8929 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8931 if (em_player_mapping_list[i].action != -1)
8932 player_mapping[p][a].action = em_player_mapping_list[i].action;
8934 if (em_player_mapping_list[i].direction != -1)
8935 player_mapping[p][a].direction =
8936 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8939 for (i = 0; i < GAME_TILE_MAX; i++)
8941 int element = object_mapping[i].element_rnd;
8942 int action = object_mapping[i].action;
8943 int direction = object_mapping[i].direction;
8944 boolean is_backside = object_mapping[i].is_backside;
8945 boolean action_exploding = ((action == ACTION_EXPLODING ||
8946 action == ACTION_SMASHED_BY_ROCK ||
8947 action == ACTION_SMASHED_BY_SPRING) &&
8948 element != EL_DIAMOND);
8949 boolean action_active = (action == ACTION_ACTIVE);
8950 boolean action_other = (action == ACTION_OTHER);
8952 for (j = 0; j < 8; j++)
8954 int effective_element = get_effective_element_EM(i, j);
8955 int effective_action = (j < 7 ? action :
8956 i == Xdrip_stretch ? action :
8957 i == Xdrip_stretchB ? action :
8958 i == Ydrip_1_s ? action :
8959 i == Ydrip_1_sB ? action :
8960 i == Yball_1 ? action :
8961 i == Xball_2 ? action :
8962 i == Yball_2 ? action :
8963 i == Yball_blank ? action :
8964 i == Ykey_1_blank ? action :
8965 i == Ykey_2_blank ? action :
8966 i == Ykey_3_blank ? action :
8967 i == Ykey_4_blank ? action :
8968 i == Ykey_5_blank ? action :
8969 i == Ykey_6_blank ? action :
8970 i == Ykey_7_blank ? action :
8971 i == Ykey_8_blank ? action :
8972 i == Ylenses_blank ? action :
8973 i == Ymagnify_blank ? action :
8974 i == Ygrass_blank ? action :
8975 i == Ydirt_blank ? action :
8976 i == Xsand_stonein_1 ? action :
8977 i == Xsand_stonein_2 ? action :
8978 i == Xsand_stonein_3 ? action :
8979 i == Xsand_stonein_4 ? action :
8980 i == Xsand_stoneout_1 ? action :
8981 i == Xsand_stoneout_2 ? action :
8982 i == Xboom_android ? ACTION_EXPLODING :
8983 action_exploding ? ACTION_EXPLODING :
8984 action_active ? action :
8985 action_other ? action :
8987 int graphic = (el_act_dir2img(effective_element, effective_action,
8989 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8991 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8992 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8993 boolean has_action_graphics = (graphic != base_graphic);
8994 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8995 struct GraphicInfo *g = &graphic_info[graphic];
8996 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8999 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9000 boolean special_animation = (action != ACTION_DEFAULT &&
9001 g->anim_frames == 3 &&
9002 g->anim_delay == 2 &&
9003 g->anim_mode & ANIM_LINEAR);
9004 int sync_frame = (i == Xdrip_stretch ? 7 :
9005 i == Xdrip_stretchB ? 7 :
9006 i == Ydrip_2_s ? j + 8 :
9007 i == Ydrip_2_sB ? j + 8 :
9016 i == Xfake_acid_1 ? 0 :
9017 i == Xfake_acid_2 ? 10 :
9018 i == Xfake_acid_3 ? 20 :
9019 i == Xfake_acid_4 ? 30 :
9020 i == Xfake_acid_5 ? 40 :
9021 i == Xfake_acid_6 ? 50 :
9022 i == Xfake_acid_7 ? 60 :
9023 i == Xfake_acid_8 ? 70 :
9024 i == Xfake_acid_1_player ? 0 :
9025 i == Xfake_acid_2_player ? 10 :
9026 i == Xfake_acid_3_player ? 20 :
9027 i == Xfake_acid_4_player ? 30 :
9028 i == Xfake_acid_5_player ? 40 :
9029 i == Xfake_acid_6_player ? 50 :
9030 i == Xfake_acid_7_player ? 60 :
9031 i == Xfake_acid_8_player ? 70 :
9033 i == Yball_2 ? j + 8 :
9034 i == Yball_blank ? j + 1 :
9035 i == Ykey_1_blank ? j + 1 :
9036 i == Ykey_2_blank ? j + 1 :
9037 i == Ykey_3_blank ? j + 1 :
9038 i == Ykey_4_blank ? j + 1 :
9039 i == Ykey_5_blank ? j + 1 :
9040 i == Ykey_6_blank ? j + 1 :
9041 i == Ykey_7_blank ? j + 1 :
9042 i == Ykey_8_blank ? j + 1 :
9043 i == Ylenses_blank ? j + 1 :
9044 i == Ymagnify_blank ? j + 1 :
9045 i == Ygrass_blank ? j + 1 :
9046 i == Ydirt_blank ? j + 1 :
9047 i == Xamoeba_1 ? 0 :
9048 i == Xamoeba_2 ? 1 :
9049 i == Xamoeba_3 ? 2 :
9050 i == Xamoeba_4 ? 3 :
9051 i == Xamoeba_5 ? 0 :
9052 i == Xamoeba_6 ? 1 :
9053 i == Xamoeba_7 ? 2 :
9054 i == Xamoeba_8 ? 3 :
9055 i == Xexit_2 ? j + 8 :
9056 i == Xexit_3 ? j + 16 :
9057 i == Xdynamite_1 ? 0 :
9058 i == Xdynamite_2 ? 8 :
9059 i == Xdynamite_3 ? 16 :
9060 i == Xdynamite_4 ? 24 :
9061 i == Xsand_stonein_1 ? j + 1 :
9062 i == Xsand_stonein_2 ? j + 9 :
9063 i == Xsand_stonein_3 ? j + 17 :
9064 i == Xsand_stonein_4 ? j + 25 :
9065 i == Xsand_stoneout_1 && j == 0 ? 0 :
9066 i == Xsand_stoneout_1 && j == 1 ? 0 :
9067 i == Xsand_stoneout_1 && j == 2 ? 1 :
9068 i == Xsand_stoneout_1 && j == 3 ? 2 :
9069 i == Xsand_stoneout_1 && j == 4 ? 2 :
9070 i == Xsand_stoneout_1 && j == 5 ? 3 :
9071 i == Xsand_stoneout_1 && j == 6 ? 4 :
9072 i == Xsand_stoneout_1 && j == 7 ? 4 :
9073 i == Xsand_stoneout_2 && j == 0 ? 5 :
9074 i == Xsand_stoneout_2 && j == 1 ? 6 :
9075 i == Xsand_stoneout_2 && j == 2 ? 7 :
9076 i == Xsand_stoneout_2 && j == 3 ? 8 :
9077 i == Xsand_stoneout_2 && j == 4 ? 9 :
9078 i == Xsand_stoneout_2 && j == 5 ? 11 :
9079 i == Xsand_stoneout_2 && j == 6 ? 13 :
9080 i == Xsand_stoneout_2 && j == 7 ? 15 :
9081 i == Xboom_bug && j == 1 ? 2 :
9082 i == Xboom_bug && j == 2 ? 2 :
9083 i == Xboom_bug && j == 3 ? 4 :
9084 i == Xboom_bug && j == 4 ? 4 :
9085 i == Xboom_bug && j == 5 ? 2 :
9086 i == Xboom_bug && j == 6 ? 2 :
9087 i == Xboom_bug && j == 7 ? 0 :
9088 i == Xboom_tank && j == 1 ? 2 :
9089 i == Xboom_tank && j == 2 ? 2 :
9090 i == Xboom_tank && j == 3 ? 4 :
9091 i == Xboom_tank && j == 4 ? 4 :
9092 i == Xboom_tank && j == 5 ? 2 :
9093 i == Xboom_tank && j == 6 ? 2 :
9094 i == Xboom_tank && j == 7 ? 0 :
9095 i == Xboom_android && j == 7 ? 6 :
9096 i == Xboom_1 && j == 1 ? 2 :
9097 i == Xboom_1 && j == 2 ? 2 :
9098 i == Xboom_1 && j == 3 ? 4 :
9099 i == Xboom_1 && j == 4 ? 4 :
9100 i == Xboom_1 && j == 5 ? 6 :
9101 i == Xboom_1 && j == 6 ? 6 :
9102 i == Xboom_1 && j == 7 ? 8 :
9103 i == Xboom_2 && j == 0 ? 8 :
9104 i == Xboom_2 && j == 1 ? 8 :
9105 i == Xboom_2 && j == 2 ? 10 :
9106 i == Xboom_2 && j == 3 ? 10 :
9107 i == Xboom_2 && j == 4 ? 10 :
9108 i == Xboom_2 && j == 5 ? 12 :
9109 i == Xboom_2 && j == 6 ? 12 :
9110 i == Xboom_2 && j == 7 ? 12 :
9111 special_animation && j == 4 ? 3 :
9112 effective_action != action ? 0 :
9114 int frame = getAnimationFrame(g->anim_frames,
9117 g->anim_start_frame,
9120 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9121 g->double_movement && is_backside);
9123 g_em->bitmap = src_bitmap;
9124 g_em->src_x = src_x;
9125 g_em->src_y = src_y;
9126 g_em->src_offset_x = 0;
9127 g_em->src_offset_y = 0;
9128 g_em->dst_offset_x = 0;
9129 g_em->dst_offset_y = 0;
9130 g_em->width = TILEX;
9131 g_em->height = TILEY;
9133 g_em->preserve_background = FALSE;
9135 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9138 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9139 effective_action == ACTION_MOVING ||
9140 effective_action == ACTION_PUSHING ||
9141 effective_action == ACTION_EATING)) ||
9142 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9143 effective_action == ACTION_EMPTYING)))
9146 (effective_action == ACTION_FALLING ||
9147 effective_action == ACTION_FILLING ||
9148 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9149 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9150 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9151 int num_steps = (i == Ydrip_1_s ? 16 :
9152 i == Ydrip_1_sB ? 16 :
9153 i == Ydrip_2_s ? 16 :
9154 i == Ydrip_2_sB ? 16 :
9155 i == Xsand_stonein_1 ? 32 :
9156 i == Xsand_stonein_2 ? 32 :
9157 i == Xsand_stonein_3 ? 32 :
9158 i == Xsand_stonein_4 ? 32 :
9159 i == Xsand_stoneout_1 ? 16 :
9160 i == Xsand_stoneout_2 ? 16 : 8);
9161 int cx = ABS(dx) * (TILEX / num_steps);
9162 int cy = ABS(dy) * (TILEY / num_steps);
9163 int step_frame = (i == Ydrip_2_s ? j + 8 :
9164 i == Ydrip_2_sB ? j + 8 :
9165 i == Xsand_stonein_2 ? j + 8 :
9166 i == Xsand_stonein_3 ? j + 16 :
9167 i == Xsand_stonein_4 ? j + 24 :
9168 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9169 int step = (is_backside ? step_frame : num_steps - step_frame);
9171 if (is_backside) // tile where movement starts
9173 if (dx < 0 || dy < 0)
9175 g_em->src_offset_x = cx * step;
9176 g_em->src_offset_y = cy * step;
9180 g_em->dst_offset_x = cx * step;
9181 g_em->dst_offset_y = cy * step;
9184 else // tile where movement ends
9186 if (dx < 0 || dy < 0)
9188 g_em->dst_offset_x = cx * step;
9189 g_em->dst_offset_y = cy * step;
9193 g_em->src_offset_x = cx * step;
9194 g_em->src_offset_y = cy * step;
9198 g_em->width = TILEX - cx * step;
9199 g_em->height = TILEY - cy * step;
9202 // create unique graphic identifier to decide if tile must be redrawn
9203 /* bit 31 - 16 (16 bit): EM style graphic
9204 bit 15 - 12 ( 4 bit): EM style frame
9205 bit 11 - 6 ( 6 bit): graphic width
9206 bit 5 - 0 ( 6 bit): graphic height */
9207 g_em->unique_identifier =
9208 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9212 for (i = 0; i < GAME_TILE_MAX; i++)
9214 for (j = 0; j < 8; j++)
9216 int element = object_mapping[i].element_rnd;
9217 int action = object_mapping[i].action;
9218 int direction = object_mapping[i].direction;
9219 boolean is_backside = object_mapping[i].is_backside;
9220 int graphic_action = el_act_dir2img(element, action, direction);
9221 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9223 if ((action == ACTION_SMASHED_BY_ROCK ||
9224 action == ACTION_SMASHED_BY_SPRING ||
9225 action == ACTION_EATING) &&
9226 graphic_action == graphic_default)
9228 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9229 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9230 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9231 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9234 // no separate animation for "smashed by rock" -- use rock instead
9235 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9236 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9238 g_em->bitmap = g_xx->bitmap;
9239 g_em->src_x = g_xx->src_x;
9240 g_em->src_y = g_xx->src_y;
9241 g_em->src_offset_x = g_xx->src_offset_x;
9242 g_em->src_offset_y = g_xx->src_offset_y;
9243 g_em->dst_offset_x = g_xx->dst_offset_x;
9244 g_em->dst_offset_y = g_xx->dst_offset_y;
9245 g_em->width = g_xx->width;
9246 g_em->height = g_xx->height;
9247 g_em->unique_identifier = g_xx->unique_identifier;
9250 g_em->preserve_background = TRUE;
9255 for (p = 0; p < MAX_PLAYERS; p++)
9257 for (i = 0; i < PLY_MAX; i++)
9259 int element = player_mapping[p][i].element_rnd;
9260 int action = player_mapping[p][i].action;
9261 int direction = player_mapping[p][i].direction;
9263 for (j = 0; j < 8; j++)
9265 int effective_element = element;
9266 int effective_action = action;
9267 int graphic = (direction == MV_NONE ?
9268 el_act2img(effective_element, effective_action) :
9269 el_act_dir2img(effective_element, effective_action,
9271 struct GraphicInfo *g = &graphic_info[graphic];
9272 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9276 int frame = getAnimationFrame(g->anim_frames,
9279 g->anim_start_frame,
9282 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9284 g_em->bitmap = src_bitmap;
9285 g_em->src_x = src_x;
9286 g_em->src_y = src_y;
9287 g_em->src_offset_x = 0;
9288 g_em->src_offset_y = 0;
9289 g_em->dst_offset_x = 0;
9290 g_em->dst_offset_y = 0;
9291 g_em->width = TILEX;
9292 g_em->height = TILEY;
9298 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9299 boolean any_player_moving,
9300 boolean any_player_snapping,
9301 boolean any_player_dropping)
9303 if (frame == 7 && !any_player_dropping)
9305 if (!local_player->was_waiting)
9307 if (!CheckSaveEngineSnapshotToList())
9310 local_player->was_waiting = TRUE;
9313 else if (any_player_moving || any_player_snapping || any_player_dropping)
9315 local_player->was_waiting = FALSE;
9319 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9320 boolean murphy_is_dropping)
9322 if (murphy_is_waiting)
9324 if (!local_player->was_waiting)
9326 if (!CheckSaveEngineSnapshotToList())
9329 local_player->was_waiting = TRUE;
9334 local_player->was_waiting = FALSE;
9338 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9339 boolean button_released)
9341 if (button_released)
9343 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9344 CheckSaveEngineSnapshotToList();
9346 else if (element_clicked)
9348 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9349 CheckSaveEngineSnapshotToList();
9351 game.snapshot.changed_action = TRUE;
9355 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9356 boolean any_player_moving,
9357 boolean any_player_snapping,
9358 boolean any_player_dropping)
9360 if (tape.single_step && tape.recording && !tape.pausing)
9361 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9362 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9364 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9365 any_player_snapping, any_player_dropping);
9367 return tape.pausing;
9370 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9371 boolean murphy_is_dropping)
9373 boolean murphy_starts_dropping = FALSE;
9376 for (i = 0; i < MAX_PLAYERS; i++)
9377 if (stored_player[i].force_dropping)
9378 murphy_starts_dropping = TRUE;
9380 if (tape.single_step && tape.recording && !tape.pausing)
9381 if (murphy_is_waiting && !murphy_starts_dropping)
9382 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9384 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9387 void CheckSingleStepMode_MM(boolean element_clicked,
9388 boolean button_released)
9390 if (tape.single_step && tape.recording && !tape.pausing)
9391 if (button_released)
9392 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9394 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9397 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9398 int graphic, int sync_frame, int x, int y)
9400 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9402 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9405 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9407 return (IS_NEXT_FRAME(sync_frame, graphic));
9410 int getGraphicInfo_Delay(int graphic)
9412 return graphic_info[graphic].anim_delay;
9415 void PlayMenuSoundExt(int sound)
9417 if (sound == SND_UNDEFINED)
9420 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9421 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9424 if (IS_LOOP_SOUND(sound))
9425 PlaySoundLoop(sound);
9430 void PlayMenuSound(void)
9432 PlayMenuSoundExt(menu.sound[game_status]);
9435 void PlayMenuSoundStereo(int sound, int stereo_position)
9437 if (sound == SND_UNDEFINED)
9440 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9441 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9444 if (IS_LOOP_SOUND(sound))
9445 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9447 PlaySoundStereo(sound, stereo_position);
9450 void PlayMenuSoundIfLoopExt(int sound)
9452 if (sound == SND_UNDEFINED)
9455 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9456 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9459 if (IS_LOOP_SOUND(sound))
9460 PlaySoundLoop(sound);
9463 void PlayMenuSoundIfLoop(void)
9465 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9468 void PlayMenuMusicExt(int music)
9470 if (music == MUS_UNDEFINED)
9473 if (!setup.sound_music)
9476 if (IS_LOOP_MUSIC(music))
9477 PlayMusicLoop(music);
9482 void PlayMenuMusic(void)
9484 char *curr_music = getCurrentlyPlayingMusicFilename();
9485 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9487 if (!strEqual(curr_music, next_music))
9488 PlayMenuMusicExt(menu.music[game_status]);
9491 void PlayMenuSoundsAndMusic(void)
9497 static void FadeMenuSounds(void)
9502 static void FadeMenuMusic(void)
9504 char *curr_music = getCurrentlyPlayingMusicFilename();
9505 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9507 if (!strEqual(curr_music, next_music))
9511 void FadeMenuSoundsAndMusic(void)
9517 void PlaySoundActivating(void)
9520 PlaySound(SND_MENU_ITEM_ACTIVATING);
9524 void PlaySoundSelecting(void)
9527 PlaySound(SND_MENU_ITEM_SELECTING);
9531 void ToggleFullscreenIfNeeded(void)
9533 // if setup and video fullscreen state are already matching, nothing do do
9534 if (setup.fullscreen == video.fullscreen_enabled ||
9535 !video.fullscreen_available)
9538 SDLSetWindowFullscreen(setup.fullscreen);
9540 // set setup value according to successfully changed fullscreen mode
9541 setup.fullscreen = video.fullscreen_enabled;
9544 void ChangeWindowScalingIfNeeded(void)
9546 // if setup and video window scaling are already matching, nothing do do
9547 if (setup.window_scaling_percent == video.window_scaling_percent ||
9548 video.fullscreen_enabled)
9551 SDLSetWindowScaling(setup.window_scaling_percent);
9553 // set setup value according to successfully changed window scaling
9554 setup.window_scaling_percent = video.window_scaling_percent;
9557 void ChangeVsyncModeIfNeeded(void)
9559 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9560 int video_vsync_mode = video.vsync_mode;
9562 // if setup and video vsync mode are already matching, nothing do do
9563 if (setup_vsync_mode == video_vsync_mode)
9566 // if renderer is using OpenGL, vsync mode can directly be changed
9567 SDLSetScreenVsyncMode(setup.vsync_mode);
9569 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9570 if (video.vsync_mode == video_vsync_mode)
9572 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9574 // save backbuffer content which gets lost when re-creating screen
9575 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9577 // force re-creating screen and renderer to set new vsync mode
9578 video.fullscreen_enabled = !setup.fullscreen;
9580 // when creating new renderer, destroy textures linked to old renderer
9581 FreeAllImageTextures(); // needs old renderer to free the textures
9583 // re-create screen and renderer (including change of vsync mode)
9584 ChangeVideoModeIfNeeded(setup.fullscreen);
9586 // set setup value according to successfully changed fullscreen mode
9587 setup.fullscreen = video.fullscreen_enabled;
9589 // restore backbuffer content from temporary backbuffer backup bitmap
9590 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9591 FreeBitmap(tmp_backbuffer);
9593 // update visible window/screen
9594 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9596 // when changing vsync mode, re-create textures for new renderer
9597 InitImageTextures();
9600 // set setup value according to successfully changed vsync mode
9601 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9604 static void JoinRectangles(int *x, int *y, int *width, int *height,
9605 int x2, int y2, int width2, int height2)
9607 // do not join with "off-screen" rectangle
9608 if (x2 == -1 || y2 == -1)
9613 *width = MAX(*width, width2);
9614 *height = MAX(*height, height2);
9617 void SetAnimStatus(int anim_status_new)
9619 if (anim_status_new == GAME_MODE_MAIN)
9620 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9621 else if (anim_status_new == GAME_MODE_NAMES)
9622 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9623 else if (anim_status_new == GAME_MODE_SCORES)
9624 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9626 global.anim_status_next = anim_status_new;
9628 // directly set screen modes that are entered without fading
9629 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9630 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9631 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9632 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9633 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9634 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9635 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9636 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9637 global.anim_status = global.anim_status_next;
9640 void SetGameStatus(int game_status_new)
9642 if (game_status_new != game_status)
9643 game_status_last_screen = game_status;
9645 game_status = game_status_new;
9647 SetAnimStatus(game_status_new);
9650 void SetFontStatus(int game_status_new)
9652 static int last_game_status = -1;
9654 if (game_status_new != -1)
9656 // set game status for font use after storing last game status
9657 last_game_status = game_status;
9658 game_status = game_status_new;
9662 // reset game status after font use from last stored game status
9663 game_status = last_game_status;
9667 void ResetFontStatus(void)
9672 void SetLevelSetInfo(char *identifier, int level_nr)
9674 setString(&levelset.identifier, identifier);
9676 levelset.level_nr = level_nr;
9679 boolean CheckIfAllViewportsHaveChanged(void)
9681 // if game status has not changed, viewports have not changed either
9682 if (game_status == game_status_last)
9685 // check if all viewports have changed with current game status
9687 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9688 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9689 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9690 int new_real_sx = vp_playfield->x;
9691 int new_real_sy = vp_playfield->y;
9692 int new_full_sxsize = vp_playfield->width;
9693 int new_full_sysize = vp_playfield->height;
9694 int new_dx = vp_door_1->x;
9695 int new_dy = vp_door_1->y;
9696 int new_dxsize = vp_door_1->width;
9697 int new_dysize = vp_door_1->height;
9698 int new_vx = vp_door_2->x;
9699 int new_vy = vp_door_2->y;
9700 int new_vxsize = vp_door_2->width;
9701 int new_vysize = vp_door_2->height;
9703 boolean playfield_viewport_has_changed =
9704 (new_real_sx != REAL_SX ||
9705 new_real_sy != REAL_SY ||
9706 new_full_sxsize != FULL_SXSIZE ||
9707 new_full_sysize != FULL_SYSIZE);
9709 boolean door_1_viewport_has_changed =
9712 new_dxsize != DXSIZE ||
9713 new_dysize != DYSIZE);
9715 boolean door_2_viewport_has_changed =
9718 new_vxsize != VXSIZE ||
9719 new_vysize != VYSIZE ||
9720 game_status_last == GAME_MODE_EDITOR);
9722 return (playfield_viewport_has_changed &&
9723 door_1_viewport_has_changed &&
9724 door_2_viewport_has_changed);
9727 boolean CheckFadeAll(void)
9729 return (CheckIfGlobalBorderHasChanged() ||
9730 CheckIfAllViewportsHaveChanged());
9733 void ChangeViewportPropertiesIfNeeded(void)
9735 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9736 FALSE : setup.small_game_graphics);
9737 int gfx_game_mode = game_status;
9738 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9740 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9741 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9742 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9743 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9744 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9745 int new_win_xsize = vp_window->width;
9746 int new_win_ysize = vp_window->height;
9747 int border_left = vp_playfield->border_left;
9748 int border_right = vp_playfield->border_right;
9749 int border_top = vp_playfield->border_top;
9750 int border_bottom = vp_playfield->border_bottom;
9751 int new_sx = vp_playfield->x + border_left;
9752 int new_sy = vp_playfield->y + border_top;
9753 int new_sxsize = vp_playfield->width - border_left - border_right;
9754 int new_sysize = vp_playfield->height - border_top - border_bottom;
9755 int new_real_sx = vp_playfield->x;
9756 int new_real_sy = vp_playfield->y;
9757 int new_full_sxsize = vp_playfield->width;
9758 int new_full_sysize = vp_playfield->height;
9759 int new_dx = vp_door_1->x;
9760 int new_dy = vp_door_1->y;
9761 int new_dxsize = vp_door_1->width;
9762 int new_dysize = vp_door_1->height;
9763 int new_vx = vp_door_2->x;
9764 int new_vy = vp_door_2->y;
9765 int new_vxsize = vp_door_2->width;
9766 int new_vysize = vp_door_2->height;
9767 int new_ex = vp_door_3->x;
9768 int new_ey = vp_door_3->y;
9769 int new_exsize = vp_door_3->width;
9770 int new_eysize = vp_door_3->height;
9771 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9772 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9773 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9774 int new_scr_fieldx = new_sxsize / tilesize;
9775 int new_scr_fieldy = new_sysize / tilesize;
9776 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9777 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9778 boolean init_gfx_buffers = FALSE;
9779 boolean init_video_buffer = FALSE;
9780 boolean init_gadgets_and_anims = FALSE;
9781 boolean init_em_graphics = FALSE;
9783 if (new_win_xsize != WIN_XSIZE ||
9784 new_win_ysize != WIN_YSIZE)
9786 WIN_XSIZE = new_win_xsize;
9787 WIN_YSIZE = new_win_ysize;
9789 init_video_buffer = TRUE;
9790 init_gfx_buffers = TRUE;
9791 init_gadgets_and_anims = TRUE;
9793 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9796 if (new_scr_fieldx != SCR_FIELDX ||
9797 new_scr_fieldy != SCR_FIELDY)
9799 // this always toggles between MAIN and GAME when using small tile size
9801 SCR_FIELDX = new_scr_fieldx;
9802 SCR_FIELDY = new_scr_fieldy;
9804 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9815 new_sxsize != SXSIZE ||
9816 new_sysize != SYSIZE ||
9817 new_dxsize != DXSIZE ||
9818 new_dysize != DYSIZE ||
9819 new_vxsize != VXSIZE ||
9820 new_vysize != VYSIZE ||
9821 new_exsize != EXSIZE ||
9822 new_eysize != EYSIZE ||
9823 new_real_sx != REAL_SX ||
9824 new_real_sy != REAL_SY ||
9825 new_full_sxsize != FULL_SXSIZE ||
9826 new_full_sysize != FULL_SYSIZE ||
9827 new_tilesize_var != TILESIZE_VAR
9830 // ------------------------------------------------------------------------
9831 // determine next fading area for changed viewport definitions
9832 // ------------------------------------------------------------------------
9834 // start with current playfield area (default fading area)
9837 FADE_SXSIZE = FULL_SXSIZE;
9838 FADE_SYSIZE = FULL_SYSIZE;
9840 // add new playfield area if position or size has changed
9841 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9842 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9844 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9845 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9848 // add current and new door 1 area if position or size has changed
9849 if (new_dx != DX || new_dy != DY ||
9850 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9852 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9853 DX, DY, DXSIZE, DYSIZE);
9854 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9855 new_dx, new_dy, new_dxsize, new_dysize);
9858 // add current and new door 2 area if position or size has changed
9859 if (new_vx != VX || new_vy != VY ||
9860 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9862 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9863 VX, VY, VXSIZE, VYSIZE);
9864 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9865 new_vx, new_vy, new_vxsize, new_vysize);
9868 // ------------------------------------------------------------------------
9869 // handle changed tile size
9870 // ------------------------------------------------------------------------
9872 if (new_tilesize_var != TILESIZE_VAR)
9874 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9876 // changing tile size invalidates scroll values of engine snapshots
9877 FreeEngineSnapshotSingle();
9879 // changing tile size requires update of graphic mapping for EM engine
9880 init_em_graphics = TRUE;
9891 SXSIZE = new_sxsize;
9892 SYSIZE = new_sysize;
9893 DXSIZE = new_dxsize;
9894 DYSIZE = new_dysize;
9895 VXSIZE = new_vxsize;
9896 VYSIZE = new_vysize;
9897 EXSIZE = new_exsize;
9898 EYSIZE = new_eysize;
9899 REAL_SX = new_real_sx;
9900 REAL_SY = new_real_sy;
9901 FULL_SXSIZE = new_full_sxsize;
9902 FULL_SYSIZE = new_full_sysize;
9903 TILESIZE_VAR = new_tilesize_var;
9905 init_gfx_buffers = TRUE;
9906 init_gadgets_and_anims = TRUE;
9908 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9909 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9912 if (init_gfx_buffers)
9914 // Debug("tools:viewport", "init_gfx_buffers");
9916 SCR_FIELDX = new_scr_fieldx_buffers;
9917 SCR_FIELDY = new_scr_fieldy_buffers;
9921 SCR_FIELDX = new_scr_fieldx;
9922 SCR_FIELDY = new_scr_fieldy;
9924 SetDrawDeactivationMask(REDRAW_NONE);
9925 SetDrawBackgroundMask(REDRAW_FIELD);
9928 if (init_video_buffer)
9930 // Debug("tools:viewport", "init_video_buffer");
9932 FreeAllImageTextures(); // needs old renderer to free the textures
9934 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9935 InitImageTextures();
9938 if (init_gadgets_and_anims)
9940 // Debug("tools:viewport", "init_gadgets_and_anims");
9943 InitGlobalAnimations();
9946 if (init_em_graphics)
9948 InitGraphicInfo_EM();
9953 // ============================================================================
9955 // ============================================================================
9957 #if defined(PLATFORM_WIN32)
9958 /* FILETIME of Jan 1 1970 00:00:00. */
9959 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9962 * timezone information is stored outside the kernel so tzp isn't used anymore.
9964 * Note: this function is not for Win32 high precision timing purpose. See
9967 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9970 SYSTEMTIME system_time;
9971 ULARGE_INTEGER ularge;
9973 GetSystemTime(&system_time);
9974 SystemTimeToFileTime(&system_time, &file_time);
9975 ularge.LowPart = file_time.dwLowDateTime;
9976 ularge.HighPart = file_time.dwHighDateTime;
9978 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9979 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9985 static char *test_init_uuid_random_function_simple(void)
9987 static char seed_text[100];
9988 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9990 sprintf(seed_text, "%d", seed);
9995 static char *test_init_uuid_random_function_better(void)
9997 static char seed_text[100];
9998 struct timeval current_time;
10000 gettimeofday(¤t_time, NULL);
10002 prng_seed_bytes(¤t_time, sizeof(current_time));
10004 sprintf(seed_text, "%ld.%ld",
10005 (long)current_time.tv_sec,
10006 (long)current_time.tv_usec);
10011 #if defined(PLATFORM_WIN32)
10012 static char *test_init_uuid_random_function_better_windows(void)
10014 static char seed_text[100];
10015 struct timeval current_time;
10017 gettimeofday_windows(¤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);
10029 static unsigned int test_uuid_random_function_simple(int max)
10031 return GetSimpleRandom(max);
10034 static unsigned int test_uuid_random_function_better(int max)
10036 return (max > 0 ? prng_get_uint() % max : 0);
10039 #if defined(PLATFORM_WIN32)
10040 #define NUM_UUID_TESTS 3
10042 #define NUM_UUID_TESTS 2
10045 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10047 struct hashtable *hash_seeds =
10048 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10049 struct hashtable *hash_uuids =
10050 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10051 static char message[100];
10054 char *random_name = (nr == 0 ? "simple" : "better");
10055 char *random_type = (always_seed ? "always" : "only once");
10056 char *(*init_random_function)(void) =
10058 test_init_uuid_random_function_simple :
10059 test_init_uuid_random_function_better);
10060 unsigned int (*random_function)(int) =
10062 test_uuid_random_function_simple :
10063 test_uuid_random_function_better);
10066 #if defined(PLATFORM_WIN32)
10069 random_name = "windows";
10070 init_random_function = test_init_uuid_random_function_better_windows;
10076 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10077 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10079 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10080 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10081 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10083 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10087 // always initialize random number generator at least once
10088 init_random_function();
10090 unsigned int time_start = SDL_GetTicks();
10092 for (i = 0; i < num_uuids; i++)
10096 char *seed = getStringCopy(init_random_function());
10098 hashtable_remove(hash_seeds, seed);
10099 hashtable_insert(hash_seeds, seed, "1");
10102 char *uuid = getStringCopy(getUUIDExt(random_function));
10104 hashtable_remove(hash_uuids, uuid);
10105 hashtable_insert(hash_uuids, uuid, "1");
10108 int num_unique_seeds = hashtable_count(hash_seeds);
10109 int num_unique_uuids = hashtable_count(hash_uuids);
10111 unsigned int time_needed = SDL_GetTicks() - time_start;
10113 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10115 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10118 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10120 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10121 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10123 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10125 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10127 Request(message, REQ_CONFIRM);
10129 hashtable_destroy(hash_seeds, 0);
10130 hashtable_destroy(hash_uuids, 0);
10133 void TestGeneratingUUIDs(void)
10135 int num_uuids = 1000000;
10138 for (i = 0; i < NUM_UUID_TESTS; i++)
10139 for (j = 0; j < 2; j++)
10140 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10142 CloseAllAndExit(0);