1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 // may happen for "border.draw_masked.*" with undefined "global.border.*"
516 if (src_bitmap == NULL)
519 if (x == -1 && y == -1)
522 if (draw_target == DRAW_TO_SCREEN)
523 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
525 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
528 static void DrawMaskedBorderExt_FIELD(int draw_target)
530 if (global.border_status >= GAME_MODE_MAIN &&
531 global.border_status <= GAME_MODE_PLAYING &&
532 border.draw_masked[global.border_status])
533 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
537 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
539 // when drawing to backbuffer, never draw border over open doors
540 if (draw_target == DRAW_TO_BACKBUFFER &&
541 (GetDoorState() & DOOR_OPEN_1))
544 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
545 (global.border_status != GAME_MODE_EDITOR ||
546 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
547 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
550 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
552 // when drawing to backbuffer, never draw border over open doors
553 if (draw_target == DRAW_TO_BACKBUFFER &&
554 (GetDoorState() & DOOR_OPEN_2))
557 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
558 global.border_status != GAME_MODE_EDITOR)
559 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
562 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
564 // currently not available
567 static void DrawMaskedBorderExt_ALL(int draw_target)
569 DrawMaskedBorderExt_FIELD(draw_target);
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 DrawMaskedBorderExt_DOOR_2(draw_target);
572 DrawMaskedBorderExt_DOOR_3(draw_target);
575 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
577 // never draw masked screen borders on borderless screens
578 if (global.border_status == GAME_MODE_LOADING ||
579 global.border_status == GAME_MODE_TITLE)
582 if (redraw_mask & REDRAW_ALL)
583 DrawMaskedBorderExt_ALL(draw_target);
586 if (redraw_mask & REDRAW_FIELD)
587 DrawMaskedBorderExt_FIELD(draw_target);
588 if (redraw_mask & REDRAW_DOOR_1)
589 DrawMaskedBorderExt_DOOR_1(draw_target);
590 if (redraw_mask & REDRAW_DOOR_2)
591 DrawMaskedBorderExt_DOOR_2(draw_target);
592 if (redraw_mask & REDRAW_DOOR_3)
593 DrawMaskedBorderExt_DOOR_3(draw_target);
597 void DrawMaskedBorder_FIELD(void)
599 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
602 void DrawMaskedBorder(int redraw_mask)
604 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
607 void DrawMaskedBorderToTarget(int draw_target)
609 if (draw_target == DRAW_TO_BACKBUFFER ||
610 draw_target == DRAW_TO_SCREEN)
612 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
616 int last_border_status = global.border_status;
618 if (draw_target == DRAW_TO_FADE_SOURCE)
620 global.border_status = gfx.fade_border_source_status;
621 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
623 else if (draw_target == DRAW_TO_FADE_TARGET)
625 global.border_status = gfx.fade_border_target_status;
626 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
629 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
631 global.border_status = last_border_status;
632 gfx.masked_border_bitmap_ptr = backbuffer;
636 void DrawTileCursor(int draw_target)
638 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
641 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
643 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
646 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
648 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
649 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
651 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
654 void BlitScreenToBitmap(Bitmap *target_bitmap)
656 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
657 BlitScreenToBitmap_EM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
659 BlitScreenToBitmap_SP(target_bitmap);
660 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
661 BlitScreenToBitmap_MM(target_bitmap);
662 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
663 BlitScreenToBitmap_RND(target_bitmap);
665 redraw_mask |= REDRAW_FIELD;
668 static void DrawFramesPerSecond(void)
671 int font_nr = FONT_TEXT_2;
672 int font_width = getFontWidth(font_nr);
673 int draw_deactivation_mask = GetDrawDeactivationMask();
674 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
676 // draw FPS with leading space (needed if field buffer deactivated)
677 sprintf(text, " %04.1f fps", global.frames_per_second);
679 // override draw deactivation mask (required for invisible warp mode)
680 SetDrawDeactivationMask(REDRAW_NONE);
682 // draw opaque FPS if field buffer deactivated, else draw masked FPS
683 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
684 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
686 // set draw deactivation mask to previous value
687 SetDrawDeactivationMask(draw_deactivation_mask);
689 // force full-screen redraw in this frame
690 redraw_mask = REDRAW_ALL;
694 static void PrintFrameTimeDebugging(void)
696 static unsigned int last_counter = 0;
697 unsigned int counter = Counter();
698 int diff_1 = counter - last_counter;
699 int diff_2 = diff_1 - GAME_FRAME_DELAY;
701 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
702 char diff_bar[2 * diff_2_max + 5];
706 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
708 for (i = 0; i < diff_2_max; i++)
709 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
710 i >= diff_2_max - diff_2_cut ? '-' : ' ');
712 diff_bar[pos++] = '|';
714 for (i = 0; i < diff_2_max; i++)
715 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
717 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
719 diff_bar[pos++] = '\0';
721 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
724 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
727 last_counter = counter;
731 static int unifiedRedrawMask(int mask)
733 if (mask & REDRAW_ALL)
736 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
742 static boolean equalRedrawMasks(int mask_1, int mask_2)
744 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
747 void BackToFront(void)
749 static int last_redraw_mask = REDRAW_NONE;
751 // force screen redraw in every frame to continue drawing global animations
752 // (but always use the last redraw mask to prevent unwanted side effects)
753 if (redraw_mask == REDRAW_NONE)
754 redraw_mask = last_redraw_mask;
756 last_redraw_mask = redraw_mask;
759 // masked border now drawn immediately when blitting backbuffer to window
761 // draw masked border to all viewports, if defined
762 DrawMaskedBorder(redraw_mask);
765 // draw frames per second (only if debug mode is enabled)
766 if (redraw_mask & REDRAW_FPS)
767 DrawFramesPerSecond();
769 // remove playfield redraw before potentially merging with doors redraw
770 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
771 redraw_mask &= ~REDRAW_FIELD;
773 // redraw complete window if both playfield and (some) doors need redraw
774 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
775 redraw_mask = REDRAW_ALL;
777 /* although redrawing the whole window would be fine for normal gameplay,
778 being able to only redraw the playfield is required for deactivating
779 certain drawing areas (mainly playfield) to work, which is needed for
780 warp-forward to be fast enough (by skipping redraw of most frames) */
782 if (redraw_mask & REDRAW_ALL)
784 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
786 else if (redraw_mask & REDRAW_FIELD)
788 BlitBitmap(backbuffer, window,
789 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
791 else if (redraw_mask & REDRAW_DOORS)
793 // merge door areas to prevent calling screen redraw more than once
799 if (redraw_mask & REDRAW_DOOR_1)
803 x2 = MAX(x2, DX + DXSIZE);
804 y2 = MAX(y2, DY + DYSIZE);
807 if (redraw_mask & REDRAW_DOOR_2)
811 x2 = MAX(x2, VX + VXSIZE);
812 y2 = MAX(y2, VY + VYSIZE);
815 if (redraw_mask & REDRAW_DOOR_3)
819 x2 = MAX(x2, EX + EXSIZE);
820 y2 = MAX(y2, EY + EYSIZE);
823 // make sure that at least one pixel is blitted, and inside the screen
824 // (else nothing is blitted, causing the animations not to be updated)
825 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
826 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
827 x2 = MIN(MAX(1, x2), WIN_XSIZE);
828 y2 = MIN(MAX(1, y2), WIN_YSIZE);
830 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
833 redraw_mask = REDRAW_NONE;
836 PrintFrameTimeDebugging();
840 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
842 unsigned int frame_delay_value_old = GetVideoFrameDelay();
844 SetVideoFrameDelay(frame_delay_value);
848 SetVideoFrameDelay(frame_delay_value_old);
851 static int fade_type_skip = FADE_TYPE_NONE;
853 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
855 void (*draw_border_function)(void) = NULL;
856 int x, y, width, height;
857 int fade_delay, post_delay;
859 if (fade_type == FADE_TYPE_FADE_OUT)
861 if (fade_type_skip != FADE_TYPE_NONE)
863 // skip all fade operations until specified fade operation
864 if (fade_type & fade_type_skip)
865 fade_type_skip = FADE_TYPE_NONE;
870 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
874 redraw_mask |= fade_mask;
876 if (fade_type == FADE_TYPE_SKIP)
878 fade_type_skip = fade_mode;
883 fade_delay = fading.fade_delay;
884 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
886 if (fade_type_skip != FADE_TYPE_NONE)
888 // skip all fade operations until specified fade operation
889 if (fade_type & fade_type_skip)
890 fade_type_skip = FADE_TYPE_NONE;
895 if (global.autoplay_leveldir)
900 if (fade_mask == REDRAW_FIELD)
905 height = FADE_SYSIZE;
907 if (border.draw_masked_when_fading)
908 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
910 DrawMaskedBorder_FIELD(); // draw once
920 // when switching screens without fading, set fade delay to zero
921 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
924 // do not display black frame when fading out without fade delay
925 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
928 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
929 draw_border_function);
931 redraw_mask &= ~fade_mask;
933 ClearAutoRepeatKeyEvents();
936 static void SetScreenStates_BeforeFadingIn(void)
938 // temporarily set screen mode for animations to screen after fading in
939 global.anim_status = global.anim_status_next;
941 // store backbuffer with all animations that will be started after fading in
942 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
944 // set screen mode for animations back to fading
945 global.anim_status = GAME_MODE_PSEUDO_FADING;
948 static void SetScreenStates_AfterFadingIn(void)
950 // store new source screen (to use correct masked border for fading)
951 gfx.fade_border_source_status = global.border_status;
953 global.anim_status = global.anim_status_next;
956 static void SetScreenStates_BeforeFadingOut(void)
958 // store new target screen (to use correct masked border for fading)
959 gfx.fade_border_target_status = game_status;
961 // set screen mode for animations to fading
962 global.anim_status = GAME_MODE_PSEUDO_FADING;
964 // store backbuffer with all animations that will be stopped for fading out
965 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
968 static void SetScreenStates_AfterFadingOut(void)
970 global.border_status = game_status;
973 void FadeIn(int fade_mask)
975 SetScreenStates_BeforeFadingIn();
978 DrawMaskedBorder(REDRAW_ALL);
981 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
982 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
984 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
988 FADE_SXSIZE = FULL_SXSIZE;
989 FADE_SYSIZE = FULL_SYSIZE;
991 // activate virtual buttons depending on upcoming game status
992 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
993 game_status == GAME_MODE_PLAYING && !tape.playing)
994 SetOverlayActive(TRUE);
996 SetScreenStates_AfterFadingIn();
998 // force update of global animation status in case of rapid screen changes
999 redraw_mask = REDRAW_ALL;
1003 void FadeOut(int fade_mask)
1005 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1006 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1007 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1010 SetScreenStates_BeforeFadingOut();
1012 SetTileCursorActive(FALSE);
1013 SetOverlayActive(FALSE);
1016 DrawMaskedBorder(REDRAW_ALL);
1019 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1020 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1022 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1024 SetScreenStates_AfterFadingOut();
1027 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1029 static struct TitleFadingInfo fading_leave_stored;
1032 fading_leave_stored = fading_leave;
1034 fading = fading_leave_stored;
1037 void FadeSetEnterMenu(void)
1039 fading = menu.enter_menu;
1041 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1044 void FadeSetLeaveMenu(void)
1046 fading = menu.leave_menu;
1048 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1051 void FadeSetEnterScreen(void)
1053 fading = menu.enter_screen[game_status];
1055 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1058 void FadeSetNextScreen(void)
1060 fading = menu.next_screen[game_status];
1062 // (do not overwrite fade mode set by FadeSetEnterScreen)
1063 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1066 void FadeSetLeaveScreen(void)
1068 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1071 void FadeSetFromType(int type)
1073 if (type & TYPE_ENTER_SCREEN)
1074 FadeSetEnterScreen();
1075 else if (type & TYPE_ENTER)
1077 else if (type & TYPE_LEAVE)
1081 void FadeSetDisabled(void)
1083 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1085 fading = fading_none;
1088 void FadeSkipNextFadeIn(void)
1090 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1093 void FadeSkipNextFadeOut(void)
1095 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1098 static int getGlobalGameStatus(int status)
1100 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1101 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1105 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1107 if (graphic == IMG_UNDEFINED)
1110 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1112 return (graphic_info[graphic].bitmap != NULL || redefined ?
1113 graphic_info[graphic].bitmap :
1114 graphic_info[default_graphic].bitmap);
1117 static Bitmap *getBackgroundBitmap(int graphic)
1119 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1122 static Bitmap *getGlobalBorderBitmap(int graphic)
1124 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1127 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1129 int status = getGlobalGameStatus(status_raw);
1131 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1132 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1133 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1134 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1137 return getGlobalBorderBitmap(graphic);
1140 void SetWindowBackgroundImageIfDefined(int graphic)
1142 if (graphic_info[graphic].bitmap)
1143 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1146 void SetMainBackgroundImageIfDefined(int graphic)
1148 if (graphic_info[graphic].bitmap)
1149 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1152 void SetDoorBackgroundImageIfDefined(int graphic)
1154 if (graphic_info[graphic].bitmap)
1155 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1158 void SetWindowBackgroundImage(int graphic)
1160 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1163 void SetMainBackgroundImage(int graphic)
1165 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1168 void SetDoorBackgroundImage(int graphic)
1170 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1173 void SetPanelBackground(void)
1175 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1177 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1178 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1180 SetDoorBackgroundBitmap(bitmap_db_panel);
1183 void DrawBackground(int x, int y, int width, int height)
1185 // "drawto" might still point to playfield buffer here (hall of fame)
1186 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1188 if (IN_GFX_FIELD_FULL(x, y))
1189 redraw_mask |= REDRAW_FIELD;
1190 else if (IN_GFX_DOOR_1(x, y))
1191 redraw_mask |= REDRAW_DOOR_1;
1192 else if (IN_GFX_DOOR_2(x, y))
1193 redraw_mask |= REDRAW_DOOR_2;
1194 else if (IN_GFX_DOOR_3(x, y))
1195 redraw_mask |= REDRAW_DOOR_3;
1198 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1200 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1202 if (font->bitmap == NULL)
1205 DrawBackground(x, y, width, height);
1208 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1210 struct GraphicInfo *g = &graphic_info[graphic];
1212 if (g->bitmap == NULL)
1215 DrawBackground(x, y, width, height);
1218 static int game_status_last = -1;
1219 static Bitmap *global_border_bitmap_last = NULL;
1220 static Bitmap *global_border_bitmap = NULL;
1221 static int real_sx_last = -1, real_sy_last = -1;
1222 static int full_sxsize_last = -1, full_sysize_last = -1;
1223 static int dx_last = -1, dy_last = -1;
1224 static int dxsize_last = -1, dysize_last = -1;
1225 static int vx_last = -1, vy_last = -1;
1226 static int vxsize_last = -1, vysize_last = -1;
1227 static int ex_last = -1, ey_last = -1;
1228 static int exsize_last = -1, eysize_last = -1;
1230 boolean CheckIfGlobalBorderHasChanged(void)
1232 // if game status has not changed, global border has not changed either
1233 if (game_status == game_status_last)
1236 // determine and store new global border bitmap for current game status
1237 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1239 return (global_border_bitmap_last != global_border_bitmap);
1242 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1244 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1245 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1247 // if game status has not changed, nothing has to be redrawn
1248 if (game_status == game_status_last)
1251 // redraw if last screen was title screen
1252 if (game_status_last == GAME_MODE_TITLE)
1255 // redraw if global screen border has changed
1256 if (CheckIfGlobalBorderHasChanged())
1259 // redraw if position or size of playfield area has changed
1260 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1261 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1264 // redraw if position or size of door area has changed
1265 if (dx_last != DX || dy_last != DY ||
1266 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1269 // redraw if position or size of tape area has changed
1270 if (vx_last != VX || vy_last != VY ||
1271 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1274 // redraw if position or size of editor area has changed
1275 if (ex_last != EX || ey_last != EY ||
1276 exsize_last != EXSIZE || eysize_last != EYSIZE)
1283 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1286 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1288 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1291 void RedrawGlobalBorder(void)
1293 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1295 RedrawGlobalBorderFromBitmap(bitmap);
1297 redraw_mask = REDRAW_ALL;
1300 static void RedrawGlobalBorderIfNeeded(void)
1302 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1303 if (game_status == game_status_last)
1307 // copy current draw buffer to later copy back areas that have not changed
1308 if (game_status_last != GAME_MODE_TITLE)
1309 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1311 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1312 if (CheckIfGlobalBorderRedrawIsNeeded())
1314 // determine and store new global border bitmap for current game status
1315 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1318 // redraw global screen border (or clear, if defined to be empty)
1319 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1321 if (game_status == GAME_MODE_EDITOR)
1322 DrawSpecialEditorDoor();
1324 // copy previous playfield and door areas, if they are defined on both
1325 // previous and current screen and if they still have the same size
1327 if (real_sx_last != -1 && real_sy_last != -1 &&
1328 REAL_SX != -1 && REAL_SY != -1 &&
1329 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1330 BlitBitmap(bitmap_db_store_1, backbuffer,
1331 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1334 if (dx_last != -1 && dy_last != -1 &&
1335 DX != -1 && DY != -1 &&
1336 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1337 BlitBitmap(bitmap_db_store_1, backbuffer,
1338 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1340 if (game_status != GAME_MODE_EDITOR)
1342 if (vx_last != -1 && vy_last != -1 &&
1343 VX != -1 && VY != -1 &&
1344 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1345 BlitBitmap(bitmap_db_store_1, backbuffer,
1346 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1350 if (ex_last != -1 && ey_last != -1 &&
1351 EX != -1 && EY != -1 &&
1352 exsize_last == EXSIZE && eysize_last == EYSIZE)
1353 BlitBitmap(bitmap_db_store_1, backbuffer,
1354 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1357 redraw_mask = REDRAW_ALL;
1360 game_status_last = game_status;
1362 global_border_bitmap_last = global_border_bitmap;
1364 real_sx_last = REAL_SX;
1365 real_sy_last = REAL_SY;
1366 full_sxsize_last = FULL_SXSIZE;
1367 full_sysize_last = FULL_SYSIZE;
1370 dxsize_last = DXSIZE;
1371 dysize_last = DYSIZE;
1374 vxsize_last = VXSIZE;
1375 vysize_last = VYSIZE;
1378 exsize_last = EXSIZE;
1379 eysize_last = EYSIZE;
1382 void ClearField(void)
1384 RedrawGlobalBorderIfNeeded();
1386 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1387 // (when entering hall of fame after playing)
1388 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1390 // !!! maybe this should be done before clearing the background !!!
1391 if (game_status == GAME_MODE_PLAYING)
1393 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1394 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1398 SetDrawtoField(DRAW_TO_BACKBUFFER);
1402 void MarkTileDirty(int x, int y)
1404 redraw_mask |= REDRAW_FIELD;
1407 void SetBorderElement(void)
1411 BorderElement = EL_EMPTY;
1413 // only the R'n'D game engine may use an additional steelwall border
1414 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1417 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1419 for (x = 0; x < lev_fieldx; x++)
1421 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1422 BorderElement = EL_STEELWALL;
1424 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1430 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1431 int max_array_fieldx, int max_array_fieldy,
1432 short field[max_array_fieldx][max_array_fieldy],
1433 int max_fieldx, int max_fieldy)
1435 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1436 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1437 int old_element = field[start_x][start_y];
1440 // do nothing if start field already has the desired content
1441 if (old_element == fill_element)
1444 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1446 while (stack_pos > 0)
1448 struct XY current = stack_buffer[--stack_pos];
1451 field[current.x][current.y] = fill_element;
1453 for (i = 0; i < 4; i++)
1455 int x = current.x + check[i].x;
1456 int y = current.y + check[i].y;
1458 // check for stack buffer overflow (should not happen)
1459 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1460 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1462 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1463 stack_buffer[stack_pos++] = (struct XY){ x, y };
1468 void FloodFillLevel(int from_x, int from_y, int fill_element,
1469 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1470 int max_fieldx, int max_fieldy)
1472 FloodFillLevelExt(from_x, from_y, fill_element,
1473 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1474 max_fieldx, max_fieldy);
1477 void SetRandomAnimationValue(int x, int y)
1479 gfx.anim_random_frame = GfxRandom[x][y];
1482 int getGraphicAnimationFrame(int graphic, int sync_frame)
1484 // animation synchronized with global frame counter, not move position
1485 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1486 sync_frame = FrameCounter;
1488 return getAnimationFrame(graphic_info[graphic].anim_frames,
1489 graphic_info[graphic].anim_delay,
1490 graphic_info[graphic].anim_mode,
1491 graphic_info[graphic].anim_start_frame,
1495 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1497 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1499 struct GraphicInfo *g = &graphic_info[graphic];
1500 int xsize = MAX(1, g->anim_frames_per_line);
1501 int ysize = MAX(1, g->anim_frames / xsize);
1502 int xoffset = g->anim_start_frame % xsize;
1503 int yoffset = g->anim_start_frame % ysize;
1504 // may be needed if screen field is significantly larger than playfield
1505 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1506 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1507 int sync_frame = y * xsize + x;
1509 return sync_frame % g->anim_frames;
1511 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1513 struct GraphicInfo *g = &graphic_info[graphic];
1514 // may be needed if screen field is significantly larger than playfield
1515 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1516 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1517 int sync_frame = GfxRandomStatic[x][y];
1519 return sync_frame % g->anim_frames;
1523 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1525 return getGraphicAnimationFrame(graphic, sync_frame);
1529 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1531 struct GraphicInfo *g = &graphic_info[graphic];
1532 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1534 if (tilesize == gfx.standard_tile_size)
1535 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1536 else if (tilesize == game.tile_size)
1537 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1539 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1542 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1543 boolean get_backside)
1545 struct GraphicInfo *g = &graphic_info[graphic];
1546 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1547 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1549 if (g->offset_y == 0) // frames are ordered horizontally
1551 int max_width = g->anim_frames_per_line * g->width;
1552 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1554 *x = pos % max_width;
1555 *y = src_y % g->height + pos / max_width * g->height;
1557 else if (g->offset_x == 0) // frames are ordered vertically
1559 int max_height = g->anim_frames_per_line * g->height;
1560 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1562 *x = src_x % g->width + pos / max_height * g->width;
1563 *y = pos % max_height;
1565 else // frames are ordered diagonally
1567 *x = src_x + frame * g->offset_x;
1568 *y = src_y + frame * g->offset_y;
1572 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1573 Bitmap **bitmap, int *x, int *y,
1574 boolean get_backside)
1576 struct GraphicInfo *g = &graphic_info[graphic];
1578 // if no graphics defined at all, use fallback graphics
1579 if (g->bitmaps == NULL)
1580 *g = graphic_info[IMG_CHAR_EXCLAM];
1582 // if no in-game graphics defined, always use standard graphic size
1583 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1584 tilesize = TILESIZE;
1586 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1587 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1589 *x = *x * tilesize / g->tile_size;
1590 *y = *y * tilesize / g->tile_size;
1593 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1594 Bitmap **bitmap, int *x, int *y)
1596 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1599 void getFixedGraphicSource(int graphic, int frame,
1600 Bitmap **bitmap, int *x, int *y)
1602 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1605 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1607 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1610 void getGlobalAnimGraphicSource(int graphic, int frame,
1611 Bitmap **bitmap, int *x, int *y)
1613 struct GraphicInfo *g = &graphic_info[graphic];
1615 // if no graphics defined at all, use fallback graphics
1616 if (g->bitmaps == NULL)
1617 *g = graphic_info[IMG_CHAR_EXCLAM];
1619 // use original size graphics, if existing, else use standard size graphics
1620 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1621 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1623 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1625 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1628 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1629 int *x, int *y, boolean get_backside)
1631 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1635 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1637 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1640 void DrawGraphic(int x, int y, int graphic, int frame)
1643 if (!IN_SCR_FIELD(x, y))
1645 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1646 Debug("draw:DrawGraphic", "This should never happen!");
1652 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1655 MarkTileDirty(x, y);
1658 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1661 if (!IN_SCR_FIELD(x, y))
1663 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1665 Debug("draw:DrawFixedGraphic", "This should never happen!");
1671 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1673 MarkTileDirty(x, y);
1676 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1682 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1684 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1687 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1693 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1694 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1697 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1700 if (!IN_SCR_FIELD(x, y))
1702 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1704 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1710 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1713 MarkTileDirty(x, y);
1716 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1719 if (!IN_SCR_FIELD(x, y))
1721 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1723 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1729 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1731 MarkTileDirty(x, y);
1734 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1740 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1742 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1746 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1747 int graphic, int frame)
1752 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1754 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1758 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1760 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1762 MarkTileDirty(x / tilesize, y / tilesize);
1765 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1768 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1769 graphic, frame, tilesize);
1770 MarkTileDirty(x / tilesize, y / tilesize);
1773 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1779 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1780 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1783 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1784 int frame, int tilesize)
1789 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1790 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1793 void DrawMiniGraphic(int x, int y, int graphic)
1795 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1796 MarkTileDirty(x / 2, y / 2);
1799 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1804 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1805 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1808 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1809 int graphic, int frame,
1810 int cut_mode, int mask_mode)
1815 int width = TILEX, height = TILEY;
1818 if (dx || dy) // shifted graphic
1820 if (x < BX1) // object enters playfield from the left
1827 else if (x > BX2) // object enters playfield from the right
1833 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1839 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1841 else if (dx) // general horizontal movement
1842 MarkTileDirty(x + SIGN(dx), y);
1844 if (y < BY1) // object enters playfield from the top
1846 if (cut_mode == CUT_BELOW) // object completely above top border
1854 else if (y > BY2) // object enters playfield from the bottom
1860 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1866 else if (dy > 0 && cut_mode == CUT_ABOVE)
1868 if (y == BY2) // object completely above bottom border
1874 MarkTileDirty(x, y + 1);
1875 } // object leaves playfield to the bottom
1876 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1878 else if (dy) // general vertical movement
1879 MarkTileDirty(x, y + SIGN(dy));
1883 if (!IN_SCR_FIELD(x, y))
1885 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1887 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1893 width = width * TILESIZE_VAR / TILESIZE;
1894 height = height * TILESIZE_VAR / TILESIZE;
1895 cx = cx * TILESIZE_VAR / TILESIZE;
1896 cy = cy * TILESIZE_VAR / TILESIZE;
1897 dx = dx * TILESIZE_VAR / TILESIZE;
1898 dy = dy * TILESIZE_VAR / TILESIZE;
1900 if (width > 0 && height > 0)
1902 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1907 dst_x = FX + x * TILEX_VAR + dx;
1908 dst_y = FY + y * TILEY_VAR + dy;
1910 if (mask_mode == USE_MASKING)
1911 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1914 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1917 MarkTileDirty(x, y);
1921 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1922 int graphic, int frame,
1923 int cut_mode, int mask_mode)
1928 int width = TILEX_VAR, height = TILEY_VAR;
1931 int x2 = x + SIGN(dx);
1932 int y2 = y + SIGN(dy);
1934 // movement with two-tile animations must be sync'ed with movement position,
1935 // not with current GfxFrame (which can be higher when using slow movement)
1936 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1937 int anim_frames = graphic_info[graphic].anim_frames;
1939 // (we also need anim_delay here for movement animations with less frames)
1940 int anim_delay = graphic_info[graphic].anim_delay;
1941 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1943 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1944 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1946 // re-calculate animation frame for two-tile movement animation
1947 frame = getGraphicAnimationFrame(graphic, sync_frame);
1949 // check if movement start graphic inside screen area and should be drawn
1950 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1952 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1954 dst_x = FX + x1 * TILEX_VAR;
1955 dst_y = FY + y1 * TILEY_VAR;
1957 if (mask_mode == USE_MASKING)
1958 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1961 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1964 MarkTileDirty(x1, y1);
1967 // check if movement end graphic inside screen area and should be drawn
1968 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1970 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1972 dst_x = FX + x2 * TILEX_VAR;
1973 dst_y = FY + y2 * TILEY_VAR;
1975 if (mask_mode == USE_MASKING)
1976 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1979 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1982 MarkTileDirty(x2, y2);
1986 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1987 int graphic, int frame,
1988 int cut_mode, int mask_mode)
1992 DrawGraphic(x, y, graphic, frame);
1997 if (graphic_info[graphic].double_movement) // EM style movement images
1998 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2000 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2003 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2004 int graphic, int frame, int cut_mode)
2006 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2009 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2010 int cut_mode, int mask_mode)
2012 int lx = LEVELX(x), ly = LEVELY(y);
2016 if (IN_LEV_FIELD(lx, ly))
2018 if (element == EL_EMPTY)
2019 element = GfxElementEmpty[lx][ly];
2021 SetRandomAnimationValue(lx, ly);
2023 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2024 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2026 // do not use double (EM style) movement graphic when not moving
2027 if (graphic_info[graphic].double_movement && !dx && !dy)
2029 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2030 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2033 if (game.use_masked_elements && (dx || dy))
2034 mask_mode = USE_MASKING;
2036 else // border element
2038 graphic = el2img(element);
2039 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2042 if (element == EL_EXPANDABLE_WALL)
2044 boolean left_stopped = FALSE, right_stopped = FALSE;
2046 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2047 left_stopped = TRUE;
2048 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2049 right_stopped = TRUE;
2051 if (left_stopped && right_stopped)
2053 else if (left_stopped)
2055 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2056 frame = graphic_info[graphic].anim_frames - 1;
2058 else if (right_stopped)
2060 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2061 frame = graphic_info[graphic].anim_frames - 1;
2066 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2067 else if (mask_mode == USE_MASKING)
2068 DrawGraphicThruMask(x, y, graphic, frame);
2070 DrawGraphic(x, y, graphic, frame);
2073 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2074 int cut_mode, int mask_mode)
2076 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2077 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2078 cut_mode, mask_mode);
2081 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2084 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2087 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2090 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2093 void DrawLevelElementThruMask(int x, int y, int element)
2095 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2098 void DrawLevelFieldThruMask(int x, int y)
2100 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2103 // !!! implementation of quicksand is totally broken !!!
2104 #define IS_CRUMBLED_TILE(x, y, e) \
2105 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2106 !IS_MOVING(x, y) || \
2107 (e) == EL_QUICKSAND_EMPTYING || \
2108 (e) == EL_QUICKSAND_FAST_EMPTYING))
2110 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2115 int width, height, cx, cy;
2116 int sx = SCREENX(x), sy = SCREENY(y);
2117 int crumbled_border_size = graphic_info[graphic].border_size;
2118 int crumbled_tile_size = graphic_info[graphic].tile_size;
2119 int crumbled_border_size_var =
2120 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2123 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2125 for (i = 1; i < 4; i++)
2127 int dxx = (i & 1 ? dx : 0);
2128 int dyy = (i & 2 ? dy : 0);
2131 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2134 // check if neighbour field is of same crumble type
2135 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2136 graphic_info[graphic].class ==
2137 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2139 // return if check prevents inner corner
2140 if (same == (dxx == dx && dyy == dy))
2144 // if we reach this point, we have an inner corner
2146 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2148 width = crumbled_border_size_var;
2149 height = crumbled_border_size_var;
2150 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2151 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2153 if (game.use_masked_elements)
2155 int graphic0 = el2img(EL_EMPTY);
2156 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2157 Bitmap *src_bitmap0;
2160 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2162 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2164 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2166 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2168 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2171 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2173 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2176 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2181 int width, height, bx, by, cx, cy;
2182 int sx = SCREENX(x), sy = SCREENY(y);
2183 int crumbled_border_size = graphic_info[graphic].border_size;
2184 int crumbled_tile_size = graphic_info[graphic].tile_size;
2185 int crumbled_border_size_var =
2186 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2187 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2190 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2192 // only needed when using masked elements
2193 int graphic0 = el2img(EL_EMPTY);
2194 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2195 Bitmap *src_bitmap0;
2198 if (game.use_masked_elements)
2199 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2201 // draw simple, sloppy, non-corner-accurate crumbled border
2203 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2204 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2205 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2206 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2208 if (game.use_masked_elements)
2210 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2212 FX + sx * TILEX_VAR + cx,
2213 FY + sy * TILEY_VAR + cy);
2215 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2217 FX + sx * TILEX_VAR + cx,
2218 FY + sy * TILEY_VAR + cy);
2221 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2223 FX + sx * TILEX_VAR + cx,
2224 FY + sy * TILEY_VAR + cy);
2226 // (remaining middle border part must be at least as big as corner part)
2227 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2228 crumbled_border_size_var >= TILESIZE_VAR / 3)
2231 // correct corners of crumbled border, if needed
2233 for (i = -1; i <= 1; i += 2)
2235 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2236 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2237 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2240 // check if neighbour field is of same crumble type
2241 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2242 graphic_info[graphic].class ==
2243 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2245 // no crumbled corner, but continued crumbled border
2247 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2248 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2249 int b1 = (i == 1 ? crumbled_border_size_var :
2250 TILESIZE_VAR - 2 * crumbled_border_size_var);
2252 width = crumbled_border_size_var;
2253 height = crumbled_border_size_var;
2255 if (dir == 1 || dir == 2)
2270 if (game.use_masked_elements)
2272 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2274 FX + sx * TILEX_VAR + cx,
2275 FY + sy * TILEY_VAR + cy);
2277 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2279 FX + sx * TILEX_VAR + cx,
2280 FY + sy * TILEY_VAR + cy);
2283 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2285 FX + sx * TILEX_VAR + cx,
2286 FY + sy * TILEY_VAR + cy);
2291 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2293 int sx = SCREENX(x), sy = SCREENY(y);
2296 static int xy[4][2] =
2304 if (!IN_LEV_FIELD(x, y))
2307 element = TILE_GFX_ELEMENT(x, y);
2309 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2311 if (!IN_SCR_FIELD(sx, sy))
2314 // crumble field borders towards direct neighbour fields
2315 for (i = 0; i < 4; i++)
2317 int xx = x + xy[i][0];
2318 int yy = y + xy[i][1];
2320 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2323 // check if neighbour field is of same crumble type
2324 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2325 graphic_info[graphic].class ==
2326 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2329 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2332 // crumble inner field corners towards corner neighbour fields
2333 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2334 graphic_info[graphic].anim_frames == 2)
2336 for (i = 0; i < 4; i++)
2338 int dx = (i & 1 ? +1 : -1);
2339 int dy = (i & 2 ? +1 : -1);
2341 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2345 MarkTileDirty(sx, sy);
2347 else // center field is not crumbled -- crumble neighbour fields
2349 // crumble field borders of direct neighbour fields
2350 for (i = 0; i < 4; i++)
2352 int xx = x + xy[i][0];
2353 int yy = y + xy[i][1];
2354 int sxx = sx + xy[i][0];
2355 int syy = sy + xy[i][1];
2357 if (!IN_LEV_FIELD(xx, yy) ||
2358 !IN_SCR_FIELD(sxx, syy))
2361 // do not crumble fields that are being digged or snapped
2362 if (Tile[xx][yy] == EL_EMPTY ||
2363 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2366 element = TILE_GFX_ELEMENT(xx, yy);
2368 if (!IS_CRUMBLED_TILE(xx, yy, element))
2371 graphic = el_act2crm(element, ACTION_DEFAULT);
2373 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2375 MarkTileDirty(sxx, syy);
2378 // crumble inner field corners of corner neighbour fields
2379 for (i = 0; i < 4; i++)
2381 int dx = (i & 1 ? +1 : -1);
2382 int dy = (i & 2 ? +1 : -1);
2388 if (!IN_LEV_FIELD(xx, yy) ||
2389 !IN_SCR_FIELD(sxx, syy))
2392 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2395 element = TILE_GFX_ELEMENT(xx, yy);
2397 if (!IS_CRUMBLED_TILE(xx, yy, element))
2400 graphic = el_act2crm(element, ACTION_DEFAULT);
2402 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2403 graphic_info[graphic].anim_frames == 2)
2404 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2406 MarkTileDirty(sxx, syy);
2411 void DrawLevelFieldCrumbled(int x, int y)
2415 if (!IN_LEV_FIELD(x, y))
2418 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2419 GfxElement[x][y] != EL_UNDEFINED &&
2420 GFX_CRUMBLED(GfxElement[x][y]))
2422 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2427 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2429 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2432 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2435 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2436 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2437 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2438 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2439 int sx = SCREENX(x), sy = SCREENY(y);
2441 DrawScreenGraphic(sx, sy, graphic1, frame1);
2442 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2445 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2447 int sx = SCREENX(x), sy = SCREENY(y);
2448 static int xy[4][2] =
2457 // crumble direct neighbour fields (required for field borders)
2458 for (i = 0; i < 4; i++)
2460 int xx = x + xy[i][0];
2461 int yy = y + xy[i][1];
2462 int sxx = sx + xy[i][0];
2463 int syy = sy + xy[i][1];
2465 if (!IN_LEV_FIELD(xx, yy) ||
2466 !IN_SCR_FIELD(sxx, syy) ||
2467 !GFX_CRUMBLED(Tile[xx][yy]) ||
2471 DrawLevelField(xx, yy);
2474 // crumble corner neighbour fields (required for inner field corners)
2475 for (i = 0; i < 4; i++)
2477 int dx = (i & 1 ? +1 : -1);
2478 int dy = (i & 2 ? +1 : -1);
2484 if (!IN_LEV_FIELD(xx, yy) ||
2485 !IN_SCR_FIELD(sxx, syy) ||
2486 !GFX_CRUMBLED(Tile[xx][yy]) ||
2490 int element = TILE_GFX_ELEMENT(xx, yy);
2491 int graphic = el_act2crm(element, ACTION_DEFAULT);
2493 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2494 graphic_info[graphic].anim_frames == 2)
2495 DrawLevelField(xx, yy);
2499 static int getBorderElement(int x, int y)
2503 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2504 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2505 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2506 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2507 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2508 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2509 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2511 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2512 int steel_position = (x == -1 && y == -1 ? 0 :
2513 x == lev_fieldx && y == -1 ? 1 :
2514 x == -1 && y == lev_fieldy ? 2 :
2515 x == lev_fieldx && y == lev_fieldy ? 3 :
2516 x == -1 || x == lev_fieldx ? 4 :
2517 y == -1 || y == lev_fieldy ? 5 : 6);
2519 return border[steel_position][steel_type];
2522 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2524 if (game.use_masked_elements)
2526 if (graphic != el2img(EL_EMPTY))
2527 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2529 DrawGraphicThruMask(x, y, graphic, frame);
2533 DrawGraphic(x, y, graphic, frame);
2537 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2539 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2542 void DrawScreenElement(int x, int y, int element)
2544 int mask_mode = NO_MASKING;
2546 if (game.use_masked_elements)
2548 int lx = LEVELX(x), ly = LEVELY(y);
2550 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2552 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2554 mask_mode = USE_MASKING;
2558 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2559 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2562 void DrawLevelElement(int x, int y, int element)
2564 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2565 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2568 void DrawScreenField(int x, int y)
2570 int lx = LEVELX(x), ly = LEVELY(y);
2571 int element, content;
2573 if (!IN_LEV_FIELD(lx, ly))
2575 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2578 element = getBorderElement(lx, ly);
2580 DrawScreenElement(x, y, element);
2585 element = Tile[lx][ly];
2586 content = Store[lx][ly];
2588 if (IS_MOVING(lx, ly))
2590 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2591 boolean cut_mode = NO_CUTTING;
2593 if (element == EL_QUICKSAND_EMPTYING ||
2594 element == EL_QUICKSAND_FAST_EMPTYING ||
2595 element == EL_MAGIC_WALL_EMPTYING ||
2596 element == EL_BD_MAGIC_WALL_EMPTYING ||
2597 element == EL_DC_MAGIC_WALL_EMPTYING ||
2598 element == EL_AMOEBA_DROPPING)
2599 cut_mode = CUT_ABOVE;
2600 else if (element == EL_QUICKSAND_FILLING ||
2601 element == EL_QUICKSAND_FAST_FILLING ||
2602 element == EL_MAGIC_WALL_FILLING ||
2603 element == EL_BD_MAGIC_WALL_FILLING ||
2604 element == EL_DC_MAGIC_WALL_FILLING)
2605 cut_mode = CUT_BELOW;
2607 if (cut_mode == CUT_ABOVE)
2608 DrawScreenElement(x, y, element);
2610 DrawScreenElement(x, y, EL_EMPTY);
2612 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2614 int dir = MovDir[lx][ly];
2615 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2616 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2618 if (IN_SCR_FIELD(newx, newy))
2619 DrawScreenElement(newx, newy, EL_EMPTY);
2623 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2624 else if (cut_mode == NO_CUTTING)
2625 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2628 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2630 if (cut_mode == CUT_BELOW &&
2631 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2632 DrawLevelElement(lx, ly + 1, element);
2635 if (content == EL_ACID)
2637 int dir = MovDir[lx][ly];
2638 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2639 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2641 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2643 // prevent target field from being drawn again (but without masking)
2644 // (this would happen if target field is scanned after moving element)
2645 Stop[newlx][newly] = TRUE;
2648 else if (IS_BLOCKED(lx, ly))
2653 boolean cut_mode = NO_CUTTING;
2654 int element_old, content_old;
2656 Blocked2Moving(lx, ly, &oldx, &oldy);
2659 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2660 MovDir[oldx][oldy] == MV_RIGHT);
2662 element_old = Tile[oldx][oldy];
2663 content_old = Store[oldx][oldy];
2665 if (element_old == EL_QUICKSAND_EMPTYING ||
2666 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2667 element_old == EL_MAGIC_WALL_EMPTYING ||
2668 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2669 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2670 element_old == EL_AMOEBA_DROPPING)
2671 cut_mode = CUT_ABOVE;
2673 DrawScreenElement(x, y, EL_EMPTY);
2676 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2678 else if (cut_mode == NO_CUTTING)
2679 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2682 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2685 else if (IS_DRAWABLE(element))
2686 DrawScreenElement(x, y, element);
2688 DrawScreenElement(x, y, EL_EMPTY);
2691 void DrawLevelField(int x, int y)
2693 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2694 DrawScreenField(SCREENX(x), SCREENY(y));
2695 else if (IS_MOVING(x, y))
2699 Moving2Blocked(x, y, &newx, &newy);
2700 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2701 DrawScreenField(SCREENX(newx), SCREENY(newy));
2703 else if (IS_BLOCKED(x, y))
2707 Blocked2Moving(x, y, &oldx, &oldy);
2708 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2709 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2713 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2714 int (*el2img_function)(int), boolean masked,
2715 int element_bits_draw)
2717 int element_base = map_mm_wall_element(element);
2718 int element_bits = (IS_DF_WALL(element) ?
2719 element - EL_DF_WALL_START :
2720 IS_MM_WALL(element) ?
2721 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2722 int graphic = el2img_function(element_base);
2723 int tilesize_draw = tilesize / 2;
2728 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2730 for (i = 0; i < 4; i++)
2732 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2733 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2735 if (!(element_bits_draw & (1 << i)))
2738 if (element_bits & (1 << i))
2741 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2742 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2744 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2745 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2750 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2751 tilesize_draw, tilesize_draw);
2756 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2757 boolean masked, int element_bits_draw)
2759 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2760 element, tilesize, el2edimg, masked, element_bits_draw);
2763 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2764 int (*el2img_function)(int))
2766 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2770 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2773 if (IS_MM_WALL(element))
2775 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2776 element, tilesize, el2edimg, masked, 0x000f);
2780 int graphic = el2edimg(element);
2783 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2785 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2789 void DrawSizedElement(int x, int y, int element, int tilesize)
2791 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2794 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2796 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2799 void DrawMiniElement(int x, int y, int element)
2803 graphic = el2edimg(element);
2804 DrawMiniGraphic(x, y, graphic);
2807 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2810 int x = sx + scroll_x, y = sy + scroll_y;
2812 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2813 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2814 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2815 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2817 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2820 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2822 int x = sx + scroll_x, y = sy + scroll_y;
2824 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2825 DrawMiniElement(sx, sy, EL_EMPTY);
2826 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2827 DrawMiniElement(sx, sy, Tile[x][y]);
2829 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2832 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2833 int x, int y, int xsize, int ysize,
2834 int tile_width, int tile_height)
2838 int dst_x = startx + x * tile_width;
2839 int dst_y = starty + y * tile_height;
2840 int width = graphic_info[graphic].width;
2841 int height = graphic_info[graphic].height;
2842 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2843 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2844 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2845 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2846 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2847 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2848 boolean draw_masked = graphic_info[graphic].draw_masked;
2850 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2852 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2854 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2858 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2859 inner_sx + (x - 1) * tile_width % inner_width);
2860 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2861 inner_sy + (y - 1) * tile_height % inner_height);
2864 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2867 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2871 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2872 int x, int y, int xsize, int ysize,
2875 int font_width = getFontWidth(font_nr);
2876 int font_height = getFontHeight(font_nr);
2878 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2879 font_width, font_height);
2882 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2884 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2885 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2886 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2887 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2888 boolean no_delay = (tape.warp_forward);
2889 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2890 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2891 DelayCounter anim_delay = { anim_delay_value };
2892 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2893 int font_width = getFontWidth(font_nr);
2894 int font_height = getFontHeight(font_nr);
2895 int max_xsize = level.envelope[envelope_nr].xsize;
2896 int max_ysize = level.envelope[envelope_nr].ysize;
2897 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2898 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2899 int xend = max_xsize;
2900 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2901 int xstep = (xstart < xend ? 1 : 0);
2902 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2904 int end = MAX(xend - xstart, yend - ystart);
2907 for (i = start; i <= end; i++)
2909 int last_frame = end; // last frame of this "for" loop
2910 int x = xstart + i * xstep;
2911 int y = ystart + i * ystep;
2912 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2913 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2914 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2915 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2918 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2920 BlitScreenToBitmap(backbuffer);
2922 SetDrawtoField(DRAW_TO_BACKBUFFER);
2924 for (yy = 0; yy < ysize; yy++)
2925 for (xx = 0; xx < xsize; xx++)
2926 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2928 DrawTextArea(sx + font_width, sy + font_height,
2929 level.envelope[envelope_nr].text, font_nr, max_xsize,
2930 xsize - 2, ysize - 2, 0, mask_mode,
2931 level.envelope[envelope_nr].autowrap,
2932 level.envelope[envelope_nr].centered, FALSE);
2934 redraw_mask |= REDRAW_FIELD;
2937 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2940 ClearAutoRepeatKeyEvents();
2943 void ShowEnvelope(int envelope_nr)
2945 int element = EL_ENVELOPE_1 + envelope_nr;
2946 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2947 int sound_opening = element_info[element].sound[ACTION_OPENING];
2948 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2949 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2950 boolean no_delay = (tape.warp_forward);
2951 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2952 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2953 int anim_mode = graphic_info[graphic].anim_mode;
2954 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2955 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2956 boolean overlay_enabled = GetOverlayEnabled();
2958 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2960 SetOverlayEnabled(FALSE);
2963 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2965 if (anim_mode == ANIM_DEFAULT)
2966 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2968 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2971 Delay_WithScreenUpdates(wait_delay_value);
2973 WaitForEventToContinue();
2976 SetOverlayEnabled(overlay_enabled);
2978 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2980 if (anim_mode != ANIM_NONE)
2981 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2983 if (anim_mode == ANIM_DEFAULT)
2984 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2986 game.envelope_active = FALSE;
2988 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2990 redraw_mask |= REDRAW_FIELD;
2994 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2995 int xsize, int ysize)
2997 if (!global.use_envelope_request ||
2998 request.sort_priority <= 0)
3001 if (request.bitmap == NULL ||
3002 xsize > request.xsize ||
3003 ysize > request.ysize)
3005 if (request.bitmap != NULL)
3006 FreeBitmap(request.bitmap);
3008 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3010 SDL_Surface *surface = request.bitmap->surface;
3012 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3013 Fail("SDLGetNativeSurface() failed");
3016 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3018 SDLFreeBitmapTextures(request.bitmap);
3019 SDLCreateBitmapTextures(request.bitmap);
3021 // set envelope request run-time values
3024 request.xsize = xsize;
3025 request.ysize = ysize;
3028 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3030 if (global.use_envelope_request &&
3031 game.request_active_or_moving &&
3032 request.sort_priority > 0 &&
3033 drawing_target == DRAW_TO_SCREEN &&
3034 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3036 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3037 request.sx, request.sy);
3041 static void setRequestBasePosition(int *x, int *y)
3043 int sx_base, sy_base;
3045 if (request.x != -1)
3046 sx_base = request.x;
3047 else if (request.align == ALIGN_LEFT)
3049 else if (request.align == ALIGN_RIGHT)
3050 sx_base = SX + SXSIZE;
3052 sx_base = SX + SXSIZE / 2;
3054 if (request.y != -1)
3055 sy_base = request.y;
3056 else if (request.valign == VALIGN_TOP)
3058 else if (request.valign == VALIGN_BOTTOM)
3059 sy_base = SY + SYSIZE;
3061 sy_base = SY + SYSIZE / 2;
3067 static void setRequestPositionExt(int *x, int *y, int width, int height,
3068 boolean add_border_size)
3070 int border_size = request.border_size;
3071 int sx_base, sy_base;
3074 setRequestBasePosition(&sx_base, &sy_base);
3076 if (request.align == ALIGN_LEFT)
3078 else if (request.align == ALIGN_RIGHT)
3079 sx = sx_base - width;
3081 sx = sx_base - width / 2;
3083 if (request.valign == VALIGN_TOP)
3085 else if (request.valign == VALIGN_BOTTOM)
3086 sy = sy_base - height;
3088 sy = sy_base - height / 2;
3090 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3091 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3093 if (add_border_size)
3103 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3105 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3108 static void DrawEnvelopeRequest(char *text)
3110 char *text_final = text;
3111 char *text_door_style = NULL;
3112 int graphic = IMG_BACKGROUND_REQUEST;
3113 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3114 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3115 int font_nr = FONT_REQUEST;
3116 int font_width = getFontWidth(font_nr);
3117 int font_height = getFontHeight(font_nr);
3118 int border_size = request.border_size;
3119 int line_spacing = request.line_spacing;
3120 int line_height = font_height + line_spacing;
3121 int max_text_width = request.width - 2 * border_size;
3122 int max_text_height = request.height - 2 * border_size;
3123 int line_length = max_text_width / font_width;
3124 int max_lines = max_text_height / line_height;
3125 int text_width = line_length * font_width;
3126 int width = request.width;
3127 int height = request.height;
3128 int tile_size = MAX(request.step_offset, 1);
3129 int x_steps = width / tile_size;
3130 int y_steps = height / tile_size;
3131 int sx_offset = border_size;
3132 int sy_offset = border_size;
3136 if (request.centered)
3137 sx_offset = (request.width - text_width) / 2;
3139 if (request.wrap_single_words && !request.autowrap)
3141 char *src_text_ptr, *dst_text_ptr;
3143 text_door_style = checked_malloc(2 * strlen(text) + 1);
3145 src_text_ptr = text;
3146 dst_text_ptr = text_door_style;
3148 while (*src_text_ptr)
3150 if (*src_text_ptr == ' ' ||
3151 *src_text_ptr == '?' ||
3152 *src_text_ptr == '!')
3153 *dst_text_ptr++ = '\n';
3155 if (*src_text_ptr != ' ')
3156 *dst_text_ptr++ = *src_text_ptr;
3161 *dst_text_ptr = '\0';
3163 text_final = text_door_style;
3166 setRequestPosition(&sx, &sy, FALSE);
3168 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3170 for (y = 0; y < y_steps; y++)
3171 for (x = 0; x < x_steps; x++)
3172 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3173 x, y, x_steps, y_steps,
3174 tile_size, tile_size);
3176 // force DOOR font inside door area
3177 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3179 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3180 line_length, -1, max_lines, line_spacing, mask_mode,
3181 request.autowrap, request.centered, FALSE);
3185 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3186 RedrawGadget(tool_gadget[i]);
3188 // store readily prepared envelope request for later use when animating
3189 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3191 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3193 if (text_door_style)
3194 free(text_door_style);
3197 static void AnimateEnvelopeRequest(int anim_mode, int action)
3199 int graphic = IMG_BACKGROUND_REQUEST;
3200 boolean draw_masked = graphic_info[graphic].draw_masked;
3201 int delay_value_normal = request.step_delay;
3202 int delay_value_fast = delay_value_normal / 2;
3203 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3204 boolean no_delay = (tape.warp_forward);
3205 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3206 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3207 DelayCounter anim_delay = { anim_delay_value };
3209 int tile_size = MAX(request.step_offset, 1);
3210 int max_xsize = request.width / tile_size;
3211 int max_ysize = request.height / tile_size;
3212 int max_xsize_inner = max_xsize - 2;
3213 int max_ysize_inner = max_ysize - 2;
3215 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3216 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3217 int xend = max_xsize_inner;
3218 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3219 int xstep = (xstart < xend ? 1 : 0);
3220 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3222 int end = MAX(xend - xstart, yend - ystart);
3225 if (setup.quick_doors)
3232 for (i = start; i <= end; i++)
3234 int last_frame = end; // last frame of this "for" loop
3235 int x = xstart + i * xstep;
3236 int y = ystart + i * ystep;
3237 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3238 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3239 int xsize_size_left = (xsize - 1) * tile_size;
3240 int ysize_size_top = (ysize - 1) * tile_size;
3241 int max_xsize_pos = (max_xsize - 1) * tile_size;
3242 int max_ysize_pos = (max_ysize - 1) * tile_size;
3243 int width = xsize * tile_size;
3244 int height = ysize * tile_size;
3249 setRequestPosition(&src_x, &src_y, FALSE);
3250 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3252 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3254 for (yy = 0; yy < 2; yy++)
3256 for (xx = 0; xx < 2; xx++)
3258 int src_xx = src_x + xx * max_xsize_pos;
3259 int src_yy = src_y + yy * max_ysize_pos;
3260 int dst_xx = dst_x + xx * xsize_size_left;
3261 int dst_yy = dst_y + yy * ysize_size_top;
3262 int xx_size = (xx ? tile_size : xsize_size_left);
3263 int yy_size = (yy ? tile_size : ysize_size_top);
3266 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3267 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3269 BlitBitmap(bitmap_db_store_2, backbuffer,
3270 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3274 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3276 redraw_mask |= REDRAW_FIELD;
3280 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3283 ClearAutoRepeatKeyEvents();
3286 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3288 int graphic = IMG_BACKGROUND_REQUEST;
3289 int sound_opening = SND_REQUEST_OPENING;
3290 int sound_closing = SND_REQUEST_CLOSING;
3291 int anim_mode_1 = request.anim_mode; // (higher priority)
3292 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3293 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3294 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3295 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3297 if (game_status == GAME_MODE_PLAYING)
3298 BlitScreenToBitmap(backbuffer);
3300 SetDrawtoField(DRAW_TO_BACKBUFFER);
3302 // SetDrawBackgroundMask(REDRAW_NONE);
3304 if (action == ACTION_OPENING)
3306 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3308 if (req_state & REQ_ASK)
3310 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3311 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3312 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3313 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3315 else if (req_state & REQ_CONFIRM)
3317 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3318 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3320 else if (req_state & REQ_PLAYER)
3322 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3323 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3324 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3325 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3328 DrawEnvelopeRequest(text);
3331 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3333 if (action == ACTION_OPENING)
3335 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3337 if (anim_mode == ANIM_DEFAULT)
3338 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3340 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3344 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3346 if (anim_mode != ANIM_NONE)
3347 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3349 if (anim_mode == ANIM_DEFAULT)
3350 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3353 game.envelope_active = FALSE;
3355 if (action == ACTION_CLOSING)
3356 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3358 // SetDrawBackgroundMask(last_draw_background_mask);
3360 redraw_mask |= REDRAW_FIELD;
3364 if (action == ACTION_CLOSING &&
3365 game_status == GAME_MODE_PLAYING &&
3366 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3367 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3370 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3372 if (IS_MM_WALL(element))
3374 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3380 int graphic = el2preimg(element);
3382 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3383 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3388 void DrawLevel(int draw_background_mask)
3392 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3393 SetDrawBackgroundMask(draw_background_mask);
3397 for (x = BX1; x <= BX2; x++)
3398 for (y = BY1; y <= BY2; y++)
3399 DrawScreenField(x, y);
3401 redraw_mask |= REDRAW_FIELD;
3404 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3409 for (x = 0; x < size_x; x++)
3410 for (y = 0; y < size_y; y++)
3411 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3413 redraw_mask |= REDRAW_FIELD;
3416 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3420 for (x = 0; x < size_x; x++)
3421 for (y = 0; y < size_y; y++)
3422 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3424 redraw_mask |= REDRAW_FIELD;
3427 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3429 boolean show_level_border = (BorderElement != EL_EMPTY);
3430 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3431 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3432 int tile_size = preview.tile_size;
3433 int preview_width = preview.xsize * tile_size;
3434 int preview_height = preview.ysize * tile_size;
3435 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3436 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3437 int real_preview_width = real_preview_xsize * tile_size;
3438 int real_preview_height = real_preview_ysize * tile_size;
3439 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3440 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3443 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3446 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3448 dst_x += (preview_width - real_preview_width) / 2;
3449 dst_y += (preview_height - real_preview_height) / 2;
3451 for (x = 0; x < real_preview_xsize; x++)
3453 for (y = 0; y < real_preview_ysize; y++)
3455 int lx = from_x + x + (show_level_border ? -1 : 0);
3456 int ly = from_y + y + (show_level_border ? -1 : 0);
3457 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3458 getBorderElement(lx, ly));
3460 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3461 element, tile_size);
3465 redraw_mask |= REDRAW_FIELD;
3468 #define MICROLABEL_EMPTY 0
3469 #define MICROLABEL_LEVEL_NAME 1
3470 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3471 #define MICROLABEL_LEVEL_AUTHOR 3
3472 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3473 #define MICROLABEL_IMPORTED_FROM 5
3474 #define MICROLABEL_IMPORTED_BY_HEAD 6
3475 #define MICROLABEL_IMPORTED_BY 7
3477 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3479 int max_text_width = SXSIZE;
3480 int font_width = getFontWidth(font_nr);
3482 if (pos->align == ALIGN_CENTER)
3483 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3484 else if (pos->align == ALIGN_RIGHT)
3485 max_text_width = pos->x;
3487 max_text_width = SXSIZE - pos->x;
3489 return max_text_width / font_width;
3492 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3494 char label_text[MAX_OUTPUT_LINESIZE + 1];
3495 int max_len_label_text;
3496 int font_nr = pos->font;
3499 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3502 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3503 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3504 mode == MICROLABEL_IMPORTED_BY_HEAD)
3505 font_nr = pos->font_alt;
3507 max_len_label_text = getMaxTextLength(pos, font_nr);
3509 if (pos->size != -1)
3510 max_len_label_text = pos->size;
3512 for (i = 0; i < max_len_label_text; i++)
3513 label_text[i] = ' ';
3514 label_text[max_len_label_text] = '\0';
3516 if (strlen(label_text) > 0)
3517 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3520 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3521 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3522 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3523 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3524 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3525 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3526 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3527 max_len_label_text);
3528 label_text[max_len_label_text] = '\0';
3530 if (strlen(label_text) > 0)
3531 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3533 redraw_mask |= REDRAW_FIELD;
3536 static void DrawPreviewLevelLabel(int mode)
3538 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3541 static void DrawPreviewLevelInfo(int mode)
3543 if (mode == MICROLABEL_LEVEL_NAME)
3544 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3545 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3546 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3549 static void DrawPreviewLevelExt(boolean restart)
3551 static DelayCounter scroll_delay = { 0 };
3552 static DelayCounter label_delay = { 0 };
3553 static int from_x, from_y, scroll_direction;
3554 static int label_state, label_counter;
3555 boolean show_level_border = (BorderElement != EL_EMPTY);
3556 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3557 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3559 scroll_delay.value = preview.step_delay;
3560 label_delay.value = MICROLEVEL_LABEL_DELAY;
3567 if (preview.anim_mode == ANIM_CENTERED)
3569 if (level_xsize > preview.xsize)
3570 from_x = (level_xsize - preview.xsize) / 2;
3571 if (level_ysize > preview.ysize)
3572 from_y = (level_ysize - preview.ysize) / 2;
3575 from_x += preview.xoffset;
3576 from_y += preview.yoffset;
3578 scroll_direction = MV_RIGHT;
3582 DrawPreviewLevelPlayfield(from_x, from_y);
3583 DrawPreviewLevelLabel(label_state);
3585 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3586 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3588 // initialize delay counters
3589 ResetDelayCounter(&scroll_delay);
3590 ResetDelayCounter(&label_delay);
3592 if (leveldir_current->name)
3594 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3595 char label_text[MAX_OUTPUT_LINESIZE + 1];
3596 int font_nr = pos->font;
3597 int max_len_label_text = getMaxTextLength(pos, font_nr);
3599 if (pos->size != -1)
3600 max_len_label_text = pos->size;
3602 strncpy(label_text, leveldir_current->name, max_len_label_text);
3603 label_text[max_len_label_text] = '\0';
3605 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3606 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3612 // scroll preview level, if needed
3613 if (preview.anim_mode != ANIM_NONE &&
3614 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3615 DelayReached(&scroll_delay))
3617 switch (scroll_direction)
3622 from_x -= preview.step_offset;
3623 from_x = (from_x < 0 ? 0 : from_x);
3626 scroll_direction = MV_UP;
3630 if (from_x < level_xsize - preview.xsize)
3632 from_x += preview.step_offset;
3633 from_x = (from_x > level_xsize - preview.xsize ?
3634 level_xsize - preview.xsize : from_x);
3637 scroll_direction = MV_DOWN;
3643 from_y -= preview.step_offset;
3644 from_y = (from_y < 0 ? 0 : from_y);
3647 scroll_direction = MV_RIGHT;
3651 if (from_y < level_ysize - preview.ysize)
3653 from_y += preview.step_offset;
3654 from_y = (from_y > level_ysize - preview.ysize ?
3655 level_ysize - preview.ysize : from_y);
3658 scroll_direction = MV_LEFT;
3665 DrawPreviewLevelPlayfield(from_x, from_y);
3668 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3669 // redraw micro level label, if needed
3670 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3671 !strEqual(level.author, ANONYMOUS_NAME) &&
3672 !strEqual(level.author, leveldir_current->name) &&
3673 DelayReached(&label_delay))
3675 int max_label_counter = 23;
3677 if (leveldir_current->imported_from != NULL &&
3678 strlen(leveldir_current->imported_from) > 0)
3679 max_label_counter += 14;
3680 if (leveldir_current->imported_by != NULL &&
3681 strlen(leveldir_current->imported_by) > 0)
3682 max_label_counter += 14;
3684 label_counter = (label_counter + 1) % max_label_counter;
3685 label_state = (label_counter >= 0 && label_counter <= 7 ?
3686 MICROLABEL_LEVEL_NAME :
3687 label_counter >= 9 && label_counter <= 12 ?
3688 MICROLABEL_LEVEL_AUTHOR_HEAD :
3689 label_counter >= 14 && label_counter <= 21 ?
3690 MICROLABEL_LEVEL_AUTHOR :
3691 label_counter >= 23 && label_counter <= 26 ?
3692 MICROLABEL_IMPORTED_FROM_HEAD :
3693 label_counter >= 28 && label_counter <= 35 ?
3694 MICROLABEL_IMPORTED_FROM :
3695 label_counter >= 37 && label_counter <= 40 ?
3696 MICROLABEL_IMPORTED_BY_HEAD :
3697 label_counter >= 42 && label_counter <= 49 ?
3698 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3700 if (leveldir_current->imported_from == NULL &&
3701 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3702 label_state == MICROLABEL_IMPORTED_FROM))
3703 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3704 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3706 DrawPreviewLevelLabel(label_state);
3710 void DrawPreviewPlayers(void)
3712 if (game_status != GAME_MODE_MAIN)
3715 // do not draw preview players if level preview redefined, but players aren't
3716 if (preview.redefined && !menu.main.preview_players.redefined)
3719 boolean player_found[MAX_PLAYERS];
3720 int num_players = 0;
3723 for (i = 0; i < MAX_PLAYERS; i++)
3724 player_found[i] = FALSE;
3726 // check which players can be found in the level (simple approach)
3727 for (x = 0; x < lev_fieldx; x++)
3729 for (y = 0; y < lev_fieldy; y++)
3731 int element = level.field[x][y];
3733 if (IS_PLAYER_ELEMENT(element))
3735 int player_nr = GET_PLAYER_NR(element);
3737 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3739 if (!player_found[player_nr])
3742 player_found[player_nr] = TRUE;
3747 struct TextPosInfo *pos = &menu.main.preview_players;
3748 int tile_size = pos->tile_size;
3749 int border_size = pos->border_size;
3750 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3751 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3752 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3753 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3754 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3755 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3756 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3757 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3758 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3759 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3760 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3761 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3763 // clear area in which the players will be drawn
3764 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3765 max_players_width, max_players_height);
3767 if (!network.enabled && !setup.team_mode)
3770 // only draw players if level is suited for team mode
3771 if (num_players < 2)
3774 // draw all players that were found in the level
3775 for (i = 0; i < MAX_PLAYERS; i++)
3777 if (player_found[i])
3779 int graphic = el2img(EL_PLAYER_1 + i);
3781 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3783 xpos += player_xoffset;
3784 ypos += player_yoffset;
3789 void DrawPreviewLevelInitial(void)
3791 DrawPreviewLevelExt(TRUE);
3792 DrawPreviewPlayers();
3795 void DrawPreviewLevelAnimation(void)
3797 DrawPreviewLevelExt(FALSE);
3800 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3801 int border_size, int font_nr)
3803 int graphic = el2img(EL_PLAYER_1 + player_nr);
3804 int font_height = getFontHeight(font_nr);
3805 int player_height = MAX(tile_size, font_height);
3806 int xoffset_text = tile_size + border_size;
3807 int yoffset_text = (player_height - font_height) / 2;
3808 int yoffset_graphic = (player_height - tile_size) / 2;
3809 char *player_name = getNetworkPlayerName(player_nr + 1);
3811 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3813 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3816 static void DrawNetworkPlayersExt(boolean force)
3818 if (game_status != GAME_MODE_MAIN)
3821 if (!network.connected && !force)
3824 // do not draw network players if level preview redefined, but players aren't
3825 if (preview.redefined && !menu.main.network_players.redefined)
3828 int num_players = 0;
3831 for (i = 0; i < MAX_PLAYERS; i++)
3832 if (stored_player[i].connected_network)
3835 struct TextPosInfo *pos = &menu.main.network_players;
3836 int tile_size = pos->tile_size;
3837 int border_size = pos->border_size;
3838 int xoffset_text = tile_size + border_size;
3839 int font_nr = pos->font;
3840 int font_width = getFontWidth(font_nr);
3841 int font_height = getFontHeight(font_nr);
3842 int player_height = MAX(tile_size, font_height);
3843 int player_yoffset = player_height + border_size;
3844 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3845 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3846 int all_players_height = num_players * player_yoffset - border_size;
3847 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3848 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3849 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3851 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3852 max_players_width, max_players_height);
3854 // first draw local network player ...
3855 for (i = 0; i < MAX_PLAYERS; i++)
3857 if (stored_player[i].connected_network &&
3858 stored_player[i].connected_locally)
3860 char *player_name = getNetworkPlayerName(i + 1);
3861 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3862 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3864 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3866 ypos += player_yoffset;
3870 // ... then draw all other network players
3871 for (i = 0; i < MAX_PLAYERS; i++)
3873 if (stored_player[i].connected_network &&
3874 !stored_player[i].connected_locally)
3876 char *player_name = getNetworkPlayerName(i + 1);
3877 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3878 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3880 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3882 ypos += player_yoffset;
3887 void DrawNetworkPlayers(void)
3889 DrawNetworkPlayersExt(FALSE);
3892 void ClearNetworkPlayers(void)
3894 DrawNetworkPlayersExt(TRUE);
3897 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3898 int graphic, int lx, int ly,
3901 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3903 if (mask_mode == USE_MASKING)
3904 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3906 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3909 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3910 int graphic, int sync_frame, int mask_mode)
3912 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3914 if (mask_mode == USE_MASKING)
3915 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3917 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3920 static void DrawGraphicAnimation(int x, int y, int graphic)
3922 int lx = LEVELX(x), ly = LEVELY(y);
3923 int mask_mode = NO_MASKING;
3925 if (!IN_SCR_FIELD(x, y))
3928 if (game.use_masked_elements)
3930 if (Tile[lx][ly] != EL_EMPTY)
3932 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3934 mask_mode = USE_MASKING;
3938 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3939 graphic, lx, ly, mask_mode);
3941 MarkTileDirty(x, y);
3944 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3946 int lx = LEVELX(x), ly = LEVELY(y);
3947 int mask_mode = NO_MASKING;
3949 if (!IN_SCR_FIELD(x, y))
3952 if (game.use_masked_elements)
3954 if (Tile[lx][ly] != EL_EMPTY)
3956 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3958 mask_mode = USE_MASKING;
3962 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3963 graphic, lx, ly, mask_mode);
3965 MarkTileDirty(x, y);
3968 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3970 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3973 void DrawLevelElementAnimation(int x, int y, int element)
3975 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3977 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3980 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3982 int sx = SCREENX(x), sy = SCREENY(y);
3984 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3987 if (Tile[x][y] == EL_EMPTY)
3988 graphic = el2img(GfxElementEmpty[x][y]);
3990 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3993 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3996 DrawGraphicAnimation(sx, sy, graphic);
3999 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4000 DrawLevelFieldCrumbled(x, y);
4002 if (GFX_CRUMBLED(Tile[x][y]))
4003 DrawLevelFieldCrumbled(x, y);
4007 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4009 int sx = SCREENX(x), sy = SCREENY(y);
4012 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4015 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4017 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4020 DrawGraphicAnimation(sx, sy, graphic);
4022 if (GFX_CRUMBLED(element))
4023 DrawLevelFieldCrumbled(x, y);
4026 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4028 if (player->use_murphy)
4030 // this works only because currently only one player can be "murphy" ...
4031 static int last_horizontal_dir = MV_LEFT;
4032 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4034 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4035 last_horizontal_dir = move_dir;
4037 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4039 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4041 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4047 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4050 static boolean equalGraphics(int graphic1, int graphic2)
4052 struct GraphicInfo *g1 = &graphic_info[graphic1];
4053 struct GraphicInfo *g2 = &graphic_info[graphic2];
4055 return (g1->bitmap == g2->bitmap &&
4056 g1->src_x == g2->src_x &&
4057 g1->src_y == g2->src_y &&
4058 g1->anim_frames == g2->anim_frames &&
4059 g1->anim_delay == g2->anim_delay &&
4060 g1->anim_mode == g2->anim_mode);
4063 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4067 DRAW_PLAYER_STAGE_INIT = 0,
4068 DRAW_PLAYER_STAGE_LAST_FIELD,
4069 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4070 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4071 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4072 DRAW_PLAYER_STAGE_PLAYER,
4074 DRAW_PLAYER_STAGE_PLAYER,
4075 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4077 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4078 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4080 NUM_DRAW_PLAYER_STAGES
4083 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4085 static int static_last_player_graphic[MAX_PLAYERS];
4086 static int static_last_player_frame[MAX_PLAYERS];
4087 static boolean static_player_is_opaque[MAX_PLAYERS];
4088 static boolean draw_player[MAX_PLAYERS];
4089 int pnr = player->index_nr;
4091 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4093 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4094 static_last_player_frame[pnr] = player->Frame;
4095 static_player_is_opaque[pnr] = FALSE;
4097 draw_player[pnr] = TRUE;
4100 if (!draw_player[pnr])
4104 if (!IN_LEV_FIELD(player->jx, player->jy))
4106 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4107 Debug("draw:DrawPlayerExt", "This should never happen!");
4109 draw_player[pnr] = FALSE;
4115 int last_player_graphic = static_last_player_graphic[pnr];
4116 int last_player_frame = static_last_player_frame[pnr];
4117 boolean player_is_opaque = static_player_is_opaque[pnr];
4119 int jx = player->jx;
4120 int jy = player->jy;
4121 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4122 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4123 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4124 int last_jx = (player->is_moving ? jx - dx : jx);
4125 int last_jy = (player->is_moving ? jy - dy : jy);
4126 int next_jx = jx + dx;
4127 int next_jy = jy + dy;
4128 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4129 int sx = SCREENX(jx);
4130 int sy = SCREENY(jy);
4131 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4132 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4133 int element = Tile[jx][jy];
4134 int last_element = Tile[last_jx][last_jy];
4135 int action = (player->is_pushing ? ACTION_PUSHING :
4136 player->is_digging ? ACTION_DIGGING :
4137 player->is_collecting ? ACTION_COLLECTING :
4138 player->is_moving ? ACTION_MOVING :
4139 player->is_snapping ? ACTION_SNAPPING :
4140 player->is_dropping ? ACTION_DROPPING :
4141 player->is_waiting ? player->action_waiting :
4144 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4146 // ------------------------------------------------------------------------
4147 // initialize drawing the player
4148 // ------------------------------------------------------------------------
4150 draw_player[pnr] = FALSE;
4152 // GfxElement[][] is set to the element the player is digging or collecting;
4153 // remove also for off-screen player if the player is not moving anymore
4154 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4155 GfxElement[jx][jy] = EL_UNDEFINED;
4157 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4160 if (element == EL_EXPLOSION)
4163 InitPlayerGfxAnimation(player, action, move_dir);
4165 draw_player[pnr] = TRUE;
4167 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4169 // ------------------------------------------------------------------------
4170 // draw things in the field the player is leaving, if needed
4171 // ------------------------------------------------------------------------
4173 if (!IN_SCR_FIELD(sx, sy))
4174 draw_player[pnr] = FALSE;
4176 if (!player->is_moving)
4179 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4181 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4183 if (last_element == EL_DYNAMITE_ACTIVE ||
4184 last_element == EL_EM_DYNAMITE_ACTIVE ||
4185 last_element == EL_SP_DISK_RED_ACTIVE)
4186 DrawDynamite(last_jx, last_jy);
4188 DrawLevelFieldThruMask(last_jx, last_jy);
4190 else if (last_element == EL_DYNAMITE_ACTIVE ||
4191 last_element == EL_EM_DYNAMITE_ACTIVE ||
4192 last_element == EL_SP_DISK_RED_ACTIVE)
4193 DrawDynamite(last_jx, last_jy);
4195 DrawLevelField(last_jx, last_jy);
4197 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4198 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4200 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4202 // ------------------------------------------------------------------------
4203 // draw things behind the player, if needed
4204 // ------------------------------------------------------------------------
4208 DrawLevelElement(jx, jy, Back[jx][jy]);
4213 if (IS_ACTIVE_BOMB(element))
4215 DrawLevelElement(jx, jy, EL_EMPTY);
4220 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4222 int old_element = GfxElement[jx][jy];
4223 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4224 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4226 if (GFX_CRUMBLED(old_element))
4227 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4229 DrawScreenGraphic(sx, sy, old_graphic, frame);
4231 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4232 static_player_is_opaque[pnr] = TRUE;
4236 GfxElement[jx][jy] = EL_UNDEFINED;
4238 // make sure that pushed elements are drawn with correct frame rate
4239 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4241 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4242 GfxFrame[jx][jy] = player->StepFrame;
4244 DrawLevelField(jx, jy);
4247 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4249 // ------------------------------------------------------------------------
4250 // draw things the player is pushing, if needed
4251 // ------------------------------------------------------------------------
4253 if (!player->is_pushing || !player->is_moving)
4256 int gfx_frame = GfxFrame[jx][jy];
4258 if (!IS_MOVING(jx, jy)) // push movement already finished
4260 element = Tile[next_jx][next_jy];
4261 gfx_frame = GfxFrame[next_jx][next_jy];
4264 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4265 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4266 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4268 // draw background element under pushed element (like the Sokoban field)
4269 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4271 // this allows transparent pushing animation over non-black background
4274 DrawLevelElement(jx, jy, Back[jx][jy]);
4276 DrawLevelElement(jx, jy, EL_EMPTY);
4278 if (Back[next_jx][next_jy])
4279 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4281 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4283 else if (Back[next_jx][next_jy])
4284 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4286 int px = SCREENX(jx), py = SCREENY(jy);
4287 int pxx = (TILEX - ABS(sxx)) * dx;
4288 int pyy = (TILEY - ABS(syy)) * dy;
4291 // do not draw (EM style) pushing animation when pushing is finished
4292 // (two-tile animations usually do not contain start and end frame)
4293 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4294 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4296 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4298 // masked drawing is needed for EMC style (double) movement graphics
4299 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4300 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4303 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4305 // ------------------------------------------------------------------------
4306 // draw player himself
4307 // ------------------------------------------------------------------------
4309 int graphic = getPlayerGraphic(player, move_dir);
4311 // in the case of changed player action or direction, prevent the current
4312 // animation frame from being restarted for identical animations
4313 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4314 player->Frame = last_player_frame;
4316 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4318 if (player_is_opaque)
4319 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4321 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4323 if (SHIELD_ON(player))
4325 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4326 IMG_SHIELD_NORMAL_ACTIVE);
4327 frame = getGraphicAnimationFrame(graphic, -1);
4329 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4332 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4334 // ------------------------------------------------------------------------
4335 // draw things in front of player (active dynamite or dynabombs)
4336 // ------------------------------------------------------------------------
4338 if (IS_ACTIVE_BOMB(element))
4340 int graphic = el2img(element);
4341 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4343 if (game.emulation == EMU_SUPAPLEX)
4344 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4346 DrawGraphicThruMask(sx, sy, graphic, frame);
4349 if (player_is_moving && last_element == EL_EXPLOSION)
4351 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4352 GfxElement[last_jx][last_jy] : EL_EMPTY);
4353 int graphic = el_act2img(element, ACTION_EXPLODING);
4354 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4355 int phase = ExplodePhase[last_jx][last_jy] - 1;
4356 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4359 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4362 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4364 // ------------------------------------------------------------------------
4365 // draw elements the player is just walking/passing through/under
4366 // ------------------------------------------------------------------------
4368 if (player_is_moving)
4370 // handle the field the player is leaving ...
4371 if (IS_ACCESSIBLE_INSIDE(last_element))
4372 DrawLevelField(last_jx, last_jy);
4373 else if (IS_ACCESSIBLE_UNDER(last_element))
4374 DrawLevelFieldThruMask(last_jx, last_jy);
4377 // do not redraw accessible elements if the player is just pushing them
4378 if (!player_is_moving || !player->is_pushing)
4380 // ... and the field the player is entering
4381 if (IS_ACCESSIBLE_INSIDE(element))
4382 DrawLevelField(jx, jy);
4383 else if (IS_ACCESSIBLE_UNDER(element))
4384 DrawLevelFieldThruMask(jx, jy);
4387 MarkTileDirty(sx, sy);
4391 void DrawPlayer(struct PlayerInfo *player)
4395 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4396 DrawPlayerExt(player, i);
4399 void DrawAllPlayers(void)
4403 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4404 for (j = 0; j < MAX_PLAYERS; j++)
4405 if (stored_player[j].active)
4406 DrawPlayerExt(&stored_player[j], i);
4409 void DrawPlayerField(int x, int y)
4411 if (!IS_PLAYER(x, y))
4414 DrawPlayer(PLAYERINFO(x, y));
4417 // ----------------------------------------------------------------------------
4419 void WaitForEventToContinue(void)
4421 boolean first_wait = TRUE;
4422 boolean still_wait = TRUE;
4424 if (program.headless)
4427 // simulate releasing mouse button over last gadget, if still pressed
4429 HandleGadgets(-1, -1, 0);
4431 button_status = MB_RELEASED;
4434 ClearPlayerAction();
4440 if (NextValidEvent(&event))
4444 case EVENT_BUTTONPRESS:
4445 case EVENT_FINGERPRESS:
4449 case EVENT_BUTTONRELEASE:
4450 case EVENT_FINGERRELEASE:
4451 still_wait = first_wait;
4454 case EVENT_KEYPRESS:
4455 case SDL_CONTROLLERBUTTONDOWN:
4456 case SDL_JOYBUTTONDOWN:
4461 HandleOtherEvents(&event);
4465 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4470 if (!PendingEvent())
4475 #define MAX_REQUEST_LINES 13
4476 #define MAX_REQUEST_LINE_FONT1_LEN 7
4477 #define MAX_REQUEST_LINE_FONT2_LEN 10
4479 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4481 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4483 int draw_buffer_last = GetDrawtoField();
4484 int width = request.width;
4485 int height = request.height;
4489 // when showing request dialog after game ended, deactivate game panel
4490 if (game_just_ended)
4491 game.panel.active = FALSE;
4493 game.request_active = TRUE;
4495 setRequestPosition(&sx, &sy, FALSE);
4497 button_status = MB_RELEASED;
4499 request_gadget_id = -1;
4504 boolean event_handled = FALSE;
4506 if (game_just_ended)
4508 SetDrawtoField(draw_buffer_game);
4510 HandleGameActions();
4512 SetDrawtoField(DRAW_TO_BACKBUFFER);
4514 if (global.use_envelope_request)
4516 // copy current state of request area to middle of playfield area
4517 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4525 while (NextValidEvent(&event))
4527 event_handled = TRUE;
4531 case EVENT_BUTTONPRESS:
4532 case EVENT_BUTTONRELEASE:
4533 case EVENT_MOTIONNOTIFY:
4537 if (event.type == EVENT_MOTIONNOTIFY)
4542 motion_status = TRUE;
4543 mx = ((MotionEvent *) &event)->x;
4544 my = ((MotionEvent *) &event)->y;
4548 motion_status = FALSE;
4549 mx = ((ButtonEvent *) &event)->x;
4550 my = ((ButtonEvent *) &event)->y;
4551 if (event.type == EVENT_BUTTONPRESS)
4552 button_status = ((ButtonEvent *) &event)->button;
4554 button_status = MB_RELEASED;
4557 // this sets 'request_gadget_id'
4558 HandleGadgets(mx, my, button_status);
4560 switch (request_gadget_id)
4562 case TOOL_CTRL_ID_YES:
4563 case TOOL_CTRL_ID_TOUCH_YES:
4566 case TOOL_CTRL_ID_NO:
4567 case TOOL_CTRL_ID_TOUCH_NO:
4570 case TOOL_CTRL_ID_CONFIRM:
4571 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4572 result = TRUE | FALSE;
4575 case TOOL_CTRL_ID_PLAYER_1:
4578 case TOOL_CTRL_ID_PLAYER_2:
4581 case TOOL_CTRL_ID_PLAYER_3:
4584 case TOOL_CTRL_ID_PLAYER_4:
4589 // only check clickable animations if no request gadget clicked
4590 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4597 case SDL_WINDOWEVENT:
4598 HandleWindowEvent((WindowEvent *) &event);
4601 case SDL_APP_WILLENTERBACKGROUND:
4602 case SDL_APP_DIDENTERBACKGROUND:
4603 case SDL_APP_WILLENTERFOREGROUND:
4604 case SDL_APP_DIDENTERFOREGROUND:
4605 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4608 case EVENT_KEYPRESS:
4610 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4615 if (req_state & REQ_CONFIRM)
4624 #if defined(KSYM_Rewind)
4625 case KSYM_Rewind: // for Amazon Fire TV remote
4634 #if defined(KSYM_FastForward)
4635 case KSYM_FastForward: // for Amazon Fire TV remote
4641 HandleKeysDebug(key, KEY_PRESSED);
4645 if (req_state & REQ_PLAYER)
4647 int old_player_nr = setup.network_player_nr;
4650 result = old_player_nr + 1;
4655 result = old_player_nr + 1;
4686 case EVENT_FINGERRELEASE:
4687 case EVENT_KEYRELEASE:
4688 ClearPlayerAction();
4691 case SDL_CONTROLLERBUTTONDOWN:
4692 switch (event.cbutton.button)
4694 case SDL_CONTROLLER_BUTTON_A:
4695 case SDL_CONTROLLER_BUTTON_X:
4696 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4697 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4701 case SDL_CONTROLLER_BUTTON_B:
4702 case SDL_CONTROLLER_BUTTON_Y:
4703 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4704 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4705 case SDL_CONTROLLER_BUTTON_BACK:
4710 if (req_state & REQ_PLAYER)
4712 int old_player_nr = setup.network_player_nr;
4715 result = old_player_nr + 1;
4717 switch (event.cbutton.button)
4719 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4720 case SDL_CONTROLLER_BUTTON_Y:
4724 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4725 case SDL_CONTROLLER_BUTTON_B:
4729 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4730 case SDL_CONTROLLER_BUTTON_A:
4734 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4735 case SDL_CONTROLLER_BUTTON_X:
4746 case SDL_CONTROLLERBUTTONUP:
4747 HandleJoystickEvent(&event);
4748 ClearPlayerAction();
4752 HandleOtherEvents(&event);
4757 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4759 int joy = AnyJoystick();
4761 if (joy & JOY_BUTTON_1)
4763 else if (joy & JOY_BUTTON_2)
4766 else if (AnyJoystick())
4768 int joy = AnyJoystick();
4770 if (req_state & REQ_PLAYER)
4774 else if (joy & JOY_RIGHT)
4776 else if (joy & JOY_DOWN)
4778 else if (joy & JOY_LEFT)
4785 if (game_just_ended)
4787 if (global.use_envelope_request)
4789 // copy back current state of pressed buttons inside request area
4790 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4794 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4800 SetDrawtoField(draw_buffer_last);
4802 game.request_active = FALSE;
4807 static boolean RequestDoor(char *text, unsigned int req_state)
4809 int draw_buffer_last = GetDrawtoField();
4810 unsigned int old_door_state;
4811 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4812 int font_nr = FONT_TEXT_2;
4817 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4819 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4820 font_nr = FONT_TEXT_1;
4823 if (game_status == GAME_MODE_PLAYING)
4824 BlitScreenToBitmap(backbuffer);
4826 // disable deactivated drawing when quick-loading level tape recording
4827 if (tape.playing && tape.deactivate_display)
4828 TapeDeactivateDisplayOff(TRUE);
4830 SetMouseCursor(CURSOR_DEFAULT);
4832 // pause network game while waiting for request to answer
4833 if (network.enabled &&
4834 game_status == GAME_MODE_PLAYING &&
4835 !game.all_players_gone &&
4836 req_state & REQUEST_WAIT_FOR_INPUT)
4837 SendToServer_PausePlaying();
4839 old_door_state = GetDoorState();
4841 // simulate releasing mouse button over last gadget, if still pressed
4843 HandleGadgets(-1, -1, 0);
4847 // draw released gadget before proceeding
4850 if (old_door_state & DOOR_OPEN_1)
4852 CloseDoor(DOOR_CLOSE_1);
4854 // save old door content
4855 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4856 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4859 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4860 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4862 // clear door drawing field
4863 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4865 // force DOOR font inside door area
4866 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4868 // write text for request
4869 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4871 char text_line[max_request_line_len + 1];
4877 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4879 tc = *(text_ptr + tx);
4880 // if (!tc || tc == ' ')
4881 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4885 if ((tc == '?' || tc == '!') && tl == 0)
4895 strncpy(text_line, text_ptr, tl);
4898 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4899 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4900 text_line, font_nr);
4902 text_ptr += tl + (tc == ' ' ? 1 : 0);
4903 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4908 if (req_state & REQ_ASK)
4910 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4911 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4912 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4913 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4915 else if (req_state & REQ_CONFIRM)
4917 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4918 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4920 else if (req_state & REQ_PLAYER)
4922 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4923 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4924 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4925 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4928 // copy request gadgets to door backbuffer
4929 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4931 OpenDoor(DOOR_OPEN_1);
4933 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4935 if (game_status == GAME_MODE_PLAYING)
4937 SetPanelBackground();
4938 SetDrawBackgroundMask(REDRAW_DOOR_1);
4942 SetDrawBackgroundMask(REDRAW_FIELD);
4948 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4950 // ---------- handle request buttons ----------
4951 result = RequestHandleEvents(req_state, draw_buffer_last);
4955 if (!(req_state & REQ_STAY_OPEN))
4957 CloseDoor(DOOR_CLOSE_1);
4959 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4960 (req_state & REQ_REOPEN))
4961 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4966 if (game_status == GAME_MODE_PLAYING)
4968 SetPanelBackground();
4969 SetDrawBackgroundMask(REDRAW_DOOR_1);
4973 SetDrawBackgroundMask(REDRAW_FIELD);
4976 // continue network game after request
4977 if (network.enabled &&
4978 game_status == GAME_MODE_PLAYING &&
4979 !game.all_players_gone &&
4980 req_state & REQUEST_WAIT_FOR_INPUT)
4981 SendToServer_ContinuePlaying();
4983 // restore deactivated drawing when quick-loading level tape recording
4984 if (tape.playing && tape.deactivate_display)
4985 TapeDeactivateDisplayOn();
4990 static boolean RequestEnvelope(char *text, unsigned int req_state)
4992 int draw_buffer_last = GetDrawtoField();
4995 if (game_status == GAME_MODE_PLAYING)
4996 BlitScreenToBitmap(backbuffer);
4998 // disable deactivated drawing when quick-loading level tape recording
4999 if (tape.playing && tape.deactivate_display)
5000 TapeDeactivateDisplayOff(TRUE);
5002 SetMouseCursor(CURSOR_DEFAULT);
5004 // pause network game while waiting for request to answer
5005 if (network.enabled &&
5006 game_status == GAME_MODE_PLAYING &&
5007 !game.all_players_gone &&
5008 req_state & REQUEST_WAIT_FOR_INPUT)
5009 SendToServer_PausePlaying();
5011 // simulate releasing mouse button over last gadget, if still pressed
5013 HandleGadgets(-1, -1, 0);
5017 // (replace with setting corresponding request background)
5018 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5019 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5021 // clear door drawing field
5022 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5024 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5026 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5028 if (game_status == GAME_MODE_PLAYING)
5030 SetPanelBackground();
5031 SetDrawBackgroundMask(REDRAW_DOOR_1);
5035 SetDrawBackgroundMask(REDRAW_FIELD);
5041 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5043 // ---------- handle request buttons ----------
5044 result = RequestHandleEvents(req_state, draw_buffer_last);
5048 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5052 if (game_status == GAME_MODE_PLAYING)
5054 SetPanelBackground();
5055 SetDrawBackgroundMask(REDRAW_DOOR_1);
5059 SetDrawBackgroundMask(REDRAW_FIELD);
5062 // continue network game after request
5063 if (network.enabled &&
5064 game_status == GAME_MODE_PLAYING &&
5065 !game.all_players_gone &&
5066 req_state & REQUEST_WAIT_FOR_INPUT)
5067 SendToServer_ContinuePlaying();
5069 // restore deactivated drawing when quick-loading level tape recording
5070 if (tape.playing && tape.deactivate_display)
5071 TapeDeactivateDisplayOn();
5076 boolean Request(char *text, unsigned int req_state)
5078 boolean overlay_enabled = GetOverlayEnabled();
5081 game.request_active_or_moving = TRUE;
5083 SetOverlayEnabled(FALSE);
5085 if (global.use_envelope_request)
5086 result = RequestEnvelope(text, req_state);
5088 result = RequestDoor(text, req_state);
5090 SetOverlayEnabled(overlay_enabled);
5092 game.request_active_or_moving = FALSE;
5097 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5099 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5100 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5103 if (dpo1->sort_priority != dpo2->sort_priority)
5104 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5106 compare_result = dpo1->nr - dpo2->nr;
5108 return compare_result;
5111 void InitGraphicCompatibilityInfo_Doors(void)
5117 struct DoorInfo *door;
5121 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5122 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5124 { -1, -1, -1, NULL }
5126 struct Rect door_rect_list[] =
5128 { DX, DY, DXSIZE, DYSIZE },
5129 { VX, VY, VXSIZE, VYSIZE }
5133 for (i = 0; doors[i].door_token != -1; i++)
5135 int door_token = doors[i].door_token;
5136 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5137 int part_1 = doors[i].part_1;
5138 int part_8 = doors[i].part_8;
5139 int part_2 = part_1 + 1;
5140 int part_3 = part_1 + 2;
5141 struct DoorInfo *door = doors[i].door;
5142 struct Rect *door_rect = &door_rect_list[door_index];
5143 boolean door_gfx_redefined = FALSE;
5145 // check if any door part graphic definitions have been redefined
5147 for (j = 0; door_part_controls[j].door_token != -1; j++)
5149 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5150 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5152 if (dpc->door_token == door_token && fi->redefined)
5153 door_gfx_redefined = TRUE;
5156 // check for old-style door graphic/animation modifications
5158 if (!door_gfx_redefined)
5160 if (door->anim_mode & ANIM_STATIC_PANEL)
5162 door->panel.step_xoffset = 0;
5163 door->panel.step_yoffset = 0;
5166 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5168 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5169 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5170 int num_door_steps, num_panel_steps;
5172 // remove door part graphics other than the two default wings
5174 for (j = 0; door_part_controls[j].door_token != -1; j++)
5176 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5177 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5179 if (dpc->graphic >= part_3 &&
5180 dpc->graphic <= part_8)
5184 // set graphics and screen positions of the default wings
5186 g_part_1->width = door_rect->width;
5187 g_part_1->height = door_rect->height;
5188 g_part_2->width = door_rect->width;
5189 g_part_2->height = door_rect->height;
5190 g_part_2->src_x = door_rect->width;
5191 g_part_2->src_y = g_part_1->src_y;
5193 door->part_2.x = door->part_1.x;
5194 door->part_2.y = door->part_1.y;
5196 if (door->width != -1)
5198 g_part_1->width = door->width;
5199 g_part_2->width = door->width;
5201 // special treatment for graphics and screen position of right wing
5202 g_part_2->src_x += door_rect->width - door->width;
5203 door->part_2.x += door_rect->width - door->width;
5206 if (door->height != -1)
5208 g_part_1->height = door->height;
5209 g_part_2->height = door->height;
5211 // special treatment for graphics and screen position of bottom wing
5212 g_part_2->src_y += door_rect->height - door->height;
5213 door->part_2.y += door_rect->height - door->height;
5216 // set animation delays for the default wings and panels
5218 door->part_1.step_delay = door->step_delay;
5219 door->part_2.step_delay = door->step_delay;
5220 door->panel.step_delay = door->step_delay;
5222 // set animation draw order for the default wings
5224 door->part_1.sort_priority = 2; // draw left wing over ...
5225 door->part_2.sort_priority = 1; // ... right wing
5227 // set animation draw offset for the default wings
5229 if (door->anim_mode & ANIM_HORIZONTAL)
5231 door->part_1.step_xoffset = door->step_offset;
5232 door->part_1.step_yoffset = 0;
5233 door->part_2.step_xoffset = door->step_offset * -1;
5234 door->part_2.step_yoffset = 0;
5236 num_door_steps = g_part_1->width / door->step_offset;
5238 else // ANIM_VERTICAL
5240 door->part_1.step_xoffset = 0;
5241 door->part_1.step_yoffset = door->step_offset;
5242 door->part_2.step_xoffset = 0;
5243 door->part_2.step_yoffset = door->step_offset * -1;
5245 num_door_steps = g_part_1->height / door->step_offset;
5248 // set animation draw offset for the default panels
5250 if (door->step_offset > 1)
5252 num_panel_steps = 2 * door_rect->height / door->step_offset;
5253 door->panel.start_step = num_panel_steps - num_door_steps;
5254 door->panel.start_step_closing = door->panel.start_step;
5258 num_panel_steps = door_rect->height / door->step_offset;
5259 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5260 door->panel.start_step_closing = door->panel.start_step;
5261 door->panel.step_delay *= 2;
5268 void InitDoors(void)
5272 for (i = 0; door_part_controls[i].door_token != -1; i++)
5274 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5275 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5277 // initialize "start_step_opening" and "start_step_closing", if needed
5278 if (dpc->pos->start_step_opening == 0 &&
5279 dpc->pos->start_step_closing == 0)
5281 // dpc->pos->start_step_opening = dpc->pos->start_step;
5282 dpc->pos->start_step_closing = dpc->pos->start_step;
5285 // fill structure for door part draw order (sorted below)
5287 dpo->sort_priority = dpc->pos->sort_priority;
5290 // sort door part controls according to sort_priority and graphic number
5291 qsort(door_part_order, MAX_DOOR_PARTS,
5292 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5295 unsigned int OpenDoor(unsigned int door_state)
5297 if (door_state & DOOR_COPY_BACK)
5299 if (door_state & DOOR_OPEN_1)
5300 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5301 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5303 if (door_state & DOOR_OPEN_2)
5304 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5305 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5307 door_state &= ~DOOR_COPY_BACK;
5310 return MoveDoor(door_state);
5313 unsigned int CloseDoor(unsigned int door_state)
5315 unsigned int old_door_state = GetDoorState();
5317 if (!(door_state & DOOR_NO_COPY_BACK))
5319 if (old_door_state & DOOR_OPEN_1)
5320 BlitBitmap(backbuffer, bitmap_db_door_1,
5321 DX, DY, DXSIZE, DYSIZE, 0, 0);
5323 if (old_door_state & DOOR_OPEN_2)
5324 BlitBitmap(backbuffer, bitmap_db_door_2,
5325 VX, VY, VXSIZE, VYSIZE, 0, 0);
5327 door_state &= ~DOOR_NO_COPY_BACK;
5330 return MoveDoor(door_state);
5333 unsigned int GetDoorState(void)
5335 return MoveDoor(DOOR_GET_STATE);
5338 unsigned int SetDoorState(unsigned int door_state)
5340 return MoveDoor(door_state | DOOR_SET_STATE);
5343 static int euclid(int a, int b)
5345 return (b ? euclid(b, a % b) : a);
5348 unsigned int MoveDoor(unsigned int door_state)
5350 struct Rect door_rect_list[] =
5352 { DX, DY, DXSIZE, DYSIZE },
5353 { VX, VY, VXSIZE, VYSIZE }
5355 static int door1 = DOOR_CLOSE_1;
5356 static int door2 = DOOR_CLOSE_2;
5357 DelayCounter door_delay = { 0 };
5360 if (door_state == DOOR_GET_STATE)
5361 return (door1 | door2);
5363 if (door_state & DOOR_SET_STATE)
5365 if (door_state & DOOR_ACTION_1)
5366 door1 = door_state & DOOR_ACTION_1;
5367 if (door_state & DOOR_ACTION_2)
5368 door2 = door_state & DOOR_ACTION_2;
5370 return (door1 | door2);
5373 if (!(door_state & DOOR_FORCE_REDRAW))
5375 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5376 door_state &= ~DOOR_OPEN_1;
5377 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5378 door_state &= ~DOOR_CLOSE_1;
5379 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5380 door_state &= ~DOOR_OPEN_2;
5381 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5382 door_state &= ~DOOR_CLOSE_2;
5385 if (global.autoplay_leveldir)
5387 door_state |= DOOR_NO_DELAY;
5388 door_state &= ~DOOR_CLOSE_ALL;
5391 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5392 door_state |= DOOR_NO_DELAY;
5394 if (door_state & DOOR_ACTION)
5396 boolean door_panel_drawn[NUM_DOORS];
5397 boolean panel_has_doors[NUM_DOORS];
5398 boolean door_part_skip[MAX_DOOR_PARTS];
5399 boolean door_part_done[MAX_DOOR_PARTS];
5400 boolean door_part_done_all;
5401 int num_steps[MAX_DOOR_PARTS];
5402 int max_move_delay = 0; // delay for complete animations of all doors
5403 int max_step_delay = 0; // delay (ms) between two animation frames
5404 int num_move_steps = 0; // number of animation steps for all doors
5405 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5406 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5410 for (i = 0; i < NUM_DOORS; i++)
5411 panel_has_doors[i] = FALSE;
5413 for (i = 0; i < MAX_DOOR_PARTS; i++)
5415 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5416 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5417 int door_token = dpc->door_token;
5419 door_part_done[i] = FALSE;
5420 door_part_skip[i] = (!(door_state & door_token) ||
5424 for (i = 0; i < MAX_DOOR_PARTS; i++)
5426 int nr = door_part_order[i].nr;
5427 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5428 struct DoorPartPosInfo *pos = dpc->pos;
5429 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5430 int door_token = dpc->door_token;
5431 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5432 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5433 int step_xoffset = ABS(pos->step_xoffset);
5434 int step_yoffset = ABS(pos->step_yoffset);
5435 int step_delay = pos->step_delay;
5436 int current_door_state = door_state & door_token;
5437 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5438 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5439 boolean part_opening = (is_panel ? door_closing : door_opening);
5440 int start_step = (part_opening ? pos->start_step_opening :
5441 pos->start_step_closing);
5442 float move_xsize = (step_xoffset ? g->width : 0);
5443 float move_ysize = (step_yoffset ? g->height : 0);
5444 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5445 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5446 int move_steps = (move_xsteps && move_ysteps ?
5447 MIN(move_xsteps, move_ysteps) :
5448 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5449 int move_delay = move_steps * step_delay;
5451 if (door_part_skip[nr])
5454 max_move_delay = MAX(max_move_delay, move_delay);
5455 max_step_delay = (max_step_delay == 0 ? step_delay :
5456 euclid(max_step_delay, step_delay));
5457 num_steps[nr] = move_steps;
5461 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5463 panel_has_doors[door_index] = TRUE;
5467 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5469 num_move_steps = max_move_delay / max_step_delay;
5470 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5472 door_delay.value = max_step_delay;
5474 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5476 start = num_move_steps - 1;
5480 // opening door sound has priority over simultaneously closing door
5481 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5483 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5485 if (door_state & DOOR_OPEN_1)
5486 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5487 if (door_state & DOOR_OPEN_2)
5488 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5490 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5492 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5494 if (door_state & DOOR_CLOSE_1)
5495 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5496 if (door_state & DOOR_CLOSE_2)
5497 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5501 for (k = start; k < num_move_steps; k++)
5503 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5505 door_part_done_all = TRUE;
5507 for (i = 0; i < NUM_DOORS; i++)
5508 door_panel_drawn[i] = FALSE;
5510 for (i = 0; i < MAX_DOOR_PARTS; i++)
5512 int nr = door_part_order[i].nr;
5513 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5514 struct DoorPartPosInfo *pos = dpc->pos;
5515 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5516 int door_token = dpc->door_token;
5517 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5518 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5519 boolean is_panel_and_door_has_closed = FALSE;
5520 struct Rect *door_rect = &door_rect_list[door_index];
5521 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5523 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5524 int current_door_state = door_state & door_token;
5525 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5526 boolean door_closing = !door_opening;
5527 boolean part_opening = (is_panel ? door_closing : door_opening);
5528 boolean part_closing = !part_opening;
5529 int start_step = (part_opening ? pos->start_step_opening :
5530 pos->start_step_closing);
5531 int step_delay = pos->step_delay;
5532 int step_factor = step_delay / max_step_delay;
5533 int k1 = (step_factor ? k / step_factor + 1 : k);
5534 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5535 int kk = MAX(0, k2);
5538 int src_x, src_y, src_xx, src_yy;
5539 int dst_x, dst_y, dst_xx, dst_yy;
5542 if (door_part_skip[nr])
5545 if (!(door_state & door_token))
5553 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5554 int kk_door = MAX(0, k2_door);
5555 int sync_frame = kk_door * door_delay.value;
5556 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5558 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5559 &g_src_x, &g_src_y);
5564 if (!door_panel_drawn[door_index])
5566 ClearRectangle(drawto, door_rect->x, door_rect->y,
5567 door_rect->width, door_rect->height);
5569 door_panel_drawn[door_index] = TRUE;
5572 // draw opening or closing door parts
5574 if (pos->step_xoffset < 0) // door part on right side
5577 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5580 if (dst_xx + width > door_rect->width)
5581 width = door_rect->width - dst_xx;
5583 else // door part on left side
5586 dst_xx = pos->x - kk * pos->step_xoffset;
5590 src_xx = ABS(dst_xx);
5594 width = g->width - src_xx;
5596 if (width > door_rect->width)
5597 width = door_rect->width;
5599 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5602 if (pos->step_yoffset < 0) // door part on bottom side
5605 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5608 if (dst_yy + height > door_rect->height)
5609 height = door_rect->height - dst_yy;
5611 else // door part on top side
5614 dst_yy = pos->y - kk * pos->step_yoffset;
5618 src_yy = ABS(dst_yy);
5622 height = g->height - src_yy;
5625 src_x = g_src_x + src_xx;
5626 src_y = g_src_y + src_yy;
5628 dst_x = door_rect->x + dst_xx;
5629 dst_y = door_rect->y + dst_yy;
5631 is_panel_and_door_has_closed =
5634 panel_has_doors[door_index] &&
5635 k >= num_move_steps_doors_only - 1);
5637 if (width >= 0 && width <= g->width &&
5638 height >= 0 && height <= g->height &&
5639 !is_panel_and_door_has_closed)
5641 if (is_panel || !pos->draw_masked)
5642 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5645 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5649 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5651 if ((part_opening && (width < 0 || height < 0)) ||
5652 (part_closing && (width >= g->width && height >= g->height)))
5653 door_part_done[nr] = TRUE;
5655 // continue door part animations, but not panel after door has closed
5656 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5657 door_part_done_all = FALSE;
5660 if (!(door_state & DOOR_NO_DELAY))
5664 SkipUntilDelayReached(&door_delay, &k, last_frame);
5666 // prevent OS (Windows) from complaining about program not responding
5670 if (door_part_done_all)
5674 if (!(door_state & DOOR_NO_DELAY))
5676 // wait for specified door action post delay
5677 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5678 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5679 else if (door_state & DOOR_ACTION_1)
5680 door_delay.value = door_1.post_delay;
5681 else if (door_state & DOOR_ACTION_2)
5682 door_delay.value = door_2.post_delay;
5684 while (!DelayReached(&door_delay))
5689 if (door_state & DOOR_ACTION_1)
5690 door1 = door_state & DOOR_ACTION_1;
5691 if (door_state & DOOR_ACTION_2)
5692 door2 = door_state & DOOR_ACTION_2;
5694 // draw masked border over door area
5695 DrawMaskedBorder(REDRAW_DOOR_1);
5696 DrawMaskedBorder(REDRAW_DOOR_2);
5698 ClearAutoRepeatKeyEvents();
5700 return (door1 | door2);
5703 static boolean useSpecialEditorDoor(void)
5705 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5706 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5708 // do not draw special editor door if editor border defined or redefined
5709 if (graphic_info[graphic].bitmap != NULL || redefined)
5712 // do not draw special editor door if global border defined to be empty
5713 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5716 // do not draw special editor door if viewport definitions do not match
5720 EY + EYSIZE != VY + VYSIZE)
5726 void DrawSpecialEditorDoor(void)
5728 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5729 int top_border_width = gfx1->width;
5730 int top_border_height = gfx1->height;
5731 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5732 int ex = EX - outer_border;
5733 int ey = EY - outer_border;
5734 int vy = VY - outer_border;
5735 int exsize = EXSIZE + 2 * outer_border;
5737 if (!useSpecialEditorDoor())
5740 // draw bigger level editor toolbox window
5741 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5742 top_border_width, top_border_height, ex, ey - top_border_height);
5743 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5744 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5746 redraw_mask |= REDRAW_ALL;
5749 void UndrawSpecialEditorDoor(void)
5751 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5752 int top_border_width = gfx1->width;
5753 int top_border_height = gfx1->height;
5754 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5755 int ex = EX - outer_border;
5756 int ey = EY - outer_border;
5757 int ey_top = ey - top_border_height;
5758 int exsize = EXSIZE + 2 * outer_border;
5759 int eysize = EYSIZE + 2 * outer_border;
5761 if (!useSpecialEditorDoor())
5764 // draw normal tape recorder window
5765 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5767 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5768 ex, ey_top, top_border_width, top_border_height,
5770 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5771 ex, ey, exsize, eysize, ex, ey);
5775 // if screen background is set to "[NONE]", clear editor toolbox window
5776 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5777 ClearRectangle(drawto, ex, ey, exsize, eysize);
5780 redraw_mask |= REDRAW_ALL;
5784 // ---------- new tool button stuff -------------------------------------------
5789 struct TextPosInfo *pos;
5791 boolean is_touch_button;
5793 } toolbutton_info[NUM_TOOL_BUTTONS] =
5796 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5797 TOOL_CTRL_ID_YES, FALSE, "yes"
5800 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5801 TOOL_CTRL_ID_NO, FALSE, "no"
5804 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5805 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5808 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5809 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5812 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5813 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5816 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5817 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5820 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5821 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5824 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5825 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5828 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5829 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5832 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5833 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5837 void CreateToolButtons(void)
5841 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5843 int graphic = toolbutton_info[i].graphic;
5844 struct GraphicInfo *gfx = &graphic_info[graphic];
5845 struct TextPosInfo *pos = toolbutton_info[i].pos;
5846 struct GadgetInfo *gi;
5847 Bitmap *deco_bitmap = None;
5848 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5849 unsigned int event_mask = GD_EVENT_RELEASED;
5850 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5851 int base_x = (is_touch_button ? 0 : DX);
5852 int base_y = (is_touch_button ? 0 : DY);
5853 int gd_x = gfx->src_x;
5854 int gd_y = gfx->src_y;
5855 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5856 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5861 // do not use touch buttons if overlay touch buttons are disabled
5862 if (is_touch_button && !setup.touch.overlay_buttons)
5865 if (global.use_envelope_request && !is_touch_button)
5867 setRequestPosition(&base_x, &base_y, TRUE);
5869 // check if request buttons are outside of envelope and fix, if needed
5870 if (x < 0 || x + gfx->width > request.width ||
5871 y < 0 || y + gfx->height > request.height)
5873 if (id == TOOL_CTRL_ID_YES)
5876 y = request.height - 2 * request.border_size - gfx->height;
5878 else if (id == TOOL_CTRL_ID_NO)
5880 x = request.width - 2 * request.border_size - gfx->width;
5881 y = request.height - 2 * request.border_size - gfx->height;
5883 else if (id == TOOL_CTRL_ID_CONFIRM)
5885 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5886 y = request.height - 2 * request.border_size - gfx->height;
5888 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5890 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5892 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5893 y = request.height - 2 * request.border_size - gfx->height * 2;
5895 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5896 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5901 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5903 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5905 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5906 pos->size, &deco_bitmap, &deco_x, &deco_y);
5907 deco_xpos = (gfx->width - pos->size) / 2;
5908 deco_ypos = (gfx->height - pos->size) / 2;
5911 gi = CreateGadget(GDI_CUSTOM_ID, id,
5912 GDI_IMAGE_ID, graphic,
5913 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5916 GDI_WIDTH, gfx->width,
5917 GDI_HEIGHT, gfx->height,
5918 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5919 GDI_STATE, GD_BUTTON_UNPRESSED,
5920 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5921 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5922 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5923 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5924 GDI_DECORATION_SIZE, pos->size, pos->size,
5925 GDI_DECORATION_SHIFTING, 1, 1,
5926 GDI_DIRECT_DRAW, FALSE,
5927 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5928 GDI_EVENT_MASK, event_mask,
5929 GDI_CALLBACK_ACTION, HandleToolButtons,
5933 Fail("cannot create gadget");
5935 tool_gadget[id] = gi;
5939 void FreeToolButtons(void)
5943 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5944 FreeGadget(tool_gadget[i]);
5947 static void UnmapToolButtons(void)
5951 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5952 UnmapGadget(tool_gadget[i]);
5955 static void HandleToolButtons(struct GadgetInfo *gi)
5957 request_gadget_id = gi->custom_id;
5960 static struct Mapping_EM_to_RND_object
5963 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5964 boolean is_backside; // backside of moving element
5970 em_object_mapping_list[GAME_TILE_MAX + 1] =
5973 Zborder, FALSE, FALSE,
5977 Zplayer, FALSE, FALSE,
5986 Ztank, FALSE, FALSE,
5990 Zeater, FALSE, FALSE,
5994 Zdynamite, FALSE, FALSE,
5998 Zboom, FALSE, FALSE,
6003 Xchain, FALSE, FALSE,
6004 EL_DEFAULT, ACTION_EXPLODING, -1
6007 Xboom_bug, FALSE, FALSE,
6008 EL_BUG, ACTION_EXPLODING, -1
6011 Xboom_tank, FALSE, FALSE,
6012 EL_SPACESHIP, ACTION_EXPLODING, -1
6015 Xboom_android, FALSE, FALSE,
6016 EL_EMC_ANDROID, ACTION_OTHER, -1
6019 Xboom_1, FALSE, FALSE,
6020 EL_DEFAULT, ACTION_EXPLODING, -1
6023 Xboom_2, FALSE, FALSE,
6024 EL_DEFAULT, ACTION_EXPLODING, -1
6028 Xblank, TRUE, FALSE,
6033 Xsplash_e, FALSE, FALSE,
6034 EL_ACID_SPLASH_RIGHT, -1, -1
6037 Xsplash_w, FALSE, FALSE,
6038 EL_ACID_SPLASH_LEFT, -1, -1
6042 Xplant, TRUE, FALSE,
6043 EL_EMC_PLANT, -1, -1
6046 Yplant, FALSE, FALSE,
6047 EL_EMC_PLANT, -1, -1
6051 Xacid_1, TRUE, FALSE,
6055 Xacid_2, FALSE, FALSE,
6059 Xacid_3, FALSE, FALSE,
6063 Xacid_4, FALSE, FALSE,
6067 Xacid_5, FALSE, FALSE,
6071 Xacid_6, FALSE, FALSE,
6075 Xacid_7, FALSE, FALSE,
6079 Xacid_8, FALSE, FALSE,
6084 Xfake_acid_1, TRUE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_2, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6092 Xfake_acid_3, FALSE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_4, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_5, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_6, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_7, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_8, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_1_player, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_2_player, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_3_player, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_4_player, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_5_player, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_6_player, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6141 Xfake_acid_7_player, FALSE, FALSE,
6142 EL_EMC_FAKE_ACID, -1, -1
6145 Xfake_acid_8_player, FALSE, FALSE,
6146 EL_EMC_FAKE_ACID, -1, -1
6150 Xgrass, TRUE, FALSE,
6151 EL_EMC_GRASS, -1, -1
6154 Ygrass_nB, FALSE, FALSE,
6155 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6158 Ygrass_eB, FALSE, FALSE,
6159 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6162 Ygrass_sB, FALSE, FALSE,
6163 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6166 Ygrass_wB, FALSE, FALSE,
6167 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6175 Ydirt_nB, FALSE, FALSE,
6176 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6179 Ydirt_eB, FALSE, FALSE,
6180 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6183 Ydirt_sB, FALSE, FALSE,
6184 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6187 Ydirt_wB, FALSE, FALSE,
6188 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6192 Xandroid, TRUE, FALSE,
6193 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6196 Xandroid_1_n, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6200 Xandroid_2_n, FALSE, FALSE,
6201 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6204 Xandroid_1_e, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6208 Xandroid_2_e, FALSE, FALSE,
6209 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6212 Xandroid_1_w, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6216 Xandroid_2_w, FALSE, FALSE,
6217 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6220 Xandroid_1_s, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6224 Xandroid_2_s, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6228 Yandroid_n, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6232 Yandroid_nB, FALSE, TRUE,
6233 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6236 Yandroid_ne, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6240 Yandroid_neB, FALSE, TRUE,
6241 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6244 Yandroid_e, FALSE, FALSE,
6245 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6248 Yandroid_eB, FALSE, TRUE,
6249 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6252 Yandroid_se, FALSE, FALSE,
6253 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6256 Yandroid_seB, FALSE, TRUE,
6257 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6260 Yandroid_s, FALSE, FALSE,
6261 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6264 Yandroid_sB, FALSE, TRUE,
6265 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6268 Yandroid_sw, FALSE, FALSE,
6269 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6272 Yandroid_swB, FALSE, TRUE,
6273 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6276 Yandroid_w, FALSE, FALSE,
6277 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6280 Yandroid_wB, FALSE, TRUE,
6281 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6284 Yandroid_nw, FALSE, FALSE,
6285 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6288 Yandroid_nwB, FALSE, TRUE,
6289 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6293 Xeater_n, TRUE, FALSE,
6294 EL_YAMYAM_UP, -1, -1
6297 Xeater_e, TRUE, FALSE,
6298 EL_YAMYAM_RIGHT, -1, -1
6301 Xeater_w, TRUE, FALSE,
6302 EL_YAMYAM_LEFT, -1, -1
6305 Xeater_s, TRUE, FALSE,
6306 EL_YAMYAM_DOWN, -1, -1
6309 Yeater_n, FALSE, FALSE,
6310 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6313 Yeater_nB, FALSE, TRUE,
6314 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6317 Yeater_e, FALSE, FALSE,
6318 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6321 Yeater_eB, FALSE, TRUE,
6322 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6325 Yeater_s, FALSE, FALSE,
6326 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6329 Yeater_sB, FALSE, TRUE,
6330 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6333 Yeater_w, FALSE, FALSE,
6334 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6337 Yeater_wB, FALSE, TRUE,
6338 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6341 Yeater_stone, FALSE, FALSE,
6342 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6345 Yeater_spring, FALSE, FALSE,
6346 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6350 Xalien, TRUE, FALSE,
6354 Xalien_pause, FALSE, FALSE,
6358 Yalien_n, FALSE, FALSE,
6359 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6362 Yalien_nB, FALSE, TRUE,
6363 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6366 Yalien_e, FALSE, FALSE,
6367 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6370 Yalien_eB, FALSE, TRUE,
6371 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6374 Yalien_s, FALSE, FALSE,
6375 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6378 Yalien_sB, FALSE, TRUE,
6379 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6382 Yalien_w, FALSE, FALSE,
6383 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6386 Yalien_wB, FALSE, TRUE,
6387 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6390 Yalien_stone, FALSE, FALSE,
6391 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6394 Yalien_spring, FALSE, FALSE,
6395 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6399 Xbug_1_n, TRUE, FALSE,
6403 Xbug_1_e, TRUE, FALSE,
6404 EL_BUG_RIGHT, -1, -1
6407 Xbug_1_s, TRUE, FALSE,
6411 Xbug_1_w, TRUE, FALSE,
6415 Xbug_2_n, FALSE, FALSE,
6419 Xbug_2_e, FALSE, FALSE,
6420 EL_BUG_RIGHT, -1, -1
6423 Xbug_2_s, FALSE, FALSE,
6427 Xbug_2_w, FALSE, FALSE,
6431 Ybug_n, FALSE, FALSE,
6432 EL_BUG, ACTION_MOVING, MV_BIT_UP
6435 Ybug_nB, FALSE, TRUE,
6436 EL_BUG, ACTION_MOVING, MV_BIT_UP
6439 Ybug_e, FALSE, FALSE,
6440 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6443 Ybug_eB, FALSE, TRUE,
6444 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6447 Ybug_s, FALSE, FALSE,
6448 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6451 Ybug_sB, FALSE, TRUE,
6452 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6455 Ybug_w, FALSE, FALSE,
6456 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6459 Ybug_wB, FALSE, TRUE,
6460 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6463 Ybug_w_n, FALSE, FALSE,
6464 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6467 Ybug_n_e, FALSE, FALSE,
6468 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6471 Ybug_e_s, FALSE, FALSE,
6472 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6475 Ybug_s_w, FALSE, FALSE,
6476 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6479 Ybug_e_n, FALSE, FALSE,
6480 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6483 Ybug_s_e, FALSE, FALSE,
6484 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6487 Ybug_w_s, FALSE, FALSE,
6488 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6491 Ybug_n_w, FALSE, FALSE,
6492 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6495 Ybug_stone, FALSE, FALSE,
6496 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6499 Ybug_spring, FALSE, FALSE,
6500 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6504 Xtank_1_n, TRUE, FALSE,
6505 EL_SPACESHIP_UP, -1, -1
6508 Xtank_1_e, TRUE, FALSE,
6509 EL_SPACESHIP_RIGHT, -1, -1
6512 Xtank_1_s, TRUE, FALSE,
6513 EL_SPACESHIP_DOWN, -1, -1
6516 Xtank_1_w, TRUE, FALSE,
6517 EL_SPACESHIP_LEFT, -1, -1
6520 Xtank_2_n, FALSE, FALSE,
6521 EL_SPACESHIP_UP, -1, -1
6524 Xtank_2_e, FALSE, FALSE,
6525 EL_SPACESHIP_RIGHT, -1, -1
6528 Xtank_2_s, FALSE, FALSE,
6529 EL_SPACESHIP_DOWN, -1, -1
6532 Xtank_2_w, FALSE, FALSE,
6533 EL_SPACESHIP_LEFT, -1, -1
6536 Ytank_n, FALSE, FALSE,
6537 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6540 Ytank_nB, FALSE, TRUE,
6541 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6544 Ytank_e, FALSE, FALSE,
6545 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6548 Ytank_eB, FALSE, TRUE,
6549 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6552 Ytank_s, FALSE, FALSE,
6553 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6556 Ytank_sB, FALSE, TRUE,
6557 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6560 Ytank_w, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6564 Ytank_wB, FALSE, TRUE,
6565 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6568 Ytank_w_n, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6572 Ytank_n_e, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6576 Ytank_e_s, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6580 Ytank_s_w, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6584 Ytank_e_n, FALSE, FALSE,
6585 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6588 Ytank_s_e, FALSE, FALSE,
6589 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6592 Ytank_w_s, FALSE, FALSE,
6593 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6596 Ytank_n_w, FALSE, FALSE,
6597 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6600 Ytank_stone, FALSE, FALSE,
6601 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6604 Ytank_spring, FALSE, FALSE,
6605 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6609 Xemerald, TRUE, FALSE,
6613 Xemerald_pause, FALSE, FALSE,
6617 Xemerald_fall, FALSE, FALSE,
6621 Xemerald_shine, FALSE, FALSE,
6622 EL_EMERALD, ACTION_TWINKLING, -1
6625 Yemerald_s, FALSE, FALSE,
6626 EL_EMERALD, ACTION_FALLING, -1
6629 Yemerald_sB, FALSE, TRUE,
6630 EL_EMERALD, ACTION_FALLING, -1
6633 Yemerald_e, FALSE, FALSE,
6634 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6637 Yemerald_eB, FALSE, TRUE,
6638 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6641 Yemerald_w, FALSE, FALSE,
6642 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6645 Yemerald_wB, FALSE, TRUE,
6646 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6649 Yemerald_blank, FALSE, FALSE,
6650 EL_EMERALD, ACTION_COLLECTING, -1
6654 Xdiamond, TRUE, FALSE,
6658 Xdiamond_pause, FALSE, FALSE,
6662 Xdiamond_fall, FALSE, FALSE,
6666 Xdiamond_shine, FALSE, FALSE,
6667 EL_DIAMOND, ACTION_TWINKLING, -1
6670 Ydiamond_s, FALSE, FALSE,
6671 EL_DIAMOND, ACTION_FALLING, -1
6674 Ydiamond_sB, FALSE, TRUE,
6675 EL_DIAMOND, ACTION_FALLING, -1
6678 Ydiamond_e, FALSE, FALSE,
6679 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6682 Ydiamond_eB, FALSE, TRUE,
6683 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6686 Ydiamond_w, FALSE, FALSE,
6687 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6690 Ydiamond_wB, FALSE, TRUE,
6691 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6694 Ydiamond_blank, FALSE, FALSE,
6695 EL_DIAMOND, ACTION_COLLECTING, -1
6698 Ydiamond_stone, FALSE, FALSE,
6699 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6703 Xstone, TRUE, FALSE,
6707 Xstone_pause, FALSE, FALSE,
6711 Xstone_fall, FALSE, FALSE,
6715 Ystone_s, FALSE, FALSE,
6716 EL_ROCK, ACTION_FALLING, -1
6719 Ystone_sB, FALSE, TRUE,
6720 EL_ROCK, ACTION_FALLING, -1
6723 Ystone_e, FALSE, FALSE,
6724 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6727 Ystone_eB, FALSE, TRUE,
6728 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6731 Ystone_w, FALSE, FALSE,
6732 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6735 Ystone_wB, FALSE, TRUE,
6736 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6744 Xbomb_pause, FALSE, FALSE,
6748 Xbomb_fall, FALSE, FALSE,
6752 Ybomb_s, FALSE, FALSE,
6753 EL_BOMB, ACTION_FALLING, -1
6756 Ybomb_sB, FALSE, TRUE,
6757 EL_BOMB, ACTION_FALLING, -1
6760 Ybomb_e, FALSE, FALSE,
6761 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6764 Ybomb_eB, FALSE, TRUE,
6765 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6768 Ybomb_w, FALSE, FALSE,
6769 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6772 Ybomb_wB, FALSE, TRUE,
6773 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6776 Ybomb_blank, FALSE, FALSE,
6777 EL_BOMB, ACTION_ACTIVATING, -1
6785 Xnut_pause, FALSE, FALSE,
6789 Xnut_fall, FALSE, FALSE,
6793 Ynut_s, FALSE, FALSE,
6794 EL_NUT, ACTION_FALLING, -1
6797 Ynut_sB, FALSE, TRUE,
6798 EL_NUT, ACTION_FALLING, -1
6801 Ynut_e, FALSE, FALSE,
6802 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6805 Ynut_eB, FALSE, TRUE,
6806 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6809 Ynut_w, FALSE, FALSE,
6810 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6813 Ynut_wB, FALSE, TRUE,
6814 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6817 Ynut_stone, FALSE, FALSE,
6818 EL_NUT, ACTION_BREAKING, -1
6822 Xspring, TRUE, FALSE,
6826 Xspring_pause, FALSE, FALSE,
6830 Xspring_e, TRUE, FALSE,
6831 EL_SPRING_RIGHT, -1, -1
6834 Xspring_w, TRUE, FALSE,
6835 EL_SPRING_LEFT, -1, -1
6838 Xspring_fall, FALSE, FALSE,
6842 Yspring_s, FALSE, FALSE,
6843 EL_SPRING, ACTION_FALLING, -1
6846 Yspring_sB, FALSE, TRUE,
6847 EL_SPRING, ACTION_FALLING, -1
6850 Yspring_e, FALSE, FALSE,
6851 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6854 Yspring_eB, FALSE, TRUE,
6855 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6858 Yspring_w, FALSE, FALSE,
6859 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6862 Yspring_wB, FALSE, TRUE,
6863 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6866 Yspring_alien_e, FALSE, FALSE,
6867 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6870 Yspring_alien_eB, FALSE, TRUE,
6871 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6874 Yspring_alien_w, FALSE, FALSE,
6875 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6878 Yspring_alien_wB, FALSE, TRUE,
6879 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6883 Xpush_emerald_e, FALSE, FALSE,
6884 EL_EMERALD, -1, MV_BIT_RIGHT
6887 Xpush_emerald_w, FALSE, FALSE,
6888 EL_EMERALD, -1, MV_BIT_LEFT
6891 Xpush_diamond_e, FALSE, FALSE,
6892 EL_DIAMOND, -1, MV_BIT_RIGHT
6895 Xpush_diamond_w, FALSE, FALSE,
6896 EL_DIAMOND, -1, MV_BIT_LEFT
6899 Xpush_stone_e, FALSE, FALSE,
6900 EL_ROCK, -1, MV_BIT_RIGHT
6903 Xpush_stone_w, FALSE, FALSE,
6904 EL_ROCK, -1, MV_BIT_LEFT
6907 Xpush_bomb_e, FALSE, FALSE,
6908 EL_BOMB, -1, MV_BIT_RIGHT
6911 Xpush_bomb_w, FALSE, FALSE,
6912 EL_BOMB, -1, MV_BIT_LEFT
6915 Xpush_nut_e, FALSE, FALSE,
6916 EL_NUT, -1, MV_BIT_RIGHT
6919 Xpush_nut_w, FALSE, FALSE,
6920 EL_NUT, -1, MV_BIT_LEFT
6923 Xpush_spring_e, FALSE, FALSE,
6924 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6927 Xpush_spring_w, FALSE, FALSE,
6928 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6932 Xdynamite, TRUE, FALSE,
6933 EL_EM_DYNAMITE, -1, -1
6936 Ydynamite_blank, FALSE, FALSE,
6937 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6940 Xdynamite_1, TRUE, FALSE,
6941 EL_EM_DYNAMITE_ACTIVE, -1, -1
6944 Xdynamite_2, FALSE, FALSE,
6945 EL_EM_DYNAMITE_ACTIVE, -1, -1
6948 Xdynamite_3, FALSE, FALSE,
6949 EL_EM_DYNAMITE_ACTIVE, -1, -1
6952 Xdynamite_4, FALSE, FALSE,
6953 EL_EM_DYNAMITE_ACTIVE, -1, -1
6957 Xkey_1, TRUE, FALSE,
6961 Xkey_2, TRUE, FALSE,
6965 Xkey_3, TRUE, FALSE,
6969 Xkey_4, TRUE, FALSE,
6973 Xkey_5, TRUE, FALSE,
6974 EL_EMC_KEY_5, -1, -1
6977 Xkey_6, TRUE, FALSE,
6978 EL_EMC_KEY_6, -1, -1
6981 Xkey_7, TRUE, FALSE,
6982 EL_EMC_KEY_7, -1, -1
6985 Xkey_8, TRUE, FALSE,
6986 EL_EMC_KEY_8, -1, -1
6990 Xdoor_1, TRUE, FALSE,
6991 EL_EM_GATE_1, -1, -1
6994 Xdoor_2, TRUE, FALSE,
6995 EL_EM_GATE_2, -1, -1
6998 Xdoor_3, TRUE, FALSE,
6999 EL_EM_GATE_3, -1, -1
7002 Xdoor_4, TRUE, FALSE,
7003 EL_EM_GATE_4, -1, -1
7006 Xdoor_5, TRUE, FALSE,
7007 EL_EMC_GATE_5, -1, -1
7010 Xdoor_6, TRUE, FALSE,
7011 EL_EMC_GATE_6, -1, -1
7014 Xdoor_7, TRUE, FALSE,
7015 EL_EMC_GATE_7, -1, -1
7018 Xdoor_8, TRUE, FALSE,
7019 EL_EMC_GATE_8, -1, -1
7023 Xfake_door_1, TRUE, FALSE,
7024 EL_EM_GATE_1_GRAY, -1, -1
7027 Xfake_door_2, TRUE, FALSE,
7028 EL_EM_GATE_2_GRAY, -1, -1
7031 Xfake_door_3, TRUE, FALSE,
7032 EL_EM_GATE_3_GRAY, -1, -1
7035 Xfake_door_4, TRUE, FALSE,
7036 EL_EM_GATE_4_GRAY, -1, -1
7039 Xfake_door_5, TRUE, FALSE,
7040 EL_EMC_GATE_5_GRAY, -1, -1
7043 Xfake_door_6, TRUE, FALSE,
7044 EL_EMC_GATE_6_GRAY, -1, -1
7047 Xfake_door_7, TRUE, FALSE,
7048 EL_EMC_GATE_7_GRAY, -1, -1
7051 Xfake_door_8, TRUE, FALSE,
7052 EL_EMC_GATE_8_GRAY, -1, -1
7056 Xballoon, TRUE, FALSE,
7060 Yballoon_n, FALSE, FALSE,
7061 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7064 Yballoon_nB, FALSE, TRUE,
7065 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7068 Yballoon_e, FALSE, FALSE,
7069 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7072 Yballoon_eB, FALSE, TRUE,
7073 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7076 Yballoon_s, FALSE, FALSE,
7077 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7080 Yballoon_sB, FALSE, TRUE,
7081 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7084 Yballoon_w, FALSE, FALSE,
7085 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7088 Yballoon_wB, FALSE, TRUE,
7089 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7093 Xball_1, TRUE, FALSE,
7094 EL_EMC_MAGIC_BALL, -1, -1
7097 Yball_1, FALSE, FALSE,
7098 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7101 Xball_2, FALSE, FALSE,
7102 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7105 Yball_2, FALSE, FALSE,
7106 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7109 Yball_blank, FALSE, FALSE,
7110 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7114 Xamoeba_1, TRUE, FALSE,
7115 EL_AMOEBA_DRY, ACTION_OTHER, -1
7118 Xamoeba_2, FALSE, FALSE,
7119 EL_AMOEBA_DRY, ACTION_OTHER, -1
7122 Xamoeba_3, FALSE, FALSE,
7123 EL_AMOEBA_DRY, ACTION_OTHER, -1
7126 Xamoeba_4, FALSE, FALSE,
7127 EL_AMOEBA_DRY, ACTION_OTHER, -1
7130 Xamoeba_5, TRUE, FALSE,
7131 EL_AMOEBA_WET, ACTION_OTHER, -1
7134 Xamoeba_6, FALSE, FALSE,
7135 EL_AMOEBA_WET, ACTION_OTHER, -1
7138 Xamoeba_7, FALSE, FALSE,
7139 EL_AMOEBA_WET, ACTION_OTHER, -1
7142 Xamoeba_8, FALSE, FALSE,
7143 EL_AMOEBA_WET, ACTION_OTHER, -1
7148 EL_AMOEBA_DROP, ACTION_GROWING, -1
7151 Xdrip_fall, FALSE, FALSE,
7152 EL_AMOEBA_DROP, -1, -1
7155 Xdrip_stretch, FALSE, FALSE,
7156 EL_AMOEBA_DROP, ACTION_FALLING, -1
7159 Xdrip_stretchB, FALSE, TRUE,
7160 EL_AMOEBA_DROP, ACTION_FALLING, -1
7163 Ydrip_1_s, FALSE, FALSE,
7164 EL_AMOEBA_DROP, ACTION_FALLING, -1
7167 Ydrip_1_sB, FALSE, TRUE,
7168 EL_AMOEBA_DROP, ACTION_FALLING, -1
7171 Ydrip_2_s, FALSE, FALSE,
7172 EL_AMOEBA_DROP, ACTION_FALLING, -1
7175 Ydrip_2_sB, FALSE, TRUE,
7176 EL_AMOEBA_DROP, ACTION_FALLING, -1
7180 Xwonderwall, TRUE, FALSE,
7181 EL_MAGIC_WALL, -1, -1
7184 Ywonderwall, FALSE, FALSE,
7185 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7189 Xwheel, TRUE, FALSE,
7190 EL_ROBOT_WHEEL, -1, -1
7193 Ywheel, FALSE, FALSE,
7194 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7198 Xswitch, TRUE, FALSE,
7199 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7202 Yswitch, FALSE, FALSE,
7203 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7207 Xbumper, TRUE, FALSE,
7208 EL_EMC_SPRING_BUMPER, -1, -1
7211 Ybumper, FALSE, FALSE,
7212 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7216 Xacid_nw, TRUE, FALSE,
7217 EL_ACID_POOL_TOPLEFT, -1, -1
7220 Xacid_ne, TRUE, FALSE,
7221 EL_ACID_POOL_TOPRIGHT, -1, -1
7224 Xacid_sw, TRUE, FALSE,
7225 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7228 Xacid_s, TRUE, FALSE,
7229 EL_ACID_POOL_BOTTOM, -1, -1
7232 Xacid_se, TRUE, FALSE,
7233 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7237 Xfake_blank, TRUE, FALSE,
7238 EL_INVISIBLE_WALL, -1, -1
7241 Yfake_blank, FALSE, FALSE,
7242 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7246 Xfake_grass, TRUE, FALSE,
7247 EL_EMC_FAKE_GRASS, -1, -1
7250 Yfake_grass, FALSE, FALSE,
7251 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7255 Xfake_amoeba, TRUE, FALSE,
7256 EL_EMC_DRIPPER, -1, -1
7259 Yfake_amoeba, FALSE, FALSE,
7260 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7264 Xlenses, TRUE, FALSE,
7265 EL_EMC_LENSES, -1, -1
7269 Xmagnify, TRUE, FALSE,
7270 EL_EMC_MAGNIFIER, -1, -1
7275 EL_QUICKSAND_EMPTY, -1, -1
7278 Xsand_stone, TRUE, FALSE,
7279 EL_QUICKSAND_FULL, -1, -1
7282 Xsand_stonein_1, FALSE, TRUE,
7283 EL_ROCK, ACTION_FILLING, -1
7286 Xsand_stonein_2, FALSE, TRUE,
7287 EL_ROCK, ACTION_FILLING, -1
7290 Xsand_stonein_3, FALSE, TRUE,
7291 EL_ROCK, ACTION_FILLING, -1
7294 Xsand_stonein_4, FALSE, TRUE,
7295 EL_ROCK, ACTION_FILLING, -1
7298 Xsand_sandstone_1, FALSE, FALSE,
7299 EL_QUICKSAND_FILLING, -1, -1
7302 Xsand_sandstone_2, FALSE, FALSE,
7303 EL_QUICKSAND_FILLING, -1, -1
7306 Xsand_sandstone_3, FALSE, FALSE,
7307 EL_QUICKSAND_FILLING, -1, -1
7310 Xsand_sandstone_4, FALSE, FALSE,
7311 EL_QUICKSAND_FILLING, -1, -1
7314 Xsand_stonesand_1, FALSE, FALSE,
7315 EL_QUICKSAND_EMPTYING, -1, -1
7318 Xsand_stonesand_2, FALSE, FALSE,
7319 EL_QUICKSAND_EMPTYING, -1, -1
7322 Xsand_stonesand_3, FALSE, FALSE,
7323 EL_QUICKSAND_EMPTYING, -1, -1
7326 Xsand_stonesand_4, FALSE, FALSE,
7327 EL_QUICKSAND_EMPTYING, -1, -1
7330 Xsand_stoneout_1, FALSE, FALSE,
7331 EL_ROCK, ACTION_EMPTYING, -1
7334 Xsand_stoneout_2, FALSE, FALSE,
7335 EL_ROCK, ACTION_EMPTYING, -1
7338 Xsand_stonesand_quickout_1, FALSE, FALSE,
7339 EL_QUICKSAND_EMPTYING, -1, -1
7342 Xsand_stonesand_quickout_2, FALSE, FALSE,
7343 EL_QUICKSAND_EMPTYING, -1, -1
7347 Xslide_ns, TRUE, FALSE,
7348 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7351 Yslide_ns_blank, FALSE, FALSE,
7352 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7355 Xslide_ew, TRUE, FALSE,
7356 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7359 Yslide_ew_blank, FALSE, FALSE,
7360 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7364 Xwind_n, TRUE, FALSE,
7365 EL_BALLOON_SWITCH_UP, -1, -1
7368 Xwind_e, TRUE, FALSE,
7369 EL_BALLOON_SWITCH_RIGHT, -1, -1
7372 Xwind_s, TRUE, FALSE,
7373 EL_BALLOON_SWITCH_DOWN, -1, -1
7376 Xwind_w, TRUE, FALSE,
7377 EL_BALLOON_SWITCH_LEFT, -1, -1
7380 Xwind_any, TRUE, FALSE,
7381 EL_BALLOON_SWITCH_ANY, -1, -1
7384 Xwind_stop, TRUE, FALSE,
7385 EL_BALLOON_SWITCH_NONE, -1, -1
7390 EL_EM_EXIT_CLOSED, -1, -1
7393 Xexit_1, TRUE, FALSE,
7394 EL_EM_EXIT_OPEN, -1, -1
7397 Xexit_2, FALSE, FALSE,
7398 EL_EM_EXIT_OPEN, -1, -1
7401 Xexit_3, FALSE, FALSE,
7402 EL_EM_EXIT_OPEN, -1, -1
7406 Xpause, FALSE, FALSE,
7411 Xwall_1, TRUE, FALSE,
7415 Xwall_2, TRUE, FALSE,
7416 EL_EMC_WALL_14, -1, -1
7419 Xwall_3, TRUE, FALSE,
7420 EL_EMC_WALL_15, -1, -1
7423 Xwall_4, TRUE, FALSE,
7424 EL_EMC_WALL_16, -1, -1
7428 Xroundwall_1, TRUE, FALSE,
7429 EL_WALL_SLIPPERY, -1, -1
7432 Xroundwall_2, TRUE, FALSE,
7433 EL_EMC_WALL_SLIPPERY_2, -1, -1
7436 Xroundwall_3, TRUE, FALSE,
7437 EL_EMC_WALL_SLIPPERY_3, -1, -1
7440 Xroundwall_4, TRUE, FALSE,
7441 EL_EMC_WALL_SLIPPERY_4, -1, -1
7445 Xsteel_1, TRUE, FALSE,
7446 EL_STEELWALL, -1, -1
7449 Xsteel_2, TRUE, FALSE,
7450 EL_EMC_STEELWALL_2, -1, -1
7453 Xsteel_3, TRUE, FALSE,
7454 EL_EMC_STEELWALL_3, -1, -1
7457 Xsteel_4, TRUE, FALSE,
7458 EL_EMC_STEELWALL_4, -1, -1
7462 Xdecor_1, TRUE, FALSE,
7463 EL_EMC_WALL_8, -1, -1
7466 Xdecor_2, TRUE, FALSE,
7467 EL_EMC_WALL_6, -1, -1
7470 Xdecor_3, TRUE, FALSE,
7471 EL_EMC_WALL_4, -1, -1
7474 Xdecor_4, TRUE, FALSE,
7475 EL_EMC_WALL_7, -1, -1
7478 Xdecor_5, TRUE, FALSE,
7479 EL_EMC_WALL_5, -1, -1
7482 Xdecor_6, TRUE, FALSE,
7483 EL_EMC_WALL_9, -1, -1
7486 Xdecor_7, TRUE, FALSE,
7487 EL_EMC_WALL_10, -1, -1
7490 Xdecor_8, TRUE, FALSE,
7491 EL_EMC_WALL_1, -1, -1
7494 Xdecor_9, TRUE, FALSE,
7495 EL_EMC_WALL_2, -1, -1
7498 Xdecor_10, TRUE, FALSE,
7499 EL_EMC_WALL_3, -1, -1
7502 Xdecor_11, TRUE, FALSE,
7503 EL_EMC_WALL_11, -1, -1
7506 Xdecor_12, TRUE, FALSE,
7507 EL_EMC_WALL_12, -1, -1
7511 Xalpha_0, TRUE, FALSE,
7512 EL_CHAR('0'), -1, -1
7515 Xalpha_1, TRUE, FALSE,
7516 EL_CHAR('1'), -1, -1
7519 Xalpha_2, TRUE, FALSE,
7520 EL_CHAR('2'), -1, -1
7523 Xalpha_3, TRUE, FALSE,
7524 EL_CHAR('3'), -1, -1
7527 Xalpha_4, TRUE, FALSE,
7528 EL_CHAR('4'), -1, -1
7531 Xalpha_5, TRUE, FALSE,
7532 EL_CHAR('5'), -1, -1
7535 Xalpha_6, TRUE, FALSE,
7536 EL_CHAR('6'), -1, -1
7539 Xalpha_7, TRUE, FALSE,
7540 EL_CHAR('7'), -1, -1
7543 Xalpha_8, TRUE, FALSE,
7544 EL_CHAR('8'), -1, -1
7547 Xalpha_9, TRUE, FALSE,
7548 EL_CHAR('9'), -1, -1
7551 Xalpha_excla, TRUE, FALSE,
7552 EL_CHAR('!'), -1, -1
7555 Xalpha_apost, TRUE, FALSE,
7556 EL_CHAR('\''), -1, -1
7559 Xalpha_comma, TRUE, FALSE,
7560 EL_CHAR(','), -1, -1
7563 Xalpha_minus, TRUE, FALSE,
7564 EL_CHAR('-'), -1, -1
7567 Xalpha_perio, TRUE, FALSE,
7568 EL_CHAR('.'), -1, -1
7571 Xalpha_colon, TRUE, FALSE,
7572 EL_CHAR(':'), -1, -1
7575 Xalpha_quest, TRUE, FALSE,
7576 EL_CHAR('?'), -1, -1
7579 Xalpha_a, TRUE, FALSE,
7580 EL_CHAR('A'), -1, -1
7583 Xalpha_b, TRUE, FALSE,
7584 EL_CHAR('B'), -1, -1
7587 Xalpha_c, TRUE, FALSE,
7588 EL_CHAR('C'), -1, -1
7591 Xalpha_d, TRUE, FALSE,
7592 EL_CHAR('D'), -1, -1
7595 Xalpha_e, TRUE, FALSE,
7596 EL_CHAR('E'), -1, -1
7599 Xalpha_f, TRUE, FALSE,
7600 EL_CHAR('F'), -1, -1
7603 Xalpha_g, TRUE, FALSE,
7604 EL_CHAR('G'), -1, -1
7607 Xalpha_h, TRUE, FALSE,
7608 EL_CHAR('H'), -1, -1
7611 Xalpha_i, TRUE, FALSE,
7612 EL_CHAR('I'), -1, -1
7615 Xalpha_j, TRUE, FALSE,
7616 EL_CHAR('J'), -1, -1
7619 Xalpha_k, TRUE, FALSE,
7620 EL_CHAR('K'), -1, -1
7623 Xalpha_l, TRUE, FALSE,
7624 EL_CHAR('L'), -1, -1
7627 Xalpha_m, TRUE, FALSE,
7628 EL_CHAR('M'), -1, -1
7631 Xalpha_n, TRUE, FALSE,
7632 EL_CHAR('N'), -1, -1
7635 Xalpha_o, TRUE, FALSE,
7636 EL_CHAR('O'), -1, -1
7639 Xalpha_p, TRUE, FALSE,
7640 EL_CHAR('P'), -1, -1
7643 Xalpha_q, TRUE, FALSE,
7644 EL_CHAR('Q'), -1, -1
7647 Xalpha_r, TRUE, FALSE,
7648 EL_CHAR('R'), -1, -1
7651 Xalpha_s, TRUE, FALSE,
7652 EL_CHAR('S'), -1, -1
7655 Xalpha_t, TRUE, FALSE,
7656 EL_CHAR('T'), -1, -1
7659 Xalpha_u, TRUE, FALSE,
7660 EL_CHAR('U'), -1, -1
7663 Xalpha_v, TRUE, FALSE,
7664 EL_CHAR('V'), -1, -1
7667 Xalpha_w, TRUE, FALSE,
7668 EL_CHAR('W'), -1, -1
7671 Xalpha_x, TRUE, FALSE,
7672 EL_CHAR('X'), -1, -1
7675 Xalpha_y, TRUE, FALSE,
7676 EL_CHAR('Y'), -1, -1
7679 Xalpha_z, TRUE, FALSE,
7680 EL_CHAR('Z'), -1, -1
7683 Xalpha_arrow_e, TRUE, FALSE,
7684 EL_CHAR('>'), -1, -1
7687 Xalpha_arrow_w, TRUE, FALSE,
7688 EL_CHAR('<'), -1, -1
7691 Xalpha_copyr, TRUE, FALSE,
7692 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7696 Ykey_1_blank, FALSE, FALSE,
7697 EL_EM_KEY_1, ACTION_COLLECTING, -1
7700 Ykey_2_blank, FALSE, FALSE,
7701 EL_EM_KEY_2, ACTION_COLLECTING, -1
7704 Ykey_3_blank, FALSE, FALSE,
7705 EL_EM_KEY_3, ACTION_COLLECTING, -1
7708 Ykey_4_blank, FALSE, FALSE,
7709 EL_EM_KEY_4, ACTION_COLLECTING, -1
7712 Ykey_5_blank, FALSE, FALSE,
7713 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7716 Ykey_6_blank, FALSE, FALSE,
7717 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7720 Ykey_7_blank, FALSE, FALSE,
7721 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7724 Ykey_8_blank, FALSE, FALSE,
7725 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7728 Ylenses_blank, FALSE, FALSE,
7729 EL_EMC_LENSES, ACTION_COLLECTING, -1
7732 Ymagnify_blank, FALSE, FALSE,
7733 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7736 Ygrass_blank, FALSE, FALSE,
7737 EL_EMC_GRASS, ACTION_SNAPPING, -1
7740 Ydirt_blank, FALSE, FALSE,
7741 EL_SAND, ACTION_SNAPPING, -1
7750 static struct Mapping_EM_to_RND_player
7759 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7763 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7767 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7771 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7775 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7779 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7783 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7787 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7791 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7795 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7799 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7803 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7807 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7811 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7815 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7819 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7823 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7827 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7831 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7835 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7839 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7843 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7847 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7851 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7855 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7859 EL_PLAYER_1, ACTION_DEFAULT, -1,
7863 EL_PLAYER_2, ACTION_DEFAULT, -1,
7867 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7871 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7875 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7879 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7883 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7887 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7891 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7895 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7899 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7903 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7907 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7911 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7915 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7919 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7923 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7927 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7931 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7935 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7939 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7943 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7947 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7951 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7955 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7959 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7963 EL_PLAYER_3, ACTION_DEFAULT, -1,
7967 EL_PLAYER_4, ACTION_DEFAULT, -1,
7976 int map_element_RND_to_EM_cave(int element_rnd)
7978 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7979 static boolean mapping_initialized = FALSE;
7981 if (!mapping_initialized)
7985 // return "Xalpha_quest" for all undefined elements in mapping array
7986 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7987 mapping_RND_to_EM[i] = Xalpha_quest;
7989 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7990 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7991 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7992 em_object_mapping_list[i].element_em;
7994 mapping_initialized = TRUE;
7997 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7999 Warn("invalid RND level element %d", element_rnd);
8004 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8007 int map_element_EM_to_RND_cave(int element_em_cave)
8009 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8010 static boolean mapping_initialized = FALSE;
8012 if (!mapping_initialized)
8016 // return "EL_UNKNOWN" for all undefined elements in mapping array
8017 for (i = 0; i < GAME_TILE_MAX; i++)
8018 mapping_EM_to_RND[i] = EL_UNKNOWN;
8020 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8021 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8022 em_object_mapping_list[i].element_rnd;
8024 mapping_initialized = TRUE;
8027 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8029 Warn("invalid EM cave element %d", element_em_cave);
8034 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8037 int map_element_EM_to_RND_game(int element_em_game)
8039 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8040 static boolean mapping_initialized = FALSE;
8042 if (!mapping_initialized)
8046 // return "EL_UNKNOWN" for all undefined elements in mapping array
8047 for (i = 0; i < GAME_TILE_MAX; i++)
8048 mapping_EM_to_RND[i] = EL_UNKNOWN;
8050 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8051 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8052 em_object_mapping_list[i].element_rnd;
8054 mapping_initialized = TRUE;
8057 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8059 Warn("invalid EM game element %d", element_em_game);
8064 return mapping_EM_to_RND[element_em_game];
8067 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8069 struct LevelInfo_EM *level_em = level->native_em_level;
8070 struct CAVE *cav = level_em->cav;
8073 for (i = 0; i < GAME_TILE_MAX; i++)
8074 cav->android_array[i] = Cblank;
8076 for (i = 0; i < level->num_android_clone_elements; i++)
8078 int element_rnd = level->android_clone_element[i];
8079 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8081 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8082 if (em_object_mapping_list[j].element_rnd == element_rnd)
8083 cav->android_array[em_object_mapping_list[j].element_em] =
8088 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8090 struct LevelInfo_EM *level_em = level->native_em_level;
8091 struct CAVE *cav = level_em->cav;
8094 level->num_android_clone_elements = 0;
8096 for (i = 0; i < GAME_TILE_MAX; i++)
8098 int element_em_cave = cav->android_array[i];
8100 boolean element_found = FALSE;
8102 if (element_em_cave == Cblank)
8105 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8107 for (j = 0; j < level->num_android_clone_elements; j++)
8108 if (level->android_clone_element[j] == element_rnd)
8109 element_found = TRUE;
8113 level->android_clone_element[level->num_android_clone_elements++] =
8116 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8121 if (level->num_android_clone_elements == 0)
8123 level->num_android_clone_elements = 1;
8124 level->android_clone_element[0] = EL_EMPTY;
8128 int map_direction_RND_to_EM(int direction)
8130 return (direction == MV_UP ? 0 :
8131 direction == MV_RIGHT ? 1 :
8132 direction == MV_DOWN ? 2 :
8133 direction == MV_LEFT ? 3 :
8137 int map_direction_EM_to_RND(int direction)
8139 return (direction == 0 ? MV_UP :
8140 direction == 1 ? MV_RIGHT :
8141 direction == 2 ? MV_DOWN :
8142 direction == 3 ? MV_LEFT :
8146 int map_element_RND_to_SP(int element_rnd)
8148 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8150 if (element_rnd >= EL_SP_START &&
8151 element_rnd <= EL_SP_END)
8152 element_sp = element_rnd - EL_SP_START;
8153 else if (element_rnd == EL_EMPTY_SPACE)
8155 else if (element_rnd == EL_INVISIBLE_WALL)
8161 int map_element_SP_to_RND(int element_sp)
8163 int element_rnd = EL_UNKNOWN;
8165 if (element_sp >= 0x00 &&
8167 element_rnd = EL_SP_START + element_sp;
8168 else if (element_sp == 0x28)
8169 element_rnd = EL_INVISIBLE_WALL;
8174 int map_action_SP_to_RND(int action_sp)
8178 case actActive: return ACTION_ACTIVE;
8179 case actImpact: return ACTION_IMPACT;
8180 case actExploding: return ACTION_EXPLODING;
8181 case actDigging: return ACTION_DIGGING;
8182 case actSnapping: return ACTION_SNAPPING;
8183 case actCollecting: return ACTION_COLLECTING;
8184 case actPassing: return ACTION_PASSING;
8185 case actPushing: return ACTION_PUSHING;
8186 case actDropping: return ACTION_DROPPING;
8188 default: return ACTION_DEFAULT;
8192 int map_element_RND_to_MM(int element_rnd)
8194 return (element_rnd >= EL_MM_START_1 &&
8195 element_rnd <= EL_MM_END_1 ?
8196 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8198 element_rnd >= EL_MM_START_2 &&
8199 element_rnd <= EL_MM_END_2 ?
8200 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8202 element_rnd >= EL_CHAR_START &&
8203 element_rnd <= EL_CHAR_END ?
8204 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8206 element_rnd >= EL_MM_RUNTIME_START &&
8207 element_rnd <= EL_MM_RUNTIME_END ?
8208 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8210 element_rnd >= EL_MM_DUMMY_START &&
8211 element_rnd <= EL_MM_DUMMY_END ?
8212 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8214 EL_MM_EMPTY_NATIVE);
8217 int map_element_MM_to_RND(int element_mm)
8219 return (element_mm == EL_MM_EMPTY_NATIVE ||
8220 element_mm == EL_DF_EMPTY_NATIVE ?
8223 element_mm >= EL_MM_START_1_NATIVE &&
8224 element_mm <= EL_MM_END_1_NATIVE ?
8225 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8227 element_mm >= EL_MM_START_2_NATIVE &&
8228 element_mm <= EL_MM_END_2_NATIVE ?
8229 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8231 element_mm >= EL_MM_CHAR_START_NATIVE &&
8232 element_mm <= EL_MM_CHAR_END_NATIVE ?
8233 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8235 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8236 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8237 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8239 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8240 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8241 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8246 int map_action_MM_to_RND(int action_mm)
8248 // all MM actions are defined to exactly match their RND counterparts
8252 int map_sound_MM_to_RND(int sound_mm)
8256 case SND_MM_GAME_LEVELTIME_CHARGING:
8257 return SND_GAME_LEVELTIME_CHARGING;
8259 case SND_MM_GAME_HEALTH_CHARGING:
8260 return SND_GAME_HEALTH_CHARGING;
8263 return SND_UNDEFINED;
8267 int map_mm_wall_element(int element)
8269 return (element >= EL_MM_STEEL_WALL_START &&
8270 element <= EL_MM_STEEL_WALL_END ?
8273 element >= EL_MM_WOODEN_WALL_START &&
8274 element <= EL_MM_WOODEN_WALL_END ?
8277 element >= EL_MM_ICE_WALL_START &&
8278 element <= EL_MM_ICE_WALL_END ?
8281 element >= EL_MM_AMOEBA_WALL_START &&
8282 element <= EL_MM_AMOEBA_WALL_END ?
8285 element >= EL_DF_STEEL_WALL_START &&
8286 element <= EL_DF_STEEL_WALL_END ?
8289 element >= EL_DF_WOODEN_WALL_START &&
8290 element <= EL_DF_WOODEN_WALL_END ?
8296 int map_mm_wall_element_editor(int element)
8300 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8301 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8302 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8303 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8304 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8305 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8307 default: return element;
8311 int get_next_element(int element)
8315 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8316 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8317 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8318 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8319 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8320 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8321 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8322 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8323 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8324 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8325 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8327 default: return element;
8331 int el2img_mm(int element_mm)
8333 return el2img(map_element_MM_to_RND(element_mm));
8336 int el_act_dir2img(int element, int action, int direction)
8338 element = GFX_ELEMENT(element);
8339 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8341 // direction_graphic[][] == graphic[] for undefined direction graphics
8342 return element_info[element].direction_graphic[action][direction];
8345 static int el_act_dir2crm(int element, int action, int direction)
8347 element = GFX_ELEMENT(element);
8348 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8350 // direction_graphic[][] == graphic[] for undefined direction graphics
8351 return element_info[element].direction_crumbled[action][direction];
8354 int el_act2img(int element, int action)
8356 element = GFX_ELEMENT(element);
8358 return element_info[element].graphic[action];
8361 int el_act2crm(int element, int action)
8363 element = GFX_ELEMENT(element);
8365 return element_info[element].crumbled[action];
8368 int el_dir2img(int element, int direction)
8370 element = GFX_ELEMENT(element);
8372 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8375 int el2baseimg(int element)
8377 return element_info[element].graphic[ACTION_DEFAULT];
8380 int el2img(int element)
8382 element = GFX_ELEMENT(element);
8384 return element_info[element].graphic[ACTION_DEFAULT];
8387 int el2edimg(int element)
8389 element = GFX_ELEMENT(element);
8391 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8394 int el2preimg(int element)
8396 element = GFX_ELEMENT(element);
8398 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8401 int el2panelimg(int element)
8403 element = GFX_ELEMENT(element);
8405 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8408 int font2baseimg(int font_nr)
8410 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8413 int getBeltNrFromBeltElement(int element)
8415 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8416 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8417 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8420 int getBeltNrFromBeltActiveElement(int element)
8422 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8423 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8424 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8427 int getBeltNrFromBeltSwitchElement(int element)
8429 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8430 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8431 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8434 int getBeltDirNrFromBeltElement(int element)
8436 static int belt_base_element[4] =
8438 EL_CONVEYOR_BELT_1_LEFT,
8439 EL_CONVEYOR_BELT_2_LEFT,
8440 EL_CONVEYOR_BELT_3_LEFT,
8441 EL_CONVEYOR_BELT_4_LEFT
8444 int belt_nr = getBeltNrFromBeltElement(element);
8445 int belt_dir_nr = element - belt_base_element[belt_nr];
8447 return (belt_dir_nr % 3);
8450 int getBeltDirNrFromBeltSwitchElement(int element)
8452 static int belt_base_element[4] =
8454 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8455 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8456 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8457 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8460 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8461 int belt_dir_nr = element - belt_base_element[belt_nr];
8463 return (belt_dir_nr % 3);
8466 int getBeltDirFromBeltElement(int element)
8468 static int belt_move_dir[3] =
8475 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8477 return belt_move_dir[belt_dir_nr];
8480 int getBeltDirFromBeltSwitchElement(int element)
8482 static int belt_move_dir[3] =
8489 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8491 return belt_move_dir[belt_dir_nr];
8494 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8496 static int belt_base_element[4] =
8498 EL_CONVEYOR_BELT_1_LEFT,
8499 EL_CONVEYOR_BELT_2_LEFT,
8500 EL_CONVEYOR_BELT_3_LEFT,
8501 EL_CONVEYOR_BELT_4_LEFT
8504 return belt_base_element[belt_nr] + belt_dir_nr;
8507 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8509 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8511 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8514 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8516 static int belt_base_element[4] =
8518 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8519 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8520 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8521 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8524 return belt_base_element[belt_nr] + belt_dir_nr;
8527 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8529 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8531 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8534 boolean swapTiles_EM(boolean is_pre_emc_cave)
8536 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8539 boolean getTeamMode_EM(void)
8541 return game.team_mode || network_playing;
8544 boolean isActivePlayer_EM(int player_nr)
8546 return stored_player[player_nr].active;
8549 unsigned int InitRND(int seed)
8551 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8552 return InitEngineRandom_EM(seed);
8553 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8554 return InitEngineRandom_SP(seed);
8555 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8556 return InitEngineRandom_MM(seed);
8558 return InitEngineRandom_RND(seed);
8561 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8562 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8564 static int get_effective_element_EM(int tile, int frame_em)
8566 int element = object_mapping[tile].element_rnd;
8567 int action = object_mapping[tile].action;
8568 boolean is_backside = object_mapping[tile].is_backside;
8569 boolean action_removing = (action == ACTION_DIGGING ||
8570 action == ACTION_SNAPPING ||
8571 action == ACTION_COLLECTING);
8579 return (frame_em > 5 ? EL_EMPTY : element);
8585 else // frame_em == 7
8596 case Ydiamond_stone:
8600 case Xdrip_stretchB:
8616 case Ymagnify_blank:
8619 case Xsand_stonein_1:
8620 case Xsand_stonein_2:
8621 case Xsand_stonein_3:
8622 case Xsand_stonein_4:
8626 return (is_backside || action_removing ? EL_EMPTY : element);
8631 static boolean check_linear_animation_EM(int tile)
8635 case Xsand_stonesand_1:
8636 case Xsand_stonesand_quickout_1:
8637 case Xsand_sandstone_1:
8638 case Xsand_stonein_1:
8639 case Xsand_stoneout_1:
8667 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8668 boolean has_crumbled_graphics,
8669 int crumbled, int sync_frame)
8671 // if element can be crumbled, but certain action graphics are just empty
8672 // space (like instantly snapping sand to empty space in 1 frame), do not
8673 // treat these empty space graphics as crumbled graphics in EMC engine
8674 if (crumbled == IMG_EMPTY_SPACE)
8675 has_crumbled_graphics = FALSE;
8677 if (has_crumbled_graphics)
8679 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8680 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8681 g_crumbled->anim_delay,
8682 g_crumbled->anim_mode,
8683 g_crumbled->anim_start_frame,
8686 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8687 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8689 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8690 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8692 g_em->has_crumbled_graphics = TRUE;
8696 g_em->crumbled_bitmap = NULL;
8697 g_em->crumbled_src_x = 0;
8698 g_em->crumbled_src_y = 0;
8699 g_em->crumbled_border_size = 0;
8700 g_em->crumbled_tile_size = 0;
8702 g_em->has_crumbled_graphics = FALSE;
8707 void ResetGfxAnimation_EM(int x, int y, int tile)
8713 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8714 int tile, int frame_em, int x, int y)
8716 int action = object_mapping[tile].action;
8717 int direction = object_mapping[tile].direction;
8718 int effective_element = get_effective_element_EM(tile, frame_em);
8719 int graphic = (direction == MV_NONE ?
8720 el_act2img(effective_element, action) :
8721 el_act_dir2img(effective_element, action, direction));
8722 struct GraphicInfo *g = &graphic_info[graphic];
8724 boolean action_removing = (action == ACTION_DIGGING ||
8725 action == ACTION_SNAPPING ||
8726 action == ACTION_COLLECTING);
8727 boolean action_moving = (action == ACTION_FALLING ||
8728 action == ACTION_MOVING ||
8729 action == ACTION_PUSHING ||
8730 action == ACTION_EATING ||
8731 action == ACTION_FILLING ||
8732 action == ACTION_EMPTYING);
8733 boolean action_falling = (action == ACTION_FALLING ||
8734 action == ACTION_FILLING ||
8735 action == ACTION_EMPTYING);
8737 // special case: graphic uses "2nd movement tile" and has defined
8738 // 7 frames for movement animation (or less) => use default graphic
8739 // for last (8th) frame which ends the movement animation
8740 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8742 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8743 graphic = (direction == MV_NONE ?
8744 el_act2img(effective_element, action) :
8745 el_act_dir2img(effective_element, action, direction));
8747 g = &graphic_info[graphic];
8750 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8754 else if (action_moving)
8756 boolean is_backside = object_mapping[tile].is_backside;
8760 int direction = object_mapping[tile].direction;
8761 int move_dir = (action_falling ? MV_DOWN : direction);
8766 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8767 if (g->double_movement && frame_em == 0)
8771 if (move_dir == MV_LEFT)
8772 GfxFrame[x - 1][y] = GfxFrame[x][y];
8773 else if (move_dir == MV_RIGHT)
8774 GfxFrame[x + 1][y] = GfxFrame[x][y];
8775 else if (move_dir == MV_UP)
8776 GfxFrame[x][y - 1] = GfxFrame[x][y];
8777 else if (move_dir == MV_DOWN)
8778 GfxFrame[x][y + 1] = GfxFrame[x][y];
8785 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8786 if (tile == Xsand_stonesand_quickout_1 ||
8787 tile == Xsand_stonesand_quickout_2)
8791 if (graphic_info[graphic].anim_global_sync)
8792 sync_frame = FrameCounter;
8793 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8794 sync_frame = GfxFrame[x][y];
8796 sync_frame = 0; // playfield border (pseudo steel)
8798 SetRandomAnimationValue(x, y);
8800 int frame = getAnimationFrame(g->anim_frames,
8803 g->anim_start_frame,
8806 g_em->unique_identifier =
8807 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8810 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8811 int tile, int frame_em, int x, int y)
8813 int action = object_mapping[tile].action;
8814 int direction = object_mapping[tile].direction;
8815 boolean is_backside = object_mapping[tile].is_backside;
8816 int effective_element = get_effective_element_EM(tile, frame_em);
8817 int effective_action = action;
8818 int graphic = (direction == MV_NONE ?
8819 el_act2img(effective_element, effective_action) :
8820 el_act_dir2img(effective_element, effective_action,
8822 int crumbled = (direction == MV_NONE ?
8823 el_act2crm(effective_element, effective_action) :
8824 el_act_dir2crm(effective_element, effective_action,
8826 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8827 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8828 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8829 struct GraphicInfo *g = &graphic_info[graphic];
8832 // special case: graphic uses "2nd movement tile" and has defined
8833 // 7 frames for movement animation (or less) => use default graphic
8834 // for last (8th) frame which ends the movement animation
8835 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8837 effective_action = ACTION_DEFAULT;
8838 graphic = (direction == MV_NONE ?
8839 el_act2img(effective_element, effective_action) :
8840 el_act_dir2img(effective_element, effective_action,
8842 crumbled = (direction == MV_NONE ?
8843 el_act2crm(effective_element, effective_action) :
8844 el_act_dir2crm(effective_element, effective_action,
8847 g = &graphic_info[graphic];
8850 if (graphic_info[graphic].anim_global_sync)
8851 sync_frame = FrameCounter;
8852 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8853 sync_frame = GfxFrame[x][y];
8855 sync_frame = 0; // playfield border (pseudo steel)
8857 SetRandomAnimationValue(x, y);
8859 int frame = getAnimationFrame(g->anim_frames,
8862 g->anim_start_frame,
8865 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8866 g->double_movement && is_backside);
8868 // (updating the "crumbled" graphic definitions is probably not really needed,
8869 // as animations for crumbled graphics can't be longer than one EMC cycle)
8870 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8874 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8875 int player_nr, int anim, int frame_em)
8877 int element = player_mapping[player_nr][anim].element_rnd;
8878 int action = player_mapping[player_nr][anim].action;
8879 int direction = player_mapping[player_nr][anim].direction;
8880 int graphic = (direction == MV_NONE ?
8881 el_act2img(element, action) :
8882 el_act_dir2img(element, action, direction));
8883 struct GraphicInfo *g = &graphic_info[graphic];
8886 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8888 stored_player[player_nr].StepFrame = frame_em;
8890 sync_frame = stored_player[player_nr].Frame;
8892 int frame = getAnimationFrame(g->anim_frames,
8895 g->anim_start_frame,
8898 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8899 &g_em->src_x, &g_em->src_y, FALSE);
8902 void InitGraphicInfo_EM(void)
8906 // always start with reliable default values
8907 for (i = 0; i < GAME_TILE_MAX; i++)
8909 object_mapping[i].element_rnd = EL_UNKNOWN;
8910 object_mapping[i].is_backside = FALSE;
8911 object_mapping[i].action = ACTION_DEFAULT;
8912 object_mapping[i].direction = MV_NONE;
8915 // always start with reliable default values
8916 for (p = 0; p < MAX_PLAYERS; p++)
8918 for (i = 0; i < PLY_MAX; i++)
8920 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8921 player_mapping[p][i].action = ACTION_DEFAULT;
8922 player_mapping[p][i].direction = MV_NONE;
8926 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8928 int e = em_object_mapping_list[i].element_em;
8930 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8931 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8933 if (em_object_mapping_list[i].action != -1)
8934 object_mapping[e].action = em_object_mapping_list[i].action;
8936 if (em_object_mapping_list[i].direction != -1)
8937 object_mapping[e].direction =
8938 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8941 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8943 int a = em_player_mapping_list[i].action_em;
8944 int p = em_player_mapping_list[i].player_nr;
8946 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8948 if (em_player_mapping_list[i].action != -1)
8949 player_mapping[p][a].action = em_player_mapping_list[i].action;
8951 if (em_player_mapping_list[i].direction != -1)
8952 player_mapping[p][a].direction =
8953 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8956 for (i = 0; i < GAME_TILE_MAX; i++)
8958 int element = object_mapping[i].element_rnd;
8959 int action = object_mapping[i].action;
8960 int direction = object_mapping[i].direction;
8961 boolean is_backside = object_mapping[i].is_backside;
8962 boolean action_exploding = ((action == ACTION_EXPLODING ||
8963 action == ACTION_SMASHED_BY_ROCK ||
8964 action == ACTION_SMASHED_BY_SPRING) &&
8965 element != EL_DIAMOND);
8966 boolean action_active = (action == ACTION_ACTIVE);
8967 boolean action_other = (action == ACTION_OTHER);
8969 for (j = 0; j < 8; j++)
8971 int effective_element = get_effective_element_EM(i, j);
8972 int effective_action = (j < 7 ? action :
8973 i == Xdrip_stretch ? action :
8974 i == Xdrip_stretchB ? action :
8975 i == Ydrip_1_s ? action :
8976 i == Ydrip_1_sB ? action :
8977 i == Yball_1 ? action :
8978 i == Xball_2 ? action :
8979 i == Yball_2 ? action :
8980 i == Yball_blank ? action :
8981 i == Ykey_1_blank ? action :
8982 i == Ykey_2_blank ? action :
8983 i == Ykey_3_blank ? action :
8984 i == Ykey_4_blank ? action :
8985 i == Ykey_5_blank ? action :
8986 i == Ykey_6_blank ? action :
8987 i == Ykey_7_blank ? action :
8988 i == Ykey_8_blank ? action :
8989 i == Ylenses_blank ? action :
8990 i == Ymagnify_blank ? action :
8991 i == Ygrass_blank ? action :
8992 i == Ydirt_blank ? action :
8993 i == Xsand_stonein_1 ? action :
8994 i == Xsand_stonein_2 ? action :
8995 i == Xsand_stonein_3 ? action :
8996 i == Xsand_stonein_4 ? action :
8997 i == Xsand_stoneout_1 ? action :
8998 i == Xsand_stoneout_2 ? action :
8999 i == Xboom_android ? ACTION_EXPLODING :
9000 action_exploding ? ACTION_EXPLODING :
9001 action_active ? action :
9002 action_other ? action :
9004 int graphic = (el_act_dir2img(effective_element, effective_action,
9006 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9008 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9009 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9010 boolean has_action_graphics = (graphic != base_graphic);
9011 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9012 struct GraphicInfo *g = &graphic_info[graphic];
9013 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9016 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9017 boolean special_animation = (action != ACTION_DEFAULT &&
9018 g->anim_frames == 3 &&
9019 g->anim_delay == 2 &&
9020 g->anim_mode & ANIM_LINEAR);
9021 int sync_frame = (i == Xdrip_stretch ? 7 :
9022 i == Xdrip_stretchB ? 7 :
9023 i == Ydrip_2_s ? j + 8 :
9024 i == Ydrip_2_sB ? j + 8 :
9033 i == Xfake_acid_1 ? 0 :
9034 i == Xfake_acid_2 ? 10 :
9035 i == Xfake_acid_3 ? 20 :
9036 i == Xfake_acid_4 ? 30 :
9037 i == Xfake_acid_5 ? 40 :
9038 i == Xfake_acid_6 ? 50 :
9039 i == Xfake_acid_7 ? 60 :
9040 i == Xfake_acid_8 ? 70 :
9041 i == Xfake_acid_1_player ? 0 :
9042 i == Xfake_acid_2_player ? 10 :
9043 i == Xfake_acid_3_player ? 20 :
9044 i == Xfake_acid_4_player ? 30 :
9045 i == Xfake_acid_5_player ? 40 :
9046 i == Xfake_acid_6_player ? 50 :
9047 i == Xfake_acid_7_player ? 60 :
9048 i == Xfake_acid_8_player ? 70 :
9050 i == Yball_2 ? j + 8 :
9051 i == Yball_blank ? j + 1 :
9052 i == Ykey_1_blank ? j + 1 :
9053 i == Ykey_2_blank ? j + 1 :
9054 i == Ykey_3_blank ? j + 1 :
9055 i == Ykey_4_blank ? j + 1 :
9056 i == Ykey_5_blank ? j + 1 :
9057 i == Ykey_6_blank ? j + 1 :
9058 i == Ykey_7_blank ? j + 1 :
9059 i == Ykey_8_blank ? j + 1 :
9060 i == Ylenses_blank ? j + 1 :
9061 i == Ymagnify_blank ? j + 1 :
9062 i == Ygrass_blank ? j + 1 :
9063 i == Ydirt_blank ? j + 1 :
9064 i == Xamoeba_1 ? 0 :
9065 i == Xamoeba_2 ? 1 :
9066 i == Xamoeba_3 ? 2 :
9067 i == Xamoeba_4 ? 3 :
9068 i == Xamoeba_5 ? 0 :
9069 i == Xamoeba_6 ? 1 :
9070 i == Xamoeba_7 ? 2 :
9071 i == Xamoeba_8 ? 3 :
9072 i == Xexit_2 ? j + 8 :
9073 i == Xexit_3 ? j + 16 :
9074 i == Xdynamite_1 ? 0 :
9075 i == Xdynamite_2 ? 8 :
9076 i == Xdynamite_3 ? 16 :
9077 i == Xdynamite_4 ? 24 :
9078 i == Xsand_stonein_1 ? j + 1 :
9079 i == Xsand_stonein_2 ? j + 9 :
9080 i == Xsand_stonein_3 ? j + 17 :
9081 i == Xsand_stonein_4 ? j + 25 :
9082 i == Xsand_stoneout_1 && j == 0 ? 0 :
9083 i == Xsand_stoneout_1 && j == 1 ? 0 :
9084 i == Xsand_stoneout_1 && j == 2 ? 1 :
9085 i == Xsand_stoneout_1 && j == 3 ? 2 :
9086 i == Xsand_stoneout_1 && j == 4 ? 2 :
9087 i == Xsand_stoneout_1 && j == 5 ? 3 :
9088 i == Xsand_stoneout_1 && j == 6 ? 4 :
9089 i == Xsand_stoneout_1 && j == 7 ? 4 :
9090 i == Xsand_stoneout_2 && j == 0 ? 5 :
9091 i == Xsand_stoneout_2 && j == 1 ? 6 :
9092 i == Xsand_stoneout_2 && j == 2 ? 7 :
9093 i == Xsand_stoneout_2 && j == 3 ? 8 :
9094 i == Xsand_stoneout_2 && j == 4 ? 9 :
9095 i == Xsand_stoneout_2 && j == 5 ? 11 :
9096 i == Xsand_stoneout_2 && j == 6 ? 13 :
9097 i == Xsand_stoneout_2 && j == 7 ? 15 :
9098 i == Xboom_bug && j == 1 ? 2 :
9099 i == Xboom_bug && j == 2 ? 2 :
9100 i == Xboom_bug && j == 3 ? 4 :
9101 i == Xboom_bug && j == 4 ? 4 :
9102 i == Xboom_bug && j == 5 ? 2 :
9103 i == Xboom_bug && j == 6 ? 2 :
9104 i == Xboom_bug && j == 7 ? 0 :
9105 i == Xboom_tank && j == 1 ? 2 :
9106 i == Xboom_tank && j == 2 ? 2 :
9107 i == Xboom_tank && j == 3 ? 4 :
9108 i == Xboom_tank && j == 4 ? 4 :
9109 i == Xboom_tank && j == 5 ? 2 :
9110 i == Xboom_tank && j == 6 ? 2 :
9111 i == Xboom_tank && j == 7 ? 0 :
9112 i == Xboom_android && j == 7 ? 6 :
9113 i == Xboom_1 && j == 1 ? 2 :
9114 i == Xboom_1 && j == 2 ? 2 :
9115 i == Xboom_1 && j == 3 ? 4 :
9116 i == Xboom_1 && j == 4 ? 4 :
9117 i == Xboom_1 && j == 5 ? 6 :
9118 i == Xboom_1 && j == 6 ? 6 :
9119 i == Xboom_1 && j == 7 ? 8 :
9120 i == Xboom_2 && j == 0 ? 8 :
9121 i == Xboom_2 && j == 1 ? 8 :
9122 i == Xboom_2 && j == 2 ? 10 :
9123 i == Xboom_2 && j == 3 ? 10 :
9124 i == Xboom_2 && j == 4 ? 10 :
9125 i == Xboom_2 && j == 5 ? 12 :
9126 i == Xboom_2 && j == 6 ? 12 :
9127 i == Xboom_2 && j == 7 ? 12 :
9128 special_animation && j == 4 ? 3 :
9129 effective_action != action ? 0 :
9131 int frame = getAnimationFrame(g->anim_frames,
9134 g->anim_start_frame,
9137 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9138 g->double_movement && is_backside);
9140 g_em->bitmap = src_bitmap;
9141 g_em->src_x = src_x;
9142 g_em->src_y = src_y;
9143 g_em->src_offset_x = 0;
9144 g_em->src_offset_y = 0;
9145 g_em->dst_offset_x = 0;
9146 g_em->dst_offset_y = 0;
9147 g_em->width = TILEX;
9148 g_em->height = TILEY;
9150 g_em->preserve_background = FALSE;
9152 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9155 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9156 effective_action == ACTION_MOVING ||
9157 effective_action == ACTION_PUSHING ||
9158 effective_action == ACTION_EATING)) ||
9159 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9160 effective_action == ACTION_EMPTYING)))
9163 (effective_action == ACTION_FALLING ||
9164 effective_action == ACTION_FILLING ||
9165 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9166 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9167 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9168 int num_steps = (i == Ydrip_1_s ? 16 :
9169 i == Ydrip_1_sB ? 16 :
9170 i == Ydrip_2_s ? 16 :
9171 i == Ydrip_2_sB ? 16 :
9172 i == Xsand_stonein_1 ? 32 :
9173 i == Xsand_stonein_2 ? 32 :
9174 i == Xsand_stonein_3 ? 32 :
9175 i == Xsand_stonein_4 ? 32 :
9176 i == Xsand_stoneout_1 ? 16 :
9177 i == Xsand_stoneout_2 ? 16 : 8);
9178 int cx = ABS(dx) * (TILEX / num_steps);
9179 int cy = ABS(dy) * (TILEY / num_steps);
9180 int step_frame = (i == Ydrip_2_s ? j + 8 :
9181 i == Ydrip_2_sB ? j + 8 :
9182 i == Xsand_stonein_2 ? j + 8 :
9183 i == Xsand_stonein_3 ? j + 16 :
9184 i == Xsand_stonein_4 ? j + 24 :
9185 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9186 int step = (is_backside ? step_frame : num_steps - step_frame);
9188 if (is_backside) // tile where movement starts
9190 if (dx < 0 || dy < 0)
9192 g_em->src_offset_x = cx * step;
9193 g_em->src_offset_y = cy * step;
9197 g_em->dst_offset_x = cx * step;
9198 g_em->dst_offset_y = cy * step;
9201 else // tile where movement ends
9203 if (dx < 0 || dy < 0)
9205 g_em->dst_offset_x = cx * step;
9206 g_em->dst_offset_y = cy * step;
9210 g_em->src_offset_x = cx * step;
9211 g_em->src_offset_y = cy * step;
9215 g_em->width = TILEX - cx * step;
9216 g_em->height = TILEY - cy * step;
9219 // create unique graphic identifier to decide if tile must be redrawn
9220 /* bit 31 - 16 (16 bit): EM style graphic
9221 bit 15 - 12 ( 4 bit): EM style frame
9222 bit 11 - 6 ( 6 bit): graphic width
9223 bit 5 - 0 ( 6 bit): graphic height */
9224 g_em->unique_identifier =
9225 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9229 for (i = 0; i < GAME_TILE_MAX; i++)
9231 for (j = 0; j < 8; j++)
9233 int element = object_mapping[i].element_rnd;
9234 int action = object_mapping[i].action;
9235 int direction = object_mapping[i].direction;
9236 boolean is_backside = object_mapping[i].is_backside;
9237 int graphic_action = el_act_dir2img(element, action, direction);
9238 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9240 if ((action == ACTION_SMASHED_BY_ROCK ||
9241 action == ACTION_SMASHED_BY_SPRING ||
9242 action == ACTION_EATING) &&
9243 graphic_action == graphic_default)
9245 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9246 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9247 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9248 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9251 // no separate animation for "smashed by rock" -- use rock instead
9252 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9253 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9255 g_em->bitmap = g_xx->bitmap;
9256 g_em->src_x = g_xx->src_x;
9257 g_em->src_y = g_xx->src_y;
9258 g_em->src_offset_x = g_xx->src_offset_x;
9259 g_em->src_offset_y = g_xx->src_offset_y;
9260 g_em->dst_offset_x = g_xx->dst_offset_x;
9261 g_em->dst_offset_y = g_xx->dst_offset_y;
9262 g_em->width = g_xx->width;
9263 g_em->height = g_xx->height;
9264 g_em->unique_identifier = g_xx->unique_identifier;
9267 g_em->preserve_background = TRUE;
9272 for (p = 0; p < MAX_PLAYERS; p++)
9274 for (i = 0; i < PLY_MAX; i++)
9276 int element = player_mapping[p][i].element_rnd;
9277 int action = player_mapping[p][i].action;
9278 int direction = player_mapping[p][i].direction;
9280 for (j = 0; j < 8; j++)
9282 int effective_element = element;
9283 int effective_action = action;
9284 int graphic = (direction == MV_NONE ?
9285 el_act2img(effective_element, effective_action) :
9286 el_act_dir2img(effective_element, effective_action,
9288 struct GraphicInfo *g = &graphic_info[graphic];
9289 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9293 int frame = getAnimationFrame(g->anim_frames,
9296 g->anim_start_frame,
9299 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9301 g_em->bitmap = src_bitmap;
9302 g_em->src_x = src_x;
9303 g_em->src_y = src_y;
9304 g_em->src_offset_x = 0;
9305 g_em->src_offset_y = 0;
9306 g_em->dst_offset_x = 0;
9307 g_em->dst_offset_y = 0;
9308 g_em->width = TILEX;
9309 g_em->height = TILEY;
9315 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9316 boolean any_player_moving,
9317 boolean any_player_snapping,
9318 boolean any_player_dropping)
9320 if (frame == 7 && !any_player_dropping)
9322 if (!local_player->was_waiting)
9324 if (!CheckSaveEngineSnapshotToList())
9327 local_player->was_waiting = TRUE;
9330 else if (any_player_moving || any_player_snapping || any_player_dropping)
9332 local_player->was_waiting = FALSE;
9336 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9337 boolean murphy_is_dropping)
9339 if (murphy_is_waiting)
9341 if (!local_player->was_waiting)
9343 if (!CheckSaveEngineSnapshotToList())
9346 local_player->was_waiting = TRUE;
9351 local_player->was_waiting = FALSE;
9355 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9356 boolean button_released)
9358 if (button_released)
9360 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9361 CheckSaveEngineSnapshotToList();
9363 else if (element_clicked)
9365 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9366 CheckSaveEngineSnapshotToList();
9368 game.snapshot.changed_action = TRUE;
9372 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9373 boolean any_player_moving,
9374 boolean any_player_snapping,
9375 boolean any_player_dropping)
9377 if (tape.single_step && tape.recording && !tape.pausing)
9378 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9379 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9381 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9382 any_player_snapping, any_player_dropping);
9384 return tape.pausing;
9387 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9388 boolean murphy_is_dropping)
9390 boolean murphy_starts_dropping = FALSE;
9393 for (i = 0; i < MAX_PLAYERS; i++)
9394 if (stored_player[i].force_dropping)
9395 murphy_starts_dropping = TRUE;
9397 if (tape.single_step && tape.recording && !tape.pausing)
9398 if (murphy_is_waiting && !murphy_starts_dropping)
9399 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9401 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9404 void CheckSingleStepMode_MM(boolean element_clicked,
9405 boolean button_released)
9407 if (tape.single_step && tape.recording && !tape.pausing)
9408 if (button_released)
9409 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9411 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9414 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9415 int graphic, int sync_frame, int x, int y)
9417 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9419 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9422 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9424 return (IS_NEXT_FRAME(sync_frame, graphic));
9427 int getGraphicInfo_Delay(int graphic)
9429 return graphic_info[graphic].anim_delay;
9432 void PlayMenuSoundExt(int sound)
9434 if (sound == SND_UNDEFINED)
9437 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9438 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9441 if (IS_LOOP_SOUND(sound))
9442 PlaySoundLoop(sound);
9447 void PlayMenuSound(void)
9449 PlayMenuSoundExt(menu.sound[game_status]);
9452 void PlayMenuSoundStereo(int sound, int stereo_position)
9454 if (sound == SND_UNDEFINED)
9457 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9458 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9461 if (IS_LOOP_SOUND(sound))
9462 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9464 PlaySoundStereo(sound, stereo_position);
9467 void PlayMenuSoundIfLoopExt(int sound)
9469 if (sound == SND_UNDEFINED)
9472 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9473 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9476 if (IS_LOOP_SOUND(sound))
9477 PlaySoundLoop(sound);
9480 void PlayMenuSoundIfLoop(void)
9482 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9485 void PlayMenuMusicExt(int music)
9487 if (music == MUS_UNDEFINED)
9490 if (!setup.sound_music)
9493 if (IS_LOOP_MUSIC(music))
9494 PlayMusicLoop(music);
9499 void PlayMenuMusic(void)
9501 char *curr_music = getCurrentlyPlayingMusicFilename();
9502 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9504 if (!strEqual(curr_music, next_music))
9505 PlayMenuMusicExt(menu.music[game_status]);
9508 void PlayMenuSoundsAndMusic(void)
9514 static void FadeMenuSounds(void)
9519 static void FadeMenuMusic(void)
9521 char *curr_music = getCurrentlyPlayingMusicFilename();
9522 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9524 if (!strEqual(curr_music, next_music))
9528 void FadeMenuSoundsAndMusic(void)
9534 void PlaySoundActivating(void)
9537 PlaySound(SND_MENU_ITEM_ACTIVATING);
9541 void PlaySoundSelecting(void)
9544 PlaySound(SND_MENU_ITEM_SELECTING);
9548 void ToggleFullscreenIfNeeded(void)
9550 // if setup and video fullscreen state are already matching, nothing do do
9551 if (setup.fullscreen == video.fullscreen_enabled ||
9552 !video.fullscreen_available)
9555 SDLSetWindowFullscreen(setup.fullscreen);
9557 // set setup value according to successfully changed fullscreen mode
9558 setup.fullscreen = video.fullscreen_enabled;
9561 void ChangeWindowScalingIfNeeded(void)
9563 // if setup and video window scaling are already matching, nothing do do
9564 if (setup.window_scaling_percent == video.window_scaling_percent ||
9565 video.fullscreen_enabled)
9568 SDLSetWindowScaling(setup.window_scaling_percent);
9570 // set setup value according to successfully changed window scaling
9571 setup.window_scaling_percent = video.window_scaling_percent;
9574 void ChangeVsyncModeIfNeeded(void)
9576 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9577 int video_vsync_mode = video.vsync_mode;
9579 // if setup and video vsync mode are already matching, nothing do do
9580 if (setup_vsync_mode == video_vsync_mode)
9583 // if renderer is using OpenGL, vsync mode can directly be changed
9584 SDLSetScreenVsyncMode(setup.vsync_mode);
9586 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9587 if (video.vsync_mode == video_vsync_mode)
9589 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9591 // save backbuffer content which gets lost when re-creating screen
9592 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9594 // force re-creating screen and renderer to set new vsync mode
9595 video.fullscreen_enabled = !setup.fullscreen;
9597 // when creating new renderer, destroy textures linked to old renderer
9598 FreeAllImageTextures(); // needs old renderer to free the textures
9600 // re-create screen and renderer (including change of vsync mode)
9601 ChangeVideoModeIfNeeded(setup.fullscreen);
9603 // set setup value according to successfully changed fullscreen mode
9604 setup.fullscreen = video.fullscreen_enabled;
9606 // restore backbuffer content from temporary backbuffer backup bitmap
9607 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9608 FreeBitmap(tmp_backbuffer);
9610 // update visible window/screen
9611 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9613 // when changing vsync mode, re-create textures for new renderer
9614 InitImageTextures();
9617 // set setup value according to successfully changed vsync mode
9618 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9621 static void JoinRectangles(int *x, int *y, int *width, int *height,
9622 int x2, int y2, int width2, int height2)
9624 // do not join with "off-screen" rectangle
9625 if (x2 == -1 || y2 == -1)
9630 *width = MAX(*width, width2);
9631 *height = MAX(*height, height2);
9634 void SetAnimStatus(int anim_status_new)
9636 if (anim_status_new == GAME_MODE_MAIN)
9637 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9638 else if (anim_status_new == GAME_MODE_NAMES)
9639 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9640 else if (anim_status_new == GAME_MODE_SCORES)
9641 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9643 global.anim_status_next = anim_status_new;
9645 // directly set screen modes that are entered without fading
9646 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9647 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9648 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9649 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9650 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9651 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9652 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9653 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9654 global.anim_status = global.anim_status_next;
9657 void SetGameStatus(int game_status_new)
9659 if (game_status_new != game_status)
9660 game_status_last_screen = game_status;
9662 game_status = game_status_new;
9664 SetAnimStatus(game_status_new);
9667 void SetFontStatus(int game_status_new)
9669 static int last_game_status = -1;
9671 if (game_status_new != -1)
9673 // set game status for font use after storing last game status
9674 last_game_status = game_status;
9675 game_status = game_status_new;
9679 // reset game status after font use from last stored game status
9680 game_status = last_game_status;
9684 void ResetFontStatus(void)
9689 void SetLevelSetInfo(char *identifier, int level_nr)
9691 setString(&levelset.identifier, identifier);
9693 levelset.level_nr = level_nr;
9696 boolean CheckIfAllViewportsHaveChanged(void)
9698 // if game status has not changed, viewports have not changed either
9699 if (game_status == game_status_last)
9702 // check if all viewports have changed with current game status
9704 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9705 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9706 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9707 int new_real_sx = vp_playfield->x;
9708 int new_real_sy = vp_playfield->y;
9709 int new_full_sxsize = vp_playfield->width;
9710 int new_full_sysize = vp_playfield->height;
9711 int new_dx = vp_door_1->x;
9712 int new_dy = vp_door_1->y;
9713 int new_dxsize = vp_door_1->width;
9714 int new_dysize = vp_door_1->height;
9715 int new_vx = vp_door_2->x;
9716 int new_vy = vp_door_2->y;
9717 int new_vxsize = vp_door_2->width;
9718 int new_vysize = vp_door_2->height;
9720 boolean playfield_viewport_has_changed =
9721 (new_real_sx != REAL_SX ||
9722 new_real_sy != REAL_SY ||
9723 new_full_sxsize != FULL_SXSIZE ||
9724 new_full_sysize != FULL_SYSIZE);
9726 boolean door_1_viewport_has_changed =
9729 new_dxsize != DXSIZE ||
9730 new_dysize != DYSIZE);
9732 boolean door_2_viewport_has_changed =
9735 new_vxsize != VXSIZE ||
9736 new_vysize != VYSIZE ||
9737 game_status_last == GAME_MODE_EDITOR);
9739 return (playfield_viewport_has_changed &&
9740 door_1_viewport_has_changed &&
9741 door_2_viewport_has_changed);
9744 boolean CheckFadeAll(void)
9746 return (CheckIfGlobalBorderHasChanged() ||
9747 CheckIfAllViewportsHaveChanged());
9750 void ChangeViewportPropertiesIfNeeded(void)
9752 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9753 FALSE : setup.small_game_graphics);
9754 int gfx_game_mode = getGlobalGameStatus(game_status);
9755 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9757 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9758 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9759 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9760 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9761 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9762 int new_win_xsize = vp_window->width;
9763 int new_win_ysize = vp_window->height;
9764 int border_left = vp_playfield->border_left;
9765 int border_right = vp_playfield->border_right;
9766 int border_top = vp_playfield->border_top;
9767 int border_bottom = vp_playfield->border_bottom;
9768 int new_sx = vp_playfield->x + border_left;
9769 int new_sy = vp_playfield->y + border_top;
9770 int new_sxsize = vp_playfield->width - border_left - border_right;
9771 int new_sysize = vp_playfield->height - border_top - border_bottom;
9772 int new_real_sx = vp_playfield->x;
9773 int new_real_sy = vp_playfield->y;
9774 int new_full_sxsize = vp_playfield->width;
9775 int new_full_sysize = vp_playfield->height;
9776 int new_dx = vp_door_1->x;
9777 int new_dy = vp_door_1->y;
9778 int new_dxsize = vp_door_1->width;
9779 int new_dysize = vp_door_1->height;
9780 int new_vx = vp_door_2->x;
9781 int new_vy = vp_door_2->y;
9782 int new_vxsize = vp_door_2->width;
9783 int new_vysize = vp_door_2->height;
9784 int new_ex = vp_door_3->x;
9785 int new_ey = vp_door_3->y;
9786 int new_exsize = vp_door_3->width;
9787 int new_eysize = vp_door_3->height;
9788 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9789 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9790 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9791 int new_scr_fieldx = new_sxsize / tilesize;
9792 int new_scr_fieldy = new_sysize / tilesize;
9793 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9794 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9795 boolean init_gfx_buffers = FALSE;
9796 boolean init_video_buffer = FALSE;
9797 boolean init_gadgets_and_anims = FALSE;
9798 boolean init_em_graphics = FALSE;
9800 if (new_win_xsize != WIN_XSIZE ||
9801 new_win_ysize != WIN_YSIZE)
9803 WIN_XSIZE = new_win_xsize;
9804 WIN_YSIZE = new_win_ysize;
9806 init_video_buffer = TRUE;
9807 init_gfx_buffers = TRUE;
9808 init_gadgets_and_anims = TRUE;
9810 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9813 if (new_scr_fieldx != SCR_FIELDX ||
9814 new_scr_fieldy != SCR_FIELDY)
9816 // this always toggles between MAIN and GAME when using small tile size
9818 SCR_FIELDX = new_scr_fieldx;
9819 SCR_FIELDY = new_scr_fieldy;
9821 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9832 new_sxsize != SXSIZE ||
9833 new_sysize != SYSIZE ||
9834 new_dxsize != DXSIZE ||
9835 new_dysize != DYSIZE ||
9836 new_vxsize != VXSIZE ||
9837 new_vysize != VYSIZE ||
9838 new_exsize != EXSIZE ||
9839 new_eysize != EYSIZE ||
9840 new_real_sx != REAL_SX ||
9841 new_real_sy != REAL_SY ||
9842 new_full_sxsize != FULL_SXSIZE ||
9843 new_full_sysize != FULL_SYSIZE ||
9844 new_tilesize_var != TILESIZE_VAR
9847 // ------------------------------------------------------------------------
9848 // determine next fading area for changed viewport definitions
9849 // ------------------------------------------------------------------------
9851 // start with current playfield area (default fading area)
9854 FADE_SXSIZE = FULL_SXSIZE;
9855 FADE_SYSIZE = FULL_SYSIZE;
9857 // add new playfield area if position or size has changed
9858 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9859 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9861 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9862 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9865 // add current and new door 1 area if position or size has changed
9866 if (new_dx != DX || new_dy != DY ||
9867 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9869 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9870 DX, DY, DXSIZE, DYSIZE);
9871 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9872 new_dx, new_dy, new_dxsize, new_dysize);
9875 // add current and new door 2 area if position or size has changed
9876 if (new_vx != VX || new_vy != VY ||
9877 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9879 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9880 VX, VY, VXSIZE, VYSIZE);
9881 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9882 new_vx, new_vy, new_vxsize, new_vysize);
9885 // ------------------------------------------------------------------------
9886 // handle changed tile size
9887 // ------------------------------------------------------------------------
9889 if (new_tilesize_var != TILESIZE_VAR)
9891 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9893 // changing tile size invalidates scroll values of engine snapshots
9894 FreeEngineSnapshotSingle();
9896 // changing tile size requires update of graphic mapping for EM engine
9897 init_em_graphics = TRUE;
9908 SXSIZE = new_sxsize;
9909 SYSIZE = new_sysize;
9910 DXSIZE = new_dxsize;
9911 DYSIZE = new_dysize;
9912 VXSIZE = new_vxsize;
9913 VYSIZE = new_vysize;
9914 EXSIZE = new_exsize;
9915 EYSIZE = new_eysize;
9916 REAL_SX = new_real_sx;
9917 REAL_SY = new_real_sy;
9918 FULL_SXSIZE = new_full_sxsize;
9919 FULL_SYSIZE = new_full_sysize;
9920 TILESIZE_VAR = new_tilesize_var;
9922 init_gfx_buffers = TRUE;
9923 init_gadgets_and_anims = TRUE;
9925 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9926 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9929 if (init_gfx_buffers)
9931 // Debug("tools:viewport", "init_gfx_buffers");
9933 SCR_FIELDX = new_scr_fieldx_buffers;
9934 SCR_FIELDY = new_scr_fieldy_buffers;
9938 SCR_FIELDX = new_scr_fieldx;
9939 SCR_FIELDY = new_scr_fieldy;
9941 SetDrawDeactivationMask(REDRAW_NONE);
9942 SetDrawBackgroundMask(REDRAW_FIELD);
9945 if (init_video_buffer)
9947 // Debug("tools:viewport", "init_video_buffer");
9949 FreeAllImageTextures(); // needs old renderer to free the textures
9951 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9952 InitImageTextures();
9955 if (init_gadgets_and_anims)
9957 // Debug("tools:viewport", "init_gadgets_and_anims");
9960 InitGlobalAnimations();
9963 if (init_em_graphics)
9965 InitGraphicInfo_EM();
9969 void OpenURL(char *url)
9974 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9976 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9980 // ============================================================================
9982 // ============================================================================
9984 #if defined(PLATFORM_WINDOWS)
9985 /* FILETIME of Jan 1 1970 00:00:00. */
9986 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9989 * timezone information is stored outside the kernel so tzp isn't used anymore.
9991 * Note: this function is not for Win32 high precision timing purpose. See
9994 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9997 SYSTEMTIME system_time;
9998 ULARGE_INTEGER ularge;
10000 GetSystemTime(&system_time);
10001 SystemTimeToFileTime(&system_time, &file_time);
10002 ularge.LowPart = file_time.dwLowDateTime;
10003 ularge.HighPart = file_time.dwHighDateTime;
10005 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10006 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10012 static char *test_init_uuid_random_function_simple(void)
10014 static char seed_text[100];
10015 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10017 sprintf(seed_text, "%d", seed);
10022 static char *test_init_uuid_random_function_better(void)
10024 static char seed_text[100];
10025 struct timeval current_time;
10027 gettimeofday(¤t_time, NULL);
10029 prng_seed_bytes(¤t_time, sizeof(current_time));
10031 sprintf(seed_text, "%ld.%ld",
10032 (long)current_time.tv_sec,
10033 (long)current_time.tv_usec);
10038 #if defined(PLATFORM_WINDOWS)
10039 static char *test_init_uuid_random_function_better_windows(void)
10041 static char seed_text[100];
10042 struct timeval current_time;
10044 gettimeofday_windows(¤t_time, NULL);
10046 prng_seed_bytes(¤t_time, sizeof(current_time));
10048 sprintf(seed_text, "%ld.%ld",
10049 (long)current_time.tv_sec,
10050 (long)current_time.tv_usec);
10056 static unsigned int test_uuid_random_function_simple(int max)
10058 return GetSimpleRandom(max);
10061 static unsigned int test_uuid_random_function_better(int max)
10063 return (max > 0 ? prng_get_uint() % max : 0);
10066 #if defined(PLATFORM_WINDOWS)
10067 #define NUM_UUID_TESTS 3
10069 #define NUM_UUID_TESTS 2
10072 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10074 struct hashtable *hash_seeds =
10075 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10076 struct hashtable *hash_uuids =
10077 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10078 static char message[100];
10081 char *random_name = (nr == 0 ? "simple" : "better");
10082 char *random_type = (always_seed ? "always" : "only once");
10083 char *(*init_random_function)(void) =
10085 test_init_uuid_random_function_simple :
10086 test_init_uuid_random_function_better);
10087 unsigned int (*random_function)(int) =
10089 test_uuid_random_function_simple :
10090 test_uuid_random_function_better);
10093 #if defined(PLATFORM_WINDOWS)
10096 random_name = "windows";
10097 init_random_function = test_init_uuid_random_function_better_windows;
10103 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10104 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10106 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10107 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10108 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10110 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10114 // always initialize random number generator at least once
10115 init_random_function();
10117 unsigned int time_start = SDL_GetTicks();
10119 for (i = 0; i < num_uuids; i++)
10123 char *seed = getStringCopy(init_random_function());
10125 hashtable_remove(hash_seeds, seed);
10126 hashtable_insert(hash_seeds, seed, "1");
10129 char *uuid = getStringCopy(getUUIDExt(random_function));
10131 hashtable_remove(hash_uuids, uuid);
10132 hashtable_insert(hash_uuids, uuid, "1");
10135 int num_unique_seeds = hashtable_count(hash_seeds);
10136 int num_unique_uuids = hashtable_count(hash_uuids);
10138 unsigned int time_needed = SDL_GetTicks() - time_start;
10140 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10142 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10145 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10147 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10148 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10150 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10152 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10154 Request(message, REQ_CONFIRM);
10156 hashtable_destroy(hash_seeds, 0);
10157 hashtable_destroy(hash_uuids, 0);
10160 void TestGeneratingUUIDs(void)
10162 int num_uuids = 1000000;
10165 for (i = 0; i < NUM_UUID_TESTS; i++)
10166 for (j = 0; j < 2; j++)
10167 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10169 CloseAllAndExit(0);