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 // set envelope request run-time values
3062 request.xsize = xsize;
3063 request.ysize = ysize;
3066 void DrawEnvelopeRequestToScreen(int drawing_target)
3068 if (global.use_envelope_request &&
3069 game.request_active &&
3070 drawing_target == DRAW_TO_SCREEN)
3072 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3073 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3074 request.sx, request.sy);
3076 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3077 request.sx, request.sy);
3081 static void setRequestBasePosition(int *x, int *y)
3083 int sx_base, sy_base;
3085 if (request.x != -1)
3086 sx_base = request.x;
3087 else if (request.align == ALIGN_LEFT)
3089 else if (request.align == ALIGN_RIGHT)
3090 sx_base = SX + SXSIZE;
3092 sx_base = SX + SXSIZE / 2;
3094 if (request.y != -1)
3095 sy_base = request.y;
3096 else if (request.valign == VALIGN_TOP)
3098 else if (request.valign == VALIGN_BOTTOM)
3099 sy_base = SY + SYSIZE;
3101 sy_base = SY + SYSIZE / 2;
3107 static void setRequestPositionExt(int *x, int *y, int width, int height,
3108 boolean add_border_size)
3110 int border_size = request.border_size;
3111 int sx_base, sy_base;
3114 setRequestBasePosition(&sx_base, &sy_base);
3116 if (request.align == ALIGN_LEFT)
3118 else if (request.align == ALIGN_RIGHT)
3119 sx = sx_base - width;
3121 sx = sx_base - width / 2;
3123 if (request.valign == VALIGN_TOP)
3125 else if (request.valign == VALIGN_BOTTOM)
3126 sy = sy_base - height;
3128 sy = sy_base - height / 2;
3130 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3131 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3133 if (add_border_size)
3143 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3145 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3148 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3150 DrawBuffer *drawto_last = drawto;
3151 char *text_final = text;
3152 char *text_door_style = NULL;
3153 int graphic = IMG_BACKGROUND_REQUEST;
3154 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3155 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3156 int font_nr = FONT_REQUEST;
3157 int font_width = getFontWidth(font_nr);
3158 int font_height = getFontHeight(font_nr);
3159 int border_size = request.border_size;
3160 int line_spacing = request.line_spacing;
3161 int line_height = font_height + line_spacing;
3162 int max_text_width = request.width - 2 * border_size;
3163 int max_text_height = request.height - 2 * border_size;
3164 int line_length = max_text_width / font_width;
3165 int max_lines = max_text_height / line_height;
3166 int text_width = line_length * font_width;
3167 int width = request.width;
3168 int height = request.height;
3169 int tile_size = MAX(request.step_offset, 1);
3170 int x_steps = width / tile_size;
3171 int y_steps = height / tile_size;
3172 int sx_offset = border_size;
3173 int sy_offset = border_size;
3177 if (request.centered)
3178 sx_offset = (request.width - text_width) / 2;
3180 if (request.wrap_single_words && !request.autowrap)
3182 char *src_text_ptr, *dst_text_ptr;
3184 text_door_style = checked_malloc(2 * strlen(text) + 1);
3186 src_text_ptr = text;
3187 dst_text_ptr = text_door_style;
3189 while (*src_text_ptr)
3191 if (*src_text_ptr == ' ' ||
3192 *src_text_ptr == '?' ||
3193 *src_text_ptr == '!')
3194 *dst_text_ptr++ = '\n';
3196 if (*src_text_ptr != ' ')
3197 *dst_text_ptr++ = *src_text_ptr;
3202 *dst_text_ptr = '\0';
3204 text_final = text_door_style;
3207 setRequestPosition(&sx, &sy, FALSE);
3209 // draw complete envelope request to temporary bitmap
3210 drawto = bitmap_db_store_1;
3212 ClearRectangle(drawto, sx, sy, width, height);
3214 for (y = 0; y < y_steps; y++)
3215 for (x = 0; x < x_steps; x++)
3216 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3217 x, y, x_steps, y_steps,
3218 tile_size, tile_size);
3220 // force DOOR font inside door area
3221 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3223 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3224 line_length, -1, max_lines, line_spacing, mask_mode,
3225 request.autowrap, request.centered, FALSE);
3229 MapToolButtons(req_state);
3231 // restore pointer to drawing buffer
3232 drawto = drawto_last;
3234 // prepare complete envelope request from temporary bitmap
3235 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3237 if (text_door_style)
3238 free(text_door_style);
3241 static void AnimateEnvelopeRequest(int anim_mode, int action)
3243 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3244 int delay_value_normal = request.step_delay;
3245 int delay_value_fast = delay_value_normal / 2;
3246 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3247 boolean no_delay = (tape.warp_forward);
3248 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3249 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3250 DelayCounter anim_delay = { anim_delay_value };
3252 int tile_size = MAX(request.step_offset, 1);
3253 int max_xsize = request.width / tile_size;
3254 int max_ysize = request.height / tile_size;
3255 int max_xsize_inner = max_xsize - 2;
3256 int max_ysize_inner = max_ysize - 2;
3258 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3259 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3260 int xend = max_xsize_inner;
3261 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3262 int xstep = (xstart < xend ? 1 : 0);
3263 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3265 int end = MAX(xend - xstart, yend - ystart);
3268 if (setup.quick_doors)
3275 for (i = start; i <= end; i++)
3277 int last_frame = end; // last frame of this "for" loop
3278 int x = xstart + i * xstep;
3279 int y = ystart + i * ystep;
3280 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3281 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3282 int xsize_size_left = (xsize - 1) * tile_size;
3283 int ysize_size_top = (ysize - 1) * tile_size;
3284 int max_xsize_pos = (max_xsize - 1) * tile_size;
3285 int max_ysize_pos = (max_ysize - 1) * tile_size;
3286 int width = xsize * tile_size;
3287 int height = ysize * tile_size;
3293 HandleGameActions();
3295 setRequestPosition(&src_x, &src_y, FALSE);
3296 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3298 for (yy = 0; yy < 2; yy++)
3300 for (xx = 0; xx < 2; xx++)
3302 int src_xx = src_x + xx * max_xsize_pos;
3303 int src_yy = src_y + yy * max_ysize_pos;
3304 int dst_xx = dst_x + xx * xsize_size_left;
3305 int dst_yy = dst_y + yy * ysize_size_top;
3306 int xx_size = (xx ? tile_size : xsize_size_left);
3307 int yy_size = (yy ? tile_size : ysize_size_top);
3309 // draw partial (animated) envelope request to temporary bitmap
3310 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3311 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3315 // prepare partial (animated) envelope request from temporary bitmap
3316 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3319 redraw_mask |= REDRAW_FIELD;
3323 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3326 ClearAutoRepeatKeyEvents();
3329 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3331 int graphic = IMG_BACKGROUND_REQUEST;
3332 int sound_opening = SND_REQUEST_OPENING;
3333 int sound_closing = SND_REQUEST_CLOSING;
3334 int anim_mode_1 = request.anim_mode; // (higher priority)
3335 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3336 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3337 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3338 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3340 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3342 if (action == ACTION_OPENING)
3344 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3346 if (anim_mode == ANIM_DEFAULT)
3347 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3349 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3353 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3355 if (anim_mode != ANIM_NONE)
3356 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3358 if (anim_mode == ANIM_DEFAULT)
3359 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3362 game.envelope_active = FALSE;
3365 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3367 if (IS_MM_WALL(element))
3369 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3375 int graphic = el2preimg(element);
3377 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3378 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3383 void DrawLevel(int draw_background_mask)
3387 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3388 SetDrawBackgroundMask(draw_background_mask);
3392 for (x = BX1; x <= BX2; x++)
3393 for (y = BY1; y <= BY2; y++)
3394 DrawScreenField(x, y);
3396 redraw_mask |= REDRAW_FIELD;
3399 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3404 for (x = 0; x < size_x; x++)
3405 for (y = 0; y < size_y; y++)
3406 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3408 redraw_mask |= REDRAW_FIELD;
3411 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3415 for (x = 0; x < size_x; x++)
3416 for (y = 0; y < size_y; y++)
3417 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3419 redraw_mask |= REDRAW_FIELD;
3422 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3424 boolean show_level_border = (BorderElement != EL_EMPTY);
3425 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3426 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3427 int tile_size = preview.tile_size;
3428 int preview_width = preview.xsize * tile_size;
3429 int preview_height = preview.ysize * tile_size;
3430 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3431 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3432 int real_preview_width = real_preview_xsize * tile_size;
3433 int real_preview_height = real_preview_ysize * tile_size;
3434 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3435 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3438 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3441 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3443 dst_x += (preview_width - real_preview_width) / 2;
3444 dst_y += (preview_height - real_preview_height) / 2;
3446 for (x = 0; x < real_preview_xsize; x++)
3448 for (y = 0; y < real_preview_ysize; y++)
3450 int lx = from_x + x + (show_level_border ? -1 : 0);
3451 int ly = from_y + y + (show_level_border ? -1 : 0);
3452 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3453 getBorderElement(lx, ly));
3455 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3456 element, tile_size);
3460 redraw_mask |= REDRAW_FIELD;
3463 #define MICROLABEL_EMPTY 0
3464 #define MICROLABEL_LEVEL_NAME 1
3465 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3466 #define MICROLABEL_LEVEL_AUTHOR 3
3467 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3468 #define MICROLABEL_IMPORTED_FROM 5
3469 #define MICROLABEL_IMPORTED_BY_HEAD 6
3470 #define MICROLABEL_IMPORTED_BY 7
3472 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3474 int max_text_width = SXSIZE;
3475 int font_width = getFontWidth(font_nr);
3477 if (pos->align == ALIGN_CENTER)
3478 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3479 else if (pos->align == ALIGN_RIGHT)
3480 max_text_width = pos->x;
3482 max_text_width = SXSIZE - pos->x;
3484 return max_text_width / font_width;
3487 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3489 char label_text[MAX_OUTPUT_LINESIZE + 1];
3490 int max_len_label_text;
3491 int font_nr = pos->font;
3494 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3497 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3498 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3499 mode == MICROLABEL_IMPORTED_BY_HEAD)
3500 font_nr = pos->font_alt;
3502 max_len_label_text = getMaxTextLength(pos, font_nr);
3504 if (pos->size != -1)
3505 max_len_label_text = pos->size;
3507 for (i = 0; i < max_len_label_text; i++)
3508 label_text[i] = ' ';
3509 label_text[max_len_label_text] = '\0';
3511 if (strlen(label_text) > 0)
3512 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3515 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3516 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3517 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3518 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3519 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3520 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3521 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3522 max_len_label_text);
3523 label_text[max_len_label_text] = '\0';
3525 if (strlen(label_text) > 0)
3526 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3528 redraw_mask |= REDRAW_FIELD;
3531 static void DrawPreviewLevelLabel(int mode)
3533 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3536 static void DrawPreviewLevelInfo(int mode)
3538 if (mode == MICROLABEL_LEVEL_NAME)
3539 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3540 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3541 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3544 static void DrawPreviewLevelExt(boolean restart)
3546 static DelayCounter scroll_delay = { 0 };
3547 static DelayCounter label_delay = { 0 };
3548 static int from_x, from_y, scroll_direction;
3549 static int label_state, label_counter;
3550 boolean show_level_border = (BorderElement != EL_EMPTY);
3551 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3552 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3554 scroll_delay.value = preview.step_delay;
3555 label_delay.value = MICROLEVEL_LABEL_DELAY;
3562 if (preview.anim_mode == ANIM_CENTERED)
3564 if (level_xsize > preview.xsize)
3565 from_x = (level_xsize - preview.xsize) / 2;
3566 if (level_ysize > preview.ysize)
3567 from_y = (level_ysize - preview.ysize) / 2;
3570 from_x += preview.xoffset;
3571 from_y += preview.yoffset;
3573 scroll_direction = MV_RIGHT;
3577 DrawPreviewLevelPlayfield(from_x, from_y);
3578 DrawPreviewLevelLabel(label_state);
3580 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3581 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3583 // initialize delay counters
3584 ResetDelayCounter(&scroll_delay);
3585 ResetDelayCounter(&label_delay);
3587 if (leveldir_current->name)
3589 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3590 char label_text[MAX_OUTPUT_LINESIZE + 1];
3591 int font_nr = pos->font;
3592 int max_len_label_text = getMaxTextLength(pos, font_nr);
3594 if (pos->size != -1)
3595 max_len_label_text = pos->size;
3597 strncpy(label_text, leveldir_current->name, max_len_label_text);
3598 label_text[max_len_label_text] = '\0';
3600 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3601 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3607 // scroll preview level, if needed
3608 if (preview.anim_mode != ANIM_NONE &&
3609 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3610 DelayReached(&scroll_delay))
3612 switch (scroll_direction)
3617 from_x -= preview.step_offset;
3618 from_x = (from_x < 0 ? 0 : from_x);
3621 scroll_direction = MV_UP;
3625 if (from_x < level_xsize - preview.xsize)
3627 from_x += preview.step_offset;
3628 from_x = (from_x > level_xsize - preview.xsize ?
3629 level_xsize - preview.xsize : from_x);
3632 scroll_direction = MV_DOWN;
3638 from_y -= preview.step_offset;
3639 from_y = (from_y < 0 ? 0 : from_y);
3642 scroll_direction = MV_RIGHT;
3646 if (from_y < level_ysize - preview.ysize)
3648 from_y += preview.step_offset;
3649 from_y = (from_y > level_ysize - preview.ysize ?
3650 level_ysize - preview.ysize : from_y);
3653 scroll_direction = MV_LEFT;
3660 DrawPreviewLevelPlayfield(from_x, from_y);
3663 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3664 // redraw micro level label, if needed
3665 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3666 !strEqual(level.author, ANONYMOUS_NAME) &&
3667 !strEqual(level.author, leveldir_current->name) &&
3668 DelayReached(&label_delay))
3670 int max_label_counter = 23;
3672 if (leveldir_current->imported_from != NULL &&
3673 strlen(leveldir_current->imported_from) > 0)
3674 max_label_counter += 14;
3675 if (leveldir_current->imported_by != NULL &&
3676 strlen(leveldir_current->imported_by) > 0)
3677 max_label_counter += 14;
3679 label_counter = (label_counter + 1) % max_label_counter;
3680 label_state = (label_counter >= 0 && label_counter <= 7 ?
3681 MICROLABEL_LEVEL_NAME :
3682 label_counter >= 9 && label_counter <= 12 ?
3683 MICROLABEL_LEVEL_AUTHOR_HEAD :
3684 label_counter >= 14 && label_counter <= 21 ?
3685 MICROLABEL_LEVEL_AUTHOR :
3686 label_counter >= 23 && label_counter <= 26 ?
3687 MICROLABEL_IMPORTED_FROM_HEAD :
3688 label_counter >= 28 && label_counter <= 35 ?
3689 MICROLABEL_IMPORTED_FROM :
3690 label_counter >= 37 && label_counter <= 40 ?
3691 MICROLABEL_IMPORTED_BY_HEAD :
3692 label_counter >= 42 && label_counter <= 49 ?
3693 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3695 if (leveldir_current->imported_from == NULL &&
3696 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3697 label_state == MICROLABEL_IMPORTED_FROM))
3698 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3699 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3701 DrawPreviewLevelLabel(label_state);
3705 void DrawPreviewPlayers(void)
3707 if (game_status != GAME_MODE_MAIN)
3710 // do not draw preview players if level preview redefined, but players aren't
3711 if (preview.redefined && !menu.main.preview_players.redefined)
3714 boolean player_found[MAX_PLAYERS];
3715 int num_players = 0;
3718 for (i = 0; i < MAX_PLAYERS; i++)
3719 player_found[i] = FALSE;
3721 // check which players can be found in the level (simple approach)
3722 for (x = 0; x < lev_fieldx; x++)
3724 for (y = 0; y < lev_fieldy; y++)
3726 int element = level.field[x][y];
3728 if (IS_PLAYER_ELEMENT(element))
3730 int player_nr = GET_PLAYER_NR(element);
3732 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3734 if (!player_found[player_nr])
3737 player_found[player_nr] = TRUE;
3742 struct TextPosInfo *pos = &menu.main.preview_players;
3743 int tile_size = pos->tile_size;
3744 int border_size = pos->border_size;
3745 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3746 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3747 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3748 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3749 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3750 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3751 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3752 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3753 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3754 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3755 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3756 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3758 // clear area in which the players will be drawn
3759 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3760 max_players_width, max_players_height);
3762 if (!network.enabled && !setup.team_mode)
3765 // only draw players if level is suited for team mode
3766 if (num_players < 2)
3769 // draw all players that were found in the level
3770 for (i = 0; i < MAX_PLAYERS; i++)
3772 if (player_found[i])
3774 int graphic = el2img(EL_PLAYER_1 + i);
3776 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3778 xpos += player_xoffset;
3779 ypos += player_yoffset;
3784 void DrawPreviewLevelInitial(void)
3786 DrawPreviewLevelExt(TRUE);
3787 DrawPreviewPlayers();
3790 void DrawPreviewLevelAnimation(void)
3792 DrawPreviewLevelExt(FALSE);
3795 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3796 int border_size, int font_nr)
3798 int graphic = el2img(EL_PLAYER_1 + player_nr);
3799 int font_height = getFontHeight(font_nr);
3800 int player_height = MAX(tile_size, font_height);
3801 int xoffset_text = tile_size + border_size;
3802 int yoffset_text = (player_height - font_height) / 2;
3803 int yoffset_graphic = (player_height - tile_size) / 2;
3804 char *player_name = getNetworkPlayerName(player_nr + 1);
3806 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3808 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3811 static void DrawNetworkPlayersExt(boolean force)
3813 if (game_status != GAME_MODE_MAIN)
3816 if (!network.connected && !force)
3819 // do not draw network players if level preview redefined, but players aren't
3820 if (preview.redefined && !menu.main.network_players.redefined)
3823 int num_players = 0;
3826 for (i = 0; i < MAX_PLAYERS; i++)
3827 if (stored_player[i].connected_network)
3830 struct TextPosInfo *pos = &menu.main.network_players;
3831 int tile_size = pos->tile_size;
3832 int border_size = pos->border_size;
3833 int xoffset_text = tile_size + border_size;
3834 int font_nr = pos->font;
3835 int font_width = getFontWidth(font_nr);
3836 int font_height = getFontHeight(font_nr);
3837 int player_height = MAX(tile_size, font_height);
3838 int player_yoffset = player_height + border_size;
3839 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3840 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3841 int all_players_height = num_players * player_yoffset - border_size;
3842 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3843 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3844 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3846 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3847 max_players_width, max_players_height);
3849 // first draw local network player ...
3850 for (i = 0; i < MAX_PLAYERS; i++)
3852 if (stored_player[i].connected_network &&
3853 stored_player[i].connected_locally)
3855 char *player_name = getNetworkPlayerName(i + 1);
3856 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3857 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3859 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3861 ypos += player_yoffset;
3865 // ... then draw all other network players
3866 for (i = 0; i < MAX_PLAYERS; i++)
3868 if (stored_player[i].connected_network &&
3869 !stored_player[i].connected_locally)
3871 char *player_name = getNetworkPlayerName(i + 1);
3872 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3873 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3875 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3877 ypos += player_yoffset;
3882 void DrawNetworkPlayers(void)
3884 DrawNetworkPlayersExt(FALSE);
3887 void ClearNetworkPlayers(void)
3889 DrawNetworkPlayersExt(TRUE);
3892 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3893 int graphic, int lx, int ly,
3896 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3898 if (mask_mode == USE_MASKING)
3899 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3901 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3904 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3905 int graphic, int sync_frame, int mask_mode)
3907 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3909 if (mask_mode == USE_MASKING)
3910 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3912 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3915 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3916 int graphic, int sync_frame, int tilesize,
3919 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3921 if (mask_mode == USE_MASKING)
3922 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3924 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3927 static void DrawGraphicAnimation(int x, int y, int graphic)
3929 int lx = LEVELX(x), ly = LEVELY(y);
3930 int mask_mode = NO_MASKING;
3932 if (!IN_SCR_FIELD(x, y))
3935 if (game.use_masked_elements)
3937 if (Tile[lx][ly] != EL_EMPTY)
3939 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3941 mask_mode = USE_MASKING;
3945 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3946 graphic, lx, ly, mask_mode);
3948 MarkTileDirty(x, y);
3951 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3953 int lx = LEVELX(x), ly = LEVELY(y);
3954 int mask_mode = NO_MASKING;
3956 if (!IN_SCR_FIELD(x, y))
3959 if (game.use_masked_elements)
3961 if (Tile[lx][ly] != EL_EMPTY)
3963 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3965 mask_mode = USE_MASKING;
3969 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3970 graphic, lx, ly, mask_mode);
3972 MarkTileDirty(x, y);
3975 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3977 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3980 void DrawLevelElementAnimation(int x, int y, int element)
3982 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3984 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3987 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3989 int sx = SCREENX(x), sy = SCREENY(y);
3991 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3994 if (Tile[x][y] == EL_EMPTY)
3995 graphic = el2img(GfxElementEmpty[x][y]);
3997 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4000 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4003 DrawGraphicAnimation(sx, sy, graphic);
4006 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4007 DrawLevelFieldCrumbled(x, y);
4009 if (GFX_CRUMBLED(Tile[x][y]))
4010 DrawLevelFieldCrumbled(x, y);
4014 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4016 int sx = SCREENX(x), sy = SCREENY(y);
4019 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4022 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4024 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4027 DrawGraphicAnimation(sx, sy, graphic);
4029 if (GFX_CRUMBLED(element))
4030 DrawLevelFieldCrumbled(x, y);
4033 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4035 if (player->use_murphy)
4037 // this works only because currently only one player can be "murphy" ...
4038 static int last_horizontal_dir = MV_LEFT;
4039 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4041 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4042 last_horizontal_dir = move_dir;
4044 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4046 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4048 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4054 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4057 static boolean equalGraphics(int graphic1, int graphic2)
4059 struct GraphicInfo *g1 = &graphic_info[graphic1];
4060 struct GraphicInfo *g2 = &graphic_info[graphic2];
4062 return (g1->bitmap == g2->bitmap &&
4063 g1->src_x == g2->src_x &&
4064 g1->src_y == g2->src_y &&
4065 g1->anim_frames == g2->anim_frames &&
4066 g1->anim_delay == g2->anim_delay &&
4067 g1->anim_mode == g2->anim_mode);
4070 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4074 DRAW_PLAYER_STAGE_INIT = 0,
4075 DRAW_PLAYER_STAGE_LAST_FIELD,
4076 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4077 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4078 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4079 DRAW_PLAYER_STAGE_PLAYER,
4081 DRAW_PLAYER_STAGE_PLAYER,
4082 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4084 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4085 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4087 NUM_DRAW_PLAYER_STAGES
4090 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4092 static int static_last_player_graphic[MAX_PLAYERS];
4093 static int static_last_player_frame[MAX_PLAYERS];
4094 static boolean static_player_is_opaque[MAX_PLAYERS];
4095 static boolean draw_player[MAX_PLAYERS];
4096 int pnr = player->index_nr;
4098 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4100 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4101 static_last_player_frame[pnr] = player->Frame;
4102 static_player_is_opaque[pnr] = FALSE;
4104 draw_player[pnr] = TRUE;
4107 if (!draw_player[pnr])
4111 if (!IN_LEV_FIELD(player->jx, player->jy))
4113 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4114 Debug("draw:DrawPlayerExt", "This should never happen!");
4116 draw_player[pnr] = FALSE;
4122 int last_player_graphic = static_last_player_graphic[pnr];
4123 int last_player_frame = static_last_player_frame[pnr];
4124 boolean player_is_opaque = static_player_is_opaque[pnr];
4126 int jx = player->jx;
4127 int jy = player->jy;
4128 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4129 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4130 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4131 int last_jx = (player->is_moving ? jx - dx : jx);
4132 int last_jy = (player->is_moving ? jy - dy : jy);
4133 int next_jx = jx + dx;
4134 int next_jy = jy + dy;
4135 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4136 int sx = SCREENX(jx);
4137 int sy = SCREENY(jy);
4138 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4139 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4140 int element = Tile[jx][jy];
4141 int last_element = Tile[last_jx][last_jy];
4142 int action = (player->is_pushing ? ACTION_PUSHING :
4143 player->is_digging ? ACTION_DIGGING :
4144 player->is_collecting ? ACTION_COLLECTING :
4145 player->is_moving ? ACTION_MOVING :
4146 player->is_snapping ? ACTION_SNAPPING :
4147 player->is_dropping ? ACTION_DROPPING :
4148 player->is_waiting ? player->action_waiting :
4151 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4153 // ------------------------------------------------------------------------
4154 // initialize drawing the player
4155 // ------------------------------------------------------------------------
4157 draw_player[pnr] = FALSE;
4159 // GfxElement[][] is set to the element the player is digging or collecting;
4160 // remove also for off-screen player if the player is not moving anymore
4161 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4162 GfxElement[jx][jy] = EL_UNDEFINED;
4164 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4167 if (element == EL_EXPLOSION)
4170 InitPlayerGfxAnimation(player, action, move_dir);
4172 draw_player[pnr] = TRUE;
4174 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4176 // ------------------------------------------------------------------------
4177 // draw things in the field the player is leaving, if needed
4178 // ------------------------------------------------------------------------
4180 if (!IN_SCR_FIELD(sx, sy))
4181 draw_player[pnr] = FALSE;
4183 if (!player->is_moving)
4186 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4188 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4190 if (last_element == EL_DYNAMITE_ACTIVE ||
4191 last_element == EL_EM_DYNAMITE_ACTIVE ||
4192 last_element == EL_SP_DISK_RED_ACTIVE)
4193 DrawDynamite(last_jx, last_jy);
4195 DrawLevelFieldThruMask(last_jx, last_jy);
4197 else if (last_element == EL_DYNAMITE_ACTIVE ||
4198 last_element == EL_EM_DYNAMITE_ACTIVE ||
4199 last_element == EL_SP_DISK_RED_ACTIVE)
4200 DrawDynamite(last_jx, last_jy);
4202 DrawLevelField(last_jx, last_jy);
4204 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4206 // ------------------------------------------------------------------------
4207 // draw things behind the player, if needed
4208 // ------------------------------------------------------------------------
4212 DrawLevelElement(jx, jy, Back[jx][jy]);
4217 if (IS_ACTIVE_BOMB(element))
4219 DrawLevelElement(jx, jy, EL_EMPTY);
4224 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4226 int old_element = GfxElement[jx][jy];
4227 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4228 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4230 if (GFX_CRUMBLED(old_element))
4231 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4233 DrawScreenGraphic(sx, sy, old_graphic, frame);
4235 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4236 static_player_is_opaque[pnr] = TRUE;
4240 GfxElement[jx][jy] = EL_UNDEFINED;
4242 // make sure that pushed elements are drawn with correct frame rate
4243 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4245 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4246 GfxFrame[jx][jy] = player->StepFrame;
4248 DrawLevelField(jx, jy);
4251 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4253 // ------------------------------------------------------------------------
4254 // draw things the player is pushing, if needed
4255 // ------------------------------------------------------------------------
4257 if (!player->is_pushing || !player->is_moving)
4260 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4263 int gfx_frame = GfxFrame[jx][jy];
4265 if (!IS_MOVING(jx, jy)) // push movement already finished
4267 element = Tile[next_jx][next_jy];
4268 gfx_frame = GfxFrame[next_jx][next_jy];
4271 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4272 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4273 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4275 // draw background element under pushed element (like the Sokoban field)
4276 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4278 // this allows transparent pushing animation over non-black background
4281 DrawLevelElement(jx, jy, Back[jx][jy]);
4283 DrawLevelElement(jx, jy, EL_EMPTY);
4286 if (Back[next_jx][next_jy])
4287 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4289 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4291 int px = SCREENX(jx), py = SCREENY(jy);
4292 int pxx = (TILEX - ABS(sxx)) * dx;
4293 int pyy = (TILEY - ABS(syy)) * dy;
4296 // do not draw (EM style) pushing animation when pushing is finished
4297 // (two-tile animations usually do not contain start and end frame)
4298 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4299 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4301 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4303 // masked drawing is needed for EMC style (double) movement graphics
4304 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4305 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4308 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4310 // ------------------------------------------------------------------------
4311 // draw player himself
4312 // ------------------------------------------------------------------------
4314 int graphic = getPlayerGraphic(player, move_dir);
4316 // in the case of changed player action or direction, prevent the current
4317 // animation frame from being restarted for identical animations
4318 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4319 player->Frame = last_player_frame;
4321 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4323 if (player_is_opaque)
4324 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4326 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4328 if (SHIELD_ON(player))
4330 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4331 IMG_SHIELD_NORMAL_ACTIVE);
4332 frame = getGraphicAnimationFrame(graphic, -1);
4334 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4337 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4339 // ------------------------------------------------------------------------
4340 // draw things in front of player (active dynamite or dynabombs)
4341 // ------------------------------------------------------------------------
4343 if (IS_ACTIVE_BOMB(element))
4345 int graphic = el2img(element);
4346 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4348 if (game.emulation == EMU_SUPAPLEX)
4349 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4351 DrawGraphicThruMask(sx, sy, graphic, frame);
4354 if (player_is_moving && last_element == EL_EXPLOSION)
4356 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4357 GfxElement[last_jx][last_jy] : EL_EMPTY);
4358 int graphic = el_act2img(element, ACTION_EXPLODING);
4359 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4360 int phase = ExplodePhase[last_jx][last_jy] - 1;
4361 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4364 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4367 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4369 // ------------------------------------------------------------------------
4370 // draw elements the player is just walking/passing through/under
4371 // ------------------------------------------------------------------------
4373 if (player_is_moving)
4375 // handle the field the player is leaving ...
4376 if (IS_ACCESSIBLE_INSIDE(last_element))
4377 DrawLevelField(last_jx, last_jy);
4378 else if (IS_ACCESSIBLE_UNDER(last_element))
4379 DrawLevelFieldThruMask(last_jx, last_jy);
4382 // do not redraw accessible elements if the player is just pushing them
4383 if (!player_is_moving || !player->is_pushing)
4385 // ... and the field the player is entering
4386 if (IS_ACCESSIBLE_INSIDE(element))
4387 DrawLevelField(jx, jy);
4388 else if (IS_ACCESSIBLE_UNDER(element))
4389 DrawLevelFieldThruMask(jx, jy);
4392 MarkTileDirty(sx, sy);
4396 void DrawPlayer(struct PlayerInfo *player)
4400 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4401 DrawPlayerExt(player, i);
4404 void DrawAllPlayers(void)
4408 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4409 for (j = 0; j < MAX_PLAYERS; j++)
4410 if (stored_player[j].active)
4411 DrawPlayerExt(&stored_player[j], i);
4414 void DrawPlayerField(int x, int y)
4416 if (!IS_PLAYER(x, y))
4419 DrawPlayer(PLAYERINFO(x, y));
4422 // ----------------------------------------------------------------------------
4424 void WaitForEventToContinue(void)
4426 boolean first_wait = TRUE;
4427 boolean still_wait = TRUE;
4429 if (program.headless)
4432 // simulate releasing mouse button over last gadget, if still pressed
4434 HandleGadgets(-1, -1, 0);
4436 button_status = MB_RELEASED;
4439 ClearPlayerAction();
4445 if (NextValidEvent(&event))
4449 case EVENT_BUTTONPRESS:
4450 case EVENT_FINGERPRESS:
4454 case EVENT_BUTTONRELEASE:
4455 case EVENT_FINGERRELEASE:
4456 still_wait = first_wait;
4459 case EVENT_KEYPRESS:
4460 case SDL_CONTROLLERBUTTONDOWN:
4461 case SDL_JOYBUTTONDOWN:
4466 HandleOtherEvents(&event);
4470 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4475 if (!PendingEvent())
4480 #define MAX_REQUEST_LINES 13
4481 #define MAX_REQUEST_LINE_FONT1_LEN 7
4482 #define MAX_REQUEST_LINE_FONT2_LEN 10
4484 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4486 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4487 int draw_buffer_last = GetDrawtoField();
4488 int width = request.width;
4489 int height = request.height;
4493 setRequestPosition(&sx, &sy, FALSE);
4495 button_status = MB_RELEASED;
4497 request_gadget_id = -1;
4504 SetDrawtoField(draw_buffer_game);
4506 HandleGameActions();
4508 SetDrawtoField(DRAW_TO_BACKBUFFER);
4515 while (NextValidEvent(&event))
4519 case EVENT_BUTTONPRESS:
4520 case EVENT_BUTTONRELEASE:
4521 case EVENT_MOTIONNOTIFY:
4523 DrawBuffer *drawto_last = drawto;
4526 if (event.type == EVENT_MOTIONNOTIFY)
4531 motion_status = TRUE;
4532 mx = ((MotionEvent *) &event)->x;
4533 my = ((MotionEvent *) &event)->y;
4537 motion_status = FALSE;
4538 mx = ((ButtonEvent *) &event)->x;
4539 my = ((ButtonEvent *) &event)->y;
4540 if (event.type == EVENT_BUTTONPRESS)
4541 button_status = ((ButtonEvent *) &event)->button;
4543 button_status = MB_RELEASED;
4546 if (global.use_envelope_request)
4548 // draw changed button states to temporary bitmap
4549 drawto = bitmap_db_store_1;
4552 // this sets 'request_gadget_id'
4553 HandleGadgets(mx, my, button_status);
4555 if (global.use_envelope_request)
4557 // restore pointer to drawing buffer
4558 drawto = drawto_last;
4560 // prepare complete envelope request from temporary bitmap
4561 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4565 switch (request_gadget_id)
4567 case TOOL_CTRL_ID_YES:
4568 case TOOL_CTRL_ID_TOUCH_YES:
4571 case TOOL_CTRL_ID_NO:
4572 case TOOL_CTRL_ID_TOUCH_NO:
4575 case TOOL_CTRL_ID_CONFIRM:
4576 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4577 result = TRUE | FALSE;
4580 case TOOL_CTRL_ID_PLAYER_1:
4583 case TOOL_CTRL_ID_PLAYER_2:
4586 case TOOL_CTRL_ID_PLAYER_3:
4589 case TOOL_CTRL_ID_PLAYER_4:
4594 // only check clickable animations if no request gadget clicked
4595 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4602 case SDL_WINDOWEVENT:
4603 HandleWindowEvent((WindowEvent *) &event);
4606 case SDL_APP_WILLENTERBACKGROUND:
4607 case SDL_APP_DIDENTERBACKGROUND:
4608 case SDL_APP_WILLENTERFOREGROUND:
4609 case SDL_APP_DIDENTERFOREGROUND:
4610 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4613 case EVENT_KEYPRESS:
4615 Key key = GetEventKey((KeyEvent *)&event);
4620 if (req_state & REQ_CONFIRM)
4629 #if defined(KSYM_Rewind)
4630 case KSYM_Rewind: // for Amazon Fire TV remote
4639 #if defined(KSYM_FastForward)
4640 case KSYM_FastForward: // for Amazon Fire TV remote
4646 HandleKeysDebug(key, KEY_PRESSED);
4650 if (req_state & REQ_PLAYER)
4652 int old_player_nr = setup.network_player_nr;
4655 result = old_player_nr + 1;
4660 result = old_player_nr + 1;
4691 case EVENT_FINGERRELEASE:
4692 case EVENT_KEYRELEASE:
4693 ClearPlayerAction();
4696 case SDL_CONTROLLERBUTTONDOWN:
4697 switch (event.cbutton.button)
4699 case SDL_CONTROLLER_BUTTON_A:
4700 case SDL_CONTROLLER_BUTTON_X:
4701 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4702 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4706 case SDL_CONTROLLER_BUTTON_B:
4707 case SDL_CONTROLLER_BUTTON_Y:
4708 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4709 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4710 case SDL_CONTROLLER_BUTTON_BACK:
4715 if (req_state & REQ_PLAYER)
4717 int old_player_nr = setup.network_player_nr;
4720 result = old_player_nr + 1;
4722 switch (event.cbutton.button)
4724 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4725 case SDL_CONTROLLER_BUTTON_Y:
4729 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4730 case SDL_CONTROLLER_BUTTON_B:
4734 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4735 case SDL_CONTROLLER_BUTTON_A:
4739 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4740 case SDL_CONTROLLER_BUTTON_X:
4751 case SDL_CONTROLLERBUTTONUP:
4752 HandleJoystickEvent(&event);
4753 ClearPlayerAction();
4757 HandleOtherEvents(&event);
4762 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4764 int joy = AnyJoystick();
4766 if (joy & JOY_BUTTON_1)
4768 else if (joy & JOY_BUTTON_2)
4771 else if (AnyJoystick())
4773 int joy = AnyJoystick();
4775 if (req_state & REQ_PLAYER)
4779 else if (joy & JOY_RIGHT)
4781 else if (joy & JOY_DOWN)
4783 else if (joy & JOY_LEFT)
4791 SetDrawtoField(draw_buffer_last);
4796 static void DoRequestBefore(void)
4798 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4800 // when showing request dialog after game ended, deactivate game panel
4802 game.panel.active = FALSE;
4804 if (game_status == GAME_MODE_PLAYING)
4805 BlitScreenToBitmap(backbuffer);
4807 // disable deactivated drawing when quick-loading level tape recording
4808 if (tape.playing && tape.deactivate_display)
4809 TapeDeactivateDisplayOff(TRUE);
4811 SetMouseCursor(CURSOR_DEFAULT);
4813 // pause network game while waiting for request to answer
4814 if (network.enabled &&
4815 game_status == GAME_MODE_PLAYING &&
4816 !game.all_players_gone)
4817 SendToServer_PausePlaying();
4819 // simulate releasing mouse button over last gadget, if still pressed
4821 HandleGadgets(-1, -1, 0);
4826 static void DoRequestAfter(void)
4830 if (game_status == GAME_MODE_PLAYING)
4832 SetPanelBackground();
4833 SetDrawBackgroundMask(REDRAW_DOOR_1);
4837 SetDrawBackgroundMask(REDRAW_FIELD);
4840 // continue network game after request
4841 if (network.enabled &&
4842 game_status == GAME_MODE_PLAYING &&
4843 !game.all_players_gone)
4844 SendToServer_ContinuePlaying();
4846 // restore deactivated drawing when quick-loading level tape recording
4847 if (tape.playing && tape.deactivate_display)
4848 TapeDeactivateDisplayOn();
4851 static void DrawRequestDoorText(char *text)
4853 char *text_ptr = text;
4854 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4855 int font_nr = FONT_TEXT_2;
4858 // force DOOR font inside door area
4859 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4861 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4863 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4864 font_nr = FONT_TEXT_1;
4867 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4869 char text_line[max_request_line_len + 1];
4875 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4877 tc = *(text_ptr + tx);
4878 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4882 if ((tc == '?' || tc == '!') && tl == 0)
4892 strncpy(text_line, text_ptr, tl);
4895 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4896 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4897 text_line, font_nr);
4899 text_ptr += tl + (tc == ' ' ? 1 : 0);
4905 static int RequestDoor(char *text, unsigned int req_state)
4907 unsigned int old_door_state = GetDoorState();
4908 int draw_buffer_last = GetDrawtoField();
4911 if (old_door_state & DOOR_OPEN_1)
4913 CloseDoor(DOOR_CLOSE_1);
4915 // save old door content
4916 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4917 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4920 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4921 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4923 // clear door drawing field
4924 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4926 // write text for request
4927 DrawRequestDoorText(text);
4929 MapToolButtons(req_state);
4931 // copy request gadgets to door backbuffer
4932 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4934 OpenDoor(DOOR_OPEN_1);
4936 // ---------- handle request buttons ----------
4937 result = RequestHandleEvents(req_state, draw_buffer_last);
4941 if (!(req_state & REQ_STAY_OPEN))
4943 CloseDoor(DOOR_CLOSE_1);
4945 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4946 (req_state & REQ_REOPEN))
4947 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4953 static int RequestEnvelope(char *text, unsigned int req_state)
4955 int draw_buffer_last = GetDrawtoField();
4958 DrawEnvelopeRequest(text, req_state);
4959 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4961 // ---------- handle request buttons ----------
4962 result = RequestHandleEvents(req_state, draw_buffer_last);
4966 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4971 int Request(char *text, unsigned int req_state)
4973 boolean overlay_enabled = GetOverlayEnabled();
4976 game.request_active = TRUE;
4978 SetOverlayEnabled(FALSE);
4982 if (global.use_envelope_request)
4983 result = RequestEnvelope(text, req_state);
4985 result = RequestDoor(text, req_state);
4989 SetOverlayEnabled(overlay_enabled);
4991 game.request_active = FALSE;
4996 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4998 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4999 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5002 if (dpo1->sort_priority != dpo2->sort_priority)
5003 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5005 compare_result = dpo1->nr - dpo2->nr;
5007 return compare_result;
5010 void InitGraphicCompatibilityInfo_Doors(void)
5016 struct DoorInfo *door;
5020 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5021 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5023 { -1, -1, -1, NULL }
5025 struct Rect door_rect_list[] =
5027 { DX, DY, DXSIZE, DYSIZE },
5028 { VX, VY, VXSIZE, VYSIZE }
5032 for (i = 0; doors[i].door_token != -1; i++)
5034 int door_token = doors[i].door_token;
5035 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5036 int part_1 = doors[i].part_1;
5037 int part_8 = doors[i].part_8;
5038 int part_2 = part_1 + 1;
5039 int part_3 = part_1 + 2;
5040 struct DoorInfo *door = doors[i].door;
5041 struct Rect *door_rect = &door_rect_list[door_index];
5042 boolean door_gfx_redefined = FALSE;
5044 // check if any door part graphic definitions have been redefined
5046 for (j = 0; door_part_controls[j].door_token != -1; j++)
5048 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5049 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5051 if (dpc->door_token == door_token && fi->redefined)
5052 door_gfx_redefined = TRUE;
5055 // check for old-style door graphic/animation modifications
5057 if (!door_gfx_redefined)
5059 if (door->anim_mode & ANIM_STATIC_PANEL)
5061 door->panel.step_xoffset = 0;
5062 door->panel.step_yoffset = 0;
5065 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5067 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5068 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5069 int num_door_steps, num_panel_steps;
5071 // remove door part graphics other than the two default wings
5073 for (j = 0; door_part_controls[j].door_token != -1; j++)
5075 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5076 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5078 if (dpc->graphic >= part_3 &&
5079 dpc->graphic <= part_8)
5083 // set graphics and screen positions of the default wings
5085 g_part_1->width = door_rect->width;
5086 g_part_1->height = door_rect->height;
5087 g_part_2->width = door_rect->width;
5088 g_part_2->height = door_rect->height;
5089 g_part_2->src_x = door_rect->width;
5090 g_part_2->src_y = g_part_1->src_y;
5092 door->part_2.x = door->part_1.x;
5093 door->part_2.y = door->part_1.y;
5095 if (door->width != -1)
5097 g_part_1->width = door->width;
5098 g_part_2->width = door->width;
5100 // special treatment for graphics and screen position of right wing
5101 g_part_2->src_x += door_rect->width - door->width;
5102 door->part_2.x += door_rect->width - door->width;
5105 if (door->height != -1)
5107 g_part_1->height = door->height;
5108 g_part_2->height = door->height;
5110 // special treatment for graphics and screen position of bottom wing
5111 g_part_2->src_y += door_rect->height - door->height;
5112 door->part_2.y += door_rect->height - door->height;
5115 // set animation delays for the default wings and panels
5117 door->part_1.step_delay = door->step_delay;
5118 door->part_2.step_delay = door->step_delay;
5119 door->panel.step_delay = door->step_delay;
5121 // set animation draw order for the default wings
5123 door->part_1.sort_priority = 2; // draw left wing over ...
5124 door->part_2.sort_priority = 1; // ... right wing
5126 // set animation draw offset for the default wings
5128 if (door->anim_mode & ANIM_HORIZONTAL)
5130 door->part_1.step_xoffset = door->step_offset;
5131 door->part_1.step_yoffset = 0;
5132 door->part_2.step_xoffset = door->step_offset * -1;
5133 door->part_2.step_yoffset = 0;
5135 num_door_steps = g_part_1->width / door->step_offset;
5137 else // ANIM_VERTICAL
5139 door->part_1.step_xoffset = 0;
5140 door->part_1.step_yoffset = door->step_offset;
5141 door->part_2.step_xoffset = 0;
5142 door->part_2.step_yoffset = door->step_offset * -1;
5144 num_door_steps = g_part_1->height / door->step_offset;
5147 // set animation draw offset for the default panels
5149 if (door->step_offset > 1)
5151 num_panel_steps = 2 * door_rect->height / door->step_offset;
5152 door->panel.start_step = num_panel_steps - num_door_steps;
5153 door->panel.start_step_closing = door->panel.start_step;
5157 num_panel_steps = door_rect->height / door->step_offset;
5158 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5159 door->panel.start_step_closing = door->panel.start_step;
5160 door->panel.step_delay *= 2;
5167 void InitDoors(void)
5171 for (i = 0; door_part_controls[i].door_token != -1; i++)
5173 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5174 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5176 // initialize "start_step_opening" and "start_step_closing", if needed
5177 if (dpc->pos->start_step_opening == 0 &&
5178 dpc->pos->start_step_closing == 0)
5180 // dpc->pos->start_step_opening = dpc->pos->start_step;
5181 dpc->pos->start_step_closing = dpc->pos->start_step;
5184 // fill structure for door part draw order (sorted below)
5186 dpo->sort_priority = dpc->pos->sort_priority;
5189 // sort door part controls according to sort_priority and graphic number
5190 qsort(door_part_order, MAX_DOOR_PARTS,
5191 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5194 unsigned int OpenDoor(unsigned int door_state)
5196 if (door_state & DOOR_COPY_BACK)
5198 if (door_state & DOOR_OPEN_1)
5199 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5200 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5202 if (door_state & DOOR_OPEN_2)
5203 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5204 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5206 door_state &= ~DOOR_COPY_BACK;
5209 return MoveDoor(door_state);
5212 unsigned int CloseDoor(unsigned int door_state)
5214 unsigned int old_door_state = GetDoorState();
5216 if (!(door_state & DOOR_NO_COPY_BACK))
5218 if (old_door_state & DOOR_OPEN_1)
5219 BlitBitmap(backbuffer, bitmap_db_door_1,
5220 DX, DY, DXSIZE, DYSIZE, 0, 0);
5222 if (old_door_state & DOOR_OPEN_2)
5223 BlitBitmap(backbuffer, bitmap_db_door_2,
5224 VX, VY, VXSIZE, VYSIZE, 0, 0);
5226 door_state &= ~DOOR_NO_COPY_BACK;
5229 return MoveDoor(door_state);
5232 unsigned int GetDoorState(void)
5234 return MoveDoor(DOOR_GET_STATE);
5237 unsigned int SetDoorState(unsigned int door_state)
5239 return MoveDoor(door_state | DOOR_SET_STATE);
5242 static int euclid(int a, int b)
5244 return (b ? euclid(b, a % b) : a);
5247 unsigned int MoveDoor(unsigned int door_state)
5249 struct Rect door_rect_list[] =
5251 { DX, DY, DXSIZE, DYSIZE },
5252 { VX, VY, VXSIZE, VYSIZE }
5254 static int door1 = DOOR_CLOSE_1;
5255 static int door2 = DOOR_CLOSE_2;
5256 DelayCounter door_delay = { 0 };
5259 if (door_state == DOOR_GET_STATE)
5260 return (door1 | door2);
5262 if (door_state & DOOR_SET_STATE)
5264 if (door_state & DOOR_ACTION_1)
5265 door1 = door_state & DOOR_ACTION_1;
5266 if (door_state & DOOR_ACTION_2)
5267 door2 = door_state & DOOR_ACTION_2;
5269 return (door1 | door2);
5272 if (!(door_state & DOOR_FORCE_REDRAW))
5274 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5275 door_state &= ~DOOR_OPEN_1;
5276 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5277 door_state &= ~DOOR_CLOSE_1;
5278 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5279 door_state &= ~DOOR_OPEN_2;
5280 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5281 door_state &= ~DOOR_CLOSE_2;
5284 if (global.autoplay_leveldir)
5286 door_state |= DOOR_NO_DELAY;
5287 door_state &= ~DOOR_CLOSE_ALL;
5290 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5291 door_state |= DOOR_NO_DELAY;
5293 if (door_state & DOOR_ACTION)
5295 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5296 boolean door_panel_drawn[NUM_DOORS];
5297 boolean panel_has_doors[NUM_DOORS];
5298 boolean door_part_skip[MAX_DOOR_PARTS];
5299 boolean door_part_done[MAX_DOOR_PARTS];
5300 boolean door_part_done_all;
5301 int num_steps[MAX_DOOR_PARTS];
5302 int max_move_delay = 0; // delay for complete animations of all doors
5303 int max_step_delay = 0; // delay (ms) between two animation frames
5304 int num_move_steps = 0; // number of animation steps for all doors
5305 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5306 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5310 for (i = 0; i < NUM_DOORS; i++)
5311 panel_has_doors[i] = FALSE;
5313 for (i = 0; i < MAX_DOOR_PARTS; i++)
5315 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5316 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5317 int door_token = dpc->door_token;
5319 door_part_done[i] = FALSE;
5320 door_part_skip[i] = (!(door_state & door_token) ||
5324 for (i = 0; i < MAX_DOOR_PARTS; i++)
5326 int nr = door_part_order[i].nr;
5327 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5328 struct DoorPartPosInfo *pos = dpc->pos;
5329 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5330 int door_token = dpc->door_token;
5331 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5332 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5333 int step_xoffset = ABS(pos->step_xoffset);
5334 int step_yoffset = ABS(pos->step_yoffset);
5335 int step_delay = pos->step_delay;
5336 int current_door_state = door_state & door_token;
5337 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5338 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5339 boolean part_opening = (is_panel ? door_closing : door_opening);
5340 int start_step = (part_opening ? pos->start_step_opening :
5341 pos->start_step_closing);
5342 float move_xsize = (step_xoffset ? g->width : 0);
5343 float move_ysize = (step_yoffset ? g->height : 0);
5344 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5345 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5346 int move_steps = (move_xsteps && move_ysteps ?
5347 MIN(move_xsteps, move_ysteps) :
5348 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5349 int move_delay = move_steps * step_delay;
5351 if (door_part_skip[nr])
5354 max_move_delay = MAX(max_move_delay, move_delay);
5355 max_step_delay = (max_step_delay == 0 ? step_delay :
5356 euclid(max_step_delay, step_delay));
5357 num_steps[nr] = move_steps;
5361 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5363 panel_has_doors[door_index] = TRUE;
5367 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5369 num_move_steps = max_move_delay / max_step_delay;
5370 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5372 door_delay.value = max_step_delay;
5374 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5376 start = num_move_steps - 1;
5380 // opening door sound has priority over simultaneously closing door
5381 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5383 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5385 if (door_state & DOOR_OPEN_1)
5386 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5387 if (door_state & DOOR_OPEN_2)
5388 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5390 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5392 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5394 if (door_state & DOOR_CLOSE_1)
5395 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5396 if (door_state & DOOR_CLOSE_2)
5397 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5401 for (k = start; k < num_move_steps; k++)
5403 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5405 door_part_done_all = TRUE;
5407 for (i = 0; i < NUM_DOORS; i++)
5408 door_panel_drawn[i] = FALSE;
5410 for (i = 0; i < MAX_DOOR_PARTS; i++)
5412 int nr = door_part_order[i].nr;
5413 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5414 struct DoorPartPosInfo *pos = dpc->pos;
5415 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5416 int door_token = dpc->door_token;
5417 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5418 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5419 boolean is_panel_and_door_has_closed = FALSE;
5420 struct Rect *door_rect = &door_rect_list[door_index];
5421 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5423 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5424 int current_door_state = door_state & door_token;
5425 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5426 boolean door_closing = !door_opening;
5427 boolean part_opening = (is_panel ? door_closing : door_opening);
5428 boolean part_closing = !part_opening;
5429 int start_step = (part_opening ? pos->start_step_opening :
5430 pos->start_step_closing);
5431 int step_delay = pos->step_delay;
5432 int step_factor = step_delay / max_step_delay;
5433 int k1 = (step_factor ? k / step_factor + 1 : k);
5434 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5435 int kk = MAX(0, k2);
5438 int src_x, src_y, src_xx, src_yy;
5439 int dst_x, dst_y, dst_xx, dst_yy;
5442 if (door_part_skip[nr])
5445 if (!(door_state & door_token))
5453 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5454 int kk_door = MAX(0, k2_door);
5455 int sync_frame = kk_door * door_delay.value;
5456 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5458 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5459 &g_src_x, &g_src_y);
5464 if (!door_panel_drawn[door_index])
5466 ClearRectangle(drawto, door_rect->x, door_rect->y,
5467 door_rect->width, door_rect->height);
5469 door_panel_drawn[door_index] = TRUE;
5472 // draw opening or closing door parts
5474 if (pos->step_xoffset < 0) // door part on right side
5477 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5480 if (dst_xx + width > door_rect->width)
5481 width = door_rect->width - dst_xx;
5483 else // door part on left side
5486 dst_xx = pos->x - kk * pos->step_xoffset;
5490 src_xx = ABS(dst_xx);
5494 width = g->width - src_xx;
5496 if (width > door_rect->width)
5497 width = door_rect->width;
5499 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5502 if (pos->step_yoffset < 0) // door part on bottom side
5505 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5508 if (dst_yy + height > door_rect->height)
5509 height = door_rect->height - dst_yy;
5511 else // door part on top side
5514 dst_yy = pos->y - kk * pos->step_yoffset;
5518 src_yy = ABS(dst_yy);
5522 height = g->height - src_yy;
5525 src_x = g_src_x + src_xx;
5526 src_y = g_src_y + src_yy;
5528 dst_x = door_rect->x + dst_xx;
5529 dst_y = door_rect->y + dst_yy;
5531 is_panel_and_door_has_closed =
5534 panel_has_doors[door_index] &&
5535 k >= num_move_steps_doors_only - 1);
5537 if (width >= 0 && width <= g->width &&
5538 height >= 0 && height <= g->height &&
5539 !is_panel_and_door_has_closed)
5541 if (is_panel || !pos->draw_masked)
5542 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5545 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5549 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5551 if ((part_opening && (width < 0 || height < 0)) ||
5552 (part_closing && (width >= g->width && height >= g->height)))
5553 door_part_done[nr] = TRUE;
5555 // continue door part animations, but not panel after door has closed
5556 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5557 door_part_done_all = FALSE;
5560 if (!(door_state & DOOR_NO_DELAY))
5563 HandleGameActions();
5567 SkipUntilDelayReached(&door_delay, &k, last_frame);
5569 // prevent OS (Windows) from complaining about program not responding
5573 if (door_part_done_all)
5577 if (!(door_state & DOOR_NO_DELAY))
5579 // wait for specified door action post delay
5580 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5581 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5582 else if (door_state & DOOR_ACTION_1)
5583 door_delay.value = door_1.post_delay;
5584 else if (door_state & DOOR_ACTION_2)
5585 door_delay.value = door_2.post_delay;
5587 while (!DelayReached(&door_delay))
5590 HandleGameActions();
5597 if (door_state & DOOR_ACTION_1)
5598 door1 = door_state & DOOR_ACTION_1;
5599 if (door_state & DOOR_ACTION_2)
5600 door2 = door_state & DOOR_ACTION_2;
5602 // draw masked border over door area
5603 DrawMaskedBorder(REDRAW_DOOR_1);
5604 DrawMaskedBorder(REDRAW_DOOR_2);
5606 ClearAutoRepeatKeyEvents();
5608 return (door1 | door2);
5611 static boolean useSpecialEditorDoor(void)
5613 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5614 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5616 // do not draw special editor door if editor border defined or redefined
5617 if (graphic_info[graphic].bitmap != NULL || redefined)
5620 // do not draw special editor door if global border defined to be empty
5621 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5624 // do not draw special editor door if viewport definitions do not match
5628 EY + EYSIZE != VY + VYSIZE)
5634 void DrawSpecialEditorDoor(void)
5636 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5637 int top_border_width = gfx1->width;
5638 int top_border_height = gfx1->height;
5639 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5640 int ex = EX - outer_border;
5641 int ey = EY - outer_border;
5642 int vy = VY - outer_border;
5643 int exsize = EXSIZE + 2 * outer_border;
5645 if (!useSpecialEditorDoor())
5648 // draw bigger level editor toolbox window
5649 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5650 top_border_width, top_border_height, ex, ey - top_border_height);
5651 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5652 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5654 redraw_mask |= REDRAW_ALL;
5657 void UndrawSpecialEditorDoor(void)
5659 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5660 int top_border_width = gfx1->width;
5661 int top_border_height = gfx1->height;
5662 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5663 int ex = EX - outer_border;
5664 int ey = EY - outer_border;
5665 int ey_top = ey - top_border_height;
5666 int exsize = EXSIZE + 2 * outer_border;
5667 int eysize = EYSIZE + 2 * outer_border;
5669 if (!useSpecialEditorDoor())
5672 // draw normal tape recorder window
5673 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5675 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5676 ex, ey_top, top_border_width, top_border_height,
5678 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5679 ex, ey, exsize, eysize, ex, ey);
5683 // if screen background is set to "[NONE]", clear editor toolbox window
5684 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5685 ClearRectangle(drawto, ex, ey, exsize, eysize);
5688 redraw_mask |= REDRAW_ALL;
5692 // ---------- new tool button stuff -------------------------------------------
5697 struct TextPosInfo *pos;
5699 boolean is_touch_button;
5701 } toolbutton_info[NUM_TOOL_BUTTONS] =
5704 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5705 TOOL_CTRL_ID_YES, FALSE, "yes"
5708 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5709 TOOL_CTRL_ID_NO, FALSE, "no"
5712 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5713 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5716 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5717 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5720 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5721 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5724 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5725 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5728 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5729 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5732 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5733 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5736 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5737 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5740 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5741 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5745 void CreateToolButtons(void)
5749 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5751 int graphic = toolbutton_info[i].graphic;
5752 struct GraphicInfo *gfx = &graphic_info[graphic];
5753 struct TextPosInfo *pos = toolbutton_info[i].pos;
5754 struct GadgetInfo *gi;
5755 Bitmap *deco_bitmap = None;
5756 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5757 unsigned int event_mask = GD_EVENT_RELEASED;
5758 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5759 int base_x = (is_touch_button ? 0 : DX);
5760 int base_y = (is_touch_button ? 0 : DY);
5761 int gd_x = gfx->src_x;
5762 int gd_y = gfx->src_y;
5763 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5764 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5769 // do not use touch buttons if overlay touch buttons are disabled
5770 if (is_touch_button && !setup.touch.overlay_buttons)
5773 if (global.use_envelope_request && !is_touch_button)
5775 setRequestPosition(&base_x, &base_y, TRUE);
5777 // check if request buttons are outside of envelope and fix, if needed
5778 if (x < 0 || x + gfx->width > request.width ||
5779 y < 0 || y + gfx->height > request.height)
5781 if (id == TOOL_CTRL_ID_YES)
5784 y = request.height - 2 * request.border_size - gfx->height;
5786 else if (id == TOOL_CTRL_ID_NO)
5788 x = request.width - 2 * request.border_size - gfx->width;
5789 y = request.height - 2 * request.border_size - gfx->height;
5791 else if (id == TOOL_CTRL_ID_CONFIRM)
5793 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5794 y = request.height - 2 * request.border_size - gfx->height;
5796 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5798 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5800 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5801 y = request.height - 2 * request.border_size - gfx->height * 2;
5803 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5804 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5809 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5812 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5814 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5815 pos->size, &deco_bitmap, &deco_x, &deco_y);
5816 deco_xpos = (gfx->width - pos->size) / 2;
5817 deco_ypos = (gfx->height - pos->size) / 2;
5820 gi = CreateGadget(GDI_CUSTOM_ID, id,
5821 GDI_IMAGE_ID, graphic,
5822 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5825 GDI_WIDTH, gfx->width,
5826 GDI_HEIGHT, gfx->height,
5827 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5828 GDI_STATE, GD_BUTTON_UNPRESSED,
5829 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5830 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5831 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5832 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5833 GDI_DECORATION_SIZE, pos->size, pos->size,
5834 GDI_DECORATION_SHIFTING, 1, 1,
5835 GDI_DIRECT_DRAW, FALSE,
5836 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5837 GDI_EVENT_MASK, event_mask,
5838 GDI_CALLBACK_ACTION, HandleToolButtons,
5842 Fail("cannot create gadget");
5844 tool_gadget[id] = gi;
5848 void FreeToolButtons(void)
5852 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5853 FreeGadget(tool_gadget[i]);
5856 static void MapToolButtons(unsigned int req_state)
5858 if (req_state & REQ_ASK)
5860 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5861 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5862 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5863 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5865 else if (req_state & REQ_CONFIRM)
5867 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5868 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5870 else if (req_state & REQ_PLAYER)
5872 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5873 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5874 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5875 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5879 static void UnmapToolButtons(void)
5883 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5884 UnmapGadget(tool_gadget[i]);
5887 static void HandleToolButtons(struct GadgetInfo *gi)
5889 request_gadget_id = gi->custom_id;
5892 static struct Mapping_EM_to_RND_object
5895 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5896 boolean is_backside; // backside of moving element
5902 em_object_mapping_list[GAME_TILE_MAX + 1] =
5905 Zborder, FALSE, FALSE,
5909 Zplayer, FALSE, FALSE,
5918 Ztank, FALSE, FALSE,
5922 Zeater, FALSE, FALSE,
5926 Zdynamite, FALSE, FALSE,
5930 Zboom, FALSE, FALSE,
5935 Xchain, FALSE, FALSE,
5936 EL_DEFAULT, ACTION_EXPLODING, -1
5939 Xboom_bug, FALSE, FALSE,
5940 EL_BUG, ACTION_EXPLODING, -1
5943 Xboom_tank, FALSE, FALSE,
5944 EL_SPACESHIP, ACTION_EXPLODING, -1
5947 Xboom_android, FALSE, FALSE,
5948 EL_EMC_ANDROID, ACTION_OTHER, -1
5951 Xboom_1, FALSE, FALSE,
5952 EL_DEFAULT, ACTION_EXPLODING, -1
5955 Xboom_2, FALSE, FALSE,
5956 EL_DEFAULT, ACTION_EXPLODING, -1
5960 Xblank, TRUE, FALSE,
5965 Xsplash_e, FALSE, FALSE,
5966 EL_ACID_SPLASH_RIGHT, -1, -1
5969 Xsplash_w, FALSE, FALSE,
5970 EL_ACID_SPLASH_LEFT, -1, -1
5974 Xplant, TRUE, FALSE,
5975 EL_EMC_PLANT, -1, -1
5978 Yplant, FALSE, FALSE,
5979 EL_EMC_PLANT, -1, -1
5983 Xacid_1, TRUE, FALSE,
5987 Xacid_2, FALSE, FALSE,
5991 Xacid_3, FALSE, FALSE,
5995 Xacid_4, FALSE, FALSE,
5999 Xacid_5, FALSE, FALSE,
6003 Xacid_6, FALSE, FALSE,
6007 Xacid_7, FALSE, FALSE,
6011 Xacid_8, FALSE, FALSE,
6016 Xfake_acid_1, TRUE, FALSE,
6017 EL_EMC_FAKE_ACID, -1, -1
6020 Xfake_acid_2, FALSE, FALSE,
6021 EL_EMC_FAKE_ACID, -1, -1
6024 Xfake_acid_3, FALSE, FALSE,
6025 EL_EMC_FAKE_ACID, -1, -1
6028 Xfake_acid_4, FALSE, FALSE,
6029 EL_EMC_FAKE_ACID, -1, -1
6032 Xfake_acid_5, FALSE, FALSE,
6033 EL_EMC_FAKE_ACID, -1, -1
6036 Xfake_acid_6, FALSE, FALSE,
6037 EL_EMC_FAKE_ACID, -1, -1
6040 Xfake_acid_7, FALSE, FALSE,
6041 EL_EMC_FAKE_ACID, -1, -1
6044 Xfake_acid_8, FALSE, FALSE,
6045 EL_EMC_FAKE_ACID, -1, -1
6049 Xfake_acid_1_player, FALSE, FALSE,
6050 EL_EMC_FAKE_ACID, -1, -1
6053 Xfake_acid_2_player, FALSE, FALSE,
6054 EL_EMC_FAKE_ACID, -1, -1
6057 Xfake_acid_3_player, FALSE, FALSE,
6058 EL_EMC_FAKE_ACID, -1, -1
6061 Xfake_acid_4_player, FALSE, FALSE,
6062 EL_EMC_FAKE_ACID, -1, -1
6065 Xfake_acid_5_player, FALSE, FALSE,
6066 EL_EMC_FAKE_ACID, -1, -1
6069 Xfake_acid_6_player, FALSE, FALSE,
6070 EL_EMC_FAKE_ACID, -1, -1
6073 Xfake_acid_7_player, FALSE, FALSE,
6074 EL_EMC_FAKE_ACID, -1, -1
6077 Xfake_acid_8_player, FALSE, FALSE,
6078 EL_EMC_FAKE_ACID, -1, -1
6082 Xgrass, TRUE, FALSE,
6083 EL_EMC_GRASS, -1, -1
6086 Ygrass_nB, FALSE, FALSE,
6087 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6090 Ygrass_eB, FALSE, FALSE,
6091 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6094 Ygrass_sB, FALSE, FALSE,
6095 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6098 Ygrass_wB, FALSE, FALSE,
6099 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6107 Ydirt_nB, FALSE, FALSE,
6108 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6111 Ydirt_eB, FALSE, FALSE,
6112 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6115 Ydirt_sB, FALSE, FALSE,
6116 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6119 Ydirt_wB, FALSE, FALSE,
6120 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6124 Xandroid, TRUE, FALSE,
6125 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6128 Xandroid_1_n, FALSE, FALSE,
6129 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6132 Xandroid_2_n, FALSE, FALSE,
6133 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6136 Xandroid_1_e, FALSE, FALSE,
6137 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6140 Xandroid_2_e, FALSE, FALSE,
6141 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6144 Xandroid_1_w, FALSE, FALSE,
6145 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6148 Xandroid_2_w, FALSE, FALSE,
6149 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6152 Xandroid_1_s, FALSE, FALSE,
6153 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6156 Xandroid_2_s, FALSE, FALSE,
6157 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6160 Yandroid_n, FALSE, FALSE,
6161 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6164 Yandroid_nB, FALSE, TRUE,
6165 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6168 Yandroid_ne, FALSE, FALSE,
6169 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6172 Yandroid_neB, FALSE, TRUE,
6173 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6176 Yandroid_e, FALSE, FALSE,
6177 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6180 Yandroid_eB, FALSE, TRUE,
6181 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6184 Yandroid_se, FALSE, FALSE,
6185 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6188 Yandroid_seB, FALSE, TRUE,
6189 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6192 Yandroid_s, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6196 Yandroid_sB, FALSE, TRUE,
6197 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6200 Yandroid_sw, FALSE, FALSE,
6201 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6204 Yandroid_swB, FALSE, TRUE,
6205 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6208 Yandroid_w, FALSE, FALSE,
6209 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6212 Yandroid_wB, FALSE, TRUE,
6213 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6216 Yandroid_nw, FALSE, FALSE,
6217 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6220 Yandroid_nwB, FALSE, TRUE,
6221 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6225 Xeater_n, TRUE, FALSE,
6226 EL_YAMYAM_UP, -1, -1
6229 Xeater_e, TRUE, FALSE,
6230 EL_YAMYAM_RIGHT, -1, -1
6233 Xeater_w, TRUE, FALSE,
6234 EL_YAMYAM_LEFT, -1, -1
6237 Xeater_s, TRUE, FALSE,
6238 EL_YAMYAM_DOWN, -1, -1
6241 Yeater_n, FALSE, FALSE,
6242 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6245 Yeater_nB, FALSE, TRUE,
6246 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6249 Yeater_e, FALSE, FALSE,
6250 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6253 Yeater_eB, FALSE, TRUE,
6254 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6257 Yeater_s, FALSE, FALSE,
6258 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6261 Yeater_sB, FALSE, TRUE,
6262 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6265 Yeater_w, FALSE, FALSE,
6266 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6269 Yeater_wB, FALSE, TRUE,
6270 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6273 Yeater_stone, FALSE, FALSE,
6274 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6277 Yeater_spring, FALSE, FALSE,
6278 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6282 Xalien, TRUE, FALSE,
6286 Xalien_pause, FALSE, FALSE,
6290 Yalien_n, FALSE, FALSE,
6291 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6294 Yalien_nB, FALSE, TRUE,
6295 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6298 Yalien_e, FALSE, FALSE,
6299 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6302 Yalien_eB, FALSE, TRUE,
6303 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6306 Yalien_s, FALSE, FALSE,
6307 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6310 Yalien_sB, FALSE, TRUE,
6311 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6314 Yalien_w, FALSE, FALSE,
6315 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6318 Yalien_wB, FALSE, TRUE,
6319 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6322 Yalien_stone, FALSE, FALSE,
6323 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6326 Yalien_spring, FALSE, FALSE,
6327 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6331 Xbug_1_n, TRUE, FALSE,
6335 Xbug_1_e, TRUE, FALSE,
6336 EL_BUG_RIGHT, -1, -1
6339 Xbug_1_s, TRUE, FALSE,
6343 Xbug_1_w, TRUE, FALSE,
6347 Xbug_2_n, FALSE, FALSE,
6351 Xbug_2_e, FALSE, FALSE,
6352 EL_BUG_RIGHT, -1, -1
6355 Xbug_2_s, FALSE, FALSE,
6359 Xbug_2_w, FALSE, FALSE,
6363 Ybug_n, FALSE, FALSE,
6364 EL_BUG, ACTION_MOVING, MV_BIT_UP
6367 Ybug_nB, FALSE, TRUE,
6368 EL_BUG, ACTION_MOVING, MV_BIT_UP
6371 Ybug_e, FALSE, FALSE,
6372 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6375 Ybug_eB, FALSE, TRUE,
6376 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6379 Ybug_s, FALSE, FALSE,
6380 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6383 Ybug_sB, FALSE, TRUE,
6384 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6387 Ybug_w, FALSE, FALSE,
6388 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6391 Ybug_wB, FALSE, TRUE,
6392 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6395 Ybug_w_n, FALSE, FALSE,
6396 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6399 Ybug_n_e, FALSE, FALSE,
6400 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6403 Ybug_e_s, FALSE, FALSE,
6404 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6407 Ybug_s_w, FALSE, FALSE,
6408 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6411 Ybug_e_n, FALSE, FALSE,
6412 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6415 Ybug_s_e, FALSE, FALSE,
6416 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6419 Ybug_w_s, FALSE, FALSE,
6420 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6423 Ybug_n_w, FALSE, FALSE,
6424 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6427 Ybug_stone, FALSE, FALSE,
6428 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6431 Ybug_spring, FALSE, FALSE,
6432 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6436 Xtank_1_n, TRUE, FALSE,
6437 EL_SPACESHIP_UP, -1, -1
6440 Xtank_1_e, TRUE, FALSE,
6441 EL_SPACESHIP_RIGHT, -1, -1
6444 Xtank_1_s, TRUE, FALSE,
6445 EL_SPACESHIP_DOWN, -1, -1
6448 Xtank_1_w, TRUE, FALSE,
6449 EL_SPACESHIP_LEFT, -1, -1
6452 Xtank_2_n, FALSE, FALSE,
6453 EL_SPACESHIP_UP, -1, -1
6456 Xtank_2_e, FALSE, FALSE,
6457 EL_SPACESHIP_RIGHT, -1, -1
6460 Xtank_2_s, FALSE, FALSE,
6461 EL_SPACESHIP_DOWN, -1, -1
6464 Xtank_2_w, FALSE, FALSE,
6465 EL_SPACESHIP_LEFT, -1, -1
6468 Ytank_n, FALSE, FALSE,
6469 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6472 Ytank_nB, FALSE, TRUE,
6473 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6476 Ytank_e, FALSE, FALSE,
6477 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6480 Ytank_eB, FALSE, TRUE,
6481 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6484 Ytank_s, FALSE, FALSE,
6485 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6488 Ytank_sB, FALSE, TRUE,
6489 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6492 Ytank_w, FALSE, FALSE,
6493 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6496 Ytank_wB, FALSE, TRUE,
6497 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6500 Ytank_w_n, FALSE, FALSE,
6501 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6504 Ytank_n_e, FALSE, FALSE,
6505 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6508 Ytank_e_s, FALSE, FALSE,
6509 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6512 Ytank_s_w, FALSE, FALSE,
6513 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6516 Ytank_e_n, FALSE, FALSE,
6517 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6520 Ytank_s_e, FALSE, FALSE,
6521 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6524 Ytank_w_s, FALSE, FALSE,
6525 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6528 Ytank_n_w, FALSE, FALSE,
6529 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6532 Ytank_stone, FALSE, FALSE,
6533 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6536 Ytank_spring, FALSE, FALSE,
6537 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6541 Xemerald, TRUE, FALSE,
6545 Xemerald_pause, FALSE, FALSE,
6549 Xemerald_fall, FALSE, FALSE,
6553 Xemerald_shine, FALSE, FALSE,
6554 EL_EMERALD, ACTION_TWINKLING, -1
6557 Yemerald_s, FALSE, FALSE,
6558 EL_EMERALD, ACTION_FALLING, -1
6561 Yemerald_sB, FALSE, TRUE,
6562 EL_EMERALD, ACTION_FALLING, -1
6565 Yemerald_e, FALSE, FALSE,
6566 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6569 Yemerald_eB, FALSE, TRUE,
6570 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6573 Yemerald_w, FALSE, FALSE,
6574 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6577 Yemerald_wB, FALSE, TRUE,
6578 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6581 Yemerald_blank, FALSE, FALSE,
6582 EL_EMERALD, ACTION_COLLECTING, -1
6586 Xdiamond, TRUE, FALSE,
6590 Xdiamond_pause, FALSE, FALSE,
6594 Xdiamond_fall, FALSE, FALSE,
6598 Xdiamond_shine, FALSE, FALSE,
6599 EL_DIAMOND, ACTION_TWINKLING, -1
6602 Ydiamond_s, FALSE, FALSE,
6603 EL_DIAMOND, ACTION_FALLING, -1
6606 Ydiamond_sB, FALSE, TRUE,
6607 EL_DIAMOND, ACTION_FALLING, -1
6610 Ydiamond_e, FALSE, FALSE,
6611 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6614 Ydiamond_eB, FALSE, TRUE,
6615 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6618 Ydiamond_w, FALSE, FALSE,
6619 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6622 Ydiamond_wB, FALSE, TRUE,
6623 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6626 Ydiamond_blank, FALSE, FALSE,
6627 EL_DIAMOND, ACTION_COLLECTING, -1
6630 Ydiamond_stone, FALSE, FALSE,
6631 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6635 Xstone, TRUE, FALSE,
6639 Xstone_pause, FALSE, FALSE,
6643 Xstone_fall, FALSE, FALSE,
6647 Ystone_s, FALSE, FALSE,
6648 EL_ROCK, ACTION_FALLING, -1
6651 Ystone_sB, FALSE, TRUE,
6652 EL_ROCK, ACTION_FALLING, -1
6655 Ystone_e, FALSE, FALSE,
6656 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6659 Ystone_eB, FALSE, TRUE,
6660 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6663 Ystone_w, FALSE, FALSE,
6664 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6667 Ystone_wB, FALSE, TRUE,
6668 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6676 Xbomb_pause, FALSE, FALSE,
6680 Xbomb_fall, FALSE, FALSE,
6684 Ybomb_s, FALSE, FALSE,
6685 EL_BOMB, ACTION_FALLING, -1
6688 Ybomb_sB, FALSE, TRUE,
6689 EL_BOMB, ACTION_FALLING, -1
6692 Ybomb_e, FALSE, FALSE,
6693 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6696 Ybomb_eB, FALSE, TRUE,
6697 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6700 Ybomb_w, FALSE, FALSE,
6701 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6704 Ybomb_wB, FALSE, TRUE,
6705 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6708 Ybomb_blank, FALSE, FALSE,
6709 EL_BOMB, ACTION_ACTIVATING, -1
6717 Xnut_pause, FALSE, FALSE,
6721 Xnut_fall, FALSE, FALSE,
6725 Ynut_s, FALSE, FALSE,
6726 EL_NUT, ACTION_FALLING, -1
6729 Ynut_sB, FALSE, TRUE,
6730 EL_NUT, ACTION_FALLING, -1
6733 Ynut_e, FALSE, FALSE,
6734 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6737 Ynut_eB, FALSE, TRUE,
6738 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6741 Ynut_w, FALSE, FALSE,
6742 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6745 Ynut_wB, FALSE, TRUE,
6746 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6749 Ynut_stone, FALSE, FALSE,
6750 EL_NUT, ACTION_BREAKING, -1
6754 Xspring, TRUE, FALSE,
6758 Xspring_pause, FALSE, FALSE,
6762 Xspring_e, TRUE, FALSE,
6763 EL_SPRING_RIGHT, -1, -1
6766 Xspring_w, TRUE, FALSE,
6767 EL_SPRING_LEFT, -1, -1
6770 Xspring_fall, FALSE, FALSE,
6774 Yspring_s, FALSE, FALSE,
6775 EL_SPRING, ACTION_FALLING, -1
6778 Yspring_sB, FALSE, TRUE,
6779 EL_SPRING, ACTION_FALLING, -1
6782 Yspring_e, FALSE, FALSE,
6783 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6786 Yspring_eB, FALSE, TRUE,
6787 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6790 Yspring_w, FALSE, FALSE,
6791 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6794 Yspring_wB, FALSE, TRUE,
6795 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6798 Yspring_alien_e, FALSE, FALSE,
6799 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6802 Yspring_alien_eB, FALSE, TRUE,
6803 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6806 Yspring_alien_w, FALSE, FALSE,
6807 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6810 Yspring_alien_wB, FALSE, TRUE,
6811 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6815 Xpush_emerald_e, FALSE, FALSE,
6816 EL_EMERALD, -1, MV_BIT_RIGHT
6819 Xpush_emerald_w, FALSE, FALSE,
6820 EL_EMERALD, -1, MV_BIT_LEFT
6823 Xpush_diamond_e, FALSE, FALSE,
6824 EL_DIAMOND, -1, MV_BIT_RIGHT
6827 Xpush_diamond_w, FALSE, FALSE,
6828 EL_DIAMOND, -1, MV_BIT_LEFT
6831 Xpush_stone_e, FALSE, FALSE,
6832 EL_ROCK, -1, MV_BIT_RIGHT
6835 Xpush_stone_w, FALSE, FALSE,
6836 EL_ROCK, -1, MV_BIT_LEFT
6839 Xpush_bomb_e, FALSE, FALSE,
6840 EL_BOMB, -1, MV_BIT_RIGHT
6843 Xpush_bomb_w, FALSE, FALSE,
6844 EL_BOMB, -1, MV_BIT_LEFT
6847 Xpush_nut_e, FALSE, FALSE,
6848 EL_NUT, -1, MV_BIT_RIGHT
6851 Xpush_nut_w, FALSE, FALSE,
6852 EL_NUT, -1, MV_BIT_LEFT
6855 Xpush_spring_e, FALSE, FALSE,
6856 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6859 Xpush_spring_w, FALSE, FALSE,
6860 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6864 Xdynamite, TRUE, FALSE,
6865 EL_EM_DYNAMITE, -1, -1
6868 Ydynamite_blank, FALSE, FALSE,
6869 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6872 Xdynamite_1, TRUE, FALSE,
6873 EL_EM_DYNAMITE_ACTIVE, -1, -1
6876 Xdynamite_2, FALSE, FALSE,
6877 EL_EM_DYNAMITE_ACTIVE, -1, -1
6880 Xdynamite_3, FALSE, FALSE,
6881 EL_EM_DYNAMITE_ACTIVE, -1, -1
6884 Xdynamite_4, FALSE, FALSE,
6885 EL_EM_DYNAMITE_ACTIVE, -1, -1
6889 Xkey_1, TRUE, FALSE,
6893 Xkey_2, TRUE, FALSE,
6897 Xkey_3, TRUE, FALSE,
6901 Xkey_4, TRUE, FALSE,
6905 Xkey_5, TRUE, FALSE,
6906 EL_EMC_KEY_5, -1, -1
6909 Xkey_6, TRUE, FALSE,
6910 EL_EMC_KEY_6, -1, -1
6913 Xkey_7, TRUE, FALSE,
6914 EL_EMC_KEY_7, -1, -1
6917 Xkey_8, TRUE, FALSE,
6918 EL_EMC_KEY_8, -1, -1
6922 Xdoor_1, TRUE, FALSE,
6923 EL_EM_GATE_1, -1, -1
6926 Xdoor_2, TRUE, FALSE,
6927 EL_EM_GATE_2, -1, -1
6930 Xdoor_3, TRUE, FALSE,
6931 EL_EM_GATE_3, -1, -1
6934 Xdoor_4, TRUE, FALSE,
6935 EL_EM_GATE_4, -1, -1
6938 Xdoor_5, TRUE, FALSE,
6939 EL_EMC_GATE_5, -1, -1
6942 Xdoor_6, TRUE, FALSE,
6943 EL_EMC_GATE_6, -1, -1
6946 Xdoor_7, TRUE, FALSE,
6947 EL_EMC_GATE_7, -1, -1
6950 Xdoor_8, TRUE, FALSE,
6951 EL_EMC_GATE_8, -1, -1
6955 Xfake_door_1, TRUE, FALSE,
6956 EL_EM_GATE_1_GRAY, -1, -1
6959 Xfake_door_2, TRUE, FALSE,
6960 EL_EM_GATE_2_GRAY, -1, -1
6963 Xfake_door_3, TRUE, FALSE,
6964 EL_EM_GATE_3_GRAY, -1, -1
6967 Xfake_door_4, TRUE, FALSE,
6968 EL_EM_GATE_4_GRAY, -1, -1
6971 Xfake_door_5, TRUE, FALSE,
6972 EL_EMC_GATE_5_GRAY, -1, -1
6975 Xfake_door_6, TRUE, FALSE,
6976 EL_EMC_GATE_6_GRAY, -1, -1
6979 Xfake_door_7, TRUE, FALSE,
6980 EL_EMC_GATE_7_GRAY, -1, -1
6983 Xfake_door_8, TRUE, FALSE,
6984 EL_EMC_GATE_8_GRAY, -1, -1
6988 Xballoon, TRUE, FALSE,
6992 Yballoon_n, FALSE, FALSE,
6993 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6996 Yballoon_nB, FALSE, TRUE,
6997 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7000 Yballoon_e, FALSE, FALSE,
7001 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7004 Yballoon_eB, FALSE, TRUE,
7005 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7008 Yballoon_s, FALSE, FALSE,
7009 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7012 Yballoon_sB, FALSE, TRUE,
7013 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7016 Yballoon_w, FALSE, FALSE,
7017 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7020 Yballoon_wB, FALSE, TRUE,
7021 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7025 Xball_1, TRUE, FALSE,
7026 EL_EMC_MAGIC_BALL, -1, -1
7029 Yball_1, FALSE, FALSE,
7030 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7033 Xball_2, FALSE, FALSE,
7034 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7037 Yball_2, FALSE, FALSE,
7038 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7041 Yball_blank, FALSE, FALSE,
7042 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7046 Xamoeba_1, TRUE, FALSE,
7047 EL_AMOEBA_DRY, ACTION_OTHER, -1
7050 Xamoeba_2, FALSE, FALSE,
7051 EL_AMOEBA_DRY, ACTION_OTHER, -1
7054 Xamoeba_3, FALSE, FALSE,
7055 EL_AMOEBA_DRY, ACTION_OTHER, -1
7058 Xamoeba_4, FALSE, FALSE,
7059 EL_AMOEBA_DRY, ACTION_OTHER, -1
7062 Xamoeba_5, TRUE, FALSE,
7063 EL_AMOEBA_WET, ACTION_OTHER, -1
7066 Xamoeba_6, FALSE, FALSE,
7067 EL_AMOEBA_WET, ACTION_OTHER, -1
7070 Xamoeba_7, FALSE, FALSE,
7071 EL_AMOEBA_WET, ACTION_OTHER, -1
7074 Xamoeba_8, FALSE, FALSE,
7075 EL_AMOEBA_WET, ACTION_OTHER, -1
7080 EL_AMOEBA_DROP, ACTION_GROWING, -1
7083 Xdrip_fall, FALSE, FALSE,
7084 EL_AMOEBA_DROP, -1, -1
7087 Xdrip_stretch, FALSE, FALSE,
7088 EL_AMOEBA_DROP, ACTION_FALLING, -1
7091 Xdrip_stretchB, FALSE, TRUE,
7092 EL_AMOEBA_DROP, ACTION_FALLING, -1
7095 Ydrip_1_s, FALSE, FALSE,
7096 EL_AMOEBA_DROP, ACTION_FALLING, -1
7099 Ydrip_1_sB, FALSE, TRUE,
7100 EL_AMOEBA_DROP, ACTION_FALLING, -1
7103 Ydrip_2_s, FALSE, FALSE,
7104 EL_AMOEBA_DROP, ACTION_FALLING, -1
7107 Ydrip_2_sB, FALSE, TRUE,
7108 EL_AMOEBA_DROP, ACTION_FALLING, -1
7112 Xwonderwall, TRUE, FALSE,
7113 EL_MAGIC_WALL, -1, -1
7116 Ywonderwall, FALSE, FALSE,
7117 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7121 Xwheel, TRUE, FALSE,
7122 EL_ROBOT_WHEEL, -1, -1
7125 Ywheel, FALSE, FALSE,
7126 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7130 Xswitch, TRUE, FALSE,
7131 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7134 Yswitch, FALSE, FALSE,
7135 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7139 Xbumper, TRUE, FALSE,
7140 EL_EMC_SPRING_BUMPER, -1, -1
7143 Ybumper, FALSE, FALSE,
7144 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7148 Xacid_nw, TRUE, FALSE,
7149 EL_ACID_POOL_TOPLEFT, -1, -1
7152 Xacid_ne, TRUE, FALSE,
7153 EL_ACID_POOL_TOPRIGHT, -1, -1
7156 Xacid_sw, TRUE, FALSE,
7157 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7160 Xacid_s, TRUE, FALSE,
7161 EL_ACID_POOL_BOTTOM, -1, -1
7164 Xacid_se, TRUE, FALSE,
7165 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7169 Xfake_blank, TRUE, FALSE,
7170 EL_INVISIBLE_WALL, -1, -1
7173 Yfake_blank, FALSE, FALSE,
7174 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7178 Xfake_grass, TRUE, FALSE,
7179 EL_EMC_FAKE_GRASS, -1, -1
7182 Yfake_grass, FALSE, FALSE,
7183 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7187 Xfake_amoeba, TRUE, FALSE,
7188 EL_EMC_DRIPPER, -1, -1
7191 Yfake_amoeba, FALSE, FALSE,
7192 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7196 Xlenses, TRUE, FALSE,
7197 EL_EMC_LENSES, -1, -1
7201 Xmagnify, TRUE, FALSE,
7202 EL_EMC_MAGNIFIER, -1, -1
7207 EL_QUICKSAND_EMPTY, -1, -1
7210 Xsand_stone, TRUE, FALSE,
7211 EL_QUICKSAND_FULL, -1, -1
7214 Xsand_stonein_1, FALSE, TRUE,
7215 EL_ROCK, ACTION_FILLING, -1
7218 Xsand_stonein_2, FALSE, TRUE,
7219 EL_ROCK, ACTION_FILLING, -1
7222 Xsand_stonein_3, FALSE, TRUE,
7223 EL_ROCK, ACTION_FILLING, -1
7226 Xsand_stonein_4, FALSE, TRUE,
7227 EL_ROCK, ACTION_FILLING, -1
7230 Xsand_sandstone_1, FALSE, FALSE,
7231 EL_QUICKSAND_FILLING, -1, -1
7234 Xsand_sandstone_2, FALSE, FALSE,
7235 EL_QUICKSAND_FILLING, -1, -1
7238 Xsand_sandstone_3, FALSE, FALSE,
7239 EL_QUICKSAND_FILLING, -1, -1
7242 Xsand_sandstone_4, FALSE, FALSE,
7243 EL_QUICKSAND_FILLING, -1, -1
7246 Xsand_stonesand_1, FALSE, FALSE,
7247 EL_QUICKSAND_EMPTYING, -1, -1
7250 Xsand_stonesand_2, FALSE, FALSE,
7251 EL_QUICKSAND_EMPTYING, -1, -1
7254 Xsand_stonesand_3, FALSE, FALSE,
7255 EL_QUICKSAND_EMPTYING, -1, -1
7258 Xsand_stonesand_4, FALSE, FALSE,
7259 EL_QUICKSAND_EMPTYING, -1, -1
7262 Xsand_stoneout_1, FALSE, FALSE,
7263 EL_ROCK, ACTION_EMPTYING, -1
7266 Xsand_stoneout_2, FALSE, FALSE,
7267 EL_ROCK, ACTION_EMPTYING, -1
7270 Xsand_stonesand_quickout_1, FALSE, FALSE,
7271 EL_QUICKSAND_EMPTYING, -1, -1
7274 Xsand_stonesand_quickout_2, FALSE, FALSE,
7275 EL_QUICKSAND_EMPTYING, -1, -1
7279 Xslide_ns, TRUE, FALSE,
7280 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7283 Yslide_ns_blank, FALSE, FALSE,
7284 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7287 Xslide_ew, TRUE, FALSE,
7288 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7291 Yslide_ew_blank, FALSE, FALSE,
7292 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7296 Xwind_n, TRUE, FALSE,
7297 EL_BALLOON_SWITCH_UP, -1, -1
7300 Xwind_e, TRUE, FALSE,
7301 EL_BALLOON_SWITCH_RIGHT, -1, -1
7304 Xwind_s, TRUE, FALSE,
7305 EL_BALLOON_SWITCH_DOWN, -1, -1
7308 Xwind_w, TRUE, FALSE,
7309 EL_BALLOON_SWITCH_LEFT, -1, -1
7312 Xwind_any, TRUE, FALSE,
7313 EL_BALLOON_SWITCH_ANY, -1, -1
7316 Xwind_stop, TRUE, FALSE,
7317 EL_BALLOON_SWITCH_NONE, -1, -1
7322 EL_EM_EXIT_CLOSED, -1, -1
7325 Xexit_1, TRUE, FALSE,
7326 EL_EM_EXIT_OPEN, -1, -1
7329 Xexit_2, FALSE, FALSE,
7330 EL_EM_EXIT_OPEN, -1, -1
7333 Xexit_3, FALSE, FALSE,
7334 EL_EM_EXIT_OPEN, -1, -1
7338 Xpause, FALSE, FALSE,
7343 Xwall_1, TRUE, FALSE,
7347 Xwall_2, TRUE, FALSE,
7348 EL_EMC_WALL_14, -1, -1
7351 Xwall_3, TRUE, FALSE,
7352 EL_EMC_WALL_15, -1, -1
7355 Xwall_4, TRUE, FALSE,
7356 EL_EMC_WALL_16, -1, -1
7360 Xroundwall_1, TRUE, FALSE,
7361 EL_WALL_SLIPPERY, -1, -1
7364 Xroundwall_2, TRUE, FALSE,
7365 EL_EMC_WALL_SLIPPERY_2, -1, -1
7368 Xroundwall_3, TRUE, FALSE,
7369 EL_EMC_WALL_SLIPPERY_3, -1, -1
7372 Xroundwall_4, TRUE, FALSE,
7373 EL_EMC_WALL_SLIPPERY_4, -1, -1
7377 Xsteel_1, TRUE, FALSE,
7378 EL_STEELWALL, -1, -1
7381 Xsteel_2, TRUE, FALSE,
7382 EL_EMC_STEELWALL_2, -1, -1
7385 Xsteel_3, TRUE, FALSE,
7386 EL_EMC_STEELWALL_3, -1, -1
7389 Xsteel_4, TRUE, FALSE,
7390 EL_EMC_STEELWALL_4, -1, -1
7394 Xdecor_1, TRUE, FALSE,
7395 EL_EMC_WALL_8, -1, -1
7398 Xdecor_2, TRUE, FALSE,
7399 EL_EMC_WALL_6, -1, -1
7402 Xdecor_3, TRUE, FALSE,
7403 EL_EMC_WALL_4, -1, -1
7406 Xdecor_4, TRUE, FALSE,
7407 EL_EMC_WALL_7, -1, -1
7410 Xdecor_5, TRUE, FALSE,
7411 EL_EMC_WALL_5, -1, -1
7414 Xdecor_6, TRUE, FALSE,
7415 EL_EMC_WALL_9, -1, -1
7418 Xdecor_7, TRUE, FALSE,
7419 EL_EMC_WALL_10, -1, -1
7422 Xdecor_8, TRUE, FALSE,
7423 EL_EMC_WALL_1, -1, -1
7426 Xdecor_9, TRUE, FALSE,
7427 EL_EMC_WALL_2, -1, -1
7430 Xdecor_10, TRUE, FALSE,
7431 EL_EMC_WALL_3, -1, -1
7434 Xdecor_11, TRUE, FALSE,
7435 EL_EMC_WALL_11, -1, -1
7438 Xdecor_12, TRUE, FALSE,
7439 EL_EMC_WALL_12, -1, -1
7443 Xalpha_0, TRUE, FALSE,
7444 EL_CHAR('0'), -1, -1
7447 Xalpha_1, TRUE, FALSE,
7448 EL_CHAR('1'), -1, -1
7451 Xalpha_2, TRUE, FALSE,
7452 EL_CHAR('2'), -1, -1
7455 Xalpha_3, TRUE, FALSE,
7456 EL_CHAR('3'), -1, -1
7459 Xalpha_4, TRUE, FALSE,
7460 EL_CHAR('4'), -1, -1
7463 Xalpha_5, TRUE, FALSE,
7464 EL_CHAR('5'), -1, -1
7467 Xalpha_6, TRUE, FALSE,
7468 EL_CHAR('6'), -1, -1
7471 Xalpha_7, TRUE, FALSE,
7472 EL_CHAR('7'), -1, -1
7475 Xalpha_8, TRUE, FALSE,
7476 EL_CHAR('8'), -1, -1
7479 Xalpha_9, TRUE, FALSE,
7480 EL_CHAR('9'), -1, -1
7483 Xalpha_excla, TRUE, FALSE,
7484 EL_CHAR('!'), -1, -1
7487 Xalpha_apost, TRUE, FALSE,
7488 EL_CHAR('\''), -1, -1
7491 Xalpha_comma, TRUE, FALSE,
7492 EL_CHAR(','), -1, -1
7495 Xalpha_minus, TRUE, FALSE,
7496 EL_CHAR('-'), -1, -1
7499 Xalpha_perio, TRUE, FALSE,
7500 EL_CHAR('.'), -1, -1
7503 Xalpha_colon, TRUE, FALSE,
7504 EL_CHAR(':'), -1, -1
7507 Xalpha_quest, TRUE, FALSE,
7508 EL_CHAR('?'), -1, -1
7511 Xalpha_a, TRUE, FALSE,
7512 EL_CHAR('A'), -1, -1
7515 Xalpha_b, TRUE, FALSE,
7516 EL_CHAR('B'), -1, -1
7519 Xalpha_c, TRUE, FALSE,
7520 EL_CHAR('C'), -1, -1
7523 Xalpha_d, TRUE, FALSE,
7524 EL_CHAR('D'), -1, -1
7527 Xalpha_e, TRUE, FALSE,
7528 EL_CHAR('E'), -1, -1
7531 Xalpha_f, TRUE, FALSE,
7532 EL_CHAR('F'), -1, -1
7535 Xalpha_g, TRUE, FALSE,
7536 EL_CHAR('G'), -1, -1
7539 Xalpha_h, TRUE, FALSE,
7540 EL_CHAR('H'), -1, -1
7543 Xalpha_i, TRUE, FALSE,
7544 EL_CHAR('I'), -1, -1
7547 Xalpha_j, TRUE, FALSE,
7548 EL_CHAR('J'), -1, -1
7551 Xalpha_k, TRUE, FALSE,
7552 EL_CHAR('K'), -1, -1
7555 Xalpha_l, TRUE, FALSE,
7556 EL_CHAR('L'), -1, -1
7559 Xalpha_m, TRUE, FALSE,
7560 EL_CHAR('M'), -1, -1
7563 Xalpha_n, TRUE, FALSE,
7564 EL_CHAR('N'), -1, -1
7567 Xalpha_o, TRUE, FALSE,
7568 EL_CHAR('O'), -1, -1
7571 Xalpha_p, TRUE, FALSE,
7572 EL_CHAR('P'), -1, -1
7575 Xalpha_q, TRUE, FALSE,
7576 EL_CHAR('Q'), -1, -1
7579 Xalpha_r, TRUE, FALSE,
7580 EL_CHAR('R'), -1, -1
7583 Xalpha_s, TRUE, FALSE,
7584 EL_CHAR('S'), -1, -1
7587 Xalpha_t, TRUE, FALSE,
7588 EL_CHAR('T'), -1, -1
7591 Xalpha_u, TRUE, FALSE,
7592 EL_CHAR('U'), -1, -1
7595 Xalpha_v, TRUE, FALSE,
7596 EL_CHAR('V'), -1, -1
7599 Xalpha_w, TRUE, FALSE,
7600 EL_CHAR('W'), -1, -1
7603 Xalpha_x, TRUE, FALSE,
7604 EL_CHAR('X'), -1, -1
7607 Xalpha_y, TRUE, FALSE,
7608 EL_CHAR('Y'), -1, -1
7611 Xalpha_z, TRUE, FALSE,
7612 EL_CHAR('Z'), -1, -1
7615 Xalpha_arrow_e, TRUE, FALSE,
7616 EL_CHAR('>'), -1, -1
7619 Xalpha_arrow_w, TRUE, FALSE,
7620 EL_CHAR('<'), -1, -1
7623 Xalpha_copyr, TRUE, FALSE,
7624 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7628 Ykey_1_blank, FALSE, FALSE,
7629 EL_EM_KEY_1, ACTION_COLLECTING, -1
7632 Ykey_2_blank, FALSE, FALSE,
7633 EL_EM_KEY_2, ACTION_COLLECTING, -1
7636 Ykey_3_blank, FALSE, FALSE,
7637 EL_EM_KEY_3, ACTION_COLLECTING, -1
7640 Ykey_4_blank, FALSE, FALSE,
7641 EL_EM_KEY_4, ACTION_COLLECTING, -1
7644 Ykey_5_blank, FALSE, FALSE,
7645 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7648 Ykey_6_blank, FALSE, FALSE,
7649 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7652 Ykey_7_blank, FALSE, FALSE,
7653 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7656 Ykey_8_blank, FALSE, FALSE,
7657 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7660 Ylenses_blank, FALSE, FALSE,
7661 EL_EMC_LENSES, ACTION_COLLECTING, -1
7664 Ymagnify_blank, FALSE, FALSE,
7665 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7668 Ygrass_blank, FALSE, FALSE,
7669 EL_EMC_GRASS, ACTION_SNAPPING, -1
7672 Ydirt_blank, FALSE, FALSE,
7673 EL_SAND, ACTION_SNAPPING, -1
7682 static struct Mapping_EM_to_RND_player
7691 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7695 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7699 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7703 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7707 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7711 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7715 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7719 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7723 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7727 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7731 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7735 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7739 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7743 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7747 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7751 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7755 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7759 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7763 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7767 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7771 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7775 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7779 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7783 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7787 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7791 EL_PLAYER_1, ACTION_DEFAULT, -1,
7795 EL_PLAYER_2, ACTION_DEFAULT, -1,
7799 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7803 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7807 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7811 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7815 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7819 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7823 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7827 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7831 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7835 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7839 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7843 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7847 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7851 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7855 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7859 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7863 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7867 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7871 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7875 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7879 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7883 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7887 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7891 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7895 EL_PLAYER_3, ACTION_DEFAULT, -1,
7899 EL_PLAYER_4, ACTION_DEFAULT, -1,
7908 int map_element_RND_to_EM_cave(int element_rnd)
7910 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7911 static boolean mapping_initialized = FALSE;
7913 if (!mapping_initialized)
7917 // return "Xalpha_quest" for all undefined elements in mapping array
7918 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7919 mapping_RND_to_EM[i] = Xalpha_quest;
7921 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7922 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7923 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7924 em_object_mapping_list[i].element_em;
7926 mapping_initialized = TRUE;
7929 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7931 Warn("invalid RND level element %d", element_rnd);
7936 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7939 int map_element_EM_to_RND_cave(int element_em_cave)
7941 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7942 static boolean mapping_initialized = FALSE;
7944 if (!mapping_initialized)
7948 // return "EL_UNKNOWN" for all undefined elements in mapping array
7949 for (i = 0; i < GAME_TILE_MAX; i++)
7950 mapping_EM_to_RND[i] = EL_UNKNOWN;
7952 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7953 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7954 em_object_mapping_list[i].element_rnd;
7956 mapping_initialized = TRUE;
7959 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7961 Warn("invalid EM cave element %d", element_em_cave);
7966 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7969 int map_element_EM_to_RND_game(int element_em_game)
7971 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7972 static boolean mapping_initialized = FALSE;
7974 if (!mapping_initialized)
7978 // return "EL_UNKNOWN" for all undefined elements in mapping array
7979 for (i = 0; i < GAME_TILE_MAX; i++)
7980 mapping_EM_to_RND[i] = EL_UNKNOWN;
7982 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7983 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7984 em_object_mapping_list[i].element_rnd;
7986 mapping_initialized = TRUE;
7989 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7991 Warn("invalid EM game element %d", element_em_game);
7996 return mapping_EM_to_RND[element_em_game];
7999 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8001 struct LevelInfo_EM *level_em = level->native_em_level;
8002 struct CAVE *cav = level_em->cav;
8005 for (i = 0; i < GAME_TILE_MAX; i++)
8006 cav->android_array[i] = Cblank;
8008 for (i = 0; i < level->num_android_clone_elements; i++)
8010 int element_rnd = level->android_clone_element[i];
8011 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8013 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8014 if (em_object_mapping_list[j].element_rnd == element_rnd)
8015 cav->android_array[em_object_mapping_list[j].element_em] =
8020 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8022 struct LevelInfo_EM *level_em = level->native_em_level;
8023 struct CAVE *cav = level_em->cav;
8026 level->num_android_clone_elements = 0;
8028 for (i = 0; i < GAME_TILE_MAX; i++)
8030 int element_em_cave = cav->android_array[i];
8032 boolean element_found = FALSE;
8034 if (element_em_cave == Cblank)
8037 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8039 for (j = 0; j < level->num_android_clone_elements; j++)
8040 if (level->android_clone_element[j] == element_rnd)
8041 element_found = TRUE;
8045 level->android_clone_element[level->num_android_clone_elements++] =
8048 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8053 if (level->num_android_clone_elements == 0)
8055 level->num_android_clone_elements = 1;
8056 level->android_clone_element[0] = EL_EMPTY;
8060 int map_direction_RND_to_EM(int direction)
8062 return (direction == MV_UP ? 0 :
8063 direction == MV_RIGHT ? 1 :
8064 direction == MV_DOWN ? 2 :
8065 direction == MV_LEFT ? 3 :
8069 int map_direction_EM_to_RND(int direction)
8071 return (direction == 0 ? MV_UP :
8072 direction == 1 ? MV_RIGHT :
8073 direction == 2 ? MV_DOWN :
8074 direction == 3 ? MV_LEFT :
8078 int map_element_RND_to_SP(int element_rnd)
8080 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8082 if (element_rnd >= EL_SP_START &&
8083 element_rnd <= EL_SP_END)
8084 element_sp = element_rnd - EL_SP_START;
8085 else if (element_rnd == EL_EMPTY_SPACE)
8087 else if (element_rnd == EL_INVISIBLE_WALL)
8093 int map_element_SP_to_RND(int element_sp)
8095 int element_rnd = EL_UNKNOWN;
8097 if (element_sp >= 0x00 &&
8099 element_rnd = EL_SP_START + element_sp;
8100 else if (element_sp == 0x28)
8101 element_rnd = EL_INVISIBLE_WALL;
8106 int map_action_SP_to_RND(int action_sp)
8110 case actActive: return ACTION_ACTIVE;
8111 case actImpact: return ACTION_IMPACT;
8112 case actExploding: return ACTION_EXPLODING;
8113 case actDigging: return ACTION_DIGGING;
8114 case actSnapping: return ACTION_SNAPPING;
8115 case actCollecting: return ACTION_COLLECTING;
8116 case actPassing: return ACTION_PASSING;
8117 case actPushing: return ACTION_PUSHING;
8118 case actDropping: return ACTION_DROPPING;
8120 default: return ACTION_DEFAULT;
8124 int map_element_RND_to_MM(int element_rnd)
8126 return (element_rnd >= EL_MM_START_1 &&
8127 element_rnd <= EL_MM_END_1 ?
8128 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8130 element_rnd >= EL_MM_START_2 &&
8131 element_rnd <= EL_MM_END_2 ?
8132 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8134 element_rnd >= EL_MM_START_3 &&
8135 element_rnd <= EL_MM_END_3 ?
8136 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8138 element_rnd >= EL_CHAR_START &&
8139 element_rnd <= EL_CHAR_END ?
8140 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8142 element_rnd >= EL_MM_RUNTIME_START &&
8143 element_rnd <= EL_MM_RUNTIME_END ?
8144 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8146 EL_MM_EMPTY_NATIVE);
8149 int map_element_MM_to_RND(int element_mm)
8151 return (element_mm == EL_MM_EMPTY_NATIVE ||
8152 element_mm == EL_DF_EMPTY_NATIVE ?
8155 element_mm >= EL_MM_START_1_NATIVE &&
8156 element_mm <= EL_MM_END_1_NATIVE ?
8157 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8159 element_mm >= EL_MM_START_2_NATIVE &&
8160 element_mm <= EL_MM_END_2_NATIVE ?
8161 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8163 element_mm >= EL_MM_START_3_NATIVE &&
8164 element_mm <= EL_MM_END_3_NATIVE ?
8165 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8167 element_mm >= EL_MM_CHAR_START_NATIVE &&
8168 element_mm <= EL_MM_CHAR_END_NATIVE ?
8169 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8171 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8172 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8173 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8178 int map_action_MM_to_RND(int action_mm)
8180 // all MM actions are defined to exactly match their RND counterparts
8184 int map_sound_MM_to_RND(int sound_mm)
8188 case SND_MM_GAME_LEVELTIME_CHARGING:
8189 return SND_GAME_LEVELTIME_CHARGING;
8191 case SND_MM_GAME_HEALTH_CHARGING:
8192 return SND_GAME_HEALTH_CHARGING;
8195 return SND_UNDEFINED;
8199 int map_mm_wall_element(int element)
8201 return (element >= EL_MM_STEEL_WALL_START &&
8202 element <= EL_MM_STEEL_WALL_END ?
8205 element >= EL_MM_WOODEN_WALL_START &&
8206 element <= EL_MM_WOODEN_WALL_END ?
8209 element >= EL_MM_ICE_WALL_START &&
8210 element <= EL_MM_ICE_WALL_END ?
8213 element >= EL_MM_AMOEBA_WALL_START &&
8214 element <= EL_MM_AMOEBA_WALL_END ?
8217 element >= EL_DF_STEEL_WALL_START &&
8218 element <= EL_DF_STEEL_WALL_END ?
8221 element >= EL_DF_WOODEN_WALL_START &&
8222 element <= EL_DF_WOODEN_WALL_END ?
8228 int map_mm_wall_element_editor(int element)
8232 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8233 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8234 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8235 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8236 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8237 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8239 default: return element;
8243 int get_next_element(int element)
8247 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8248 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8249 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8250 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8251 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8252 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8253 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8254 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8255 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8256 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8257 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8259 default: return element;
8263 int el2img_mm(int element_mm)
8265 return el2img(map_element_MM_to_RND(element_mm));
8268 int el_act2img_mm(int element_mm, int action)
8270 return el_act2img(map_element_MM_to_RND(element_mm), action);
8273 int el_act_dir2img(int element, int action, int direction)
8275 element = GFX_ELEMENT(element);
8276 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8278 // direction_graphic[][] == graphic[] for undefined direction graphics
8279 return element_info[element].direction_graphic[action][direction];
8282 static int el_act_dir2crm(int element, int action, int direction)
8284 element = GFX_ELEMENT(element);
8285 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8287 // direction_graphic[][] == graphic[] for undefined direction graphics
8288 return element_info[element].direction_crumbled[action][direction];
8291 int el_act2img(int element, int action)
8293 element = GFX_ELEMENT(element);
8295 return element_info[element].graphic[action];
8298 int el_act2crm(int element, int action)
8300 element = GFX_ELEMENT(element);
8302 return element_info[element].crumbled[action];
8305 int el_dir2img(int element, int direction)
8307 element = GFX_ELEMENT(element);
8309 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8312 int el2baseimg(int element)
8314 return element_info[element].graphic[ACTION_DEFAULT];
8317 int el2img(int element)
8319 element = GFX_ELEMENT(element);
8321 return element_info[element].graphic[ACTION_DEFAULT];
8324 int el2edimg(int element)
8326 element = GFX_ELEMENT(element);
8328 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8331 int el2preimg(int element)
8333 element = GFX_ELEMENT(element);
8335 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8338 int el2panelimg(int element)
8340 element = GFX_ELEMENT(element);
8342 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8345 int font2baseimg(int font_nr)
8347 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8350 int getBeltNrFromBeltElement(int element)
8352 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8353 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8354 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8357 int getBeltNrFromBeltActiveElement(int element)
8359 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8360 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8361 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8364 int getBeltNrFromBeltSwitchElement(int element)
8366 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8367 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8368 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8371 int getBeltDirNrFromBeltElement(int element)
8373 static int belt_base_element[4] =
8375 EL_CONVEYOR_BELT_1_LEFT,
8376 EL_CONVEYOR_BELT_2_LEFT,
8377 EL_CONVEYOR_BELT_3_LEFT,
8378 EL_CONVEYOR_BELT_4_LEFT
8381 int belt_nr = getBeltNrFromBeltElement(element);
8382 int belt_dir_nr = element - belt_base_element[belt_nr];
8384 return (belt_dir_nr % 3);
8387 int getBeltDirNrFromBeltSwitchElement(int element)
8389 static int belt_base_element[4] =
8391 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8392 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8393 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8394 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8397 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8398 int belt_dir_nr = element - belt_base_element[belt_nr];
8400 return (belt_dir_nr % 3);
8403 int getBeltDirFromBeltElement(int element)
8405 static int belt_move_dir[3] =
8412 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8414 return belt_move_dir[belt_dir_nr];
8417 int getBeltDirFromBeltSwitchElement(int element)
8419 static int belt_move_dir[3] =
8426 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8428 return belt_move_dir[belt_dir_nr];
8431 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8433 static int belt_base_element[4] =
8435 EL_CONVEYOR_BELT_1_LEFT,
8436 EL_CONVEYOR_BELT_2_LEFT,
8437 EL_CONVEYOR_BELT_3_LEFT,
8438 EL_CONVEYOR_BELT_4_LEFT
8441 return belt_base_element[belt_nr] + belt_dir_nr;
8444 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8446 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8448 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8451 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8453 static int belt_base_element[4] =
8455 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8456 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8457 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8458 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8461 return belt_base_element[belt_nr] + belt_dir_nr;
8464 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8466 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8468 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8471 boolean swapTiles_EM(boolean is_pre_emc_cave)
8473 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8476 boolean getTeamMode_EM(void)
8478 return game.team_mode || network_playing;
8481 boolean isActivePlayer_EM(int player_nr)
8483 return stored_player[player_nr].active;
8486 unsigned int InitRND(int seed)
8488 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8489 return InitEngineRandom_EM(seed);
8490 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8491 return InitEngineRandom_SP(seed);
8492 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8493 return InitEngineRandom_MM(seed);
8495 return InitEngineRandom_RND(seed);
8498 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8499 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8501 static int get_effective_element_EM(int tile, int frame_em)
8503 int element = object_mapping[tile].element_rnd;
8504 int action = object_mapping[tile].action;
8505 boolean is_backside = object_mapping[tile].is_backside;
8506 boolean action_removing = (action == ACTION_DIGGING ||
8507 action == ACTION_SNAPPING ||
8508 action == ACTION_COLLECTING);
8516 return (frame_em > 5 ? EL_EMPTY : element);
8522 else // frame_em == 7
8533 case Ydiamond_stone:
8537 case Xdrip_stretchB:
8553 case Ymagnify_blank:
8556 case Xsand_stonein_1:
8557 case Xsand_stonein_2:
8558 case Xsand_stonein_3:
8559 case Xsand_stonein_4:
8563 return (is_backside || action_removing ? EL_EMPTY : element);
8568 static boolean check_linear_animation_EM(int tile)
8572 case Xsand_stonesand_1:
8573 case Xsand_stonesand_quickout_1:
8574 case Xsand_sandstone_1:
8575 case Xsand_stonein_1:
8576 case Xsand_stoneout_1:
8604 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8605 boolean has_crumbled_graphics,
8606 int crumbled, int sync_frame)
8608 // if element can be crumbled, but certain action graphics are just empty
8609 // space (like instantly snapping sand to empty space in 1 frame), do not
8610 // treat these empty space graphics as crumbled graphics in EMC engine
8611 if (crumbled == IMG_EMPTY_SPACE)
8612 has_crumbled_graphics = FALSE;
8614 if (has_crumbled_graphics)
8616 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8617 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8618 g_crumbled->anim_delay,
8619 g_crumbled->anim_mode,
8620 g_crumbled->anim_start_frame,
8623 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8624 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8626 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8627 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8629 g_em->has_crumbled_graphics = TRUE;
8633 g_em->crumbled_bitmap = NULL;
8634 g_em->crumbled_src_x = 0;
8635 g_em->crumbled_src_y = 0;
8636 g_em->crumbled_border_size = 0;
8637 g_em->crumbled_tile_size = 0;
8639 g_em->has_crumbled_graphics = FALSE;
8644 void ResetGfxAnimation_EM(int x, int y, int tile)
8650 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8651 int tile, int frame_em, int x, int y)
8653 int action = object_mapping[tile].action;
8654 int direction = object_mapping[tile].direction;
8655 int effective_element = get_effective_element_EM(tile, frame_em);
8656 int graphic = (direction == MV_NONE ?
8657 el_act2img(effective_element, action) :
8658 el_act_dir2img(effective_element, action, direction));
8659 struct GraphicInfo *g = &graphic_info[graphic];
8661 boolean action_removing = (action == ACTION_DIGGING ||
8662 action == ACTION_SNAPPING ||
8663 action == ACTION_COLLECTING);
8664 boolean action_moving = (action == ACTION_FALLING ||
8665 action == ACTION_MOVING ||
8666 action == ACTION_PUSHING ||
8667 action == ACTION_EATING ||
8668 action == ACTION_FILLING ||
8669 action == ACTION_EMPTYING);
8670 boolean action_falling = (action == ACTION_FALLING ||
8671 action == ACTION_FILLING ||
8672 action == ACTION_EMPTYING);
8674 // special case: graphic uses "2nd movement tile" and has defined
8675 // 7 frames for movement animation (or less) => use default graphic
8676 // for last (8th) frame which ends the movement animation
8677 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8679 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8680 graphic = (direction == MV_NONE ?
8681 el_act2img(effective_element, action) :
8682 el_act_dir2img(effective_element, action, direction));
8684 g = &graphic_info[graphic];
8687 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8691 else if (action_moving)
8693 boolean is_backside = object_mapping[tile].is_backside;
8697 int direction = object_mapping[tile].direction;
8698 int move_dir = (action_falling ? MV_DOWN : direction);
8703 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8704 if (g->double_movement && frame_em == 0)
8708 if (move_dir == MV_LEFT)
8709 GfxFrame[x - 1][y] = GfxFrame[x][y];
8710 else if (move_dir == MV_RIGHT)
8711 GfxFrame[x + 1][y] = GfxFrame[x][y];
8712 else if (move_dir == MV_UP)
8713 GfxFrame[x][y - 1] = GfxFrame[x][y];
8714 else if (move_dir == MV_DOWN)
8715 GfxFrame[x][y + 1] = GfxFrame[x][y];
8722 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8723 if (tile == Xsand_stonesand_quickout_1 ||
8724 tile == Xsand_stonesand_quickout_2)
8728 if (graphic_info[graphic].anim_global_sync)
8729 sync_frame = FrameCounter;
8730 else if (graphic_info[graphic].anim_global_anim_sync)
8731 sync_frame = getGlobalAnimSyncFrame();
8732 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8733 sync_frame = GfxFrame[x][y];
8735 sync_frame = 0; // playfield border (pseudo steel)
8737 SetRandomAnimationValue(x, y);
8739 int frame = getAnimationFrame(g->anim_frames,
8742 g->anim_start_frame,
8745 g_em->unique_identifier =
8746 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8749 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8750 int tile, int frame_em, int x, int y)
8752 int action = object_mapping[tile].action;
8753 int direction = object_mapping[tile].direction;
8754 boolean is_backside = object_mapping[tile].is_backside;
8755 int effective_element = get_effective_element_EM(tile, frame_em);
8756 int effective_action = action;
8757 int graphic = (direction == MV_NONE ?
8758 el_act2img(effective_element, effective_action) :
8759 el_act_dir2img(effective_element, effective_action,
8761 int crumbled = (direction == MV_NONE ?
8762 el_act2crm(effective_element, effective_action) :
8763 el_act_dir2crm(effective_element, effective_action,
8765 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8766 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8767 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8768 struct GraphicInfo *g = &graphic_info[graphic];
8771 // special case: graphic uses "2nd movement tile" and has defined
8772 // 7 frames for movement animation (or less) => use default graphic
8773 // for last (8th) frame which ends the movement animation
8774 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8776 effective_action = ACTION_DEFAULT;
8777 graphic = (direction == MV_NONE ?
8778 el_act2img(effective_element, effective_action) :
8779 el_act_dir2img(effective_element, effective_action,
8781 crumbled = (direction == MV_NONE ?
8782 el_act2crm(effective_element, effective_action) :
8783 el_act_dir2crm(effective_element, effective_action,
8786 g = &graphic_info[graphic];
8789 if (graphic_info[graphic].anim_global_sync)
8790 sync_frame = FrameCounter;
8791 else if (graphic_info[graphic].anim_global_anim_sync)
8792 sync_frame = getGlobalAnimSyncFrame();
8793 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8794 sync_frame = GfxFrame[x][y];
8796 sync_frame = 0; // playfield border (pseudo steel)
8798 SetRandomAnimationValue(x, y);
8800 int frame = getAnimationFrame(g->anim_frames,
8803 g->anim_start_frame,
8806 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8807 g->double_movement && is_backside);
8809 // (updating the "crumbled" graphic definitions is probably not really needed,
8810 // as animations for crumbled graphics can't be longer than one EMC cycle)
8811 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8815 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8816 int player_nr, int anim, int frame_em)
8818 int element = player_mapping[player_nr][anim].element_rnd;
8819 int action = player_mapping[player_nr][anim].action;
8820 int direction = player_mapping[player_nr][anim].direction;
8821 int graphic = (direction == MV_NONE ?
8822 el_act2img(element, action) :
8823 el_act_dir2img(element, action, direction));
8824 struct GraphicInfo *g = &graphic_info[graphic];
8827 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8829 stored_player[player_nr].StepFrame = frame_em;
8831 sync_frame = stored_player[player_nr].Frame;
8833 int frame = getAnimationFrame(g->anim_frames,
8836 g->anim_start_frame,
8839 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8840 &g_em->src_x, &g_em->src_y, FALSE);
8843 void InitGraphicInfo_EM(void)
8847 // always start with reliable default values
8848 for (i = 0; i < GAME_TILE_MAX; i++)
8850 object_mapping[i].element_rnd = EL_UNKNOWN;
8851 object_mapping[i].is_backside = FALSE;
8852 object_mapping[i].action = ACTION_DEFAULT;
8853 object_mapping[i].direction = MV_NONE;
8856 // always start with reliable default values
8857 for (p = 0; p < MAX_PLAYERS; p++)
8859 for (i = 0; i < PLY_MAX; i++)
8861 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8862 player_mapping[p][i].action = ACTION_DEFAULT;
8863 player_mapping[p][i].direction = MV_NONE;
8867 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8869 int e = em_object_mapping_list[i].element_em;
8871 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8872 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8874 if (em_object_mapping_list[i].action != -1)
8875 object_mapping[e].action = em_object_mapping_list[i].action;
8877 if (em_object_mapping_list[i].direction != -1)
8878 object_mapping[e].direction =
8879 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8882 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8884 int a = em_player_mapping_list[i].action_em;
8885 int p = em_player_mapping_list[i].player_nr;
8887 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8889 if (em_player_mapping_list[i].action != -1)
8890 player_mapping[p][a].action = em_player_mapping_list[i].action;
8892 if (em_player_mapping_list[i].direction != -1)
8893 player_mapping[p][a].direction =
8894 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8897 for (i = 0; i < GAME_TILE_MAX; i++)
8899 int element = object_mapping[i].element_rnd;
8900 int action = object_mapping[i].action;
8901 int direction = object_mapping[i].direction;
8902 boolean is_backside = object_mapping[i].is_backside;
8903 boolean action_exploding = ((action == ACTION_EXPLODING ||
8904 action == ACTION_SMASHED_BY_ROCK ||
8905 action == ACTION_SMASHED_BY_SPRING) &&
8906 element != EL_DIAMOND);
8907 boolean action_active = (action == ACTION_ACTIVE);
8908 boolean action_other = (action == ACTION_OTHER);
8910 for (j = 0; j < 8; j++)
8912 int effective_element = get_effective_element_EM(i, j);
8913 int effective_action = (j < 7 ? action :
8914 i == Xdrip_stretch ? action :
8915 i == Xdrip_stretchB ? action :
8916 i == Ydrip_1_s ? action :
8917 i == Ydrip_1_sB ? action :
8918 i == Yball_1 ? action :
8919 i == Xball_2 ? action :
8920 i == Yball_2 ? action :
8921 i == Yball_blank ? action :
8922 i == Ykey_1_blank ? action :
8923 i == Ykey_2_blank ? action :
8924 i == Ykey_3_blank ? action :
8925 i == Ykey_4_blank ? action :
8926 i == Ykey_5_blank ? action :
8927 i == Ykey_6_blank ? action :
8928 i == Ykey_7_blank ? action :
8929 i == Ykey_8_blank ? action :
8930 i == Ylenses_blank ? action :
8931 i == Ymagnify_blank ? action :
8932 i == Ygrass_blank ? action :
8933 i == Ydirt_blank ? action :
8934 i == Xsand_stonein_1 ? action :
8935 i == Xsand_stonein_2 ? action :
8936 i == Xsand_stonein_3 ? action :
8937 i == Xsand_stonein_4 ? action :
8938 i == Xsand_stoneout_1 ? action :
8939 i == Xsand_stoneout_2 ? action :
8940 i == Xboom_android ? ACTION_EXPLODING :
8941 action_exploding ? ACTION_EXPLODING :
8942 action_active ? action :
8943 action_other ? action :
8945 int graphic = (el_act_dir2img(effective_element, effective_action,
8947 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8949 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8950 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8951 boolean has_action_graphics = (graphic != base_graphic);
8952 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8953 struct GraphicInfo *g = &graphic_info[graphic];
8954 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8957 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8958 boolean special_animation = (action != ACTION_DEFAULT &&
8959 g->anim_frames == 3 &&
8960 g->anim_delay == 2 &&
8961 g->anim_mode & ANIM_LINEAR);
8962 int sync_frame = (i == Xdrip_stretch ? 7 :
8963 i == Xdrip_stretchB ? 7 :
8964 i == Ydrip_2_s ? j + 8 :
8965 i == Ydrip_2_sB ? j + 8 :
8974 i == Xfake_acid_1 ? 0 :
8975 i == Xfake_acid_2 ? 10 :
8976 i == Xfake_acid_3 ? 20 :
8977 i == Xfake_acid_4 ? 30 :
8978 i == Xfake_acid_5 ? 40 :
8979 i == Xfake_acid_6 ? 50 :
8980 i == Xfake_acid_7 ? 60 :
8981 i == Xfake_acid_8 ? 70 :
8982 i == Xfake_acid_1_player ? 0 :
8983 i == Xfake_acid_2_player ? 10 :
8984 i == Xfake_acid_3_player ? 20 :
8985 i == Xfake_acid_4_player ? 30 :
8986 i == Xfake_acid_5_player ? 40 :
8987 i == Xfake_acid_6_player ? 50 :
8988 i == Xfake_acid_7_player ? 60 :
8989 i == Xfake_acid_8_player ? 70 :
8991 i == Yball_2 ? j + 8 :
8992 i == Yball_blank ? j + 1 :
8993 i == Ykey_1_blank ? j + 1 :
8994 i == Ykey_2_blank ? j + 1 :
8995 i == Ykey_3_blank ? j + 1 :
8996 i == Ykey_4_blank ? j + 1 :
8997 i == Ykey_5_blank ? j + 1 :
8998 i == Ykey_6_blank ? j + 1 :
8999 i == Ykey_7_blank ? j + 1 :
9000 i == Ykey_8_blank ? j + 1 :
9001 i == Ylenses_blank ? j + 1 :
9002 i == Ymagnify_blank ? j + 1 :
9003 i == Ygrass_blank ? j + 1 :
9004 i == Ydirt_blank ? j + 1 :
9005 i == Xamoeba_1 ? 0 :
9006 i == Xamoeba_2 ? 1 :
9007 i == Xamoeba_3 ? 2 :
9008 i == Xamoeba_4 ? 3 :
9009 i == Xamoeba_5 ? 0 :
9010 i == Xamoeba_6 ? 1 :
9011 i == Xamoeba_7 ? 2 :
9012 i == Xamoeba_8 ? 3 :
9013 i == Xexit_2 ? j + 8 :
9014 i == Xexit_3 ? j + 16 :
9015 i == Xdynamite_1 ? 0 :
9016 i == Xdynamite_2 ? 8 :
9017 i == Xdynamite_3 ? 16 :
9018 i == Xdynamite_4 ? 24 :
9019 i == Xsand_stonein_1 ? j + 1 :
9020 i == Xsand_stonein_2 ? j + 9 :
9021 i == Xsand_stonein_3 ? j + 17 :
9022 i == Xsand_stonein_4 ? j + 25 :
9023 i == Xsand_stoneout_1 && j == 0 ? 0 :
9024 i == Xsand_stoneout_1 && j == 1 ? 0 :
9025 i == Xsand_stoneout_1 && j == 2 ? 1 :
9026 i == Xsand_stoneout_1 && j == 3 ? 2 :
9027 i == Xsand_stoneout_1 && j == 4 ? 2 :
9028 i == Xsand_stoneout_1 && j == 5 ? 3 :
9029 i == Xsand_stoneout_1 && j == 6 ? 4 :
9030 i == Xsand_stoneout_1 && j == 7 ? 4 :
9031 i == Xsand_stoneout_2 && j == 0 ? 5 :
9032 i == Xsand_stoneout_2 && j == 1 ? 6 :
9033 i == Xsand_stoneout_2 && j == 2 ? 7 :
9034 i == Xsand_stoneout_2 && j == 3 ? 8 :
9035 i == Xsand_stoneout_2 && j == 4 ? 9 :
9036 i == Xsand_stoneout_2 && j == 5 ? 11 :
9037 i == Xsand_stoneout_2 && j == 6 ? 13 :
9038 i == Xsand_stoneout_2 && j == 7 ? 15 :
9039 i == Xboom_bug && j == 1 ? 2 :
9040 i == Xboom_bug && j == 2 ? 2 :
9041 i == Xboom_bug && j == 3 ? 4 :
9042 i == Xboom_bug && j == 4 ? 4 :
9043 i == Xboom_bug && j == 5 ? 2 :
9044 i == Xboom_bug && j == 6 ? 2 :
9045 i == Xboom_bug && j == 7 ? 0 :
9046 i == Xboom_tank && j == 1 ? 2 :
9047 i == Xboom_tank && j == 2 ? 2 :
9048 i == Xboom_tank && j == 3 ? 4 :
9049 i == Xboom_tank && j == 4 ? 4 :
9050 i == Xboom_tank && j == 5 ? 2 :
9051 i == Xboom_tank && j == 6 ? 2 :
9052 i == Xboom_tank && j == 7 ? 0 :
9053 i == Xboom_android && j == 7 ? 6 :
9054 i == Xboom_1 && j == 1 ? 2 :
9055 i == Xboom_1 && j == 2 ? 2 :
9056 i == Xboom_1 && j == 3 ? 4 :
9057 i == Xboom_1 && j == 4 ? 4 :
9058 i == Xboom_1 && j == 5 ? 6 :
9059 i == Xboom_1 && j == 6 ? 6 :
9060 i == Xboom_1 && j == 7 ? 8 :
9061 i == Xboom_2 && j == 0 ? 8 :
9062 i == Xboom_2 && j == 1 ? 8 :
9063 i == Xboom_2 && j == 2 ? 10 :
9064 i == Xboom_2 && j == 3 ? 10 :
9065 i == Xboom_2 && j == 4 ? 10 :
9066 i == Xboom_2 && j == 5 ? 12 :
9067 i == Xboom_2 && j == 6 ? 12 :
9068 i == Xboom_2 && j == 7 ? 12 :
9069 special_animation && j == 4 ? 3 :
9070 effective_action != action ? 0 :
9072 int frame = getAnimationFrame(g->anim_frames,
9075 g->anim_start_frame,
9078 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9079 g->double_movement && is_backside);
9081 g_em->bitmap = src_bitmap;
9082 g_em->src_x = src_x;
9083 g_em->src_y = src_y;
9084 g_em->src_offset_x = 0;
9085 g_em->src_offset_y = 0;
9086 g_em->dst_offset_x = 0;
9087 g_em->dst_offset_y = 0;
9088 g_em->width = TILEX;
9089 g_em->height = TILEY;
9091 g_em->preserve_background = FALSE;
9093 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9096 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9097 effective_action == ACTION_MOVING ||
9098 effective_action == ACTION_PUSHING ||
9099 effective_action == ACTION_EATING)) ||
9100 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9101 effective_action == ACTION_EMPTYING)))
9104 (effective_action == ACTION_FALLING ||
9105 effective_action == ACTION_FILLING ||
9106 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9107 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9108 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9109 int num_steps = (i == Ydrip_1_s ? 16 :
9110 i == Ydrip_1_sB ? 16 :
9111 i == Ydrip_2_s ? 16 :
9112 i == Ydrip_2_sB ? 16 :
9113 i == Xsand_stonein_1 ? 32 :
9114 i == Xsand_stonein_2 ? 32 :
9115 i == Xsand_stonein_3 ? 32 :
9116 i == Xsand_stonein_4 ? 32 :
9117 i == Xsand_stoneout_1 ? 16 :
9118 i == Xsand_stoneout_2 ? 16 : 8);
9119 int cx = ABS(dx) * (TILEX / num_steps);
9120 int cy = ABS(dy) * (TILEY / num_steps);
9121 int step_frame = (i == Ydrip_2_s ? j + 8 :
9122 i == Ydrip_2_sB ? j + 8 :
9123 i == Xsand_stonein_2 ? j + 8 :
9124 i == Xsand_stonein_3 ? j + 16 :
9125 i == Xsand_stonein_4 ? j + 24 :
9126 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9127 int step = (is_backside ? step_frame : num_steps - step_frame);
9129 if (is_backside) // tile where movement starts
9131 if (dx < 0 || dy < 0)
9133 g_em->src_offset_x = cx * step;
9134 g_em->src_offset_y = cy * step;
9138 g_em->dst_offset_x = cx * step;
9139 g_em->dst_offset_y = cy * step;
9142 else // tile where movement ends
9144 if (dx < 0 || dy < 0)
9146 g_em->dst_offset_x = cx * step;
9147 g_em->dst_offset_y = cy * step;
9151 g_em->src_offset_x = cx * step;
9152 g_em->src_offset_y = cy * step;
9156 g_em->width = TILEX - cx * step;
9157 g_em->height = TILEY - cy * step;
9160 // create unique graphic identifier to decide if tile must be redrawn
9161 /* bit 31 - 16 (16 bit): EM style graphic
9162 bit 15 - 12 ( 4 bit): EM style frame
9163 bit 11 - 6 ( 6 bit): graphic width
9164 bit 5 - 0 ( 6 bit): graphic height */
9165 g_em->unique_identifier =
9166 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9170 for (i = 0; i < GAME_TILE_MAX; i++)
9172 for (j = 0; j < 8; j++)
9174 int element = object_mapping[i].element_rnd;
9175 int action = object_mapping[i].action;
9176 int direction = object_mapping[i].direction;
9177 boolean is_backside = object_mapping[i].is_backside;
9178 int graphic_action = el_act_dir2img(element, action, direction);
9179 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9181 if ((action == ACTION_SMASHED_BY_ROCK ||
9182 action == ACTION_SMASHED_BY_SPRING ||
9183 action == ACTION_EATING) &&
9184 graphic_action == graphic_default)
9186 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9187 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9188 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9189 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9192 // no separate animation for "smashed by rock" -- use rock instead
9193 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9194 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9196 g_em->bitmap = g_xx->bitmap;
9197 g_em->src_x = g_xx->src_x;
9198 g_em->src_y = g_xx->src_y;
9199 g_em->src_offset_x = g_xx->src_offset_x;
9200 g_em->src_offset_y = g_xx->src_offset_y;
9201 g_em->dst_offset_x = g_xx->dst_offset_x;
9202 g_em->dst_offset_y = g_xx->dst_offset_y;
9203 g_em->width = g_xx->width;
9204 g_em->height = g_xx->height;
9205 g_em->unique_identifier = g_xx->unique_identifier;
9208 g_em->preserve_background = TRUE;
9213 for (p = 0; p < MAX_PLAYERS; p++)
9215 for (i = 0; i < PLY_MAX; i++)
9217 int element = player_mapping[p][i].element_rnd;
9218 int action = player_mapping[p][i].action;
9219 int direction = player_mapping[p][i].direction;
9221 for (j = 0; j < 8; j++)
9223 int effective_element = element;
9224 int effective_action = action;
9225 int graphic = (direction == MV_NONE ?
9226 el_act2img(effective_element, effective_action) :
9227 el_act_dir2img(effective_element, effective_action,
9229 struct GraphicInfo *g = &graphic_info[graphic];
9230 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9234 int frame = getAnimationFrame(g->anim_frames,
9237 g->anim_start_frame,
9240 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9242 g_em->bitmap = src_bitmap;
9243 g_em->src_x = src_x;
9244 g_em->src_y = src_y;
9245 g_em->src_offset_x = 0;
9246 g_em->src_offset_y = 0;
9247 g_em->dst_offset_x = 0;
9248 g_em->dst_offset_y = 0;
9249 g_em->width = TILEX;
9250 g_em->height = TILEY;
9256 static void CheckSaveEngineSnapshot_EM(int frame,
9257 boolean any_player_moving,
9258 boolean any_player_snapping,
9259 boolean any_player_dropping)
9261 if (frame == 7 && !any_player_dropping)
9263 if (!local_player->was_waiting)
9265 if (!CheckSaveEngineSnapshotToList())
9268 local_player->was_waiting = TRUE;
9271 else if (any_player_moving || any_player_snapping || any_player_dropping)
9273 local_player->was_waiting = FALSE;
9277 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9278 boolean murphy_is_dropping)
9280 if (murphy_is_waiting)
9282 if (!local_player->was_waiting)
9284 if (!CheckSaveEngineSnapshotToList())
9287 local_player->was_waiting = TRUE;
9292 local_player->was_waiting = FALSE;
9296 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9297 boolean button_released)
9299 if (button_released)
9301 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9302 CheckSaveEngineSnapshotToList();
9304 else if (element_clicked)
9306 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9307 CheckSaveEngineSnapshotToList();
9309 game.snapshot.changed_action = TRUE;
9313 boolean CheckSingleStepMode_EM(int frame,
9314 boolean any_player_moving,
9315 boolean any_player_snapping,
9316 boolean any_player_dropping)
9318 if (tape.single_step && tape.recording && !tape.pausing)
9319 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9320 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9322 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9323 any_player_snapping, any_player_dropping);
9325 return tape.pausing;
9328 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9329 boolean murphy_is_dropping)
9331 boolean murphy_starts_dropping = FALSE;
9334 for (i = 0; i < MAX_PLAYERS; i++)
9335 if (stored_player[i].force_dropping)
9336 murphy_starts_dropping = TRUE;
9338 if (tape.single_step && tape.recording && !tape.pausing)
9339 if (murphy_is_waiting && !murphy_starts_dropping)
9340 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9342 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9345 void CheckSingleStepMode_MM(boolean element_clicked,
9346 boolean button_released)
9348 if (tape.single_step && tape.recording && !tape.pausing)
9349 if (button_released)
9350 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9352 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9355 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9356 int graphic, int sync_frame)
9358 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9360 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9363 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9365 return (IS_NEXT_FRAME(sync_frame, graphic));
9368 int getGraphicInfo_Delay(int graphic)
9370 return graphic_info[graphic].anim_delay;
9373 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9375 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9378 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9384 void PlayMenuSoundExt(int sound)
9386 if (sound == SND_UNDEFINED)
9389 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9390 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9393 if (IS_LOOP_SOUND(sound))
9394 PlaySoundLoop(sound);
9399 void PlayMenuSound(void)
9401 PlayMenuSoundExt(menu.sound[game_status]);
9404 void PlayMenuSoundStereo(int sound, int stereo_position)
9406 if (sound == SND_UNDEFINED)
9409 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9410 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9413 if (IS_LOOP_SOUND(sound))
9414 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9416 PlaySoundStereo(sound, stereo_position);
9419 void PlayMenuSoundIfLoopExt(int sound)
9421 if (sound == SND_UNDEFINED)
9424 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9425 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9428 if (IS_LOOP_SOUND(sound))
9429 PlaySoundLoop(sound);
9432 void PlayMenuSoundIfLoop(void)
9434 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9437 void PlayMenuMusicExt(int music)
9439 if (music == MUS_UNDEFINED)
9442 if (!setup.sound_music)
9445 if (IS_LOOP_MUSIC(music))
9446 PlayMusicLoop(music);
9451 void PlayMenuMusic(void)
9453 char *curr_music = getCurrentlyPlayingMusicFilename();
9454 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9456 if (!strEqual(curr_music, next_music))
9457 PlayMenuMusicExt(menu.music[game_status]);
9460 void PlayMenuSoundsAndMusic(void)
9466 static void FadeMenuSounds(void)
9471 static void FadeMenuMusic(void)
9473 char *curr_music = getCurrentlyPlayingMusicFilename();
9474 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9476 if (!strEqual(curr_music, next_music))
9480 void FadeMenuSoundsAndMusic(void)
9486 void PlaySoundActivating(void)
9489 PlaySound(SND_MENU_ITEM_ACTIVATING);
9493 void PlaySoundSelecting(void)
9496 PlaySound(SND_MENU_ITEM_SELECTING);
9500 void ToggleFullscreenIfNeeded(void)
9502 // if setup and video fullscreen state are already matching, nothing do do
9503 if (setup.fullscreen == video.fullscreen_enabled ||
9504 !video.fullscreen_available)
9507 SDLSetWindowFullscreen(setup.fullscreen);
9509 // set setup value according to successfully changed fullscreen mode
9510 setup.fullscreen = video.fullscreen_enabled;
9513 void ChangeWindowScalingIfNeeded(void)
9515 // if setup and video window scaling are already matching, nothing do do
9516 if (setup.window_scaling_percent == video.window_scaling_percent ||
9517 video.fullscreen_enabled)
9520 SDLSetWindowScaling(setup.window_scaling_percent);
9522 // set setup value according to successfully changed window scaling
9523 setup.window_scaling_percent = video.window_scaling_percent;
9526 void ChangeVsyncModeIfNeeded(void)
9528 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9529 int video_vsync_mode = video.vsync_mode;
9531 // if setup and video vsync mode are already matching, nothing do do
9532 if (setup_vsync_mode == video_vsync_mode)
9535 // if renderer is using OpenGL, vsync mode can directly be changed
9536 SDLSetScreenVsyncMode(setup.vsync_mode);
9538 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9539 if (video.vsync_mode == video_vsync_mode)
9541 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9543 // save backbuffer content which gets lost when re-creating screen
9544 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9546 // force re-creating screen and renderer to set new vsync mode
9547 video.fullscreen_enabled = !setup.fullscreen;
9549 // when creating new renderer, destroy textures linked to old renderer
9550 FreeAllImageTextures(); // needs old renderer to free the textures
9552 // re-create screen and renderer (including change of vsync mode)
9553 ChangeVideoModeIfNeeded(setup.fullscreen);
9555 // set setup value according to successfully changed fullscreen mode
9556 setup.fullscreen = video.fullscreen_enabled;
9558 // restore backbuffer content from temporary backbuffer backup bitmap
9559 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9560 FreeBitmap(tmp_backbuffer);
9562 // update visible window/screen
9563 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9565 // when changing vsync mode, re-create textures for new renderer
9566 InitImageTextures();
9569 // set setup value according to successfully changed vsync mode
9570 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9573 static void JoinRectangles(int *x, int *y, int *width, int *height,
9574 int x2, int y2, int width2, int height2)
9576 // do not join with "off-screen" rectangle
9577 if (x2 == -1 || y2 == -1)
9582 *width = MAX(*width, width2);
9583 *height = MAX(*height, height2);
9586 void SetAnimStatus(int anim_status_new)
9588 if (anim_status_new == GAME_MODE_MAIN)
9589 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9590 else if (anim_status_new == GAME_MODE_NAMES)
9591 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9592 else if (anim_status_new == GAME_MODE_SCORES)
9593 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9595 global.anim_status_next = anim_status_new;
9597 // directly set screen modes that are entered without fading
9598 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9599 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9600 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9601 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9602 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9603 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9604 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9605 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9606 global.anim_status = global.anim_status_next;
9609 void SetGameStatus(int game_status_new)
9611 if (game_status_new != game_status)
9612 game_status_last_screen = game_status;
9614 game_status = game_status_new;
9616 SetAnimStatus(game_status_new);
9619 void SetFontStatus(int game_status_new)
9621 static int last_game_status = -1;
9623 if (game_status_new != -1)
9625 // set game status for font use after storing last game status
9626 last_game_status = game_status;
9627 game_status = game_status_new;
9631 // reset game status after font use from last stored game status
9632 game_status = last_game_status;
9636 void ResetFontStatus(void)
9641 void SetLevelSetInfo(char *identifier, int level_nr)
9643 setString(&levelset.identifier, identifier);
9645 levelset.level_nr = level_nr;
9648 boolean CheckIfAllViewportsHaveChanged(void)
9650 // if game status has not changed, viewports have not changed either
9651 if (game_status == game_status_last)
9654 // check if all viewports have changed with current game status
9656 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9657 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9658 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9659 int new_real_sx = vp_playfield->x;
9660 int new_real_sy = vp_playfield->y;
9661 int new_full_sxsize = vp_playfield->width;
9662 int new_full_sysize = vp_playfield->height;
9663 int new_dx = vp_door_1->x;
9664 int new_dy = vp_door_1->y;
9665 int new_dxsize = vp_door_1->width;
9666 int new_dysize = vp_door_1->height;
9667 int new_vx = vp_door_2->x;
9668 int new_vy = vp_door_2->y;
9669 int new_vxsize = vp_door_2->width;
9670 int new_vysize = vp_door_2->height;
9672 boolean playfield_viewport_has_changed =
9673 (new_real_sx != REAL_SX ||
9674 new_real_sy != REAL_SY ||
9675 new_full_sxsize != FULL_SXSIZE ||
9676 new_full_sysize != FULL_SYSIZE);
9678 boolean door_1_viewport_has_changed =
9681 new_dxsize != DXSIZE ||
9682 new_dysize != DYSIZE);
9684 boolean door_2_viewport_has_changed =
9687 new_vxsize != VXSIZE ||
9688 new_vysize != VYSIZE ||
9689 game_status_last == GAME_MODE_EDITOR);
9691 return (playfield_viewport_has_changed &&
9692 door_1_viewport_has_changed &&
9693 door_2_viewport_has_changed);
9696 boolean CheckFadeAll(void)
9698 return (CheckIfGlobalBorderHasChanged() ||
9699 CheckIfAllViewportsHaveChanged());
9702 void ChangeViewportPropertiesIfNeeded(void)
9704 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9705 FALSE : setup.small_game_graphics);
9706 int gfx_game_mode = getGlobalGameStatus(game_status);
9707 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9709 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9710 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9711 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9712 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9713 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9714 int new_win_xsize = vp_window->width;
9715 int new_win_ysize = vp_window->height;
9716 int border_left = vp_playfield->border_left;
9717 int border_right = vp_playfield->border_right;
9718 int border_top = vp_playfield->border_top;
9719 int border_bottom = vp_playfield->border_bottom;
9720 int new_sx = vp_playfield->x + border_left;
9721 int new_sy = vp_playfield->y + border_top;
9722 int new_sxsize = vp_playfield->width - border_left - border_right;
9723 int new_sysize = vp_playfield->height - border_top - border_bottom;
9724 int new_real_sx = vp_playfield->x;
9725 int new_real_sy = vp_playfield->y;
9726 int new_full_sxsize = vp_playfield->width;
9727 int new_full_sysize = vp_playfield->height;
9728 int new_dx = vp_door_1->x;
9729 int new_dy = vp_door_1->y;
9730 int new_dxsize = vp_door_1->width;
9731 int new_dysize = vp_door_1->height;
9732 int new_vx = vp_door_2->x;
9733 int new_vy = vp_door_2->y;
9734 int new_vxsize = vp_door_2->width;
9735 int new_vysize = vp_door_2->height;
9736 int new_ex = vp_door_3->x;
9737 int new_ey = vp_door_3->y;
9738 int new_exsize = vp_door_3->width;
9739 int new_eysize = vp_door_3->height;
9740 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9741 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9742 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9743 int new_scr_fieldx = new_sxsize / tilesize;
9744 int new_scr_fieldy = new_sysize / tilesize;
9745 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9746 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9747 boolean init_gfx_buffers = FALSE;
9748 boolean init_video_buffer = FALSE;
9749 boolean init_gadgets_and_anims = FALSE;
9750 boolean init_em_graphics = FALSE;
9752 if (new_win_xsize != WIN_XSIZE ||
9753 new_win_ysize != WIN_YSIZE)
9755 WIN_XSIZE = new_win_xsize;
9756 WIN_YSIZE = new_win_ysize;
9758 init_video_buffer = TRUE;
9759 init_gfx_buffers = TRUE;
9760 init_gadgets_and_anims = TRUE;
9762 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9765 if (new_scr_fieldx != SCR_FIELDX ||
9766 new_scr_fieldy != SCR_FIELDY)
9768 // this always toggles between MAIN and GAME when using small tile size
9770 SCR_FIELDX = new_scr_fieldx;
9771 SCR_FIELDY = new_scr_fieldy;
9773 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9784 new_sxsize != SXSIZE ||
9785 new_sysize != SYSIZE ||
9786 new_dxsize != DXSIZE ||
9787 new_dysize != DYSIZE ||
9788 new_vxsize != VXSIZE ||
9789 new_vysize != VYSIZE ||
9790 new_exsize != EXSIZE ||
9791 new_eysize != EYSIZE ||
9792 new_real_sx != REAL_SX ||
9793 new_real_sy != REAL_SY ||
9794 new_full_sxsize != FULL_SXSIZE ||
9795 new_full_sysize != FULL_SYSIZE ||
9796 new_tilesize_var != TILESIZE_VAR
9799 // ------------------------------------------------------------------------
9800 // determine next fading area for changed viewport definitions
9801 // ------------------------------------------------------------------------
9803 // start with current playfield area (default fading area)
9806 FADE_SXSIZE = FULL_SXSIZE;
9807 FADE_SYSIZE = FULL_SYSIZE;
9809 // add new playfield area if position or size has changed
9810 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9811 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9813 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9814 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9817 // add current and new door 1 area if position or size has changed
9818 if (new_dx != DX || new_dy != DY ||
9819 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9821 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9822 DX, DY, DXSIZE, DYSIZE);
9823 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9824 new_dx, new_dy, new_dxsize, new_dysize);
9827 // add current and new door 2 area if position or size has changed
9828 if (new_vx != VX || new_vy != VY ||
9829 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9831 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9832 VX, VY, VXSIZE, VYSIZE);
9833 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9834 new_vx, new_vy, new_vxsize, new_vysize);
9837 // ------------------------------------------------------------------------
9838 // handle changed tile size
9839 // ------------------------------------------------------------------------
9841 if (new_tilesize_var != TILESIZE_VAR)
9843 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9845 // changing tile size invalidates scroll values of engine snapshots
9846 FreeEngineSnapshotSingle();
9848 // changing tile size requires update of graphic mapping for EM engine
9849 init_em_graphics = TRUE;
9860 SXSIZE = new_sxsize;
9861 SYSIZE = new_sysize;
9862 DXSIZE = new_dxsize;
9863 DYSIZE = new_dysize;
9864 VXSIZE = new_vxsize;
9865 VYSIZE = new_vysize;
9866 EXSIZE = new_exsize;
9867 EYSIZE = new_eysize;
9868 REAL_SX = new_real_sx;
9869 REAL_SY = new_real_sy;
9870 FULL_SXSIZE = new_full_sxsize;
9871 FULL_SYSIZE = new_full_sysize;
9872 TILESIZE_VAR = new_tilesize_var;
9874 init_gfx_buffers = TRUE;
9875 init_gadgets_and_anims = TRUE;
9877 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9878 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9881 if (init_gfx_buffers)
9883 // Debug("tools:viewport", "init_gfx_buffers");
9885 SCR_FIELDX = new_scr_fieldx_buffers;
9886 SCR_FIELDY = new_scr_fieldy_buffers;
9890 SCR_FIELDX = new_scr_fieldx;
9891 SCR_FIELDY = new_scr_fieldy;
9893 SetDrawDeactivationMask(REDRAW_NONE);
9894 SetDrawBackgroundMask(REDRAW_FIELD);
9897 if (init_video_buffer)
9899 // Debug("tools:viewport", "init_video_buffer");
9901 FreeAllImageTextures(); // needs old renderer to free the textures
9903 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9904 InitImageTextures();
9907 if (init_gadgets_and_anims)
9909 // Debug("tools:viewport", "init_gadgets_and_anims");
9912 InitGlobalAnimations();
9915 if (init_em_graphics)
9917 InitGraphicInfo_EM();
9921 void OpenURL(char *url)
9923 #if SDL_VERSION_ATLEAST(2,0,14)
9926 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9927 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9928 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9932 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9934 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9938 // ============================================================================
9940 // ============================================================================
9942 #if defined(PLATFORM_WINDOWS)
9943 /* FILETIME of Jan 1 1970 00:00:00. */
9944 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9947 * timezone information is stored outside the kernel so tzp isn't used anymore.
9949 * Note: this function is not for Win32 high precision timing purpose. See
9952 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9955 SYSTEMTIME system_time;
9956 ULARGE_INTEGER ularge;
9958 GetSystemTime(&system_time);
9959 SystemTimeToFileTime(&system_time, &file_time);
9960 ularge.LowPart = file_time.dwLowDateTime;
9961 ularge.HighPart = file_time.dwHighDateTime;
9963 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9964 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9970 static char *test_init_uuid_random_function_simple(void)
9972 static char seed_text[100];
9973 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9975 sprintf(seed_text, "%d", seed);
9980 static char *test_init_uuid_random_function_better(void)
9982 static char seed_text[100];
9983 struct timeval current_time;
9985 gettimeofday(¤t_time, NULL);
9987 prng_seed_bytes(¤t_time, sizeof(current_time));
9989 sprintf(seed_text, "%ld.%ld",
9990 (long)current_time.tv_sec,
9991 (long)current_time.tv_usec);
9996 #if defined(PLATFORM_WINDOWS)
9997 static char *test_init_uuid_random_function_better_windows(void)
9999 static char seed_text[100];
10000 struct timeval current_time;
10002 gettimeofday_windows(¤t_time, NULL);
10004 prng_seed_bytes(¤t_time, sizeof(current_time));
10006 sprintf(seed_text, "%ld.%ld",
10007 (long)current_time.tv_sec,
10008 (long)current_time.tv_usec);
10014 static unsigned int test_uuid_random_function_simple(int max)
10016 return GetSimpleRandom(max);
10019 static unsigned int test_uuid_random_function_better(int max)
10021 return (max > 0 ? prng_get_uint() % max : 0);
10024 #if defined(PLATFORM_WINDOWS)
10025 #define NUM_UUID_TESTS 3
10027 #define NUM_UUID_TESTS 2
10030 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10032 struct hashtable *hash_seeds =
10033 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10034 struct hashtable *hash_uuids =
10035 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10036 static char message[100];
10039 char *random_name = (nr == 0 ? "simple" : "better");
10040 char *random_type = (always_seed ? "always" : "only once");
10041 char *(*init_random_function)(void) =
10043 test_init_uuid_random_function_simple :
10044 test_init_uuid_random_function_better);
10045 unsigned int (*random_function)(int) =
10047 test_uuid_random_function_simple :
10048 test_uuid_random_function_better);
10051 #if defined(PLATFORM_WINDOWS)
10054 random_name = "windows";
10055 init_random_function = test_init_uuid_random_function_better_windows;
10061 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10062 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10064 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10065 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10066 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10068 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10072 // always initialize random number generator at least once
10073 init_random_function();
10075 unsigned int time_start = SDL_GetTicks();
10077 for (i = 0; i < num_uuids; i++)
10081 char *seed = getStringCopy(init_random_function());
10083 hashtable_remove(hash_seeds, seed);
10084 hashtable_insert(hash_seeds, seed, "1");
10087 char *uuid = getStringCopy(getUUIDExt(random_function));
10089 hashtable_remove(hash_uuids, uuid);
10090 hashtable_insert(hash_uuids, uuid, "1");
10093 int num_unique_seeds = hashtable_count(hash_seeds);
10094 int num_unique_uuids = hashtable_count(hash_uuids);
10096 unsigned int time_needed = SDL_GetTicks() - time_start;
10098 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10100 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10103 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10105 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10106 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10108 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10110 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10112 Request(message, REQ_CONFIRM);
10114 hashtable_destroy(hash_seeds, 0);
10115 hashtable_destroy(hash_uuids, 0);
10118 void TestGeneratingUUIDs(void)
10120 int num_uuids = 1000000;
10123 for (i = 0; i < NUM_UUID_TESTS; i++)
10124 for (j = 0; j < 2; j++)
10125 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10127 CloseAllAndExit(0);