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,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void MapToolButtons(unsigned int);
179 static void UnmapToolButtons(void);
180 static void HandleToolButtons(struct GadgetInfo *);
181 static int el_act_dir2crm(int, int, int);
182 static int el_act2crm(int, int);
184 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
185 static int request_gadget_id = -1;
187 static char *print_if_not_empty(int element)
189 static char *s = NULL;
190 char *token_name = element_info[element].token_name;
195 s = checked_malloc(strlen(token_name) + 10 + 1);
197 if (element != EL_EMPTY)
198 sprintf(s, "%d\t['%s']", element, token_name);
200 sprintf(s, "%d", element);
205 int getFieldbufferOffsetX_RND(int dir, int pos)
207 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
208 int dx = (dir & MV_HORIZONTAL ? pos : 0);
209 int dx_var = dx * TILESIZE_VAR / TILESIZE;
212 if (EVEN(SCR_FIELDX))
214 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
215 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
217 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
218 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
220 fx += (dx_var > 0 ? TILEX_VAR : 0);
227 if (full_lev_fieldx <= SCR_FIELDX)
229 if (EVEN(SCR_FIELDX))
230 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
232 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 int getFieldbufferOffsetY_RND(int dir, int pos)
240 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
241 int dy = (dir & MV_VERTICAL ? pos : 0);
242 int dy_var = dy * TILESIZE_VAR / TILESIZE;
245 if (EVEN(SCR_FIELDY))
247 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
248 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
250 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
251 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
253 fy += (dy_var > 0 ? TILEY_VAR : 0);
260 if (full_lev_fieldy <= SCR_FIELDY)
262 if (EVEN(SCR_FIELDY))
263 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
265 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 static int getLevelFromScreenX_RND(int sx)
273 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
276 int lx = LEVELX((px + dx) / TILESIZE_VAR);
281 static int getLevelFromScreenY_RND(int sy)
283 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
286 int ly = LEVELY((py + dy) / TILESIZE_VAR);
291 static int getLevelFromScreenX_EM(int sx)
293 int level_xsize = level.native_em_level->cav->width;
294 int full_xsize = level_xsize * TILESIZE_VAR;
296 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
298 int fx = getFieldbufferOffsetX_EM();
301 int lx = LEVELX((px + dx) / TILESIZE_VAR);
306 static int getLevelFromScreenY_EM(int sy)
308 int level_ysize = level.native_em_level->cav->height;
309 int full_ysize = level_ysize * TILESIZE_VAR;
311 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
313 int fy = getFieldbufferOffsetY_EM();
316 int ly = LEVELY((py + dy) / TILESIZE_VAR);
321 static int getLevelFromScreenX_SP(int sx)
323 int menBorder = setup.sp_show_border_elements;
324 int level_xsize = level.native_sp_level->width;
325 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
327 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
329 int fx = getFieldbufferOffsetX_SP();
332 int lx = LEVELX((px + dx) / TILESIZE_VAR);
337 static int getLevelFromScreenY_SP(int sy)
339 int menBorder = setup.sp_show_border_elements;
340 int level_ysize = level.native_sp_level->height;
341 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
343 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
345 int fy = getFieldbufferOffsetY_SP();
348 int ly = LEVELY((py + dy) / TILESIZE_VAR);
353 static int getLevelFromScreenX_MM(int sx)
355 int level_xsize = level.native_mm_level->fieldx;
356 int full_xsize = level_xsize * TILESIZE_VAR;
358 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
361 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
366 static int getLevelFromScreenY_MM(int sy)
368 int level_ysize = level.native_mm_level->fieldy;
369 int full_ysize = level_ysize * TILESIZE_VAR;
371 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
374 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
379 int getLevelFromScreenX(int x)
381 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
382 return getLevelFromScreenX_EM(x);
383 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
384 return getLevelFromScreenX_SP(x);
385 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
386 return getLevelFromScreenX_MM(x);
388 return getLevelFromScreenX_RND(x);
391 int getLevelFromScreenY(int y)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenY_EM(y);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenY_SP(y);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenY_MM(y);
400 return getLevelFromScreenY_RND(y);
403 int getScreenFieldSizeX(void)
405 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
408 int getScreenFieldSizeY(void)
410 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
413 void DumpTile(int x, int y)
420 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
423 if (!IN_LEV_FIELD(x, y))
425 Info("(not in level field)");
431 token_name = element_info[Tile[x][y]].token_name;
433 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
434 Info("Back: %s", print_if_not_empty(Back[x][y]));
435 Info("Store: %s", print_if_not_empty(Store[x][y]));
436 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
437 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
438 Info("MovPos: %d", MovPos[x][y]);
439 Info("MovDir: %d", MovDir[x][y]);
440 Info("MovDelay: %d", MovDelay[x][y]);
441 Info("ChangeDelay: %d", ChangeDelay[x][y]);
442 Info("CustomValue: %d", CustomValue[x][y]);
443 Info("GfxElement: %d", GfxElement[x][y]);
444 Info("GfxAction: %d", GfxAction[x][y]);
445 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
446 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 int GetDrawtoField(void)
486 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
489 static void RedrawPlayfield_RND(void)
491 if (game.envelope_active)
494 DrawLevel(REDRAW_ALL);
498 void RedrawPlayfield(void)
500 if (game_status != GAME_MODE_PLAYING)
503 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
504 RedrawPlayfield_EM(TRUE);
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
506 RedrawPlayfield_SP(TRUE);
507 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
508 RedrawPlayfield_MM();
509 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
510 RedrawPlayfield_RND();
512 BlitScreenToBitmap(backbuffer);
514 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
518 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
521 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
522 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
524 // may happen for "border.draw_masked.*" with undefined "global.border.*"
525 if (src_bitmap == NULL)
528 if (x == -1 && y == -1)
531 if (draw_target == DRAW_TO_SCREEN)
532 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
534 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
537 static void DrawMaskedBorderExt_FIELD(int draw_target)
539 if (global.border_status >= GAME_MODE_MAIN &&
540 global.border_status <= GAME_MODE_PLAYING &&
541 border.draw_masked[global.border_status])
542 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
546 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_1))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 (global.border_status != GAME_MODE_EDITOR ||
555 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
556 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
559 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
561 // when drawing to backbuffer, never draw border over open doors
562 if (draw_target == DRAW_TO_BACKBUFFER &&
563 (GetDoorState() & DOOR_OPEN_2))
566 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
567 global.border_status != GAME_MODE_EDITOR)
568 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
571 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
573 // currently not available
576 static void DrawMaskedBorderExt_ALL(int draw_target)
578 DrawMaskedBorderExt_FIELD(draw_target);
579 DrawMaskedBorderExt_DOOR_1(draw_target);
580 DrawMaskedBorderExt_DOOR_2(draw_target);
581 DrawMaskedBorderExt_DOOR_3(draw_target);
584 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
586 // never draw masked screen borders on borderless screens
587 if (global.border_status == GAME_MODE_LOADING ||
588 global.border_status == GAME_MODE_TITLE)
591 if (redraw_mask & REDRAW_ALL)
592 DrawMaskedBorderExt_ALL(draw_target);
595 if (redraw_mask & REDRAW_FIELD)
596 DrawMaskedBorderExt_FIELD(draw_target);
597 if (redraw_mask & REDRAW_DOOR_1)
598 DrawMaskedBorderExt_DOOR_1(draw_target);
599 if (redraw_mask & REDRAW_DOOR_2)
600 DrawMaskedBorderExt_DOOR_2(draw_target);
601 if (redraw_mask & REDRAW_DOOR_3)
602 DrawMaskedBorderExt_DOOR_3(draw_target);
606 void DrawMaskedBorder_FIELD(void)
608 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
611 void DrawMaskedBorder(int redraw_mask)
613 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
616 void DrawMaskedBorderToTarget(int draw_target)
618 if (draw_target == DRAW_TO_BACKBUFFER ||
619 draw_target == DRAW_TO_SCREEN)
621 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
625 int last_border_status = global.border_status;
627 if (draw_target == DRAW_TO_FADE_SOURCE)
629 global.border_status = gfx.fade_border_source_status;
630 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
632 else if (draw_target == DRAW_TO_FADE_TARGET)
634 global.border_status = gfx.fade_border_target_status;
635 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
638 // always use global border for PLAYING when restarting the game
639 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
640 global.border_status = GAME_MODE_PLAYING;
642 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
644 global.border_status = last_border_status;
645 gfx.masked_border_bitmap_ptr = backbuffer;
649 void DrawTileCursor(int draw_target, int drawing_stage)
651 int tile_cursor_active = (game_status == GAME_MODE_PLAYING);
653 DrawTileCursor_MM(draw_target, drawing_stage, tile_cursor_active);
656 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
658 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
661 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
663 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
664 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
666 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
669 void BlitScreenToBitmap(Bitmap *target_bitmap)
671 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
672 BlitScreenToBitmap_EM(target_bitmap);
673 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
674 BlitScreenToBitmap_SP(target_bitmap);
675 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
676 BlitScreenToBitmap_MM(target_bitmap);
677 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
678 BlitScreenToBitmap_RND(target_bitmap);
680 redraw_mask |= REDRAW_FIELD;
683 static void DrawFramesPerSecond(void)
686 int font_nr = FONT_TEXT_2;
687 int font_width = getFontWidth(font_nr);
688 int draw_deactivation_mask = GetDrawDeactivationMask();
689 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
691 // draw FPS with leading space (needed if field buffer deactivated)
692 sprintf(text, " %04.1f fps", global.frames_per_second);
694 // override draw deactivation mask (required for invisible warp mode)
695 SetDrawDeactivationMask(REDRAW_NONE);
697 // draw opaque FPS if field buffer deactivated, else draw masked FPS
698 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
699 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
701 // set draw deactivation mask to previous value
702 SetDrawDeactivationMask(draw_deactivation_mask);
704 // force full-screen redraw in this frame
705 redraw_mask = REDRAW_ALL;
709 static void PrintFrameTimeDebugging(void)
711 static unsigned int last_counter = 0;
712 unsigned int counter = Counter();
713 int diff_1 = counter - last_counter;
714 int diff_2 = diff_1 - GAME_FRAME_DELAY;
716 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
717 char diff_bar[2 * diff_2_max + 5];
721 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
723 for (i = 0; i < diff_2_max; i++)
724 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
725 i >= diff_2_max - diff_2_cut ? '-' : ' ');
727 diff_bar[pos++] = '|';
729 for (i = 0; i < diff_2_max; i++)
730 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
732 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
734 diff_bar[pos++] = '\0';
736 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
739 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
742 last_counter = counter;
746 static int unifiedRedrawMask(int mask)
748 if (mask & REDRAW_ALL)
751 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
757 static boolean equalRedrawMasks(int mask_1, int mask_2)
759 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
762 void BackToFront(void)
764 static int last_redraw_mask = REDRAW_NONE;
766 // force screen redraw in every frame to continue drawing global animations
767 // (but always use the last redraw mask to prevent unwanted side effects)
768 if (redraw_mask == REDRAW_NONE)
769 redraw_mask = last_redraw_mask;
771 last_redraw_mask = redraw_mask;
774 // masked border now drawn immediately when blitting backbuffer to window
776 // draw masked border to all viewports, if defined
777 DrawMaskedBorder(redraw_mask);
780 // draw frames per second (only if debug mode is enabled)
781 if (redraw_mask & REDRAW_FPS)
782 DrawFramesPerSecond();
784 // remove playfield redraw before potentially merging with doors redraw
785 if (DrawingDeactivated(REAL_SX, REAL_SY))
786 redraw_mask &= ~REDRAW_FIELD;
788 // redraw complete window if both playfield and (some) doors need redraw
789 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
790 redraw_mask = REDRAW_ALL;
792 /* although redrawing the whole window would be fine for normal gameplay,
793 being able to only redraw the playfield is required for deactivating
794 certain drawing areas (mainly playfield) to work, which is needed for
795 warp-forward to be fast enough (by skipping redraw of most frames) */
797 if (redraw_mask & REDRAW_ALL)
799 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
801 else if (redraw_mask & REDRAW_FIELD)
803 BlitBitmap(backbuffer, window,
804 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
806 else if (redraw_mask & REDRAW_DOORS)
808 // merge door areas to prevent calling screen redraw more than once
814 if (redraw_mask & REDRAW_DOOR_1)
818 x2 = MAX(x2, DX + DXSIZE);
819 y2 = MAX(y2, DY + DYSIZE);
822 if (redraw_mask & REDRAW_DOOR_2)
826 x2 = MAX(x2, VX + VXSIZE);
827 y2 = MAX(y2, VY + VYSIZE);
830 if (redraw_mask & REDRAW_DOOR_3)
834 x2 = MAX(x2, EX + EXSIZE);
835 y2 = MAX(y2, EY + EYSIZE);
838 // make sure that at least one pixel is blitted, and inside the screen
839 // (else nothing is blitted, causing the animations not to be updated)
840 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
841 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
842 x2 = MIN(MAX(1, x2), WIN_XSIZE);
843 y2 = MIN(MAX(1, y2), WIN_YSIZE);
845 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
848 redraw_mask = REDRAW_NONE;
851 PrintFrameTimeDebugging();
855 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
857 unsigned int frame_delay_value_old = GetVideoFrameDelay();
859 SetVideoFrameDelay(frame_delay_value);
863 SetVideoFrameDelay(frame_delay_value_old);
866 static int fade_type_skip = FADE_TYPE_NONE;
868 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
870 void (*draw_border_function)(void) = NULL;
871 int x, y, width, height;
872 int fade_delay, post_delay;
874 if (fade_type == FADE_TYPE_FADE_OUT)
876 if (fade_type_skip != FADE_TYPE_NONE)
878 // skip all fade operations until specified fade operation
879 if (fade_type & fade_type_skip)
880 fade_type_skip = FADE_TYPE_NONE;
885 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
889 redraw_mask |= fade_mask;
891 if (fade_type == FADE_TYPE_SKIP)
893 fade_type_skip = fade_mode;
898 fade_delay = fading.fade_delay;
899 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
901 if (fade_type_skip != FADE_TYPE_NONE)
903 // skip all fade operations until specified fade operation
904 if (fade_type & fade_type_skip)
905 fade_type_skip = FADE_TYPE_NONE;
910 if (global.autoplay_leveldir)
915 if (fade_mask == REDRAW_FIELD)
920 height = FADE_SYSIZE;
922 if (border.draw_masked_when_fading)
923 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
925 DrawMaskedBorder_FIELD(); // draw once
935 // when switching screens without fading, set fade delay to zero
936 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
939 // do not display black frame when fading out without fade delay
940 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
943 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
944 draw_border_function);
946 redraw_mask &= ~fade_mask;
948 ClearAutoRepeatKeyEvents();
951 static void SetScreenStates_BeforeFadingIn(void)
953 // temporarily set screen mode for animations to screen after fading in
954 global.anim_status = global.anim_status_next;
956 // store backbuffer with all animations that will be started after fading in
957 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
959 // set screen mode for animations back to fading
960 global.anim_status = GAME_MODE_PSEUDO_FADING;
963 static void SetScreenStates_AfterFadingIn(void)
965 // store new source screen (to use correct masked border for fading)
966 gfx.fade_border_source_status = global.border_status;
968 global.anim_status = global.anim_status_next;
971 static void SetScreenStates_BeforeFadingOut(void)
973 // store new target screen (to use correct masked border for fading)
974 gfx.fade_border_target_status = game_status;
976 // set screen mode for animations to fading
977 global.anim_status = GAME_MODE_PSEUDO_FADING;
979 // store backbuffer with all animations that will be stopped for fading out
980 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
983 static void SetScreenStates_AfterFadingOut(void)
985 global.border_status = game_status;
987 // always use global border for PLAYING when restarting the game
988 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
989 global.border_status = GAME_MODE_PLAYING;
992 void FadeIn(int fade_mask)
994 SetScreenStates_BeforeFadingIn();
997 DrawMaskedBorder(REDRAW_ALL);
1000 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1001 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1003 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1007 FADE_SXSIZE = FULL_SXSIZE;
1008 FADE_SYSIZE = FULL_SYSIZE;
1010 // activate virtual buttons depending on upcoming game status
1011 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1012 game_status == GAME_MODE_PLAYING && !tape.playing)
1013 SetOverlayActive(TRUE);
1015 SetScreenStates_AfterFadingIn();
1017 // force update of global animation status in case of rapid screen changes
1018 redraw_mask = REDRAW_ALL;
1022 void FadeOut(int fade_mask)
1024 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1025 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1026 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1029 SetScreenStates_BeforeFadingOut();
1031 SetTileCursorActive(FALSE);
1032 SetOverlayActive(FALSE);
1035 DrawMaskedBorder(REDRAW_ALL);
1038 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1039 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1041 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1043 SetScreenStates_AfterFadingOut();
1046 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1048 static struct TitleFadingInfo fading_leave_stored;
1051 fading_leave_stored = fading_leave;
1053 fading = fading_leave_stored;
1056 void FadeSetEnterMenu(void)
1058 fading = menu.enter_menu;
1060 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1063 void FadeSetLeaveMenu(void)
1065 fading = menu.leave_menu;
1067 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1070 void FadeSetEnterScreen(void)
1072 fading = menu.enter_screen[game_status];
1074 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1077 void FadeSetNextScreen(void)
1079 fading = menu.next_screen[game_status];
1081 // (do not overwrite fade mode set by FadeSetEnterScreen)
1082 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1085 void FadeSetLeaveScreen(void)
1087 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1090 void FadeSetFromType(int type)
1092 if (type & TYPE_ENTER_SCREEN)
1093 FadeSetEnterScreen();
1094 else if (type & TYPE_ENTER)
1096 else if (type & TYPE_LEAVE)
1100 void FadeSetDisabled(void)
1102 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1104 fading = fading_none;
1107 void FadeSkipNextFadeIn(void)
1109 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1112 void FadeSkipNextFadeOut(void)
1114 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1117 static int getGlobalGameStatus(int status)
1119 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1120 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1124 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1126 if (graphic == IMG_UNDEFINED)
1127 return IMG_UNDEFINED;
1129 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1131 return (graphic_info[graphic].bitmap != NULL || redefined ?
1132 graphic : default_graphic);
1135 static int getBackgroundImage(int graphic)
1137 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1140 static int getGlobalBorderImage(int graphic)
1142 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1145 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1147 int status = getGlobalGameStatus(status_raw);
1149 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1150 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1151 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1152 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1154 int graphic_final = getGlobalBorderImage(graphic);
1156 return graphic_info[graphic_final].bitmap;
1159 void SetBackgroundImage(int graphic, int redraw_mask)
1161 struct GraphicInfo *g = &graphic_info[graphic];
1162 struct GraphicInfo g_undefined = { 0 };
1164 if (graphic == IMG_UNDEFINED)
1167 // always use original size bitmap for backgrounds, if existing
1168 Bitmap *bitmap = (g->bitmaps != NULL &&
1169 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1170 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1172 // remove every mask before setting mask for window, and
1173 // remove window area mask before setting mask for main or door area
1174 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1176 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1177 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1178 SetBackgroundBitmap(bitmap, redraw_mask,
1180 g->width, g->height);
1183 void SetWindowBackgroundImageIfDefined(int graphic)
1185 if (graphic_info[graphic].bitmap)
1186 SetBackgroundImage(graphic, REDRAW_ALL);
1189 void SetMainBackgroundImageIfDefined(int graphic)
1191 if (graphic_info[graphic].bitmap)
1192 SetBackgroundImage(graphic, REDRAW_FIELD);
1195 void SetDoorBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1201 void SetWindowBackgroundImage(int graphic)
1203 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1206 void SetMainBackgroundImage(int graphic)
1208 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1211 void SetDoorBackgroundImage(int graphic)
1213 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1216 void SetPanelBackground(void)
1218 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1221 void DrawBackground(int x, int y, int width, int height)
1223 // "drawto" might still point to playfield buffer here (hall of fame)
1224 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1226 if (IN_GFX_FIELD_FULL(x, y))
1227 redraw_mask |= REDRAW_FIELD;
1228 else if (IN_GFX_DOOR_1(x, y))
1229 redraw_mask |= REDRAW_DOOR_1;
1230 else if (IN_GFX_DOOR_2(x, y))
1231 redraw_mask |= REDRAW_DOOR_2;
1232 else if (IN_GFX_DOOR_3(x, y))
1233 redraw_mask |= REDRAW_DOOR_3;
1236 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1238 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1240 if (font->bitmap == NULL)
1243 DrawBackground(x, y, width, height);
1246 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1248 struct GraphicInfo *g = &graphic_info[graphic];
1250 if (g->bitmap == NULL)
1253 DrawBackground(x, y, width, height);
1256 static int game_status_last = -1;
1257 static Bitmap *global_border_bitmap_last = NULL;
1258 static Bitmap *global_border_bitmap = NULL;
1259 static int real_sx_last = -1, real_sy_last = -1;
1260 static int full_sxsize_last = -1, full_sysize_last = -1;
1261 static int dx_last = -1, dy_last = -1;
1262 static int dxsize_last = -1, dysize_last = -1;
1263 static int vx_last = -1, vy_last = -1;
1264 static int vxsize_last = -1, vysize_last = -1;
1265 static int ex_last = -1, ey_last = -1;
1266 static int exsize_last = -1, eysize_last = -1;
1268 boolean CheckIfGlobalBorderHasChanged(void)
1270 // if game status has not changed, global border has not changed either
1271 if (game_status == game_status_last)
1274 // determine and store new global border bitmap for current game status
1275 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1277 return (global_border_bitmap_last != global_border_bitmap);
1280 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1282 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1283 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1285 // if game status has not changed, nothing has to be redrawn
1286 if (game_status == game_status_last)
1289 // redraw if last screen was title screen
1290 if (game_status_last == GAME_MODE_TITLE)
1293 // redraw if global screen border has changed
1294 if (CheckIfGlobalBorderHasChanged())
1297 // redraw if position or size of playfield area has changed
1298 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1299 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1302 // redraw if position or size of door area has changed
1303 if (dx_last != DX || dy_last != DY ||
1304 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1307 // redraw if position or size of tape area has changed
1308 if (vx_last != VX || vy_last != VY ||
1309 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1312 // redraw if position or size of editor area has changed
1313 if (ex_last != EX || ey_last != EY ||
1314 exsize_last != EXSIZE || eysize_last != EYSIZE)
1321 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1324 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1326 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1329 void RedrawGlobalBorder(void)
1331 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1333 RedrawGlobalBorderFromBitmap(bitmap);
1335 redraw_mask = REDRAW_ALL;
1338 static void RedrawGlobalBorderIfNeeded(void)
1340 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1341 if (game_status == game_status_last)
1345 // copy current draw buffer to later copy back areas that have not changed
1346 if (game_status_last != GAME_MODE_TITLE)
1347 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1349 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1350 if (CheckIfGlobalBorderRedrawIsNeeded())
1352 // determine and store new global border bitmap for current game status
1353 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1356 // redraw global screen border (or clear, if defined to be empty)
1357 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1359 if (game_status == GAME_MODE_EDITOR)
1360 DrawSpecialEditorDoor();
1362 // copy previous playfield and door areas, if they are defined on both
1363 // previous and current screen and if they still have the same size
1365 if (real_sx_last != -1 && real_sy_last != -1 &&
1366 REAL_SX != -1 && REAL_SY != -1 &&
1367 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1368 BlitBitmap(bitmap_db_store_1, backbuffer,
1369 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1372 if (dx_last != -1 && dy_last != -1 &&
1373 DX != -1 && DY != -1 &&
1374 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1378 if (game_status != GAME_MODE_EDITOR)
1380 if (vx_last != -1 && vy_last != -1 &&
1381 VX != -1 && VY != -1 &&
1382 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1388 if (ex_last != -1 && ey_last != -1 &&
1389 EX != -1 && EY != -1 &&
1390 exsize_last == EXSIZE && eysize_last == EYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1395 redraw_mask = REDRAW_ALL;
1398 game_status_last = game_status;
1400 global_border_bitmap_last = global_border_bitmap;
1402 real_sx_last = REAL_SX;
1403 real_sy_last = REAL_SY;
1404 full_sxsize_last = FULL_SXSIZE;
1405 full_sysize_last = FULL_SYSIZE;
1408 dxsize_last = DXSIZE;
1409 dysize_last = DYSIZE;
1412 vxsize_last = VXSIZE;
1413 vysize_last = VYSIZE;
1416 exsize_last = EXSIZE;
1417 eysize_last = EYSIZE;
1420 void ClearField(void)
1422 RedrawGlobalBorderIfNeeded();
1424 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1425 // (when entering hall of fame after playing)
1426 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1428 // !!! maybe this should be done before clearing the background !!!
1429 if (game_status == GAME_MODE_PLAYING)
1431 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1432 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1436 SetDrawtoField(DRAW_TO_BACKBUFFER);
1440 void MarkTileDirty(int x, int y)
1442 redraw_mask |= REDRAW_FIELD;
1445 void SetBorderElement(void)
1449 BorderElement = EL_EMPTY;
1451 // only the R'n'D game engine may use an additional steelwall border
1452 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1455 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1457 for (x = 0; x < lev_fieldx; x++)
1459 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1460 BorderElement = EL_STEELWALL;
1462 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1468 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1469 int max_array_fieldx, int max_array_fieldy,
1470 short field[max_array_fieldx][max_array_fieldy],
1471 int max_fieldx, int max_fieldy)
1473 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1474 struct XY *check = xy_topdown;
1475 int old_element = field[start_x][start_y];
1478 // do nothing if start field already has the desired content
1479 if (old_element == fill_element)
1482 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1484 while (stack_pos > 0)
1486 struct XY current = stack_buffer[--stack_pos];
1489 field[current.x][current.y] = fill_element;
1491 for (i = 0; i < 4; i++)
1493 int x = current.x + check[i].x;
1494 int y = current.y + check[i].y;
1496 // check for stack buffer overflow (should not happen)
1497 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1498 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1500 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1501 stack_buffer[stack_pos++] = (struct XY){ x, y };
1506 void FloodFillLevel(int from_x, int from_y, int fill_element,
1507 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1508 int max_fieldx, int max_fieldy)
1510 FloodFillLevelExt(from_x, from_y, fill_element,
1511 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1512 max_fieldx, max_fieldy);
1515 void SetRandomAnimationValue(int x, int y)
1517 gfx.anim_random_frame = GfxRandom[x][y];
1520 void SetAnimationFirstLevel(int first_level)
1522 gfx.anim_first_level = first_level;
1525 int getGraphicAnimationFrame(int graphic, int sync_frame)
1527 // animation synchronized with global frame counter, not move position
1528 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1529 sync_frame = FrameCounter;
1530 else if (graphic_info[graphic].anim_global_anim_sync)
1531 sync_frame = getGlobalAnimSyncFrame();
1533 return getAnimationFrame(graphic_info[graphic].anim_frames,
1534 graphic_info[graphic].anim_delay,
1535 graphic_info[graphic].anim_mode,
1536 graphic_info[graphic].anim_start_frame,
1540 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1542 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1544 struct GraphicInfo *g = &graphic_info[graphic];
1545 int xsize = MAX(1, g->anim_frames_per_line);
1546 int ysize = MAX(1, g->anim_frames / xsize);
1547 int xoffset = g->anim_start_frame % xsize;
1548 int yoffset = g->anim_start_frame % ysize;
1549 // may be needed if screen field is significantly larger than playfield
1550 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1551 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1552 int sync_frame = y * xsize + x;
1554 return sync_frame % g->anim_frames;
1556 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1558 struct GraphicInfo *g = &graphic_info[graphic];
1559 // may be needed if screen field is significantly larger than playfield
1560 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1561 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1562 int sync_frame = GfxRandomStatic[x][y];
1564 return sync_frame % g->anim_frames;
1568 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1570 return getGraphicAnimationFrame(graphic, sync_frame);
1574 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1576 struct GraphicInfo *g = &graphic_info[graphic];
1577 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1579 if (tilesize == gfx.standard_tile_size)
1580 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1581 else if (tilesize == game.tile_size)
1582 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1584 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1587 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1588 boolean get_backside)
1590 struct GraphicInfo *g = &graphic_info[graphic];
1591 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1592 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1594 if (g->offset_y == 0) // frames are ordered horizontally
1596 int max_width = g->anim_frames_per_line * g->width;
1597 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1599 *x = pos % max_width;
1600 *y = src_y % g->height + pos / max_width * g->height;
1602 else if (g->offset_x == 0) // frames are ordered vertically
1604 int max_height = g->anim_frames_per_line * g->height;
1605 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1607 *x = src_x % g->width + pos / max_height * g->width;
1608 *y = pos % max_height;
1610 else // frames are ordered diagonally
1612 *x = src_x + frame * g->offset_x;
1613 *y = src_y + frame * g->offset_y;
1617 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1618 Bitmap **bitmap, int *x, int *y,
1619 boolean get_backside)
1621 struct GraphicInfo *g = &graphic_info[graphic];
1623 // if no graphics defined at all, use fallback graphics
1624 if (g->bitmaps == NULL)
1625 *g = graphic_info[IMG_CHAR_EXCLAM];
1627 // if no in-game graphics defined, always use standard graphic size
1628 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1629 tilesize = TILESIZE;
1631 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1632 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1634 *x = *x * tilesize / g->tile_size;
1635 *y = *y * tilesize / g->tile_size;
1638 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1639 Bitmap **bitmap, int *x, int *y)
1641 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1644 void getFixedGraphicSource(int graphic, int frame,
1645 Bitmap **bitmap, int *x, int *y)
1647 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1650 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1652 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1655 void getGlobalAnimGraphicSource(int graphic, int frame,
1656 Bitmap **bitmap, int *x, int *y)
1658 struct GraphicInfo *g = &graphic_info[graphic];
1660 // if no graphics defined at all, use fallback graphics
1661 if (g->bitmaps == NULL)
1662 *g = graphic_info[IMG_CHAR_EXCLAM];
1664 // use original size graphics, if existing, else use standard size graphics
1665 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1666 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1668 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1670 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1673 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1674 int *x, int *y, boolean get_backside)
1676 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1680 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1682 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1685 void DrawGraphic(int x, int y, int graphic, int frame)
1688 if (!IN_SCR_FIELD(x, y))
1690 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1691 Debug("draw:DrawGraphic", "This should never happen!");
1697 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1700 MarkTileDirty(x, y);
1703 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1706 if (!IN_SCR_FIELD(x, y))
1708 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1710 Debug("draw:DrawFixedGraphic", "This should never happen!");
1716 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1718 MarkTileDirty(x, y);
1721 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1727 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1729 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1732 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1738 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1739 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1742 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1745 if (!IN_SCR_FIELD(x, y))
1747 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1749 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1755 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1758 MarkTileDirty(x, y);
1761 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1764 if (!IN_SCR_FIELD(x, y))
1766 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1768 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1774 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1776 MarkTileDirty(x, y);
1779 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1785 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1787 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1791 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1792 int graphic, int frame)
1797 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1799 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1803 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1805 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1807 MarkTileDirty(x / tilesize, y / tilesize);
1810 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1813 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1814 graphic, frame, tilesize);
1815 MarkTileDirty(x / tilesize, y / tilesize);
1818 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1824 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1825 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1828 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1829 int frame, int tilesize)
1834 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1835 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1838 void DrawMiniGraphic(int x, int y, int graphic)
1840 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1841 MarkTileDirty(x / 2, y / 2);
1844 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1849 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1850 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1853 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1854 int graphic, int frame,
1855 int cut_mode, int mask_mode)
1860 int width = TILEX, height = TILEY;
1863 if (dx || dy) // shifted graphic
1865 if (x < BX1) // object enters playfield from the left
1872 else if (x > BX2) // object enters playfield from the right
1878 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1884 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1886 else if (dx) // general horizontal movement
1887 MarkTileDirty(x + SIGN(dx), y);
1889 if (y < BY1) // object enters playfield from the top
1891 if (cut_mode == CUT_BELOW) // object completely above top border
1899 else if (y > BY2) // object enters playfield from the bottom
1905 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1911 else if (dy > 0 && cut_mode == CUT_ABOVE)
1913 if (y == BY2) // object completely above bottom border
1919 MarkTileDirty(x, y + 1);
1920 } // object leaves playfield to the bottom
1921 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1923 else if (dy) // general vertical movement
1924 MarkTileDirty(x, y + SIGN(dy));
1928 if (!IN_SCR_FIELD(x, y))
1930 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1932 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1938 width = width * TILESIZE_VAR / TILESIZE;
1939 height = height * TILESIZE_VAR / TILESIZE;
1940 cx = cx * TILESIZE_VAR / TILESIZE;
1941 cy = cy * TILESIZE_VAR / TILESIZE;
1942 dx = dx * TILESIZE_VAR / TILESIZE;
1943 dy = dy * TILESIZE_VAR / TILESIZE;
1945 if (width > 0 && height > 0)
1947 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1952 dst_x = FX + x * TILEX_VAR + dx;
1953 dst_y = FY + y * TILEY_VAR + dy;
1955 if (mask_mode == USE_MASKING)
1956 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1959 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1962 MarkTileDirty(x, y);
1966 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1967 int graphic, int frame,
1968 int cut_mode, int mask_mode)
1973 int width = TILEX_VAR, height = TILEY_VAR;
1976 int x2 = x + SIGN(dx);
1977 int y2 = y + SIGN(dy);
1979 // movement with two-tile animations must be sync'ed with movement position,
1980 // not with current GfxFrame (which can be higher when using slow movement)
1981 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1982 int anim_frames = graphic_info[graphic].anim_frames;
1984 // (we also need anim_delay here for movement animations with less frames)
1985 int anim_delay = graphic_info[graphic].anim_delay;
1986 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1988 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1989 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1991 // re-calculate animation frame for two-tile movement animation
1992 frame = getGraphicAnimationFrame(graphic, sync_frame);
1994 // check if movement start graphic inside screen area and should be drawn
1995 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1997 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1999 dst_x = FX + x1 * TILEX_VAR;
2000 dst_y = FY + y1 * TILEY_VAR;
2002 if (mask_mode == USE_MASKING)
2003 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2006 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2009 MarkTileDirty(x1, y1);
2012 // check if movement end graphic inside screen area and should be drawn
2013 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2015 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2017 dst_x = FX + x2 * TILEX_VAR;
2018 dst_y = FY + y2 * TILEY_VAR;
2020 if (mask_mode == USE_MASKING)
2021 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2024 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2027 MarkTileDirty(x2, y2);
2031 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2032 int graphic, int frame,
2033 int cut_mode, int mask_mode)
2037 DrawGraphic(x, y, graphic, frame);
2042 if (graphic_info[graphic].double_movement) // EM style movement images
2043 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2045 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2048 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2049 int graphic, int frame, int cut_mode)
2051 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2054 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2055 int cut_mode, int mask_mode)
2057 int lx = LEVELX(x), ly = LEVELY(y);
2061 if (IN_LEV_FIELD(lx, ly))
2063 if (element == EL_EMPTY)
2064 element = GfxElementEmpty[lx][ly];
2066 SetRandomAnimationValue(lx, ly);
2068 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2069 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2071 // do not use double (EM style) movement graphic when not moving
2072 if (graphic_info[graphic].double_movement && !dx && !dy)
2074 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2075 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2078 if (game.use_masked_elements && (dx || dy))
2079 mask_mode = USE_MASKING;
2081 else // border element
2083 graphic = el2img(element);
2084 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2087 if (element == EL_EXPANDABLE_WALL)
2089 boolean left_stopped = FALSE, right_stopped = FALSE;
2091 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2092 left_stopped = TRUE;
2093 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2094 right_stopped = TRUE;
2096 if (left_stopped && right_stopped)
2098 else if (left_stopped)
2100 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2101 frame = graphic_info[graphic].anim_frames - 1;
2103 else if (right_stopped)
2105 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2106 frame = graphic_info[graphic].anim_frames - 1;
2111 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2112 else if (mask_mode == USE_MASKING)
2113 DrawGraphicThruMask(x, y, graphic, frame);
2115 DrawGraphic(x, y, graphic, frame);
2118 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2119 int cut_mode, int mask_mode)
2121 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2122 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2123 cut_mode, mask_mode);
2126 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2129 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2132 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2135 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2138 void DrawLevelElementThruMask(int x, int y, int element)
2140 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2143 void DrawLevelFieldThruMask(int x, int y)
2145 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2148 // !!! implementation of quicksand is totally broken !!!
2149 #define IS_CRUMBLED_TILE(x, y, e) \
2150 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2151 !IS_MOVING(x, y) || \
2152 (e) == EL_QUICKSAND_EMPTYING || \
2153 (e) == EL_QUICKSAND_FAST_EMPTYING))
2155 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2160 int width, height, cx, cy;
2161 int sx = SCREENX(x), sy = SCREENY(y);
2162 int crumbled_border_size = graphic_info[graphic].border_size;
2163 int crumbled_tile_size = graphic_info[graphic].tile_size;
2164 int crumbled_border_size_var =
2165 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2168 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2170 for (i = 1; i < 4; i++)
2172 int dxx = (i & 1 ? dx : 0);
2173 int dyy = (i & 2 ? dy : 0);
2176 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2179 // check if neighbour field is of same crumble type
2180 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2181 graphic_info[graphic].class ==
2182 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2184 // return if check prevents inner corner
2185 if (same == (dxx == dx && dyy == dy))
2189 // if we reach this point, we have an inner corner
2191 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2193 width = crumbled_border_size_var;
2194 height = crumbled_border_size_var;
2195 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2196 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2198 if (game.use_masked_elements)
2200 int graphic0 = el2img(EL_EMPTY);
2201 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2202 Bitmap *src_bitmap0;
2205 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2207 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2209 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2211 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2213 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2216 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2218 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2221 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2226 int width, height, bx, by, cx, cy;
2227 int sx = SCREENX(x), sy = SCREENY(y);
2228 int crumbled_border_size = graphic_info[graphic].border_size;
2229 int crumbled_tile_size = graphic_info[graphic].tile_size;
2230 int crumbled_border_size_var =
2231 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2232 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2235 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2237 // only needed when using masked elements
2238 int graphic0 = el2img(EL_EMPTY);
2239 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2240 Bitmap *src_bitmap0;
2243 if (game.use_masked_elements)
2244 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2246 // draw simple, sloppy, non-corner-accurate crumbled border
2248 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2249 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2250 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2251 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2253 if (game.use_masked_elements)
2255 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2257 FX + sx * TILEX_VAR + cx,
2258 FY + sy * TILEY_VAR + cy);
2260 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2262 FX + sx * TILEX_VAR + cx,
2263 FY + sy * TILEY_VAR + cy);
2266 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2268 FX + sx * TILEX_VAR + cx,
2269 FY + sy * TILEY_VAR + cy);
2271 // (remaining middle border part must be at least as big as corner part)
2272 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2273 crumbled_border_size_var >= TILESIZE_VAR / 3)
2276 // correct corners of crumbled border, if needed
2278 for (i = -1; i <= 1; i += 2)
2280 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2281 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2282 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2285 // check if neighbour field is of same crumble type
2286 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2287 graphic_info[graphic].class ==
2288 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2290 // no crumbled corner, but continued crumbled border
2292 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2293 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2294 int b1 = (i == 1 ? crumbled_border_size_var :
2295 TILESIZE_VAR - 2 * crumbled_border_size_var);
2297 width = crumbled_border_size_var;
2298 height = crumbled_border_size_var;
2300 if (dir == 1 || dir == 2)
2315 if (game.use_masked_elements)
2317 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2319 FX + sx * TILEX_VAR + cx,
2320 FY + sy * TILEY_VAR + cy);
2322 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2324 FX + sx * TILEX_VAR + cx,
2325 FY + sy * TILEY_VAR + cy);
2328 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2330 FX + sx * TILEX_VAR + cx,
2331 FY + sy * TILEY_VAR + cy);
2336 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2338 int sx = SCREENX(x), sy = SCREENY(y);
2341 struct XY *xy = xy_topdown;
2343 if (!IN_LEV_FIELD(x, y))
2346 element = TILE_GFX_ELEMENT(x, y);
2348 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2350 if (!IN_SCR_FIELD(sx, sy))
2353 // crumble field borders towards direct neighbour fields
2354 for (i = 0; i < 4; i++)
2356 int xx = x + xy[i].x;
2357 int yy = y + xy[i].y;
2359 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2362 // check if neighbour field is of same crumble type
2363 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2364 graphic_info[graphic].class ==
2365 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2368 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2371 // crumble inner field corners towards corner neighbour fields
2372 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2373 graphic_info[graphic].anim_frames == 2)
2375 for (i = 0; i < 4; i++)
2377 int dx = (i & 1 ? +1 : -1);
2378 int dy = (i & 2 ? +1 : -1);
2380 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2384 MarkTileDirty(sx, sy);
2386 else // center field is not crumbled -- crumble neighbour fields
2388 // crumble field borders of direct neighbour fields
2389 for (i = 0; i < 4; i++)
2391 int xx = x + xy[i].x;
2392 int yy = y + xy[i].y;
2393 int sxx = sx + xy[i].x;
2394 int syy = sy + xy[i].y;
2396 if (!IN_LEV_FIELD(xx, yy) ||
2397 !IN_SCR_FIELD(sxx, syy))
2400 // do not crumble fields that are being digged or snapped
2401 if (Tile[xx][yy] == EL_EMPTY ||
2402 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2405 element = TILE_GFX_ELEMENT(xx, yy);
2407 if (!IS_CRUMBLED_TILE(xx, yy, element))
2410 graphic = el_act2crm(element, ACTION_DEFAULT);
2412 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2414 MarkTileDirty(sxx, syy);
2417 // crumble inner field corners of corner neighbour fields
2418 for (i = 0; i < 4; i++)
2420 int dx = (i & 1 ? +1 : -1);
2421 int dy = (i & 2 ? +1 : -1);
2427 if (!IN_LEV_FIELD(xx, yy) ||
2428 !IN_SCR_FIELD(sxx, syy))
2431 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2434 element = TILE_GFX_ELEMENT(xx, yy);
2436 if (!IS_CRUMBLED_TILE(xx, yy, element))
2439 graphic = el_act2crm(element, ACTION_DEFAULT);
2441 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2442 graphic_info[graphic].anim_frames == 2)
2443 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2445 MarkTileDirty(sxx, syy);
2450 void DrawLevelFieldCrumbled(int x, int y)
2454 if (!IN_LEV_FIELD(x, y))
2457 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2458 GfxElement[x][y] != EL_UNDEFINED &&
2459 GFX_CRUMBLED(GfxElement[x][y]))
2461 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2466 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2468 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2471 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2474 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2475 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2476 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2477 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2478 int sx = SCREENX(x), sy = SCREENY(y);
2480 DrawScreenGraphic(sx, sy, graphic1, frame1);
2481 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2484 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2486 int sx = SCREENX(x), sy = SCREENY(y);
2487 struct XY *xy = xy_topdown;
2490 // crumble direct neighbour fields (required for field borders)
2491 for (i = 0; i < 4; i++)
2493 int xx = x + xy[i].x;
2494 int yy = y + xy[i].y;
2495 int sxx = sx + xy[i].x;
2496 int syy = sy + xy[i].y;
2498 if (!IN_LEV_FIELD(xx, yy) ||
2499 !IN_SCR_FIELD(sxx, syy) ||
2500 !GFX_CRUMBLED(Tile[xx][yy]) ||
2504 DrawLevelField(xx, yy);
2507 // crumble corner neighbour fields (required for inner field corners)
2508 for (i = 0; i < 4; i++)
2510 int dx = (i & 1 ? +1 : -1);
2511 int dy = (i & 2 ? +1 : -1);
2517 if (!IN_LEV_FIELD(xx, yy) ||
2518 !IN_SCR_FIELD(sxx, syy) ||
2519 !GFX_CRUMBLED(Tile[xx][yy]) ||
2523 int element = TILE_GFX_ELEMENT(xx, yy);
2524 int graphic = el_act2crm(element, ACTION_DEFAULT);
2526 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2527 graphic_info[graphic].anim_frames == 2)
2528 DrawLevelField(xx, yy);
2532 static int getBorderElement(int x, int y)
2536 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2537 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2538 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2539 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2540 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2541 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2542 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2544 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2545 int steel_position = (x == -1 && y == -1 ? 0 :
2546 x == lev_fieldx && y == -1 ? 1 :
2547 x == -1 && y == lev_fieldy ? 2 :
2548 x == lev_fieldx && y == lev_fieldy ? 3 :
2549 x == -1 || x == lev_fieldx ? 4 :
2550 y == -1 || y == lev_fieldy ? 5 : 6);
2552 return border[steel_position][steel_type];
2555 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2557 if (game.use_masked_elements)
2559 if (graphic != el2img(EL_EMPTY))
2560 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2562 DrawGraphicThruMask(x, y, graphic, frame);
2566 DrawGraphic(x, y, graphic, frame);
2570 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2572 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2575 void DrawScreenElement(int x, int y, int element)
2577 int mask_mode = NO_MASKING;
2579 if (game.use_masked_elements)
2581 int lx = LEVELX(x), ly = LEVELY(y);
2583 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2585 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2587 mask_mode = USE_MASKING;
2591 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2592 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2595 void DrawLevelElement(int x, int y, int element)
2597 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2598 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2601 void DrawScreenField(int x, int y)
2603 int lx = LEVELX(x), ly = LEVELY(y);
2604 int element, content;
2606 if (!IN_LEV_FIELD(lx, ly))
2608 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2611 element = getBorderElement(lx, ly);
2613 DrawScreenElement(x, y, element);
2618 element = Tile[lx][ly];
2619 content = Store[lx][ly];
2621 if (IS_MOVING(lx, ly))
2623 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2624 boolean cut_mode = NO_CUTTING;
2626 if (element == EL_QUICKSAND_EMPTYING ||
2627 element == EL_QUICKSAND_FAST_EMPTYING ||
2628 element == EL_MAGIC_WALL_EMPTYING ||
2629 element == EL_BD_MAGIC_WALL_EMPTYING ||
2630 element == EL_DC_MAGIC_WALL_EMPTYING ||
2631 element == EL_AMOEBA_DROPPING)
2632 cut_mode = CUT_ABOVE;
2633 else if (element == EL_QUICKSAND_FILLING ||
2634 element == EL_QUICKSAND_FAST_FILLING ||
2635 element == EL_MAGIC_WALL_FILLING ||
2636 element == EL_BD_MAGIC_WALL_FILLING ||
2637 element == EL_DC_MAGIC_WALL_FILLING)
2638 cut_mode = CUT_BELOW;
2640 if (cut_mode == CUT_ABOVE)
2641 DrawScreenElement(x, y, element);
2643 DrawScreenElement(x, y, EL_EMPTY);
2645 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2647 int dir = MovDir[lx][ly];
2648 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2649 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2651 if (IN_SCR_FIELD(newx, newy))
2652 DrawScreenElement(newx, newy, EL_EMPTY);
2656 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2657 else if (cut_mode == NO_CUTTING)
2658 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2661 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2663 if (cut_mode == CUT_BELOW &&
2664 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2665 DrawLevelElement(lx, ly + 1, element);
2668 if (content == EL_ACID)
2670 int dir = MovDir[lx][ly];
2671 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2672 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2674 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2676 // prevent target field from being drawn again (but without masking)
2677 // (this would happen if target field is scanned after moving element)
2678 Stop[newlx][newly] = TRUE;
2681 else if (IS_BLOCKED(lx, ly))
2686 boolean cut_mode = NO_CUTTING;
2687 int element_old, content_old;
2689 Blocked2Moving(lx, ly, &oldx, &oldy);
2692 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2693 MovDir[oldx][oldy] == MV_RIGHT);
2695 element_old = Tile[oldx][oldy];
2696 content_old = Store[oldx][oldy];
2698 if (element_old == EL_QUICKSAND_EMPTYING ||
2699 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2700 element_old == EL_MAGIC_WALL_EMPTYING ||
2701 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2702 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2703 element_old == EL_AMOEBA_DROPPING)
2704 cut_mode = CUT_ABOVE;
2706 DrawScreenElement(x, y, EL_EMPTY);
2709 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2711 else if (cut_mode == NO_CUTTING)
2712 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2715 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2718 else if (IS_DRAWABLE(element))
2719 DrawScreenElement(x, y, element);
2721 DrawScreenElement(x, y, EL_EMPTY);
2724 void DrawLevelField(int x, int y)
2726 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2727 DrawScreenField(SCREENX(x), SCREENY(y));
2728 else if (IS_MOVING(x, y))
2732 Moving2Blocked(x, y, &newx, &newy);
2733 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2734 DrawScreenField(SCREENX(newx), SCREENY(newy));
2736 else if (IS_BLOCKED(x, y))
2740 Blocked2Moving(x, y, &oldx, &oldy);
2741 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2742 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2746 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2747 int (*el2img_function)(int), boolean masked,
2748 int element_bits_draw)
2750 int element_base = map_mm_wall_element(element);
2751 int element_bits = (IS_DF_WALL(element) ?
2752 element - EL_DF_WALL_START :
2753 IS_MM_WALL(element) ?
2754 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2755 int graphic = el2img_function(element_base);
2756 int tilesize_draw = tilesize / 2;
2761 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2763 for (i = 0; i < 4; i++)
2765 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2766 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2768 if (!(element_bits_draw & (1 << i)))
2771 if (element_bits & (1 << i))
2774 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2775 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2777 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2778 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2783 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2784 tilesize_draw, tilesize_draw);
2789 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2790 boolean masked, int element_bits_draw)
2792 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2793 element, tilesize, el2edimg, masked, element_bits_draw);
2796 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2797 int (*el2img_function)(int))
2799 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2803 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2806 if (IS_MM_WALL(element))
2808 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2809 element, tilesize, el2edimg, masked, 0x000f);
2813 int graphic = el2edimg(element);
2816 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2818 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2822 void DrawSizedElement(int x, int y, int element, int tilesize)
2824 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2827 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2829 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2832 void DrawMiniElement(int x, int y, int element)
2836 graphic = el2edimg(element);
2837 DrawMiniGraphic(x, y, graphic);
2840 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2843 int x = sx + scroll_x, y = sy + scroll_y;
2845 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2846 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2847 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2848 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2850 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2853 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2855 int x = sx + scroll_x, y = sy + scroll_y;
2857 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2858 DrawMiniElement(sx, sy, EL_EMPTY);
2859 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2860 DrawMiniElement(sx, sy, Tile[x][y]);
2862 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2865 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2866 int x, int y, int xsize, int ysize,
2867 int tile_width, int tile_height)
2871 int dst_x = startx + x * tile_width;
2872 int dst_y = starty + y * tile_height;
2873 int width = graphic_info[graphic].width;
2874 int height = graphic_info[graphic].height;
2875 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2876 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2877 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2878 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2879 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2880 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2881 boolean draw_masked = graphic_info[graphic].draw_masked;
2883 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2885 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2887 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2891 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2892 inner_sx + (x - 1) * tile_width % inner_width);
2893 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2894 inner_sy + (y - 1) * tile_height % inner_height);
2897 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2900 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2904 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2905 int x, int y, int xsize, int ysize,
2908 int font_width = getFontWidth(font_nr);
2909 int font_height = getFontHeight(font_nr);
2911 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2912 font_width, font_height);
2915 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2917 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2918 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2919 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2920 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2921 boolean no_delay = (tape.warp_forward);
2922 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2923 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2924 DelayCounter anim_delay = { anim_delay_value };
2925 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2926 int font_width = getFontWidth(font_nr);
2927 int font_height = getFontHeight(font_nr);
2928 int max_xsize = level.envelope[envelope_nr].xsize;
2929 int max_ysize = level.envelope[envelope_nr].ysize;
2930 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2931 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2932 int xend = max_xsize;
2933 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2934 int xstep = (xstart < xend ? 1 : 0);
2935 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2937 int end = MAX(xend - xstart, yend - ystart);
2940 for (i = start; i <= end; i++)
2942 int last_frame = end; // last frame of this "for" loop
2943 int x = xstart + i * xstep;
2944 int y = ystart + i * ystep;
2945 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2946 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2947 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2948 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2951 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2953 BlitScreenToBitmap(backbuffer);
2955 SetDrawtoField(DRAW_TO_BACKBUFFER);
2957 for (yy = 0; yy < ysize; yy++)
2958 for (xx = 0; xx < xsize; xx++)
2959 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2961 DrawTextArea(sx + font_width, sy + font_height,
2962 level.envelope[envelope_nr].text, font_nr, max_xsize,
2963 xsize - 2, ysize - 2, 0, mask_mode,
2964 level.envelope[envelope_nr].autowrap,
2965 level.envelope[envelope_nr].centered, FALSE);
2967 redraw_mask |= REDRAW_FIELD;
2970 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2973 ClearAutoRepeatKeyEvents();
2976 void ShowEnvelope(int envelope_nr)
2978 int element = EL_ENVELOPE_1 + envelope_nr;
2979 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2980 int sound_opening = element_info[element].sound[ACTION_OPENING];
2981 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2982 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2983 boolean no_delay = (tape.warp_forward);
2984 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2985 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2986 int anim_mode = graphic_info[graphic].anim_mode;
2987 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2988 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2989 boolean overlay_enabled = GetOverlayEnabled();
2991 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2993 SetOverlayEnabled(FALSE);
2996 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2998 if (anim_mode == ANIM_DEFAULT)
2999 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
3001 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
3004 Delay_WithScreenUpdates(wait_delay_value);
3006 WaitForEventToContinue();
3009 SetOverlayEnabled(overlay_enabled);
3011 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3013 if (anim_mode != ANIM_NONE)
3014 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3016 if (anim_mode == ANIM_DEFAULT)
3017 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3019 game.envelope_active = FALSE;
3021 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3023 redraw_mask |= REDRAW_FIELD;
3027 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3028 int xsize, int ysize)
3030 if (!global.use_envelope_request)
3033 if (request.bitmap == NULL ||
3034 xsize > request.xsize ||
3035 ysize > request.ysize)
3037 if (request.bitmap != NULL)
3038 FreeBitmap(request.bitmap);
3040 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3042 SDL_Surface *surface = request.bitmap->surface;
3044 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3045 Fail("SDLGetNativeSurface() failed");
3048 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3050 // create masked surface for request bitmap, if needed
3051 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3053 SDL_Surface *surface = request.bitmap->surface;
3054 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3056 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3057 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3058 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3061 SDLFreeBitmapTextures(request.bitmap);
3062 SDLCreateBitmapTextures(request.bitmap);
3064 ResetBitmapAlpha(request.bitmap);
3066 // set envelope request run-time values
3069 request.xsize = xsize;
3070 request.ysize = ysize;
3073 void DrawEnvelopeRequestToScreen(int drawing_target)
3075 if (global.use_envelope_request &&
3076 game.request_active &&
3077 drawing_target == DRAW_TO_SCREEN)
3079 struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST];
3081 SetBitmapAlphaNextBlit(request.bitmap, g->alpha);
3084 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3085 request.sx, request.sy);
3087 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3088 request.sx, request.sy);
3092 static void setRequestBasePosition(int *x, int *y)
3094 int sx_base, sy_base;
3096 if (request.x != -1)
3097 sx_base = request.x;
3098 else if (request.align == ALIGN_LEFT)
3100 else if (request.align == ALIGN_RIGHT)
3101 sx_base = SX + SXSIZE;
3103 sx_base = SX + SXSIZE / 2;
3105 if (request.y != -1)
3106 sy_base = request.y;
3107 else if (request.valign == VALIGN_TOP)
3109 else if (request.valign == VALIGN_BOTTOM)
3110 sy_base = SY + SYSIZE;
3112 sy_base = SY + SYSIZE / 2;
3118 static void setRequestPositionExt(int *x, int *y, int width, int height,
3119 boolean add_border_size)
3121 int border_size = request.border_size;
3122 int sx_base, sy_base;
3125 setRequestBasePosition(&sx_base, &sy_base);
3127 if (request.align == ALIGN_LEFT)
3129 else if (request.align == ALIGN_RIGHT)
3130 sx = sx_base - width;
3132 sx = sx_base - width / 2;
3134 if (request.valign == VALIGN_TOP)
3136 else if (request.valign == VALIGN_BOTTOM)
3137 sy = sy_base - height;
3139 sy = sy_base - height / 2;
3141 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3142 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3144 if (add_border_size)
3154 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3156 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3159 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3161 char *text_final = text;
3162 char *text_door_style = NULL;
3163 int graphic = IMG_BACKGROUND_REQUEST;
3164 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3165 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3166 int font_nr = FONT_REQUEST;
3167 int font_width = getFontWidth(font_nr);
3168 int font_height = getFontHeight(font_nr);
3169 int border_size = request.border_size;
3170 int line_spacing = request.line_spacing;
3171 int line_height = font_height + line_spacing;
3172 int max_text_width = request.width - 2 * border_size;
3173 int max_text_height = request.height - 2 * border_size;
3174 int line_length = max_text_width / font_width;
3175 int max_lines = max_text_height / line_height;
3176 int text_width = line_length * font_width;
3177 int sx_offset = border_size;
3178 int sy_offset = border_size;
3180 // force DOOR font inside door area
3181 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3183 if (request.centered)
3184 sx_offset = (request.width - text_width) / 2;
3186 if (request.wrap_single_words && !request.autowrap)
3188 char *src_text_ptr, *dst_text_ptr;
3190 if (maxWordLengthInRequestString(text) > line_length)
3192 font_nr = FONT_REQUEST_NARROW;
3193 font_width = getFontWidth(font_nr);
3194 line_length = max_text_width / font_width;
3197 text_door_style = checked_malloc(2 * strlen(text) + 1);
3199 src_text_ptr = text;
3200 dst_text_ptr = text_door_style;
3202 while (*src_text_ptr)
3204 if (*src_text_ptr == ' ' ||
3205 *src_text_ptr == '?' ||
3206 *src_text_ptr == '!')
3207 *dst_text_ptr++ = '\n';
3209 if (*src_text_ptr != ' ')
3210 *dst_text_ptr++ = *src_text_ptr;
3215 *dst_text_ptr = '\0';
3217 text_final = text_door_style;
3220 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3221 line_length, -1, max_lines, line_spacing, mask_mode,
3222 request.autowrap, request.centered, FALSE);
3224 if (text_door_style)
3225 free(text_door_style);
3230 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3232 DrawBuffer *drawto_last = drawto;
3233 int graphic = IMG_BACKGROUND_REQUEST;
3234 int width = request.width;
3235 int height = request.height;
3236 int tile_size = MAX(request.step_offset, 1);
3237 int x_steps = width / tile_size;
3238 int y_steps = height / tile_size;
3242 setRequestPosition(&sx, &sy, FALSE);
3244 // draw complete envelope request to temporary bitmap
3245 drawto = bitmap_db_store_1;
3247 ClearRectangle(drawto, sx, sy, width, height);
3249 for (y = 0; y < y_steps; y++)
3250 for (x = 0; x < x_steps; x++)
3251 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3252 x, y, x_steps, y_steps,
3253 tile_size, tile_size);
3255 // write text for request
3256 DrawEnvelopeRequestText(sx, sy, text);
3258 MapToolButtons(req_state);
3260 // restore pointer to drawing buffer
3261 drawto = drawto_last;
3263 // prepare complete envelope request from temporary bitmap
3264 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3267 static void AnimateEnvelopeRequest(int anim_mode, int action)
3269 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3270 int delay_value_normal = request.step_delay;
3271 int delay_value_fast = delay_value_normal / 2;
3272 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3273 boolean no_delay = (tape.warp_forward);
3274 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3275 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3276 DelayCounter anim_delay = { anim_delay_value };
3278 int tile_size = MAX(request.step_offset, 1);
3279 int max_xsize = request.width / tile_size;
3280 int max_ysize = request.height / tile_size;
3281 int max_xsize_inner = max_xsize - 2;
3282 int max_ysize_inner = max_ysize - 2;
3284 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3285 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3286 int xend = max_xsize_inner;
3287 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3288 int xstep = (xstart < xend ? 1 : 0);
3289 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3291 int end = MAX(xend - xstart, yend - ystart);
3294 if (setup.quick_doors)
3301 for (i = start; i <= end; i++)
3303 int last_frame = end; // last frame of this "for" loop
3304 int x = xstart + i * xstep;
3305 int y = ystart + i * ystep;
3306 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3307 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3308 int xsize_size_left = (xsize - 1) * tile_size;
3309 int ysize_size_top = (ysize - 1) * tile_size;
3310 int max_xsize_pos = (max_xsize - 1) * tile_size;
3311 int max_ysize_pos = (max_ysize - 1) * tile_size;
3312 int width = xsize * tile_size;
3313 int height = ysize * tile_size;
3319 HandleGameActions();
3321 setRequestPosition(&src_x, &src_y, FALSE);
3322 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3324 for (yy = 0; yy < 2; yy++)
3326 for (xx = 0; xx < 2; xx++)
3328 int src_xx = src_x + xx * max_xsize_pos;
3329 int src_yy = src_y + yy * max_ysize_pos;
3330 int dst_xx = dst_x + xx * xsize_size_left;
3331 int dst_yy = dst_y + yy * ysize_size_top;
3332 int xx_size = (xx ? tile_size : xsize_size_left);
3333 int yy_size = (yy ? tile_size : ysize_size_top);
3335 // draw partial (animated) envelope request to temporary bitmap
3336 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3337 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3341 // prepare partial (animated) envelope request from temporary bitmap
3342 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3345 redraw_mask |= REDRAW_FIELD;
3349 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3352 ClearAutoRepeatKeyEvents();
3355 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3357 int graphic = IMG_BACKGROUND_REQUEST;
3358 int sound_opening = SND_REQUEST_OPENING;
3359 int sound_closing = SND_REQUEST_CLOSING;
3360 int anim_mode_1 = request.anim_mode; // (higher priority)
3361 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3362 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3363 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3364 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3366 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3368 if (action == ACTION_OPENING)
3370 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3372 if (anim_mode == ANIM_DEFAULT)
3373 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3375 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3379 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3381 if (anim_mode != ANIM_NONE)
3382 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3384 if (anim_mode == ANIM_DEFAULT)
3385 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3388 game.envelope_active = FALSE;
3391 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3393 if (IS_MM_WALL(element))
3395 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3401 int graphic = el2preimg(element);
3403 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3404 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3409 void DrawLevel(int draw_background_mask)
3413 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3414 SetDrawBackgroundMask(draw_background_mask);
3418 for (x = BX1; x <= BX2; x++)
3419 for (y = BY1; y <= BY2; y++)
3420 DrawScreenField(x, y);
3422 redraw_mask |= REDRAW_FIELD;
3425 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3430 for (x = 0; x < size_x; x++)
3431 for (y = 0; y < size_y; y++)
3432 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3434 redraw_mask |= REDRAW_FIELD;
3437 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3441 for (x = 0; x < size_x; x++)
3442 for (y = 0; y < size_y; y++)
3443 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3445 redraw_mask |= REDRAW_FIELD;
3448 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3450 boolean show_level_border = (BorderElement != EL_EMPTY);
3451 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3452 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3453 int tile_size = preview.tile_size;
3454 int preview_width = preview.xsize * tile_size;
3455 int preview_height = preview.ysize * tile_size;
3456 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3457 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3458 int real_preview_width = real_preview_xsize * tile_size;
3459 int real_preview_height = real_preview_ysize * tile_size;
3460 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3461 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3464 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3467 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3469 dst_x += (preview_width - real_preview_width) / 2;
3470 dst_y += (preview_height - real_preview_height) / 2;
3472 for (x = 0; x < real_preview_xsize; x++)
3474 for (y = 0; y < real_preview_ysize; y++)
3476 int lx = from_x + x + (show_level_border ? -1 : 0);
3477 int ly = from_y + y + (show_level_border ? -1 : 0);
3478 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3479 getBorderElement(lx, ly));
3481 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3482 element, tile_size);
3486 redraw_mask |= REDRAW_FIELD;
3489 #define MICROLABEL_EMPTY 0
3490 #define MICROLABEL_LEVEL_NAME 1
3491 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3492 #define MICROLABEL_LEVEL_AUTHOR 3
3493 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3494 #define MICROLABEL_IMPORTED_FROM 5
3495 #define MICROLABEL_IMPORTED_BY_HEAD 6
3496 #define MICROLABEL_IMPORTED_BY 7
3498 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3500 int max_text_width = SXSIZE;
3501 int font_width = getFontWidth(font_nr);
3503 if (pos->align == ALIGN_CENTER)
3504 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3505 else if (pos->align == ALIGN_RIGHT)
3506 max_text_width = pos->x;
3508 max_text_width = SXSIZE - pos->x;
3510 return max_text_width / font_width;
3513 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3515 char label_text[MAX_OUTPUT_LINESIZE + 1];
3516 int max_len_label_text;
3517 int font_nr = pos->font;
3520 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3523 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3524 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3525 mode == MICROLABEL_IMPORTED_BY_HEAD)
3526 font_nr = pos->font_alt;
3528 max_len_label_text = getMaxTextLength(pos, font_nr);
3530 if (pos->size != -1)
3531 max_len_label_text = pos->size;
3533 for (i = 0; i < max_len_label_text; i++)
3534 label_text[i] = ' ';
3535 label_text[max_len_label_text] = '\0';
3537 if (strlen(label_text) > 0)
3538 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3541 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3542 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3543 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3544 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3545 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3546 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3547 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3548 max_len_label_text);
3549 label_text[max_len_label_text] = '\0';
3551 if (strlen(label_text) > 0)
3552 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3554 redraw_mask |= REDRAW_FIELD;
3557 static void DrawPreviewLevelLabel(int mode)
3559 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3562 static void DrawPreviewLevelInfo(int mode)
3564 if (mode == MICROLABEL_LEVEL_NAME)
3565 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3566 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3567 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3570 static void DrawPreviewLevelExt(boolean restart)
3572 static DelayCounter scroll_delay = { 0 };
3573 static DelayCounter label_delay = { 0 };
3574 static int from_x, from_y, scroll_direction;
3575 static int label_state, label_counter;
3576 boolean show_level_border = (BorderElement != EL_EMPTY);
3577 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3578 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3580 scroll_delay.value = preview.step_delay;
3581 label_delay.value = MICROLEVEL_LABEL_DELAY;
3588 if (preview.anim_mode == ANIM_CENTERED)
3590 if (level_xsize > preview.xsize)
3591 from_x = (level_xsize - preview.xsize) / 2;
3592 if (level_ysize > preview.ysize)
3593 from_y = (level_ysize - preview.ysize) / 2;
3596 from_x += preview.xoffset;
3597 from_y += preview.yoffset;
3599 scroll_direction = MV_RIGHT;
3603 DrawPreviewLevelPlayfield(from_x, from_y);
3604 DrawPreviewLevelLabel(label_state);
3606 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3607 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3609 // initialize delay counters
3610 ResetDelayCounter(&scroll_delay);
3611 ResetDelayCounter(&label_delay);
3613 if (leveldir_current->name)
3615 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3616 char label_text[MAX_OUTPUT_LINESIZE + 1];
3617 int font_nr = pos->font;
3618 int max_len_label_text = getMaxTextLength(pos, font_nr);
3620 if (pos->size != -1)
3621 max_len_label_text = pos->size;
3623 strncpy(label_text, leveldir_current->name, max_len_label_text);
3624 label_text[max_len_label_text] = '\0';
3626 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3627 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3633 // scroll preview level, if needed
3634 if (preview.anim_mode != ANIM_NONE &&
3635 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3636 DelayReached(&scroll_delay))
3638 switch (scroll_direction)
3643 from_x -= preview.step_offset;
3644 from_x = (from_x < 0 ? 0 : from_x);
3647 scroll_direction = MV_UP;
3651 if (from_x < level_xsize - preview.xsize)
3653 from_x += preview.step_offset;
3654 from_x = (from_x > level_xsize - preview.xsize ?
3655 level_xsize - preview.xsize : from_x);
3658 scroll_direction = MV_DOWN;
3664 from_y -= preview.step_offset;
3665 from_y = (from_y < 0 ? 0 : from_y);
3668 scroll_direction = MV_RIGHT;
3672 if (from_y < level_ysize - preview.ysize)
3674 from_y += preview.step_offset;
3675 from_y = (from_y > level_ysize - preview.ysize ?
3676 level_ysize - preview.ysize : from_y);
3679 scroll_direction = MV_LEFT;
3686 DrawPreviewLevelPlayfield(from_x, from_y);
3689 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3690 // redraw micro level label, if needed
3691 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3692 !strEqual(level.author, ANONYMOUS_NAME) &&
3693 !strEqual(level.author, leveldir_current->name) &&
3694 DelayReached(&label_delay))
3696 int max_label_counter = 23;
3698 if (leveldir_current->imported_from != NULL &&
3699 strlen(leveldir_current->imported_from) > 0)
3700 max_label_counter += 14;
3701 if (leveldir_current->imported_by != NULL &&
3702 strlen(leveldir_current->imported_by) > 0)
3703 max_label_counter += 14;
3705 label_counter = (label_counter + 1) % max_label_counter;
3706 label_state = (label_counter >= 0 && label_counter <= 7 ?
3707 MICROLABEL_LEVEL_NAME :
3708 label_counter >= 9 && label_counter <= 12 ?
3709 MICROLABEL_LEVEL_AUTHOR_HEAD :
3710 label_counter >= 14 && label_counter <= 21 ?
3711 MICROLABEL_LEVEL_AUTHOR :
3712 label_counter >= 23 && label_counter <= 26 ?
3713 MICROLABEL_IMPORTED_FROM_HEAD :
3714 label_counter >= 28 && label_counter <= 35 ?
3715 MICROLABEL_IMPORTED_FROM :
3716 label_counter >= 37 && label_counter <= 40 ?
3717 MICROLABEL_IMPORTED_BY_HEAD :
3718 label_counter >= 42 && label_counter <= 49 ?
3719 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3721 if (leveldir_current->imported_from == NULL &&
3722 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3723 label_state == MICROLABEL_IMPORTED_FROM))
3724 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3725 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3727 DrawPreviewLevelLabel(label_state);
3731 void DrawPreviewPlayers(void)
3733 if (game_status != GAME_MODE_MAIN)
3736 // do not draw preview players if level preview redefined, but players aren't
3737 if (preview.redefined && !menu.main.preview_players.redefined)
3740 boolean player_found[MAX_PLAYERS];
3741 int num_players = 0;
3744 for (i = 0; i < MAX_PLAYERS; i++)
3745 player_found[i] = FALSE;
3747 // check which players can be found in the level (simple approach)
3748 for (x = 0; x < lev_fieldx; x++)
3750 for (y = 0; y < lev_fieldy; y++)
3752 int element = level.field[x][y];
3754 if (IS_PLAYER_ELEMENT(element))
3756 int player_nr = GET_PLAYER_NR(element);
3758 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3760 if (!player_found[player_nr])
3763 player_found[player_nr] = TRUE;
3768 struct TextPosInfo *pos = &menu.main.preview_players;
3769 int tile_size = pos->tile_size;
3770 int border_size = pos->border_size;
3771 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3772 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3773 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3774 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3775 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3776 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3777 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3778 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3779 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3780 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3781 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3782 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3784 // clear area in which the players will be drawn
3785 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3786 max_players_width, max_players_height);
3788 if (!network.enabled && !setup.team_mode)
3791 // only draw players if level is suited for team mode
3792 if (num_players < 2)
3795 // draw all players that were found in the level
3796 for (i = 0; i < MAX_PLAYERS; i++)
3798 if (player_found[i])
3800 int graphic = el2img(EL_PLAYER_1 + i);
3802 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3804 xpos += player_xoffset;
3805 ypos += player_yoffset;
3810 void DrawPreviewLevelInitial(void)
3812 DrawPreviewLevelExt(TRUE);
3813 DrawPreviewPlayers();
3816 void DrawPreviewLevelAnimation(void)
3818 DrawPreviewLevelExt(FALSE);
3821 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3822 int border_size, int font_nr)
3824 int graphic = el2img(EL_PLAYER_1 + player_nr);
3825 int font_height = getFontHeight(font_nr);
3826 int player_height = MAX(tile_size, font_height);
3827 int xoffset_text = tile_size + border_size;
3828 int yoffset_text = (player_height - font_height) / 2;
3829 int yoffset_graphic = (player_height - tile_size) / 2;
3830 char *player_name = getNetworkPlayerName(player_nr + 1);
3832 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3834 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3837 static void DrawNetworkPlayersExt(boolean force)
3839 if (game_status != GAME_MODE_MAIN)
3842 if (!network.connected && !force)
3845 // do not draw network players if level preview redefined, but players aren't
3846 if (preview.redefined && !menu.main.network_players.redefined)
3849 int num_players = 0;
3852 for (i = 0; i < MAX_PLAYERS; i++)
3853 if (stored_player[i].connected_network)
3856 struct TextPosInfo *pos = &menu.main.network_players;
3857 int tile_size = pos->tile_size;
3858 int border_size = pos->border_size;
3859 int xoffset_text = tile_size + border_size;
3860 int font_nr = pos->font;
3861 int font_width = getFontWidth(font_nr);
3862 int font_height = getFontHeight(font_nr);
3863 int player_height = MAX(tile_size, font_height);
3864 int player_yoffset = player_height + border_size;
3865 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3866 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3867 int all_players_height = num_players * player_yoffset - border_size;
3868 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3869 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3870 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3872 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3873 max_players_width, max_players_height);
3875 // first draw local network player ...
3876 for (i = 0; i < MAX_PLAYERS; i++)
3878 if (stored_player[i].connected_network &&
3879 stored_player[i].connected_locally)
3881 char *player_name = getNetworkPlayerName(i + 1);
3882 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3883 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3885 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3887 ypos += player_yoffset;
3891 // ... then draw all other network players
3892 for (i = 0; i < MAX_PLAYERS; i++)
3894 if (stored_player[i].connected_network &&
3895 !stored_player[i].connected_locally)
3897 char *player_name = getNetworkPlayerName(i + 1);
3898 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3899 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3901 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3903 ypos += player_yoffset;
3908 void DrawNetworkPlayers(void)
3910 DrawNetworkPlayersExt(FALSE);
3913 void ClearNetworkPlayers(void)
3915 DrawNetworkPlayersExt(TRUE);
3918 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3919 int graphic, int lx, int ly,
3922 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3924 if (mask_mode == USE_MASKING)
3925 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3927 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3930 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3931 int graphic, int sync_frame, int mask_mode)
3933 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3935 if (mask_mode == USE_MASKING)
3936 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3938 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3941 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3942 int graphic, int sync_frame, int tilesize,
3945 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3947 if (mask_mode == USE_MASKING)
3948 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3950 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3953 static void DrawGraphicAnimation(int x, int y, int graphic)
3955 int lx = LEVELX(x), ly = LEVELY(y);
3956 int mask_mode = NO_MASKING;
3958 if (!IN_SCR_FIELD(x, y))
3961 if (game.use_masked_elements)
3963 if (Tile[lx][ly] != EL_EMPTY)
3965 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3967 mask_mode = USE_MASKING;
3971 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3972 graphic, lx, ly, mask_mode);
3974 MarkTileDirty(x, y);
3977 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3979 int lx = LEVELX(x), ly = LEVELY(y);
3980 int mask_mode = NO_MASKING;
3982 if (!IN_SCR_FIELD(x, y))
3985 if (game.use_masked_elements)
3987 if (Tile[lx][ly] != EL_EMPTY)
3989 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3991 mask_mode = USE_MASKING;
3995 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3996 graphic, lx, ly, mask_mode);
3998 MarkTileDirty(x, y);
4001 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4003 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4006 void DrawLevelElementAnimation(int x, int y, int element)
4008 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4010 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4013 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4015 int sx = SCREENX(x), sy = SCREENY(y);
4017 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4020 if (Tile[x][y] == EL_EMPTY)
4021 graphic = el2img(GfxElementEmpty[x][y]);
4023 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4026 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4029 DrawGraphicAnimation(sx, sy, graphic);
4032 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4033 DrawLevelFieldCrumbled(x, y);
4035 if (GFX_CRUMBLED(Tile[x][y]))
4036 DrawLevelFieldCrumbled(x, y);
4040 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4042 int sx = SCREENX(x), sy = SCREENY(y);
4045 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4048 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4050 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4053 DrawGraphicAnimation(sx, sy, graphic);
4055 if (GFX_CRUMBLED(element))
4056 DrawLevelFieldCrumbled(x, y);
4059 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4061 if (player->use_murphy)
4063 // this works only because currently only one player can be "murphy" ...
4064 static int last_horizontal_dir = MV_LEFT;
4065 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4067 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4068 last_horizontal_dir = move_dir;
4070 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4072 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4074 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4080 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4083 static boolean equalGraphics(int graphic1, int graphic2)
4085 struct GraphicInfo *g1 = &graphic_info[graphic1];
4086 struct GraphicInfo *g2 = &graphic_info[graphic2];
4088 return (g1->bitmap == g2->bitmap &&
4089 g1->src_x == g2->src_x &&
4090 g1->src_y == g2->src_y &&
4091 g1->anim_frames == g2->anim_frames &&
4092 g1->anim_delay == g2->anim_delay &&
4093 g1->anim_mode == g2->anim_mode);
4096 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4100 DRAW_PLAYER_STAGE_INIT = 0,
4101 DRAW_PLAYER_STAGE_LAST_FIELD,
4102 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4103 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4104 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4105 DRAW_PLAYER_STAGE_PLAYER,
4107 DRAW_PLAYER_STAGE_PLAYER,
4108 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4110 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4111 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4113 NUM_DRAW_PLAYER_STAGES
4116 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4118 static int static_last_player_graphic[MAX_PLAYERS];
4119 static int static_last_player_frame[MAX_PLAYERS];
4120 static boolean static_player_is_opaque[MAX_PLAYERS];
4121 static boolean draw_player[MAX_PLAYERS];
4122 int pnr = player->index_nr;
4124 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4126 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4127 static_last_player_frame[pnr] = player->Frame;
4128 static_player_is_opaque[pnr] = FALSE;
4130 draw_player[pnr] = TRUE;
4133 if (!draw_player[pnr])
4137 if (!IN_LEV_FIELD(player->jx, player->jy))
4139 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4140 Debug("draw:DrawPlayerExt", "This should never happen!");
4142 draw_player[pnr] = FALSE;
4148 int last_player_graphic = static_last_player_graphic[pnr];
4149 int last_player_frame = static_last_player_frame[pnr];
4150 boolean player_is_opaque = static_player_is_opaque[pnr];
4152 int jx = player->jx;
4153 int jy = player->jy;
4154 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4155 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4156 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4157 int last_jx = (player->is_moving ? jx - dx : jx);
4158 int last_jy = (player->is_moving ? jy - dy : jy);
4159 int next_jx = jx + dx;
4160 int next_jy = jy + dy;
4161 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4162 int sx = SCREENX(jx);
4163 int sy = SCREENY(jy);
4164 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4165 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4166 int element = Tile[jx][jy];
4167 int last_element = Tile[last_jx][last_jy];
4168 int action = (player->is_pushing ? ACTION_PUSHING :
4169 player->is_digging ? ACTION_DIGGING :
4170 player->is_collecting ? ACTION_COLLECTING :
4171 player->is_moving ? ACTION_MOVING :
4172 player->is_snapping ? ACTION_SNAPPING :
4173 player->is_dropping ? ACTION_DROPPING :
4174 player->is_waiting ? player->action_waiting :
4177 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4179 // ------------------------------------------------------------------------
4180 // initialize drawing the player
4181 // ------------------------------------------------------------------------
4183 draw_player[pnr] = FALSE;
4185 // GfxElement[][] is set to the element the player is digging or collecting;
4186 // remove also for off-screen player if the player is not moving anymore
4187 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4188 GfxElement[jx][jy] = EL_UNDEFINED;
4190 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4193 if (element == EL_EXPLOSION)
4196 InitPlayerGfxAnimation(player, action, move_dir);
4198 draw_player[pnr] = TRUE;
4200 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4202 // ------------------------------------------------------------------------
4203 // draw things in the field the player is leaving, if needed
4204 // ------------------------------------------------------------------------
4206 if (!IN_SCR_FIELD(sx, sy))
4207 draw_player[pnr] = FALSE;
4209 if (!player->is_moving)
4212 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4214 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4216 if (last_element == EL_DYNAMITE_ACTIVE ||
4217 last_element == EL_EM_DYNAMITE_ACTIVE ||
4218 last_element == EL_SP_DISK_RED_ACTIVE)
4219 DrawDynamite(last_jx, last_jy);
4221 DrawLevelFieldThruMask(last_jx, last_jy);
4223 else if (last_element == EL_DYNAMITE_ACTIVE ||
4224 last_element == EL_EM_DYNAMITE_ACTIVE ||
4225 last_element == EL_SP_DISK_RED_ACTIVE)
4226 DrawDynamite(last_jx, last_jy);
4228 DrawLevelField(last_jx, last_jy);
4230 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4232 // ------------------------------------------------------------------------
4233 // draw things behind the player, if needed
4234 // ------------------------------------------------------------------------
4238 DrawLevelElement(jx, jy, Back[jx][jy]);
4243 if (IS_ACTIVE_BOMB(element))
4245 DrawLevelElement(jx, jy, EL_EMPTY);
4250 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4252 int old_element = GfxElement[jx][jy];
4253 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4254 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4256 if (GFX_CRUMBLED(old_element))
4257 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4259 DrawScreenGraphic(sx, sy, old_graphic, frame);
4261 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4262 static_player_is_opaque[pnr] = TRUE;
4266 GfxElement[jx][jy] = EL_UNDEFINED;
4268 // make sure that pushed elements are drawn with correct frame rate
4269 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4271 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4272 GfxFrame[jx][jy] = player->StepFrame;
4274 DrawLevelField(jx, jy);
4277 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4279 // ------------------------------------------------------------------------
4280 // draw things the player is pushing, if needed
4281 // ------------------------------------------------------------------------
4283 if (!player->is_pushing || !player->is_moving)
4286 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4289 int gfx_frame = GfxFrame[jx][jy];
4291 if (!IS_MOVING(jx, jy)) // push movement already finished
4293 element = Tile[next_jx][next_jy];
4294 gfx_frame = GfxFrame[next_jx][next_jy];
4297 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4298 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4299 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4301 // draw background element under pushed element (like the Sokoban field)
4302 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4304 // this allows transparent pushing animation over non-black background
4307 DrawLevelElement(jx, jy, Back[jx][jy]);
4309 DrawLevelElement(jx, jy, EL_EMPTY);
4312 if (Back[next_jx][next_jy])
4313 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4315 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4317 int px = SCREENX(jx), py = SCREENY(jy);
4318 int pxx = (TILEX - ABS(sxx)) * dx;
4319 int pyy = (TILEY - ABS(syy)) * dy;
4322 // do not draw (EM style) pushing animation when pushing is finished
4323 // (two-tile animations usually do not contain start and end frame)
4324 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4325 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4327 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4329 // masked drawing is needed for EMC style (double) movement graphics
4330 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4331 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4334 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4336 // ------------------------------------------------------------------------
4337 // draw player himself
4338 // ------------------------------------------------------------------------
4340 int graphic = getPlayerGraphic(player, move_dir);
4342 // in the case of changed player action or direction, prevent the current
4343 // animation frame from being restarted for identical animations
4344 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4345 player->Frame = last_player_frame;
4347 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4349 if (player_is_opaque)
4350 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4352 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4354 if (SHIELD_ON(player))
4356 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4357 IMG_SHIELD_NORMAL_ACTIVE);
4358 frame = getGraphicAnimationFrame(graphic, -1);
4360 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4363 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4365 // ------------------------------------------------------------------------
4366 // draw things in front of player (active dynamite or dynabombs)
4367 // ------------------------------------------------------------------------
4369 if (IS_ACTIVE_BOMB(element))
4371 int graphic = el2img(element);
4372 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4374 if (game.emulation == EMU_SUPAPLEX)
4375 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4377 DrawGraphicThruMask(sx, sy, graphic, frame);
4380 if (player_is_moving && last_element == EL_EXPLOSION)
4382 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4383 GfxElement[last_jx][last_jy] : EL_EMPTY);
4384 int graphic = el_act2img(element, ACTION_EXPLODING);
4385 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4386 int phase = ExplodePhase[last_jx][last_jy] - 1;
4387 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4390 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4393 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4395 // ------------------------------------------------------------------------
4396 // draw elements the player is just walking/passing through/under
4397 // ------------------------------------------------------------------------
4399 if (player_is_moving)
4401 // handle the field the player is leaving ...
4402 if (IS_ACCESSIBLE_INSIDE(last_element))
4403 DrawLevelField(last_jx, last_jy);
4404 else if (IS_ACCESSIBLE_UNDER(last_element))
4405 DrawLevelFieldThruMask(last_jx, last_jy);
4408 // do not redraw accessible elements if the player is just pushing them
4409 if (!player_is_moving || !player->is_pushing)
4411 // ... and the field the player is entering
4412 if (IS_ACCESSIBLE_INSIDE(element))
4413 DrawLevelField(jx, jy);
4414 else if (IS_ACCESSIBLE_UNDER(element))
4415 DrawLevelFieldThruMask(jx, jy);
4418 MarkTileDirty(sx, sy);
4422 void DrawPlayer(struct PlayerInfo *player)
4426 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4427 DrawPlayerExt(player, i);
4430 void DrawAllPlayers(void)
4434 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4435 for (j = 0; j < MAX_PLAYERS; j++)
4436 if (stored_player[j].active)
4437 DrawPlayerExt(&stored_player[j], i);
4440 void DrawPlayerField(int x, int y)
4442 if (!IS_PLAYER(x, y))
4445 DrawPlayer(PLAYERINFO(x, y));
4448 // ----------------------------------------------------------------------------
4450 void WaitForEventToContinue(void)
4452 boolean first_wait = TRUE;
4453 boolean still_wait = TRUE;
4455 if (program.headless)
4458 // simulate releasing mouse button over last gadget, if still pressed
4460 HandleGadgets(-1, -1, 0);
4462 button_status = MB_RELEASED;
4465 ClearPlayerAction();
4471 if (NextValidEvent(&event))
4475 case EVENT_BUTTONPRESS:
4476 case EVENT_FINGERPRESS:
4480 case EVENT_BUTTONRELEASE:
4481 case EVENT_FINGERRELEASE:
4482 still_wait = first_wait;
4485 case EVENT_KEYPRESS:
4486 case SDL_CONTROLLERBUTTONDOWN:
4487 case SDL_JOYBUTTONDOWN:
4492 HandleOtherEvents(&event);
4496 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4501 if (!PendingEvent())
4506 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4508 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4509 int draw_buffer_last = GetDrawtoField();
4510 int width = request.width;
4511 int height = request.height;
4515 setRequestPosition(&sx, &sy, FALSE);
4517 button_status = MB_RELEASED;
4519 request_gadget_id = -1;
4526 SetDrawtoField(draw_buffer_game);
4528 HandleGameActions();
4530 SetDrawtoField(DRAW_TO_BACKBUFFER);
4537 while (NextValidEvent(&event))
4541 case EVENT_BUTTONPRESS:
4542 case EVENT_BUTTONRELEASE:
4543 case EVENT_MOTIONNOTIFY:
4545 DrawBuffer *drawto_last = drawto;
4548 if (event.type == EVENT_MOTIONNOTIFY)
4553 motion_status = TRUE;
4554 mx = ((MotionEvent *) &event)->x;
4555 my = ((MotionEvent *) &event)->y;
4559 motion_status = FALSE;
4560 mx = ((ButtonEvent *) &event)->x;
4561 my = ((ButtonEvent *) &event)->y;
4562 if (event.type == EVENT_BUTTONPRESS)
4563 button_status = ((ButtonEvent *) &event)->button;
4565 button_status = MB_RELEASED;
4568 if (global.use_envelope_request)
4570 // draw changed button states to temporary bitmap
4571 drawto = bitmap_db_store_1;
4574 // this sets 'request_gadget_id'
4575 HandleGadgets(mx, my, button_status);
4577 if (global.use_envelope_request)
4579 // restore pointer to drawing buffer
4580 drawto = drawto_last;
4582 // prepare complete envelope request from temporary bitmap
4583 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4587 switch (request_gadget_id)
4589 case TOOL_CTRL_ID_YES:
4590 case TOOL_CTRL_ID_TOUCH_YES:
4593 case TOOL_CTRL_ID_NO:
4594 case TOOL_CTRL_ID_TOUCH_NO:
4597 case TOOL_CTRL_ID_CONFIRM:
4598 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4599 result = TRUE | FALSE;
4602 case TOOL_CTRL_ID_PLAYER_1:
4605 case TOOL_CTRL_ID_PLAYER_2:
4608 case TOOL_CTRL_ID_PLAYER_3:
4611 case TOOL_CTRL_ID_PLAYER_4:
4619 // only needed to handle clickable pointer animations here
4620 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4625 case SDL_WINDOWEVENT:
4626 HandleWindowEvent((WindowEvent *) &event);
4629 case SDL_APP_WILLENTERBACKGROUND:
4630 case SDL_APP_DIDENTERBACKGROUND:
4631 case SDL_APP_WILLENTERFOREGROUND:
4632 case SDL_APP_DIDENTERFOREGROUND:
4633 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4636 case EVENT_KEYPRESS:
4638 Key key = GetEventKey((KeyEvent *)&event);
4643 if (req_state & REQ_CONFIRM)
4652 #if defined(KSYM_Rewind)
4653 case KSYM_Rewind: // for Amazon Fire TV remote
4662 #if defined(KSYM_FastForward)
4663 case KSYM_FastForward: // for Amazon Fire TV remote
4669 HandleKeysDebug(key, KEY_PRESSED);
4673 if (req_state & REQ_PLAYER)
4675 int old_player_nr = setup.network_player_nr;
4678 result = old_player_nr + 1;
4683 result = old_player_nr + 1;
4714 case EVENT_FINGERRELEASE:
4715 case EVENT_KEYRELEASE:
4716 ClearPlayerAction();
4719 case SDL_CONTROLLERBUTTONDOWN:
4720 switch (event.cbutton.button)
4722 case SDL_CONTROLLER_BUTTON_A:
4723 case SDL_CONTROLLER_BUTTON_X:
4724 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4725 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4729 case SDL_CONTROLLER_BUTTON_B:
4730 case SDL_CONTROLLER_BUTTON_Y:
4731 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4732 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4733 case SDL_CONTROLLER_BUTTON_BACK:
4738 if (req_state & REQ_PLAYER)
4740 int old_player_nr = setup.network_player_nr;
4743 result = old_player_nr + 1;
4745 switch (event.cbutton.button)
4747 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4748 case SDL_CONTROLLER_BUTTON_Y:
4752 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4753 case SDL_CONTROLLER_BUTTON_B:
4757 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4758 case SDL_CONTROLLER_BUTTON_A:
4762 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4763 case SDL_CONTROLLER_BUTTON_X:
4774 case SDL_CONTROLLERBUTTONUP:
4775 HandleJoystickEvent(&event);
4776 ClearPlayerAction();
4780 HandleOtherEvents(&event);
4785 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4787 int joy = AnyJoystick();
4789 if (joy & JOY_BUTTON_1)
4791 else if (joy & JOY_BUTTON_2)
4794 else if (AnyJoystick())
4796 int joy = AnyJoystick();
4798 if (req_state & REQ_PLAYER)
4802 else if (joy & JOY_RIGHT)
4804 else if (joy & JOY_DOWN)
4806 else if (joy & JOY_LEFT)
4814 SetDrawtoField(draw_buffer_last);
4819 static void DoRequestBefore(void)
4821 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4823 // when showing request dialog after game ended, deactivate game panel
4825 game.panel.active = FALSE;
4827 if (game_status == GAME_MODE_PLAYING)
4828 BlitScreenToBitmap(backbuffer);
4830 // disable deactivated drawing when quick-loading level tape recording
4831 if (tape.playing && tape.deactivate_display)
4832 TapeDeactivateDisplayOff(TRUE);
4834 SetMouseCursor(CURSOR_DEFAULT);
4836 // pause network game while waiting for request to answer
4837 if (network.enabled &&
4838 game_status == GAME_MODE_PLAYING &&
4839 !game.all_players_gone)
4840 SendToServer_PausePlaying();
4842 // simulate releasing mouse button over last gadget, if still pressed
4844 HandleGadgets(-1, -1, 0);
4849 static void DoRequestAfter(void)
4853 if (game_status == GAME_MODE_PLAYING)
4855 SetPanelBackground();
4856 SetDrawBackgroundMask(REDRAW_DOOR_1);
4860 SetDrawBackgroundMask(REDRAW_FIELD);
4863 // continue network game after request
4864 if (network.enabled &&
4865 game_status == GAME_MODE_PLAYING &&
4866 !game.all_players_gone)
4867 SendToServer_ContinuePlaying();
4869 // restore deactivated drawing when quick-loading level tape recording
4870 if (tape.playing && tape.deactivate_display)
4871 TapeDeactivateDisplayOn();
4874 static void setRequestDoorTextProperties(char *text,
4879 int *set_max_line_length)
4881 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4882 struct TextPosInfo *pos = &request.button.confirm;
4883 int button_ypos = pos->y;
4884 int font_nr = FONT_TEXT_2;
4885 int font_width = getFontWidth(font_nr);
4886 int font_height = getFontHeight(font_nr);
4887 int line_height = font_height + line_spacing;
4888 int max_text_width = vp_door_1->width;
4889 int max_text_height = button_ypos - 2 * text_spacing;
4890 int max_line_length = max_text_width / font_width;
4891 int max_lines = max_text_height / line_height;
4893 if (maxWordLengthInRequestString(text) > max_line_length)
4895 font_nr = FONT_TEXT_1;
4896 font_width = getFontWidth(font_nr);
4897 max_line_length = max_text_width / font_width;
4900 *set_font_nr = font_nr;
4901 *set_max_lines = max_lines;
4902 *set_max_line_length = max_line_length;
4905 static void DrawRequestDoorText(char *text)
4907 char *text_ptr = text;
4908 int text_spacing = 8;
4909 int line_spacing = 2;
4910 int max_request_lines;
4911 int max_request_line_len;
4915 // force DOOR font inside door area
4916 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4918 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4919 &max_request_lines, &max_request_line_len);
4921 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4923 char text_line[max_request_line_len + 1];
4929 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4931 tc = *(text_ptr + tx);
4932 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4936 if ((tc == '?' || tc == '!') && tl == 0)
4946 strncpy(text_line, text_ptr, tl);
4949 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4950 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4951 text_line, font_nr);
4953 text_ptr += tl + (tc == ' ' ? 1 : 0);
4959 static int RequestDoor(char *text, unsigned int req_state)
4961 unsigned int old_door_state = GetDoorState();
4962 int draw_buffer_last = GetDrawtoField();
4965 if (old_door_state & DOOR_OPEN_1)
4967 CloseDoor(DOOR_CLOSE_1);
4969 // save old door content
4970 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4971 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4974 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4975 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4977 // clear door drawing field
4978 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4980 // write text for request
4981 DrawRequestDoorText(text);
4983 MapToolButtons(req_state);
4985 // copy request gadgets to door backbuffer
4986 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4988 OpenDoor(DOOR_OPEN_1);
4990 // ---------- handle request buttons ----------
4991 result = RequestHandleEvents(req_state, draw_buffer_last);
4995 if (!(req_state & REQ_STAY_OPEN))
4997 CloseDoor(DOOR_CLOSE_1);
4999 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5000 (req_state & REQ_REOPEN))
5001 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5007 static int RequestEnvelope(char *text, unsigned int req_state)
5009 int draw_buffer_last = GetDrawtoField();
5012 DrawEnvelopeRequest(text, req_state);
5013 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5015 // ---------- handle request buttons ----------
5016 result = RequestHandleEvents(req_state, draw_buffer_last);
5020 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5025 int Request(char *text, unsigned int req_state)
5027 boolean overlay_enabled = GetOverlayEnabled();
5030 game.request_active = TRUE;
5032 SetOverlayEnabled(FALSE);
5036 if (global.use_envelope_request)
5037 result = RequestEnvelope(text, req_state);
5039 result = RequestDoor(text, req_state);
5043 SetOverlayEnabled(overlay_enabled);
5045 game.request_active = FALSE;
5050 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5052 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5053 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5056 if (dpo1->sort_priority != dpo2->sort_priority)
5057 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5059 compare_result = dpo1->nr - dpo2->nr;
5061 return compare_result;
5064 void InitGraphicCompatibilityInfo_Doors(void)
5070 struct DoorInfo *door;
5074 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5075 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5077 { -1, -1, -1, NULL }
5079 struct Rect door_rect_list[] =
5081 { DX, DY, DXSIZE, DYSIZE },
5082 { VX, VY, VXSIZE, VYSIZE }
5086 for (i = 0; doors[i].door_token != -1; i++)
5088 int door_token = doors[i].door_token;
5089 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5090 int part_1 = doors[i].part_1;
5091 int part_8 = doors[i].part_8;
5092 int part_2 = part_1 + 1;
5093 int part_3 = part_1 + 2;
5094 struct DoorInfo *door = doors[i].door;
5095 struct Rect *door_rect = &door_rect_list[door_index];
5096 boolean door_gfx_redefined = FALSE;
5098 // check if any door part graphic definitions have been redefined
5100 for (j = 0; door_part_controls[j].door_token != -1; j++)
5102 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5103 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5105 if (dpc->door_token == door_token && fi->redefined)
5106 door_gfx_redefined = TRUE;
5109 // check for old-style door graphic/animation modifications
5111 if (!door_gfx_redefined)
5113 if (door->anim_mode & ANIM_STATIC_PANEL)
5115 door->panel.step_xoffset = 0;
5116 door->panel.step_yoffset = 0;
5119 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5121 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5122 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5123 int num_door_steps, num_panel_steps;
5125 // remove door part graphics other than the two default wings
5127 for (j = 0; door_part_controls[j].door_token != -1; j++)
5129 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5130 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5132 if (dpc->graphic >= part_3 &&
5133 dpc->graphic <= part_8)
5137 // set graphics and screen positions of the default wings
5139 g_part_1->width = door_rect->width;
5140 g_part_1->height = door_rect->height;
5141 g_part_2->width = door_rect->width;
5142 g_part_2->height = door_rect->height;
5143 g_part_2->src_x = door_rect->width;
5144 g_part_2->src_y = g_part_1->src_y;
5146 door->part_2.x = door->part_1.x;
5147 door->part_2.y = door->part_1.y;
5149 if (door->width != -1)
5151 g_part_1->width = door->width;
5152 g_part_2->width = door->width;
5154 // special treatment for graphics and screen position of right wing
5155 g_part_2->src_x += door_rect->width - door->width;
5156 door->part_2.x += door_rect->width - door->width;
5159 if (door->height != -1)
5161 g_part_1->height = door->height;
5162 g_part_2->height = door->height;
5164 // special treatment for graphics and screen position of bottom wing
5165 g_part_2->src_y += door_rect->height - door->height;
5166 door->part_2.y += door_rect->height - door->height;
5169 // set animation delays for the default wings and panels
5171 door->part_1.step_delay = door->step_delay;
5172 door->part_2.step_delay = door->step_delay;
5173 door->panel.step_delay = door->step_delay;
5175 // set animation draw order for the default wings
5177 door->part_1.sort_priority = 2; // draw left wing over ...
5178 door->part_2.sort_priority = 1; // ... right wing
5180 // set animation draw offset for the default wings
5182 if (door->anim_mode & ANIM_HORIZONTAL)
5184 door->part_1.step_xoffset = door->step_offset;
5185 door->part_1.step_yoffset = 0;
5186 door->part_2.step_xoffset = door->step_offset * -1;
5187 door->part_2.step_yoffset = 0;
5189 num_door_steps = g_part_1->width / door->step_offset;
5191 else // ANIM_VERTICAL
5193 door->part_1.step_xoffset = 0;
5194 door->part_1.step_yoffset = door->step_offset;
5195 door->part_2.step_xoffset = 0;
5196 door->part_2.step_yoffset = door->step_offset * -1;
5198 num_door_steps = g_part_1->height / door->step_offset;
5201 // set animation draw offset for the default panels
5203 if (door->step_offset > 1)
5205 num_panel_steps = 2 * door_rect->height / door->step_offset;
5206 door->panel.start_step = num_panel_steps - num_door_steps;
5207 door->panel.start_step_closing = door->panel.start_step;
5211 num_panel_steps = door_rect->height / door->step_offset;
5212 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5213 door->panel.start_step_closing = door->panel.start_step;
5214 door->panel.step_delay *= 2;
5221 void InitDoors(void)
5225 for (i = 0; door_part_controls[i].door_token != -1; i++)
5227 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5228 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5230 // initialize "start_step_opening" and "start_step_closing", if needed
5231 if (dpc->pos->start_step_opening == 0 &&
5232 dpc->pos->start_step_closing == 0)
5234 // dpc->pos->start_step_opening = dpc->pos->start_step;
5235 dpc->pos->start_step_closing = dpc->pos->start_step;
5238 // fill structure for door part draw order (sorted below)
5240 dpo->sort_priority = dpc->pos->sort_priority;
5243 // sort door part controls according to sort_priority and graphic number
5244 qsort(door_part_order, MAX_DOOR_PARTS,
5245 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5248 unsigned int OpenDoor(unsigned int door_state)
5250 if (door_state & DOOR_COPY_BACK)
5252 if (door_state & DOOR_OPEN_1)
5253 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5254 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5256 if (door_state & DOOR_OPEN_2)
5257 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5258 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5260 door_state &= ~DOOR_COPY_BACK;
5263 return MoveDoor(door_state);
5266 unsigned int CloseDoor(unsigned int door_state)
5268 unsigned int old_door_state = GetDoorState();
5270 if (!(door_state & DOOR_NO_COPY_BACK))
5272 if (old_door_state & DOOR_OPEN_1)
5273 BlitBitmap(backbuffer, bitmap_db_door_1,
5274 DX, DY, DXSIZE, DYSIZE, 0, 0);
5276 if (old_door_state & DOOR_OPEN_2)
5277 BlitBitmap(backbuffer, bitmap_db_door_2,
5278 VX, VY, VXSIZE, VYSIZE, 0, 0);
5280 door_state &= ~DOOR_NO_COPY_BACK;
5283 return MoveDoor(door_state);
5286 unsigned int GetDoorState(void)
5288 return MoveDoor(DOOR_GET_STATE);
5291 unsigned int SetDoorState(unsigned int door_state)
5293 return MoveDoor(door_state | DOOR_SET_STATE);
5296 static int euclid(int a, int b)
5298 return (b ? euclid(b, a % b) : a);
5301 unsigned int MoveDoor(unsigned int door_state)
5303 struct Rect door_rect_list[] =
5305 { DX, DY, DXSIZE, DYSIZE },
5306 { VX, VY, VXSIZE, VYSIZE }
5308 static int door1 = DOOR_CLOSE_1;
5309 static int door2 = DOOR_CLOSE_2;
5310 DelayCounter door_delay = { 0 };
5313 if (door_state == DOOR_GET_STATE)
5314 return (door1 | door2);
5316 if (door_state & DOOR_SET_STATE)
5318 if (door_state & DOOR_ACTION_1)
5319 door1 = door_state & DOOR_ACTION_1;
5320 if (door_state & DOOR_ACTION_2)
5321 door2 = door_state & DOOR_ACTION_2;
5323 return (door1 | door2);
5326 if (!(door_state & DOOR_FORCE_REDRAW))
5328 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5329 door_state &= ~DOOR_OPEN_1;
5330 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5331 door_state &= ~DOOR_CLOSE_1;
5332 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5333 door_state &= ~DOOR_OPEN_2;
5334 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5335 door_state &= ~DOOR_CLOSE_2;
5338 if (global.autoplay_leveldir)
5340 door_state |= DOOR_NO_DELAY;
5341 door_state &= ~DOOR_CLOSE_ALL;
5344 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5345 door_state |= DOOR_NO_DELAY;
5347 if (door_state & DOOR_ACTION)
5349 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5350 boolean door_panel_drawn[NUM_DOORS];
5351 boolean panel_has_doors[NUM_DOORS];
5352 boolean door_part_skip[MAX_DOOR_PARTS];
5353 boolean door_part_done[MAX_DOOR_PARTS];
5354 boolean door_part_done_all;
5355 int num_steps[MAX_DOOR_PARTS];
5356 int max_move_delay = 0; // delay for complete animations of all doors
5357 int max_step_delay = 0; // delay (ms) between two animation frames
5358 int num_move_steps = 0; // number of animation steps for all doors
5359 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5360 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5364 for (i = 0; i < NUM_DOORS; i++)
5365 panel_has_doors[i] = FALSE;
5367 for (i = 0; i < MAX_DOOR_PARTS; i++)
5369 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5370 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5371 int door_token = dpc->door_token;
5373 door_part_done[i] = FALSE;
5374 door_part_skip[i] = (!(door_state & door_token) ||
5378 for (i = 0; i < MAX_DOOR_PARTS; i++)
5380 int nr = door_part_order[i].nr;
5381 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5382 struct DoorPartPosInfo *pos = dpc->pos;
5383 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5384 int door_token = dpc->door_token;
5385 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5386 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5387 int step_xoffset = ABS(pos->step_xoffset);
5388 int step_yoffset = ABS(pos->step_yoffset);
5389 int step_delay = pos->step_delay;
5390 int current_door_state = door_state & door_token;
5391 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5392 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5393 boolean part_opening = (is_panel ? door_closing : door_opening);
5394 int start_step = (part_opening ? pos->start_step_opening :
5395 pos->start_step_closing);
5396 float move_xsize = (step_xoffset ? g->width : 0);
5397 float move_ysize = (step_yoffset ? g->height : 0);
5398 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5399 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5400 int move_steps = (move_xsteps && move_ysteps ?
5401 MIN(move_xsteps, move_ysteps) :
5402 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5403 int move_delay = move_steps * step_delay;
5405 if (door_part_skip[nr])
5408 max_move_delay = MAX(max_move_delay, move_delay);
5409 max_step_delay = (max_step_delay == 0 ? step_delay :
5410 euclid(max_step_delay, step_delay));
5411 num_steps[nr] = move_steps;
5415 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5417 panel_has_doors[door_index] = TRUE;
5421 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5423 num_move_steps = max_move_delay / max_step_delay;
5424 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5426 door_delay.value = max_step_delay;
5428 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5430 start = num_move_steps - 1;
5434 // opening door sound has priority over simultaneously closing door
5435 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5437 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5439 if (door_state & DOOR_OPEN_1)
5440 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5441 if (door_state & DOOR_OPEN_2)
5442 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5444 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5446 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5448 if (door_state & DOOR_CLOSE_1)
5449 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5450 if (door_state & DOOR_CLOSE_2)
5451 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5455 for (k = start; k < num_move_steps; k++)
5457 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5459 door_part_done_all = TRUE;
5461 for (i = 0; i < NUM_DOORS; i++)
5462 door_panel_drawn[i] = FALSE;
5464 for (i = 0; i < MAX_DOOR_PARTS; i++)
5466 int nr = door_part_order[i].nr;
5467 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5468 struct DoorPartPosInfo *pos = dpc->pos;
5469 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5470 int door_token = dpc->door_token;
5471 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5472 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5473 boolean is_panel_and_door_has_closed = FALSE;
5474 struct Rect *door_rect = &door_rect_list[door_index];
5475 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5477 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5478 int current_door_state = door_state & door_token;
5479 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5480 boolean door_closing = !door_opening;
5481 boolean part_opening = (is_panel ? door_closing : door_opening);
5482 boolean part_closing = !part_opening;
5483 int start_step = (part_opening ? pos->start_step_opening :
5484 pos->start_step_closing);
5485 int step_delay = pos->step_delay;
5486 int step_factor = step_delay / max_step_delay;
5487 int k1 = (step_factor ? k / step_factor + 1 : k);
5488 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5489 int kk = MAX(0, k2);
5492 int src_x, src_y, src_xx, src_yy;
5493 int dst_x, dst_y, dst_xx, dst_yy;
5496 if (door_part_skip[nr])
5499 if (!(door_state & door_token))
5507 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5508 int kk_door = MAX(0, k2_door);
5509 int sync_frame = kk_door * door_delay.value;
5510 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5512 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5513 &g_src_x, &g_src_y);
5518 if (!door_panel_drawn[door_index])
5520 ClearRectangle(drawto, door_rect->x, door_rect->y,
5521 door_rect->width, door_rect->height);
5523 door_panel_drawn[door_index] = TRUE;
5526 // draw opening or closing door parts
5528 if (pos->step_xoffset < 0) // door part on right side
5531 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5534 if (dst_xx + width > door_rect->width)
5535 width = door_rect->width - dst_xx;
5537 else // door part on left side
5540 dst_xx = pos->x - kk * pos->step_xoffset;
5544 src_xx = ABS(dst_xx);
5548 width = g->width - src_xx;
5550 if (width > door_rect->width)
5551 width = door_rect->width;
5553 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5556 if (pos->step_yoffset < 0) // door part on bottom side
5559 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5562 if (dst_yy + height > door_rect->height)
5563 height = door_rect->height - dst_yy;
5565 else // door part on top side
5568 dst_yy = pos->y - kk * pos->step_yoffset;
5572 src_yy = ABS(dst_yy);
5576 height = g->height - src_yy;
5579 src_x = g_src_x + src_xx;
5580 src_y = g_src_y + src_yy;
5582 dst_x = door_rect->x + dst_xx;
5583 dst_y = door_rect->y + dst_yy;
5585 is_panel_and_door_has_closed =
5588 panel_has_doors[door_index] &&
5589 k >= num_move_steps_doors_only - 1);
5591 if (width >= 0 && width <= g->width &&
5592 height >= 0 && height <= g->height &&
5593 !is_panel_and_door_has_closed)
5595 if (is_panel || !pos->draw_masked)
5596 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5599 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5603 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5605 if ((part_opening && (width < 0 || height < 0)) ||
5606 (part_closing && (width >= g->width && height >= g->height)))
5607 door_part_done[nr] = TRUE;
5609 // continue door part animations, but not panel after door has closed
5610 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5611 door_part_done_all = FALSE;
5614 if (!(door_state & DOOR_NO_DELAY))
5617 HandleGameActions();
5621 SkipUntilDelayReached(&door_delay, &k, last_frame);
5623 // prevent OS (Windows) from complaining about program not responding
5627 if (door_part_done_all)
5631 if (!(door_state & DOOR_NO_DELAY))
5633 // wait for specified door action post delay
5634 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5635 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5636 else if (door_state & DOOR_ACTION_1)
5637 door_delay.value = door_1.post_delay;
5638 else if (door_state & DOOR_ACTION_2)
5639 door_delay.value = door_2.post_delay;
5641 while (!DelayReached(&door_delay))
5644 HandleGameActions();
5651 if (door_state & DOOR_ACTION_1)
5652 door1 = door_state & DOOR_ACTION_1;
5653 if (door_state & DOOR_ACTION_2)
5654 door2 = door_state & DOOR_ACTION_2;
5656 // draw masked border over door area
5657 DrawMaskedBorder(REDRAW_DOOR_1);
5658 DrawMaskedBorder(REDRAW_DOOR_2);
5660 ClearAutoRepeatKeyEvents();
5662 return (door1 | door2);
5665 static boolean useSpecialEditorDoor(void)
5667 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5668 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5670 // do not draw special editor door if editor border defined or redefined
5671 if (graphic_info[graphic].bitmap != NULL || redefined)
5674 // do not draw special editor door if global border defined to be empty
5675 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5678 // do not draw special editor door if viewport definitions do not match
5682 EY + EYSIZE != VY + VYSIZE)
5688 void DrawSpecialEditorDoor(void)
5690 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5691 int top_border_width = gfx1->width;
5692 int top_border_height = gfx1->height;
5693 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5694 int ex = EX - outer_border;
5695 int ey = EY - outer_border;
5696 int vy = VY - outer_border;
5697 int exsize = EXSIZE + 2 * outer_border;
5699 if (!useSpecialEditorDoor())
5702 // draw bigger level editor toolbox window
5703 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5704 top_border_width, top_border_height, ex, ey - top_border_height);
5705 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5706 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5708 redraw_mask |= REDRAW_ALL;
5711 void UndrawSpecialEditorDoor(void)
5713 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5714 int top_border_width = gfx1->width;
5715 int top_border_height = gfx1->height;
5716 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5717 int ex = EX - outer_border;
5718 int ey = EY - outer_border;
5719 int ey_top = ey - top_border_height;
5720 int exsize = EXSIZE + 2 * outer_border;
5721 int eysize = EYSIZE + 2 * outer_border;
5723 if (!useSpecialEditorDoor())
5726 // draw normal tape recorder window
5727 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5729 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5730 ex, ey_top, top_border_width, top_border_height,
5732 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5733 ex, ey, exsize, eysize, ex, ey);
5737 // if screen background is set to "[NONE]", clear editor toolbox window
5738 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5739 ClearRectangle(drawto, ex, ey, exsize, eysize);
5742 redraw_mask |= REDRAW_ALL;
5746 // ---------- new tool button stuff -------------------------------------------
5751 struct TextPosInfo *pos;
5753 boolean is_touch_button;
5755 } toolbutton_info[NUM_TOOL_BUTTONS] =
5758 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5759 TOOL_CTRL_ID_YES, FALSE, "yes"
5762 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5763 TOOL_CTRL_ID_NO, FALSE, "no"
5766 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5767 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5770 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5771 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5774 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5775 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5778 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5779 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5782 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5783 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5786 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5787 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5790 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5791 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5794 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5795 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5799 void CreateToolButtons(void)
5803 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5805 int graphic = toolbutton_info[i].graphic;
5806 struct GraphicInfo *gfx = &graphic_info[graphic];
5807 struct TextPosInfo *pos = toolbutton_info[i].pos;
5808 struct GadgetInfo *gi;
5809 Bitmap *deco_bitmap = None;
5810 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5811 unsigned int event_mask = GD_EVENT_RELEASED;
5812 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5813 int base_x = (is_touch_button ? 0 : DX);
5814 int base_y = (is_touch_button ? 0 : DY);
5815 int gd_x = gfx->src_x;
5816 int gd_y = gfx->src_y;
5817 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5818 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5823 // do not use touch buttons if overlay touch buttons are disabled
5824 if (is_touch_button && !setup.touch.overlay_buttons)
5827 if (global.use_envelope_request && !is_touch_button)
5829 setRequestPosition(&base_x, &base_y, TRUE);
5831 // check if request buttons are outside of envelope and fix, if needed
5832 if (x < 0 || x + gfx->width > request.width ||
5833 y < 0 || y + gfx->height > request.height)
5835 if (id == TOOL_CTRL_ID_YES)
5838 y = request.height - 2 * request.border_size - gfx->height;
5840 else if (id == TOOL_CTRL_ID_NO)
5842 x = request.width - 2 * request.border_size - gfx->width;
5843 y = request.height - 2 * request.border_size - gfx->height;
5845 else if (id == TOOL_CTRL_ID_CONFIRM)
5847 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5848 y = request.height - 2 * request.border_size - gfx->height;
5850 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5852 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5854 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5855 y = request.height - 2 * request.border_size - gfx->height * 2;
5857 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5858 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5863 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5866 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5868 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5869 pos->size, &deco_bitmap, &deco_x, &deco_y);
5870 deco_xpos = (gfx->width - pos->size) / 2;
5871 deco_ypos = (gfx->height - pos->size) / 2;
5874 gi = CreateGadget(GDI_CUSTOM_ID, id,
5875 GDI_IMAGE_ID, graphic,
5876 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5879 GDI_WIDTH, gfx->width,
5880 GDI_HEIGHT, gfx->height,
5881 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5882 GDI_STATE, GD_BUTTON_UNPRESSED,
5883 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5884 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5885 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5886 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5887 GDI_DECORATION_SIZE, pos->size, pos->size,
5888 GDI_DECORATION_SHIFTING, 1, 1,
5889 GDI_DIRECT_DRAW, FALSE,
5890 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5891 GDI_EVENT_MASK, event_mask,
5892 GDI_CALLBACK_ACTION, HandleToolButtons,
5896 Fail("cannot create gadget");
5898 tool_gadget[id] = gi;
5902 void FreeToolButtons(void)
5906 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5907 FreeGadget(tool_gadget[i]);
5910 static void MapToolButtons(unsigned int req_state)
5912 if (req_state & REQ_ASK)
5914 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5915 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5916 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5917 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5919 else if (req_state & REQ_CONFIRM)
5921 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5922 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5924 else if (req_state & REQ_PLAYER)
5926 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5927 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5928 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5929 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5933 static void UnmapToolButtons(void)
5937 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5938 UnmapGadget(tool_gadget[i]);
5941 static void HandleToolButtons(struct GadgetInfo *gi)
5943 request_gadget_id = gi->custom_id;
5946 static struct Mapping_EM_to_RND_object
5949 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5950 boolean is_backside; // backside of moving element
5956 em_object_mapping_list[GAME_TILE_MAX + 1] =
5959 Zborder, FALSE, FALSE,
5963 Zplayer, FALSE, FALSE,
5972 Ztank, FALSE, FALSE,
5976 Zeater, FALSE, FALSE,
5980 Zdynamite, FALSE, FALSE,
5984 Zboom, FALSE, FALSE,
5989 Xchain, FALSE, FALSE,
5990 EL_DEFAULT, ACTION_EXPLODING, -1
5993 Xboom_bug, FALSE, FALSE,
5994 EL_BUG, ACTION_EXPLODING, -1
5997 Xboom_tank, FALSE, FALSE,
5998 EL_SPACESHIP, ACTION_EXPLODING, -1
6001 Xboom_android, FALSE, FALSE,
6002 EL_EMC_ANDROID, ACTION_OTHER, -1
6005 Xboom_1, FALSE, FALSE,
6006 EL_DEFAULT, ACTION_EXPLODING, -1
6009 Xboom_2, FALSE, FALSE,
6010 EL_DEFAULT, ACTION_EXPLODING, -1
6014 Xblank, TRUE, FALSE,
6019 Xsplash_e, FALSE, FALSE,
6020 EL_ACID_SPLASH_RIGHT, -1, -1
6023 Xsplash_w, FALSE, FALSE,
6024 EL_ACID_SPLASH_LEFT, -1, -1
6028 Xplant, TRUE, FALSE,
6029 EL_EMC_PLANT, -1, -1
6032 Yplant, FALSE, FALSE,
6033 EL_EMC_PLANT, -1, -1
6037 Xacid_1, TRUE, FALSE,
6041 Xacid_2, FALSE, FALSE,
6045 Xacid_3, FALSE, FALSE,
6049 Xacid_4, FALSE, FALSE,
6053 Xacid_5, FALSE, FALSE,
6057 Xacid_6, FALSE, FALSE,
6061 Xacid_7, FALSE, FALSE,
6065 Xacid_8, FALSE, FALSE,
6070 Xfake_acid_1, TRUE, FALSE,
6071 EL_EMC_FAKE_ACID, -1, -1
6074 Xfake_acid_2, FALSE, FALSE,
6075 EL_EMC_FAKE_ACID, -1, -1
6078 Xfake_acid_3, FALSE, FALSE,
6079 EL_EMC_FAKE_ACID, -1, -1
6082 Xfake_acid_4, FALSE, FALSE,
6083 EL_EMC_FAKE_ACID, -1, -1
6086 Xfake_acid_5, FALSE, FALSE,
6087 EL_EMC_FAKE_ACID, -1, -1
6090 Xfake_acid_6, FALSE, FALSE,
6091 EL_EMC_FAKE_ACID, -1, -1
6094 Xfake_acid_7, FALSE, FALSE,
6095 EL_EMC_FAKE_ACID, -1, -1
6098 Xfake_acid_8, FALSE, FALSE,
6099 EL_EMC_FAKE_ACID, -1, -1
6103 Xfake_acid_1_player, FALSE, FALSE,
6104 EL_EMC_FAKE_ACID, -1, -1
6107 Xfake_acid_2_player, FALSE, FALSE,
6108 EL_EMC_FAKE_ACID, -1, -1
6111 Xfake_acid_3_player, FALSE, FALSE,
6112 EL_EMC_FAKE_ACID, -1, -1
6115 Xfake_acid_4_player, FALSE, FALSE,
6116 EL_EMC_FAKE_ACID, -1, -1
6119 Xfake_acid_5_player, FALSE, FALSE,
6120 EL_EMC_FAKE_ACID, -1, -1
6123 Xfake_acid_6_player, FALSE, FALSE,
6124 EL_EMC_FAKE_ACID, -1, -1
6127 Xfake_acid_7_player, FALSE, FALSE,
6128 EL_EMC_FAKE_ACID, -1, -1
6131 Xfake_acid_8_player, FALSE, FALSE,
6132 EL_EMC_FAKE_ACID, -1, -1
6136 Xgrass, TRUE, FALSE,
6137 EL_EMC_GRASS, -1, -1
6140 Ygrass_nB, FALSE, FALSE,
6141 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6144 Ygrass_eB, FALSE, FALSE,
6145 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6148 Ygrass_sB, FALSE, FALSE,
6149 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6152 Ygrass_wB, FALSE, FALSE,
6153 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6161 Ydirt_nB, FALSE, FALSE,
6162 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6165 Ydirt_eB, FALSE, FALSE,
6166 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6169 Ydirt_sB, FALSE, FALSE,
6170 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6173 Ydirt_wB, FALSE, FALSE,
6174 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6178 Xandroid, TRUE, FALSE,
6179 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6182 Xandroid_1_n, FALSE, FALSE,
6183 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6186 Xandroid_2_n, FALSE, FALSE,
6187 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6190 Xandroid_1_e, FALSE, FALSE,
6191 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6194 Xandroid_2_e, FALSE, FALSE,
6195 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6198 Xandroid_1_w, FALSE, FALSE,
6199 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6202 Xandroid_2_w, FALSE, FALSE,
6203 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6206 Xandroid_1_s, FALSE, FALSE,
6207 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6210 Xandroid_2_s, FALSE, FALSE,
6211 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6214 Yandroid_n, FALSE, FALSE,
6215 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6218 Yandroid_nB, FALSE, TRUE,
6219 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6222 Yandroid_ne, FALSE, FALSE,
6223 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6226 Yandroid_neB, FALSE, TRUE,
6227 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6230 Yandroid_e, FALSE, FALSE,
6231 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6234 Yandroid_eB, FALSE, TRUE,
6235 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6238 Yandroid_se, FALSE, FALSE,
6239 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6242 Yandroid_seB, FALSE, TRUE,
6243 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6246 Yandroid_s, FALSE, FALSE,
6247 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6250 Yandroid_sB, FALSE, TRUE,
6251 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6254 Yandroid_sw, FALSE, FALSE,
6255 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6258 Yandroid_swB, FALSE, TRUE,
6259 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6262 Yandroid_w, FALSE, FALSE,
6263 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6266 Yandroid_wB, FALSE, TRUE,
6267 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6270 Yandroid_nw, FALSE, FALSE,
6271 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6274 Yandroid_nwB, FALSE, TRUE,
6275 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6279 Xeater_n, TRUE, FALSE,
6280 EL_YAMYAM_UP, -1, -1
6283 Xeater_e, TRUE, FALSE,
6284 EL_YAMYAM_RIGHT, -1, -1
6287 Xeater_w, TRUE, FALSE,
6288 EL_YAMYAM_LEFT, -1, -1
6291 Xeater_s, TRUE, FALSE,
6292 EL_YAMYAM_DOWN, -1, -1
6295 Yeater_n, FALSE, FALSE,
6296 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6299 Yeater_nB, FALSE, TRUE,
6300 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6303 Yeater_e, FALSE, FALSE,
6304 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6307 Yeater_eB, FALSE, TRUE,
6308 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6311 Yeater_s, FALSE, FALSE,
6312 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6315 Yeater_sB, FALSE, TRUE,
6316 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6319 Yeater_w, FALSE, FALSE,
6320 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6323 Yeater_wB, FALSE, TRUE,
6324 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6327 Yeater_stone, FALSE, FALSE,
6328 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6331 Yeater_spring, FALSE, FALSE,
6332 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6336 Xalien, TRUE, FALSE,
6340 Xalien_pause, FALSE, FALSE,
6344 Yalien_n, FALSE, FALSE,
6345 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6348 Yalien_nB, FALSE, TRUE,
6349 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6352 Yalien_e, FALSE, FALSE,
6353 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6356 Yalien_eB, FALSE, TRUE,
6357 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6360 Yalien_s, FALSE, FALSE,
6361 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6364 Yalien_sB, FALSE, TRUE,
6365 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6368 Yalien_w, FALSE, FALSE,
6369 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6372 Yalien_wB, FALSE, TRUE,
6373 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6376 Yalien_stone, FALSE, FALSE,
6377 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6380 Yalien_spring, FALSE, FALSE,
6381 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6385 Xbug_1_n, TRUE, FALSE,
6389 Xbug_1_e, TRUE, FALSE,
6390 EL_BUG_RIGHT, -1, -1
6393 Xbug_1_s, TRUE, FALSE,
6397 Xbug_1_w, TRUE, FALSE,
6401 Xbug_2_n, FALSE, FALSE,
6405 Xbug_2_e, FALSE, FALSE,
6406 EL_BUG_RIGHT, -1, -1
6409 Xbug_2_s, FALSE, FALSE,
6413 Xbug_2_w, FALSE, FALSE,
6417 Ybug_n, FALSE, FALSE,
6418 EL_BUG, ACTION_MOVING, MV_BIT_UP
6421 Ybug_nB, FALSE, TRUE,
6422 EL_BUG, ACTION_MOVING, MV_BIT_UP
6425 Ybug_e, FALSE, FALSE,
6426 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6429 Ybug_eB, FALSE, TRUE,
6430 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6433 Ybug_s, FALSE, FALSE,
6434 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6437 Ybug_sB, FALSE, TRUE,
6438 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6441 Ybug_w, FALSE, FALSE,
6442 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6445 Ybug_wB, FALSE, TRUE,
6446 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6449 Ybug_w_n, FALSE, FALSE,
6450 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6453 Ybug_n_e, FALSE, FALSE,
6454 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6457 Ybug_e_s, FALSE, FALSE,
6458 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6461 Ybug_s_w, FALSE, FALSE,
6462 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6465 Ybug_e_n, FALSE, FALSE,
6466 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6469 Ybug_s_e, FALSE, FALSE,
6470 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6473 Ybug_w_s, FALSE, FALSE,
6474 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6477 Ybug_n_w, FALSE, FALSE,
6478 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6481 Ybug_stone, FALSE, FALSE,
6482 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6485 Ybug_spring, FALSE, FALSE,
6486 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6490 Xtank_1_n, TRUE, FALSE,
6491 EL_SPACESHIP_UP, -1, -1
6494 Xtank_1_e, TRUE, FALSE,
6495 EL_SPACESHIP_RIGHT, -1, -1
6498 Xtank_1_s, TRUE, FALSE,
6499 EL_SPACESHIP_DOWN, -1, -1
6502 Xtank_1_w, TRUE, FALSE,
6503 EL_SPACESHIP_LEFT, -1, -1
6506 Xtank_2_n, FALSE, FALSE,
6507 EL_SPACESHIP_UP, -1, -1
6510 Xtank_2_e, FALSE, FALSE,
6511 EL_SPACESHIP_RIGHT, -1, -1
6514 Xtank_2_s, FALSE, FALSE,
6515 EL_SPACESHIP_DOWN, -1, -1
6518 Xtank_2_w, FALSE, FALSE,
6519 EL_SPACESHIP_LEFT, -1, -1
6522 Ytank_n, FALSE, FALSE,
6523 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6526 Ytank_nB, FALSE, TRUE,
6527 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6530 Ytank_e, FALSE, FALSE,
6531 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6534 Ytank_eB, FALSE, TRUE,
6535 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6538 Ytank_s, FALSE, FALSE,
6539 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6542 Ytank_sB, FALSE, TRUE,
6543 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6546 Ytank_w, FALSE, FALSE,
6547 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6550 Ytank_wB, FALSE, TRUE,
6551 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6554 Ytank_w_n, FALSE, FALSE,
6555 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6558 Ytank_n_e, FALSE, FALSE,
6559 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6562 Ytank_e_s, FALSE, FALSE,
6563 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6566 Ytank_s_w, FALSE, FALSE,
6567 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6570 Ytank_e_n, FALSE, FALSE,
6571 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6574 Ytank_s_e, FALSE, FALSE,
6575 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6578 Ytank_w_s, FALSE, FALSE,
6579 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6582 Ytank_n_w, FALSE, FALSE,
6583 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6586 Ytank_stone, FALSE, FALSE,
6587 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6590 Ytank_spring, FALSE, FALSE,
6591 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6595 Xemerald, TRUE, FALSE,
6599 Xemerald_pause, FALSE, FALSE,
6603 Xemerald_fall, FALSE, FALSE,
6607 Xemerald_shine, FALSE, FALSE,
6608 EL_EMERALD, ACTION_TWINKLING, -1
6611 Yemerald_s, FALSE, FALSE,
6612 EL_EMERALD, ACTION_FALLING, -1
6615 Yemerald_sB, FALSE, TRUE,
6616 EL_EMERALD, ACTION_FALLING, -1
6619 Yemerald_e, FALSE, FALSE,
6620 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6623 Yemerald_eB, FALSE, TRUE,
6624 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6627 Yemerald_w, FALSE, FALSE,
6628 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6631 Yemerald_wB, FALSE, TRUE,
6632 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6635 Yemerald_blank, FALSE, FALSE,
6636 EL_EMERALD, ACTION_COLLECTING, -1
6640 Xdiamond, TRUE, FALSE,
6644 Xdiamond_pause, FALSE, FALSE,
6648 Xdiamond_fall, FALSE, FALSE,
6652 Xdiamond_shine, FALSE, FALSE,
6653 EL_DIAMOND, ACTION_TWINKLING, -1
6656 Ydiamond_s, FALSE, FALSE,
6657 EL_DIAMOND, ACTION_FALLING, -1
6660 Ydiamond_sB, FALSE, TRUE,
6661 EL_DIAMOND, ACTION_FALLING, -1
6664 Ydiamond_e, FALSE, FALSE,
6665 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6668 Ydiamond_eB, FALSE, TRUE,
6669 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6672 Ydiamond_w, FALSE, FALSE,
6673 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6676 Ydiamond_wB, FALSE, TRUE,
6677 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6680 Ydiamond_blank, FALSE, FALSE,
6681 EL_DIAMOND, ACTION_COLLECTING, -1
6684 Ydiamond_stone, FALSE, FALSE,
6685 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6689 Xstone, TRUE, FALSE,
6693 Xstone_pause, FALSE, FALSE,
6697 Xstone_fall, FALSE, FALSE,
6701 Ystone_s, FALSE, FALSE,
6702 EL_ROCK, ACTION_FALLING, -1
6705 Ystone_sB, FALSE, TRUE,
6706 EL_ROCK, ACTION_FALLING, -1
6709 Ystone_e, FALSE, FALSE,
6710 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6713 Ystone_eB, FALSE, TRUE,
6714 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6717 Ystone_w, FALSE, FALSE,
6718 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6721 Ystone_wB, FALSE, TRUE,
6722 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6730 Xbomb_pause, FALSE, FALSE,
6734 Xbomb_fall, FALSE, FALSE,
6738 Ybomb_s, FALSE, FALSE,
6739 EL_BOMB, ACTION_FALLING, -1
6742 Ybomb_sB, FALSE, TRUE,
6743 EL_BOMB, ACTION_FALLING, -1
6746 Ybomb_e, FALSE, FALSE,
6747 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6750 Ybomb_eB, FALSE, TRUE,
6751 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6754 Ybomb_w, FALSE, FALSE,
6755 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6758 Ybomb_wB, FALSE, TRUE,
6759 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6762 Ybomb_blank, FALSE, FALSE,
6763 EL_BOMB, ACTION_ACTIVATING, -1
6771 Xnut_pause, FALSE, FALSE,
6775 Xnut_fall, FALSE, FALSE,
6779 Ynut_s, FALSE, FALSE,
6780 EL_NUT, ACTION_FALLING, -1
6783 Ynut_sB, FALSE, TRUE,
6784 EL_NUT, ACTION_FALLING, -1
6787 Ynut_e, FALSE, FALSE,
6788 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6791 Ynut_eB, FALSE, TRUE,
6792 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6795 Ynut_w, FALSE, FALSE,
6796 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6799 Ynut_wB, FALSE, TRUE,
6800 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6803 Ynut_stone, FALSE, FALSE,
6804 EL_NUT, ACTION_BREAKING, -1
6808 Xspring, TRUE, FALSE,
6812 Xspring_pause, FALSE, FALSE,
6816 Xspring_e, TRUE, FALSE,
6817 EL_SPRING_RIGHT, -1, -1
6820 Xspring_w, TRUE, FALSE,
6821 EL_SPRING_LEFT, -1, -1
6824 Xspring_fall, FALSE, FALSE,
6828 Yspring_s, FALSE, FALSE,
6829 EL_SPRING, ACTION_FALLING, -1
6832 Yspring_sB, FALSE, TRUE,
6833 EL_SPRING, ACTION_FALLING, -1
6836 Yspring_e, FALSE, FALSE,
6837 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6840 Yspring_eB, FALSE, TRUE,
6841 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6844 Yspring_w, FALSE, FALSE,
6845 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6848 Yspring_wB, FALSE, TRUE,
6849 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6852 Yspring_alien_e, FALSE, FALSE,
6853 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6856 Yspring_alien_eB, FALSE, TRUE,
6857 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6860 Yspring_alien_w, FALSE, FALSE,
6861 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6864 Yspring_alien_wB, FALSE, TRUE,
6865 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6869 Xpush_emerald_e, FALSE, FALSE,
6870 EL_EMERALD, -1, MV_BIT_RIGHT
6873 Xpush_emerald_w, FALSE, FALSE,
6874 EL_EMERALD, -1, MV_BIT_LEFT
6877 Xpush_diamond_e, FALSE, FALSE,
6878 EL_DIAMOND, -1, MV_BIT_RIGHT
6881 Xpush_diamond_w, FALSE, FALSE,
6882 EL_DIAMOND, -1, MV_BIT_LEFT
6885 Xpush_stone_e, FALSE, FALSE,
6886 EL_ROCK, -1, MV_BIT_RIGHT
6889 Xpush_stone_w, FALSE, FALSE,
6890 EL_ROCK, -1, MV_BIT_LEFT
6893 Xpush_bomb_e, FALSE, FALSE,
6894 EL_BOMB, -1, MV_BIT_RIGHT
6897 Xpush_bomb_w, FALSE, FALSE,
6898 EL_BOMB, -1, MV_BIT_LEFT
6901 Xpush_nut_e, FALSE, FALSE,
6902 EL_NUT, -1, MV_BIT_RIGHT
6905 Xpush_nut_w, FALSE, FALSE,
6906 EL_NUT, -1, MV_BIT_LEFT
6909 Xpush_spring_e, FALSE, FALSE,
6910 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6913 Xpush_spring_w, FALSE, FALSE,
6914 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6918 Xdynamite, TRUE, FALSE,
6919 EL_EM_DYNAMITE, -1, -1
6922 Ydynamite_blank, FALSE, FALSE,
6923 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6926 Xdynamite_1, TRUE, FALSE,
6927 EL_EM_DYNAMITE_ACTIVE, -1, -1
6930 Xdynamite_2, FALSE, FALSE,
6931 EL_EM_DYNAMITE_ACTIVE, -1, -1
6934 Xdynamite_3, FALSE, FALSE,
6935 EL_EM_DYNAMITE_ACTIVE, -1, -1
6938 Xdynamite_4, FALSE, FALSE,
6939 EL_EM_DYNAMITE_ACTIVE, -1, -1
6943 Xkey_1, TRUE, FALSE,
6947 Xkey_2, TRUE, FALSE,
6951 Xkey_3, TRUE, FALSE,
6955 Xkey_4, TRUE, FALSE,
6959 Xkey_5, TRUE, FALSE,
6960 EL_EMC_KEY_5, -1, -1
6963 Xkey_6, TRUE, FALSE,
6964 EL_EMC_KEY_6, -1, -1
6967 Xkey_7, TRUE, FALSE,
6968 EL_EMC_KEY_7, -1, -1
6971 Xkey_8, TRUE, FALSE,
6972 EL_EMC_KEY_8, -1, -1
6976 Xdoor_1, TRUE, FALSE,
6977 EL_EM_GATE_1, -1, -1
6980 Xdoor_2, TRUE, FALSE,
6981 EL_EM_GATE_2, -1, -1
6984 Xdoor_3, TRUE, FALSE,
6985 EL_EM_GATE_3, -1, -1
6988 Xdoor_4, TRUE, FALSE,
6989 EL_EM_GATE_4, -1, -1
6992 Xdoor_5, TRUE, FALSE,
6993 EL_EMC_GATE_5, -1, -1
6996 Xdoor_6, TRUE, FALSE,
6997 EL_EMC_GATE_6, -1, -1
7000 Xdoor_7, TRUE, FALSE,
7001 EL_EMC_GATE_7, -1, -1
7004 Xdoor_8, TRUE, FALSE,
7005 EL_EMC_GATE_8, -1, -1
7009 Xfake_door_1, TRUE, FALSE,
7010 EL_EM_GATE_1_GRAY, -1, -1
7013 Xfake_door_2, TRUE, FALSE,
7014 EL_EM_GATE_2_GRAY, -1, -1
7017 Xfake_door_3, TRUE, FALSE,
7018 EL_EM_GATE_3_GRAY, -1, -1
7021 Xfake_door_4, TRUE, FALSE,
7022 EL_EM_GATE_4_GRAY, -1, -1
7025 Xfake_door_5, TRUE, FALSE,
7026 EL_EMC_GATE_5_GRAY, -1, -1
7029 Xfake_door_6, TRUE, FALSE,
7030 EL_EMC_GATE_6_GRAY, -1, -1
7033 Xfake_door_7, TRUE, FALSE,
7034 EL_EMC_GATE_7_GRAY, -1, -1
7037 Xfake_door_8, TRUE, FALSE,
7038 EL_EMC_GATE_8_GRAY, -1, -1
7042 Xballoon, TRUE, FALSE,
7046 Yballoon_n, FALSE, FALSE,
7047 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7050 Yballoon_nB, FALSE, TRUE,
7051 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7054 Yballoon_e, FALSE, FALSE,
7055 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7058 Yballoon_eB, FALSE, TRUE,
7059 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7062 Yballoon_s, FALSE, FALSE,
7063 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7066 Yballoon_sB, FALSE, TRUE,
7067 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7070 Yballoon_w, FALSE, FALSE,
7071 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7074 Yballoon_wB, FALSE, TRUE,
7075 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7079 Xball_1, TRUE, FALSE,
7080 EL_EMC_MAGIC_BALL, -1, -1
7083 Yball_1, FALSE, FALSE,
7084 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7087 Xball_2, FALSE, FALSE,
7088 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7091 Yball_2, FALSE, FALSE,
7092 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7095 Yball_blank, FALSE, FALSE,
7096 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7100 Xamoeba_1, TRUE, FALSE,
7101 EL_AMOEBA_DRY, ACTION_OTHER, -1
7104 Xamoeba_2, FALSE, FALSE,
7105 EL_AMOEBA_DRY, ACTION_OTHER, -1
7108 Xamoeba_3, FALSE, FALSE,
7109 EL_AMOEBA_DRY, ACTION_OTHER, -1
7112 Xamoeba_4, FALSE, FALSE,
7113 EL_AMOEBA_DRY, ACTION_OTHER, -1
7116 Xamoeba_5, TRUE, FALSE,
7117 EL_AMOEBA_WET, ACTION_OTHER, -1
7120 Xamoeba_6, FALSE, FALSE,
7121 EL_AMOEBA_WET, ACTION_OTHER, -1
7124 Xamoeba_7, FALSE, FALSE,
7125 EL_AMOEBA_WET, ACTION_OTHER, -1
7128 Xamoeba_8, FALSE, FALSE,
7129 EL_AMOEBA_WET, ACTION_OTHER, -1
7134 EL_AMOEBA_DROP, ACTION_GROWING, -1
7137 Xdrip_fall, FALSE, FALSE,
7138 EL_AMOEBA_DROP, -1, -1
7141 Xdrip_stretch, FALSE, FALSE,
7142 EL_AMOEBA_DROP, ACTION_FALLING, -1
7145 Xdrip_stretchB, FALSE, TRUE,
7146 EL_AMOEBA_DROP, ACTION_FALLING, -1
7149 Ydrip_1_s, FALSE, FALSE,
7150 EL_AMOEBA_DROP, ACTION_FALLING, -1
7153 Ydrip_1_sB, FALSE, TRUE,
7154 EL_AMOEBA_DROP, ACTION_FALLING, -1
7157 Ydrip_2_s, FALSE, FALSE,
7158 EL_AMOEBA_DROP, ACTION_FALLING, -1
7161 Ydrip_2_sB, FALSE, TRUE,
7162 EL_AMOEBA_DROP, ACTION_FALLING, -1
7166 Xwonderwall, TRUE, FALSE,
7167 EL_MAGIC_WALL, -1, -1
7170 Ywonderwall, FALSE, FALSE,
7171 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7175 Xwheel, TRUE, FALSE,
7176 EL_ROBOT_WHEEL, -1, -1
7179 Ywheel, FALSE, FALSE,
7180 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7184 Xswitch, TRUE, FALSE,
7185 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7188 Yswitch, FALSE, FALSE,
7189 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7193 Xbumper, TRUE, FALSE,
7194 EL_EMC_SPRING_BUMPER, -1, -1
7197 Ybumper, FALSE, FALSE,
7198 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7202 Xacid_nw, TRUE, FALSE,
7203 EL_ACID_POOL_TOPLEFT, -1, -1
7206 Xacid_ne, TRUE, FALSE,
7207 EL_ACID_POOL_TOPRIGHT, -1, -1
7210 Xacid_sw, TRUE, FALSE,
7211 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7214 Xacid_s, TRUE, FALSE,
7215 EL_ACID_POOL_BOTTOM, -1, -1
7218 Xacid_se, TRUE, FALSE,
7219 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7223 Xfake_blank, TRUE, FALSE,
7224 EL_INVISIBLE_WALL, -1, -1
7227 Yfake_blank, FALSE, FALSE,
7228 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7232 Xfake_grass, TRUE, FALSE,
7233 EL_EMC_FAKE_GRASS, -1, -1
7236 Yfake_grass, FALSE, FALSE,
7237 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7241 Xfake_amoeba, TRUE, FALSE,
7242 EL_EMC_DRIPPER, -1, -1
7245 Yfake_amoeba, FALSE, FALSE,
7246 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7250 Xlenses, TRUE, FALSE,
7251 EL_EMC_LENSES, -1, -1
7255 Xmagnify, TRUE, FALSE,
7256 EL_EMC_MAGNIFIER, -1, -1
7261 EL_QUICKSAND_EMPTY, -1, -1
7264 Xsand_stone, TRUE, FALSE,
7265 EL_QUICKSAND_FULL, -1, -1
7268 Xsand_stonein_1, FALSE, TRUE,
7269 EL_ROCK, ACTION_FILLING, -1
7272 Xsand_stonein_2, FALSE, TRUE,
7273 EL_ROCK, ACTION_FILLING, -1
7276 Xsand_stonein_3, FALSE, TRUE,
7277 EL_ROCK, ACTION_FILLING, -1
7280 Xsand_stonein_4, FALSE, TRUE,
7281 EL_ROCK, ACTION_FILLING, -1
7284 Xsand_sandstone_1, FALSE, FALSE,
7285 EL_QUICKSAND_FILLING, -1, -1
7288 Xsand_sandstone_2, FALSE, FALSE,
7289 EL_QUICKSAND_FILLING, -1, -1
7292 Xsand_sandstone_3, FALSE, FALSE,
7293 EL_QUICKSAND_FILLING, -1, -1
7296 Xsand_sandstone_4, FALSE, FALSE,
7297 EL_QUICKSAND_FILLING, -1, -1
7300 Xsand_stonesand_1, FALSE, FALSE,
7301 EL_QUICKSAND_EMPTYING, -1, -1
7304 Xsand_stonesand_2, FALSE, FALSE,
7305 EL_QUICKSAND_EMPTYING, -1, -1
7308 Xsand_stonesand_3, FALSE, FALSE,
7309 EL_QUICKSAND_EMPTYING, -1, -1
7312 Xsand_stonesand_4, FALSE, FALSE,
7313 EL_QUICKSAND_EMPTYING, -1, -1
7316 Xsand_stoneout_1, FALSE, FALSE,
7317 EL_ROCK, ACTION_EMPTYING, -1
7320 Xsand_stoneout_2, FALSE, FALSE,
7321 EL_ROCK, ACTION_EMPTYING, -1
7324 Xsand_stonesand_quickout_1, FALSE, FALSE,
7325 EL_QUICKSAND_EMPTYING, -1, -1
7328 Xsand_stonesand_quickout_2, FALSE, FALSE,
7329 EL_QUICKSAND_EMPTYING, -1, -1
7333 Xslide_ns, TRUE, FALSE,
7334 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7337 Yslide_ns_blank, FALSE, FALSE,
7338 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7341 Xslide_ew, TRUE, FALSE,
7342 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7345 Yslide_ew_blank, FALSE, FALSE,
7346 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7350 Xwind_n, TRUE, FALSE,
7351 EL_BALLOON_SWITCH_UP, -1, -1
7354 Xwind_e, TRUE, FALSE,
7355 EL_BALLOON_SWITCH_RIGHT, -1, -1
7358 Xwind_s, TRUE, FALSE,
7359 EL_BALLOON_SWITCH_DOWN, -1, -1
7362 Xwind_w, TRUE, FALSE,
7363 EL_BALLOON_SWITCH_LEFT, -1, -1
7366 Xwind_any, TRUE, FALSE,
7367 EL_BALLOON_SWITCH_ANY, -1, -1
7370 Xwind_stop, TRUE, FALSE,
7371 EL_BALLOON_SWITCH_NONE, -1, -1
7376 EL_EM_EXIT_CLOSED, -1, -1
7379 Xexit_1, TRUE, FALSE,
7380 EL_EM_EXIT_OPEN, -1, -1
7383 Xexit_2, FALSE, FALSE,
7384 EL_EM_EXIT_OPEN, -1, -1
7387 Xexit_3, FALSE, FALSE,
7388 EL_EM_EXIT_OPEN, -1, -1
7392 Xpause, FALSE, FALSE,
7397 Xwall_1, TRUE, FALSE,
7401 Xwall_2, TRUE, FALSE,
7402 EL_EMC_WALL_14, -1, -1
7405 Xwall_3, TRUE, FALSE,
7406 EL_EMC_WALL_15, -1, -1
7409 Xwall_4, TRUE, FALSE,
7410 EL_EMC_WALL_16, -1, -1
7414 Xroundwall_1, TRUE, FALSE,
7415 EL_WALL_SLIPPERY, -1, -1
7418 Xroundwall_2, TRUE, FALSE,
7419 EL_EMC_WALL_SLIPPERY_2, -1, -1
7422 Xroundwall_3, TRUE, FALSE,
7423 EL_EMC_WALL_SLIPPERY_3, -1, -1
7426 Xroundwall_4, TRUE, FALSE,
7427 EL_EMC_WALL_SLIPPERY_4, -1, -1
7431 Xsteel_1, TRUE, FALSE,
7432 EL_STEELWALL, -1, -1
7435 Xsteel_2, TRUE, FALSE,
7436 EL_EMC_STEELWALL_2, -1, -1
7439 Xsteel_3, TRUE, FALSE,
7440 EL_EMC_STEELWALL_3, -1, -1
7443 Xsteel_4, TRUE, FALSE,
7444 EL_EMC_STEELWALL_4, -1, -1
7448 Xdecor_1, TRUE, FALSE,
7449 EL_EMC_WALL_8, -1, -1
7452 Xdecor_2, TRUE, FALSE,
7453 EL_EMC_WALL_6, -1, -1
7456 Xdecor_3, TRUE, FALSE,
7457 EL_EMC_WALL_4, -1, -1
7460 Xdecor_4, TRUE, FALSE,
7461 EL_EMC_WALL_7, -1, -1
7464 Xdecor_5, TRUE, FALSE,
7465 EL_EMC_WALL_5, -1, -1
7468 Xdecor_6, TRUE, FALSE,
7469 EL_EMC_WALL_9, -1, -1
7472 Xdecor_7, TRUE, FALSE,
7473 EL_EMC_WALL_10, -1, -1
7476 Xdecor_8, TRUE, FALSE,
7477 EL_EMC_WALL_1, -1, -1
7480 Xdecor_9, TRUE, FALSE,
7481 EL_EMC_WALL_2, -1, -1
7484 Xdecor_10, TRUE, FALSE,
7485 EL_EMC_WALL_3, -1, -1
7488 Xdecor_11, TRUE, FALSE,
7489 EL_EMC_WALL_11, -1, -1
7492 Xdecor_12, TRUE, FALSE,
7493 EL_EMC_WALL_12, -1, -1
7497 Xalpha_0, TRUE, FALSE,
7498 EL_CHAR('0'), -1, -1
7501 Xalpha_1, TRUE, FALSE,
7502 EL_CHAR('1'), -1, -1
7505 Xalpha_2, TRUE, FALSE,
7506 EL_CHAR('2'), -1, -1
7509 Xalpha_3, TRUE, FALSE,
7510 EL_CHAR('3'), -1, -1
7513 Xalpha_4, TRUE, FALSE,
7514 EL_CHAR('4'), -1, -1
7517 Xalpha_5, TRUE, FALSE,
7518 EL_CHAR('5'), -1, -1
7521 Xalpha_6, TRUE, FALSE,
7522 EL_CHAR('6'), -1, -1
7525 Xalpha_7, TRUE, FALSE,
7526 EL_CHAR('7'), -1, -1
7529 Xalpha_8, TRUE, FALSE,
7530 EL_CHAR('8'), -1, -1
7533 Xalpha_9, TRUE, FALSE,
7534 EL_CHAR('9'), -1, -1
7537 Xalpha_excla, TRUE, FALSE,
7538 EL_CHAR('!'), -1, -1
7541 Xalpha_apost, TRUE, FALSE,
7542 EL_CHAR('\''), -1, -1
7545 Xalpha_comma, TRUE, FALSE,
7546 EL_CHAR(','), -1, -1
7549 Xalpha_minus, TRUE, FALSE,
7550 EL_CHAR('-'), -1, -1
7553 Xalpha_perio, TRUE, FALSE,
7554 EL_CHAR('.'), -1, -1
7557 Xalpha_colon, TRUE, FALSE,
7558 EL_CHAR(':'), -1, -1
7561 Xalpha_quest, TRUE, FALSE,
7562 EL_CHAR('?'), -1, -1
7565 Xalpha_a, TRUE, FALSE,
7566 EL_CHAR('A'), -1, -1
7569 Xalpha_b, TRUE, FALSE,
7570 EL_CHAR('B'), -1, -1
7573 Xalpha_c, TRUE, FALSE,
7574 EL_CHAR('C'), -1, -1
7577 Xalpha_d, TRUE, FALSE,
7578 EL_CHAR('D'), -1, -1
7581 Xalpha_e, TRUE, FALSE,
7582 EL_CHAR('E'), -1, -1
7585 Xalpha_f, TRUE, FALSE,
7586 EL_CHAR('F'), -1, -1
7589 Xalpha_g, TRUE, FALSE,
7590 EL_CHAR('G'), -1, -1
7593 Xalpha_h, TRUE, FALSE,
7594 EL_CHAR('H'), -1, -1
7597 Xalpha_i, TRUE, FALSE,
7598 EL_CHAR('I'), -1, -1
7601 Xalpha_j, TRUE, FALSE,
7602 EL_CHAR('J'), -1, -1
7605 Xalpha_k, TRUE, FALSE,
7606 EL_CHAR('K'), -1, -1
7609 Xalpha_l, TRUE, FALSE,
7610 EL_CHAR('L'), -1, -1
7613 Xalpha_m, TRUE, FALSE,
7614 EL_CHAR('M'), -1, -1
7617 Xalpha_n, TRUE, FALSE,
7618 EL_CHAR('N'), -1, -1
7621 Xalpha_o, TRUE, FALSE,
7622 EL_CHAR('O'), -1, -1
7625 Xalpha_p, TRUE, FALSE,
7626 EL_CHAR('P'), -1, -1
7629 Xalpha_q, TRUE, FALSE,
7630 EL_CHAR('Q'), -1, -1
7633 Xalpha_r, TRUE, FALSE,
7634 EL_CHAR('R'), -1, -1
7637 Xalpha_s, TRUE, FALSE,
7638 EL_CHAR('S'), -1, -1
7641 Xalpha_t, TRUE, FALSE,
7642 EL_CHAR('T'), -1, -1
7645 Xalpha_u, TRUE, FALSE,
7646 EL_CHAR('U'), -1, -1
7649 Xalpha_v, TRUE, FALSE,
7650 EL_CHAR('V'), -1, -1
7653 Xalpha_w, TRUE, FALSE,
7654 EL_CHAR('W'), -1, -1
7657 Xalpha_x, TRUE, FALSE,
7658 EL_CHAR('X'), -1, -1
7661 Xalpha_y, TRUE, FALSE,
7662 EL_CHAR('Y'), -1, -1
7665 Xalpha_z, TRUE, FALSE,
7666 EL_CHAR('Z'), -1, -1
7669 Xalpha_arrow_e, TRUE, FALSE,
7670 EL_CHAR('>'), -1, -1
7673 Xalpha_arrow_w, TRUE, FALSE,
7674 EL_CHAR('<'), -1, -1
7677 Xalpha_copyr, TRUE, FALSE,
7678 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7682 Ykey_1_blank, FALSE, FALSE,
7683 EL_EM_KEY_1, ACTION_COLLECTING, -1
7686 Ykey_2_blank, FALSE, FALSE,
7687 EL_EM_KEY_2, ACTION_COLLECTING, -1
7690 Ykey_3_blank, FALSE, FALSE,
7691 EL_EM_KEY_3, ACTION_COLLECTING, -1
7694 Ykey_4_blank, FALSE, FALSE,
7695 EL_EM_KEY_4, ACTION_COLLECTING, -1
7698 Ykey_5_blank, FALSE, FALSE,
7699 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7702 Ykey_6_blank, FALSE, FALSE,
7703 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7706 Ykey_7_blank, FALSE, FALSE,
7707 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7710 Ykey_8_blank, FALSE, FALSE,
7711 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7714 Ylenses_blank, FALSE, FALSE,
7715 EL_EMC_LENSES, ACTION_COLLECTING, -1
7718 Ymagnify_blank, FALSE, FALSE,
7719 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7722 Ygrass_blank, FALSE, FALSE,
7723 EL_EMC_GRASS, ACTION_SNAPPING, -1
7726 Ydirt_blank, FALSE, FALSE,
7727 EL_SAND, ACTION_SNAPPING, -1
7736 static struct Mapping_EM_to_RND_player
7745 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7749 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7753 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7757 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7761 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7765 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7769 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7773 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7777 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7781 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7785 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7789 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7793 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7797 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7801 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7805 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7809 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7813 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7817 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7821 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7825 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7829 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7833 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7837 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7841 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7845 EL_PLAYER_1, ACTION_DEFAULT, -1,
7849 EL_PLAYER_2, ACTION_DEFAULT, -1,
7853 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7857 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7861 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7865 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7869 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7873 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7877 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7881 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7885 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7889 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7893 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7897 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7901 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7905 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7909 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7913 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7917 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7921 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7925 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7929 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7933 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7937 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7941 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7945 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7949 EL_PLAYER_3, ACTION_DEFAULT, -1,
7953 EL_PLAYER_4, ACTION_DEFAULT, -1,
7962 int map_element_RND_to_EM_cave(int element_rnd)
7964 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7965 static boolean mapping_initialized = FALSE;
7967 if (!mapping_initialized)
7971 // return "Xalpha_quest" for all undefined elements in mapping array
7972 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7973 mapping_RND_to_EM[i] = Xalpha_quest;
7975 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7976 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7977 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7978 em_object_mapping_list[i].element_em;
7980 mapping_initialized = TRUE;
7983 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7985 Warn("invalid RND level element %d", element_rnd);
7990 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7993 int map_element_EM_to_RND_cave(int element_em_cave)
7995 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7996 static boolean mapping_initialized = FALSE;
7998 if (!mapping_initialized)
8002 // return "EL_UNKNOWN" for all undefined elements in mapping array
8003 for (i = 0; i < GAME_TILE_MAX; i++)
8004 mapping_EM_to_RND[i] = EL_UNKNOWN;
8006 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8007 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8008 em_object_mapping_list[i].element_rnd;
8010 mapping_initialized = TRUE;
8013 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8015 Warn("invalid EM cave element %d", element_em_cave);
8020 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8023 int map_element_EM_to_RND_game(int element_em_game)
8025 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8026 static boolean mapping_initialized = FALSE;
8028 if (!mapping_initialized)
8032 // return "EL_UNKNOWN" for all undefined elements in mapping array
8033 for (i = 0; i < GAME_TILE_MAX; i++)
8034 mapping_EM_to_RND[i] = EL_UNKNOWN;
8036 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8037 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8038 em_object_mapping_list[i].element_rnd;
8040 mapping_initialized = TRUE;
8043 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8045 Warn("invalid EM game element %d", element_em_game);
8050 return mapping_EM_to_RND[element_em_game];
8053 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8055 struct LevelInfo_EM *level_em = level->native_em_level;
8056 struct CAVE *cav = level_em->cav;
8059 for (i = 0; i < GAME_TILE_MAX; i++)
8060 cav->android_array[i] = Cblank;
8062 for (i = 0; i < level->num_android_clone_elements; i++)
8064 int element_rnd = level->android_clone_element[i];
8065 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8067 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8068 if (em_object_mapping_list[j].element_rnd == element_rnd)
8069 cav->android_array[em_object_mapping_list[j].element_em] =
8074 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8076 struct LevelInfo_EM *level_em = level->native_em_level;
8077 struct CAVE *cav = level_em->cav;
8080 level->num_android_clone_elements = 0;
8082 for (i = 0; i < GAME_TILE_MAX; i++)
8084 int element_em_cave = cav->android_array[i];
8086 boolean element_found = FALSE;
8088 if (element_em_cave == Cblank)
8091 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8093 for (j = 0; j < level->num_android_clone_elements; j++)
8094 if (level->android_clone_element[j] == element_rnd)
8095 element_found = TRUE;
8099 level->android_clone_element[level->num_android_clone_elements++] =
8102 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8107 if (level->num_android_clone_elements == 0)
8109 level->num_android_clone_elements = 1;
8110 level->android_clone_element[0] = EL_EMPTY;
8114 int map_direction_RND_to_EM(int direction)
8116 return (direction == MV_UP ? 0 :
8117 direction == MV_RIGHT ? 1 :
8118 direction == MV_DOWN ? 2 :
8119 direction == MV_LEFT ? 3 :
8123 int map_direction_EM_to_RND(int direction)
8125 return (direction == 0 ? MV_UP :
8126 direction == 1 ? MV_RIGHT :
8127 direction == 2 ? MV_DOWN :
8128 direction == 3 ? MV_LEFT :
8132 int map_element_RND_to_SP(int element_rnd)
8134 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8136 if (element_rnd >= EL_SP_START &&
8137 element_rnd <= EL_SP_END)
8138 element_sp = element_rnd - EL_SP_START;
8139 else if (element_rnd == EL_EMPTY_SPACE)
8141 else if (element_rnd == EL_INVISIBLE_WALL)
8147 int map_element_SP_to_RND(int element_sp)
8149 int element_rnd = EL_UNKNOWN;
8151 if (element_sp >= 0x00 &&
8153 element_rnd = EL_SP_START + element_sp;
8154 else if (element_sp == 0x28)
8155 element_rnd = EL_INVISIBLE_WALL;
8160 int map_action_SP_to_RND(int action_sp)
8164 case actActive: return ACTION_ACTIVE;
8165 case actImpact: return ACTION_IMPACT;
8166 case actExploding: return ACTION_EXPLODING;
8167 case actDigging: return ACTION_DIGGING;
8168 case actSnapping: return ACTION_SNAPPING;
8169 case actCollecting: return ACTION_COLLECTING;
8170 case actPassing: return ACTION_PASSING;
8171 case actPushing: return ACTION_PUSHING;
8172 case actDropping: return ACTION_DROPPING;
8174 default: return ACTION_DEFAULT;
8178 int map_element_RND_to_MM(int element_rnd)
8180 return (element_rnd >= EL_MM_START_1 &&
8181 element_rnd <= EL_MM_END_1 ?
8182 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8184 element_rnd >= EL_MM_START_2 &&
8185 element_rnd <= EL_MM_END_2 ?
8186 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8188 element_rnd >= EL_MM_START_3 &&
8189 element_rnd <= EL_MM_END_3 ?
8190 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8192 element_rnd >= EL_CHAR_START &&
8193 element_rnd <= EL_CHAR_END ?
8194 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8196 element_rnd >= EL_MM_RUNTIME_START &&
8197 element_rnd <= EL_MM_RUNTIME_END ?
8198 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8200 EL_MM_EMPTY_NATIVE);
8203 int map_element_MM_to_RND(int element_mm)
8205 return (element_mm == EL_MM_EMPTY_NATIVE ||
8206 element_mm == EL_DF_EMPTY_NATIVE ?
8209 element_mm >= EL_MM_START_1_NATIVE &&
8210 element_mm <= EL_MM_END_1_NATIVE ?
8211 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8213 element_mm >= EL_MM_START_2_NATIVE &&
8214 element_mm <= EL_MM_END_2_NATIVE ?
8215 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8217 element_mm >= EL_MM_START_3_NATIVE &&
8218 element_mm <= EL_MM_END_3_NATIVE ?
8219 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8221 element_mm >= EL_MM_CHAR_START_NATIVE &&
8222 element_mm <= EL_MM_CHAR_END_NATIVE ?
8223 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8225 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8226 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8227 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8232 int map_action_MM_to_RND(int action_mm)
8234 // all MM actions are defined to exactly match their RND counterparts
8238 int map_sound_MM_to_RND(int sound_mm)
8242 case SND_MM_GAME_LEVELTIME_CHARGING:
8243 return SND_GAME_LEVELTIME_CHARGING;
8245 case SND_MM_GAME_HEALTH_CHARGING:
8246 return SND_GAME_HEALTH_CHARGING;
8249 return SND_UNDEFINED;
8253 int map_mm_wall_element(int element)
8255 return (element >= EL_MM_STEEL_WALL_START &&
8256 element <= EL_MM_STEEL_WALL_END ?
8259 element >= EL_MM_WOODEN_WALL_START &&
8260 element <= EL_MM_WOODEN_WALL_END ?
8263 element >= EL_MM_ICE_WALL_START &&
8264 element <= EL_MM_ICE_WALL_END ?
8267 element >= EL_MM_AMOEBA_WALL_START &&
8268 element <= EL_MM_AMOEBA_WALL_END ?
8271 element >= EL_DF_STEEL_WALL_START &&
8272 element <= EL_DF_STEEL_WALL_END ?
8275 element >= EL_DF_WOODEN_WALL_START &&
8276 element <= EL_DF_WOODEN_WALL_END ?
8282 int map_mm_wall_element_editor(int element)
8286 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8287 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8288 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8289 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8290 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8291 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8293 default: return element;
8297 int get_next_element(int element)
8301 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8302 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8303 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8304 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8305 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8306 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8307 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8308 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8309 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8310 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8311 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8313 default: return element;
8317 int el2img_mm(int element_mm)
8319 return el2img(map_element_MM_to_RND(element_mm));
8322 int el_act2img_mm(int element_mm, int action)
8324 return el_act2img(map_element_MM_to_RND(element_mm), action);
8327 int el_act_dir2img(int element, int action, int direction)
8329 element = GFX_ELEMENT(element);
8330 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8332 // direction_graphic[][] == graphic[] for undefined direction graphics
8333 return element_info[element].direction_graphic[action][direction];
8336 static int el_act_dir2crm(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_crumbled[action][direction];
8345 int el_act2img(int element, int action)
8347 element = GFX_ELEMENT(element);
8349 return element_info[element].graphic[action];
8352 int el_act2crm(int element, int action)
8354 element = GFX_ELEMENT(element);
8356 return element_info[element].crumbled[action];
8359 int el_dir2img(int element, int direction)
8361 element = GFX_ELEMENT(element);
8363 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8366 int el2baseimg(int element)
8368 return element_info[element].graphic[ACTION_DEFAULT];
8371 int el2img(int element)
8373 element = GFX_ELEMENT(element);
8375 return element_info[element].graphic[ACTION_DEFAULT];
8378 int el2edimg(int element)
8380 element = GFX_ELEMENT(element);
8382 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8385 int el2preimg(int element)
8387 element = GFX_ELEMENT(element);
8389 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8392 int el2panelimg(int element)
8394 element = GFX_ELEMENT(element);
8396 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8399 int font2baseimg(int font_nr)
8401 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8404 int getBeltNrFromBeltElement(int element)
8406 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8407 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8408 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8411 int getBeltNrFromBeltActiveElement(int element)
8413 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8414 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8415 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8418 int getBeltNrFromBeltSwitchElement(int element)
8420 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8421 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8422 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8425 int getBeltDirNrFromBeltElement(int element)
8427 static int belt_base_element[4] =
8429 EL_CONVEYOR_BELT_1_LEFT,
8430 EL_CONVEYOR_BELT_2_LEFT,
8431 EL_CONVEYOR_BELT_3_LEFT,
8432 EL_CONVEYOR_BELT_4_LEFT
8435 int belt_nr = getBeltNrFromBeltElement(element);
8436 int belt_dir_nr = element - belt_base_element[belt_nr];
8438 return (belt_dir_nr % 3);
8441 int getBeltDirNrFromBeltSwitchElement(int element)
8443 static int belt_base_element[4] =
8445 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8446 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8447 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8448 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8451 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8452 int belt_dir_nr = element - belt_base_element[belt_nr];
8454 return (belt_dir_nr % 3);
8457 int getBeltDirFromBeltElement(int element)
8459 static int belt_move_dir[3] =
8466 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8468 return belt_move_dir[belt_dir_nr];
8471 int getBeltDirFromBeltSwitchElement(int element)
8473 static int belt_move_dir[3] =
8480 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8482 return belt_move_dir[belt_dir_nr];
8485 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8487 static int belt_base_element[4] =
8489 EL_CONVEYOR_BELT_1_LEFT,
8490 EL_CONVEYOR_BELT_2_LEFT,
8491 EL_CONVEYOR_BELT_3_LEFT,
8492 EL_CONVEYOR_BELT_4_LEFT
8495 return belt_base_element[belt_nr] + belt_dir_nr;
8498 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8500 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8502 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8505 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8507 static int belt_base_element[4] =
8509 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8510 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8511 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8512 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8515 return belt_base_element[belt_nr] + belt_dir_nr;
8518 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8520 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8522 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8525 boolean swapTiles_EM(boolean is_pre_emc_cave)
8527 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8530 boolean getTeamMode_EM(void)
8532 return game.team_mode || network_playing;
8535 boolean isActivePlayer_EM(int player_nr)
8537 return stored_player[player_nr].active;
8540 unsigned int InitRND(int seed)
8542 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8543 return InitEngineRandom_EM(seed);
8544 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8545 return InitEngineRandom_SP(seed);
8546 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8547 return InitEngineRandom_MM(seed);
8549 return InitEngineRandom_RND(seed);
8552 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
8553 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
8555 static int get_effective_element_EM(int tile, int frame_em)
8557 int element = em_object_mapping[tile].element_rnd;
8558 int action = em_object_mapping[tile].action;
8559 boolean is_backside = em_object_mapping[tile].is_backside;
8560 boolean action_removing = (action == ACTION_DIGGING ||
8561 action == ACTION_SNAPPING ||
8562 action == ACTION_COLLECTING);
8570 return (frame_em > 5 ? EL_EMPTY : element);
8576 else // frame_em == 7
8587 case Ydiamond_stone:
8591 case Xdrip_stretchB:
8607 case Ymagnify_blank:
8610 case Xsand_stonein_1:
8611 case Xsand_stonein_2:
8612 case Xsand_stonein_3:
8613 case Xsand_stonein_4:
8617 return (is_backside || action_removing ? EL_EMPTY : element);
8622 static boolean check_linear_animation_EM(int tile)
8626 case Xsand_stonesand_1:
8627 case Xsand_stonesand_quickout_1:
8628 case Xsand_sandstone_1:
8629 case Xsand_stonein_1:
8630 case Xsand_stoneout_1:
8658 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8659 boolean has_crumbled_graphics,
8660 int crumbled, int sync_frame)
8662 // if element can be crumbled, but certain action graphics are just empty
8663 // space (like instantly snapping sand to empty space in 1 frame), do not
8664 // treat these empty space graphics as crumbled graphics in EMC engine
8665 if (crumbled == IMG_EMPTY_SPACE)
8666 has_crumbled_graphics = FALSE;
8668 if (has_crumbled_graphics)
8670 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8671 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8672 g_crumbled->anim_delay,
8673 g_crumbled->anim_mode,
8674 g_crumbled->anim_start_frame,
8677 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8678 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8680 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8681 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8683 g_em->has_crumbled_graphics = TRUE;
8687 g_em->crumbled_bitmap = NULL;
8688 g_em->crumbled_src_x = 0;
8689 g_em->crumbled_src_y = 0;
8690 g_em->crumbled_border_size = 0;
8691 g_em->crumbled_tile_size = 0;
8693 g_em->has_crumbled_graphics = FALSE;
8698 void ResetGfxAnimation_EM(int x, int y, int tile)
8704 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8705 int tile, int frame_em, int x, int y)
8707 int action = em_object_mapping[tile].action;
8708 int direction = em_object_mapping[tile].direction;
8709 int effective_element = get_effective_element_EM(tile, frame_em);
8710 int graphic = (direction == MV_NONE ?
8711 el_act2img(effective_element, action) :
8712 el_act_dir2img(effective_element, action, direction));
8713 struct GraphicInfo *g = &graphic_info[graphic];
8715 boolean action_removing = (action == ACTION_DIGGING ||
8716 action == ACTION_SNAPPING ||
8717 action == ACTION_COLLECTING);
8718 boolean action_moving = (action == ACTION_FALLING ||
8719 action == ACTION_MOVING ||
8720 action == ACTION_PUSHING ||
8721 action == ACTION_EATING ||
8722 action == ACTION_FILLING ||
8723 action == ACTION_EMPTYING);
8724 boolean action_falling = (action == ACTION_FALLING ||
8725 action == ACTION_FILLING ||
8726 action == ACTION_EMPTYING);
8728 // special case: graphic uses "2nd movement tile" and has defined
8729 // 7 frames for movement animation (or less) => use default graphic
8730 // for last (8th) frame which ends the movement animation
8731 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8733 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8734 graphic = (direction == MV_NONE ?
8735 el_act2img(effective_element, action) :
8736 el_act_dir2img(effective_element, action, direction));
8738 g = &graphic_info[graphic];
8741 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8745 else if (action_moving)
8747 boolean is_backside = em_object_mapping[tile].is_backside;
8751 int direction = em_object_mapping[tile].direction;
8752 int move_dir = (action_falling ? MV_DOWN : direction);
8757 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8758 if (g->double_movement && frame_em == 0)
8762 if (move_dir == MV_LEFT)
8763 GfxFrame[x - 1][y] = GfxFrame[x][y];
8764 else if (move_dir == MV_RIGHT)
8765 GfxFrame[x + 1][y] = GfxFrame[x][y];
8766 else if (move_dir == MV_UP)
8767 GfxFrame[x][y - 1] = GfxFrame[x][y];
8768 else if (move_dir == MV_DOWN)
8769 GfxFrame[x][y + 1] = GfxFrame[x][y];
8776 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8777 if (tile == Xsand_stonesand_quickout_1 ||
8778 tile == Xsand_stonesand_quickout_2)
8782 if (graphic_info[graphic].anim_global_sync)
8783 sync_frame = FrameCounter;
8784 else if (graphic_info[graphic].anim_global_anim_sync)
8785 sync_frame = getGlobalAnimSyncFrame();
8786 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8787 sync_frame = GfxFrame[x][y];
8789 sync_frame = 0; // playfield border (pseudo steel)
8791 SetRandomAnimationValue(x, y);
8793 int frame = getAnimationFrame(g->anim_frames,
8796 g->anim_start_frame,
8799 g_em->unique_identifier =
8800 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8803 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8804 int tile, int frame_em, int x, int y)
8806 int action = em_object_mapping[tile].action;
8807 int direction = em_object_mapping[tile].direction;
8808 boolean is_backside = em_object_mapping[tile].is_backside;
8809 int effective_element = get_effective_element_EM(tile, frame_em);
8810 int effective_action = action;
8811 int graphic = (direction == MV_NONE ?
8812 el_act2img(effective_element, effective_action) :
8813 el_act_dir2img(effective_element, effective_action,
8815 int crumbled = (direction == MV_NONE ?
8816 el_act2crm(effective_element, effective_action) :
8817 el_act_dir2crm(effective_element, effective_action,
8819 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8820 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8821 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8822 struct GraphicInfo *g = &graphic_info[graphic];
8825 // special case: graphic uses "2nd movement tile" and has defined
8826 // 7 frames for movement animation (or less) => use default graphic
8827 // for last (8th) frame which ends the movement animation
8828 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8830 effective_action = ACTION_DEFAULT;
8831 graphic = (direction == MV_NONE ?
8832 el_act2img(effective_element, effective_action) :
8833 el_act_dir2img(effective_element, effective_action,
8835 crumbled = (direction == MV_NONE ?
8836 el_act2crm(effective_element, effective_action) :
8837 el_act_dir2crm(effective_element, effective_action,
8840 g = &graphic_info[graphic];
8843 if (graphic_info[graphic].anim_global_sync)
8844 sync_frame = FrameCounter;
8845 else if (graphic_info[graphic].anim_global_anim_sync)
8846 sync_frame = getGlobalAnimSyncFrame();
8847 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8848 sync_frame = GfxFrame[x][y];
8850 sync_frame = 0; // playfield border (pseudo steel)
8852 SetRandomAnimationValue(x, y);
8854 int frame = getAnimationFrame(g->anim_frames,
8857 g->anim_start_frame,
8860 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8861 g->double_movement && is_backside);
8863 // (updating the "crumbled" graphic definitions is probably not really needed,
8864 // as animations for crumbled graphics can't be longer than one EMC cycle)
8865 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8869 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8870 int player_nr, int anim, int frame_em)
8872 int element = em_player_mapping[player_nr][anim].element_rnd;
8873 int action = em_player_mapping[player_nr][anim].action;
8874 int direction = em_player_mapping[player_nr][anim].direction;
8875 int graphic = (direction == MV_NONE ?
8876 el_act2img(element, action) :
8877 el_act_dir2img(element, action, direction));
8878 struct GraphicInfo *g = &graphic_info[graphic];
8881 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8883 stored_player[player_nr].StepFrame = frame_em;
8885 sync_frame = stored_player[player_nr].Frame;
8887 int frame = getAnimationFrame(g->anim_frames,
8890 g->anim_start_frame,
8893 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8894 &g_em->src_x, &g_em->src_y, FALSE);
8897 void InitGraphicInfo_EM(void)
8901 // always start with reliable default values
8902 for (i = 0; i < GAME_TILE_MAX; i++)
8904 em_object_mapping[i].element_rnd = EL_UNKNOWN;
8905 em_object_mapping[i].is_backside = FALSE;
8906 em_object_mapping[i].action = ACTION_DEFAULT;
8907 em_object_mapping[i].direction = MV_NONE;
8910 // always start with reliable default values
8911 for (p = 0; p < MAX_PLAYERS; p++)
8913 for (i = 0; i < PLY_MAX; i++)
8915 em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
8916 em_player_mapping[p][i].action = ACTION_DEFAULT;
8917 em_player_mapping[p][i].direction = MV_NONE;
8921 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8923 int e = em_object_mapping_list[i].element_em;
8925 em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8926 em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8928 if (em_object_mapping_list[i].action != -1)
8929 em_object_mapping[e].action = em_object_mapping_list[i].action;
8931 if (em_object_mapping_list[i].direction != -1)
8932 em_object_mapping[e].direction =
8933 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8936 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8938 int a = em_player_mapping_list[i].action_em;
8939 int p = em_player_mapping_list[i].player_nr;
8941 em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8943 if (em_player_mapping_list[i].action != -1)
8944 em_player_mapping[p][a].action = em_player_mapping_list[i].action;
8946 if (em_player_mapping_list[i].direction != -1)
8947 em_player_mapping[p][a].direction =
8948 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8951 for (i = 0; i < GAME_TILE_MAX; i++)
8953 int element = em_object_mapping[i].element_rnd;
8954 int action = em_object_mapping[i].action;
8955 int direction = em_object_mapping[i].direction;
8956 boolean is_backside = em_object_mapping[i].is_backside;
8957 boolean action_exploding = ((action == ACTION_EXPLODING ||
8958 action == ACTION_SMASHED_BY_ROCK ||
8959 action == ACTION_SMASHED_BY_SPRING) &&
8960 element != EL_DIAMOND);
8961 boolean action_active = (action == ACTION_ACTIVE);
8962 boolean action_other = (action == ACTION_OTHER);
8964 for (j = 0; j < 8; j++)
8966 int effective_element = get_effective_element_EM(i, j);
8967 int effective_action = (j < 7 ? action :
8968 i == Xdrip_stretch ? action :
8969 i == Xdrip_stretchB ? action :
8970 i == Ydrip_1_s ? action :
8971 i == Ydrip_1_sB ? action :
8972 i == Yball_1 ? action :
8973 i == Xball_2 ? action :
8974 i == Yball_2 ? action :
8975 i == Yball_blank ? action :
8976 i == Ykey_1_blank ? action :
8977 i == Ykey_2_blank ? action :
8978 i == Ykey_3_blank ? action :
8979 i == Ykey_4_blank ? action :
8980 i == Ykey_5_blank ? action :
8981 i == Ykey_6_blank ? action :
8982 i == Ykey_7_blank ? action :
8983 i == Ykey_8_blank ? action :
8984 i == Ylenses_blank ? action :
8985 i == Ymagnify_blank ? action :
8986 i == Ygrass_blank ? action :
8987 i == Ydirt_blank ? action :
8988 i == Xsand_stonein_1 ? action :
8989 i == Xsand_stonein_2 ? action :
8990 i == Xsand_stonein_3 ? action :
8991 i == Xsand_stonein_4 ? action :
8992 i == Xsand_stoneout_1 ? action :
8993 i == Xsand_stoneout_2 ? action :
8994 i == Xboom_android ? ACTION_EXPLODING :
8995 action_exploding ? ACTION_EXPLODING :
8996 action_active ? action :
8997 action_other ? action :
8999 int graphic = (el_act_dir2img(effective_element, effective_action,
9001 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9003 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9004 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9005 boolean has_action_graphics = (graphic != base_graphic);
9006 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9007 struct GraphicInfo *g = &graphic_info[graphic];
9008 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9011 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9012 boolean special_animation = (action != ACTION_DEFAULT &&
9013 g->anim_frames == 3 &&
9014 g->anim_delay == 2 &&
9015 g->anim_mode & ANIM_LINEAR);
9016 int sync_frame = (i == Xdrip_stretch ? 7 :
9017 i == Xdrip_stretchB ? 7 :
9018 i == Ydrip_2_s ? j + 8 :
9019 i == Ydrip_2_sB ? j + 8 :
9028 i == Xfake_acid_1 ? 0 :
9029 i == Xfake_acid_2 ? 10 :
9030 i == Xfake_acid_3 ? 20 :
9031 i == Xfake_acid_4 ? 30 :
9032 i == Xfake_acid_5 ? 40 :
9033 i == Xfake_acid_6 ? 50 :
9034 i == Xfake_acid_7 ? 60 :
9035 i == Xfake_acid_8 ? 70 :
9036 i == Xfake_acid_1_player ? 0 :
9037 i == Xfake_acid_2_player ? 10 :
9038 i == Xfake_acid_3_player ? 20 :
9039 i == Xfake_acid_4_player ? 30 :
9040 i == Xfake_acid_5_player ? 40 :
9041 i == Xfake_acid_6_player ? 50 :
9042 i == Xfake_acid_7_player ? 60 :
9043 i == Xfake_acid_8_player ? 70 :
9045 i == Yball_2 ? j + 8 :
9046 i == Yball_blank ? j + 1 :
9047 i == Ykey_1_blank ? j + 1 :
9048 i == Ykey_2_blank ? j + 1 :
9049 i == Ykey_3_blank ? j + 1 :
9050 i == Ykey_4_blank ? j + 1 :
9051 i == Ykey_5_blank ? j + 1 :
9052 i == Ykey_6_blank ? j + 1 :
9053 i == Ykey_7_blank ? j + 1 :
9054 i == Ykey_8_blank ? j + 1 :
9055 i == Ylenses_blank ? j + 1 :
9056 i == Ymagnify_blank ? j + 1 :
9057 i == Ygrass_blank ? j + 1 :
9058 i == Ydirt_blank ? j + 1 :
9059 i == Xamoeba_1 ? 0 :
9060 i == Xamoeba_2 ? 1 :
9061 i == Xamoeba_3 ? 2 :
9062 i == Xamoeba_4 ? 3 :
9063 i == Xamoeba_5 ? 0 :
9064 i == Xamoeba_6 ? 1 :
9065 i == Xamoeba_7 ? 2 :
9066 i == Xamoeba_8 ? 3 :
9067 i == Xexit_2 ? j + 8 :
9068 i == Xexit_3 ? j + 16 :
9069 i == Xdynamite_1 ? 0 :
9070 i == Xdynamite_2 ? 8 :
9071 i == Xdynamite_3 ? 16 :
9072 i == Xdynamite_4 ? 24 :
9073 i == Xsand_stonein_1 ? j + 1 :
9074 i == Xsand_stonein_2 ? j + 9 :
9075 i == Xsand_stonein_3 ? j + 17 :
9076 i == Xsand_stonein_4 ? j + 25 :
9077 i == Xsand_stoneout_1 && j == 0 ? 0 :
9078 i == Xsand_stoneout_1 && j == 1 ? 0 :
9079 i == Xsand_stoneout_1 && j == 2 ? 1 :
9080 i == Xsand_stoneout_1 && j == 3 ? 2 :
9081 i == Xsand_stoneout_1 && j == 4 ? 2 :
9082 i == Xsand_stoneout_1 && j == 5 ? 3 :
9083 i == Xsand_stoneout_1 && j == 6 ? 4 :
9084 i == Xsand_stoneout_1 && j == 7 ? 4 :
9085 i == Xsand_stoneout_2 && j == 0 ? 5 :
9086 i == Xsand_stoneout_2 && j == 1 ? 6 :
9087 i == Xsand_stoneout_2 && j == 2 ? 7 :
9088 i == Xsand_stoneout_2 && j == 3 ? 8 :
9089 i == Xsand_stoneout_2 && j == 4 ? 9 :
9090 i == Xsand_stoneout_2 && j == 5 ? 11 :
9091 i == Xsand_stoneout_2 && j == 6 ? 13 :
9092 i == Xsand_stoneout_2 && j == 7 ? 15 :
9093 i == Xboom_bug && j == 1 ? 2 :
9094 i == Xboom_bug && j == 2 ? 2 :
9095 i == Xboom_bug && j == 3 ? 4 :
9096 i == Xboom_bug && j == 4 ? 4 :
9097 i == Xboom_bug && j == 5 ? 2 :
9098 i == Xboom_bug && j == 6 ? 2 :
9099 i == Xboom_bug && j == 7 ? 0 :
9100 i == Xboom_tank && j == 1 ? 2 :
9101 i == Xboom_tank && j == 2 ? 2 :
9102 i == Xboom_tank && j == 3 ? 4 :
9103 i == Xboom_tank && j == 4 ? 4 :
9104 i == Xboom_tank && j == 5 ? 2 :
9105 i == Xboom_tank && j == 6 ? 2 :
9106 i == Xboom_tank && j == 7 ? 0 :
9107 i == Xboom_android && j == 7 ? 6 :
9108 i == Xboom_1 && j == 1 ? 2 :
9109 i == Xboom_1 && j == 2 ? 2 :
9110 i == Xboom_1 && j == 3 ? 4 :
9111 i == Xboom_1 && j == 4 ? 4 :
9112 i == Xboom_1 && j == 5 ? 6 :
9113 i == Xboom_1 && j == 6 ? 6 :
9114 i == Xboom_1 && j == 7 ? 8 :
9115 i == Xboom_2 && j == 0 ? 8 :
9116 i == Xboom_2 && j == 1 ? 8 :
9117 i == Xboom_2 && j == 2 ? 10 :
9118 i == Xboom_2 && j == 3 ? 10 :
9119 i == Xboom_2 && j == 4 ? 10 :
9120 i == Xboom_2 && j == 5 ? 12 :
9121 i == Xboom_2 && j == 6 ? 12 :
9122 i == Xboom_2 && j == 7 ? 12 :
9123 special_animation && j == 4 ? 3 :
9124 effective_action != action ? 0 :
9126 int frame = getAnimationFrame(g->anim_frames,
9129 g->anim_start_frame,
9132 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9133 g->double_movement && is_backside);
9135 g_em->bitmap = src_bitmap;
9136 g_em->src_x = src_x;
9137 g_em->src_y = src_y;
9138 g_em->src_offset_x = 0;
9139 g_em->src_offset_y = 0;
9140 g_em->dst_offset_x = 0;
9141 g_em->dst_offset_y = 0;
9142 g_em->width = TILEX;
9143 g_em->height = TILEY;
9145 g_em->preserve_background = FALSE;
9147 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9150 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9151 effective_action == ACTION_MOVING ||
9152 effective_action == ACTION_PUSHING ||
9153 effective_action == ACTION_EATING)) ||
9154 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9155 effective_action == ACTION_EMPTYING)))
9158 (effective_action == ACTION_FALLING ||
9159 effective_action == ACTION_FILLING ||
9160 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9161 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9162 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9163 int num_steps = (i == Ydrip_1_s ? 16 :
9164 i == Ydrip_1_sB ? 16 :
9165 i == Ydrip_2_s ? 16 :
9166 i == Ydrip_2_sB ? 16 :
9167 i == Xsand_stonein_1 ? 32 :
9168 i == Xsand_stonein_2 ? 32 :
9169 i == Xsand_stonein_3 ? 32 :
9170 i == Xsand_stonein_4 ? 32 :
9171 i == Xsand_stoneout_1 ? 16 :
9172 i == Xsand_stoneout_2 ? 16 : 8);
9173 int cx = ABS(dx) * (TILEX / num_steps);
9174 int cy = ABS(dy) * (TILEY / num_steps);
9175 int step_frame = (i == Ydrip_2_s ? j + 8 :
9176 i == Ydrip_2_sB ? j + 8 :
9177 i == Xsand_stonein_2 ? j + 8 :
9178 i == Xsand_stonein_3 ? j + 16 :
9179 i == Xsand_stonein_4 ? j + 24 :
9180 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9181 int step = (is_backside ? step_frame : num_steps - step_frame);
9183 if (is_backside) // tile where movement starts
9185 if (dx < 0 || dy < 0)
9187 g_em->src_offset_x = cx * step;
9188 g_em->src_offset_y = cy * step;
9192 g_em->dst_offset_x = cx * step;
9193 g_em->dst_offset_y = cy * step;
9196 else // tile where movement ends
9198 if (dx < 0 || dy < 0)
9200 g_em->dst_offset_x = cx * step;
9201 g_em->dst_offset_y = cy * step;
9205 g_em->src_offset_x = cx * step;
9206 g_em->src_offset_y = cy * step;
9210 g_em->width = TILEX - cx * step;
9211 g_em->height = TILEY - cy * step;
9214 // create unique graphic identifier to decide if tile must be redrawn
9215 /* bit 31 - 16 (16 bit): EM style graphic
9216 bit 15 - 12 ( 4 bit): EM style frame
9217 bit 11 - 6 ( 6 bit): graphic width
9218 bit 5 - 0 ( 6 bit): graphic height */
9219 g_em->unique_identifier =
9220 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9224 for (i = 0; i < GAME_TILE_MAX; i++)
9226 for (j = 0; j < 8; j++)
9228 int element = em_object_mapping[i].element_rnd;
9229 int action = em_object_mapping[i].action;
9230 int direction = em_object_mapping[i].direction;
9231 boolean is_backside = em_object_mapping[i].is_backside;
9232 int graphic_action = el_act_dir2img(element, action, direction);
9233 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9235 if ((action == ACTION_SMASHED_BY_ROCK ||
9236 action == ACTION_SMASHED_BY_SPRING ||
9237 action == ACTION_EATING) &&
9238 graphic_action == graphic_default)
9240 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9241 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9242 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9243 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9246 // no separate animation for "smashed by rock" -- use rock instead
9247 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9248 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9250 g_em->bitmap = g_xx->bitmap;
9251 g_em->src_x = g_xx->src_x;
9252 g_em->src_y = g_xx->src_y;
9253 g_em->src_offset_x = g_xx->src_offset_x;
9254 g_em->src_offset_y = g_xx->src_offset_y;
9255 g_em->dst_offset_x = g_xx->dst_offset_x;
9256 g_em->dst_offset_y = g_xx->dst_offset_y;
9257 g_em->width = g_xx->width;
9258 g_em->height = g_xx->height;
9259 g_em->unique_identifier = g_xx->unique_identifier;
9262 g_em->preserve_background = TRUE;
9267 for (p = 0; p < MAX_PLAYERS; p++)
9269 for (i = 0; i < PLY_MAX; i++)
9271 int element = em_player_mapping[p][i].element_rnd;
9272 int action = em_player_mapping[p][i].action;
9273 int direction = em_player_mapping[p][i].direction;
9275 for (j = 0; j < 8; j++)
9277 int effective_element = element;
9278 int effective_action = action;
9279 int graphic = (direction == MV_NONE ?
9280 el_act2img(effective_element, effective_action) :
9281 el_act_dir2img(effective_element, effective_action,
9283 struct GraphicInfo *g = &graphic_info[graphic];
9284 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9288 int frame = getAnimationFrame(g->anim_frames,
9291 g->anim_start_frame,
9294 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9296 g_em->bitmap = src_bitmap;
9297 g_em->src_x = src_x;
9298 g_em->src_y = src_y;
9299 g_em->src_offset_x = 0;
9300 g_em->src_offset_y = 0;
9301 g_em->dst_offset_x = 0;
9302 g_em->dst_offset_y = 0;
9303 g_em->width = TILEX;
9304 g_em->height = TILEY;
9310 static void CheckSaveEngineSnapshot_EM(int frame,
9311 boolean any_player_moving,
9312 boolean any_player_snapping,
9313 boolean any_player_dropping)
9315 if (frame == 7 && !any_player_dropping)
9317 if (!local_player->was_waiting)
9319 if (!CheckSaveEngineSnapshotToList())
9322 local_player->was_waiting = TRUE;
9325 else if (any_player_moving || any_player_snapping || any_player_dropping)
9327 local_player->was_waiting = FALSE;
9331 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9332 boolean murphy_is_dropping)
9334 if (murphy_is_waiting)
9336 if (!local_player->was_waiting)
9338 if (!CheckSaveEngineSnapshotToList())
9341 local_player->was_waiting = TRUE;
9346 local_player->was_waiting = FALSE;
9350 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9351 boolean button_released)
9353 if (button_released)
9355 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9356 CheckSaveEngineSnapshotToList();
9358 else if (element_clicked)
9360 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9361 CheckSaveEngineSnapshotToList();
9363 game.snapshot.changed_action = TRUE;
9367 boolean CheckSingleStepMode_EM(int frame,
9368 boolean any_player_moving,
9369 boolean any_player_snapping,
9370 boolean any_player_dropping)
9372 if (tape.single_step && tape.recording && !tape.pausing)
9373 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9374 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9376 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9377 any_player_snapping, any_player_dropping);
9379 return tape.pausing;
9382 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9383 boolean murphy_is_dropping)
9385 boolean murphy_starts_dropping = FALSE;
9388 for (i = 0; i < MAX_PLAYERS; i++)
9389 if (stored_player[i].force_dropping)
9390 murphy_starts_dropping = TRUE;
9392 if (tape.single_step && tape.recording && !tape.pausing)
9393 if (murphy_is_waiting && !murphy_starts_dropping)
9394 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9396 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9399 void CheckSingleStepMode_MM(boolean element_clicked,
9400 boolean button_released)
9402 if (tape.single_step && tape.recording && !tape.pausing)
9403 if (button_released)
9404 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9406 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9409 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9410 int graphic, int sync_frame)
9412 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9414 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9417 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9419 return (IS_NEXT_FRAME(sync_frame, graphic));
9422 int getGraphicInfo_Delay(int graphic)
9424 return graphic_info[graphic].anim_delay;
9427 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9429 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9432 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9438 void PlayMenuSoundExt(int sound)
9440 if (sound == SND_UNDEFINED)
9443 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9444 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9447 if (IS_LOOP_SOUND(sound))
9448 PlaySoundLoop(sound);
9453 void PlayMenuSound(void)
9455 PlayMenuSoundExt(menu.sound[game_status]);
9458 void PlayMenuSoundStereo(int sound, int stereo_position)
9460 if (sound == SND_UNDEFINED)
9463 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9464 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9467 if (IS_LOOP_SOUND(sound))
9468 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9470 PlaySoundStereo(sound, stereo_position);
9473 void PlayMenuSoundIfLoopExt(int sound)
9475 if (sound == SND_UNDEFINED)
9478 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9479 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9482 if (IS_LOOP_SOUND(sound))
9483 PlaySoundLoop(sound);
9486 void PlayMenuSoundIfLoop(void)
9488 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9491 void PlayMenuMusicExt(int music)
9493 if (music == MUS_UNDEFINED)
9496 if (!setup.sound_music)
9499 if (IS_LOOP_MUSIC(music))
9500 PlayMusicLoop(music);
9505 void PlayMenuMusic(void)
9507 char *curr_music = getCurrentlyPlayingMusicFilename();
9508 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9510 if (!strEqual(curr_music, next_music))
9511 PlayMenuMusicExt(menu.music[game_status]);
9514 void PlayMenuSoundsAndMusic(void)
9520 static void FadeMenuSounds(void)
9525 static void FadeMenuMusic(void)
9527 char *curr_music = getCurrentlyPlayingMusicFilename();
9528 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9530 if (!strEqual(curr_music, next_music))
9534 void FadeMenuSoundsAndMusic(void)
9540 void PlaySoundActivating(void)
9543 PlaySound(SND_MENU_ITEM_ACTIVATING);
9547 void PlaySoundSelecting(void)
9550 PlaySound(SND_MENU_ITEM_SELECTING);
9554 void ToggleFullscreenIfNeeded(void)
9556 // if setup and video fullscreen state are already matching, nothing do do
9557 if (setup.fullscreen == video.fullscreen_enabled ||
9558 !video.fullscreen_available)
9561 SDLSetWindowFullscreen(setup.fullscreen);
9563 // set setup value according to successfully changed fullscreen mode
9564 setup.fullscreen = video.fullscreen_enabled;
9567 void ChangeWindowScalingIfNeeded(void)
9569 // if setup and video window scaling are already matching, nothing do do
9570 if (setup.window_scaling_percent == video.window_scaling_percent ||
9571 video.fullscreen_enabled)
9574 SDLSetWindowScaling(setup.window_scaling_percent);
9576 // set setup value according to successfully changed window scaling
9577 setup.window_scaling_percent = video.window_scaling_percent;
9580 void ChangeVsyncModeIfNeeded(void)
9582 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9583 int video_vsync_mode = video.vsync_mode;
9585 // if setup and video vsync mode are already matching, nothing do do
9586 if (setup_vsync_mode == video_vsync_mode)
9589 // if renderer is using OpenGL, vsync mode can directly be changed
9590 SDLSetScreenVsyncMode(setup.vsync_mode);
9592 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9593 if (video.vsync_mode == video_vsync_mode)
9595 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9597 // save backbuffer content which gets lost when re-creating screen
9598 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9600 // force re-creating screen and renderer to set new vsync mode
9601 video.fullscreen_enabled = !setup.fullscreen;
9603 // when creating new renderer, destroy textures linked to old renderer
9604 FreeAllImageTextures(); // needs old renderer to free the textures
9606 // re-create screen and renderer (including change of vsync mode)
9607 ChangeVideoModeIfNeeded(setup.fullscreen);
9609 // set setup value according to successfully changed fullscreen mode
9610 setup.fullscreen = video.fullscreen_enabled;
9612 // restore backbuffer content from temporary backbuffer backup bitmap
9613 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9614 FreeBitmap(tmp_backbuffer);
9616 // update visible window/screen
9617 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9619 // when changing vsync mode, re-create textures for new renderer
9620 InitImageTextures();
9623 // set setup value according to successfully changed vsync mode
9624 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9627 static void JoinRectangles(int *x, int *y, int *width, int *height,
9628 int x2, int y2, int width2, int height2)
9630 // do not join with "off-screen" rectangle
9631 if (x2 == -1 || y2 == -1)
9636 *width = MAX(*width, width2);
9637 *height = MAX(*height, height2);
9640 void SetAnimStatus(int anim_status_new)
9642 if (anim_status_new == GAME_MODE_MAIN)
9643 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9644 else if (anim_status_new == GAME_MODE_NAMES)
9645 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9646 else if (anim_status_new == GAME_MODE_SCORES)
9647 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9649 global.anim_status_next = anim_status_new;
9651 // directly set screen modes that are entered without fading
9652 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9653 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9654 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9655 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9656 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9657 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9658 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9659 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9660 global.anim_status = global.anim_status_next;
9663 void SetGameStatus(int game_status_new)
9665 if (game_status_new != game_status)
9666 game_status_last_screen = game_status;
9668 game_status = game_status_new;
9670 SetAnimStatus(game_status_new);
9673 void SetFontStatus(int game_status_new)
9675 static int last_game_status = -1;
9677 if (game_status_new != -1)
9679 // set game status for font use after storing last game status
9680 last_game_status = game_status;
9681 game_status = game_status_new;
9685 // reset game status after font use from last stored game status
9686 game_status = last_game_status;
9690 void ResetFontStatus(void)
9695 void SetLevelSetInfo(char *identifier, int level_nr)
9697 setString(&levelset.identifier, identifier);
9699 levelset.level_nr = level_nr;
9702 boolean CheckIfAllViewportsHaveChanged(void)
9704 // if game status has not changed, viewports have not changed either
9705 if (game_status == game_status_last)
9708 // check if all viewports have changed with current game status
9710 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9711 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9712 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9713 int new_real_sx = vp_playfield->x;
9714 int new_real_sy = vp_playfield->y;
9715 int new_full_sxsize = vp_playfield->width;
9716 int new_full_sysize = vp_playfield->height;
9717 int new_dx = vp_door_1->x;
9718 int new_dy = vp_door_1->y;
9719 int new_dxsize = vp_door_1->width;
9720 int new_dysize = vp_door_1->height;
9721 int new_vx = vp_door_2->x;
9722 int new_vy = vp_door_2->y;
9723 int new_vxsize = vp_door_2->width;
9724 int new_vysize = vp_door_2->height;
9726 boolean playfield_viewport_has_changed =
9727 (new_real_sx != REAL_SX ||
9728 new_real_sy != REAL_SY ||
9729 new_full_sxsize != FULL_SXSIZE ||
9730 new_full_sysize != FULL_SYSIZE);
9732 boolean door_1_viewport_has_changed =
9735 new_dxsize != DXSIZE ||
9736 new_dysize != DYSIZE);
9738 boolean door_2_viewport_has_changed =
9741 new_vxsize != VXSIZE ||
9742 new_vysize != VYSIZE ||
9743 game_status_last == GAME_MODE_EDITOR);
9745 return (playfield_viewport_has_changed &&
9746 door_1_viewport_has_changed &&
9747 door_2_viewport_has_changed);
9750 boolean CheckFadeAll(void)
9752 return (CheckIfGlobalBorderHasChanged() ||
9753 CheckIfAllViewportsHaveChanged());
9756 void ChangeViewportPropertiesIfNeeded(void)
9758 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9759 FALSE : setup.small_game_graphics);
9760 int gfx_game_mode = getGlobalGameStatus(game_status);
9761 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9763 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9764 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9765 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9766 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9767 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9768 int new_win_xsize = vp_window->width;
9769 int new_win_ysize = vp_window->height;
9770 int border_left = vp_playfield->border_left;
9771 int border_right = vp_playfield->border_right;
9772 int border_top = vp_playfield->border_top;
9773 int border_bottom = vp_playfield->border_bottom;
9774 int new_sx = vp_playfield->x + border_left;
9775 int new_sy = vp_playfield->y + border_top;
9776 int new_sxsize = vp_playfield->width - border_left - border_right;
9777 int new_sysize = vp_playfield->height - border_top - border_bottom;
9778 int new_real_sx = vp_playfield->x;
9779 int new_real_sy = vp_playfield->y;
9780 int new_full_sxsize = vp_playfield->width;
9781 int new_full_sysize = vp_playfield->height;
9782 int new_dx = vp_door_1->x;
9783 int new_dy = vp_door_1->y;
9784 int new_dxsize = vp_door_1->width;
9785 int new_dysize = vp_door_1->height;
9786 int new_vx = vp_door_2->x;
9787 int new_vy = vp_door_2->y;
9788 int new_vxsize = vp_door_2->width;
9789 int new_vysize = vp_door_2->height;
9790 int new_ex = vp_door_3->x;
9791 int new_ey = vp_door_3->y;
9792 int new_exsize = vp_door_3->width;
9793 int new_eysize = vp_door_3->height;
9794 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9795 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9796 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9797 int new_scr_fieldx = new_sxsize / tilesize;
9798 int new_scr_fieldy = new_sysize / tilesize;
9799 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9800 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9801 boolean init_gfx_buffers = FALSE;
9802 boolean init_video_buffer = FALSE;
9803 boolean init_gadgets_and_anims = FALSE;
9804 boolean init_em_graphics = FALSE;
9806 if (new_win_xsize != WIN_XSIZE ||
9807 new_win_ysize != WIN_YSIZE)
9809 WIN_XSIZE = new_win_xsize;
9810 WIN_YSIZE = new_win_ysize;
9812 init_video_buffer = TRUE;
9813 init_gfx_buffers = TRUE;
9814 init_gadgets_and_anims = TRUE;
9816 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9819 if (new_scr_fieldx != SCR_FIELDX ||
9820 new_scr_fieldy != SCR_FIELDY)
9822 // this always toggles between MAIN and GAME when using small tile size
9824 SCR_FIELDX = new_scr_fieldx;
9825 SCR_FIELDY = new_scr_fieldy;
9827 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9838 new_sxsize != SXSIZE ||
9839 new_sysize != SYSIZE ||
9840 new_dxsize != DXSIZE ||
9841 new_dysize != DYSIZE ||
9842 new_vxsize != VXSIZE ||
9843 new_vysize != VYSIZE ||
9844 new_exsize != EXSIZE ||
9845 new_eysize != EYSIZE ||
9846 new_real_sx != REAL_SX ||
9847 new_real_sy != REAL_SY ||
9848 new_full_sxsize != FULL_SXSIZE ||
9849 new_full_sysize != FULL_SYSIZE ||
9850 new_tilesize_var != TILESIZE_VAR
9853 // ------------------------------------------------------------------------
9854 // determine next fading area for changed viewport definitions
9855 // ------------------------------------------------------------------------
9857 // start with current playfield area (default fading area)
9860 FADE_SXSIZE = FULL_SXSIZE;
9861 FADE_SYSIZE = FULL_SYSIZE;
9863 // add new playfield area if position or size has changed
9864 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9865 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9867 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9868 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9871 // add current and new door 1 area if position or size has changed
9872 if (new_dx != DX || new_dy != DY ||
9873 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9875 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9876 DX, DY, DXSIZE, DYSIZE);
9877 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9878 new_dx, new_dy, new_dxsize, new_dysize);
9881 // add current and new door 2 area if position or size has changed
9882 if (new_vx != VX || new_vy != VY ||
9883 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9885 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9886 VX, VY, VXSIZE, VYSIZE);
9887 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9888 new_vx, new_vy, new_vxsize, new_vysize);
9891 // ------------------------------------------------------------------------
9892 // handle changed tile size
9893 // ------------------------------------------------------------------------
9895 if (new_tilesize_var != TILESIZE_VAR)
9897 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9899 // changing tile size invalidates scroll values of engine snapshots
9900 FreeEngineSnapshotSingle();
9902 // changing tile size requires update of graphic mapping for EM engine
9903 init_em_graphics = TRUE;
9914 SXSIZE = new_sxsize;
9915 SYSIZE = new_sysize;
9916 DXSIZE = new_dxsize;
9917 DYSIZE = new_dysize;
9918 VXSIZE = new_vxsize;
9919 VYSIZE = new_vysize;
9920 EXSIZE = new_exsize;
9921 EYSIZE = new_eysize;
9922 REAL_SX = new_real_sx;
9923 REAL_SY = new_real_sy;
9924 FULL_SXSIZE = new_full_sxsize;
9925 FULL_SYSIZE = new_full_sysize;
9926 TILESIZE_VAR = new_tilesize_var;
9928 init_gfx_buffers = TRUE;
9929 init_gadgets_and_anims = TRUE;
9931 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9932 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9935 if (init_gfx_buffers)
9937 // Debug("tools:viewport", "init_gfx_buffers");
9939 SCR_FIELDX = new_scr_fieldx_buffers;
9940 SCR_FIELDY = new_scr_fieldy_buffers;
9944 SCR_FIELDX = new_scr_fieldx;
9945 SCR_FIELDY = new_scr_fieldy;
9947 SetDrawDeactivationMask(REDRAW_NONE);
9948 SetDrawBackgroundMask(REDRAW_FIELD);
9951 if (init_video_buffer)
9953 // Debug("tools:viewport", "init_video_buffer");
9955 FreeAllImageTextures(); // needs old renderer to free the textures
9957 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9958 InitImageTextures();
9961 if (init_gadgets_and_anims)
9963 // Debug("tools:viewport", "init_gadgets_and_anims");
9966 InitGlobalAnimations();
9969 if (init_em_graphics)
9971 InitGraphicInfo_EM();
9975 void OpenURL(char *url)
9977 #if SDL_VERSION_ATLEAST(2,0,14)
9980 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9981 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9982 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9986 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9988 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9992 // ============================================================================
9994 // ============================================================================
9996 #if defined(PLATFORM_WINDOWS)
9997 /* FILETIME of Jan 1 1970 00:00:00. */
9998 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10001 * timezone information is stored outside the kernel so tzp isn't used anymore.
10003 * Note: this function is not for Win32 high precision timing purpose. See
10006 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10008 FILETIME file_time;
10009 SYSTEMTIME system_time;
10010 ULARGE_INTEGER ularge;
10012 GetSystemTime(&system_time);
10013 SystemTimeToFileTime(&system_time, &file_time);
10014 ularge.LowPart = file_time.dwLowDateTime;
10015 ularge.HighPart = file_time.dwHighDateTime;
10017 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10018 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10024 static char *test_init_uuid_random_function_simple(void)
10026 static char seed_text[100];
10027 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10029 sprintf(seed_text, "%d", seed);
10034 static char *test_init_uuid_random_function_better(void)
10036 static char seed_text[100];
10037 struct timeval current_time;
10039 gettimeofday(¤t_time, NULL);
10041 prng_seed_bytes(¤t_time, sizeof(current_time));
10043 sprintf(seed_text, "%ld.%ld",
10044 (long)current_time.tv_sec,
10045 (long)current_time.tv_usec);
10050 #if defined(PLATFORM_WINDOWS)
10051 static char *test_init_uuid_random_function_better_windows(void)
10053 static char seed_text[100];
10054 struct timeval current_time;
10056 gettimeofday_windows(¤t_time, NULL);
10058 prng_seed_bytes(¤t_time, sizeof(current_time));
10060 sprintf(seed_text, "%ld.%ld",
10061 (long)current_time.tv_sec,
10062 (long)current_time.tv_usec);
10068 static unsigned int test_uuid_random_function_simple(int max)
10070 return GetSimpleRandom(max);
10073 static unsigned int test_uuid_random_function_better(int max)
10075 return (max > 0 ? prng_get_uint() % max : 0);
10078 #if defined(PLATFORM_WINDOWS)
10079 #define NUM_UUID_TESTS 3
10081 #define NUM_UUID_TESTS 2
10084 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10086 struct hashtable *hash_seeds =
10087 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10088 struct hashtable *hash_uuids =
10089 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10090 static char message[100];
10093 char *random_name = (nr == 0 ? "simple" : "better");
10094 char *random_type = (always_seed ? "always" : "only once");
10095 char *(*init_random_function)(void) =
10097 test_init_uuid_random_function_simple :
10098 test_init_uuid_random_function_better);
10099 unsigned int (*random_function)(int) =
10101 test_uuid_random_function_simple :
10102 test_uuid_random_function_better);
10105 #if defined(PLATFORM_WINDOWS)
10108 random_name = "windows";
10109 init_random_function = test_init_uuid_random_function_better_windows;
10115 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10116 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10118 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10119 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10120 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10122 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10126 // always initialize random number generator at least once
10127 init_random_function();
10129 unsigned int time_start = SDL_GetTicks();
10131 for (i = 0; i < num_uuids; i++)
10135 char *seed = getStringCopy(init_random_function());
10137 hashtable_remove(hash_seeds, seed);
10138 hashtable_insert(hash_seeds, seed, "1");
10141 char *uuid = getStringCopy(getUUIDExt(random_function));
10143 hashtable_remove(hash_uuids, uuid);
10144 hashtable_insert(hash_uuids, uuid, "1");
10147 int num_unique_seeds = hashtable_count(hash_seeds);
10148 int num_unique_uuids = hashtable_count(hash_uuids);
10150 unsigned int time_needed = SDL_GetTicks() - time_start;
10152 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10154 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10157 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10159 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10160 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10162 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10164 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10166 Request(message, REQ_CONFIRM);
10168 hashtable_destroy(hash_seeds, 0);
10169 hashtable_destroy(hash_uuids, 0);
10172 void TestGeneratingUUIDs(void)
10174 int num_uuids = 1000000;
10177 for (i = 0; i < NUM_UUID_TESTS; i++)
10178 for (j = 0; j < 2; j++)
10179 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10181 CloseAllAndExit(0);