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 int getGraphicAnimationFrame(int graphic, int sync_frame)
1522 // animation synchronized with global frame counter, not move position
1523 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1524 sync_frame = FrameCounter;
1525 else if (graphic_info[graphic].anim_global_anim_sync)
1526 sync_frame = getGlobalAnimSyncFrame();
1528 return getAnimationFrame(graphic_info[graphic].anim_frames,
1529 graphic_info[graphic].anim_delay,
1530 graphic_info[graphic].anim_mode,
1531 graphic_info[graphic].anim_start_frame,
1535 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1537 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1539 struct GraphicInfo *g = &graphic_info[graphic];
1540 int xsize = MAX(1, g->anim_frames_per_line);
1541 int ysize = MAX(1, g->anim_frames / xsize);
1542 int xoffset = g->anim_start_frame % xsize;
1543 int yoffset = g->anim_start_frame % ysize;
1544 // may be needed if screen field is significantly larger than playfield
1545 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1546 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1547 int sync_frame = y * xsize + x;
1549 return sync_frame % g->anim_frames;
1551 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1553 struct GraphicInfo *g = &graphic_info[graphic];
1554 // may be needed if screen field is significantly larger than playfield
1555 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1556 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1557 int sync_frame = GfxRandomStatic[x][y];
1559 return sync_frame % g->anim_frames;
1563 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1565 return getGraphicAnimationFrame(graphic, sync_frame);
1569 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1571 struct GraphicInfo *g = &graphic_info[graphic];
1572 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1574 if (tilesize == gfx.standard_tile_size)
1575 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1576 else if (tilesize == game.tile_size)
1577 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1579 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1582 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1583 boolean get_backside)
1585 struct GraphicInfo *g = &graphic_info[graphic];
1586 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1587 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1589 if (g->offset_y == 0) // frames are ordered horizontally
1591 int max_width = g->anim_frames_per_line * g->width;
1592 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1594 *x = pos % max_width;
1595 *y = src_y % g->height + pos / max_width * g->height;
1597 else if (g->offset_x == 0) // frames are ordered vertically
1599 int max_height = g->anim_frames_per_line * g->height;
1600 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1602 *x = src_x % g->width + pos / max_height * g->width;
1603 *y = pos % max_height;
1605 else // frames are ordered diagonally
1607 *x = src_x + frame * g->offset_x;
1608 *y = src_y + frame * g->offset_y;
1612 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1613 Bitmap **bitmap, int *x, int *y,
1614 boolean get_backside)
1616 struct GraphicInfo *g = &graphic_info[graphic];
1618 // if no graphics defined at all, use fallback graphics
1619 if (g->bitmaps == NULL)
1620 *g = graphic_info[IMG_CHAR_EXCLAM];
1622 // if no in-game graphics defined, always use standard graphic size
1623 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1624 tilesize = TILESIZE;
1626 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1627 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1629 *x = *x * tilesize / g->tile_size;
1630 *y = *y * tilesize / g->tile_size;
1633 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1634 Bitmap **bitmap, int *x, int *y)
1636 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1639 void getFixedGraphicSource(int graphic, int frame,
1640 Bitmap **bitmap, int *x, int *y)
1642 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1645 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1647 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1650 void getGlobalAnimGraphicSource(int graphic, int frame,
1651 Bitmap **bitmap, int *x, int *y)
1653 struct GraphicInfo *g = &graphic_info[graphic];
1655 // if no graphics defined at all, use fallback graphics
1656 if (g->bitmaps == NULL)
1657 *g = graphic_info[IMG_CHAR_EXCLAM];
1659 // use original size graphics, if existing, else use standard size graphics
1660 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1661 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1663 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1665 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1668 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1669 int *x, int *y, boolean get_backside)
1671 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1675 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1677 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1680 void DrawGraphic(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1686 Debug("draw:DrawGraphic", "This should never happen!");
1692 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1695 MarkTileDirty(x, y);
1698 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1701 if (!IN_SCR_FIELD(x, y))
1703 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1705 Debug("draw:DrawFixedGraphic", "This should never happen!");
1711 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1713 MarkTileDirty(x, y);
1716 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1722 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1724 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1727 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1733 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1734 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1737 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1740 if (!IN_SCR_FIELD(x, y))
1742 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1744 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1750 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1753 MarkTileDirty(x, y);
1756 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1759 if (!IN_SCR_FIELD(x, y))
1761 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1763 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1769 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1771 MarkTileDirty(x, y);
1774 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1780 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1782 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1786 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1787 int graphic, int frame)
1792 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1794 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1798 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1800 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1802 MarkTileDirty(x / tilesize, y / tilesize);
1805 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1808 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1809 graphic, frame, tilesize);
1810 MarkTileDirty(x / tilesize, y / tilesize);
1813 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1819 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1820 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1823 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1824 int frame, int tilesize)
1829 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1830 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1833 void DrawMiniGraphic(int x, int y, int graphic)
1835 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1836 MarkTileDirty(x / 2, y / 2);
1839 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1844 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1845 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1848 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1849 int graphic, int frame,
1850 int cut_mode, int mask_mode)
1855 int width = TILEX, height = TILEY;
1858 if (dx || dy) // shifted graphic
1860 if (x < BX1) // object enters playfield from the left
1867 else if (x > BX2) // object enters playfield from the right
1873 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1879 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1881 else if (dx) // general horizontal movement
1882 MarkTileDirty(x + SIGN(dx), y);
1884 if (y < BY1) // object enters playfield from the top
1886 if (cut_mode == CUT_BELOW) // object completely above top border
1894 else if (y > BY2) // object enters playfield from the bottom
1900 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1906 else if (dy > 0 && cut_mode == CUT_ABOVE)
1908 if (y == BY2) // object completely above bottom border
1914 MarkTileDirty(x, y + 1);
1915 } // object leaves playfield to the bottom
1916 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1918 else if (dy) // general vertical movement
1919 MarkTileDirty(x, y + SIGN(dy));
1923 if (!IN_SCR_FIELD(x, y))
1925 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1927 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1933 width = width * TILESIZE_VAR / TILESIZE;
1934 height = height * TILESIZE_VAR / TILESIZE;
1935 cx = cx * TILESIZE_VAR / TILESIZE;
1936 cy = cy * TILESIZE_VAR / TILESIZE;
1937 dx = dx * TILESIZE_VAR / TILESIZE;
1938 dy = dy * TILESIZE_VAR / TILESIZE;
1940 if (width > 0 && height > 0)
1942 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1947 dst_x = FX + x * TILEX_VAR + dx;
1948 dst_y = FY + y * TILEY_VAR + dy;
1950 if (mask_mode == USE_MASKING)
1951 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1957 MarkTileDirty(x, y);
1961 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1962 int graphic, int frame,
1963 int cut_mode, int mask_mode)
1968 int width = TILEX_VAR, height = TILEY_VAR;
1971 int x2 = x + SIGN(dx);
1972 int y2 = y + SIGN(dy);
1974 // movement with two-tile animations must be sync'ed with movement position,
1975 // not with current GfxFrame (which can be higher when using slow movement)
1976 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1977 int anim_frames = graphic_info[graphic].anim_frames;
1979 // (we also need anim_delay here for movement animations with less frames)
1980 int anim_delay = graphic_info[graphic].anim_delay;
1981 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1983 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1984 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1986 // re-calculate animation frame for two-tile movement animation
1987 frame = getGraphicAnimationFrame(graphic, sync_frame);
1989 // check if movement start graphic inside screen area and should be drawn
1990 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1992 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1994 dst_x = FX + x1 * TILEX_VAR;
1995 dst_y = FY + y1 * TILEY_VAR;
1997 if (mask_mode == USE_MASKING)
1998 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2001 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2004 MarkTileDirty(x1, y1);
2007 // check if movement end graphic inside screen area and should be drawn
2008 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2010 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2012 dst_x = FX + x2 * TILEX_VAR;
2013 dst_y = FY + y2 * TILEY_VAR;
2015 if (mask_mode == USE_MASKING)
2016 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2019 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2022 MarkTileDirty(x2, y2);
2026 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2027 int graphic, int frame,
2028 int cut_mode, int mask_mode)
2032 DrawGraphic(x, y, graphic, frame);
2037 if (graphic_info[graphic].double_movement) // EM style movement images
2038 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2043 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2044 int graphic, int frame, int cut_mode)
2046 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2049 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2050 int cut_mode, int mask_mode)
2052 int lx = LEVELX(x), ly = LEVELY(y);
2056 if (IN_LEV_FIELD(lx, ly))
2058 if (element == EL_EMPTY)
2059 element = GfxElementEmpty[lx][ly];
2061 SetRandomAnimationValue(lx, ly);
2063 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2064 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2066 // do not use double (EM style) movement graphic when not moving
2067 if (graphic_info[graphic].double_movement && !dx && !dy)
2069 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2070 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2073 if (game.use_masked_elements && (dx || dy))
2074 mask_mode = USE_MASKING;
2076 else // border element
2078 graphic = el2img(element);
2079 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2082 if (element == EL_EXPANDABLE_WALL)
2084 boolean left_stopped = FALSE, right_stopped = FALSE;
2086 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2087 left_stopped = TRUE;
2088 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2089 right_stopped = TRUE;
2091 if (left_stopped && right_stopped)
2093 else if (left_stopped)
2095 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2096 frame = graphic_info[graphic].anim_frames - 1;
2098 else if (right_stopped)
2100 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2101 frame = graphic_info[graphic].anim_frames - 1;
2106 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2107 else if (mask_mode == USE_MASKING)
2108 DrawGraphicThruMask(x, y, graphic, frame);
2110 DrawGraphic(x, y, graphic, frame);
2113 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2114 int cut_mode, int mask_mode)
2116 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2117 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2118 cut_mode, mask_mode);
2121 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2124 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2127 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2130 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2133 void DrawLevelElementThruMask(int x, int y, int element)
2135 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2138 void DrawLevelFieldThruMask(int x, int y)
2140 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2143 // !!! implementation of quicksand is totally broken !!!
2144 #define IS_CRUMBLED_TILE(x, y, e) \
2145 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2146 !IS_MOVING(x, y) || \
2147 (e) == EL_QUICKSAND_EMPTYING || \
2148 (e) == EL_QUICKSAND_FAST_EMPTYING))
2150 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2155 int width, height, cx, cy;
2156 int sx = SCREENX(x), sy = SCREENY(y);
2157 int crumbled_border_size = graphic_info[graphic].border_size;
2158 int crumbled_tile_size = graphic_info[graphic].tile_size;
2159 int crumbled_border_size_var =
2160 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2163 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2165 for (i = 1; i < 4; i++)
2167 int dxx = (i & 1 ? dx : 0);
2168 int dyy = (i & 2 ? dy : 0);
2171 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2174 // check if neighbour field is of same crumble type
2175 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2176 graphic_info[graphic].class ==
2177 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2179 // return if check prevents inner corner
2180 if (same == (dxx == dx && dyy == dy))
2184 // if we reach this point, we have an inner corner
2186 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2188 width = crumbled_border_size_var;
2189 height = crumbled_border_size_var;
2190 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2191 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2193 if (game.use_masked_elements)
2195 int graphic0 = el2img(EL_EMPTY);
2196 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2197 Bitmap *src_bitmap0;
2200 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2202 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2204 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2206 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2208 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2211 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2213 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2216 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2221 int width, height, bx, by, cx, cy;
2222 int sx = SCREENX(x), sy = SCREENY(y);
2223 int crumbled_border_size = graphic_info[graphic].border_size;
2224 int crumbled_tile_size = graphic_info[graphic].tile_size;
2225 int crumbled_border_size_var =
2226 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2227 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2230 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2232 // only needed when using masked elements
2233 int graphic0 = el2img(EL_EMPTY);
2234 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2235 Bitmap *src_bitmap0;
2238 if (game.use_masked_elements)
2239 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2241 // draw simple, sloppy, non-corner-accurate crumbled border
2243 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2244 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2245 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2246 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2248 if (game.use_masked_elements)
2250 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2252 FX + sx * TILEX_VAR + cx,
2253 FY + sy * TILEY_VAR + cy);
2255 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2257 FX + sx * TILEX_VAR + cx,
2258 FY + sy * TILEY_VAR + cy);
2261 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2263 FX + sx * TILEX_VAR + cx,
2264 FY + sy * TILEY_VAR + cy);
2266 // (remaining middle border part must be at least as big as corner part)
2267 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2268 crumbled_border_size_var >= TILESIZE_VAR / 3)
2271 // correct corners of crumbled border, if needed
2273 for (i = -1; i <= 1; i += 2)
2275 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2276 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2277 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2280 // check if neighbour field is of same crumble type
2281 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2282 graphic_info[graphic].class ==
2283 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2285 // no crumbled corner, but continued crumbled border
2287 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2288 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2289 int b1 = (i == 1 ? crumbled_border_size_var :
2290 TILESIZE_VAR - 2 * crumbled_border_size_var);
2292 width = crumbled_border_size_var;
2293 height = crumbled_border_size_var;
2295 if (dir == 1 || dir == 2)
2310 if (game.use_masked_elements)
2312 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2314 FX + sx * TILEX_VAR + cx,
2315 FY + sy * TILEY_VAR + cy);
2317 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2319 FX + sx * TILEX_VAR + cx,
2320 FY + sy * TILEY_VAR + cy);
2323 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2325 FX + sx * TILEX_VAR + cx,
2326 FY + sy * TILEY_VAR + cy);
2331 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2333 int sx = SCREENX(x), sy = SCREENY(y);
2336 struct XY *xy = xy_topdown;
2338 if (!IN_LEV_FIELD(x, y))
2341 element = TILE_GFX_ELEMENT(x, y);
2343 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2345 if (!IN_SCR_FIELD(sx, sy))
2348 // crumble field borders towards direct neighbour fields
2349 for (i = 0; i < 4; i++)
2351 int xx = x + xy[i].x;
2352 int yy = y + xy[i].y;
2354 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2357 // check if neighbour field is of same crumble type
2358 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2359 graphic_info[graphic].class ==
2360 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2363 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2366 // crumble inner field corners towards corner neighbour fields
2367 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2368 graphic_info[graphic].anim_frames == 2)
2370 for (i = 0; i < 4; i++)
2372 int dx = (i & 1 ? +1 : -1);
2373 int dy = (i & 2 ? +1 : -1);
2375 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2379 MarkTileDirty(sx, sy);
2381 else // center field is not crumbled -- crumble neighbour fields
2383 // crumble field borders of direct neighbour fields
2384 for (i = 0; i < 4; i++)
2386 int xx = x + xy[i].x;
2387 int yy = y + xy[i].y;
2388 int sxx = sx + xy[i].x;
2389 int syy = sy + xy[i].y;
2391 if (!IN_LEV_FIELD(xx, yy) ||
2392 !IN_SCR_FIELD(sxx, syy))
2395 // do not crumble fields that are being digged or snapped
2396 if (Tile[xx][yy] == EL_EMPTY ||
2397 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2400 element = TILE_GFX_ELEMENT(xx, yy);
2402 if (!IS_CRUMBLED_TILE(xx, yy, element))
2405 graphic = el_act2crm(element, ACTION_DEFAULT);
2407 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2409 MarkTileDirty(sxx, syy);
2412 // crumble inner field corners of corner neighbour fields
2413 for (i = 0; i < 4; i++)
2415 int dx = (i & 1 ? +1 : -1);
2416 int dy = (i & 2 ? +1 : -1);
2422 if (!IN_LEV_FIELD(xx, yy) ||
2423 !IN_SCR_FIELD(sxx, syy))
2426 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2429 element = TILE_GFX_ELEMENT(xx, yy);
2431 if (!IS_CRUMBLED_TILE(xx, yy, element))
2434 graphic = el_act2crm(element, ACTION_DEFAULT);
2436 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2437 graphic_info[graphic].anim_frames == 2)
2438 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2440 MarkTileDirty(sxx, syy);
2445 void DrawLevelFieldCrumbled(int x, int y)
2449 if (!IN_LEV_FIELD(x, y))
2452 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2453 GfxElement[x][y] != EL_UNDEFINED &&
2454 GFX_CRUMBLED(GfxElement[x][y]))
2456 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2461 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2463 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2466 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2469 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2470 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2471 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2472 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2473 int sx = SCREENX(x), sy = SCREENY(y);
2475 DrawScreenGraphic(sx, sy, graphic1, frame1);
2476 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2479 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2481 int sx = SCREENX(x), sy = SCREENY(y);
2482 struct XY *xy = xy_topdown;
2485 // crumble direct neighbour fields (required for field borders)
2486 for (i = 0; i < 4; i++)
2488 int xx = x + xy[i].x;
2489 int yy = y + xy[i].y;
2490 int sxx = sx + xy[i].x;
2491 int syy = sy + xy[i].y;
2493 if (!IN_LEV_FIELD(xx, yy) ||
2494 !IN_SCR_FIELD(sxx, syy) ||
2495 !GFX_CRUMBLED(Tile[xx][yy]) ||
2499 DrawLevelField(xx, yy);
2502 // crumble corner neighbour fields (required for inner field corners)
2503 for (i = 0; i < 4; i++)
2505 int dx = (i & 1 ? +1 : -1);
2506 int dy = (i & 2 ? +1 : -1);
2512 if (!IN_LEV_FIELD(xx, yy) ||
2513 !IN_SCR_FIELD(sxx, syy) ||
2514 !GFX_CRUMBLED(Tile[xx][yy]) ||
2518 int element = TILE_GFX_ELEMENT(xx, yy);
2519 int graphic = el_act2crm(element, ACTION_DEFAULT);
2521 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2522 graphic_info[graphic].anim_frames == 2)
2523 DrawLevelField(xx, yy);
2527 static int getBorderElement(int x, int y)
2531 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2532 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2533 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2534 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2535 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2536 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2537 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2539 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2540 int steel_position = (x == -1 && y == -1 ? 0 :
2541 x == lev_fieldx && y == -1 ? 1 :
2542 x == -1 && y == lev_fieldy ? 2 :
2543 x == lev_fieldx && y == lev_fieldy ? 3 :
2544 x == -1 || x == lev_fieldx ? 4 :
2545 y == -1 || y == lev_fieldy ? 5 : 6);
2547 return border[steel_position][steel_type];
2550 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2552 if (game.use_masked_elements)
2554 if (graphic != el2img(EL_EMPTY))
2555 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2557 DrawGraphicThruMask(x, y, graphic, frame);
2561 DrawGraphic(x, y, graphic, frame);
2565 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2567 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2570 void DrawScreenElement(int x, int y, int element)
2572 int mask_mode = NO_MASKING;
2574 if (game.use_masked_elements)
2576 int lx = LEVELX(x), ly = LEVELY(y);
2578 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2580 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2582 mask_mode = USE_MASKING;
2586 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2587 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2590 void DrawLevelElement(int x, int y, int element)
2592 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2593 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2596 void DrawScreenField(int x, int y)
2598 int lx = LEVELX(x), ly = LEVELY(y);
2599 int element, content;
2601 if (!IN_LEV_FIELD(lx, ly))
2603 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2606 element = getBorderElement(lx, ly);
2608 DrawScreenElement(x, y, element);
2613 element = Tile[lx][ly];
2614 content = Store[lx][ly];
2616 if (IS_MOVING(lx, ly))
2618 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2619 boolean cut_mode = NO_CUTTING;
2621 if (element == EL_QUICKSAND_EMPTYING ||
2622 element == EL_QUICKSAND_FAST_EMPTYING ||
2623 element == EL_MAGIC_WALL_EMPTYING ||
2624 element == EL_BD_MAGIC_WALL_EMPTYING ||
2625 element == EL_DC_MAGIC_WALL_EMPTYING ||
2626 element == EL_AMOEBA_DROPPING)
2627 cut_mode = CUT_ABOVE;
2628 else if (element == EL_QUICKSAND_FILLING ||
2629 element == EL_QUICKSAND_FAST_FILLING ||
2630 element == EL_MAGIC_WALL_FILLING ||
2631 element == EL_BD_MAGIC_WALL_FILLING ||
2632 element == EL_DC_MAGIC_WALL_FILLING)
2633 cut_mode = CUT_BELOW;
2635 if (cut_mode == CUT_ABOVE)
2636 DrawScreenElement(x, y, element);
2638 DrawScreenElement(x, y, EL_EMPTY);
2640 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2642 int dir = MovDir[lx][ly];
2643 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2644 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2646 if (IN_SCR_FIELD(newx, newy))
2647 DrawScreenElement(newx, newy, EL_EMPTY);
2651 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2652 else if (cut_mode == NO_CUTTING)
2653 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2656 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2658 if (cut_mode == CUT_BELOW &&
2659 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2660 DrawLevelElement(lx, ly + 1, element);
2663 if (content == EL_ACID)
2665 int dir = MovDir[lx][ly];
2666 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2667 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2669 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2671 // prevent target field from being drawn again (but without masking)
2672 // (this would happen if target field is scanned after moving element)
2673 Stop[newlx][newly] = TRUE;
2676 else if (IS_BLOCKED(lx, ly))
2681 boolean cut_mode = NO_CUTTING;
2682 int element_old, content_old;
2684 Blocked2Moving(lx, ly, &oldx, &oldy);
2687 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2688 MovDir[oldx][oldy] == MV_RIGHT);
2690 element_old = Tile[oldx][oldy];
2691 content_old = Store[oldx][oldy];
2693 if (element_old == EL_QUICKSAND_EMPTYING ||
2694 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2695 element_old == EL_MAGIC_WALL_EMPTYING ||
2696 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2697 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2698 element_old == EL_AMOEBA_DROPPING)
2699 cut_mode = CUT_ABOVE;
2701 DrawScreenElement(x, y, EL_EMPTY);
2704 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2706 else if (cut_mode == NO_CUTTING)
2707 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2710 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2713 else if (IS_DRAWABLE(element))
2714 DrawScreenElement(x, y, element);
2716 DrawScreenElement(x, y, EL_EMPTY);
2719 void DrawLevelField(int x, int y)
2721 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2722 DrawScreenField(SCREENX(x), SCREENY(y));
2723 else if (IS_MOVING(x, y))
2727 Moving2Blocked(x, y, &newx, &newy);
2728 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2729 DrawScreenField(SCREENX(newx), SCREENY(newy));
2731 else if (IS_BLOCKED(x, y))
2735 Blocked2Moving(x, y, &oldx, &oldy);
2736 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2737 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2741 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2742 int (*el2img_function)(int), boolean masked,
2743 int element_bits_draw)
2745 int element_base = map_mm_wall_element(element);
2746 int element_bits = (IS_DF_WALL(element) ?
2747 element - EL_DF_WALL_START :
2748 IS_MM_WALL(element) ?
2749 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2750 int graphic = el2img_function(element_base);
2751 int tilesize_draw = tilesize / 2;
2756 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2758 for (i = 0; i < 4; i++)
2760 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2761 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2763 if (!(element_bits_draw & (1 << i)))
2766 if (element_bits & (1 << i))
2769 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2770 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2772 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2773 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2778 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2779 tilesize_draw, tilesize_draw);
2784 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2785 boolean masked, int element_bits_draw)
2787 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2788 element, tilesize, el2edimg, masked, element_bits_draw);
2791 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2792 int (*el2img_function)(int))
2794 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2798 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2801 if (IS_MM_WALL(element))
2803 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2804 element, tilesize, el2edimg, masked, 0x000f);
2808 int graphic = el2edimg(element);
2811 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2813 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2817 void DrawSizedElement(int x, int y, int element, int tilesize)
2819 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2822 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2824 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2827 void DrawMiniElement(int x, int y, int element)
2831 graphic = el2edimg(element);
2832 DrawMiniGraphic(x, y, graphic);
2835 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2838 int x = sx + scroll_x, y = sy + scroll_y;
2840 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2841 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2842 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2843 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2845 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2848 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2850 int x = sx + scroll_x, y = sy + scroll_y;
2852 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2853 DrawMiniElement(sx, sy, EL_EMPTY);
2854 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2855 DrawMiniElement(sx, sy, Tile[x][y]);
2857 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2860 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2861 int x, int y, int xsize, int ysize,
2862 int tile_width, int tile_height)
2866 int dst_x = startx + x * tile_width;
2867 int dst_y = starty + y * tile_height;
2868 int width = graphic_info[graphic].width;
2869 int height = graphic_info[graphic].height;
2870 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2871 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2872 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2873 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2874 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2875 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2876 boolean draw_masked = graphic_info[graphic].draw_masked;
2878 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2880 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2882 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2886 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2887 inner_sx + (x - 1) * tile_width % inner_width);
2888 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2889 inner_sy + (y - 1) * tile_height % inner_height);
2892 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2895 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2899 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2900 int x, int y, int xsize, int ysize,
2903 int font_width = getFontWidth(font_nr);
2904 int font_height = getFontHeight(font_nr);
2906 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2907 font_width, font_height);
2910 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2912 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2913 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2914 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2915 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2916 boolean no_delay = (tape.warp_forward);
2917 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2918 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2919 DelayCounter anim_delay = { anim_delay_value };
2920 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2921 int font_width = getFontWidth(font_nr);
2922 int font_height = getFontHeight(font_nr);
2923 int max_xsize = level.envelope[envelope_nr].xsize;
2924 int max_ysize = level.envelope[envelope_nr].ysize;
2925 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2926 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2927 int xend = max_xsize;
2928 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2929 int xstep = (xstart < xend ? 1 : 0);
2930 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2932 int end = MAX(xend - xstart, yend - ystart);
2935 for (i = start; i <= end; i++)
2937 int last_frame = end; // last frame of this "for" loop
2938 int x = xstart + i * xstep;
2939 int y = ystart + i * ystep;
2940 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2941 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2942 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2943 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2946 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2948 BlitScreenToBitmap(backbuffer);
2950 SetDrawtoField(DRAW_TO_BACKBUFFER);
2952 for (yy = 0; yy < ysize; yy++)
2953 for (xx = 0; xx < xsize; xx++)
2954 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2956 DrawTextArea(sx + font_width, sy + font_height,
2957 level.envelope[envelope_nr].text, font_nr, max_xsize,
2958 xsize - 2, ysize - 2, 0, mask_mode,
2959 level.envelope[envelope_nr].autowrap,
2960 level.envelope[envelope_nr].centered, FALSE);
2962 redraw_mask |= REDRAW_FIELD;
2965 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2968 ClearAutoRepeatKeyEvents();
2971 void ShowEnvelope(int envelope_nr)
2973 int element = EL_ENVELOPE_1 + envelope_nr;
2974 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2975 int sound_opening = element_info[element].sound[ACTION_OPENING];
2976 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2977 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2978 boolean no_delay = (tape.warp_forward);
2979 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2980 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2981 int anim_mode = graphic_info[graphic].anim_mode;
2982 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2983 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2984 boolean overlay_enabled = GetOverlayEnabled();
2986 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2988 SetOverlayEnabled(FALSE);
2991 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2993 if (anim_mode == ANIM_DEFAULT)
2994 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2996 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2999 Delay_WithScreenUpdates(wait_delay_value);
3001 WaitForEventToContinue();
3004 SetOverlayEnabled(overlay_enabled);
3006 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3008 if (anim_mode != ANIM_NONE)
3009 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3011 if (anim_mode == ANIM_DEFAULT)
3012 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3014 game.envelope_active = FALSE;
3016 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3018 redraw_mask |= REDRAW_FIELD;
3022 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3023 int xsize, int ysize)
3025 if (!global.use_envelope_request)
3028 if (request.bitmap == NULL ||
3029 xsize > request.xsize ||
3030 ysize > request.ysize)
3032 if (request.bitmap != NULL)
3033 FreeBitmap(request.bitmap);
3035 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3037 SDL_Surface *surface = request.bitmap->surface;
3039 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3040 Fail("SDLGetNativeSurface() failed");
3043 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3045 // create masked surface for request bitmap, if needed
3046 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3048 SDL_Surface *surface = request.bitmap->surface;
3049 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3051 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3052 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3053 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3056 SDLFreeBitmapTextures(request.bitmap);
3057 SDLCreateBitmapTextures(request.bitmap);
3059 ResetBitmapAlpha(request.bitmap);
3061 // set envelope request run-time values
3064 request.xsize = xsize;
3065 request.ysize = ysize;
3068 void DrawEnvelopeRequestToScreen(int drawing_target)
3070 if (global.use_envelope_request &&
3071 game.request_active &&
3072 drawing_target == DRAW_TO_SCREEN)
3074 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3075 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3076 request.sx, request.sy);
3078 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3079 request.sx, request.sy);
3083 static void setRequestBasePosition(int *x, int *y)
3085 int sx_base, sy_base;
3087 if (request.x != -1)
3088 sx_base = request.x;
3089 else if (request.align == ALIGN_LEFT)
3091 else if (request.align == ALIGN_RIGHT)
3092 sx_base = SX + SXSIZE;
3094 sx_base = SX + SXSIZE / 2;
3096 if (request.y != -1)
3097 sy_base = request.y;
3098 else if (request.valign == VALIGN_TOP)
3100 else if (request.valign == VALIGN_BOTTOM)
3101 sy_base = SY + SYSIZE;
3103 sy_base = SY + SYSIZE / 2;
3109 static void setRequestPositionExt(int *x, int *y, int width, int height,
3110 boolean add_border_size)
3112 int border_size = request.border_size;
3113 int sx_base, sy_base;
3116 setRequestBasePosition(&sx_base, &sy_base);
3118 if (request.align == ALIGN_LEFT)
3120 else if (request.align == ALIGN_RIGHT)
3121 sx = sx_base - width;
3123 sx = sx_base - width / 2;
3125 if (request.valign == VALIGN_TOP)
3127 else if (request.valign == VALIGN_BOTTOM)
3128 sy = sy_base - height;
3130 sy = sy_base - height / 2;
3132 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3133 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3135 if (add_border_size)
3145 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3147 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3150 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3152 char *text_final = text;
3153 char *text_door_style = NULL;
3154 int graphic = IMG_BACKGROUND_REQUEST;
3155 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3156 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3157 int font_nr = FONT_REQUEST;
3158 int font_width = getFontWidth(font_nr);
3159 int font_height = getFontHeight(font_nr);
3160 int border_size = request.border_size;
3161 int line_spacing = request.line_spacing;
3162 int line_height = font_height + line_spacing;
3163 int max_text_width = request.width - 2 * border_size;
3164 int max_text_height = request.height - 2 * border_size;
3165 int line_length = max_text_width / font_width;
3166 int max_lines = max_text_height / line_height;
3167 int text_width = line_length * font_width;
3168 int sx_offset = border_size;
3169 int sy_offset = border_size;
3171 // force DOOR font inside door area
3172 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3174 if (request.centered)
3175 sx_offset = (request.width - text_width) / 2;
3177 if (request.wrap_single_words && !request.autowrap)
3179 char *src_text_ptr, *dst_text_ptr;
3181 if (maxWordLengthInRequestString(text) > line_length)
3183 font_nr = FONT_REQUEST_NARROW;
3184 font_width = getFontWidth(font_nr);
3185 line_length = max_text_width / font_width;
3188 text_door_style = checked_malloc(2 * strlen(text) + 1);
3190 src_text_ptr = text;
3191 dst_text_ptr = text_door_style;
3193 while (*src_text_ptr)
3195 if (*src_text_ptr == ' ' ||
3196 *src_text_ptr == '?' ||
3197 *src_text_ptr == '!')
3198 *dst_text_ptr++ = '\n';
3200 if (*src_text_ptr != ' ')
3201 *dst_text_ptr++ = *src_text_ptr;
3206 *dst_text_ptr = '\0';
3208 text_final = text_door_style;
3211 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3212 line_length, -1, max_lines, line_spacing, mask_mode,
3213 request.autowrap, request.centered, FALSE);
3215 if (text_door_style)
3216 free(text_door_style);
3221 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3223 DrawBuffer *drawto_last = drawto;
3224 int graphic = IMG_BACKGROUND_REQUEST;
3225 int width = request.width;
3226 int height = request.height;
3227 int tile_size = MAX(request.step_offset, 1);
3228 int x_steps = width / tile_size;
3229 int y_steps = height / tile_size;
3233 setRequestPosition(&sx, &sy, FALSE);
3235 // draw complete envelope request to temporary bitmap
3236 drawto = bitmap_db_store_1;
3238 ClearRectangle(drawto, sx, sy, width, height);
3240 for (y = 0; y < y_steps; y++)
3241 for (x = 0; x < x_steps; x++)
3242 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3243 x, y, x_steps, y_steps,
3244 tile_size, tile_size);
3246 // write text for request
3247 DrawEnvelopeRequestText(sx, sy, text);
3249 MapToolButtons(req_state);
3251 // restore pointer to drawing buffer
3252 drawto = drawto_last;
3254 // prepare complete envelope request from temporary bitmap
3255 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3258 static void AnimateEnvelopeRequest(int anim_mode, int action)
3260 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3261 int delay_value_normal = request.step_delay;
3262 int delay_value_fast = delay_value_normal / 2;
3263 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3264 boolean no_delay = (tape.warp_forward);
3265 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3266 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3267 DelayCounter anim_delay = { anim_delay_value };
3269 int tile_size = MAX(request.step_offset, 1);
3270 int max_xsize = request.width / tile_size;
3271 int max_ysize = request.height / tile_size;
3272 int max_xsize_inner = max_xsize - 2;
3273 int max_ysize_inner = max_ysize - 2;
3275 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3276 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3277 int xend = max_xsize_inner;
3278 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3279 int xstep = (xstart < xend ? 1 : 0);
3280 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3282 int end = MAX(xend - xstart, yend - ystart);
3285 if (setup.quick_doors)
3292 for (i = start; i <= end; i++)
3294 int last_frame = end; // last frame of this "for" loop
3295 int x = xstart + i * xstep;
3296 int y = ystart + i * ystep;
3297 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3298 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3299 int xsize_size_left = (xsize - 1) * tile_size;
3300 int ysize_size_top = (ysize - 1) * tile_size;
3301 int max_xsize_pos = (max_xsize - 1) * tile_size;
3302 int max_ysize_pos = (max_ysize - 1) * tile_size;
3303 int width = xsize * tile_size;
3304 int height = ysize * tile_size;
3310 HandleGameActions();
3312 setRequestPosition(&src_x, &src_y, FALSE);
3313 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3315 for (yy = 0; yy < 2; yy++)
3317 for (xx = 0; xx < 2; xx++)
3319 int src_xx = src_x + xx * max_xsize_pos;
3320 int src_yy = src_y + yy * max_ysize_pos;
3321 int dst_xx = dst_x + xx * xsize_size_left;
3322 int dst_yy = dst_y + yy * ysize_size_top;
3323 int xx_size = (xx ? tile_size : xsize_size_left);
3324 int yy_size = (yy ? tile_size : ysize_size_top);
3326 // draw partial (animated) envelope request to temporary bitmap
3327 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3328 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3332 // prepare partial (animated) envelope request from temporary bitmap
3333 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3336 redraw_mask |= REDRAW_FIELD;
3340 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3343 ClearAutoRepeatKeyEvents();
3346 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3348 int graphic = IMG_BACKGROUND_REQUEST;
3349 int sound_opening = SND_REQUEST_OPENING;
3350 int sound_closing = SND_REQUEST_CLOSING;
3351 int anim_mode_1 = request.anim_mode; // (higher priority)
3352 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3353 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3354 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3355 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3357 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3359 if (action == ACTION_OPENING)
3361 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3363 if (anim_mode == ANIM_DEFAULT)
3364 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3366 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3370 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3372 if (anim_mode != ANIM_NONE)
3373 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3375 if (anim_mode == ANIM_DEFAULT)
3376 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3379 game.envelope_active = FALSE;
3382 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3384 if (IS_MM_WALL(element))
3386 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3392 int graphic = el2preimg(element);
3394 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3395 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3400 void DrawLevel(int draw_background_mask)
3404 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3405 SetDrawBackgroundMask(draw_background_mask);
3409 for (x = BX1; x <= BX2; x++)
3410 for (y = BY1; y <= BY2; y++)
3411 DrawScreenField(x, y);
3413 redraw_mask |= REDRAW_FIELD;
3416 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3421 for (x = 0; x < size_x; x++)
3422 for (y = 0; y < size_y; y++)
3423 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3425 redraw_mask |= REDRAW_FIELD;
3428 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3432 for (x = 0; x < size_x; x++)
3433 for (y = 0; y < size_y; y++)
3434 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3436 redraw_mask |= REDRAW_FIELD;
3439 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3441 boolean show_level_border = (BorderElement != EL_EMPTY);
3442 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3443 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3444 int tile_size = preview.tile_size;
3445 int preview_width = preview.xsize * tile_size;
3446 int preview_height = preview.ysize * tile_size;
3447 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3448 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3449 int real_preview_width = real_preview_xsize * tile_size;
3450 int real_preview_height = real_preview_ysize * tile_size;
3451 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3452 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3455 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3458 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3460 dst_x += (preview_width - real_preview_width) / 2;
3461 dst_y += (preview_height - real_preview_height) / 2;
3463 for (x = 0; x < real_preview_xsize; x++)
3465 for (y = 0; y < real_preview_ysize; y++)
3467 int lx = from_x + x + (show_level_border ? -1 : 0);
3468 int ly = from_y + y + (show_level_border ? -1 : 0);
3469 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3470 getBorderElement(lx, ly));
3472 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3473 element, tile_size);
3477 redraw_mask |= REDRAW_FIELD;
3480 #define MICROLABEL_EMPTY 0
3481 #define MICROLABEL_LEVEL_NAME 1
3482 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3483 #define MICROLABEL_LEVEL_AUTHOR 3
3484 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3485 #define MICROLABEL_IMPORTED_FROM 5
3486 #define MICROLABEL_IMPORTED_BY_HEAD 6
3487 #define MICROLABEL_IMPORTED_BY 7
3489 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3491 int max_text_width = SXSIZE;
3492 int font_width = getFontWidth(font_nr);
3494 if (pos->align == ALIGN_CENTER)
3495 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3496 else if (pos->align == ALIGN_RIGHT)
3497 max_text_width = pos->x;
3499 max_text_width = SXSIZE - pos->x;
3501 return max_text_width / font_width;
3504 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3506 char label_text[MAX_OUTPUT_LINESIZE + 1];
3507 int max_len_label_text;
3508 int font_nr = pos->font;
3511 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3514 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3515 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3516 mode == MICROLABEL_IMPORTED_BY_HEAD)
3517 font_nr = pos->font_alt;
3519 max_len_label_text = getMaxTextLength(pos, font_nr);
3521 if (pos->size != -1)
3522 max_len_label_text = pos->size;
3524 for (i = 0; i < max_len_label_text; i++)
3525 label_text[i] = ' ';
3526 label_text[max_len_label_text] = '\0';
3528 if (strlen(label_text) > 0)
3529 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3532 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3533 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3534 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3535 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3536 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3537 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3538 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3539 max_len_label_text);
3540 label_text[max_len_label_text] = '\0';
3542 if (strlen(label_text) > 0)
3543 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3545 redraw_mask |= REDRAW_FIELD;
3548 static void DrawPreviewLevelLabel(int mode)
3550 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3553 static void DrawPreviewLevelInfo(int mode)
3555 if (mode == MICROLABEL_LEVEL_NAME)
3556 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3557 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3558 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3561 static void DrawPreviewLevelExt(boolean restart)
3563 static DelayCounter scroll_delay = { 0 };
3564 static DelayCounter label_delay = { 0 };
3565 static int from_x, from_y, scroll_direction;
3566 static int label_state, label_counter;
3567 boolean show_level_border = (BorderElement != EL_EMPTY);
3568 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3569 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3571 scroll_delay.value = preview.step_delay;
3572 label_delay.value = MICROLEVEL_LABEL_DELAY;
3579 if (preview.anim_mode == ANIM_CENTERED)
3581 if (level_xsize > preview.xsize)
3582 from_x = (level_xsize - preview.xsize) / 2;
3583 if (level_ysize > preview.ysize)
3584 from_y = (level_ysize - preview.ysize) / 2;
3587 from_x += preview.xoffset;
3588 from_y += preview.yoffset;
3590 scroll_direction = MV_RIGHT;
3594 DrawPreviewLevelPlayfield(from_x, from_y);
3595 DrawPreviewLevelLabel(label_state);
3597 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3598 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3600 // initialize delay counters
3601 ResetDelayCounter(&scroll_delay);
3602 ResetDelayCounter(&label_delay);
3604 if (leveldir_current->name)
3606 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3607 char label_text[MAX_OUTPUT_LINESIZE + 1];
3608 int font_nr = pos->font;
3609 int max_len_label_text = getMaxTextLength(pos, font_nr);
3611 if (pos->size != -1)
3612 max_len_label_text = pos->size;
3614 strncpy(label_text, leveldir_current->name, max_len_label_text);
3615 label_text[max_len_label_text] = '\0';
3617 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3618 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3624 // scroll preview level, if needed
3625 if (preview.anim_mode != ANIM_NONE &&
3626 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3627 DelayReached(&scroll_delay))
3629 switch (scroll_direction)
3634 from_x -= preview.step_offset;
3635 from_x = (from_x < 0 ? 0 : from_x);
3638 scroll_direction = MV_UP;
3642 if (from_x < level_xsize - preview.xsize)
3644 from_x += preview.step_offset;
3645 from_x = (from_x > level_xsize - preview.xsize ?
3646 level_xsize - preview.xsize : from_x);
3649 scroll_direction = MV_DOWN;
3655 from_y -= preview.step_offset;
3656 from_y = (from_y < 0 ? 0 : from_y);
3659 scroll_direction = MV_RIGHT;
3663 if (from_y < level_ysize - preview.ysize)
3665 from_y += preview.step_offset;
3666 from_y = (from_y > level_ysize - preview.ysize ?
3667 level_ysize - preview.ysize : from_y);
3670 scroll_direction = MV_LEFT;
3677 DrawPreviewLevelPlayfield(from_x, from_y);
3680 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3681 // redraw micro level label, if needed
3682 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3683 !strEqual(level.author, ANONYMOUS_NAME) &&
3684 !strEqual(level.author, leveldir_current->name) &&
3685 DelayReached(&label_delay))
3687 int max_label_counter = 23;
3689 if (leveldir_current->imported_from != NULL &&
3690 strlen(leveldir_current->imported_from) > 0)
3691 max_label_counter += 14;
3692 if (leveldir_current->imported_by != NULL &&
3693 strlen(leveldir_current->imported_by) > 0)
3694 max_label_counter += 14;
3696 label_counter = (label_counter + 1) % max_label_counter;
3697 label_state = (label_counter >= 0 && label_counter <= 7 ?
3698 MICROLABEL_LEVEL_NAME :
3699 label_counter >= 9 && label_counter <= 12 ?
3700 MICROLABEL_LEVEL_AUTHOR_HEAD :
3701 label_counter >= 14 && label_counter <= 21 ?
3702 MICROLABEL_LEVEL_AUTHOR :
3703 label_counter >= 23 && label_counter <= 26 ?
3704 MICROLABEL_IMPORTED_FROM_HEAD :
3705 label_counter >= 28 && label_counter <= 35 ?
3706 MICROLABEL_IMPORTED_FROM :
3707 label_counter >= 37 && label_counter <= 40 ?
3708 MICROLABEL_IMPORTED_BY_HEAD :
3709 label_counter >= 42 && label_counter <= 49 ?
3710 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3712 if (leveldir_current->imported_from == NULL &&
3713 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3714 label_state == MICROLABEL_IMPORTED_FROM))
3715 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3716 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3718 DrawPreviewLevelLabel(label_state);
3722 void DrawPreviewPlayers(void)
3724 if (game_status != GAME_MODE_MAIN)
3727 // do not draw preview players if level preview redefined, but players aren't
3728 if (preview.redefined && !menu.main.preview_players.redefined)
3731 boolean player_found[MAX_PLAYERS];
3732 int num_players = 0;
3735 for (i = 0; i < MAX_PLAYERS; i++)
3736 player_found[i] = FALSE;
3738 // check which players can be found in the level (simple approach)
3739 for (x = 0; x < lev_fieldx; x++)
3741 for (y = 0; y < lev_fieldy; y++)
3743 int element = level.field[x][y];
3745 if (IS_PLAYER_ELEMENT(element))
3747 int player_nr = GET_PLAYER_NR(element);
3749 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3751 if (!player_found[player_nr])
3754 player_found[player_nr] = TRUE;
3759 struct TextPosInfo *pos = &menu.main.preview_players;
3760 int tile_size = pos->tile_size;
3761 int border_size = pos->border_size;
3762 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3763 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3764 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3765 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3766 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3767 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3768 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3769 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3770 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3771 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3772 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3773 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3775 // clear area in which the players will be drawn
3776 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3777 max_players_width, max_players_height);
3779 if (!network.enabled && !setup.team_mode)
3782 // only draw players if level is suited for team mode
3783 if (num_players < 2)
3786 // draw all players that were found in the level
3787 for (i = 0; i < MAX_PLAYERS; i++)
3789 if (player_found[i])
3791 int graphic = el2img(EL_PLAYER_1 + i);
3793 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3795 xpos += player_xoffset;
3796 ypos += player_yoffset;
3801 void DrawPreviewLevelInitial(void)
3803 DrawPreviewLevelExt(TRUE);
3804 DrawPreviewPlayers();
3807 void DrawPreviewLevelAnimation(void)
3809 DrawPreviewLevelExt(FALSE);
3812 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3813 int border_size, int font_nr)
3815 int graphic = el2img(EL_PLAYER_1 + player_nr);
3816 int font_height = getFontHeight(font_nr);
3817 int player_height = MAX(tile_size, font_height);
3818 int xoffset_text = tile_size + border_size;
3819 int yoffset_text = (player_height - font_height) / 2;
3820 int yoffset_graphic = (player_height - tile_size) / 2;
3821 char *player_name = getNetworkPlayerName(player_nr + 1);
3823 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3825 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3828 static void DrawNetworkPlayersExt(boolean force)
3830 if (game_status != GAME_MODE_MAIN)
3833 if (!network.connected && !force)
3836 // do not draw network players if level preview redefined, but players aren't
3837 if (preview.redefined && !menu.main.network_players.redefined)
3840 int num_players = 0;
3843 for (i = 0; i < MAX_PLAYERS; i++)
3844 if (stored_player[i].connected_network)
3847 struct TextPosInfo *pos = &menu.main.network_players;
3848 int tile_size = pos->tile_size;
3849 int border_size = pos->border_size;
3850 int xoffset_text = tile_size + border_size;
3851 int font_nr = pos->font;
3852 int font_width = getFontWidth(font_nr);
3853 int font_height = getFontHeight(font_nr);
3854 int player_height = MAX(tile_size, font_height);
3855 int player_yoffset = player_height + border_size;
3856 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3857 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3858 int all_players_height = num_players * player_yoffset - border_size;
3859 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3860 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3861 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3863 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3864 max_players_width, max_players_height);
3866 // first draw local network player ...
3867 for (i = 0; i < MAX_PLAYERS; i++)
3869 if (stored_player[i].connected_network &&
3870 stored_player[i].connected_locally)
3872 char *player_name = getNetworkPlayerName(i + 1);
3873 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3874 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3876 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3878 ypos += player_yoffset;
3882 // ... then draw all other network players
3883 for (i = 0; i < MAX_PLAYERS; i++)
3885 if (stored_player[i].connected_network &&
3886 !stored_player[i].connected_locally)
3888 char *player_name = getNetworkPlayerName(i + 1);
3889 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3890 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3892 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3894 ypos += player_yoffset;
3899 void DrawNetworkPlayers(void)
3901 DrawNetworkPlayersExt(FALSE);
3904 void ClearNetworkPlayers(void)
3906 DrawNetworkPlayersExt(TRUE);
3909 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3910 int graphic, int lx, int ly,
3913 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3915 if (mask_mode == USE_MASKING)
3916 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3918 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3921 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3922 int graphic, int sync_frame, int mask_mode)
3924 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3926 if (mask_mode == USE_MASKING)
3927 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3929 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3932 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3933 int graphic, int sync_frame, int tilesize,
3936 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3938 if (mask_mode == USE_MASKING)
3939 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3941 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3944 static void DrawGraphicAnimation(int x, int y, int graphic)
3946 int lx = LEVELX(x), ly = LEVELY(y);
3947 int mask_mode = NO_MASKING;
3949 if (!IN_SCR_FIELD(x, y))
3952 if (game.use_masked_elements)
3954 if (Tile[lx][ly] != EL_EMPTY)
3956 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3958 mask_mode = USE_MASKING;
3962 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3963 graphic, lx, ly, mask_mode);
3965 MarkTileDirty(x, y);
3968 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3970 int lx = LEVELX(x), ly = LEVELY(y);
3971 int mask_mode = NO_MASKING;
3973 if (!IN_SCR_FIELD(x, y))
3976 if (game.use_masked_elements)
3978 if (Tile[lx][ly] != EL_EMPTY)
3980 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3982 mask_mode = USE_MASKING;
3986 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3987 graphic, lx, ly, mask_mode);
3989 MarkTileDirty(x, y);
3992 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3994 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3997 void DrawLevelElementAnimation(int x, int y, int element)
3999 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4001 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4004 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4006 int sx = SCREENX(x), sy = SCREENY(y);
4008 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4011 if (Tile[x][y] == EL_EMPTY)
4012 graphic = el2img(GfxElementEmpty[x][y]);
4014 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4017 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4020 DrawGraphicAnimation(sx, sy, graphic);
4023 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4024 DrawLevelFieldCrumbled(x, y);
4026 if (GFX_CRUMBLED(Tile[x][y]))
4027 DrawLevelFieldCrumbled(x, y);
4031 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4033 int sx = SCREENX(x), sy = SCREENY(y);
4036 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4039 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4041 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4044 DrawGraphicAnimation(sx, sy, graphic);
4046 if (GFX_CRUMBLED(element))
4047 DrawLevelFieldCrumbled(x, y);
4050 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4052 if (player->use_murphy)
4054 // this works only because currently only one player can be "murphy" ...
4055 static int last_horizontal_dir = MV_LEFT;
4056 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4058 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4059 last_horizontal_dir = move_dir;
4061 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4063 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4065 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4071 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4074 static boolean equalGraphics(int graphic1, int graphic2)
4076 struct GraphicInfo *g1 = &graphic_info[graphic1];
4077 struct GraphicInfo *g2 = &graphic_info[graphic2];
4079 return (g1->bitmap == g2->bitmap &&
4080 g1->src_x == g2->src_x &&
4081 g1->src_y == g2->src_y &&
4082 g1->anim_frames == g2->anim_frames &&
4083 g1->anim_delay == g2->anim_delay &&
4084 g1->anim_mode == g2->anim_mode);
4087 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4091 DRAW_PLAYER_STAGE_INIT = 0,
4092 DRAW_PLAYER_STAGE_LAST_FIELD,
4093 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4094 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4095 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4096 DRAW_PLAYER_STAGE_PLAYER,
4098 DRAW_PLAYER_STAGE_PLAYER,
4099 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4101 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4102 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4104 NUM_DRAW_PLAYER_STAGES
4107 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4109 static int static_last_player_graphic[MAX_PLAYERS];
4110 static int static_last_player_frame[MAX_PLAYERS];
4111 static boolean static_player_is_opaque[MAX_PLAYERS];
4112 static boolean draw_player[MAX_PLAYERS];
4113 int pnr = player->index_nr;
4115 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4117 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4118 static_last_player_frame[pnr] = player->Frame;
4119 static_player_is_opaque[pnr] = FALSE;
4121 draw_player[pnr] = TRUE;
4124 if (!draw_player[pnr])
4128 if (!IN_LEV_FIELD(player->jx, player->jy))
4130 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4131 Debug("draw:DrawPlayerExt", "This should never happen!");
4133 draw_player[pnr] = FALSE;
4139 int last_player_graphic = static_last_player_graphic[pnr];
4140 int last_player_frame = static_last_player_frame[pnr];
4141 boolean player_is_opaque = static_player_is_opaque[pnr];
4143 int jx = player->jx;
4144 int jy = player->jy;
4145 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4146 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4147 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4148 int last_jx = (player->is_moving ? jx - dx : jx);
4149 int last_jy = (player->is_moving ? jy - dy : jy);
4150 int next_jx = jx + dx;
4151 int next_jy = jy + dy;
4152 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4153 int sx = SCREENX(jx);
4154 int sy = SCREENY(jy);
4155 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4156 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4157 int element = Tile[jx][jy];
4158 int last_element = Tile[last_jx][last_jy];
4159 int action = (player->is_pushing ? ACTION_PUSHING :
4160 player->is_digging ? ACTION_DIGGING :
4161 player->is_collecting ? ACTION_COLLECTING :
4162 player->is_moving ? ACTION_MOVING :
4163 player->is_snapping ? ACTION_SNAPPING :
4164 player->is_dropping ? ACTION_DROPPING :
4165 player->is_waiting ? player->action_waiting :
4168 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4170 // ------------------------------------------------------------------------
4171 // initialize drawing the player
4172 // ------------------------------------------------------------------------
4174 draw_player[pnr] = FALSE;
4176 // GfxElement[][] is set to the element the player is digging or collecting;
4177 // remove also for off-screen player if the player is not moving anymore
4178 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4179 GfxElement[jx][jy] = EL_UNDEFINED;
4181 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4184 if (element == EL_EXPLOSION)
4187 InitPlayerGfxAnimation(player, action, move_dir);
4189 draw_player[pnr] = TRUE;
4191 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4193 // ------------------------------------------------------------------------
4194 // draw things in the field the player is leaving, if needed
4195 // ------------------------------------------------------------------------
4197 if (!IN_SCR_FIELD(sx, sy))
4198 draw_player[pnr] = FALSE;
4200 if (!player->is_moving)
4203 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4205 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4207 if (last_element == EL_DYNAMITE_ACTIVE ||
4208 last_element == EL_EM_DYNAMITE_ACTIVE ||
4209 last_element == EL_SP_DISK_RED_ACTIVE)
4210 DrawDynamite(last_jx, last_jy);
4212 DrawLevelFieldThruMask(last_jx, last_jy);
4214 else if (last_element == EL_DYNAMITE_ACTIVE ||
4215 last_element == EL_EM_DYNAMITE_ACTIVE ||
4216 last_element == EL_SP_DISK_RED_ACTIVE)
4217 DrawDynamite(last_jx, last_jy);
4219 DrawLevelField(last_jx, last_jy);
4221 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4223 // ------------------------------------------------------------------------
4224 // draw things behind the player, if needed
4225 // ------------------------------------------------------------------------
4229 DrawLevelElement(jx, jy, Back[jx][jy]);
4234 if (IS_ACTIVE_BOMB(element))
4236 DrawLevelElement(jx, jy, EL_EMPTY);
4241 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4243 int old_element = GfxElement[jx][jy];
4244 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4245 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4247 if (GFX_CRUMBLED(old_element))
4248 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4250 DrawScreenGraphic(sx, sy, old_graphic, frame);
4252 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4253 static_player_is_opaque[pnr] = TRUE;
4257 GfxElement[jx][jy] = EL_UNDEFINED;
4259 // make sure that pushed elements are drawn with correct frame rate
4260 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4262 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4263 GfxFrame[jx][jy] = player->StepFrame;
4265 DrawLevelField(jx, jy);
4268 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4270 // ------------------------------------------------------------------------
4271 // draw things the player is pushing, if needed
4272 // ------------------------------------------------------------------------
4274 if (!player->is_pushing || !player->is_moving)
4277 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4280 int gfx_frame = GfxFrame[jx][jy];
4282 if (!IS_MOVING(jx, jy)) // push movement already finished
4284 element = Tile[next_jx][next_jy];
4285 gfx_frame = GfxFrame[next_jx][next_jy];
4288 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4289 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4290 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4292 // draw background element under pushed element (like the Sokoban field)
4293 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4295 // this allows transparent pushing animation over non-black background
4298 DrawLevelElement(jx, jy, Back[jx][jy]);
4300 DrawLevelElement(jx, jy, EL_EMPTY);
4303 if (Back[next_jx][next_jy])
4304 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4306 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4308 int px = SCREENX(jx), py = SCREENY(jy);
4309 int pxx = (TILEX - ABS(sxx)) * dx;
4310 int pyy = (TILEY - ABS(syy)) * dy;
4313 // do not draw (EM style) pushing animation when pushing is finished
4314 // (two-tile animations usually do not contain start and end frame)
4315 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4316 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4318 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4320 // masked drawing is needed for EMC style (double) movement graphics
4321 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4322 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4325 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4327 // ------------------------------------------------------------------------
4328 // draw player himself
4329 // ------------------------------------------------------------------------
4331 int graphic = getPlayerGraphic(player, move_dir);
4333 // in the case of changed player action or direction, prevent the current
4334 // animation frame from being restarted for identical animations
4335 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4336 player->Frame = last_player_frame;
4338 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4340 if (player_is_opaque)
4341 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4343 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4345 if (SHIELD_ON(player))
4347 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4348 IMG_SHIELD_NORMAL_ACTIVE);
4349 frame = getGraphicAnimationFrame(graphic, -1);
4351 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4354 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4356 // ------------------------------------------------------------------------
4357 // draw things in front of player (active dynamite or dynabombs)
4358 // ------------------------------------------------------------------------
4360 if (IS_ACTIVE_BOMB(element))
4362 int graphic = el2img(element);
4363 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4365 if (game.emulation == EMU_SUPAPLEX)
4366 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4368 DrawGraphicThruMask(sx, sy, graphic, frame);
4371 if (player_is_moving && last_element == EL_EXPLOSION)
4373 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4374 GfxElement[last_jx][last_jy] : EL_EMPTY);
4375 int graphic = el_act2img(element, ACTION_EXPLODING);
4376 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4377 int phase = ExplodePhase[last_jx][last_jy] - 1;
4378 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4381 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4384 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4386 // ------------------------------------------------------------------------
4387 // draw elements the player is just walking/passing through/under
4388 // ------------------------------------------------------------------------
4390 if (player_is_moving)
4392 // handle the field the player is leaving ...
4393 if (IS_ACCESSIBLE_INSIDE(last_element))
4394 DrawLevelField(last_jx, last_jy);
4395 else if (IS_ACCESSIBLE_UNDER(last_element))
4396 DrawLevelFieldThruMask(last_jx, last_jy);
4399 // do not redraw accessible elements if the player is just pushing them
4400 if (!player_is_moving || !player->is_pushing)
4402 // ... and the field the player is entering
4403 if (IS_ACCESSIBLE_INSIDE(element))
4404 DrawLevelField(jx, jy);
4405 else if (IS_ACCESSIBLE_UNDER(element))
4406 DrawLevelFieldThruMask(jx, jy);
4409 MarkTileDirty(sx, sy);
4413 void DrawPlayer(struct PlayerInfo *player)
4417 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4418 DrawPlayerExt(player, i);
4421 void DrawAllPlayers(void)
4425 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4426 for (j = 0; j < MAX_PLAYERS; j++)
4427 if (stored_player[j].active)
4428 DrawPlayerExt(&stored_player[j], i);
4431 void DrawPlayerField(int x, int y)
4433 if (!IS_PLAYER(x, y))
4436 DrawPlayer(PLAYERINFO(x, y));
4439 // ----------------------------------------------------------------------------
4441 void WaitForEventToContinue(void)
4443 boolean first_wait = TRUE;
4444 boolean still_wait = TRUE;
4446 if (program.headless)
4449 // simulate releasing mouse button over last gadget, if still pressed
4451 HandleGadgets(-1, -1, 0);
4453 button_status = MB_RELEASED;
4456 ClearPlayerAction();
4462 if (NextValidEvent(&event))
4466 case EVENT_BUTTONPRESS:
4467 case EVENT_FINGERPRESS:
4471 case EVENT_BUTTONRELEASE:
4472 case EVENT_FINGERRELEASE:
4473 still_wait = first_wait;
4476 case EVENT_KEYPRESS:
4477 case SDL_CONTROLLERBUTTONDOWN:
4478 case SDL_JOYBUTTONDOWN:
4483 HandleOtherEvents(&event);
4487 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4492 if (!PendingEvent())
4497 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4499 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4500 int draw_buffer_last = GetDrawtoField();
4501 int width = request.width;
4502 int height = request.height;
4506 setRequestPosition(&sx, &sy, FALSE);
4508 button_status = MB_RELEASED;
4510 request_gadget_id = -1;
4517 SetDrawtoField(draw_buffer_game);
4519 HandleGameActions();
4521 SetDrawtoField(DRAW_TO_BACKBUFFER);
4528 while (NextValidEvent(&event))
4532 case EVENT_BUTTONPRESS:
4533 case EVENT_BUTTONRELEASE:
4534 case EVENT_MOTIONNOTIFY:
4536 DrawBuffer *drawto_last = drawto;
4539 if (event.type == EVENT_MOTIONNOTIFY)
4544 motion_status = TRUE;
4545 mx = ((MotionEvent *) &event)->x;
4546 my = ((MotionEvent *) &event)->y;
4550 motion_status = FALSE;
4551 mx = ((ButtonEvent *) &event)->x;
4552 my = ((ButtonEvent *) &event)->y;
4553 if (event.type == EVENT_BUTTONPRESS)
4554 button_status = ((ButtonEvent *) &event)->button;
4556 button_status = MB_RELEASED;
4559 if (global.use_envelope_request)
4561 // draw changed button states to temporary bitmap
4562 drawto = bitmap_db_store_1;
4565 // this sets 'request_gadget_id'
4566 HandleGadgets(mx, my, button_status);
4568 if (global.use_envelope_request)
4570 // restore pointer to drawing buffer
4571 drawto = drawto_last;
4573 // prepare complete envelope request from temporary bitmap
4574 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4578 switch (request_gadget_id)
4580 case TOOL_CTRL_ID_YES:
4581 case TOOL_CTRL_ID_TOUCH_YES:
4584 case TOOL_CTRL_ID_NO:
4585 case TOOL_CTRL_ID_TOUCH_NO:
4588 case TOOL_CTRL_ID_CONFIRM:
4589 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4590 result = TRUE | FALSE;
4593 case TOOL_CTRL_ID_PLAYER_1:
4596 case TOOL_CTRL_ID_PLAYER_2:
4599 case TOOL_CTRL_ID_PLAYER_3:
4602 case TOOL_CTRL_ID_PLAYER_4:
4607 // only check clickable animations if no request gadget clicked
4608 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4615 case SDL_WINDOWEVENT:
4616 HandleWindowEvent((WindowEvent *) &event);
4619 case SDL_APP_WILLENTERBACKGROUND:
4620 case SDL_APP_DIDENTERBACKGROUND:
4621 case SDL_APP_WILLENTERFOREGROUND:
4622 case SDL_APP_DIDENTERFOREGROUND:
4623 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4626 case EVENT_KEYPRESS:
4628 Key key = GetEventKey((KeyEvent *)&event);
4633 if (req_state & REQ_CONFIRM)
4642 #if defined(KSYM_Rewind)
4643 case KSYM_Rewind: // for Amazon Fire TV remote
4652 #if defined(KSYM_FastForward)
4653 case KSYM_FastForward: // for Amazon Fire TV remote
4659 HandleKeysDebug(key, KEY_PRESSED);
4663 if (req_state & REQ_PLAYER)
4665 int old_player_nr = setup.network_player_nr;
4668 result = old_player_nr + 1;
4673 result = old_player_nr + 1;
4704 case EVENT_FINGERRELEASE:
4705 case EVENT_KEYRELEASE:
4706 ClearPlayerAction();
4709 case SDL_CONTROLLERBUTTONDOWN:
4710 switch (event.cbutton.button)
4712 case SDL_CONTROLLER_BUTTON_A:
4713 case SDL_CONTROLLER_BUTTON_X:
4714 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4715 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4719 case SDL_CONTROLLER_BUTTON_B:
4720 case SDL_CONTROLLER_BUTTON_Y:
4721 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4722 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4723 case SDL_CONTROLLER_BUTTON_BACK:
4728 if (req_state & REQ_PLAYER)
4730 int old_player_nr = setup.network_player_nr;
4733 result = old_player_nr + 1;
4735 switch (event.cbutton.button)
4737 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4738 case SDL_CONTROLLER_BUTTON_Y:
4742 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4743 case SDL_CONTROLLER_BUTTON_B:
4747 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4748 case SDL_CONTROLLER_BUTTON_A:
4752 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4753 case SDL_CONTROLLER_BUTTON_X:
4764 case SDL_CONTROLLERBUTTONUP:
4765 HandleJoystickEvent(&event);
4766 ClearPlayerAction();
4770 HandleOtherEvents(&event);
4775 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4777 int joy = AnyJoystick();
4779 if (joy & JOY_BUTTON_1)
4781 else if (joy & JOY_BUTTON_2)
4784 else if (AnyJoystick())
4786 int joy = AnyJoystick();
4788 if (req_state & REQ_PLAYER)
4792 else if (joy & JOY_RIGHT)
4794 else if (joy & JOY_DOWN)
4796 else if (joy & JOY_LEFT)
4804 SetDrawtoField(draw_buffer_last);
4809 static void DoRequestBefore(void)
4811 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4813 // when showing request dialog after game ended, deactivate game panel
4815 game.panel.active = FALSE;
4817 if (game_status == GAME_MODE_PLAYING)
4818 BlitScreenToBitmap(backbuffer);
4820 // disable deactivated drawing when quick-loading level tape recording
4821 if (tape.playing && tape.deactivate_display)
4822 TapeDeactivateDisplayOff(TRUE);
4824 SetMouseCursor(CURSOR_DEFAULT);
4826 // pause network game while waiting for request to answer
4827 if (network.enabled &&
4828 game_status == GAME_MODE_PLAYING &&
4829 !game.all_players_gone)
4830 SendToServer_PausePlaying();
4832 // simulate releasing mouse button over last gadget, if still pressed
4834 HandleGadgets(-1, -1, 0);
4839 static void DoRequestAfter(void)
4843 if (game_status == GAME_MODE_PLAYING)
4845 SetPanelBackground();
4846 SetDrawBackgroundMask(REDRAW_DOOR_1);
4850 SetDrawBackgroundMask(REDRAW_FIELD);
4853 // continue network game after request
4854 if (network.enabled &&
4855 game_status == GAME_MODE_PLAYING &&
4856 !game.all_players_gone)
4857 SendToServer_ContinuePlaying();
4859 // restore deactivated drawing when quick-loading level tape recording
4860 if (tape.playing && tape.deactivate_display)
4861 TapeDeactivateDisplayOn();
4864 static void setRequestDoorTextProperties(char *text,
4869 int *set_max_line_length)
4871 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4872 struct TextPosInfo *pos = &request.button.confirm;
4873 int button_ypos = pos->y;
4874 int font_nr = FONT_TEXT_2;
4875 int font_width = getFontWidth(font_nr);
4876 int font_height = getFontHeight(font_nr);
4877 int line_height = font_height + line_spacing;
4878 int max_text_width = vp_door_1->width;
4879 int max_text_height = button_ypos - 2 * text_spacing;
4880 int max_line_length = max_text_width / font_width;
4881 int max_lines = max_text_height / line_height;
4883 if (maxWordLengthInRequestString(text) > max_line_length)
4885 font_nr = FONT_TEXT_1;
4886 font_width = getFontWidth(font_nr);
4887 max_line_length = max_text_width / font_width;
4890 *set_font_nr = font_nr;
4891 *set_max_lines = max_lines;
4892 *set_max_line_length = max_line_length;
4895 static void DrawRequestDoorText(char *text)
4897 char *text_ptr = text;
4898 int text_spacing = 8;
4899 int line_spacing = 2;
4900 int max_request_lines;
4901 int max_request_line_len;
4905 // force DOOR font inside door area
4906 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4908 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4909 &max_request_lines, &max_request_line_len);
4911 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4913 char text_line[max_request_line_len + 1];
4919 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4921 tc = *(text_ptr + tx);
4922 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4926 if ((tc == '?' || tc == '!') && tl == 0)
4936 strncpy(text_line, text_ptr, tl);
4939 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4940 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4941 text_line, font_nr);
4943 text_ptr += tl + (tc == ' ' ? 1 : 0);
4949 static int RequestDoor(char *text, unsigned int req_state)
4951 unsigned int old_door_state = GetDoorState();
4952 int draw_buffer_last = GetDrawtoField();
4955 if (old_door_state & DOOR_OPEN_1)
4957 CloseDoor(DOOR_CLOSE_1);
4959 // save old door content
4960 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4961 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4964 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4965 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4967 // clear door drawing field
4968 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4970 // write text for request
4971 DrawRequestDoorText(text);
4973 MapToolButtons(req_state);
4975 // copy request gadgets to door backbuffer
4976 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4978 OpenDoor(DOOR_OPEN_1);
4980 // ---------- handle request buttons ----------
4981 result = RequestHandleEvents(req_state, draw_buffer_last);
4985 if (!(req_state & REQ_STAY_OPEN))
4987 CloseDoor(DOOR_CLOSE_1);
4989 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4990 (req_state & REQ_REOPEN))
4991 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4997 static int RequestEnvelope(char *text, unsigned int req_state)
4999 int draw_buffer_last = GetDrawtoField();
5002 DrawEnvelopeRequest(text, req_state);
5003 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5005 // ---------- handle request buttons ----------
5006 result = RequestHandleEvents(req_state, draw_buffer_last);
5010 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5015 int Request(char *text, unsigned int req_state)
5017 boolean overlay_enabled = GetOverlayEnabled();
5020 game.request_active = TRUE;
5022 SetOverlayEnabled(FALSE);
5026 if (global.use_envelope_request)
5027 result = RequestEnvelope(text, req_state);
5029 result = RequestDoor(text, req_state);
5033 SetOverlayEnabled(overlay_enabled);
5035 game.request_active = FALSE;
5040 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5042 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5043 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5046 if (dpo1->sort_priority != dpo2->sort_priority)
5047 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5049 compare_result = dpo1->nr - dpo2->nr;
5051 return compare_result;
5054 void InitGraphicCompatibilityInfo_Doors(void)
5060 struct DoorInfo *door;
5064 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5065 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5067 { -1, -1, -1, NULL }
5069 struct Rect door_rect_list[] =
5071 { DX, DY, DXSIZE, DYSIZE },
5072 { VX, VY, VXSIZE, VYSIZE }
5076 for (i = 0; doors[i].door_token != -1; i++)
5078 int door_token = doors[i].door_token;
5079 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5080 int part_1 = doors[i].part_1;
5081 int part_8 = doors[i].part_8;
5082 int part_2 = part_1 + 1;
5083 int part_3 = part_1 + 2;
5084 struct DoorInfo *door = doors[i].door;
5085 struct Rect *door_rect = &door_rect_list[door_index];
5086 boolean door_gfx_redefined = FALSE;
5088 // check if any door part graphic definitions have been redefined
5090 for (j = 0; door_part_controls[j].door_token != -1; j++)
5092 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5093 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5095 if (dpc->door_token == door_token && fi->redefined)
5096 door_gfx_redefined = TRUE;
5099 // check for old-style door graphic/animation modifications
5101 if (!door_gfx_redefined)
5103 if (door->anim_mode & ANIM_STATIC_PANEL)
5105 door->panel.step_xoffset = 0;
5106 door->panel.step_yoffset = 0;
5109 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5111 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5112 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5113 int num_door_steps, num_panel_steps;
5115 // remove door part graphics other than the two default wings
5117 for (j = 0; door_part_controls[j].door_token != -1; j++)
5119 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5120 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5122 if (dpc->graphic >= part_3 &&
5123 dpc->graphic <= part_8)
5127 // set graphics and screen positions of the default wings
5129 g_part_1->width = door_rect->width;
5130 g_part_1->height = door_rect->height;
5131 g_part_2->width = door_rect->width;
5132 g_part_2->height = door_rect->height;
5133 g_part_2->src_x = door_rect->width;
5134 g_part_2->src_y = g_part_1->src_y;
5136 door->part_2.x = door->part_1.x;
5137 door->part_2.y = door->part_1.y;
5139 if (door->width != -1)
5141 g_part_1->width = door->width;
5142 g_part_2->width = door->width;
5144 // special treatment for graphics and screen position of right wing
5145 g_part_2->src_x += door_rect->width - door->width;
5146 door->part_2.x += door_rect->width - door->width;
5149 if (door->height != -1)
5151 g_part_1->height = door->height;
5152 g_part_2->height = door->height;
5154 // special treatment for graphics and screen position of bottom wing
5155 g_part_2->src_y += door_rect->height - door->height;
5156 door->part_2.y += door_rect->height - door->height;
5159 // set animation delays for the default wings and panels
5161 door->part_1.step_delay = door->step_delay;
5162 door->part_2.step_delay = door->step_delay;
5163 door->panel.step_delay = door->step_delay;
5165 // set animation draw order for the default wings
5167 door->part_1.sort_priority = 2; // draw left wing over ...
5168 door->part_2.sort_priority = 1; // ... right wing
5170 // set animation draw offset for the default wings
5172 if (door->anim_mode & ANIM_HORIZONTAL)
5174 door->part_1.step_xoffset = door->step_offset;
5175 door->part_1.step_yoffset = 0;
5176 door->part_2.step_xoffset = door->step_offset * -1;
5177 door->part_2.step_yoffset = 0;
5179 num_door_steps = g_part_1->width / door->step_offset;
5181 else // ANIM_VERTICAL
5183 door->part_1.step_xoffset = 0;
5184 door->part_1.step_yoffset = door->step_offset;
5185 door->part_2.step_xoffset = 0;
5186 door->part_2.step_yoffset = door->step_offset * -1;
5188 num_door_steps = g_part_1->height / door->step_offset;
5191 // set animation draw offset for the default panels
5193 if (door->step_offset > 1)
5195 num_panel_steps = 2 * door_rect->height / door->step_offset;
5196 door->panel.start_step = num_panel_steps - num_door_steps;
5197 door->panel.start_step_closing = door->panel.start_step;
5201 num_panel_steps = door_rect->height / door->step_offset;
5202 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5203 door->panel.start_step_closing = door->panel.start_step;
5204 door->panel.step_delay *= 2;
5211 void InitDoors(void)
5215 for (i = 0; door_part_controls[i].door_token != -1; i++)
5217 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5218 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5220 // initialize "start_step_opening" and "start_step_closing", if needed
5221 if (dpc->pos->start_step_opening == 0 &&
5222 dpc->pos->start_step_closing == 0)
5224 // dpc->pos->start_step_opening = dpc->pos->start_step;
5225 dpc->pos->start_step_closing = dpc->pos->start_step;
5228 // fill structure for door part draw order (sorted below)
5230 dpo->sort_priority = dpc->pos->sort_priority;
5233 // sort door part controls according to sort_priority and graphic number
5234 qsort(door_part_order, MAX_DOOR_PARTS,
5235 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5238 unsigned int OpenDoor(unsigned int door_state)
5240 if (door_state & DOOR_COPY_BACK)
5242 if (door_state & DOOR_OPEN_1)
5243 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5244 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5246 if (door_state & DOOR_OPEN_2)
5247 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5248 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5250 door_state &= ~DOOR_COPY_BACK;
5253 return MoveDoor(door_state);
5256 unsigned int CloseDoor(unsigned int door_state)
5258 unsigned int old_door_state = GetDoorState();
5260 if (!(door_state & DOOR_NO_COPY_BACK))
5262 if (old_door_state & DOOR_OPEN_1)
5263 BlitBitmap(backbuffer, bitmap_db_door_1,
5264 DX, DY, DXSIZE, DYSIZE, 0, 0);
5266 if (old_door_state & DOOR_OPEN_2)
5267 BlitBitmap(backbuffer, bitmap_db_door_2,
5268 VX, VY, VXSIZE, VYSIZE, 0, 0);
5270 door_state &= ~DOOR_NO_COPY_BACK;
5273 return MoveDoor(door_state);
5276 unsigned int GetDoorState(void)
5278 return MoveDoor(DOOR_GET_STATE);
5281 unsigned int SetDoorState(unsigned int door_state)
5283 return MoveDoor(door_state | DOOR_SET_STATE);
5286 static int euclid(int a, int b)
5288 return (b ? euclid(b, a % b) : a);
5291 unsigned int MoveDoor(unsigned int door_state)
5293 struct Rect door_rect_list[] =
5295 { DX, DY, DXSIZE, DYSIZE },
5296 { VX, VY, VXSIZE, VYSIZE }
5298 static int door1 = DOOR_CLOSE_1;
5299 static int door2 = DOOR_CLOSE_2;
5300 DelayCounter door_delay = { 0 };
5303 if (door_state == DOOR_GET_STATE)
5304 return (door1 | door2);
5306 if (door_state & DOOR_SET_STATE)
5308 if (door_state & DOOR_ACTION_1)
5309 door1 = door_state & DOOR_ACTION_1;
5310 if (door_state & DOOR_ACTION_2)
5311 door2 = door_state & DOOR_ACTION_2;
5313 return (door1 | door2);
5316 if (!(door_state & DOOR_FORCE_REDRAW))
5318 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5319 door_state &= ~DOOR_OPEN_1;
5320 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5321 door_state &= ~DOOR_CLOSE_1;
5322 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5323 door_state &= ~DOOR_OPEN_2;
5324 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5325 door_state &= ~DOOR_CLOSE_2;
5328 if (global.autoplay_leveldir)
5330 door_state |= DOOR_NO_DELAY;
5331 door_state &= ~DOOR_CLOSE_ALL;
5334 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5335 door_state |= DOOR_NO_DELAY;
5337 if (door_state & DOOR_ACTION)
5339 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5340 boolean door_panel_drawn[NUM_DOORS];
5341 boolean panel_has_doors[NUM_DOORS];
5342 boolean door_part_skip[MAX_DOOR_PARTS];
5343 boolean door_part_done[MAX_DOOR_PARTS];
5344 boolean door_part_done_all;
5345 int num_steps[MAX_DOOR_PARTS];
5346 int max_move_delay = 0; // delay for complete animations of all doors
5347 int max_step_delay = 0; // delay (ms) between two animation frames
5348 int num_move_steps = 0; // number of animation steps for all doors
5349 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5350 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5354 for (i = 0; i < NUM_DOORS; i++)
5355 panel_has_doors[i] = FALSE;
5357 for (i = 0; i < MAX_DOOR_PARTS; i++)
5359 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5360 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5361 int door_token = dpc->door_token;
5363 door_part_done[i] = FALSE;
5364 door_part_skip[i] = (!(door_state & door_token) ||
5368 for (i = 0; i < MAX_DOOR_PARTS; i++)
5370 int nr = door_part_order[i].nr;
5371 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5372 struct DoorPartPosInfo *pos = dpc->pos;
5373 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5374 int door_token = dpc->door_token;
5375 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5376 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5377 int step_xoffset = ABS(pos->step_xoffset);
5378 int step_yoffset = ABS(pos->step_yoffset);
5379 int step_delay = pos->step_delay;
5380 int current_door_state = door_state & door_token;
5381 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5382 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5383 boolean part_opening = (is_panel ? door_closing : door_opening);
5384 int start_step = (part_opening ? pos->start_step_opening :
5385 pos->start_step_closing);
5386 float move_xsize = (step_xoffset ? g->width : 0);
5387 float move_ysize = (step_yoffset ? g->height : 0);
5388 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5389 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5390 int move_steps = (move_xsteps && move_ysteps ?
5391 MIN(move_xsteps, move_ysteps) :
5392 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5393 int move_delay = move_steps * step_delay;
5395 if (door_part_skip[nr])
5398 max_move_delay = MAX(max_move_delay, move_delay);
5399 max_step_delay = (max_step_delay == 0 ? step_delay :
5400 euclid(max_step_delay, step_delay));
5401 num_steps[nr] = move_steps;
5405 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5407 panel_has_doors[door_index] = TRUE;
5411 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5413 num_move_steps = max_move_delay / max_step_delay;
5414 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5416 door_delay.value = max_step_delay;
5418 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5420 start = num_move_steps - 1;
5424 // opening door sound has priority over simultaneously closing door
5425 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5427 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5429 if (door_state & DOOR_OPEN_1)
5430 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5431 if (door_state & DOOR_OPEN_2)
5432 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5434 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5436 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5438 if (door_state & DOOR_CLOSE_1)
5439 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5440 if (door_state & DOOR_CLOSE_2)
5441 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5445 for (k = start; k < num_move_steps; k++)
5447 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5449 door_part_done_all = TRUE;
5451 for (i = 0; i < NUM_DOORS; i++)
5452 door_panel_drawn[i] = FALSE;
5454 for (i = 0; i < MAX_DOOR_PARTS; i++)
5456 int nr = door_part_order[i].nr;
5457 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5458 struct DoorPartPosInfo *pos = dpc->pos;
5459 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5460 int door_token = dpc->door_token;
5461 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5462 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5463 boolean is_panel_and_door_has_closed = FALSE;
5464 struct Rect *door_rect = &door_rect_list[door_index];
5465 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5467 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5468 int current_door_state = door_state & door_token;
5469 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5470 boolean door_closing = !door_opening;
5471 boolean part_opening = (is_panel ? door_closing : door_opening);
5472 boolean part_closing = !part_opening;
5473 int start_step = (part_opening ? pos->start_step_opening :
5474 pos->start_step_closing);
5475 int step_delay = pos->step_delay;
5476 int step_factor = step_delay / max_step_delay;
5477 int k1 = (step_factor ? k / step_factor + 1 : k);
5478 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5479 int kk = MAX(0, k2);
5482 int src_x, src_y, src_xx, src_yy;
5483 int dst_x, dst_y, dst_xx, dst_yy;
5486 if (door_part_skip[nr])
5489 if (!(door_state & door_token))
5497 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5498 int kk_door = MAX(0, k2_door);
5499 int sync_frame = kk_door * door_delay.value;
5500 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5502 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5503 &g_src_x, &g_src_y);
5508 if (!door_panel_drawn[door_index])
5510 ClearRectangle(drawto, door_rect->x, door_rect->y,
5511 door_rect->width, door_rect->height);
5513 door_panel_drawn[door_index] = TRUE;
5516 // draw opening or closing door parts
5518 if (pos->step_xoffset < 0) // door part on right side
5521 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5524 if (dst_xx + width > door_rect->width)
5525 width = door_rect->width - dst_xx;
5527 else // door part on left side
5530 dst_xx = pos->x - kk * pos->step_xoffset;
5534 src_xx = ABS(dst_xx);
5538 width = g->width - src_xx;
5540 if (width > door_rect->width)
5541 width = door_rect->width;
5543 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5546 if (pos->step_yoffset < 0) // door part on bottom side
5549 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5552 if (dst_yy + height > door_rect->height)
5553 height = door_rect->height - dst_yy;
5555 else // door part on top side
5558 dst_yy = pos->y - kk * pos->step_yoffset;
5562 src_yy = ABS(dst_yy);
5566 height = g->height - src_yy;
5569 src_x = g_src_x + src_xx;
5570 src_y = g_src_y + src_yy;
5572 dst_x = door_rect->x + dst_xx;
5573 dst_y = door_rect->y + dst_yy;
5575 is_panel_and_door_has_closed =
5578 panel_has_doors[door_index] &&
5579 k >= num_move_steps_doors_only - 1);
5581 if (width >= 0 && width <= g->width &&
5582 height >= 0 && height <= g->height &&
5583 !is_panel_and_door_has_closed)
5585 if (is_panel || !pos->draw_masked)
5586 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5589 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5593 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5595 if ((part_opening && (width < 0 || height < 0)) ||
5596 (part_closing && (width >= g->width && height >= g->height)))
5597 door_part_done[nr] = TRUE;
5599 // continue door part animations, but not panel after door has closed
5600 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5601 door_part_done_all = FALSE;
5604 if (!(door_state & DOOR_NO_DELAY))
5607 HandleGameActions();
5611 SkipUntilDelayReached(&door_delay, &k, last_frame);
5613 // prevent OS (Windows) from complaining about program not responding
5617 if (door_part_done_all)
5621 if (!(door_state & DOOR_NO_DELAY))
5623 // wait for specified door action post delay
5624 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5625 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5626 else if (door_state & DOOR_ACTION_1)
5627 door_delay.value = door_1.post_delay;
5628 else if (door_state & DOOR_ACTION_2)
5629 door_delay.value = door_2.post_delay;
5631 while (!DelayReached(&door_delay))
5634 HandleGameActions();
5641 if (door_state & DOOR_ACTION_1)
5642 door1 = door_state & DOOR_ACTION_1;
5643 if (door_state & DOOR_ACTION_2)
5644 door2 = door_state & DOOR_ACTION_2;
5646 // draw masked border over door area
5647 DrawMaskedBorder(REDRAW_DOOR_1);
5648 DrawMaskedBorder(REDRAW_DOOR_2);
5650 ClearAutoRepeatKeyEvents();
5652 return (door1 | door2);
5655 static boolean useSpecialEditorDoor(void)
5657 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5658 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5660 // do not draw special editor door if editor border defined or redefined
5661 if (graphic_info[graphic].bitmap != NULL || redefined)
5664 // do not draw special editor door if global border defined to be empty
5665 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5668 // do not draw special editor door if viewport definitions do not match
5672 EY + EYSIZE != VY + VYSIZE)
5678 void DrawSpecialEditorDoor(void)
5680 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5681 int top_border_width = gfx1->width;
5682 int top_border_height = gfx1->height;
5683 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5684 int ex = EX - outer_border;
5685 int ey = EY - outer_border;
5686 int vy = VY - outer_border;
5687 int exsize = EXSIZE + 2 * outer_border;
5689 if (!useSpecialEditorDoor())
5692 // draw bigger level editor toolbox window
5693 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5694 top_border_width, top_border_height, ex, ey - top_border_height);
5695 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5696 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5698 redraw_mask |= REDRAW_ALL;
5701 void UndrawSpecialEditorDoor(void)
5703 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5704 int top_border_width = gfx1->width;
5705 int top_border_height = gfx1->height;
5706 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5707 int ex = EX - outer_border;
5708 int ey = EY - outer_border;
5709 int ey_top = ey - top_border_height;
5710 int exsize = EXSIZE + 2 * outer_border;
5711 int eysize = EYSIZE + 2 * outer_border;
5713 if (!useSpecialEditorDoor())
5716 // draw normal tape recorder window
5717 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5719 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5720 ex, ey_top, top_border_width, top_border_height,
5722 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5723 ex, ey, exsize, eysize, ex, ey);
5727 // if screen background is set to "[NONE]", clear editor toolbox window
5728 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5729 ClearRectangle(drawto, ex, ey, exsize, eysize);
5732 redraw_mask |= REDRAW_ALL;
5736 // ---------- new tool button stuff -------------------------------------------
5741 struct TextPosInfo *pos;
5743 boolean is_touch_button;
5745 } toolbutton_info[NUM_TOOL_BUTTONS] =
5748 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5749 TOOL_CTRL_ID_YES, FALSE, "yes"
5752 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5753 TOOL_CTRL_ID_NO, FALSE, "no"
5756 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5757 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5760 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5761 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5764 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5765 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5768 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5769 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5772 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5773 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5776 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5777 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5780 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5781 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5784 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5785 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5789 void CreateToolButtons(void)
5793 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5795 int graphic = toolbutton_info[i].graphic;
5796 struct GraphicInfo *gfx = &graphic_info[graphic];
5797 struct TextPosInfo *pos = toolbutton_info[i].pos;
5798 struct GadgetInfo *gi;
5799 Bitmap *deco_bitmap = None;
5800 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5801 unsigned int event_mask = GD_EVENT_RELEASED;
5802 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5803 int base_x = (is_touch_button ? 0 : DX);
5804 int base_y = (is_touch_button ? 0 : DY);
5805 int gd_x = gfx->src_x;
5806 int gd_y = gfx->src_y;
5807 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5808 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5813 // do not use touch buttons if overlay touch buttons are disabled
5814 if (is_touch_button && !setup.touch.overlay_buttons)
5817 if (global.use_envelope_request && !is_touch_button)
5819 setRequestPosition(&base_x, &base_y, TRUE);
5821 // check if request buttons are outside of envelope and fix, if needed
5822 if (x < 0 || x + gfx->width > request.width ||
5823 y < 0 || y + gfx->height > request.height)
5825 if (id == TOOL_CTRL_ID_YES)
5828 y = request.height - 2 * request.border_size - gfx->height;
5830 else if (id == TOOL_CTRL_ID_NO)
5832 x = request.width - 2 * request.border_size - gfx->width;
5833 y = request.height - 2 * request.border_size - gfx->height;
5835 else if (id == TOOL_CTRL_ID_CONFIRM)
5837 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5838 y = request.height - 2 * request.border_size - gfx->height;
5840 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5842 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5844 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5845 y = request.height - 2 * request.border_size - gfx->height * 2;
5847 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5848 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5853 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5856 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5858 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5859 pos->size, &deco_bitmap, &deco_x, &deco_y);
5860 deco_xpos = (gfx->width - pos->size) / 2;
5861 deco_ypos = (gfx->height - pos->size) / 2;
5864 gi = CreateGadget(GDI_CUSTOM_ID, id,
5865 GDI_IMAGE_ID, graphic,
5866 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5869 GDI_WIDTH, gfx->width,
5870 GDI_HEIGHT, gfx->height,
5871 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5872 GDI_STATE, GD_BUTTON_UNPRESSED,
5873 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5874 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5875 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5876 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5877 GDI_DECORATION_SIZE, pos->size, pos->size,
5878 GDI_DECORATION_SHIFTING, 1, 1,
5879 GDI_DIRECT_DRAW, FALSE,
5880 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5881 GDI_EVENT_MASK, event_mask,
5882 GDI_CALLBACK_ACTION, HandleToolButtons,
5886 Fail("cannot create gadget");
5888 tool_gadget[id] = gi;
5892 void FreeToolButtons(void)
5896 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5897 FreeGadget(tool_gadget[i]);
5900 static void MapToolButtons(unsigned int req_state)
5902 if (req_state & REQ_ASK)
5904 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5905 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5906 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5907 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5909 else if (req_state & REQ_CONFIRM)
5911 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5912 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5914 else if (req_state & REQ_PLAYER)
5916 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5917 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5918 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5919 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5923 static void UnmapToolButtons(void)
5927 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5928 UnmapGadget(tool_gadget[i]);
5931 static void HandleToolButtons(struct GadgetInfo *gi)
5933 request_gadget_id = gi->custom_id;
5936 static struct Mapping_EM_to_RND_object
5939 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5940 boolean is_backside; // backside of moving element
5946 em_object_mapping_list[GAME_TILE_MAX + 1] =
5949 Zborder, FALSE, FALSE,
5953 Zplayer, FALSE, FALSE,
5962 Ztank, FALSE, FALSE,
5966 Zeater, FALSE, FALSE,
5970 Zdynamite, FALSE, FALSE,
5974 Zboom, FALSE, FALSE,
5979 Xchain, FALSE, FALSE,
5980 EL_DEFAULT, ACTION_EXPLODING, -1
5983 Xboom_bug, FALSE, FALSE,
5984 EL_BUG, ACTION_EXPLODING, -1
5987 Xboom_tank, FALSE, FALSE,
5988 EL_SPACESHIP, ACTION_EXPLODING, -1
5991 Xboom_android, FALSE, FALSE,
5992 EL_EMC_ANDROID, ACTION_OTHER, -1
5995 Xboom_1, FALSE, FALSE,
5996 EL_DEFAULT, ACTION_EXPLODING, -1
5999 Xboom_2, FALSE, FALSE,
6000 EL_DEFAULT, ACTION_EXPLODING, -1
6004 Xblank, TRUE, FALSE,
6009 Xsplash_e, FALSE, FALSE,
6010 EL_ACID_SPLASH_RIGHT, -1, -1
6013 Xsplash_w, FALSE, FALSE,
6014 EL_ACID_SPLASH_LEFT, -1, -1
6018 Xplant, TRUE, FALSE,
6019 EL_EMC_PLANT, -1, -1
6022 Yplant, FALSE, FALSE,
6023 EL_EMC_PLANT, -1, -1
6027 Xacid_1, TRUE, FALSE,
6031 Xacid_2, FALSE, FALSE,
6035 Xacid_3, FALSE, FALSE,
6039 Xacid_4, FALSE, FALSE,
6043 Xacid_5, FALSE, FALSE,
6047 Xacid_6, FALSE, FALSE,
6051 Xacid_7, FALSE, FALSE,
6055 Xacid_8, FALSE, FALSE,
6060 Xfake_acid_1, TRUE, FALSE,
6061 EL_EMC_FAKE_ACID, -1, -1
6064 Xfake_acid_2, FALSE, FALSE,
6065 EL_EMC_FAKE_ACID, -1, -1
6068 Xfake_acid_3, FALSE, FALSE,
6069 EL_EMC_FAKE_ACID, -1, -1
6072 Xfake_acid_4, FALSE, FALSE,
6073 EL_EMC_FAKE_ACID, -1, -1
6076 Xfake_acid_5, FALSE, FALSE,
6077 EL_EMC_FAKE_ACID, -1, -1
6080 Xfake_acid_6, FALSE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_7, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_8, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6093 Xfake_acid_1_player, FALSE, FALSE,
6094 EL_EMC_FAKE_ACID, -1, -1
6097 Xfake_acid_2_player, FALSE, FALSE,
6098 EL_EMC_FAKE_ACID, -1, -1
6101 Xfake_acid_3_player, FALSE, FALSE,
6102 EL_EMC_FAKE_ACID, -1, -1
6105 Xfake_acid_4_player, FALSE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_5_player, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_6_player, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_7_player, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_8_player, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6126 Xgrass, TRUE, FALSE,
6127 EL_EMC_GRASS, -1, -1
6130 Ygrass_nB, FALSE, FALSE,
6131 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6134 Ygrass_eB, FALSE, FALSE,
6135 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6138 Ygrass_sB, FALSE, FALSE,
6139 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6142 Ygrass_wB, FALSE, FALSE,
6143 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6151 Ydirt_nB, FALSE, FALSE,
6152 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6155 Ydirt_eB, FALSE, FALSE,
6156 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6159 Ydirt_sB, FALSE, FALSE,
6160 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6163 Ydirt_wB, FALSE, FALSE,
6164 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6168 Xandroid, TRUE, FALSE,
6169 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6172 Xandroid_1_n, FALSE, FALSE,
6173 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6176 Xandroid_2_n, FALSE, FALSE,
6177 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6180 Xandroid_1_e, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6184 Xandroid_2_e, FALSE, FALSE,
6185 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6188 Xandroid_1_w, FALSE, FALSE,
6189 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6192 Xandroid_2_w, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6196 Xandroid_1_s, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6200 Xandroid_2_s, FALSE, FALSE,
6201 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6204 Yandroid_n, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6208 Yandroid_nB, FALSE, TRUE,
6209 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6212 Yandroid_ne, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6216 Yandroid_neB, FALSE, TRUE,
6217 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6220 Yandroid_e, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6224 Yandroid_eB, FALSE, TRUE,
6225 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6228 Yandroid_se, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6232 Yandroid_seB, FALSE, TRUE,
6233 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6236 Yandroid_s, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6240 Yandroid_sB, FALSE, TRUE,
6241 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6244 Yandroid_sw, FALSE, FALSE,
6245 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6248 Yandroid_swB, FALSE, TRUE,
6249 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6252 Yandroid_w, FALSE, FALSE,
6253 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6256 Yandroid_wB, FALSE, TRUE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6260 Yandroid_nw, FALSE, FALSE,
6261 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6264 Yandroid_nwB, FALSE, TRUE,
6265 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6269 Xeater_n, TRUE, FALSE,
6270 EL_YAMYAM_UP, -1, -1
6273 Xeater_e, TRUE, FALSE,
6274 EL_YAMYAM_RIGHT, -1, -1
6277 Xeater_w, TRUE, FALSE,
6278 EL_YAMYAM_LEFT, -1, -1
6281 Xeater_s, TRUE, FALSE,
6282 EL_YAMYAM_DOWN, -1, -1
6285 Yeater_n, FALSE, FALSE,
6286 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6289 Yeater_nB, FALSE, TRUE,
6290 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6293 Yeater_e, FALSE, FALSE,
6294 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6297 Yeater_eB, FALSE, TRUE,
6298 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6301 Yeater_s, FALSE, FALSE,
6302 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6305 Yeater_sB, FALSE, TRUE,
6306 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6309 Yeater_w, FALSE, FALSE,
6310 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6313 Yeater_wB, FALSE, TRUE,
6314 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6317 Yeater_stone, FALSE, FALSE,
6318 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6321 Yeater_spring, FALSE, FALSE,
6322 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6326 Xalien, TRUE, FALSE,
6330 Xalien_pause, FALSE, FALSE,
6334 Yalien_n, FALSE, FALSE,
6335 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6338 Yalien_nB, FALSE, TRUE,
6339 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6342 Yalien_e, FALSE, FALSE,
6343 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6346 Yalien_eB, FALSE, TRUE,
6347 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6350 Yalien_s, FALSE, FALSE,
6351 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6354 Yalien_sB, FALSE, TRUE,
6355 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6358 Yalien_w, FALSE, FALSE,
6359 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6362 Yalien_wB, FALSE, TRUE,
6363 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6366 Yalien_stone, FALSE, FALSE,
6367 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6370 Yalien_spring, FALSE, FALSE,
6371 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6375 Xbug_1_n, TRUE, FALSE,
6379 Xbug_1_e, TRUE, FALSE,
6380 EL_BUG_RIGHT, -1, -1
6383 Xbug_1_s, TRUE, FALSE,
6387 Xbug_1_w, TRUE, FALSE,
6391 Xbug_2_n, FALSE, FALSE,
6395 Xbug_2_e, FALSE, FALSE,
6396 EL_BUG_RIGHT, -1, -1
6399 Xbug_2_s, FALSE, FALSE,
6403 Xbug_2_w, FALSE, FALSE,
6407 Ybug_n, FALSE, FALSE,
6408 EL_BUG, ACTION_MOVING, MV_BIT_UP
6411 Ybug_nB, FALSE, TRUE,
6412 EL_BUG, ACTION_MOVING, MV_BIT_UP
6415 Ybug_e, FALSE, FALSE,
6416 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6419 Ybug_eB, FALSE, TRUE,
6420 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6423 Ybug_s, FALSE, FALSE,
6424 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6427 Ybug_sB, FALSE, TRUE,
6428 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6431 Ybug_w, FALSE, FALSE,
6432 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6435 Ybug_wB, FALSE, TRUE,
6436 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6439 Ybug_w_n, FALSE, FALSE,
6440 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6443 Ybug_n_e, FALSE, FALSE,
6444 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6447 Ybug_e_s, FALSE, FALSE,
6448 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6451 Ybug_s_w, FALSE, FALSE,
6452 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6455 Ybug_e_n, FALSE, FALSE,
6456 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6459 Ybug_s_e, FALSE, FALSE,
6460 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6463 Ybug_w_s, FALSE, FALSE,
6464 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6467 Ybug_n_w, FALSE, FALSE,
6468 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6471 Ybug_stone, FALSE, FALSE,
6472 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6475 Ybug_spring, FALSE, FALSE,
6476 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6480 Xtank_1_n, TRUE, FALSE,
6481 EL_SPACESHIP_UP, -1, -1
6484 Xtank_1_e, TRUE, FALSE,
6485 EL_SPACESHIP_RIGHT, -1, -1
6488 Xtank_1_s, TRUE, FALSE,
6489 EL_SPACESHIP_DOWN, -1, -1
6492 Xtank_1_w, TRUE, FALSE,
6493 EL_SPACESHIP_LEFT, -1, -1
6496 Xtank_2_n, FALSE, FALSE,
6497 EL_SPACESHIP_UP, -1, -1
6500 Xtank_2_e, FALSE, FALSE,
6501 EL_SPACESHIP_RIGHT, -1, -1
6504 Xtank_2_s, FALSE, FALSE,
6505 EL_SPACESHIP_DOWN, -1, -1
6508 Xtank_2_w, FALSE, FALSE,
6509 EL_SPACESHIP_LEFT, -1, -1
6512 Ytank_n, FALSE, FALSE,
6513 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6516 Ytank_nB, FALSE, TRUE,
6517 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6520 Ytank_e, FALSE, FALSE,
6521 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6524 Ytank_eB, FALSE, TRUE,
6525 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6528 Ytank_s, FALSE, FALSE,
6529 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6532 Ytank_sB, FALSE, TRUE,
6533 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6536 Ytank_w, FALSE, FALSE,
6537 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6540 Ytank_wB, FALSE, TRUE,
6541 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6544 Ytank_w_n, FALSE, FALSE,
6545 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6548 Ytank_n_e, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6552 Ytank_e_s, FALSE, FALSE,
6553 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6556 Ytank_s_w, FALSE, FALSE,
6557 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6560 Ytank_e_n, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6564 Ytank_s_e, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6568 Ytank_w_s, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6572 Ytank_n_w, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6576 Ytank_stone, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6580 Ytank_spring, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6585 Xemerald, TRUE, FALSE,
6589 Xemerald_pause, FALSE, FALSE,
6593 Xemerald_fall, FALSE, FALSE,
6597 Xemerald_shine, FALSE, FALSE,
6598 EL_EMERALD, ACTION_TWINKLING, -1
6601 Yemerald_s, FALSE, FALSE,
6602 EL_EMERALD, ACTION_FALLING, -1
6605 Yemerald_sB, FALSE, TRUE,
6606 EL_EMERALD, ACTION_FALLING, -1
6609 Yemerald_e, FALSE, FALSE,
6610 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6613 Yemerald_eB, FALSE, TRUE,
6614 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6617 Yemerald_w, FALSE, FALSE,
6618 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6621 Yemerald_wB, FALSE, TRUE,
6622 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6625 Yemerald_blank, FALSE, FALSE,
6626 EL_EMERALD, ACTION_COLLECTING, -1
6630 Xdiamond, TRUE, FALSE,
6634 Xdiamond_pause, FALSE, FALSE,
6638 Xdiamond_fall, FALSE, FALSE,
6642 Xdiamond_shine, FALSE, FALSE,
6643 EL_DIAMOND, ACTION_TWINKLING, -1
6646 Ydiamond_s, FALSE, FALSE,
6647 EL_DIAMOND, ACTION_FALLING, -1
6650 Ydiamond_sB, FALSE, TRUE,
6651 EL_DIAMOND, ACTION_FALLING, -1
6654 Ydiamond_e, FALSE, FALSE,
6655 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6658 Ydiamond_eB, FALSE, TRUE,
6659 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6662 Ydiamond_w, FALSE, FALSE,
6663 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6666 Ydiamond_wB, FALSE, TRUE,
6667 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6670 Ydiamond_blank, FALSE, FALSE,
6671 EL_DIAMOND, ACTION_COLLECTING, -1
6674 Ydiamond_stone, FALSE, FALSE,
6675 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6679 Xstone, TRUE, FALSE,
6683 Xstone_pause, FALSE, FALSE,
6687 Xstone_fall, FALSE, FALSE,
6691 Ystone_s, FALSE, FALSE,
6692 EL_ROCK, ACTION_FALLING, -1
6695 Ystone_sB, FALSE, TRUE,
6696 EL_ROCK, ACTION_FALLING, -1
6699 Ystone_e, FALSE, FALSE,
6700 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6703 Ystone_eB, FALSE, TRUE,
6704 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6707 Ystone_w, FALSE, FALSE,
6708 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6711 Ystone_wB, FALSE, TRUE,
6712 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6720 Xbomb_pause, FALSE, FALSE,
6724 Xbomb_fall, FALSE, FALSE,
6728 Ybomb_s, FALSE, FALSE,
6729 EL_BOMB, ACTION_FALLING, -1
6732 Ybomb_sB, FALSE, TRUE,
6733 EL_BOMB, ACTION_FALLING, -1
6736 Ybomb_e, FALSE, FALSE,
6737 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6740 Ybomb_eB, FALSE, TRUE,
6741 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6744 Ybomb_w, FALSE, FALSE,
6745 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6748 Ybomb_wB, FALSE, TRUE,
6749 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6752 Ybomb_blank, FALSE, FALSE,
6753 EL_BOMB, ACTION_ACTIVATING, -1
6761 Xnut_pause, FALSE, FALSE,
6765 Xnut_fall, FALSE, FALSE,
6769 Ynut_s, FALSE, FALSE,
6770 EL_NUT, ACTION_FALLING, -1
6773 Ynut_sB, FALSE, TRUE,
6774 EL_NUT, ACTION_FALLING, -1
6777 Ynut_e, FALSE, FALSE,
6778 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6781 Ynut_eB, FALSE, TRUE,
6782 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6785 Ynut_w, FALSE, FALSE,
6786 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6789 Ynut_wB, FALSE, TRUE,
6790 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6793 Ynut_stone, FALSE, FALSE,
6794 EL_NUT, ACTION_BREAKING, -1
6798 Xspring, TRUE, FALSE,
6802 Xspring_pause, FALSE, FALSE,
6806 Xspring_e, TRUE, FALSE,
6807 EL_SPRING_RIGHT, -1, -1
6810 Xspring_w, TRUE, FALSE,
6811 EL_SPRING_LEFT, -1, -1
6814 Xspring_fall, FALSE, FALSE,
6818 Yspring_s, FALSE, FALSE,
6819 EL_SPRING, ACTION_FALLING, -1
6822 Yspring_sB, FALSE, TRUE,
6823 EL_SPRING, ACTION_FALLING, -1
6826 Yspring_e, FALSE, FALSE,
6827 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6830 Yspring_eB, FALSE, TRUE,
6831 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6834 Yspring_w, FALSE, FALSE,
6835 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6838 Yspring_wB, FALSE, TRUE,
6839 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6842 Yspring_alien_e, FALSE, FALSE,
6843 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6846 Yspring_alien_eB, FALSE, TRUE,
6847 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6850 Yspring_alien_w, FALSE, FALSE,
6851 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6854 Yspring_alien_wB, FALSE, TRUE,
6855 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6859 Xpush_emerald_e, FALSE, FALSE,
6860 EL_EMERALD, -1, MV_BIT_RIGHT
6863 Xpush_emerald_w, FALSE, FALSE,
6864 EL_EMERALD, -1, MV_BIT_LEFT
6867 Xpush_diamond_e, FALSE, FALSE,
6868 EL_DIAMOND, -1, MV_BIT_RIGHT
6871 Xpush_diamond_w, FALSE, FALSE,
6872 EL_DIAMOND, -1, MV_BIT_LEFT
6875 Xpush_stone_e, FALSE, FALSE,
6876 EL_ROCK, -1, MV_BIT_RIGHT
6879 Xpush_stone_w, FALSE, FALSE,
6880 EL_ROCK, -1, MV_BIT_LEFT
6883 Xpush_bomb_e, FALSE, FALSE,
6884 EL_BOMB, -1, MV_BIT_RIGHT
6887 Xpush_bomb_w, FALSE, FALSE,
6888 EL_BOMB, -1, MV_BIT_LEFT
6891 Xpush_nut_e, FALSE, FALSE,
6892 EL_NUT, -1, MV_BIT_RIGHT
6895 Xpush_nut_w, FALSE, FALSE,
6896 EL_NUT, -1, MV_BIT_LEFT
6899 Xpush_spring_e, FALSE, FALSE,
6900 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6903 Xpush_spring_w, FALSE, FALSE,
6904 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6908 Xdynamite, TRUE, FALSE,
6909 EL_EM_DYNAMITE, -1, -1
6912 Ydynamite_blank, FALSE, FALSE,
6913 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6916 Xdynamite_1, TRUE, FALSE,
6917 EL_EM_DYNAMITE_ACTIVE, -1, -1
6920 Xdynamite_2, FALSE, FALSE,
6921 EL_EM_DYNAMITE_ACTIVE, -1, -1
6924 Xdynamite_3, FALSE, FALSE,
6925 EL_EM_DYNAMITE_ACTIVE, -1, -1
6928 Xdynamite_4, FALSE, FALSE,
6929 EL_EM_DYNAMITE_ACTIVE, -1, -1
6933 Xkey_1, TRUE, FALSE,
6937 Xkey_2, TRUE, FALSE,
6941 Xkey_3, TRUE, FALSE,
6945 Xkey_4, TRUE, FALSE,
6949 Xkey_5, TRUE, FALSE,
6950 EL_EMC_KEY_5, -1, -1
6953 Xkey_6, TRUE, FALSE,
6954 EL_EMC_KEY_6, -1, -1
6957 Xkey_7, TRUE, FALSE,
6958 EL_EMC_KEY_7, -1, -1
6961 Xkey_8, TRUE, FALSE,
6962 EL_EMC_KEY_8, -1, -1
6966 Xdoor_1, TRUE, FALSE,
6967 EL_EM_GATE_1, -1, -1
6970 Xdoor_2, TRUE, FALSE,
6971 EL_EM_GATE_2, -1, -1
6974 Xdoor_3, TRUE, FALSE,
6975 EL_EM_GATE_3, -1, -1
6978 Xdoor_4, TRUE, FALSE,
6979 EL_EM_GATE_4, -1, -1
6982 Xdoor_5, TRUE, FALSE,
6983 EL_EMC_GATE_5, -1, -1
6986 Xdoor_6, TRUE, FALSE,
6987 EL_EMC_GATE_6, -1, -1
6990 Xdoor_7, TRUE, FALSE,
6991 EL_EMC_GATE_7, -1, -1
6994 Xdoor_8, TRUE, FALSE,
6995 EL_EMC_GATE_8, -1, -1
6999 Xfake_door_1, TRUE, FALSE,
7000 EL_EM_GATE_1_GRAY, -1, -1
7003 Xfake_door_2, TRUE, FALSE,
7004 EL_EM_GATE_2_GRAY, -1, -1
7007 Xfake_door_3, TRUE, FALSE,
7008 EL_EM_GATE_3_GRAY, -1, -1
7011 Xfake_door_4, TRUE, FALSE,
7012 EL_EM_GATE_4_GRAY, -1, -1
7015 Xfake_door_5, TRUE, FALSE,
7016 EL_EMC_GATE_5_GRAY, -1, -1
7019 Xfake_door_6, TRUE, FALSE,
7020 EL_EMC_GATE_6_GRAY, -1, -1
7023 Xfake_door_7, TRUE, FALSE,
7024 EL_EMC_GATE_7_GRAY, -1, -1
7027 Xfake_door_8, TRUE, FALSE,
7028 EL_EMC_GATE_8_GRAY, -1, -1
7032 Xballoon, TRUE, FALSE,
7036 Yballoon_n, FALSE, FALSE,
7037 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7040 Yballoon_nB, FALSE, TRUE,
7041 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7044 Yballoon_e, FALSE, FALSE,
7045 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7048 Yballoon_eB, FALSE, TRUE,
7049 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7052 Yballoon_s, FALSE, FALSE,
7053 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7056 Yballoon_sB, FALSE, TRUE,
7057 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7060 Yballoon_w, FALSE, FALSE,
7061 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7064 Yballoon_wB, FALSE, TRUE,
7065 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7069 Xball_1, TRUE, FALSE,
7070 EL_EMC_MAGIC_BALL, -1, -1
7073 Yball_1, FALSE, FALSE,
7074 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7077 Xball_2, FALSE, FALSE,
7078 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7081 Yball_2, FALSE, FALSE,
7082 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7085 Yball_blank, FALSE, FALSE,
7086 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7090 Xamoeba_1, TRUE, FALSE,
7091 EL_AMOEBA_DRY, ACTION_OTHER, -1
7094 Xamoeba_2, FALSE, FALSE,
7095 EL_AMOEBA_DRY, ACTION_OTHER, -1
7098 Xamoeba_3, FALSE, FALSE,
7099 EL_AMOEBA_DRY, ACTION_OTHER, -1
7102 Xamoeba_4, FALSE, FALSE,
7103 EL_AMOEBA_DRY, ACTION_OTHER, -1
7106 Xamoeba_5, TRUE, FALSE,
7107 EL_AMOEBA_WET, ACTION_OTHER, -1
7110 Xamoeba_6, FALSE, FALSE,
7111 EL_AMOEBA_WET, ACTION_OTHER, -1
7114 Xamoeba_7, FALSE, FALSE,
7115 EL_AMOEBA_WET, ACTION_OTHER, -1
7118 Xamoeba_8, FALSE, FALSE,
7119 EL_AMOEBA_WET, ACTION_OTHER, -1
7124 EL_AMOEBA_DROP, ACTION_GROWING, -1
7127 Xdrip_fall, FALSE, FALSE,
7128 EL_AMOEBA_DROP, -1, -1
7131 Xdrip_stretch, FALSE, FALSE,
7132 EL_AMOEBA_DROP, ACTION_FALLING, -1
7135 Xdrip_stretchB, FALSE, TRUE,
7136 EL_AMOEBA_DROP, ACTION_FALLING, -1
7139 Ydrip_1_s, FALSE, FALSE,
7140 EL_AMOEBA_DROP, ACTION_FALLING, -1
7143 Ydrip_1_sB, FALSE, TRUE,
7144 EL_AMOEBA_DROP, ACTION_FALLING, -1
7147 Ydrip_2_s, FALSE, FALSE,
7148 EL_AMOEBA_DROP, ACTION_FALLING, -1
7151 Ydrip_2_sB, FALSE, TRUE,
7152 EL_AMOEBA_DROP, ACTION_FALLING, -1
7156 Xwonderwall, TRUE, FALSE,
7157 EL_MAGIC_WALL, -1, -1
7160 Ywonderwall, FALSE, FALSE,
7161 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7165 Xwheel, TRUE, FALSE,
7166 EL_ROBOT_WHEEL, -1, -1
7169 Ywheel, FALSE, FALSE,
7170 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7174 Xswitch, TRUE, FALSE,
7175 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7178 Yswitch, FALSE, FALSE,
7179 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7183 Xbumper, TRUE, FALSE,
7184 EL_EMC_SPRING_BUMPER, -1, -1
7187 Ybumper, FALSE, FALSE,
7188 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7192 Xacid_nw, TRUE, FALSE,
7193 EL_ACID_POOL_TOPLEFT, -1, -1
7196 Xacid_ne, TRUE, FALSE,
7197 EL_ACID_POOL_TOPRIGHT, -1, -1
7200 Xacid_sw, TRUE, FALSE,
7201 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7204 Xacid_s, TRUE, FALSE,
7205 EL_ACID_POOL_BOTTOM, -1, -1
7208 Xacid_se, TRUE, FALSE,
7209 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7213 Xfake_blank, TRUE, FALSE,
7214 EL_INVISIBLE_WALL, -1, -1
7217 Yfake_blank, FALSE, FALSE,
7218 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7222 Xfake_grass, TRUE, FALSE,
7223 EL_EMC_FAKE_GRASS, -1, -1
7226 Yfake_grass, FALSE, FALSE,
7227 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7231 Xfake_amoeba, TRUE, FALSE,
7232 EL_EMC_DRIPPER, -1, -1
7235 Yfake_amoeba, FALSE, FALSE,
7236 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7240 Xlenses, TRUE, FALSE,
7241 EL_EMC_LENSES, -1, -1
7245 Xmagnify, TRUE, FALSE,
7246 EL_EMC_MAGNIFIER, -1, -1
7251 EL_QUICKSAND_EMPTY, -1, -1
7254 Xsand_stone, TRUE, FALSE,
7255 EL_QUICKSAND_FULL, -1, -1
7258 Xsand_stonein_1, FALSE, TRUE,
7259 EL_ROCK, ACTION_FILLING, -1
7262 Xsand_stonein_2, FALSE, TRUE,
7263 EL_ROCK, ACTION_FILLING, -1
7266 Xsand_stonein_3, FALSE, TRUE,
7267 EL_ROCK, ACTION_FILLING, -1
7270 Xsand_stonein_4, FALSE, TRUE,
7271 EL_ROCK, ACTION_FILLING, -1
7274 Xsand_sandstone_1, FALSE, FALSE,
7275 EL_QUICKSAND_FILLING, -1, -1
7278 Xsand_sandstone_2, FALSE, FALSE,
7279 EL_QUICKSAND_FILLING, -1, -1
7282 Xsand_sandstone_3, FALSE, FALSE,
7283 EL_QUICKSAND_FILLING, -1, -1
7286 Xsand_sandstone_4, FALSE, FALSE,
7287 EL_QUICKSAND_FILLING, -1, -1
7290 Xsand_stonesand_1, FALSE, FALSE,
7291 EL_QUICKSAND_EMPTYING, -1, -1
7294 Xsand_stonesand_2, FALSE, FALSE,
7295 EL_QUICKSAND_EMPTYING, -1, -1
7298 Xsand_stonesand_3, FALSE, FALSE,
7299 EL_QUICKSAND_EMPTYING, -1, -1
7302 Xsand_stonesand_4, FALSE, FALSE,
7303 EL_QUICKSAND_EMPTYING, -1, -1
7306 Xsand_stoneout_1, FALSE, FALSE,
7307 EL_ROCK, ACTION_EMPTYING, -1
7310 Xsand_stoneout_2, FALSE, FALSE,
7311 EL_ROCK, ACTION_EMPTYING, -1
7314 Xsand_stonesand_quickout_1, FALSE, FALSE,
7315 EL_QUICKSAND_EMPTYING, -1, -1
7318 Xsand_stonesand_quickout_2, FALSE, FALSE,
7319 EL_QUICKSAND_EMPTYING, -1, -1
7323 Xslide_ns, TRUE, FALSE,
7324 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7327 Yslide_ns_blank, FALSE, FALSE,
7328 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7331 Xslide_ew, TRUE, FALSE,
7332 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7335 Yslide_ew_blank, FALSE, FALSE,
7336 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7340 Xwind_n, TRUE, FALSE,
7341 EL_BALLOON_SWITCH_UP, -1, -1
7344 Xwind_e, TRUE, FALSE,
7345 EL_BALLOON_SWITCH_RIGHT, -1, -1
7348 Xwind_s, TRUE, FALSE,
7349 EL_BALLOON_SWITCH_DOWN, -1, -1
7352 Xwind_w, TRUE, FALSE,
7353 EL_BALLOON_SWITCH_LEFT, -1, -1
7356 Xwind_any, TRUE, FALSE,
7357 EL_BALLOON_SWITCH_ANY, -1, -1
7360 Xwind_stop, TRUE, FALSE,
7361 EL_BALLOON_SWITCH_NONE, -1, -1
7366 EL_EM_EXIT_CLOSED, -1, -1
7369 Xexit_1, TRUE, FALSE,
7370 EL_EM_EXIT_OPEN, -1, -1
7373 Xexit_2, FALSE, FALSE,
7374 EL_EM_EXIT_OPEN, -1, -1
7377 Xexit_3, FALSE, FALSE,
7378 EL_EM_EXIT_OPEN, -1, -1
7382 Xpause, FALSE, FALSE,
7387 Xwall_1, TRUE, FALSE,
7391 Xwall_2, TRUE, FALSE,
7392 EL_EMC_WALL_14, -1, -1
7395 Xwall_3, TRUE, FALSE,
7396 EL_EMC_WALL_15, -1, -1
7399 Xwall_4, TRUE, FALSE,
7400 EL_EMC_WALL_16, -1, -1
7404 Xroundwall_1, TRUE, FALSE,
7405 EL_WALL_SLIPPERY, -1, -1
7408 Xroundwall_2, TRUE, FALSE,
7409 EL_EMC_WALL_SLIPPERY_2, -1, -1
7412 Xroundwall_3, TRUE, FALSE,
7413 EL_EMC_WALL_SLIPPERY_3, -1, -1
7416 Xroundwall_4, TRUE, FALSE,
7417 EL_EMC_WALL_SLIPPERY_4, -1, -1
7421 Xsteel_1, TRUE, FALSE,
7422 EL_STEELWALL, -1, -1
7425 Xsteel_2, TRUE, FALSE,
7426 EL_EMC_STEELWALL_2, -1, -1
7429 Xsteel_3, TRUE, FALSE,
7430 EL_EMC_STEELWALL_3, -1, -1
7433 Xsteel_4, TRUE, FALSE,
7434 EL_EMC_STEELWALL_4, -1, -1
7438 Xdecor_1, TRUE, FALSE,
7439 EL_EMC_WALL_8, -1, -1
7442 Xdecor_2, TRUE, FALSE,
7443 EL_EMC_WALL_6, -1, -1
7446 Xdecor_3, TRUE, FALSE,
7447 EL_EMC_WALL_4, -1, -1
7450 Xdecor_4, TRUE, FALSE,
7451 EL_EMC_WALL_7, -1, -1
7454 Xdecor_5, TRUE, FALSE,
7455 EL_EMC_WALL_5, -1, -1
7458 Xdecor_6, TRUE, FALSE,
7459 EL_EMC_WALL_9, -1, -1
7462 Xdecor_7, TRUE, FALSE,
7463 EL_EMC_WALL_10, -1, -1
7466 Xdecor_8, TRUE, FALSE,
7467 EL_EMC_WALL_1, -1, -1
7470 Xdecor_9, TRUE, FALSE,
7471 EL_EMC_WALL_2, -1, -1
7474 Xdecor_10, TRUE, FALSE,
7475 EL_EMC_WALL_3, -1, -1
7478 Xdecor_11, TRUE, FALSE,
7479 EL_EMC_WALL_11, -1, -1
7482 Xdecor_12, TRUE, FALSE,
7483 EL_EMC_WALL_12, -1, -1
7487 Xalpha_0, TRUE, FALSE,
7488 EL_CHAR('0'), -1, -1
7491 Xalpha_1, TRUE, FALSE,
7492 EL_CHAR('1'), -1, -1
7495 Xalpha_2, TRUE, FALSE,
7496 EL_CHAR('2'), -1, -1
7499 Xalpha_3, TRUE, FALSE,
7500 EL_CHAR('3'), -1, -1
7503 Xalpha_4, TRUE, FALSE,
7504 EL_CHAR('4'), -1, -1
7507 Xalpha_5, TRUE, FALSE,
7508 EL_CHAR('5'), -1, -1
7511 Xalpha_6, TRUE, FALSE,
7512 EL_CHAR('6'), -1, -1
7515 Xalpha_7, TRUE, FALSE,
7516 EL_CHAR('7'), -1, -1
7519 Xalpha_8, TRUE, FALSE,
7520 EL_CHAR('8'), -1, -1
7523 Xalpha_9, TRUE, FALSE,
7524 EL_CHAR('9'), -1, -1
7527 Xalpha_excla, TRUE, FALSE,
7528 EL_CHAR('!'), -1, -1
7531 Xalpha_apost, TRUE, FALSE,
7532 EL_CHAR('\''), -1, -1
7535 Xalpha_comma, TRUE, FALSE,
7536 EL_CHAR(','), -1, -1
7539 Xalpha_minus, TRUE, FALSE,
7540 EL_CHAR('-'), -1, -1
7543 Xalpha_perio, TRUE, FALSE,
7544 EL_CHAR('.'), -1, -1
7547 Xalpha_colon, TRUE, FALSE,
7548 EL_CHAR(':'), -1, -1
7551 Xalpha_quest, TRUE, FALSE,
7552 EL_CHAR('?'), -1, -1
7555 Xalpha_a, TRUE, FALSE,
7556 EL_CHAR('A'), -1, -1
7559 Xalpha_b, TRUE, FALSE,
7560 EL_CHAR('B'), -1, -1
7563 Xalpha_c, TRUE, FALSE,
7564 EL_CHAR('C'), -1, -1
7567 Xalpha_d, TRUE, FALSE,
7568 EL_CHAR('D'), -1, -1
7571 Xalpha_e, TRUE, FALSE,
7572 EL_CHAR('E'), -1, -1
7575 Xalpha_f, TRUE, FALSE,
7576 EL_CHAR('F'), -1, -1
7579 Xalpha_g, TRUE, FALSE,
7580 EL_CHAR('G'), -1, -1
7583 Xalpha_h, TRUE, FALSE,
7584 EL_CHAR('H'), -1, -1
7587 Xalpha_i, TRUE, FALSE,
7588 EL_CHAR('I'), -1, -1
7591 Xalpha_j, TRUE, FALSE,
7592 EL_CHAR('J'), -1, -1
7595 Xalpha_k, TRUE, FALSE,
7596 EL_CHAR('K'), -1, -1
7599 Xalpha_l, TRUE, FALSE,
7600 EL_CHAR('L'), -1, -1
7603 Xalpha_m, TRUE, FALSE,
7604 EL_CHAR('M'), -1, -1
7607 Xalpha_n, TRUE, FALSE,
7608 EL_CHAR('N'), -1, -1
7611 Xalpha_o, TRUE, FALSE,
7612 EL_CHAR('O'), -1, -1
7615 Xalpha_p, TRUE, FALSE,
7616 EL_CHAR('P'), -1, -1
7619 Xalpha_q, TRUE, FALSE,
7620 EL_CHAR('Q'), -1, -1
7623 Xalpha_r, TRUE, FALSE,
7624 EL_CHAR('R'), -1, -1
7627 Xalpha_s, TRUE, FALSE,
7628 EL_CHAR('S'), -1, -1
7631 Xalpha_t, TRUE, FALSE,
7632 EL_CHAR('T'), -1, -1
7635 Xalpha_u, TRUE, FALSE,
7636 EL_CHAR('U'), -1, -1
7639 Xalpha_v, TRUE, FALSE,
7640 EL_CHAR('V'), -1, -1
7643 Xalpha_w, TRUE, FALSE,
7644 EL_CHAR('W'), -1, -1
7647 Xalpha_x, TRUE, FALSE,
7648 EL_CHAR('X'), -1, -1
7651 Xalpha_y, TRUE, FALSE,
7652 EL_CHAR('Y'), -1, -1
7655 Xalpha_z, TRUE, FALSE,
7656 EL_CHAR('Z'), -1, -1
7659 Xalpha_arrow_e, TRUE, FALSE,
7660 EL_CHAR('>'), -1, -1
7663 Xalpha_arrow_w, TRUE, FALSE,
7664 EL_CHAR('<'), -1, -1
7667 Xalpha_copyr, TRUE, FALSE,
7668 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7672 Ykey_1_blank, FALSE, FALSE,
7673 EL_EM_KEY_1, ACTION_COLLECTING, -1
7676 Ykey_2_blank, FALSE, FALSE,
7677 EL_EM_KEY_2, ACTION_COLLECTING, -1
7680 Ykey_3_blank, FALSE, FALSE,
7681 EL_EM_KEY_3, ACTION_COLLECTING, -1
7684 Ykey_4_blank, FALSE, FALSE,
7685 EL_EM_KEY_4, ACTION_COLLECTING, -1
7688 Ykey_5_blank, FALSE, FALSE,
7689 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7692 Ykey_6_blank, FALSE, FALSE,
7693 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7696 Ykey_7_blank, FALSE, FALSE,
7697 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7700 Ykey_8_blank, FALSE, FALSE,
7701 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7704 Ylenses_blank, FALSE, FALSE,
7705 EL_EMC_LENSES, ACTION_COLLECTING, -1
7708 Ymagnify_blank, FALSE, FALSE,
7709 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7712 Ygrass_blank, FALSE, FALSE,
7713 EL_EMC_GRASS, ACTION_SNAPPING, -1
7716 Ydirt_blank, FALSE, FALSE,
7717 EL_SAND, ACTION_SNAPPING, -1
7726 static struct Mapping_EM_to_RND_player
7735 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7739 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7743 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7747 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7751 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7755 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7759 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7763 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7767 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7771 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7775 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7779 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7783 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7787 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7791 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7795 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7799 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7803 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7807 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7811 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7815 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7819 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7823 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7827 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7831 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7835 EL_PLAYER_1, ACTION_DEFAULT, -1,
7839 EL_PLAYER_2, ACTION_DEFAULT, -1,
7843 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7847 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7851 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7855 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7859 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7863 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7867 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7871 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7875 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7879 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7883 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7887 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7891 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7895 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7899 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7903 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7907 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7911 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7915 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7919 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7923 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7927 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7931 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7935 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7939 EL_PLAYER_3, ACTION_DEFAULT, -1,
7943 EL_PLAYER_4, ACTION_DEFAULT, -1,
7952 int map_element_RND_to_EM_cave(int element_rnd)
7954 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7955 static boolean mapping_initialized = FALSE;
7957 if (!mapping_initialized)
7961 // return "Xalpha_quest" for all undefined elements in mapping array
7962 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7963 mapping_RND_to_EM[i] = Xalpha_quest;
7965 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7966 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7967 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7968 em_object_mapping_list[i].element_em;
7970 mapping_initialized = TRUE;
7973 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7975 Warn("invalid RND level element %d", element_rnd);
7980 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7983 int map_element_EM_to_RND_cave(int element_em_cave)
7985 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7986 static boolean mapping_initialized = FALSE;
7988 if (!mapping_initialized)
7992 // return "EL_UNKNOWN" for all undefined elements in mapping array
7993 for (i = 0; i < GAME_TILE_MAX; i++)
7994 mapping_EM_to_RND[i] = EL_UNKNOWN;
7996 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7997 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7998 em_object_mapping_list[i].element_rnd;
8000 mapping_initialized = TRUE;
8003 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8005 Warn("invalid EM cave element %d", element_em_cave);
8010 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8013 int map_element_EM_to_RND_game(int element_em_game)
8015 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8016 static boolean mapping_initialized = FALSE;
8018 if (!mapping_initialized)
8022 // return "EL_UNKNOWN" for all undefined elements in mapping array
8023 for (i = 0; i < GAME_TILE_MAX; i++)
8024 mapping_EM_to_RND[i] = EL_UNKNOWN;
8026 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8027 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8028 em_object_mapping_list[i].element_rnd;
8030 mapping_initialized = TRUE;
8033 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8035 Warn("invalid EM game element %d", element_em_game);
8040 return mapping_EM_to_RND[element_em_game];
8043 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8045 struct LevelInfo_EM *level_em = level->native_em_level;
8046 struct CAVE *cav = level_em->cav;
8049 for (i = 0; i < GAME_TILE_MAX; i++)
8050 cav->android_array[i] = Cblank;
8052 for (i = 0; i < level->num_android_clone_elements; i++)
8054 int element_rnd = level->android_clone_element[i];
8055 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8057 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8058 if (em_object_mapping_list[j].element_rnd == element_rnd)
8059 cav->android_array[em_object_mapping_list[j].element_em] =
8064 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8066 struct LevelInfo_EM *level_em = level->native_em_level;
8067 struct CAVE *cav = level_em->cav;
8070 level->num_android_clone_elements = 0;
8072 for (i = 0; i < GAME_TILE_MAX; i++)
8074 int element_em_cave = cav->android_array[i];
8076 boolean element_found = FALSE;
8078 if (element_em_cave == Cblank)
8081 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8083 for (j = 0; j < level->num_android_clone_elements; j++)
8084 if (level->android_clone_element[j] == element_rnd)
8085 element_found = TRUE;
8089 level->android_clone_element[level->num_android_clone_elements++] =
8092 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8097 if (level->num_android_clone_elements == 0)
8099 level->num_android_clone_elements = 1;
8100 level->android_clone_element[0] = EL_EMPTY;
8104 int map_direction_RND_to_EM(int direction)
8106 return (direction == MV_UP ? 0 :
8107 direction == MV_RIGHT ? 1 :
8108 direction == MV_DOWN ? 2 :
8109 direction == MV_LEFT ? 3 :
8113 int map_direction_EM_to_RND(int direction)
8115 return (direction == 0 ? MV_UP :
8116 direction == 1 ? MV_RIGHT :
8117 direction == 2 ? MV_DOWN :
8118 direction == 3 ? MV_LEFT :
8122 int map_element_RND_to_SP(int element_rnd)
8124 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8126 if (element_rnd >= EL_SP_START &&
8127 element_rnd <= EL_SP_END)
8128 element_sp = element_rnd - EL_SP_START;
8129 else if (element_rnd == EL_EMPTY_SPACE)
8131 else if (element_rnd == EL_INVISIBLE_WALL)
8137 int map_element_SP_to_RND(int element_sp)
8139 int element_rnd = EL_UNKNOWN;
8141 if (element_sp >= 0x00 &&
8143 element_rnd = EL_SP_START + element_sp;
8144 else if (element_sp == 0x28)
8145 element_rnd = EL_INVISIBLE_WALL;
8150 int map_action_SP_to_RND(int action_sp)
8154 case actActive: return ACTION_ACTIVE;
8155 case actImpact: return ACTION_IMPACT;
8156 case actExploding: return ACTION_EXPLODING;
8157 case actDigging: return ACTION_DIGGING;
8158 case actSnapping: return ACTION_SNAPPING;
8159 case actCollecting: return ACTION_COLLECTING;
8160 case actPassing: return ACTION_PASSING;
8161 case actPushing: return ACTION_PUSHING;
8162 case actDropping: return ACTION_DROPPING;
8164 default: return ACTION_DEFAULT;
8168 int map_element_RND_to_MM(int element_rnd)
8170 return (element_rnd >= EL_MM_START_1 &&
8171 element_rnd <= EL_MM_END_1 ?
8172 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8174 element_rnd >= EL_MM_START_2 &&
8175 element_rnd <= EL_MM_END_2 ?
8176 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8178 element_rnd >= EL_MM_START_3 &&
8179 element_rnd <= EL_MM_END_3 ?
8180 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8182 element_rnd >= EL_CHAR_START &&
8183 element_rnd <= EL_CHAR_END ?
8184 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8186 element_rnd >= EL_MM_RUNTIME_START &&
8187 element_rnd <= EL_MM_RUNTIME_END ?
8188 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8190 EL_MM_EMPTY_NATIVE);
8193 int map_element_MM_to_RND(int element_mm)
8195 return (element_mm == EL_MM_EMPTY_NATIVE ||
8196 element_mm == EL_DF_EMPTY_NATIVE ?
8199 element_mm >= EL_MM_START_1_NATIVE &&
8200 element_mm <= EL_MM_END_1_NATIVE ?
8201 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8203 element_mm >= EL_MM_START_2_NATIVE &&
8204 element_mm <= EL_MM_END_2_NATIVE ?
8205 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8207 element_mm >= EL_MM_START_3_NATIVE &&
8208 element_mm <= EL_MM_END_3_NATIVE ?
8209 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8211 element_mm >= EL_MM_CHAR_START_NATIVE &&
8212 element_mm <= EL_MM_CHAR_END_NATIVE ?
8213 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8215 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8216 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8217 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8222 int map_action_MM_to_RND(int action_mm)
8224 // all MM actions are defined to exactly match their RND counterparts
8228 int map_sound_MM_to_RND(int sound_mm)
8232 case SND_MM_GAME_LEVELTIME_CHARGING:
8233 return SND_GAME_LEVELTIME_CHARGING;
8235 case SND_MM_GAME_HEALTH_CHARGING:
8236 return SND_GAME_HEALTH_CHARGING;
8239 return SND_UNDEFINED;
8243 int map_mm_wall_element(int element)
8245 return (element >= EL_MM_STEEL_WALL_START &&
8246 element <= EL_MM_STEEL_WALL_END ?
8249 element >= EL_MM_WOODEN_WALL_START &&
8250 element <= EL_MM_WOODEN_WALL_END ?
8253 element >= EL_MM_ICE_WALL_START &&
8254 element <= EL_MM_ICE_WALL_END ?
8257 element >= EL_MM_AMOEBA_WALL_START &&
8258 element <= EL_MM_AMOEBA_WALL_END ?
8261 element >= EL_DF_STEEL_WALL_START &&
8262 element <= EL_DF_STEEL_WALL_END ?
8265 element >= EL_DF_WOODEN_WALL_START &&
8266 element <= EL_DF_WOODEN_WALL_END ?
8272 int map_mm_wall_element_editor(int element)
8276 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8277 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8278 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8279 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8280 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8281 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8283 default: return element;
8287 int get_next_element(int element)
8291 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8292 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8293 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8294 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8295 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8296 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8297 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8298 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8299 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8300 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8301 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8303 default: return element;
8307 int el2img_mm(int element_mm)
8309 return el2img(map_element_MM_to_RND(element_mm));
8312 int el_act2img_mm(int element_mm, int action)
8314 return el_act2img(map_element_MM_to_RND(element_mm), action);
8317 int el_act_dir2img(int element, int action, int direction)
8319 element = GFX_ELEMENT(element);
8320 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8322 // direction_graphic[][] == graphic[] for undefined direction graphics
8323 return element_info[element].direction_graphic[action][direction];
8326 static int el_act_dir2crm(int element, int action, int direction)
8328 element = GFX_ELEMENT(element);
8329 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8331 // direction_graphic[][] == graphic[] for undefined direction graphics
8332 return element_info[element].direction_crumbled[action][direction];
8335 int el_act2img(int element, int action)
8337 element = GFX_ELEMENT(element);
8339 return element_info[element].graphic[action];
8342 int el_act2crm(int element, int action)
8344 element = GFX_ELEMENT(element);
8346 return element_info[element].crumbled[action];
8349 int el_dir2img(int element, int direction)
8351 element = GFX_ELEMENT(element);
8353 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8356 int el2baseimg(int element)
8358 return element_info[element].graphic[ACTION_DEFAULT];
8361 int el2img(int element)
8363 element = GFX_ELEMENT(element);
8365 return element_info[element].graphic[ACTION_DEFAULT];
8368 int el2edimg(int element)
8370 element = GFX_ELEMENT(element);
8372 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8375 int el2preimg(int element)
8377 element = GFX_ELEMENT(element);
8379 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8382 int el2panelimg(int element)
8384 element = GFX_ELEMENT(element);
8386 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8389 int font2baseimg(int font_nr)
8391 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8394 int getBeltNrFromBeltElement(int element)
8396 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8397 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8398 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8401 int getBeltNrFromBeltActiveElement(int element)
8403 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8404 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8405 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8408 int getBeltNrFromBeltSwitchElement(int element)
8410 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8411 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8412 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8415 int getBeltDirNrFromBeltElement(int element)
8417 static int belt_base_element[4] =
8419 EL_CONVEYOR_BELT_1_LEFT,
8420 EL_CONVEYOR_BELT_2_LEFT,
8421 EL_CONVEYOR_BELT_3_LEFT,
8422 EL_CONVEYOR_BELT_4_LEFT
8425 int belt_nr = getBeltNrFromBeltElement(element);
8426 int belt_dir_nr = element - belt_base_element[belt_nr];
8428 return (belt_dir_nr % 3);
8431 int getBeltDirNrFromBeltSwitchElement(int element)
8433 static int belt_base_element[4] =
8435 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8436 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8437 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8438 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8441 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8442 int belt_dir_nr = element - belt_base_element[belt_nr];
8444 return (belt_dir_nr % 3);
8447 int getBeltDirFromBeltElement(int element)
8449 static int belt_move_dir[3] =
8456 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8458 return belt_move_dir[belt_dir_nr];
8461 int getBeltDirFromBeltSwitchElement(int element)
8463 static int belt_move_dir[3] =
8470 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8472 return belt_move_dir[belt_dir_nr];
8475 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8477 static int belt_base_element[4] =
8479 EL_CONVEYOR_BELT_1_LEFT,
8480 EL_CONVEYOR_BELT_2_LEFT,
8481 EL_CONVEYOR_BELT_3_LEFT,
8482 EL_CONVEYOR_BELT_4_LEFT
8485 return belt_base_element[belt_nr] + belt_dir_nr;
8488 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8490 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8492 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8495 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8497 static int belt_base_element[4] =
8499 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8500 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8501 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8502 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8505 return belt_base_element[belt_nr] + belt_dir_nr;
8508 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8510 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8512 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8515 boolean swapTiles_EM(boolean is_pre_emc_cave)
8517 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8520 boolean getTeamMode_EM(void)
8522 return game.team_mode || network_playing;
8525 boolean isActivePlayer_EM(int player_nr)
8527 return stored_player[player_nr].active;
8530 unsigned int InitRND(int seed)
8532 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8533 return InitEngineRandom_EM(seed);
8534 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8535 return InitEngineRandom_SP(seed);
8536 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8537 return InitEngineRandom_MM(seed);
8539 return InitEngineRandom_RND(seed);
8542 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8543 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8545 static int get_effective_element_EM(int tile, int frame_em)
8547 int element = object_mapping[tile].element_rnd;
8548 int action = object_mapping[tile].action;
8549 boolean is_backside = object_mapping[tile].is_backside;
8550 boolean action_removing = (action == ACTION_DIGGING ||
8551 action == ACTION_SNAPPING ||
8552 action == ACTION_COLLECTING);
8560 return (frame_em > 5 ? EL_EMPTY : element);
8566 else // frame_em == 7
8577 case Ydiamond_stone:
8581 case Xdrip_stretchB:
8597 case Ymagnify_blank:
8600 case Xsand_stonein_1:
8601 case Xsand_stonein_2:
8602 case Xsand_stonein_3:
8603 case Xsand_stonein_4:
8607 return (is_backside || action_removing ? EL_EMPTY : element);
8612 static boolean check_linear_animation_EM(int tile)
8616 case Xsand_stonesand_1:
8617 case Xsand_stonesand_quickout_1:
8618 case Xsand_sandstone_1:
8619 case Xsand_stonein_1:
8620 case Xsand_stoneout_1:
8648 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8649 boolean has_crumbled_graphics,
8650 int crumbled, int sync_frame)
8652 // if element can be crumbled, but certain action graphics are just empty
8653 // space (like instantly snapping sand to empty space in 1 frame), do not
8654 // treat these empty space graphics as crumbled graphics in EMC engine
8655 if (crumbled == IMG_EMPTY_SPACE)
8656 has_crumbled_graphics = FALSE;
8658 if (has_crumbled_graphics)
8660 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8661 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8662 g_crumbled->anim_delay,
8663 g_crumbled->anim_mode,
8664 g_crumbled->anim_start_frame,
8667 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8668 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8670 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8671 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8673 g_em->has_crumbled_graphics = TRUE;
8677 g_em->crumbled_bitmap = NULL;
8678 g_em->crumbled_src_x = 0;
8679 g_em->crumbled_src_y = 0;
8680 g_em->crumbled_border_size = 0;
8681 g_em->crumbled_tile_size = 0;
8683 g_em->has_crumbled_graphics = FALSE;
8688 void ResetGfxAnimation_EM(int x, int y, int tile)
8694 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8695 int tile, int frame_em, int x, int y)
8697 int action = object_mapping[tile].action;
8698 int direction = object_mapping[tile].direction;
8699 int effective_element = get_effective_element_EM(tile, frame_em);
8700 int graphic = (direction == MV_NONE ?
8701 el_act2img(effective_element, action) :
8702 el_act_dir2img(effective_element, action, direction));
8703 struct GraphicInfo *g = &graphic_info[graphic];
8705 boolean action_removing = (action == ACTION_DIGGING ||
8706 action == ACTION_SNAPPING ||
8707 action == ACTION_COLLECTING);
8708 boolean action_moving = (action == ACTION_FALLING ||
8709 action == ACTION_MOVING ||
8710 action == ACTION_PUSHING ||
8711 action == ACTION_EATING ||
8712 action == ACTION_FILLING ||
8713 action == ACTION_EMPTYING);
8714 boolean action_falling = (action == ACTION_FALLING ||
8715 action == ACTION_FILLING ||
8716 action == ACTION_EMPTYING);
8718 // special case: graphic uses "2nd movement tile" and has defined
8719 // 7 frames for movement animation (or less) => use default graphic
8720 // for last (8th) frame which ends the movement animation
8721 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8723 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8724 graphic = (direction == MV_NONE ?
8725 el_act2img(effective_element, action) :
8726 el_act_dir2img(effective_element, action, direction));
8728 g = &graphic_info[graphic];
8731 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8735 else if (action_moving)
8737 boolean is_backside = object_mapping[tile].is_backside;
8741 int direction = object_mapping[tile].direction;
8742 int move_dir = (action_falling ? MV_DOWN : direction);
8747 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8748 if (g->double_movement && frame_em == 0)
8752 if (move_dir == MV_LEFT)
8753 GfxFrame[x - 1][y] = GfxFrame[x][y];
8754 else if (move_dir == MV_RIGHT)
8755 GfxFrame[x + 1][y] = GfxFrame[x][y];
8756 else if (move_dir == MV_UP)
8757 GfxFrame[x][y - 1] = GfxFrame[x][y];
8758 else if (move_dir == MV_DOWN)
8759 GfxFrame[x][y + 1] = GfxFrame[x][y];
8766 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8767 if (tile == Xsand_stonesand_quickout_1 ||
8768 tile == Xsand_stonesand_quickout_2)
8772 if (graphic_info[graphic].anim_global_sync)
8773 sync_frame = FrameCounter;
8774 else if (graphic_info[graphic].anim_global_anim_sync)
8775 sync_frame = getGlobalAnimSyncFrame();
8776 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8777 sync_frame = GfxFrame[x][y];
8779 sync_frame = 0; // playfield border (pseudo steel)
8781 SetRandomAnimationValue(x, y);
8783 int frame = getAnimationFrame(g->anim_frames,
8786 g->anim_start_frame,
8789 g_em->unique_identifier =
8790 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8793 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8794 int tile, int frame_em, int x, int y)
8796 int action = object_mapping[tile].action;
8797 int direction = object_mapping[tile].direction;
8798 boolean is_backside = object_mapping[tile].is_backside;
8799 int effective_element = get_effective_element_EM(tile, frame_em);
8800 int effective_action = action;
8801 int graphic = (direction == MV_NONE ?
8802 el_act2img(effective_element, effective_action) :
8803 el_act_dir2img(effective_element, effective_action,
8805 int crumbled = (direction == MV_NONE ?
8806 el_act2crm(effective_element, effective_action) :
8807 el_act_dir2crm(effective_element, effective_action,
8809 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8810 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8811 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8812 struct GraphicInfo *g = &graphic_info[graphic];
8815 // special case: graphic uses "2nd movement tile" and has defined
8816 // 7 frames for movement animation (or less) => use default graphic
8817 // for last (8th) frame which ends the movement animation
8818 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8820 effective_action = ACTION_DEFAULT;
8821 graphic = (direction == MV_NONE ?
8822 el_act2img(effective_element, effective_action) :
8823 el_act_dir2img(effective_element, effective_action,
8825 crumbled = (direction == MV_NONE ?
8826 el_act2crm(effective_element, effective_action) :
8827 el_act_dir2crm(effective_element, effective_action,
8830 g = &graphic_info[graphic];
8833 if (graphic_info[graphic].anim_global_sync)
8834 sync_frame = FrameCounter;
8835 else if (graphic_info[graphic].anim_global_anim_sync)
8836 sync_frame = getGlobalAnimSyncFrame();
8837 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8838 sync_frame = GfxFrame[x][y];
8840 sync_frame = 0; // playfield border (pseudo steel)
8842 SetRandomAnimationValue(x, y);
8844 int frame = getAnimationFrame(g->anim_frames,
8847 g->anim_start_frame,
8850 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8851 g->double_movement && is_backside);
8853 // (updating the "crumbled" graphic definitions is probably not really needed,
8854 // as animations for crumbled graphics can't be longer than one EMC cycle)
8855 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8859 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8860 int player_nr, int anim, int frame_em)
8862 int element = player_mapping[player_nr][anim].element_rnd;
8863 int action = player_mapping[player_nr][anim].action;
8864 int direction = player_mapping[player_nr][anim].direction;
8865 int graphic = (direction == MV_NONE ?
8866 el_act2img(element, action) :
8867 el_act_dir2img(element, action, direction));
8868 struct GraphicInfo *g = &graphic_info[graphic];
8871 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8873 stored_player[player_nr].StepFrame = frame_em;
8875 sync_frame = stored_player[player_nr].Frame;
8877 int frame = getAnimationFrame(g->anim_frames,
8880 g->anim_start_frame,
8883 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8884 &g_em->src_x, &g_em->src_y, FALSE);
8887 void InitGraphicInfo_EM(void)
8891 // always start with reliable default values
8892 for (i = 0; i < GAME_TILE_MAX; i++)
8894 object_mapping[i].element_rnd = EL_UNKNOWN;
8895 object_mapping[i].is_backside = FALSE;
8896 object_mapping[i].action = ACTION_DEFAULT;
8897 object_mapping[i].direction = MV_NONE;
8900 // always start with reliable default values
8901 for (p = 0; p < MAX_PLAYERS; p++)
8903 for (i = 0; i < PLY_MAX; i++)
8905 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8906 player_mapping[p][i].action = ACTION_DEFAULT;
8907 player_mapping[p][i].direction = MV_NONE;
8911 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8913 int e = em_object_mapping_list[i].element_em;
8915 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8916 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8918 if (em_object_mapping_list[i].action != -1)
8919 object_mapping[e].action = em_object_mapping_list[i].action;
8921 if (em_object_mapping_list[i].direction != -1)
8922 object_mapping[e].direction =
8923 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8926 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8928 int a = em_player_mapping_list[i].action_em;
8929 int p = em_player_mapping_list[i].player_nr;
8931 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8933 if (em_player_mapping_list[i].action != -1)
8934 player_mapping[p][a].action = em_player_mapping_list[i].action;
8936 if (em_player_mapping_list[i].direction != -1)
8937 player_mapping[p][a].direction =
8938 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8941 for (i = 0; i < GAME_TILE_MAX; i++)
8943 int element = object_mapping[i].element_rnd;
8944 int action = object_mapping[i].action;
8945 int direction = object_mapping[i].direction;
8946 boolean is_backside = object_mapping[i].is_backside;
8947 boolean action_exploding = ((action == ACTION_EXPLODING ||
8948 action == ACTION_SMASHED_BY_ROCK ||
8949 action == ACTION_SMASHED_BY_SPRING) &&
8950 element != EL_DIAMOND);
8951 boolean action_active = (action == ACTION_ACTIVE);
8952 boolean action_other = (action == ACTION_OTHER);
8954 for (j = 0; j < 8; j++)
8956 int effective_element = get_effective_element_EM(i, j);
8957 int effective_action = (j < 7 ? action :
8958 i == Xdrip_stretch ? action :
8959 i == Xdrip_stretchB ? action :
8960 i == Ydrip_1_s ? action :
8961 i == Ydrip_1_sB ? action :
8962 i == Yball_1 ? action :
8963 i == Xball_2 ? action :
8964 i == Yball_2 ? action :
8965 i == Yball_blank ? action :
8966 i == Ykey_1_blank ? action :
8967 i == Ykey_2_blank ? action :
8968 i == Ykey_3_blank ? action :
8969 i == Ykey_4_blank ? action :
8970 i == Ykey_5_blank ? action :
8971 i == Ykey_6_blank ? action :
8972 i == Ykey_7_blank ? action :
8973 i == Ykey_8_blank ? action :
8974 i == Ylenses_blank ? action :
8975 i == Ymagnify_blank ? action :
8976 i == Ygrass_blank ? action :
8977 i == Ydirt_blank ? action :
8978 i == Xsand_stonein_1 ? action :
8979 i == Xsand_stonein_2 ? action :
8980 i == Xsand_stonein_3 ? action :
8981 i == Xsand_stonein_4 ? action :
8982 i == Xsand_stoneout_1 ? action :
8983 i == Xsand_stoneout_2 ? action :
8984 i == Xboom_android ? ACTION_EXPLODING :
8985 action_exploding ? ACTION_EXPLODING :
8986 action_active ? action :
8987 action_other ? action :
8989 int graphic = (el_act_dir2img(effective_element, effective_action,
8991 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8993 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8994 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8995 boolean has_action_graphics = (graphic != base_graphic);
8996 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8997 struct GraphicInfo *g = &graphic_info[graphic];
8998 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9001 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9002 boolean special_animation = (action != ACTION_DEFAULT &&
9003 g->anim_frames == 3 &&
9004 g->anim_delay == 2 &&
9005 g->anim_mode & ANIM_LINEAR);
9006 int sync_frame = (i == Xdrip_stretch ? 7 :
9007 i == Xdrip_stretchB ? 7 :
9008 i == Ydrip_2_s ? j + 8 :
9009 i == Ydrip_2_sB ? j + 8 :
9018 i == Xfake_acid_1 ? 0 :
9019 i == Xfake_acid_2 ? 10 :
9020 i == Xfake_acid_3 ? 20 :
9021 i == Xfake_acid_4 ? 30 :
9022 i == Xfake_acid_5 ? 40 :
9023 i == Xfake_acid_6 ? 50 :
9024 i == Xfake_acid_7 ? 60 :
9025 i == Xfake_acid_8 ? 70 :
9026 i == Xfake_acid_1_player ? 0 :
9027 i == Xfake_acid_2_player ? 10 :
9028 i == Xfake_acid_3_player ? 20 :
9029 i == Xfake_acid_4_player ? 30 :
9030 i == Xfake_acid_5_player ? 40 :
9031 i == Xfake_acid_6_player ? 50 :
9032 i == Xfake_acid_7_player ? 60 :
9033 i == Xfake_acid_8_player ? 70 :
9035 i == Yball_2 ? j + 8 :
9036 i == Yball_blank ? j + 1 :
9037 i == Ykey_1_blank ? j + 1 :
9038 i == Ykey_2_blank ? j + 1 :
9039 i == Ykey_3_blank ? j + 1 :
9040 i == Ykey_4_blank ? j + 1 :
9041 i == Ykey_5_blank ? j + 1 :
9042 i == Ykey_6_blank ? j + 1 :
9043 i == Ykey_7_blank ? j + 1 :
9044 i == Ykey_8_blank ? j + 1 :
9045 i == Ylenses_blank ? j + 1 :
9046 i == Ymagnify_blank ? j + 1 :
9047 i == Ygrass_blank ? j + 1 :
9048 i == Ydirt_blank ? j + 1 :
9049 i == Xamoeba_1 ? 0 :
9050 i == Xamoeba_2 ? 1 :
9051 i == Xamoeba_3 ? 2 :
9052 i == Xamoeba_4 ? 3 :
9053 i == Xamoeba_5 ? 0 :
9054 i == Xamoeba_6 ? 1 :
9055 i == Xamoeba_7 ? 2 :
9056 i == Xamoeba_8 ? 3 :
9057 i == Xexit_2 ? j + 8 :
9058 i == Xexit_3 ? j + 16 :
9059 i == Xdynamite_1 ? 0 :
9060 i == Xdynamite_2 ? 8 :
9061 i == Xdynamite_3 ? 16 :
9062 i == Xdynamite_4 ? 24 :
9063 i == Xsand_stonein_1 ? j + 1 :
9064 i == Xsand_stonein_2 ? j + 9 :
9065 i == Xsand_stonein_3 ? j + 17 :
9066 i == Xsand_stonein_4 ? j + 25 :
9067 i == Xsand_stoneout_1 && j == 0 ? 0 :
9068 i == Xsand_stoneout_1 && j == 1 ? 0 :
9069 i == Xsand_stoneout_1 && j == 2 ? 1 :
9070 i == Xsand_stoneout_1 && j == 3 ? 2 :
9071 i == Xsand_stoneout_1 && j == 4 ? 2 :
9072 i == Xsand_stoneout_1 && j == 5 ? 3 :
9073 i == Xsand_stoneout_1 && j == 6 ? 4 :
9074 i == Xsand_stoneout_1 && j == 7 ? 4 :
9075 i == Xsand_stoneout_2 && j == 0 ? 5 :
9076 i == Xsand_stoneout_2 && j == 1 ? 6 :
9077 i == Xsand_stoneout_2 && j == 2 ? 7 :
9078 i == Xsand_stoneout_2 && j == 3 ? 8 :
9079 i == Xsand_stoneout_2 && j == 4 ? 9 :
9080 i == Xsand_stoneout_2 && j == 5 ? 11 :
9081 i == Xsand_stoneout_2 && j == 6 ? 13 :
9082 i == Xsand_stoneout_2 && j == 7 ? 15 :
9083 i == Xboom_bug && j == 1 ? 2 :
9084 i == Xboom_bug && j == 2 ? 2 :
9085 i == Xboom_bug && j == 3 ? 4 :
9086 i == Xboom_bug && j == 4 ? 4 :
9087 i == Xboom_bug && j == 5 ? 2 :
9088 i == Xboom_bug && j == 6 ? 2 :
9089 i == Xboom_bug && j == 7 ? 0 :
9090 i == Xboom_tank && j == 1 ? 2 :
9091 i == Xboom_tank && j == 2 ? 2 :
9092 i == Xboom_tank && j == 3 ? 4 :
9093 i == Xboom_tank && j == 4 ? 4 :
9094 i == Xboom_tank && j == 5 ? 2 :
9095 i == Xboom_tank && j == 6 ? 2 :
9096 i == Xboom_tank && j == 7 ? 0 :
9097 i == Xboom_android && j == 7 ? 6 :
9098 i == Xboom_1 && j == 1 ? 2 :
9099 i == Xboom_1 && j == 2 ? 2 :
9100 i == Xboom_1 && j == 3 ? 4 :
9101 i == Xboom_1 && j == 4 ? 4 :
9102 i == Xboom_1 && j == 5 ? 6 :
9103 i == Xboom_1 && j == 6 ? 6 :
9104 i == Xboom_1 && j == 7 ? 8 :
9105 i == Xboom_2 && j == 0 ? 8 :
9106 i == Xboom_2 && j == 1 ? 8 :
9107 i == Xboom_2 && j == 2 ? 10 :
9108 i == Xboom_2 && j == 3 ? 10 :
9109 i == Xboom_2 && j == 4 ? 10 :
9110 i == Xboom_2 && j == 5 ? 12 :
9111 i == Xboom_2 && j == 6 ? 12 :
9112 i == Xboom_2 && j == 7 ? 12 :
9113 special_animation && j == 4 ? 3 :
9114 effective_action != action ? 0 :
9116 int frame = getAnimationFrame(g->anim_frames,
9119 g->anim_start_frame,
9122 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9123 g->double_movement && is_backside);
9125 g_em->bitmap = src_bitmap;
9126 g_em->src_x = src_x;
9127 g_em->src_y = src_y;
9128 g_em->src_offset_x = 0;
9129 g_em->src_offset_y = 0;
9130 g_em->dst_offset_x = 0;
9131 g_em->dst_offset_y = 0;
9132 g_em->width = TILEX;
9133 g_em->height = TILEY;
9135 g_em->preserve_background = FALSE;
9137 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9140 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9141 effective_action == ACTION_MOVING ||
9142 effective_action == ACTION_PUSHING ||
9143 effective_action == ACTION_EATING)) ||
9144 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9145 effective_action == ACTION_EMPTYING)))
9148 (effective_action == ACTION_FALLING ||
9149 effective_action == ACTION_FILLING ||
9150 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9151 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9152 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9153 int num_steps = (i == Ydrip_1_s ? 16 :
9154 i == Ydrip_1_sB ? 16 :
9155 i == Ydrip_2_s ? 16 :
9156 i == Ydrip_2_sB ? 16 :
9157 i == Xsand_stonein_1 ? 32 :
9158 i == Xsand_stonein_2 ? 32 :
9159 i == Xsand_stonein_3 ? 32 :
9160 i == Xsand_stonein_4 ? 32 :
9161 i == Xsand_stoneout_1 ? 16 :
9162 i == Xsand_stoneout_2 ? 16 : 8);
9163 int cx = ABS(dx) * (TILEX / num_steps);
9164 int cy = ABS(dy) * (TILEY / num_steps);
9165 int step_frame = (i == Ydrip_2_s ? j + 8 :
9166 i == Ydrip_2_sB ? j + 8 :
9167 i == Xsand_stonein_2 ? j + 8 :
9168 i == Xsand_stonein_3 ? j + 16 :
9169 i == Xsand_stonein_4 ? j + 24 :
9170 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9171 int step = (is_backside ? step_frame : num_steps - step_frame);
9173 if (is_backside) // tile where movement starts
9175 if (dx < 0 || dy < 0)
9177 g_em->src_offset_x = cx * step;
9178 g_em->src_offset_y = cy * step;
9182 g_em->dst_offset_x = cx * step;
9183 g_em->dst_offset_y = cy * step;
9186 else // tile where movement ends
9188 if (dx < 0 || dy < 0)
9190 g_em->dst_offset_x = cx * step;
9191 g_em->dst_offset_y = cy * step;
9195 g_em->src_offset_x = cx * step;
9196 g_em->src_offset_y = cy * step;
9200 g_em->width = TILEX - cx * step;
9201 g_em->height = TILEY - cy * step;
9204 // create unique graphic identifier to decide if tile must be redrawn
9205 /* bit 31 - 16 (16 bit): EM style graphic
9206 bit 15 - 12 ( 4 bit): EM style frame
9207 bit 11 - 6 ( 6 bit): graphic width
9208 bit 5 - 0 ( 6 bit): graphic height */
9209 g_em->unique_identifier =
9210 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9214 for (i = 0; i < GAME_TILE_MAX; i++)
9216 for (j = 0; j < 8; j++)
9218 int element = object_mapping[i].element_rnd;
9219 int action = object_mapping[i].action;
9220 int direction = object_mapping[i].direction;
9221 boolean is_backside = object_mapping[i].is_backside;
9222 int graphic_action = el_act_dir2img(element, action, direction);
9223 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9225 if ((action == ACTION_SMASHED_BY_ROCK ||
9226 action == ACTION_SMASHED_BY_SPRING ||
9227 action == ACTION_EATING) &&
9228 graphic_action == graphic_default)
9230 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9231 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9232 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9233 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9236 // no separate animation for "smashed by rock" -- use rock instead
9237 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9238 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9240 g_em->bitmap = g_xx->bitmap;
9241 g_em->src_x = g_xx->src_x;
9242 g_em->src_y = g_xx->src_y;
9243 g_em->src_offset_x = g_xx->src_offset_x;
9244 g_em->src_offset_y = g_xx->src_offset_y;
9245 g_em->dst_offset_x = g_xx->dst_offset_x;
9246 g_em->dst_offset_y = g_xx->dst_offset_y;
9247 g_em->width = g_xx->width;
9248 g_em->height = g_xx->height;
9249 g_em->unique_identifier = g_xx->unique_identifier;
9252 g_em->preserve_background = TRUE;
9257 for (p = 0; p < MAX_PLAYERS; p++)
9259 for (i = 0; i < PLY_MAX; i++)
9261 int element = player_mapping[p][i].element_rnd;
9262 int action = player_mapping[p][i].action;
9263 int direction = player_mapping[p][i].direction;
9265 for (j = 0; j < 8; j++)
9267 int effective_element = element;
9268 int effective_action = action;
9269 int graphic = (direction == MV_NONE ?
9270 el_act2img(effective_element, effective_action) :
9271 el_act_dir2img(effective_element, effective_action,
9273 struct GraphicInfo *g = &graphic_info[graphic];
9274 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9278 int frame = getAnimationFrame(g->anim_frames,
9281 g->anim_start_frame,
9284 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9286 g_em->bitmap = src_bitmap;
9287 g_em->src_x = src_x;
9288 g_em->src_y = src_y;
9289 g_em->src_offset_x = 0;
9290 g_em->src_offset_y = 0;
9291 g_em->dst_offset_x = 0;
9292 g_em->dst_offset_y = 0;
9293 g_em->width = TILEX;
9294 g_em->height = TILEY;
9300 static void CheckSaveEngineSnapshot_EM(int frame,
9301 boolean any_player_moving,
9302 boolean any_player_snapping,
9303 boolean any_player_dropping)
9305 if (frame == 7 && !any_player_dropping)
9307 if (!local_player->was_waiting)
9309 if (!CheckSaveEngineSnapshotToList())
9312 local_player->was_waiting = TRUE;
9315 else if (any_player_moving || any_player_snapping || any_player_dropping)
9317 local_player->was_waiting = FALSE;
9321 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9322 boolean murphy_is_dropping)
9324 if (murphy_is_waiting)
9326 if (!local_player->was_waiting)
9328 if (!CheckSaveEngineSnapshotToList())
9331 local_player->was_waiting = TRUE;
9336 local_player->was_waiting = FALSE;
9340 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9341 boolean button_released)
9343 if (button_released)
9345 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9346 CheckSaveEngineSnapshotToList();
9348 else if (element_clicked)
9350 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9351 CheckSaveEngineSnapshotToList();
9353 game.snapshot.changed_action = TRUE;
9357 boolean CheckSingleStepMode_EM(int frame,
9358 boolean any_player_moving,
9359 boolean any_player_snapping,
9360 boolean any_player_dropping)
9362 if (tape.single_step && tape.recording && !tape.pausing)
9363 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9364 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9366 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9367 any_player_snapping, any_player_dropping);
9369 return tape.pausing;
9372 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9373 boolean murphy_is_dropping)
9375 boolean murphy_starts_dropping = FALSE;
9378 for (i = 0; i < MAX_PLAYERS; i++)
9379 if (stored_player[i].force_dropping)
9380 murphy_starts_dropping = TRUE;
9382 if (tape.single_step && tape.recording && !tape.pausing)
9383 if (murphy_is_waiting && !murphy_starts_dropping)
9384 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9386 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9389 void CheckSingleStepMode_MM(boolean element_clicked,
9390 boolean button_released)
9392 if (tape.single_step && tape.recording && !tape.pausing)
9393 if (button_released)
9394 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9396 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9399 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9400 int graphic, int sync_frame)
9402 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9404 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9407 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9409 return (IS_NEXT_FRAME(sync_frame, graphic));
9412 int getGraphicInfo_Delay(int graphic)
9414 return graphic_info[graphic].anim_delay;
9417 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9419 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9422 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9428 void PlayMenuSoundExt(int sound)
9430 if (sound == SND_UNDEFINED)
9433 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9434 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9437 if (IS_LOOP_SOUND(sound))
9438 PlaySoundLoop(sound);
9443 void PlayMenuSound(void)
9445 PlayMenuSoundExt(menu.sound[game_status]);
9448 void PlayMenuSoundStereo(int sound, int stereo_position)
9450 if (sound == SND_UNDEFINED)
9453 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9454 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9457 if (IS_LOOP_SOUND(sound))
9458 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9460 PlaySoundStereo(sound, stereo_position);
9463 void PlayMenuSoundIfLoopExt(int sound)
9465 if (sound == SND_UNDEFINED)
9468 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9469 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9472 if (IS_LOOP_SOUND(sound))
9473 PlaySoundLoop(sound);
9476 void PlayMenuSoundIfLoop(void)
9478 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9481 void PlayMenuMusicExt(int music)
9483 if (music == MUS_UNDEFINED)
9486 if (!setup.sound_music)
9489 if (IS_LOOP_MUSIC(music))
9490 PlayMusicLoop(music);
9495 void PlayMenuMusic(void)
9497 char *curr_music = getCurrentlyPlayingMusicFilename();
9498 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9500 if (!strEqual(curr_music, next_music))
9501 PlayMenuMusicExt(menu.music[game_status]);
9504 void PlayMenuSoundsAndMusic(void)
9510 static void FadeMenuSounds(void)
9515 static void FadeMenuMusic(void)
9517 char *curr_music = getCurrentlyPlayingMusicFilename();
9518 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9520 if (!strEqual(curr_music, next_music))
9524 void FadeMenuSoundsAndMusic(void)
9530 void PlaySoundActivating(void)
9533 PlaySound(SND_MENU_ITEM_ACTIVATING);
9537 void PlaySoundSelecting(void)
9540 PlaySound(SND_MENU_ITEM_SELECTING);
9544 void ToggleFullscreenIfNeeded(void)
9546 // if setup and video fullscreen state are already matching, nothing do do
9547 if (setup.fullscreen == video.fullscreen_enabled ||
9548 !video.fullscreen_available)
9551 SDLSetWindowFullscreen(setup.fullscreen);
9553 // set setup value according to successfully changed fullscreen mode
9554 setup.fullscreen = video.fullscreen_enabled;
9557 void ChangeWindowScalingIfNeeded(void)
9559 // if setup and video window scaling are already matching, nothing do do
9560 if (setup.window_scaling_percent == video.window_scaling_percent ||
9561 video.fullscreen_enabled)
9564 SDLSetWindowScaling(setup.window_scaling_percent);
9566 // set setup value according to successfully changed window scaling
9567 setup.window_scaling_percent = video.window_scaling_percent;
9570 void ChangeVsyncModeIfNeeded(void)
9572 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9573 int video_vsync_mode = video.vsync_mode;
9575 // if setup and video vsync mode are already matching, nothing do do
9576 if (setup_vsync_mode == video_vsync_mode)
9579 // if renderer is using OpenGL, vsync mode can directly be changed
9580 SDLSetScreenVsyncMode(setup.vsync_mode);
9582 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9583 if (video.vsync_mode == video_vsync_mode)
9585 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9587 // save backbuffer content which gets lost when re-creating screen
9588 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9590 // force re-creating screen and renderer to set new vsync mode
9591 video.fullscreen_enabled = !setup.fullscreen;
9593 // when creating new renderer, destroy textures linked to old renderer
9594 FreeAllImageTextures(); // needs old renderer to free the textures
9596 // re-create screen and renderer (including change of vsync mode)
9597 ChangeVideoModeIfNeeded(setup.fullscreen);
9599 // set setup value according to successfully changed fullscreen mode
9600 setup.fullscreen = video.fullscreen_enabled;
9602 // restore backbuffer content from temporary backbuffer backup bitmap
9603 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9604 FreeBitmap(tmp_backbuffer);
9606 // update visible window/screen
9607 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9609 // when changing vsync mode, re-create textures for new renderer
9610 InitImageTextures();
9613 // set setup value according to successfully changed vsync mode
9614 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9617 static void JoinRectangles(int *x, int *y, int *width, int *height,
9618 int x2, int y2, int width2, int height2)
9620 // do not join with "off-screen" rectangle
9621 if (x2 == -1 || y2 == -1)
9626 *width = MAX(*width, width2);
9627 *height = MAX(*height, height2);
9630 void SetAnimStatus(int anim_status_new)
9632 if (anim_status_new == GAME_MODE_MAIN)
9633 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9634 else if (anim_status_new == GAME_MODE_NAMES)
9635 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9636 else if (anim_status_new == GAME_MODE_SCORES)
9637 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9639 global.anim_status_next = anim_status_new;
9641 // directly set screen modes that are entered without fading
9642 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9643 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9644 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9645 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9646 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9647 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9648 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9649 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9650 global.anim_status = global.anim_status_next;
9653 void SetGameStatus(int game_status_new)
9655 if (game_status_new != game_status)
9656 game_status_last_screen = game_status;
9658 game_status = game_status_new;
9660 SetAnimStatus(game_status_new);
9663 void SetFontStatus(int game_status_new)
9665 static int last_game_status = -1;
9667 if (game_status_new != -1)
9669 // set game status for font use after storing last game status
9670 last_game_status = game_status;
9671 game_status = game_status_new;
9675 // reset game status after font use from last stored game status
9676 game_status = last_game_status;
9680 void ResetFontStatus(void)
9685 void SetLevelSetInfo(char *identifier, int level_nr)
9687 setString(&levelset.identifier, identifier);
9689 levelset.level_nr = level_nr;
9692 boolean CheckIfAllViewportsHaveChanged(void)
9694 // if game status has not changed, viewports have not changed either
9695 if (game_status == game_status_last)
9698 // check if all viewports have changed with current game status
9700 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9701 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9702 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9703 int new_real_sx = vp_playfield->x;
9704 int new_real_sy = vp_playfield->y;
9705 int new_full_sxsize = vp_playfield->width;
9706 int new_full_sysize = vp_playfield->height;
9707 int new_dx = vp_door_1->x;
9708 int new_dy = vp_door_1->y;
9709 int new_dxsize = vp_door_1->width;
9710 int new_dysize = vp_door_1->height;
9711 int new_vx = vp_door_2->x;
9712 int new_vy = vp_door_2->y;
9713 int new_vxsize = vp_door_2->width;
9714 int new_vysize = vp_door_2->height;
9716 boolean playfield_viewport_has_changed =
9717 (new_real_sx != REAL_SX ||
9718 new_real_sy != REAL_SY ||
9719 new_full_sxsize != FULL_SXSIZE ||
9720 new_full_sysize != FULL_SYSIZE);
9722 boolean door_1_viewport_has_changed =
9725 new_dxsize != DXSIZE ||
9726 new_dysize != DYSIZE);
9728 boolean door_2_viewport_has_changed =
9731 new_vxsize != VXSIZE ||
9732 new_vysize != VYSIZE ||
9733 game_status_last == GAME_MODE_EDITOR);
9735 return (playfield_viewport_has_changed &&
9736 door_1_viewport_has_changed &&
9737 door_2_viewport_has_changed);
9740 boolean CheckFadeAll(void)
9742 return (CheckIfGlobalBorderHasChanged() ||
9743 CheckIfAllViewportsHaveChanged());
9746 void ChangeViewportPropertiesIfNeeded(void)
9748 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9749 FALSE : setup.small_game_graphics);
9750 int gfx_game_mode = getGlobalGameStatus(game_status);
9751 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9753 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9754 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9755 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9756 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9757 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9758 int new_win_xsize = vp_window->width;
9759 int new_win_ysize = vp_window->height;
9760 int border_left = vp_playfield->border_left;
9761 int border_right = vp_playfield->border_right;
9762 int border_top = vp_playfield->border_top;
9763 int border_bottom = vp_playfield->border_bottom;
9764 int new_sx = vp_playfield->x + border_left;
9765 int new_sy = vp_playfield->y + border_top;
9766 int new_sxsize = vp_playfield->width - border_left - border_right;
9767 int new_sysize = vp_playfield->height - border_top - border_bottom;
9768 int new_real_sx = vp_playfield->x;
9769 int new_real_sy = vp_playfield->y;
9770 int new_full_sxsize = vp_playfield->width;
9771 int new_full_sysize = vp_playfield->height;
9772 int new_dx = vp_door_1->x;
9773 int new_dy = vp_door_1->y;
9774 int new_dxsize = vp_door_1->width;
9775 int new_dysize = vp_door_1->height;
9776 int new_vx = vp_door_2->x;
9777 int new_vy = vp_door_2->y;
9778 int new_vxsize = vp_door_2->width;
9779 int new_vysize = vp_door_2->height;
9780 int new_ex = vp_door_3->x;
9781 int new_ey = vp_door_3->y;
9782 int new_exsize = vp_door_3->width;
9783 int new_eysize = vp_door_3->height;
9784 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9785 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9786 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9787 int new_scr_fieldx = new_sxsize / tilesize;
9788 int new_scr_fieldy = new_sysize / tilesize;
9789 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9790 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9791 boolean init_gfx_buffers = FALSE;
9792 boolean init_video_buffer = FALSE;
9793 boolean init_gadgets_and_anims = FALSE;
9794 boolean init_em_graphics = FALSE;
9796 if (new_win_xsize != WIN_XSIZE ||
9797 new_win_ysize != WIN_YSIZE)
9799 WIN_XSIZE = new_win_xsize;
9800 WIN_YSIZE = new_win_ysize;
9802 init_video_buffer = TRUE;
9803 init_gfx_buffers = TRUE;
9804 init_gadgets_and_anims = TRUE;
9806 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9809 if (new_scr_fieldx != SCR_FIELDX ||
9810 new_scr_fieldy != SCR_FIELDY)
9812 // this always toggles between MAIN and GAME when using small tile size
9814 SCR_FIELDX = new_scr_fieldx;
9815 SCR_FIELDY = new_scr_fieldy;
9817 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9828 new_sxsize != SXSIZE ||
9829 new_sysize != SYSIZE ||
9830 new_dxsize != DXSIZE ||
9831 new_dysize != DYSIZE ||
9832 new_vxsize != VXSIZE ||
9833 new_vysize != VYSIZE ||
9834 new_exsize != EXSIZE ||
9835 new_eysize != EYSIZE ||
9836 new_real_sx != REAL_SX ||
9837 new_real_sy != REAL_SY ||
9838 new_full_sxsize != FULL_SXSIZE ||
9839 new_full_sysize != FULL_SYSIZE ||
9840 new_tilesize_var != TILESIZE_VAR
9843 // ------------------------------------------------------------------------
9844 // determine next fading area for changed viewport definitions
9845 // ------------------------------------------------------------------------
9847 // start with current playfield area (default fading area)
9850 FADE_SXSIZE = FULL_SXSIZE;
9851 FADE_SYSIZE = FULL_SYSIZE;
9853 // add new playfield area if position or size has changed
9854 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9855 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9857 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9858 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9861 // add current and new door 1 area if position or size has changed
9862 if (new_dx != DX || new_dy != DY ||
9863 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9865 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9866 DX, DY, DXSIZE, DYSIZE);
9867 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9868 new_dx, new_dy, new_dxsize, new_dysize);
9871 // add current and new door 2 area if position or size has changed
9872 if (new_vx != VX || new_vy != VY ||
9873 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9875 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9876 VX, VY, VXSIZE, VYSIZE);
9877 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9878 new_vx, new_vy, new_vxsize, new_vysize);
9881 // ------------------------------------------------------------------------
9882 // handle changed tile size
9883 // ------------------------------------------------------------------------
9885 if (new_tilesize_var != TILESIZE_VAR)
9887 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9889 // changing tile size invalidates scroll values of engine snapshots
9890 FreeEngineSnapshotSingle();
9892 // changing tile size requires update of graphic mapping for EM engine
9893 init_em_graphics = TRUE;
9904 SXSIZE = new_sxsize;
9905 SYSIZE = new_sysize;
9906 DXSIZE = new_dxsize;
9907 DYSIZE = new_dysize;
9908 VXSIZE = new_vxsize;
9909 VYSIZE = new_vysize;
9910 EXSIZE = new_exsize;
9911 EYSIZE = new_eysize;
9912 REAL_SX = new_real_sx;
9913 REAL_SY = new_real_sy;
9914 FULL_SXSIZE = new_full_sxsize;
9915 FULL_SYSIZE = new_full_sysize;
9916 TILESIZE_VAR = new_tilesize_var;
9918 init_gfx_buffers = TRUE;
9919 init_gadgets_and_anims = TRUE;
9921 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9922 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9925 if (init_gfx_buffers)
9927 // Debug("tools:viewport", "init_gfx_buffers");
9929 SCR_FIELDX = new_scr_fieldx_buffers;
9930 SCR_FIELDY = new_scr_fieldy_buffers;
9934 SCR_FIELDX = new_scr_fieldx;
9935 SCR_FIELDY = new_scr_fieldy;
9937 SetDrawDeactivationMask(REDRAW_NONE);
9938 SetDrawBackgroundMask(REDRAW_FIELD);
9941 if (init_video_buffer)
9943 // Debug("tools:viewport", "init_video_buffer");
9945 FreeAllImageTextures(); // needs old renderer to free the textures
9947 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9948 InitImageTextures();
9951 if (init_gadgets_and_anims)
9953 // Debug("tools:viewport", "init_gadgets_and_anims");
9956 InitGlobalAnimations();
9959 if (init_em_graphics)
9961 InitGraphicInfo_EM();
9965 void OpenURL(char *url)
9967 #if SDL_VERSION_ATLEAST(2,0,14)
9970 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9971 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9972 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9976 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9978 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9982 // ============================================================================
9984 // ============================================================================
9986 #if defined(PLATFORM_WINDOWS)
9987 /* FILETIME of Jan 1 1970 00:00:00. */
9988 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9991 * timezone information is stored outside the kernel so tzp isn't used anymore.
9993 * Note: this function is not for Win32 high precision timing purpose. See
9996 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9999 SYSTEMTIME system_time;
10000 ULARGE_INTEGER ularge;
10002 GetSystemTime(&system_time);
10003 SystemTimeToFileTime(&system_time, &file_time);
10004 ularge.LowPart = file_time.dwLowDateTime;
10005 ularge.HighPart = file_time.dwHighDateTime;
10007 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10008 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10014 static char *test_init_uuid_random_function_simple(void)
10016 static char seed_text[100];
10017 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10019 sprintf(seed_text, "%d", seed);
10024 static char *test_init_uuid_random_function_better(void)
10026 static char seed_text[100];
10027 struct timeval current_time;
10029 gettimeofday(¤t_time, NULL);
10031 prng_seed_bytes(¤t_time, sizeof(current_time));
10033 sprintf(seed_text, "%ld.%ld",
10034 (long)current_time.tv_sec,
10035 (long)current_time.tv_usec);
10040 #if defined(PLATFORM_WINDOWS)
10041 static char *test_init_uuid_random_function_better_windows(void)
10043 static char seed_text[100];
10044 struct timeval current_time;
10046 gettimeofday_windows(¤t_time, NULL);
10048 prng_seed_bytes(¤t_time, sizeof(current_time));
10050 sprintf(seed_text, "%ld.%ld",
10051 (long)current_time.tv_sec,
10052 (long)current_time.tv_usec);
10058 static unsigned int test_uuid_random_function_simple(int max)
10060 return GetSimpleRandom(max);
10063 static unsigned int test_uuid_random_function_better(int max)
10065 return (max > 0 ? prng_get_uint() % max : 0);
10068 #if defined(PLATFORM_WINDOWS)
10069 #define NUM_UUID_TESTS 3
10071 #define NUM_UUID_TESTS 2
10074 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10076 struct hashtable *hash_seeds =
10077 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10078 struct hashtable *hash_uuids =
10079 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10080 static char message[100];
10083 char *random_name = (nr == 0 ? "simple" : "better");
10084 char *random_type = (always_seed ? "always" : "only once");
10085 char *(*init_random_function)(void) =
10087 test_init_uuid_random_function_simple :
10088 test_init_uuid_random_function_better);
10089 unsigned int (*random_function)(int) =
10091 test_uuid_random_function_simple :
10092 test_uuid_random_function_better);
10095 #if defined(PLATFORM_WINDOWS)
10098 random_name = "windows";
10099 init_random_function = test_init_uuid_random_function_better_windows;
10105 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10106 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10108 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10109 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10110 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10112 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10116 // always initialize random number generator at least once
10117 init_random_function();
10119 unsigned int time_start = SDL_GetTicks();
10121 for (i = 0; i < num_uuids; i++)
10125 char *seed = getStringCopy(init_random_function());
10127 hashtable_remove(hash_seeds, seed);
10128 hashtable_insert(hash_seeds, seed, "1");
10131 char *uuid = getStringCopy(getUUIDExt(random_function));
10133 hashtable_remove(hash_uuids, uuid);
10134 hashtable_insert(hash_uuids, uuid, "1");
10137 int num_unique_seeds = hashtable_count(hash_seeds);
10138 int num_unique_uuids = hashtable_count(hash_uuids);
10140 unsigned int time_needed = SDL_GetTicks() - time_start;
10142 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10144 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10147 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10149 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10150 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10152 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10154 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10156 Request(message, REQ_CONFIRM);
10158 hashtable_destroy(hash_seeds, 0);
10159 hashtable_destroy(hash_uuids, 0);
10162 void TestGeneratingUUIDs(void)
10164 int num_uuids = 1000000;
10167 for (i = 0; i < NUM_UUID_TESTS; i++)
10168 for (j = 0; j < 2; j++)
10169 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10171 CloseAllAndExit(0);