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 DrawEnvelopeRequestText(int sx, int sy, char *text)
3150 char *text_final = text;
3151 char *text_door_style = NULL;
3152 int graphic = IMG_BACKGROUND_REQUEST;
3153 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3154 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3155 int font_nr = FONT_REQUEST;
3156 int font_width = getFontWidth(font_nr);
3157 int font_height = getFontHeight(font_nr);
3158 int border_size = request.border_size;
3159 int line_spacing = request.line_spacing;
3160 int line_height = font_height + line_spacing;
3161 int max_text_width = request.width - 2 * border_size;
3162 int max_text_height = request.height - 2 * border_size;
3163 int line_length = max_text_width / font_width;
3164 int max_lines = max_text_height / line_height;
3165 int text_width = line_length * font_width;
3166 int sx_offset = border_size;
3167 int sy_offset = border_size;
3169 // force DOOR font inside door area
3170 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3172 if (request.centered)
3173 sx_offset = (request.width - text_width) / 2;
3175 if (request.wrap_single_words && !request.autowrap)
3177 char *src_text_ptr, *dst_text_ptr;
3179 text_door_style = checked_malloc(2 * strlen(text) + 1);
3181 src_text_ptr = text;
3182 dst_text_ptr = text_door_style;
3184 while (*src_text_ptr)
3186 if (*src_text_ptr == ' ' ||
3187 *src_text_ptr == '?' ||
3188 *src_text_ptr == '!')
3189 *dst_text_ptr++ = '\n';
3191 if (*src_text_ptr != ' ')
3192 *dst_text_ptr++ = *src_text_ptr;
3197 *dst_text_ptr = '\0';
3199 text_final = text_door_style;
3202 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3203 line_length, -1, max_lines, line_spacing, mask_mode,
3204 request.autowrap, request.centered, FALSE);
3206 if (text_door_style)
3207 free(text_door_style);
3212 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3214 DrawBuffer *drawto_last = drawto;
3215 int graphic = IMG_BACKGROUND_REQUEST;
3216 int width = request.width;
3217 int height = request.height;
3218 int tile_size = MAX(request.step_offset, 1);
3219 int x_steps = width / tile_size;
3220 int y_steps = height / tile_size;
3224 setRequestPosition(&sx, &sy, FALSE);
3226 // draw complete envelope request to temporary bitmap
3227 drawto = bitmap_db_store_1;
3229 ClearRectangle(drawto, sx, sy, width, height);
3231 for (y = 0; y < y_steps; y++)
3232 for (x = 0; x < x_steps; x++)
3233 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3234 x, y, x_steps, y_steps,
3235 tile_size, tile_size);
3237 // write text for request
3238 DrawEnvelopeRequestText(sx, sy, text);
3240 MapToolButtons(req_state);
3242 // restore pointer to drawing buffer
3243 drawto = drawto_last;
3245 // prepare complete envelope request from temporary bitmap
3246 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3249 static void AnimateEnvelopeRequest(int anim_mode, int action)
3251 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3252 int delay_value_normal = request.step_delay;
3253 int delay_value_fast = delay_value_normal / 2;
3254 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3255 boolean no_delay = (tape.warp_forward);
3256 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3257 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3258 DelayCounter anim_delay = { anim_delay_value };
3260 int tile_size = MAX(request.step_offset, 1);
3261 int max_xsize = request.width / tile_size;
3262 int max_ysize = request.height / tile_size;
3263 int max_xsize_inner = max_xsize - 2;
3264 int max_ysize_inner = max_ysize - 2;
3266 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3267 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3268 int xend = max_xsize_inner;
3269 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3270 int xstep = (xstart < xend ? 1 : 0);
3271 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3273 int end = MAX(xend - xstart, yend - ystart);
3276 if (setup.quick_doors)
3283 for (i = start; i <= end; i++)
3285 int last_frame = end; // last frame of this "for" loop
3286 int x = xstart + i * xstep;
3287 int y = ystart + i * ystep;
3288 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3289 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3290 int xsize_size_left = (xsize - 1) * tile_size;
3291 int ysize_size_top = (ysize - 1) * tile_size;
3292 int max_xsize_pos = (max_xsize - 1) * tile_size;
3293 int max_ysize_pos = (max_ysize - 1) * tile_size;
3294 int width = xsize * tile_size;
3295 int height = ysize * tile_size;
3301 HandleGameActions();
3303 setRequestPosition(&src_x, &src_y, FALSE);
3304 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3306 for (yy = 0; yy < 2; yy++)
3308 for (xx = 0; xx < 2; xx++)
3310 int src_xx = src_x + xx * max_xsize_pos;
3311 int src_yy = src_y + yy * max_ysize_pos;
3312 int dst_xx = dst_x + xx * xsize_size_left;
3313 int dst_yy = dst_y + yy * ysize_size_top;
3314 int xx_size = (xx ? tile_size : xsize_size_left);
3315 int yy_size = (yy ? tile_size : ysize_size_top);
3317 // draw partial (animated) envelope request to temporary bitmap
3318 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3319 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3323 // prepare partial (animated) envelope request from temporary bitmap
3324 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3327 redraw_mask |= REDRAW_FIELD;
3331 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3334 ClearAutoRepeatKeyEvents();
3337 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3339 int graphic = IMG_BACKGROUND_REQUEST;
3340 int sound_opening = SND_REQUEST_OPENING;
3341 int sound_closing = SND_REQUEST_CLOSING;
3342 int anim_mode_1 = request.anim_mode; // (higher priority)
3343 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3344 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3345 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3346 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3348 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3350 if (action == ACTION_OPENING)
3352 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3354 if (anim_mode == ANIM_DEFAULT)
3355 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3357 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3361 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3363 if (anim_mode != ANIM_NONE)
3364 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3366 if (anim_mode == ANIM_DEFAULT)
3367 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3370 game.envelope_active = FALSE;
3373 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3375 if (IS_MM_WALL(element))
3377 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3383 int graphic = el2preimg(element);
3385 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3386 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3391 void DrawLevel(int draw_background_mask)
3395 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3396 SetDrawBackgroundMask(draw_background_mask);
3400 for (x = BX1; x <= BX2; x++)
3401 for (y = BY1; y <= BY2; y++)
3402 DrawScreenField(x, y);
3404 redraw_mask |= REDRAW_FIELD;
3407 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3412 for (x = 0; x < size_x; x++)
3413 for (y = 0; y < size_y; y++)
3414 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3416 redraw_mask |= REDRAW_FIELD;
3419 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3423 for (x = 0; x < size_x; x++)
3424 for (y = 0; y < size_y; y++)
3425 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3427 redraw_mask |= REDRAW_FIELD;
3430 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3432 boolean show_level_border = (BorderElement != EL_EMPTY);
3433 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3434 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3435 int tile_size = preview.tile_size;
3436 int preview_width = preview.xsize * tile_size;
3437 int preview_height = preview.ysize * tile_size;
3438 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3439 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3440 int real_preview_width = real_preview_xsize * tile_size;
3441 int real_preview_height = real_preview_ysize * tile_size;
3442 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3443 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3446 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3449 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3451 dst_x += (preview_width - real_preview_width) / 2;
3452 dst_y += (preview_height - real_preview_height) / 2;
3454 for (x = 0; x < real_preview_xsize; x++)
3456 for (y = 0; y < real_preview_ysize; y++)
3458 int lx = from_x + x + (show_level_border ? -1 : 0);
3459 int ly = from_y + y + (show_level_border ? -1 : 0);
3460 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3461 getBorderElement(lx, ly));
3463 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3464 element, tile_size);
3468 redraw_mask |= REDRAW_FIELD;
3471 #define MICROLABEL_EMPTY 0
3472 #define MICROLABEL_LEVEL_NAME 1
3473 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3474 #define MICROLABEL_LEVEL_AUTHOR 3
3475 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3476 #define MICROLABEL_IMPORTED_FROM 5
3477 #define MICROLABEL_IMPORTED_BY_HEAD 6
3478 #define MICROLABEL_IMPORTED_BY 7
3480 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3482 int max_text_width = SXSIZE;
3483 int font_width = getFontWidth(font_nr);
3485 if (pos->align == ALIGN_CENTER)
3486 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3487 else if (pos->align == ALIGN_RIGHT)
3488 max_text_width = pos->x;
3490 max_text_width = SXSIZE - pos->x;
3492 return max_text_width / font_width;
3495 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3497 char label_text[MAX_OUTPUT_LINESIZE + 1];
3498 int max_len_label_text;
3499 int font_nr = pos->font;
3502 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3505 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3506 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3507 mode == MICROLABEL_IMPORTED_BY_HEAD)
3508 font_nr = pos->font_alt;
3510 max_len_label_text = getMaxTextLength(pos, font_nr);
3512 if (pos->size != -1)
3513 max_len_label_text = pos->size;
3515 for (i = 0; i < max_len_label_text; i++)
3516 label_text[i] = ' ';
3517 label_text[max_len_label_text] = '\0';
3519 if (strlen(label_text) > 0)
3520 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3523 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3524 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3525 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3526 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3527 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3528 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3529 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3530 max_len_label_text);
3531 label_text[max_len_label_text] = '\0';
3533 if (strlen(label_text) > 0)
3534 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3536 redraw_mask |= REDRAW_FIELD;
3539 static void DrawPreviewLevelLabel(int mode)
3541 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3544 static void DrawPreviewLevelInfo(int mode)
3546 if (mode == MICROLABEL_LEVEL_NAME)
3547 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3548 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3549 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3552 static void DrawPreviewLevelExt(boolean restart)
3554 static DelayCounter scroll_delay = { 0 };
3555 static DelayCounter label_delay = { 0 };
3556 static int from_x, from_y, scroll_direction;
3557 static int label_state, label_counter;
3558 boolean show_level_border = (BorderElement != EL_EMPTY);
3559 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3560 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3562 scroll_delay.value = preview.step_delay;
3563 label_delay.value = MICROLEVEL_LABEL_DELAY;
3570 if (preview.anim_mode == ANIM_CENTERED)
3572 if (level_xsize > preview.xsize)
3573 from_x = (level_xsize - preview.xsize) / 2;
3574 if (level_ysize > preview.ysize)
3575 from_y = (level_ysize - preview.ysize) / 2;
3578 from_x += preview.xoffset;
3579 from_y += preview.yoffset;
3581 scroll_direction = MV_RIGHT;
3585 DrawPreviewLevelPlayfield(from_x, from_y);
3586 DrawPreviewLevelLabel(label_state);
3588 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3589 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3591 // initialize delay counters
3592 ResetDelayCounter(&scroll_delay);
3593 ResetDelayCounter(&label_delay);
3595 if (leveldir_current->name)
3597 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3598 char label_text[MAX_OUTPUT_LINESIZE + 1];
3599 int font_nr = pos->font;
3600 int max_len_label_text = getMaxTextLength(pos, font_nr);
3602 if (pos->size != -1)
3603 max_len_label_text = pos->size;
3605 strncpy(label_text, leveldir_current->name, max_len_label_text);
3606 label_text[max_len_label_text] = '\0';
3608 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3609 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3615 // scroll preview level, if needed
3616 if (preview.anim_mode != ANIM_NONE &&
3617 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3618 DelayReached(&scroll_delay))
3620 switch (scroll_direction)
3625 from_x -= preview.step_offset;
3626 from_x = (from_x < 0 ? 0 : from_x);
3629 scroll_direction = MV_UP;
3633 if (from_x < level_xsize - preview.xsize)
3635 from_x += preview.step_offset;
3636 from_x = (from_x > level_xsize - preview.xsize ?
3637 level_xsize - preview.xsize : from_x);
3640 scroll_direction = MV_DOWN;
3646 from_y -= preview.step_offset;
3647 from_y = (from_y < 0 ? 0 : from_y);
3650 scroll_direction = MV_RIGHT;
3654 if (from_y < level_ysize - preview.ysize)
3656 from_y += preview.step_offset;
3657 from_y = (from_y > level_ysize - preview.ysize ?
3658 level_ysize - preview.ysize : from_y);
3661 scroll_direction = MV_LEFT;
3668 DrawPreviewLevelPlayfield(from_x, from_y);
3671 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3672 // redraw micro level label, if needed
3673 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3674 !strEqual(level.author, ANONYMOUS_NAME) &&
3675 !strEqual(level.author, leveldir_current->name) &&
3676 DelayReached(&label_delay))
3678 int max_label_counter = 23;
3680 if (leveldir_current->imported_from != NULL &&
3681 strlen(leveldir_current->imported_from) > 0)
3682 max_label_counter += 14;
3683 if (leveldir_current->imported_by != NULL &&
3684 strlen(leveldir_current->imported_by) > 0)
3685 max_label_counter += 14;
3687 label_counter = (label_counter + 1) % max_label_counter;
3688 label_state = (label_counter >= 0 && label_counter <= 7 ?
3689 MICROLABEL_LEVEL_NAME :
3690 label_counter >= 9 && label_counter <= 12 ?
3691 MICROLABEL_LEVEL_AUTHOR_HEAD :
3692 label_counter >= 14 && label_counter <= 21 ?
3693 MICROLABEL_LEVEL_AUTHOR :
3694 label_counter >= 23 && label_counter <= 26 ?
3695 MICROLABEL_IMPORTED_FROM_HEAD :
3696 label_counter >= 28 && label_counter <= 35 ?
3697 MICROLABEL_IMPORTED_FROM :
3698 label_counter >= 37 && label_counter <= 40 ?
3699 MICROLABEL_IMPORTED_BY_HEAD :
3700 label_counter >= 42 && label_counter <= 49 ?
3701 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3703 if (leveldir_current->imported_from == NULL &&
3704 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3705 label_state == MICROLABEL_IMPORTED_FROM))
3706 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3707 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3709 DrawPreviewLevelLabel(label_state);
3713 void DrawPreviewPlayers(void)
3715 if (game_status != GAME_MODE_MAIN)
3718 // do not draw preview players if level preview redefined, but players aren't
3719 if (preview.redefined && !menu.main.preview_players.redefined)
3722 boolean player_found[MAX_PLAYERS];
3723 int num_players = 0;
3726 for (i = 0; i < MAX_PLAYERS; i++)
3727 player_found[i] = FALSE;
3729 // check which players can be found in the level (simple approach)
3730 for (x = 0; x < lev_fieldx; x++)
3732 for (y = 0; y < lev_fieldy; y++)
3734 int element = level.field[x][y];
3736 if (IS_PLAYER_ELEMENT(element))
3738 int player_nr = GET_PLAYER_NR(element);
3740 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3742 if (!player_found[player_nr])
3745 player_found[player_nr] = TRUE;
3750 struct TextPosInfo *pos = &menu.main.preview_players;
3751 int tile_size = pos->tile_size;
3752 int border_size = pos->border_size;
3753 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3754 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3755 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3756 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3757 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3758 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3759 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3760 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3761 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3762 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3763 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3764 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3766 // clear area in which the players will be drawn
3767 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3768 max_players_width, max_players_height);
3770 if (!network.enabled && !setup.team_mode)
3773 // only draw players if level is suited for team mode
3774 if (num_players < 2)
3777 // draw all players that were found in the level
3778 for (i = 0; i < MAX_PLAYERS; i++)
3780 if (player_found[i])
3782 int graphic = el2img(EL_PLAYER_1 + i);
3784 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3786 xpos += player_xoffset;
3787 ypos += player_yoffset;
3792 void DrawPreviewLevelInitial(void)
3794 DrawPreviewLevelExt(TRUE);
3795 DrawPreviewPlayers();
3798 void DrawPreviewLevelAnimation(void)
3800 DrawPreviewLevelExt(FALSE);
3803 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3804 int border_size, int font_nr)
3806 int graphic = el2img(EL_PLAYER_1 + player_nr);
3807 int font_height = getFontHeight(font_nr);
3808 int player_height = MAX(tile_size, font_height);
3809 int xoffset_text = tile_size + border_size;
3810 int yoffset_text = (player_height - font_height) / 2;
3811 int yoffset_graphic = (player_height - tile_size) / 2;
3812 char *player_name = getNetworkPlayerName(player_nr + 1);
3814 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3816 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3819 static void DrawNetworkPlayersExt(boolean force)
3821 if (game_status != GAME_MODE_MAIN)
3824 if (!network.connected && !force)
3827 // do not draw network players if level preview redefined, but players aren't
3828 if (preview.redefined && !menu.main.network_players.redefined)
3831 int num_players = 0;
3834 for (i = 0; i < MAX_PLAYERS; i++)
3835 if (stored_player[i].connected_network)
3838 struct TextPosInfo *pos = &menu.main.network_players;
3839 int tile_size = pos->tile_size;
3840 int border_size = pos->border_size;
3841 int xoffset_text = tile_size + border_size;
3842 int font_nr = pos->font;
3843 int font_width = getFontWidth(font_nr);
3844 int font_height = getFontHeight(font_nr);
3845 int player_height = MAX(tile_size, font_height);
3846 int player_yoffset = player_height + border_size;
3847 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3848 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3849 int all_players_height = num_players * player_yoffset - border_size;
3850 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3851 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3852 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3854 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3855 max_players_width, max_players_height);
3857 // first draw local network player ...
3858 for (i = 0; i < MAX_PLAYERS; i++)
3860 if (stored_player[i].connected_network &&
3861 stored_player[i].connected_locally)
3863 char *player_name = getNetworkPlayerName(i + 1);
3864 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3865 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3867 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3869 ypos += player_yoffset;
3873 // ... then draw all other network players
3874 for (i = 0; i < MAX_PLAYERS; i++)
3876 if (stored_player[i].connected_network &&
3877 !stored_player[i].connected_locally)
3879 char *player_name = getNetworkPlayerName(i + 1);
3880 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3881 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3883 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3885 ypos += player_yoffset;
3890 void DrawNetworkPlayers(void)
3892 DrawNetworkPlayersExt(FALSE);
3895 void ClearNetworkPlayers(void)
3897 DrawNetworkPlayersExt(TRUE);
3900 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3901 int graphic, int lx, int ly,
3904 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3906 if (mask_mode == USE_MASKING)
3907 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3909 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3912 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3913 int graphic, int sync_frame, int mask_mode)
3915 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3917 if (mask_mode == USE_MASKING)
3918 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3920 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3923 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3924 int graphic, int sync_frame, int tilesize,
3927 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3929 if (mask_mode == USE_MASKING)
3930 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3932 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3935 static void DrawGraphicAnimation(int x, int y, int graphic)
3937 int lx = LEVELX(x), ly = LEVELY(y);
3938 int mask_mode = NO_MASKING;
3940 if (!IN_SCR_FIELD(x, y))
3943 if (game.use_masked_elements)
3945 if (Tile[lx][ly] != EL_EMPTY)
3947 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3949 mask_mode = USE_MASKING;
3953 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3954 graphic, lx, ly, mask_mode);
3956 MarkTileDirty(x, y);
3959 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3961 int lx = LEVELX(x), ly = LEVELY(y);
3962 int mask_mode = NO_MASKING;
3964 if (!IN_SCR_FIELD(x, y))
3967 if (game.use_masked_elements)
3969 if (Tile[lx][ly] != EL_EMPTY)
3971 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3973 mask_mode = USE_MASKING;
3977 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3978 graphic, lx, ly, mask_mode);
3980 MarkTileDirty(x, y);
3983 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3985 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3988 void DrawLevelElementAnimation(int x, int y, int element)
3990 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3992 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3995 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3997 int sx = SCREENX(x), sy = SCREENY(y);
3999 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4002 if (Tile[x][y] == EL_EMPTY)
4003 graphic = el2img(GfxElementEmpty[x][y]);
4005 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4008 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4011 DrawGraphicAnimation(sx, sy, graphic);
4014 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4015 DrawLevelFieldCrumbled(x, y);
4017 if (GFX_CRUMBLED(Tile[x][y]))
4018 DrawLevelFieldCrumbled(x, y);
4022 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4024 int sx = SCREENX(x), sy = SCREENY(y);
4027 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4030 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4032 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4035 DrawGraphicAnimation(sx, sy, graphic);
4037 if (GFX_CRUMBLED(element))
4038 DrawLevelFieldCrumbled(x, y);
4041 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4043 if (player->use_murphy)
4045 // this works only because currently only one player can be "murphy" ...
4046 static int last_horizontal_dir = MV_LEFT;
4047 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4049 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4050 last_horizontal_dir = move_dir;
4052 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4054 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4056 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4062 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4065 static boolean equalGraphics(int graphic1, int graphic2)
4067 struct GraphicInfo *g1 = &graphic_info[graphic1];
4068 struct GraphicInfo *g2 = &graphic_info[graphic2];
4070 return (g1->bitmap == g2->bitmap &&
4071 g1->src_x == g2->src_x &&
4072 g1->src_y == g2->src_y &&
4073 g1->anim_frames == g2->anim_frames &&
4074 g1->anim_delay == g2->anim_delay &&
4075 g1->anim_mode == g2->anim_mode);
4078 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4082 DRAW_PLAYER_STAGE_INIT = 0,
4083 DRAW_PLAYER_STAGE_LAST_FIELD,
4084 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4085 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4086 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4087 DRAW_PLAYER_STAGE_PLAYER,
4089 DRAW_PLAYER_STAGE_PLAYER,
4090 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4092 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4093 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4095 NUM_DRAW_PLAYER_STAGES
4098 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4100 static int static_last_player_graphic[MAX_PLAYERS];
4101 static int static_last_player_frame[MAX_PLAYERS];
4102 static boolean static_player_is_opaque[MAX_PLAYERS];
4103 static boolean draw_player[MAX_PLAYERS];
4104 int pnr = player->index_nr;
4106 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4108 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4109 static_last_player_frame[pnr] = player->Frame;
4110 static_player_is_opaque[pnr] = FALSE;
4112 draw_player[pnr] = TRUE;
4115 if (!draw_player[pnr])
4119 if (!IN_LEV_FIELD(player->jx, player->jy))
4121 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4122 Debug("draw:DrawPlayerExt", "This should never happen!");
4124 draw_player[pnr] = FALSE;
4130 int last_player_graphic = static_last_player_graphic[pnr];
4131 int last_player_frame = static_last_player_frame[pnr];
4132 boolean player_is_opaque = static_player_is_opaque[pnr];
4134 int jx = player->jx;
4135 int jy = player->jy;
4136 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4137 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4138 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4139 int last_jx = (player->is_moving ? jx - dx : jx);
4140 int last_jy = (player->is_moving ? jy - dy : jy);
4141 int next_jx = jx + dx;
4142 int next_jy = jy + dy;
4143 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4144 int sx = SCREENX(jx);
4145 int sy = SCREENY(jy);
4146 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4147 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4148 int element = Tile[jx][jy];
4149 int last_element = Tile[last_jx][last_jy];
4150 int action = (player->is_pushing ? ACTION_PUSHING :
4151 player->is_digging ? ACTION_DIGGING :
4152 player->is_collecting ? ACTION_COLLECTING :
4153 player->is_moving ? ACTION_MOVING :
4154 player->is_snapping ? ACTION_SNAPPING :
4155 player->is_dropping ? ACTION_DROPPING :
4156 player->is_waiting ? player->action_waiting :
4159 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4161 // ------------------------------------------------------------------------
4162 // initialize drawing the player
4163 // ------------------------------------------------------------------------
4165 draw_player[pnr] = FALSE;
4167 // GfxElement[][] is set to the element the player is digging or collecting;
4168 // remove also for off-screen player if the player is not moving anymore
4169 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4170 GfxElement[jx][jy] = EL_UNDEFINED;
4172 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4175 if (element == EL_EXPLOSION)
4178 InitPlayerGfxAnimation(player, action, move_dir);
4180 draw_player[pnr] = TRUE;
4182 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4184 // ------------------------------------------------------------------------
4185 // draw things in the field the player is leaving, if needed
4186 // ------------------------------------------------------------------------
4188 if (!IN_SCR_FIELD(sx, sy))
4189 draw_player[pnr] = FALSE;
4191 if (!player->is_moving)
4194 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4196 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4198 if (last_element == EL_DYNAMITE_ACTIVE ||
4199 last_element == EL_EM_DYNAMITE_ACTIVE ||
4200 last_element == EL_SP_DISK_RED_ACTIVE)
4201 DrawDynamite(last_jx, last_jy);
4203 DrawLevelFieldThruMask(last_jx, last_jy);
4205 else if (last_element == EL_DYNAMITE_ACTIVE ||
4206 last_element == EL_EM_DYNAMITE_ACTIVE ||
4207 last_element == EL_SP_DISK_RED_ACTIVE)
4208 DrawDynamite(last_jx, last_jy);
4210 DrawLevelField(last_jx, last_jy);
4212 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4214 // ------------------------------------------------------------------------
4215 // draw things behind the player, if needed
4216 // ------------------------------------------------------------------------
4220 DrawLevelElement(jx, jy, Back[jx][jy]);
4225 if (IS_ACTIVE_BOMB(element))
4227 DrawLevelElement(jx, jy, EL_EMPTY);
4232 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4234 int old_element = GfxElement[jx][jy];
4235 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4236 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4238 if (GFX_CRUMBLED(old_element))
4239 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4241 DrawScreenGraphic(sx, sy, old_graphic, frame);
4243 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4244 static_player_is_opaque[pnr] = TRUE;
4248 GfxElement[jx][jy] = EL_UNDEFINED;
4250 // make sure that pushed elements are drawn with correct frame rate
4251 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4253 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4254 GfxFrame[jx][jy] = player->StepFrame;
4256 DrawLevelField(jx, jy);
4259 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4261 // ------------------------------------------------------------------------
4262 // draw things the player is pushing, if needed
4263 // ------------------------------------------------------------------------
4265 if (!player->is_pushing || !player->is_moving)
4268 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4271 int gfx_frame = GfxFrame[jx][jy];
4273 if (!IS_MOVING(jx, jy)) // push movement already finished
4275 element = Tile[next_jx][next_jy];
4276 gfx_frame = GfxFrame[next_jx][next_jy];
4279 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4280 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4281 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4283 // draw background element under pushed element (like the Sokoban field)
4284 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4286 // this allows transparent pushing animation over non-black background
4289 DrawLevelElement(jx, jy, Back[jx][jy]);
4291 DrawLevelElement(jx, jy, EL_EMPTY);
4294 if (Back[next_jx][next_jy])
4295 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4297 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4299 int px = SCREENX(jx), py = SCREENY(jy);
4300 int pxx = (TILEX - ABS(sxx)) * dx;
4301 int pyy = (TILEY - ABS(syy)) * dy;
4304 // do not draw (EM style) pushing animation when pushing is finished
4305 // (two-tile animations usually do not contain start and end frame)
4306 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4307 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4309 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4311 // masked drawing is needed for EMC style (double) movement graphics
4312 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4313 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4316 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4318 // ------------------------------------------------------------------------
4319 // draw player himself
4320 // ------------------------------------------------------------------------
4322 int graphic = getPlayerGraphic(player, move_dir);
4324 // in the case of changed player action or direction, prevent the current
4325 // animation frame from being restarted for identical animations
4326 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4327 player->Frame = last_player_frame;
4329 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4331 if (player_is_opaque)
4332 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4334 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4336 if (SHIELD_ON(player))
4338 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4339 IMG_SHIELD_NORMAL_ACTIVE);
4340 frame = getGraphicAnimationFrame(graphic, -1);
4342 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4345 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4347 // ------------------------------------------------------------------------
4348 // draw things in front of player (active dynamite or dynabombs)
4349 // ------------------------------------------------------------------------
4351 if (IS_ACTIVE_BOMB(element))
4353 int graphic = el2img(element);
4354 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4356 if (game.emulation == EMU_SUPAPLEX)
4357 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4359 DrawGraphicThruMask(sx, sy, graphic, frame);
4362 if (player_is_moving && last_element == EL_EXPLOSION)
4364 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4365 GfxElement[last_jx][last_jy] : EL_EMPTY);
4366 int graphic = el_act2img(element, ACTION_EXPLODING);
4367 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4368 int phase = ExplodePhase[last_jx][last_jy] - 1;
4369 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4372 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4375 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4377 // ------------------------------------------------------------------------
4378 // draw elements the player is just walking/passing through/under
4379 // ------------------------------------------------------------------------
4381 if (player_is_moving)
4383 // handle the field the player is leaving ...
4384 if (IS_ACCESSIBLE_INSIDE(last_element))
4385 DrawLevelField(last_jx, last_jy);
4386 else if (IS_ACCESSIBLE_UNDER(last_element))
4387 DrawLevelFieldThruMask(last_jx, last_jy);
4390 // do not redraw accessible elements if the player is just pushing them
4391 if (!player_is_moving || !player->is_pushing)
4393 // ... and the field the player is entering
4394 if (IS_ACCESSIBLE_INSIDE(element))
4395 DrawLevelField(jx, jy);
4396 else if (IS_ACCESSIBLE_UNDER(element))
4397 DrawLevelFieldThruMask(jx, jy);
4400 MarkTileDirty(sx, sy);
4404 void DrawPlayer(struct PlayerInfo *player)
4408 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4409 DrawPlayerExt(player, i);
4412 void DrawAllPlayers(void)
4416 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4417 for (j = 0; j < MAX_PLAYERS; j++)
4418 if (stored_player[j].active)
4419 DrawPlayerExt(&stored_player[j], i);
4422 void DrawPlayerField(int x, int y)
4424 if (!IS_PLAYER(x, y))
4427 DrawPlayer(PLAYERINFO(x, y));
4430 // ----------------------------------------------------------------------------
4432 void WaitForEventToContinue(void)
4434 boolean first_wait = TRUE;
4435 boolean still_wait = TRUE;
4437 if (program.headless)
4440 // simulate releasing mouse button over last gadget, if still pressed
4442 HandleGadgets(-1, -1, 0);
4444 button_status = MB_RELEASED;
4447 ClearPlayerAction();
4453 if (NextValidEvent(&event))
4457 case EVENT_BUTTONPRESS:
4458 case EVENT_FINGERPRESS:
4462 case EVENT_BUTTONRELEASE:
4463 case EVENT_FINGERRELEASE:
4464 still_wait = first_wait;
4467 case EVENT_KEYPRESS:
4468 case SDL_CONTROLLERBUTTONDOWN:
4469 case SDL_JOYBUTTONDOWN:
4474 HandleOtherEvents(&event);
4478 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4483 if (!PendingEvent())
4488 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4490 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4491 int draw_buffer_last = GetDrawtoField();
4492 int width = request.width;
4493 int height = request.height;
4497 setRequestPosition(&sx, &sy, FALSE);
4499 button_status = MB_RELEASED;
4501 request_gadget_id = -1;
4508 SetDrawtoField(draw_buffer_game);
4510 HandleGameActions();
4512 SetDrawtoField(DRAW_TO_BACKBUFFER);
4519 while (NextValidEvent(&event))
4523 case EVENT_BUTTONPRESS:
4524 case EVENT_BUTTONRELEASE:
4525 case EVENT_MOTIONNOTIFY:
4527 DrawBuffer *drawto_last = drawto;
4530 if (event.type == EVENT_MOTIONNOTIFY)
4535 motion_status = TRUE;
4536 mx = ((MotionEvent *) &event)->x;
4537 my = ((MotionEvent *) &event)->y;
4541 motion_status = FALSE;
4542 mx = ((ButtonEvent *) &event)->x;
4543 my = ((ButtonEvent *) &event)->y;
4544 if (event.type == EVENT_BUTTONPRESS)
4545 button_status = ((ButtonEvent *) &event)->button;
4547 button_status = MB_RELEASED;
4550 if (global.use_envelope_request)
4552 // draw changed button states to temporary bitmap
4553 drawto = bitmap_db_store_1;
4556 // this sets 'request_gadget_id'
4557 HandleGadgets(mx, my, button_status);
4559 if (global.use_envelope_request)
4561 // restore pointer to drawing buffer
4562 drawto = drawto_last;
4564 // prepare complete envelope request from temporary bitmap
4565 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4569 switch (request_gadget_id)
4571 case TOOL_CTRL_ID_YES:
4572 case TOOL_CTRL_ID_TOUCH_YES:
4575 case TOOL_CTRL_ID_NO:
4576 case TOOL_CTRL_ID_TOUCH_NO:
4579 case TOOL_CTRL_ID_CONFIRM:
4580 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4581 result = TRUE | FALSE;
4584 case TOOL_CTRL_ID_PLAYER_1:
4587 case TOOL_CTRL_ID_PLAYER_2:
4590 case TOOL_CTRL_ID_PLAYER_3:
4593 case TOOL_CTRL_ID_PLAYER_4:
4598 // only check clickable animations if no request gadget clicked
4599 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4606 case SDL_WINDOWEVENT:
4607 HandleWindowEvent((WindowEvent *) &event);
4610 case SDL_APP_WILLENTERBACKGROUND:
4611 case SDL_APP_DIDENTERBACKGROUND:
4612 case SDL_APP_WILLENTERFOREGROUND:
4613 case SDL_APP_DIDENTERFOREGROUND:
4614 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4617 case EVENT_KEYPRESS:
4619 Key key = GetEventKey((KeyEvent *)&event);
4624 if (req_state & REQ_CONFIRM)
4633 #if defined(KSYM_Rewind)
4634 case KSYM_Rewind: // for Amazon Fire TV remote
4643 #if defined(KSYM_FastForward)
4644 case KSYM_FastForward: // for Amazon Fire TV remote
4650 HandleKeysDebug(key, KEY_PRESSED);
4654 if (req_state & REQ_PLAYER)
4656 int old_player_nr = setup.network_player_nr;
4659 result = old_player_nr + 1;
4664 result = old_player_nr + 1;
4695 case EVENT_FINGERRELEASE:
4696 case EVENT_KEYRELEASE:
4697 ClearPlayerAction();
4700 case SDL_CONTROLLERBUTTONDOWN:
4701 switch (event.cbutton.button)
4703 case SDL_CONTROLLER_BUTTON_A:
4704 case SDL_CONTROLLER_BUTTON_X:
4705 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4706 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4710 case SDL_CONTROLLER_BUTTON_B:
4711 case SDL_CONTROLLER_BUTTON_Y:
4712 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4713 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4714 case SDL_CONTROLLER_BUTTON_BACK:
4719 if (req_state & REQ_PLAYER)
4721 int old_player_nr = setup.network_player_nr;
4724 result = old_player_nr + 1;
4726 switch (event.cbutton.button)
4728 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4729 case SDL_CONTROLLER_BUTTON_Y:
4733 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4734 case SDL_CONTROLLER_BUTTON_B:
4738 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4739 case SDL_CONTROLLER_BUTTON_A:
4743 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4744 case SDL_CONTROLLER_BUTTON_X:
4755 case SDL_CONTROLLERBUTTONUP:
4756 HandleJoystickEvent(&event);
4757 ClearPlayerAction();
4761 HandleOtherEvents(&event);
4766 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4768 int joy = AnyJoystick();
4770 if (joy & JOY_BUTTON_1)
4772 else if (joy & JOY_BUTTON_2)
4775 else if (AnyJoystick())
4777 int joy = AnyJoystick();
4779 if (req_state & REQ_PLAYER)
4783 else if (joy & JOY_RIGHT)
4785 else if (joy & JOY_DOWN)
4787 else if (joy & JOY_LEFT)
4795 SetDrawtoField(draw_buffer_last);
4800 static void DoRequestBefore(void)
4802 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4804 // when showing request dialog after game ended, deactivate game panel
4806 game.panel.active = FALSE;
4808 if (game_status == GAME_MODE_PLAYING)
4809 BlitScreenToBitmap(backbuffer);
4811 // disable deactivated drawing when quick-loading level tape recording
4812 if (tape.playing && tape.deactivate_display)
4813 TapeDeactivateDisplayOff(TRUE);
4815 SetMouseCursor(CURSOR_DEFAULT);
4817 // pause network game while waiting for request to answer
4818 if (network.enabled &&
4819 game_status == GAME_MODE_PLAYING &&
4820 !game.all_players_gone)
4821 SendToServer_PausePlaying();
4823 // simulate releasing mouse button over last gadget, if still pressed
4825 HandleGadgets(-1, -1, 0);
4830 static void DoRequestAfter(void)
4834 if (game_status == GAME_MODE_PLAYING)
4836 SetPanelBackground();
4837 SetDrawBackgroundMask(REDRAW_DOOR_1);
4841 SetDrawBackgroundMask(REDRAW_FIELD);
4844 // continue network game after request
4845 if (network.enabled &&
4846 game_status == GAME_MODE_PLAYING &&
4847 !game.all_players_gone)
4848 SendToServer_ContinuePlaying();
4850 // restore deactivated drawing when quick-loading level tape recording
4851 if (tape.playing && tape.deactivate_display)
4852 TapeDeactivateDisplayOn();
4855 static void setRequestDoorTextProperties(char *text,
4860 int *set_max_line_length)
4862 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4863 struct TextPosInfo *pos = &request.button.confirm;
4864 int button_ypos = pos->y;
4865 int font_nr = FONT_TEXT_2;
4866 int font_width = getFontWidth(font_nr);
4867 int font_height = getFontHeight(font_nr);
4868 int line_height = font_height + line_spacing;
4869 int max_text_width = vp_door_1->width;
4870 int max_text_height = button_ypos - 2 * text_spacing;
4871 int max_line_length = max_text_width / font_width;
4872 int max_lines = max_text_height / line_height;
4874 if (maxWordLengthInRequestString(text) > max_line_length)
4876 font_nr = FONT_TEXT_1;
4877 font_width = getFontWidth(font_nr);
4878 max_line_length = max_text_width / font_width;
4881 *set_font_nr = font_nr;
4882 *set_max_lines = max_lines;
4883 *set_max_line_length = max_line_length;
4886 static void DrawRequestDoorText(char *text)
4888 char *text_ptr = text;
4889 int text_spacing = 8;
4890 int line_spacing = 2;
4891 int max_request_lines;
4892 int max_request_line_len;
4896 // force DOOR font inside door area
4897 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4899 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4900 &max_request_lines, &max_request_line_len);
4902 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4904 char text_line[max_request_line_len + 1];
4910 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4912 tc = *(text_ptr + tx);
4913 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4917 if ((tc == '?' || tc == '!') && tl == 0)
4927 strncpy(text_line, text_ptr, tl);
4930 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4931 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4932 text_line, font_nr);
4934 text_ptr += tl + (tc == ' ' ? 1 : 0);
4940 static int RequestDoor(char *text, unsigned int req_state)
4942 unsigned int old_door_state = GetDoorState();
4943 int draw_buffer_last = GetDrawtoField();
4946 if (old_door_state & DOOR_OPEN_1)
4948 CloseDoor(DOOR_CLOSE_1);
4950 // save old door content
4951 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4952 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4955 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4956 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4958 // clear door drawing field
4959 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4961 // write text for request
4962 DrawRequestDoorText(text);
4964 MapToolButtons(req_state);
4966 // copy request gadgets to door backbuffer
4967 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4969 OpenDoor(DOOR_OPEN_1);
4971 // ---------- handle request buttons ----------
4972 result = RequestHandleEvents(req_state, draw_buffer_last);
4976 if (!(req_state & REQ_STAY_OPEN))
4978 CloseDoor(DOOR_CLOSE_1);
4980 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4981 (req_state & REQ_REOPEN))
4982 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4988 static int RequestEnvelope(char *text, unsigned int req_state)
4990 int draw_buffer_last = GetDrawtoField();
4993 DrawEnvelopeRequest(text, req_state);
4994 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4996 // ---------- handle request buttons ----------
4997 result = RequestHandleEvents(req_state, draw_buffer_last);
5001 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5006 int Request(char *text, unsigned int req_state)
5008 boolean overlay_enabled = GetOverlayEnabled();
5011 game.request_active = TRUE;
5013 SetOverlayEnabled(FALSE);
5017 if (global.use_envelope_request)
5018 result = RequestEnvelope(text, req_state);
5020 result = RequestDoor(text, req_state);
5024 SetOverlayEnabled(overlay_enabled);
5026 game.request_active = FALSE;
5031 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5033 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5034 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5037 if (dpo1->sort_priority != dpo2->sort_priority)
5038 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5040 compare_result = dpo1->nr - dpo2->nr;
5042 return compare_result;
5045 void InitGraphicCompatibilityInfo_Doors(void)
5051 struct DoorInfo *door;
5055 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5056 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5058 { -1, -1, -1, NULL }
5060 struct Rect door_rect_list[] =
5062 { DX, DY, DXSIZE, DYSIZE },
5063 { VX, VY, VXSIZE, VYSIZE }
5067 for (i = 0; doors[i].door_token != -1; i++)
5069 int door_token = doors[i].door_token;
5070 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5071 int part_1 = doors[i].part_1;
5072 int part_8 = doors[i].part_8;
5073 int part_2 = part_1 + 1;
5074 int part_3 = part_1 + 2;
5075 struct DoorInfo *door = doors[i].door;
5076 struct Rect *door_rect = &door_rect_list[door_index];
5077 boolean door_gfx_redefined = FALSE;
5079 // check if any door part graphic definitions have been redefined
5081 for (j = 0; door_part_controls[j].door_token != -1; j++)
5083 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5084 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5086 if (dpc->door_token == door_token && fi->redefined)
5087 door_gfx_redefined = TRUE;
5090 // check for old-style door graphic/animation modifications
5092 if (!door_gfx_redefined)
5094 if (door->anim_mode & ANIM_STATIC_PANEL)
5096 door->panel.step_xoffset = 0;
5097 door->panel.step_yoffset = 0;
5100 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5102 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5103 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5104 int num_door_steps, num_panel_steps;
5106 // remove door part graphics other than the two default wings
5108 for (j = 0; door_part_controls[j].door_token != -1; j++)
5110 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5111 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5113 if (dpc->graphic >= part_3 &&
5114 dpc->graphic <= part_8)
5118 // set graphics and screen positions of the default wings
5120 g_part_1->width = door_rect->width;
5121 g_part_1->height = door_rect->height;
5122 g_part_2->width = door_rect->width;
5123 g_part_2->height = door_rect->height;
5124 g_part_2->src_x = door_rect->width;
5125 g_part_2->src_y = g_part_1->src_y;
5127 door->part_2.x = door->part_1.x;
5128 door->part_2.y = door->part_1.y;
5130 if (door->width != -1)
5132 g_part_1->width = door->width;
5133 g_part_2->width = door->width;
5135 // special treatment for graphics and screen position of right wing
5136 g_part_2->src_x += door_rect->width - door->width;
5137 door->part_2.x += door_rect->width - door->width;
5140 if (door->height != -1)
5142 g_part_1->height = door->height;
5143 g_part_2->height = door->height;
5145 // special treatment for graphics and screen position of bottom wing
5146 g_part_2->src_y += door_rect->height - door->height;
5147 door->part_2.y += door_rect->height - door->height;
5150 // set animation delays for the default wings and panels
5152 door->part_1.step_delay = door->step_delay;
5153 door->part_2.step_delay = door->step_delay;
5154 door->panel.step_delay = door->step_delay;
5156 // set animation draw order for the default wings
5158 door->part_1.sort_priority = 2; // draw left wing over ...
5159 door->part_2.sort_priority = 1; // ... right wing
5161 // set animation draw offset for the default wings
5163 if (door->anim_mode & ANIM_HORIZONTAL)
5165 door->part_1.step_xoffset = door->step_offset;
5166 door->part_1.step_yoffset = 0;
5167 door->part_2.step_xoffset = door->step_offset * -1;
5168 door->part_2.step_yoffset = 0;
5170 num_door_steps = g_part_1->width / door->step_offset;
5172 else // ANIM_VERTICAL
5174 door->part_1.step_xoffset = 0;
5175 door->part_1.step_yoffset = door->step_offset;
5176 door->part_2.step_xoffset = 0;
5177 door->part_2.step_yoffset = door->step_offset * -1;
5179 num_door_steps = g_part_1->height / door->step_offset;
5182 // set animation draw offset for the default panels
5184 if (door->step_offset > 1)
5186 num_panel_steps = 2 * door_rect->height / door->step_offset;
5187 door->panel.start_step = num_panel_steps - num_door_steps;
5188 door->panel.start_step_closing = door->panel.start_step;
5192 num_panel_steps = door_rect->height / door->step_offset;
5193 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5194 door->panel.start_step_closing = door->panel.start_step;
5195 door->panel.step_delay *= 2;
5202 void InitDoors(void)
5206 for (i = 0; door_part_controls[i].door_token != -1; i++)
5208 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5209 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5211 // initialize "start_step_opening" and "start_step_closing", if needed
5212 if (dpc->pos->start_step_opening == 0 &&
5213 dpc->pos->start_step_closing == 0)
5215 // dpc->pos->start_step_opening = dpc->pos->start_step;
5216 dpc->pos->start_step_closing = dpc->pos->start_step;
5219 // fill structure for door part draw order (sorted below)
5221 dpo->sort_priority = dpc->pos->sort_priority;
5224 // sort door part controls according to sort_priority and graphic number
5225 qsort(door_part_order, MAX_DOOR_PARTS,
5226 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5229 unsigned int OpenDoor(unsigned int door_state)
5231 if (door_state & DOOR_COPY_BACK)
5233 if (door_state & DOOR_OPEN_1)
5234 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5235 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5237 if (door_state & DOOR_OPEN_2)
5238 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5239 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5241 door_state &= ~DOOR_COPY_BACK;
5244 return MoveDoor(door_state);
5247 unsigned int CloseDoor(unsigned int door_state)
5249 unsigned int old_door_state = GetDoorState();
5251 if (!(door_state & DOOR_NO_COPY_BACK))
5253 if (old_door_state & DOOR_OPEN_1)
5254 BlitBitmap(backbuffer, bitmap_db_door_1,
5255 DX, DY, DXSIZE, DYSIZE, 0, 0);
5257 if (old_door_state & DOOR_OPEN_2)
5258 BlitBitmap(backbuffer, bitmap_db_door_2,
5259 VX, VY, VXSIZE, VYSIZE, 0, 0);
5261 door_state &= ~DOOR_NO_COPY_BACK;
5264 return MoveDoor(door_state);
5267 unsigned int GetDoorState(void)
5269 return MoveDoor(DOOR_GET_STATE);
5272 unsigned int SetDoorState(unsigned int door_state)
5274 return MoveDoor(door_state | DOOR_SET_STATE);
5277 static int euclid(int a, int b)
5279 return (b ? euclid(b, a % b) : a);
5282 unsigned int MoveDoor(unsigned int door_state)
5284 struct Rect door_rect_list[] =
5286 { DX, DY, DXSIZE, DYSIZE },
5287 { VX, VY, VXSIZE, VYSIZE }
5289 static int door1 = DOOR_CLOSE_1;
5290 static int door2 = DOOR_CLOSE_2;
5291 DelayCounter door_delay = { 0 };
5294 if (door_state == DOOR_GET_STATE)
5295 return (door1 | door2);
5297 if (door_state & DOOR_SET_STATE)
5299 if (door_state & DOOR_ACTION_1)
5300 door1 = door_state & DOOR_ACTION_1;
5301 if (door_state & DOOR_ACTION_2)
5302 door2 = door_state & DOOR_ACTION_2;
5304 return (door1 | door2);
5307 if (!(door_state & DOOR_FORCE_REDRAW))
5309 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5310 door_state &= ~DOOR_OPEN_1;
5311 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5312 door_state &= ~DOOR_CLOSE_1;
5313 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5314 door_state &= ~DOOR_OPEN_2;
5315 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5316 door_state &= ~DOOR_CLOSE_2;
5319 if (global.autoplay_leveldir)
5321 door_state |= DOOR_NO_DELAY;
5322 door_state &= ~DOOR_CLOSE_ALL;
5325 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5326 door_state |= DOOR_NO_DELAY;
5328 if (door_state & DOOR_ACTION)
5330 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5331 boolean door_panel_drawn[NUM_DOORS];
5332 boolean panel_has_doors[NUM_DOORS];
5333 boolean door_part_skip[MAX_DOOR_PARTS];
5334 boolean door_part_done[MAX_DOOR_PARTS];
5335 boolean door_part_done_all;
5336 int num_steps[MAX_DOOR_PARTS];
5337 int max_move_delay = 0; // delay for complete animations of all doors
5338 int max_step_delay = 0; // delay (ms) between two animation frames
5339 int num_move_steps = 0; // number of animation steps for all doors
5340 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5341 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5345 for (i = 0; i < NUM_DOORS; i++)
5346 panel_has_doors[i] = FALSE;
5348 for (i = 0; i < MAX_DOOR_PARTS; i++)
5350 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5351 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5352 int door_token = dpc->door_token;
5354 door_part_done[i] = FALSE;
5355 door_part_skip[i] = (!(door_state & door_token) ||
5359 for (i = 0; i < MAX_DOOR_PARTS; i++)
5361 int nr = door_part_order[i].nr;
5362 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5363 struct DoorPartPosInfo *pos = dpc->pos;
5364 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5365 int door_token = dpc->door_token;
5366 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5367 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5368 int step_xoffset = ABS(pos->step_xoffset);
5369 int step_yoffset = ABS(pos->step_yoffset);
5370 int step_delay = pos->step_delay;
5371 int current_door_state = door_state & door_token;
5372 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5373 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5374 boolean part_opening = (is_panel ? door_closing : door_opening);
5375 int start_step = (part_opening ? pos->start_step_opening :
5376 pos->start_step_closing);
5377 float move_xsize = (step_xoffset ? g->width : 0);
5378 float move_ysize = (step_yoffset ? g->height : 0);
5379 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5380 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5381 int move_steps = (move_xsteps && move_ysteps ?
5382 MIN(move_xsteps, move_ysteps) :
5383 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5384 int move_delay = move_steps * step_delay;
5386 if (door_part_skip[nr])
5389 max_move_delay = MAX(max_move_delay, move_delay);
5390 max_step_delay = (max_step_delay == 0 ? step_delay :
5391 euclid(max_step_delay, step_delay));
5392 num_steps[nr] = move_steps;
5396 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5398 panel_has_doors[door_index] = TRUE;
5402 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5404 num_move_steps = max_move_delay / max_step_delay;
5405 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5407 door_delay.value = max_step_delay;
5409 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5411 start = num_move_steps - 1;
5415 // opening door sound has priority over simultaneously closing door
5416 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5418 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5420 if (door_state & DOOR_OPEN_1)
5421 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5422 if (door_state & DOOR_OPEN_2)
5423 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5425 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5427 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5429 if (door_state & DOOR_CLOSE_1)
5430 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5431 if (door_state & DOOR_CLOSE_2)
5432 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5436 for (k = start; k < num_move_steps; k++)
5438 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5440 door_part_done_all = TRUE;
5442 for (i = 0; i < NUM_DOORS; i++)
5443 door_panel_drawn[i] = FALSE;
5445 for (i = 0; i < MAX_DOOR_PARTS; i++)
5447 int nr = door_part_order[i].nr;
5448 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5449 struct DoorPartPosInfo *pos = dpc->pos;
5450 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5451 int door_token = dpc->door_token;
5452 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5453 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5454 boolean is_panel_and_door_has_closed = FALSE;
5455 struct Rect *door_rect = &door_rect_list[door_index];
5456 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5458 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5459 int current_door_state = door_state & door_token;
5460 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5461 boolean door_closing = !door_opening;
5462 boolean part_opening = (is_panel ? door_closing : door_opening);
5463 boolean part_closing = !part_opening;
5464 int start_step = (part_opening ? pos->start_step_opening :
5465 pos->start_step_closing);
5466 int step_delay = pos->step_delay;
5467 int step_factor = step_delay / max_step_delay;
5468 int k1 = (step_factor ? k / step_factor + 1 : k);
5469 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5470 int kk = MAX(0, k2);
5473 int src_x, src_y, src_xx, src_yy;
5474 int dst_x, dst_y, dst_xx, dst_yy;
5477 if (door_part_skip[nr])
5480 if (!(door_state & door_token))
5488 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5489 int kk_door = MAX(0, k2_door);
5490 int sync_frame = kk_door * door_delay.value;
5491 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5493 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5494 &g_src_x, &g_src_y);
5499 if (!door_panel_drawn[door_index])
5501 ClearRectangle(drawto, door_rect->x, door_rect->y,
5502 door_rect->width, door_rect->height);
5504 door_panel_drawn[door_index] = TRUE;
5507 // draw opening or closing door parts
5509 if (pos->step_xoffset < 0) // door part on right side
5512 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5515 if (dst_xx + width > door_rect->width)
5516 width = door_rect->width - dst_xx;
5518 else // door part on left side
5521 dst_xx = pos->x - kk * pos->step_xoffset;
5525 src_xx = ABS(dst_xx);
5529 width = g->width - src_xx;
5531 if (width > door_rect->width)
5532 width = door_rect->width;
5534 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5537 if (pos->step_yoffset < 0) // door part on bottom side
5540 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5543 if (dst_yy + height > door_rect->height)
5544 height = door_rect->height - dst_yy;
5546 else // door part on top side
5549 dst_yy = pos->y - kk * pos->step_yoffset;
5553 src_yy = ABS(dst_yy);
5557 height = g->height - src_yy;
5560 src_x = g_src_x + src_xx;
5561 src_y = g_src_y + src_yy;
5563 dst_x = door_rect->x + dst_xx;
5564 dst_y = door_rect->y + dst_yy;
5566 is_panel_and_door_has_closed =
5569 panel_has_doors[door_index] &&
5570 k >= num_move_steps_doors_only - 1);
5572 if (width >= 0 && width <= g->width &&
5573 height >= 0 && height <= g->height &&
5574 !is_panel_and_door_has_closed)
5576 if (is_panel || !pos->draw_masked)
5577 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5580 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5584 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5586 if ((part_opening && (width < 0 || height < 0)) ||
5587 (part_closing && (width >= g->width && height >= g->height)))
5588 door_part_done[nr] = TRUE;
5590 // continue door part animations, but not panel after door has closed
5591 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5592 door_part_done_all = FALSE;
5595 if (!(door_state & DOOR_NO_DELAY))
5598 HandleGameActions();
5602 SkipUntilDelayReached(&door_delay, &k, last_frame);
5604 // prevent OS (Windows) from complaining about program not responding
5608 if (door_part_done_all)
5612 if (!(door_state & DOOR_NO_DELAY))
5614 // wait for specified door action post delay
5615 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5616 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5617 else if (door_state & DOOR_ACTION_1)
5618 door_delay.value = door_1.post_delay;
5619 else if (door_state & DOOR_ACTION_2)
5620 door_delay.value = door_2.post_delay;
5622 while (!DelayReached(&door_delay))
5625 HandleGameActions();
5632 if (door_state & DOOR_ACTION_1)
5633 door1 = door_state & DOOR_ACTION_1;
5634 if (door_state & DOOR_ACTION_2)
5635 door2 = door_state & DOOR_ACTION_2;
5637 // draw masked border over door area
5638 DrawMaskedBorder(REDRAW_DOOR_1);
5639 DrawMaskedBorder(REDRAW_DOOR_2);
5641 ClearAutoRepeatKeyEvents();
5643 return (door1 | door2);
5646 static boolean useSpecialEditorDoor(void)
5648 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5649 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5651 // do not draw special editor door if editor border defined or redefined
5652 if (graphic_info[graphic].bitmap != NULL || redefined)
5655 // do not draw special editor door if global border defined to be empty
5656 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5659 // do not draw special editor door if viewport definitions do not match
5663 EY + EYSIZE != VY + VYSIZE)
5669 void DrawSpecialEditorDoor(void)
5671 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5672 int top_border_width = gfx1->width;
5673 int top_border_height = gfx1->height;
5674 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5675 int ex = EX - outer_border;
5676 int ey = EY - outer_border;
5677 int vy = VY - outer_border;
5678 int exsize = EXSIZE + 2 * outer_border;
5680 if (!useSpecialEditorDoor())
5683 // draw bigger level editor toolbox window
5684 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5685 top_border_width, top_border_height, ex, ey - top_border_height);
5686 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5687 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5689 redraw_mask |= REDRAW_ALL;
5692 void UndrawSpecialEditorDoor(void)
5694 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5695 int top_border_width = gfx1->width;
5696 int top_border_height = gfx1->height;
5697 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5698 int ex = EX - outer_border;
5699 int ey = EY - outer_border;
5700 int ey_top = ey - top_border_height;
5701 int exsize = EXSIZE + 2 * outer_border;
5702 int eysize = EYSIZE + 2 * outer_border;
5704 if (!useSpecialEditorDoor())
5707 // draw normal tape recorder window
5708 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5710 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5711 ex, ey_top, top_border_width, top_border_height,
5713 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5714 ex, ey, exsize, eysize, ex, ey);
5718 // if screen background is set to "[NONE]", clear editor toolbox window
5719 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5720 ClearRectangle(drawto, ex, ey, exsize, eysize);
5723 redraw_mask |= REDRAW_ALL;
5727 // ---------- new tool button stuff -------------------------------------------
5732 struct TextPosInfo *pos;
5734 boolean is_touch_button;
5736 } toolbutton_info[NUM_TOOL_BUTTONS] =
5739 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5740 TOOL_CTRL_ID_YES, FALSE, "yes"
5743 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5744 TOOL_CTRL_ID_NO, FALSE, "no"
5747 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5748 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5751 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5752 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5755 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5756 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5759 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5760 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5763 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5764 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5767 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5768 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5771 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5772 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5775 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5776 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5780 void CreateToolButtons(void)
5784 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5786 int graphic = toolbutton_info[i].graphic;
5787 struct GraphicInfo *gfx = &graphic_info[graphic];
5788 struct TextPosInfo *pos = toolbutton_info[i].pos;
5789 struct GadgetInfo *gi;
5790 Bitmap *deco_bitmap = None;
5791 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5792 unsigned int event_mask = GD_EVENT_RELEASED;
5793 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5794 int base_x = (is_touch_button ? 0 : DX);
5795 int base_y = (is_touch_button ? 0 : DY);
5796 int gd_x = gfx->src_x;
5797 int gd_y = gfx->src_y;
5798 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5799 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5804 // do not use touch buttons if overlay touch buttons are disabled
5805 if (is_touch_button && !setup.touch.overlay_buttons)
5808 if (global.use_envelope_request && !is_touch_button)
5810 setRequestPosition(&base_x, &base_y, TRUE);
5812 // check if request buttons are outside of envelope and fix, if needed
5813 if (x < 0 || x + gfx->width > request.width ||
5814 y < 0 || y + gfx->height > request.height)
5816 if (id == TOOL_CTRL_ID_YES)
5819 y = request.height - 2 * request.border_size - gfx->height;
5821 else if (id == TOOL_CTRL_ID_NO)
5823 x = request.width - 2 * request.border_size - gfx->width;
5824 y = request.height - 2 * request.border_size - gfx->height;
5826 else if (id == TOOL_CTRL_ID_CONFIRM)
5828 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5829 y = request.height - 2 * request.border_size - gfx->height;
5831 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5833 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5835 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5836 y = request.height - 2 * request.border_size - gfx->height * 2;
5838 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5839 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5844 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5847 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5849 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5850 pos->size, &deco_bitmap, &deco_x, &deco_y);
5851 deco_xpos = (gfx->width - pos->size) / 2;
5852 deco_ypos = (gfx->height - pos->size) / 2;
5855 gi = CreateGadget(GDI_CUSTOM_ID, id,
5856 GDI_IMAGE_ID, graphic,
5857 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5860 GDI_WIDTH, gfx->width,
5861 GDI_HEIGHT, gfx->height,
5862 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5863 GDI_STATE, GD_BUTTON_UNPRESSED,
5864 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5865 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5866 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5867 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5868 GDI_DECORATION_SIZE, pos->size, pos->size,
5869 GDI_DECORATION_SHIFTING, 1, 1,
5870 GDI_DIRECT_DRAW, FALSE,
5871 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5872 GDI_EVENT_MASK, event_mask,
5873 GDI_CALLBACK_ACTION, HandleToolButtons,
5877 Fail("cannot create gadget");
5879 tool_gadget[id] = gi;
5883 void FreeToolButtons(void)
5887 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5888 FreeGadget(tool_gadget[i]);
5891 static void MapToolButtons(unsigned int req_state)
5893 if (req_state & REQ_ASK)
5895 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5896 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5897 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5898 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5900 else if (req_state & REQ_CONFIRM)
5902 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5903 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5905 else if (req_state & REQ_PLAYER)
5907 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5908 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5909 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5910 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5914 static void UnmapToolButtons(void)
5918 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5919 UnmapGadget(tool_gadget[i]);
5922 static void HandleToolButtons(struct GadgetInfo *gi)
5924 request_gadget_id = gi->custom_id;
5927 static struct Mapping_EM_to_RND_object
5930 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5931 boolean is_backside; // backside of moving element
5937 em_object_mapping_list[GAME_TILE_MAX + 1] =
5940 Zborder, FALSE, FALSE,
5944 Zplayer, FALSE, FALSE,
5953 Ztank, FALSE, FALSE,
5957 Zeater, FALSE, FALSE,
5961 Zdynamite, FALSE, FALSE,
5965 Zboom, FALSE, FALSE,
5970 Xchain, FALSE, FALSE,
5971 EL_DEFAULT, ACTION_EXPLODING, -1
5974 Xboom_bug, FALSE, FALSE,
5975 EL_BUG, ACTION_EXPLODING, -1
5978 Xboom_tank, FALSE, FALSE,
5979 EL_SPACESHIP, ACTION_EXPLODING, -1
5982 Xboom_android, FALSE, FALSE,
5983 EL_EMC_ANDROID, ACTION_OTHER, -1
5986 Xboom_1, FALSE, FALSE,
5987 EL_DEFAULT, ACTION_EXPLODING, -1
5990 Xboom_2, FALSE, FALSE,
5991 EL_DEFAULT, ACTION_EXPLODING, -1
5995 Xblank, TRUE, FALSE,
6000 Xsplash_e, FALSE, FALSE,
6001 EL_ACID_SPLASH_RIGHT, -1, -1
6004 Xsplash_w, FALSE, FALSE,
6005 EL_ACID_SPLASH_LEFT, -1, -1
6009 Xplant, TRUE, FALSE,
6010 EL_EMC_PLANT, -1, -1
6013 Yplant, FALSE, FALSE,
6014 EL_EMC_PLANT, -1, -1
6018 Xacid_1, TRUE, FALSE,
6022 Xacid_2, FALSE, FALSE,
6026 Xacid_3, FALSE, FALSE,
6030 Xacid_4, FALSE, FALSE,
6034 Xacid_5, FALSE, FALSE,
6038 Xacid_6, FALSE, FALSE,
6042 Xacid_7, FALSE, FALSE,
6046 Xacid_8, FALSE, FALSE,
6051 Xfake_acid_1, TRUE, FALSE,
6052 EL_EMC_FAKE_ACID, -1, -1
6055 Xfake_acid_2, FALSE, FALSE,
6056 EL_EMC_FAKE_ACID, -1, -1
6059 Xfake_acid_3, FALSE, FALSE,
6060 EL_EMC_FAKE_ACID, -1, -1
6063 Xfake_acid_4, FALSE, FALSE,
6064 EL_EMC_FAKE_ACID, -1, -1
6067 Xfake_acid_5, FALSE, FALSE,
6068 EL_EMC_FAKE_ACID, -1, -1
6071 Xfake_acid_6, FALSE, FALSE,
6072 EL_EMC_FAKE_ACID, -1, -1
6075 Xfake_acid_7, FALSE, FALSE,
6076 EL_EMC_FAKE_ACID, -1, -1
6079 Xfake_acid_8, FALSE, FALSE,
6080 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_1_player, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_2_player, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6092 Xfake_acid_3_player, FALSE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_4_player, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_5_player, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_6_player, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_7_player, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_8_player, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6117 Xgrass, TRUE, FALSE,
6118 EL_EMC_GRASS, -1, -1
6121 Ygrass_nB, FALSE, FALSE,
6122 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6125 Ygrass_eB, FALSE, FALSE,
6126 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6129 Ygrass_sB, FALSE, FALSE,
6130 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6133 Ygrass_wB, FALSE, FALSE,
6134 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6142 Ydirt_nB, FALSE, FALSE,
6143 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6146 Ydirt_eB, FALSE, FALSE,
6147 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6150 Ydirt_sB, FALSE, FALSE,
6151 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6154 Ydirt_wB, FALSE, FALSE,
6155 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6159 Xandroid, TRUE, FALSE,
6160 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6163 Xandroid_1_n, FALSE, FALSE,
6164 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6167 Xandroid_2_n, FALSE, FALSE,
6168 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6171 Xandroid_1_e, FALSE, FALSE,
6172 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6175 Xandroid_2_e, FALSE, FALSE,
6176 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6179 Xandroid_1_w, FALSE, FALSE,
6180 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6183 Xandroid_2_w, FALSE, FALSE,
6184 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6187 Xandroid_1_s, FALSE, FALSE,
6188 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6191 Xandroid_2_s, FALSE, FALSE,
6192 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6195 Yandroid_n, FALSE, FALSE,
6196 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6199 Yandroid_nB, FALSE, TRUE,
6200 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6203 Yandroid_ne, FALSE, FALSE,
6204 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6207 Yandroid_neB, FALSE, TRUE,
6208 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6211 Yandroid_e, FALSE, FALSE,
6212 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6215 Yandroid_eB, FALSE, TRUE,
6216 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6219 Yandroid_se, FALSE, FALSE,
6220 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6223 Yandroid_seB, FALSE, TRUE,
6224 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6227 Yandroid_s, FALSE, FALSE,
6228 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6231 Yandroid_sB, FALSE, TRUE,
6232 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6235 Yandroid_sw, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6239 Yandroid_swB, FALSE, TRUE,
6240 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6243 Yandroid_w, FALSE, FALSE,
6244 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6247 Yandroid_wB, FALSE, TRUE,
6248 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6251 Yandroid_nw, FALSE, FALSE,
6252 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6255 Yandroid_nwB, FALSE, TRUE,
6256 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6260 Xeater_n, TRUE, FALSE,
6261 EL_YAMYAM_UP, -1, -1
6264 Xeater_e, TRUE, FALSE,
6265 EL_YAMYAM_RIGHT, -1, -1
6268 Xeater_w, TRUE, FALSE,
6269 EL_YAMYAM_LEFT, -1, -1
6272 Xeater_s, TRUE, FALSE,
6273 EL_YAMYAM_DOWN, -1, -1
6276 Yeater_n, FALSE, FALSE,
6277 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6280 Yeater_nB, FALSE, TRUE,
6281 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6284 Yeater_e, FALSE, FALSE,
6285 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6288 Yeater_eB, FALSE, TRUE,
6289 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6292 Yeater_s, FALSE, FALSE,
6293 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6296 Yeater_sB, FALSE, TRUE,
6297 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6300 Yeater_w, FALSE, FALSE,
6301 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6304 Yeater_wB, FALSE, TRUE,
6305 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6308 Yeater_stone, FALSE, FALSE,
6309 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6312 Yeater_spring, FALSE, FALSE,
6313 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6317 Xalien, TRUE, FALSE,
6321 Xalien_pause, FALSE, FALSE,
6325 Yalien_n, FALSE, FALSE,
6326 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6329 Yalien_nB, FALSE, TRUE,
6330 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6333 Yalien_e, FALSE, FALSE,
6334 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6337 Yalien_eB, FALSE, TRUE,
6338 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6341 Yalien_s, FALSE, FALSE,
6342 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6345 Yalien_sB, FALSE, TRUE,
6346 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6349 Yalien_w, FALSE, FALSE,
6350 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6353 Yalien_wB, FALSE, TRUE,
6354 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6357 Yalien_stone, FALSE, FALSE,
6358 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6361 Yalien_spring, FALSE, FALSE,
6362 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6366 Xbug_1_n, TRUE, FALSE,
6370 Xbug_1_e, TRUE, FALSE,
6371 EL_BUG_RIGHT, -1, -1
6374 Xbug_1_s, TRUE, FALSE,
6378 Xbug_1_w, TRUE, FALSE,
6382 Xbug_2_n, FALSE, FALSE,
6386 Xbug_2_e, FALSE, FALSE,
6387 EL_BUG_RIGHT, -1, -1
6390 Xbug_2_s, FALSE, FALSE,
6394 Xbug_2_w, FALSE, FALSE,
6398 Ybug_n, FALSE, FALSE,
6399 EL_BUG, ACTION_MOVING, MV_BIT_UP
6402 Ybug_nB, FALSE, TRUE,
6403 EL_BUG, ACTION_MOVING, MV_BIT_UP
6406 Ybug_e, FALSE, FALSE,
6407 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6410 Ybug_eB, FALSE, TRUE,
6411 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6414 Ybug_s, FALSE, FALSE,
6415 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6418 Ybug_sB, FALSE, TRUE,
6419 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6422 Ybug_w, FALSE, FALSE,
6423 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6426 Ybug_wB, FALSE, TRUE,
6427 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6430 Ybug_w_n, FALSE, FALSE,
6431 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6434 Ybug_n_e, FALSE, FALSE,
6435 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6438 Ybug_e_s, FALSE, FALSE,
6439 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6442 Ybug_s_w, FALSE, FALSE,
6443 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6446 Ybug_e_n, FALSE, FALSE,
6447 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6450 Ybug_s_e, FALSE, FALSE,
6451 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6454 Ybug_w_s, FALSE, FALSE,
6455 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6458 Ybug_n_w, FALSE, FALSE,
6459 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6462 Ybug_stone, FALSE, FALSE,
6463 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6466 Ybug_spring, FALSE, FALSE,
6467 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6471 Xtank_1_n, TRUE, FALSE,
6472 EL_SPACESHIP_UP, -1, -1
6475 Xtank_1_e, TRUE, FALSE,
6476 EL_SPACESHIP_RIGHT, -1, -1
6479 Xtank_1_s, TRUE, FALSE,
6480 EL_SPACESHIP_DOWN, -1, -1
6483 Xtank_1_w, TRUE, FALSE,
6484 EL_SPACESHIP_LEFT, -1, -1
6487 Xtank_2_n, FALSE, FALSE,
6488 EL_SPACESHIP_UP, -1, -1
6491 Xtank_2_e, FALSE, FALSE,
6492 EL_SPACESHIP_RIGHT, -1, -1
6495 Xtank_2_s, FALSE, FALSE,
6496 EL_SPACESHIP_DOWN, -1, -1
6499 Xtank_2_w, FALSE, FALSE,
6500 EL_SPACESHIP_LEFT, -1, -1
6503 Ytank_n, FALSE, FALSE,
6504 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6507 Ytank_nB, FALSE, TRUE,
6508 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6511 Ytank_e, FALSE, FALSE,
6512 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6515 Ytank_eB, FALSE, TRUE,
6516 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6519 Ytank_s, FALSE, FALSE,
6520 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6523 Ytank_sB, FALSE, TRUE,
6524 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6527 Ytank_w, FALSE, FALSE,
6528 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6531 Ytank_wB, FALSE, TRUE,
6532 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6535 Ytank_w_n, FALSE, FALSE,
6536 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6539 Ytank_n_e, FALSE, FALSE,
6540 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6543 Ytank_e_s, FALSE, FALSE,
6544 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6547 Ytank_s_w, FALSE, FALSE,
6548 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6551 Ytank_e_n, FALSE, FALSE,
6552 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6555 Ytank_s_e, FALSE, FALSE,
6556 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6559 Ytank_w_s, FALSE, FALSE,
6560 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6563 Ytank_n_w, FALSE, FALSE,
6564 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6567 Ytank_stone, FALSE, FALSE,
6568 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6571 Ytank_spring, FALSE, FALSE,
6572 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6576 Xemerald, TRUE, FALSE,
6580 Xemerald_pause, FALSE, FALSE,
6584 Xemerald_fall, FALSE, FALSE,
6588 Xemerald_shine, FALSE, FALSE,
6589 EL_EMERALD, ACTION_TWINKLING, -1
6592 Yemerald_s, FALSE, FALSE,
6593 EL_EMERALD, ACTION_FALLING, -1
6596 Yemerald_sB, FALSE, TRUE,
6597 EL_EMERALD, ACTION_FALLING, -1
6600 Yemerald_e, FALSE, FALSE,
6601 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6604 Yemerald_eB, FALSE, TRUE,
6605 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6608 Yemerald_w, FALSE, FALSE,
6609 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6612 Yemerald_wB, FALSE, TRUE,
6613 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6616 Yemerald_blank, FALSE, FALSE,
6617 EL_EMERALD, ACTION_COLLECTING, -1
6621 Xdiamond, TRUE, FALSE,
6625 Xdiamond_pause, FALSE, FALSE,
6629 Xdiamond_fall, FALSE, FALSE,
6633 Xdiamond_shine, FALSE, FALSE,
6634 EL_DIAMOND, ACTION_TWINKLING, -1
6637 Ydiamond_s, FALSE, FALSE,
6638 EL_DIAMOND, ACTION_FALLING, -1
6641 Ydiamond_sB, FALSE, TRUE,
6642 EL_DIAMOND, ACTION_FALLING, -1
6645 Ydiamond_e, FALSE, FALSE,
6646 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6649 Ydiamond_eB, FALSE, TRUE,
6650 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6653 Ydiamond_w, FALSE, FALSE,
6654 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6657 Ydiamond_wB, FALSE, TRUE,
6658 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6661 Ydiamond_blank, FALSE, FALSE,
6662 EL_DIAMOND, ACTION_COLLECTING, -1
6665 Ydiamond_stone, FALSE, FALSE,
6666 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6670 Xstone, TRUE, FALSE,
6674 Xstone_pause, FALSE, FALSE,
6678 Xstone_fall, FALSE, FALSE,
6682 Ystone_s, FALSE, FALSE,
6683 EL_ROCK, ACTION_FALLING, -1
6686 Ystone_sB, FALSE, TRUE,
6687 EL_ROCK, ACTION_FALLING, -1
6690 Ystone_e, FALSE, FALSE,
6691 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6694 Ystone_eB, FALSE, TRUE,
6695 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6698 Ystone_w, FALSE, FALSE,
6699 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6702 Ystone_wB, FALSE, TRUE,
6703 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6711 Xbomb_pause, FALSE, FALSE,
6715 Xbomb_fall, FALSE, FALSE,
6719 Ybomb_s, FALSE, FALSE,
6720 EL_BOMB, ACTION_FALLING, -1
6723 Ybomb_sB, FALSE, TRUE,
6724 EL_BOMB, ACTION_FALLING, -1
6727 Ybomb_e, FALSE, FALSE,
6728 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6731 Ybomb_eB, FALSE, TRUE,
6732 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6735 Ybomb_w, FALSE, FALSE,
6736 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6739 Ybomb_wB, FALSE, TRUE,
6740 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6743 Ybomb_blank, FALSE, FALSE,
6744 EL_BOMB, ACTION_ACTIVATING, -1
6752 Xnut_pause, FALSE, FALSE,
6756 Xnut_fall, FALSE, FALSE,
6760 Ynut_s, FALSE, FALSE,
6761 EL_NUT, ACTION_FALLING, -1
6764 Ynut_sB, FALSE, TRUE,
6765 EL_NUT, ACTION_FALLING, -1
6768 Ynut_e, FALSE, FALSE,
6769 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6772 Ynut_eB, FALSE, TRUE,
6773 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6776 Ynut_w, FALSE, FALSE,
6777 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6780 Ynut_wB, FALSE, TRUE,
6781 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6784 Ynut_stone, FALSE, FALSE,
6785 EL_NUT, ACTION_BREAKING, -1
6789 Xspring, TRUE, FALSE,
6793 Xspring_pause, FALSE, FALSE,
6797 Xspring_e, TRUE, FALSE,
6798 EL_SPRING_RIGHT, -1, -1
6801 Xspring_w, TRUE, FALSE,
6802 EL_SPRING_LEFT, -1, -1
6805 Xspring_fall, FALSE, FALSE,
6809 Yspring_s, FALSE, FALSE,
6810 EL_SPRING, ACTION_FALLING, -1
6813 Yspring_sB, FALSE, TRUE,
6814 EL_SPRING, ACTION_FALLING, -1
6817 Yspring_e, FALSE, FALSE,
6818 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6821 Yspring_eB, FALSE, TRUE,
6822 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6825 Yspring_w, FALSE, FALSE,
6826 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6829 Yspring_wB, FALSE, TRUE,
6830 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6833 Yspring_alien_e, FALSE, FALSE,
6834 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6837 Yspring_alien_eB, FALSE, TRUE,
6838 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6841 Yspring_alien_w, FALSE, FALSE,
6842 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6845 Yspring_alien_wB, FALSE, TRUE,
6846 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6850 Xpush_emerald_e, FALSE, FALSE,
6851 EL_EMERALD, -1, MV_BIT_RIGHT
6854 Xpush_emerald_w, FALSE, FALSE,
6855 EL_EMERALD, -1, MV_BIT_LEFT
6858 Xpush_diamond_e, FALSE, FALSE,
6859 EL_DIAMOND, -1, MV_BIT_RIGHT
6862 Xpush_diamond_w, FALSE, FALSE,
6863 EL_DIAMOND, -1, MV_BIT_LEFT
6866 Xpush_stone_e, FALSE, FALSE,
6867 EL_ROCK, -1, MV_BIT_RIGHT
6870 Xpush_stone_w, FALSE, FALSE,
6871 EL_ROCK, -1, MV_BIT_LEFT
6874 Xpush_bomb_e, FALSE, FALSE,
6875 EL_BOMB, -1, MV_BIT_RIGHT
6878 Xpush_bomb_w, FALSE, FALSE,
6879 EL_BOMB, -1, MV_BIT_LEFT
6882 Xpush_nut_e, FALSE, FALSE,
6883 EL_NUT, -1, MV_BIT_RIGHT
6886 Xpush_nut_w, FALSE, FALSE,
6887 EL_NUT, -1, MV_BIT_LEFT
6890 Xpush_spring_e, FALSE, FALSE,
6891 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6894 Xpush_spring_w, FALSE, FALSE,
6895 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6899 Xdynamite, TRUE, FALSE,
6900 EL_EM_DYNAMITE, -1, -1
6903 Ydynamite_blank, FALSE, FALSE,
6904 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6907 Xdynamite_1, TRUE, FALSE,
6908 EL_EM_DYNAMITE_ACTIVE, -1, -1
6911 Xdynamite_2, FALSE, FALSE,
6912 EL_EM_DYNAMITE_ACTIVE, -1, -1
6915 Xdynamite_3, FALSE, FALSE,
6916 EL_EM_DYNAMITE_ACTIVE, -1, -1
6919 Xdynamite_4, FALSE, FALSE,
6920 EL_EM_DYNAMITE_ACTIVE, -1, -1
6924 Xkey_1, TRUE, FALSE,
6928 Xkey_2, TRUE, FALSE,
6932 Xkey_3, TRUE, FALSE,
6936 Xkey_4, TRUE, FALSE,
6940 Xkey_5, TRUE, FALSE,
6941 EL_EMC_KEY_5, -1, -1
6944 Xkey_6, TRUE, FALSE,
6945 EL_EMC_KEY_6, -1, -1
6948 Xkey_7, TRUE, FALSE,
6949 EL_EMC_KEY_7, -1, -1
6952 Xkey_8, TRUE, FALSE,
6953 EL_EMC_KEY_8, -1, -1
6957 Xdoor_1, TRUE, FALSE,
6958 EL_EM_GATE_1, -1, -1
6961 Xdoor_2, TRUE, FALSE,
6962 EL_EM_GATE_2, -1, -1
6965 Xdoor_3, TRUE, FALSE,
6966 EL_EM_GATE_3, -1, -1
6969 Xdoor_4, TRUE, FALSE,
6970 EL_EM_GATE_4, -1, -1
6973 Xdoor_5, TRUE, FALSE,
6974 EL_EMC_GATE_5, -1, -1
6977 Xdoor_6, TRUE, FALSE,
6978 EL_EMC_GATE_6, -1, -1
6981 Xdoor_7, TRUE, FALSE,
6982 EL_EMC_GATE_7, -1, -1
6985 Xdoor_8, TRUE, FALSE,
6986 EL_EMC_GATE_8, -1, -1
6990 Xfake_door_1, TRUE, FALSE,
6991 EL_EM_GATE_1_GRAY, -1, -1
6994 Xfake_door_2, TRUE, FALSE,
6995 EL_EM_GATE_2_GRAY, -1, -1
6998 Xfake_door_3, TRUE, FALSE,
6999 EL_EM_GATE_3_GRAY, -1, -1
7002 Xfake_door_4, TRUE, FALSE,
7003 EL_EM_GATE_4_GRAY, -1, -1
7006 Xfake_door_5, TRUE, FALSE,
7007 EL_EMC_GATE_5_GRAY, -1, -1
7010 Xfake_door_6, TRUE, FALSE,
7011 EL_EMC_GATE_6_GRAY, -1, -1
7014 Xfake_door_7, TRUE, FALSE,
7015 EL_EMC_GATE_7_GRAY, -1, -1
7018 Xfake_door_8, TRUE, FALSE,
7019 EL_EMC_GATE_8_GRAY, -1, -1
7023 Xballoon, TRUE, FALSE,
7027 Yballoon_n, FALSE, FALSE,
7028 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7031 Yballoon_nB, FALSE, TRUE,
7032 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7035 Yballoon_e, FALSE, FALSE,
7036 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7039 Yballoon_eB, FALSE, TRUE,
7040 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7043 Yballoon_s, FALSE, FALSE,
7044 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7047 Yballoon_sB, FALSE, TRUE,
7048 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7051 Yballoon_w, FALSE, FALSE,
7052 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7055 Yballoon_wB, FALSE, TRUE,
7056 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7060 Xball_1, TRUE, FALSE,
7061 EL_EMC_MAGIC_BALL, -1, -1
7064 Yball_1, FALSE, FALSE,
7065 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7068 Xball_2, FALSE, FALSE,
7069 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7072 Yball_2, FALSE, FALSE,
7073 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7076 Yball_blank, FALSE, FALSE,
7077 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7081 Xamoeba_1, TRUE, FALSE,
7082 EL_AMOEBA_DRY, ACTION_OTHER, -1
7085 Xamoeba_2, FALSE, FALSE,
7086 EL_AMOEBA_DRY, ACTION_OTHER, -1
7089 Xamoeba_3, FALSE, FALSE,
7090 EL_AMOEBA_DRY, ACTION_OTHER, -1
7093 Xamoeba_4, FALSE, FALSE,
7094 EL_AMOEBA_DRY, ACTION_OTHER, -1
7097 Xamoeba_5, TRUE, FALSE,
7098 EL_AMOEBA_WET, ACTION_OTHER, -1
7101 Xamoeba_6, FALSE, FALSE,
7102 EL_AMOEBA_WET, ACTION_OTHER, -1
7105 Xamoeba_7, FALSE, FALSE,
7106 EL_AMOEBA_WET, ACTION_OTHER, -1
7109 Xamoeba_8, FALSE, FALSE,
7110 EL_AMOEBA_WET, ACTION_OTHER, -1
7115 EL_AMOEBA_DROP, ACTION_GROWING, -1
7118 Xdrip_fall, FALSE, FALSE,
7119 EL_AMOEBA_DROP, -1, -1
7122 Xdrip_stretch, FALSE, FALSE,
7123 EL_AMOEBA_DROP, ACTION_FALLING, -1
7126 Xdrip_stretchB, FALSE, TRUE,
7127 EL_AMOEBA_DROP, ACTION_FALLING, -1
7130 Ydrip_1_s, FALSE, FALSE,
7131 EL_AMOEBA_DROP, ACTION_FALLING, -1
7134 Ydrip_1_sB, FALSE, TRUE,
7135 EL_AMOEBA_DROP, ACTION_FALLING, -1
7138 Ydrip_2_s, FALSE, FALSE,
7139 EL_AMOEBA_DROP, ACTION_FALLING, -1
7142 Ydrip_2_sB, FALSE, TRUE,
7143 EL_AMOEBA_DROP, ACTION_FALLING, -1
7147 Xwonderwall, TRUE, FALSE,
7148 EL_MAGIC_WALL, -1, -1
7151 Ywonderwall, FALSE, FALSE,
7152 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7156 Xwheel, TRUE, FALSE,
7157 EL_ROBOT_WHEEL, -1, -1
7160 Ywheel, FALSE, FALSE,
7161 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7165 Xswitch, TRUE, FALSE,
7166 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7169 Yswitch, FALSE, FALSE,
7170 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7174 Xbumper, TRUE, FALSE,
7175 EL_EMC_SPRING_BUMPER, -1, -1
7178 Ybumper, FALSE, FALSE,
7179 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7183 Xacid_nw, TRUE, FALSE,
7184 EL_ACID_POOL_TOPLEFT, -1, -1
7187 Xacid_ne, TRUE, FALSE,
7188 EL_ACID_POOL_TOPRIGHT, -1, -1
7191 Xacid_sw, TRUE, FALSE,
7192 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7195 Xacid_s, TRUE, FALSE,
7196 EL_ACID_POOL_BOTTOM, -1, -1
7199 Xacid_se, TRUE, FALSE,
7200 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7204 Xfake_blank, TRUE, FALSE,
7205 EL_INVISIBLE_WALL, -1, -1
7208 Yfake_blank, FALSE, FALSE,
7209 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7213 Xfake_grass, TRUE, FALSE,
7214 EL_EMC_FAKE_GRASS, -1, -1
7217 Yfake_grass, FALSE, FALSE,
7218 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7222 Xfake_amoeba, TRUE, FALSE,
7223 EL_EMC_DRIPPER, -1, -1
7226 Yfake_amoeba, FALSE, FALSE,
7227 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7231 Xlenses, TRUE, FALSE,
7232 EL_EMC_LENSES, -1, -1
7236 Xmagnify, TRUE, FALSE,
7237 EL_EMC_MAGNIFIER, -1, -1
7242 EL_QUICKSAND_EMPTY, -1, -1
7245 Xsand_stone, TRUE, FALSE,
7246 EL_QUICKSAND_FULL, -1, -1
7249 Xsand_stonein_1, FALSE, TRUE,
7250 EL_ROCK, ACTION_FILLING, -1
7253 Xsand_stonein_2, FALSE, TRUE,
7254 EL_ROCK, ACTION_FILLING, -1
7257 Xsand_stonein_3, FALSE, TRUE,
7258 EL_ROCK, ACTION_FILLING, -1
7261 Xsand_stonein_4, FALSE, TRUE,
7262 EL_ROCK, ACTION_FILLING, -1
7265 Xsand_sandstone_1, FALSE, FALSE,
7266 EL_QUICKSAND_FILLING, -1, -1
7269 Xsand_sandstone_2, FALSE, FALSE,
7270 EL_QUICKSAND_FILLING, -1, -1
7273 Xsand_sandstone_3, FALSE, FALSE,
7274 EL_QUICKSAND_FILLING, -1, -1
7277 Xsand_sandstone_4, FALSE, FALSE,
7278 EL_QUICKSAND_FILLING, -1, -1
7281 Xsand_stonesand_1, FALSE, FALSE,
7282 EL_QUICKSAND_EMPTYING, -1, -1
7285 Xsand_stonesand_2, FALSE, FALSE,
7286 EL_QUICKSAND_EMPTYING, -1, -1
7289 Xsand_stonesand_3, FALSE, FALSE,
7290 EL_QUICKSAND_EMPTYING, -1, -1
7293 Xsand_stonesand_4, FALSE, FALSE,
7294 EL_QUICKSAND_EMPTYING, -1, -1
7297 Xsand_stoneout_1, FALSE, FALSE,
7298 EL_ROCK, ACTION_EMPTYING, -1
7301 Xsand_stoneout_2, FALSE, FALSE,
7302 EL_ROCK, ACTION_EMPTYING, -1
7305 Xsand_stonesand_quickout_1, FALSE, FALSE,
7306 EL_QUICKSAND_EMPTYING, -1, -1
7309 Xsand_stonesand_quickout_2, FALSE, FALSE,
7310 EL_QUICKSAND_EMPTYING, -1, -1
7314 Xslide_ns, TRUE, FALSE,
7315 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7318 Yslide_ns_blank, FALSE, FALSE,
7319 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7322 Xslide_ew, TRUE, FALSE,
7323 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7326 Yslide_ew_blank, FALSE, FALSE,
7327 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7331 Xwind_n, TRUE, FALSE,
7332 EL_BALLOON_SWITCH_UP, -1, -1
7335 Xwind_e, TRUE, FALSE,
7336 EL_BALLOON_SWITCH_RIGHT, -1, -1
7339 Xwind_s, TRUE, FALSE,
7340 EL_BALLOON_SWITCH_DOWN, -1, -1
7343 Xwind_w, TRUE, FALSE,
7344 EL_BALLOON_SWITCH_LEFT, -1, -1
7347 Xwind_any, TRUE, FALSE,
7348 EL_BALLOON_SWITCH_ANY, -1, -1
7351 Xwind_stop, TRUE, FALSE,
7352 EL_BALLOON_SWITCH_NONE, -1, -1
7357 EL_EM_EXIT_CLOSED, -1, -1
7360 Xexit_1, TRUE, FALSE,
7361 EL_EM_EXIT_OPEN, -1, -1
7364 Xexit_2, FALSE, FALSE,
7365 EL_EM_EXIT_OPEN, -1, -1
7368 Xexit_3, FALSE, FALSE,
7369 EL_EM_EXIT_OPEN, -1, -1
7373 Xpause, FALSE, FALSE,
7378 Xwall_1, TRUE, FALSE,
7382 Xwall_2, TRUE, FALSE,
7383 EL_EMC_WALL_14, -1, -1
7386 Xwall_3, TRUE, FALSE,
7387 EL_EMC_WALL_15, -1, -1
7390 Xwall_4, TRUE, FALSE,
7391 EL_EMC_WALL_16, -1, -1
7395 Xroundwall_1, TRUE, FALSE,
7396 EL_WALL_SLIPPERY, -1, -1
7399 Xroundwall_2, TRUE, FALSE,
7400 EL_EMC_WALL_SLIPPERY_2, -1, -1
7403 Xroundwall_3, TRUE, FALSE,
7404 EL_EMC_WALL_SLIPPERY_3, -1, -1
7407 Xroundwall_4, TRUE, FALSE,
7408 EL_EMC_WALL_SLIPPERY_4, -1, -1
7412 Xsteel_1, TRUE, FALSE,
7413 EL_STEELWALL, -1, -1
7416 Xsteel_2, TRUE, FALSE,
7417 EL_EMC_STEELWALL_2, -1, -1
7420 Xsteel_3, TRUE, FALSE,
7421 EL_EMC_STEELWALL_3, -1, -1
7424 Xsteel_4, TRUE, FALSE,
7425 EL_EMC_STEELWALL_4, -1, -1
7429 Xdecor_1, TRUE, FALSE,
7430 EL_EMC_WALL_8, -1, -1
7433 Xdecor_2, TRUE, FALSE,
7434 EL_EMC_WALL_6, -1, -1
7437 Xdecor_3, TRUE, FALSE,
7438 EL_EMC_WALL_4, -1, -1
7441 Xdecor_4, TRUE, FALSE,
7442 EL_EMC_WALL_7, -1, -1
7445 Xdecor_5, TRUE, FALSE,
7446 EL_EMC_WALL_5, -1, -1
7449 Xdecor_6, TRUE, FALSE,
7450 EL_EMC_WALL_9, -1, -1
7453 Xdecor_7, TRUE, FALSE,
7454 EL_EMC_WALL_10, -1, -1
7457 Xdecor_8, TRUE, FALSE,
7458 EL_EMC_WALL_1, -1, -1
7461 Xdecor_9, TRUE, FALSE,
7462 EL_EMC_WALL_2, -1, -1
7465 Xdecor_10, TRUE, FALSE,
7466 EL_EMC_WALL_3, -1, -1
7469 Xdecor_11, TRUE, FALSE,
7470 EL_EMC_WALL_11, -1, -1
7473 Xdecor_12, TRUE, FALSE,
7474 EL_EMC_WALL_12, -1, -1
7478 Xalpha_0, TRUE, FALSE,
7479 EL_CHAR('0'), -1, -1
7482 Xalpha_1, TRUE, FALSE,
7483 EL_CHAR('1'), -1, -1
7486 Xalpha_2, TRUE, FALSE,
7487 EL_CHAR('2'), -1, -1
7490 Xalpha_3, TRUE, FALSE,
7491 EL_CHAR('3'), -1, -1
7494 Xalpha_4, TRUE, FALSE,
7495 EL_CHAR('4'), -1, -1
7498 Xalpha_5, TRUE, FALSE,
7499 EL_CHAR('5'), -1, -1
7502 Xalpha_6, TRUE, FALSE,
7503 EL_CHAR('6'), -1, -1
7506 Xalpha_7, TRUE, FALSE,
7507 EL_CHAR('7'), -1, -1
7510 Xalpha_8, TRUE, FALSE,
7511 EL_CHAR('8'), -1, -1
7514 Xalpha_9, TRUE, FALSE,
7515 EL_CHAR('9'), -1, -1
7518 Xalpha_excla, TRUE, FALSE,
7519 EL_CHAR('!'), -1, -1
7522 Xalpha_apost, TRUE, FALSE,
7523 EL_CHAR('\''), -1, -1
7526 Xalpha_comma, TRUE, FALSE,
7527 EL_CHAR(','), -1, -1
7530 Xalpha_minus, TRUE, FALSE,
7531 EL_CHAR('-'), -1, -1
7534 Xalpha_perio, TRUE, FALSE,
7535 EL_CHAR('.'), -1, -1
7538 Xalpha_colon, TRUE, FALSE,
7539 EL_CHAR(':'), -1, -1
7542 Xalpha_quest, TRUE, FALSE,
7543 EL_CHAR('?'), -1, -1
7546 Xalpha_a, TRUE, FALSE,
7547 EL_CHAR('A'), -1, -1
7550 Xalpha_b, TRUE, FALSE,
7551 EL_CHAR('B'), -1, -1
7554 Xalpha_c, TRUE, FALSE,
7555 EL_CHAR('C'), -1, -1
7558 Xalpha_d, TRUE, FALSE,
7559 EL_CHAR('D'), -1, -1
7562 Xalpha_e, TRUE, FALSE,
7563 EL_CHAR('E'), -1, -1
7566 Xalpha_f, TRUE, FALSE,
7567 EL_CHAR('F'), -1, -1
7570 Xalpha_g, TRUE, FALSE,
7571 EL_CHAR('G'), -1, -1
7574 Xalpha_h, TRUE, FALSE,
7575 EL_CHAR('H'), -1, -1
7578 Xalpha_i, TRUE, FALSE,
7579 EL_CHAR('I'), -1, -1
7582 Xalpha_j, TRUE, FALSE,
7583 EL_CHAR('J'), -1, -1
7586 Xalpha_k, TRUE, FALSE,
7587 EL_CHAR('K'), -1, -1
7590 Xalpha_l, TRUE, FALSE,
7591 EL_CHAR('L'), -1, -1
7594 Xalpha_m, TRUE, FALSE,
7595 EL_CHAR('M'), -1, -1
7598 Xalpha_n, TRUE, FALSE,
7599 EL_CHAR('N'), -1, -1
7602 Xalpha_o, TRUE, FALSE,
7603 EL_CHAR('O'), -1, -1
7606 Xalpha_p, TRUE, FALSE,
7607 EL_CHAR('P'), -1, -1
7610 Xalpha_q, TRUE, FALSE,
7611 EL_CHAR('Q'), -1, -1
7614 Xalpha_r, TRUE, FALSE,
7615 EL_CHAR('R'), -1, -1
7618 Xalpha_s, TRUE, FALSE,
7619 EL_CHAR('S'), -1, -1
7622 Xalpha_t, TRUE, FALSE,
7623 EL_CHAR('T'), -1, -1
7626 Xalpha_u, TRUE, FALSE,
7627 EL_CHAR('U'), -1, -1
7630 Xalpha_v, TRUE, FALSE,
7631 EL_CHAR('V'), -1, -1
7634 Xalpha_w, TRUE, FALSE,
7635 EL_CHAR('W'), -1, -1
7638 Xalpha_x, TRUE, FALSE,
7639 EL_CHAR('X'), -1, -1
7642 Xalpha_y, TRUE, FALSE,
7643 EL_CHAR('Y'), -1, -1
7646 Xalpha_z, TRUE, FALSE,
7647 EL_CHAR('Z'), -1, -1
7650 Xalpha_arrow_e, TRUE, FALSE,
7651 EL_CHAR('>'), -1, -1
7654 Xalpha_arrow_w, TRUE, FALSE,
7655 EL_CHAR('<'), -1, -1
7658 Xalpha_copyr, TRUE, FALSE,
7659 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7663 Ykey_1_blank, FALSE, FALSE,
7664 EL_EM_KEY_1, ACTION_COLLECTING, -1
7667 Ykey_2_blank, FALSE, FALSE,
7668 EL_EM_KEY_2, ACTION_COLLECTING, -1
7671 Ykey_3_blank, FALSE, FALSE,
7672 EL_EM_KEY_3, ACTION_COLLECTING, -1
7675 Ykey_4_blank, FALSE, FALSE,
7676 EL_EM_KEY_4, ACTION_COLLECTING, -1
7679 Ykey_5_blank, FALSE, FALSE,
7680 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7683 Ykey_6_blank, FALSE, FALSE,
7684 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7687 Ykey_7_blank, FALSE, FALSE,
7688 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7691 Ykey_8_blank, FALSE, FALSE,
7692 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7695 Ylenses_blank, FALSE, FALSE,
7696 EL_EMC_LENSES, ACTION_COLLECTING, -1
7699 Ymagnify_blank, FALSE, FALSE,
7700 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7703 Ygrass_blank, FALSE, FALSE,
7704 EL_EMC_GRASS, ACTION_SNAPPING, -1
7707 Ydirt_blank, FALSE, FALSE,
7708 EL_SAND, ACTION_SNAPPING, -1
7717 static struct Mapping_EM_to_RND_player
7726 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7730 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7734 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7738 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7742 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7746 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7750 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7754 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7758 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7762 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7766 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7770 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7774 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7778 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7782 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7786 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7790 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7794 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7798 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7802 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7806 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7810 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7814 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7818 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7822 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7826 EL_PLAYER_1, ACTION_DEFAULT, -1,
7830 EL_PLAYER_2, ACTION_DEFAULT, -1,
7834 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7838 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7842 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7846 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7850 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7854 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7858 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7862 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7866 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7870 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7874 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7878 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7882 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7886 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7890 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7894 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7898 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7902 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7906 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7910 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7914 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7918 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7922 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7926 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7930 EL_PLAYER_3, ACTION_DEFAULT, -1,
7934 EL_PLAYER_4, ACTION_DEFAULT, -1,
7943 int map_element_RND_to_EM_cave(int element_rnd)
7945 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7946 static boolean mapping_initialized = FALSE;
7948 if (!mapping_initialized)
7952 // return "Xalpha_quest" for all undefined elements in mapping array
7953 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7954 mapping_RND_to_EM[i] = Xalpha_quest;
7956 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7957 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7958 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7959 em_object_mapping_list[i].element_em;
7961 mapping_initialized = TRUE;
7964 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7966 Warn("invalid RND level element %d", element_rnd);
7971 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7974 int map_element_EM_to_RND_cave(int element_em_cave)
7976 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7977 static boolean mapping_initialized = FALSE;
7979 if (!mapping_initialized)
7983 // return "EL_UNKNOWN" for all undefined elements in mapping array
7984 for (i = 0; i < GAME_TILE_MAX; i++)
7985 mapping_EM_to_RND[i] = EL_UNKNOWN;
7987 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7988 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7989 em_object_mapping_list[i].element_rnd;
7991 mapping_initialized = TRUE;
7994 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7996 Warn("invalid EM cave element %d", element_em_cave);
8001 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8004 int map_element_EM_to_RND_game(int element_em_game)
8006 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8007 static boolean mapping_initialized = FALSE;
8009 if (!mapping_initialized)
8013 // return "EL_UNKNOWN" for all undefined elements in mapping array
8014 for (i = 0; i < GAME_TILE_MAX; i++)
8015 mapping_EM_to_RND[i] = EL_UNKNOWN;
8017 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8018 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8019 em_object_mapping_list[i].element_rnd;
8021 mapping_initialized = TRUE;
8024 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8026 Warn("invalid EM game element %d", element_em_game);
8031 return mapping_EM_to_RND[element_em_game];
8034 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8036 struct LevelInfo_EM *level_em = level->native_em_level;
8037 struct CAVE *cav = level_em->cav;
8040 for (i = 0; i < GAME_TILE_MAX; i++)
8041 cav->android_array[i] = Cblank;
8043 for (i = 0; i < level->num_android_clone_elements; i++)
8045 int element_rnd = level->android_clone_element[i];
8046 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8048 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8049 if (em_object_mapping_list[j].element_rnd == element_rnd)
8050 cav->android_array[em_object_mapping_list[j].element_em] =
8055 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8057 struct LevelInfo_EM *level_em = level->native_em_level;
8058 struct CAVE *cav = level_em->cav;
8061 level->num_android_clone_elements = 0;
8063 for (i = 0; i < GAME_TILE_MAX; i++)
8065 int element_em_cave = cav->android_array[i];
8067 boolean element_found = FALSE;
8069 if (element_em_cave == Cblank)
8072 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8074 for (j = 0; j < level->num_android_clone_elements; j++)
8075 if (level->android_clone_element[j] == element_rnd)
8076 element_found = TRUE;
8080 level->android_clone_element[level->num_android_clone_elements++] =
8083 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8088 if (level->num_android_clone_elements == 0)
8090 level->num_android_clone_elements = 1;
8091 level->android_clone_element[0] = EL_EMPTY;
8095 int map_direction_RND_to_EM(int direction)
8097 return (direction == MV_UP ? 0 :
8098 direction == MV_RIGHT ? 1 :
8099 direction == MV_DOWN ? 2 :
8100 direction == MV_LEFT ? 3 :
8104 int map_direction_EM_to_RND(int direction)
8106 return (direction == 0 ? MV_UP :
8107 direction == 1 ? MV_RIGHT :
8108 direction == 2 ? MV_DOWN :
8109 direction == 3 ? MV_LEFT :
8113 int map_element_RND_to_SP(int element_rnd)
8115 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8117 if (element_rnd >= EL_SP_START &&
8118 element_rnd <= EL_SP_END)
8119 element_sp = element_rnd - EL_SP_START;
8120 else if (element_rnd == EL_EMPTY_SPACE)
8122 else if (element_rnd == EL_INVISIBLE_WALL)
8128 int map_element_SP_to_RND(int element_sp)
8130 int element_rnd = EL_UNKNOWN;
8132 if (element_sp >= 0x00 &&
8134 element_rnd = EL_SP_START + element_sp;
8135 else if (element_sp == 0x28)
8136 element_rnd = EL_INVISIBLE_WALL;
8141 int map_action_SP_to_RND(int action_sp)
8145 case actActive: return ACTION_ACTIVE;
8146 case actImpact: return ACTION_IMPACT;
8147 case actExploding: return ACTION_EXPLODING;
8148 case actDigging: return ACTION_DIGGING;
8149 case actSnapping: return ACTION_SNAPPING;
8150 case actCollecting: return ACTION_COLLECTING;
8151 case actPassing: return ACTION_PASSING;
8152 case actPushing: return ACTION_PUSHING;
8153 case actDropping: return ACTION_DROPPING;
8155 default: return ACTION_DEFAULT;
8159 int map_element_RND_to_MM(int element_rnd)
8161 return (element_rnd >= EL_MM_START_1 &&
8162 element_rnd <= EL_MM_END_1 ?
8163 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8165 element_rnd >= EL_MM_START_2 &&
8166 element_rnd <= EL_MM_END_2 ?
8167 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8169 element_rnd >= EL_MM_START_3 &&
8170 element_rnd <= EL_MM_END_3 ?
8171 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8173 element_rnd >= EL_CHAR_START &&
8174 element_rnd <= EL_CHAR_END ?
8175 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8177 element_rnd >= EL_MM_RUNTIME_START &&
8178 element_rnd <= EL_MM_RUNTIME_END ?
8179 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8181 EL_MM_EMPTY_NATIVE);
8184 int map_element_MM_to_RND(int element_mm)
8186 return (element_mm == EL_MM_EMPTY_NATIVE ||
8187 element_mm == EL_DF_EMPTY_NATIVE ?
8190 element_mm >= EL_MM_START_1_NATIVE &&
8191 element_mm <= EL_MM_END_1_NATIVE ?
8192 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8194 element_mm >= EL_MM_START_2_NATIVE &&
8195 element_mm <= EL_MM_END_2_NATIVE ?
8196 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8198 element_mm >= EL_MM_START_3_NATIVE &&
8199 element_mm <= EL_MM_END_3_NATIVE ?
8200 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8202 element_mm >= EL_MM_CHAR_START_NATIVE &&
8203 element_mm <= EL_MM_CHAR_END_NATIVE ?
8204 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8206 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8207 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8208 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8213 int map_action_MM_to_RND(int action_mm)
8215 // all MM actions are defined to exactly match their RND counterparts
8219 int map_sound_MM_to_RND(int sound_mm)
8223 case SND_MM_GAME_LEVELTIME_CHARGING:
8224 return SND_GAME_LEVELTIME_CHARGING;
8226 case SND_MM_GAME_HEALTH_CHARGING:
8227 return SND_GAME_HEALTH_CHARGING;
8230 return SND_UNDEFINED;
8234 int map_mm_wall_element(int element)
8236 return (element >= EL_MM_STEEL_WALL_START &&
8237 element <= EL_MM_STEEL_WALL_END ?
8240 element >= EL_MM_WOODEN_WALL_START &&
8241 element <= EL_MM_WOODEN_WALL_END ?
8244 element >= EL_MM_ICE_WALL_START &&
8245 element <= EL_MM_ICE_WALL_END ?
8248 element >= EL_MM_AMOEBA_WALL_START &&
8249 element <= EL_MM_AMOEBA_WALL_END ?
8252 element >= EL_DF_STEEL_WALL_START &&
8253 element <= EL_DF_STEEL_WALL_END ?
8256 element >= EL_DF_WOODEN_WALL_START &&
8257 element <= EL_DF_WOODEN_WALL_END ?
8263 int map_mm_wall_element_editor(int element)
8267 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8268 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8269 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8270 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8271 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8272 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8274 default: return element;
8278 int get_next_element(int element)
8282 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8283 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8284 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8285 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8286 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8287 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8288 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8289 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8290 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8291 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8292 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8294 default: return element;
8298 int el2img_mm(int element_mm)
8300 return el2img(map_element_MM_to_RND(element_mm));
8303 int el_act2img_mm(int element_mm, int action)
8305 return el_act2img(map_element_MM_to_RND(element_mm), action);
8308 int el_act_dir2img(int element, int action, int direction)
8310 element = GFX_ELEMENT(element);
8311 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8313 // direction_graphic[][] == graphic[] for undefined direction graphics
8314 return element_info[element].direction_graphic[action][direction];
8317 static int el_act_dir2crm(int element, int action, int direction)
8319 element = GFX_ELEMENT(element);
8320 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8322 // direction_graphic[][] == graphic[] for undefined direction graphics
8323 return element_info[element].direction_crumbled[action][direction];
8326 int el_act2img(int element, int action)
8328 element = GFX_ELEMENT(element);
8330 return element_info[element].graphic[action];
8333 int el_act2crm(int element, int action)
8335 element = GFX_ELEMENT(element);
8337 return element_info[element].crumbled[action];
8340 int el_dir2img(int element, int direction)
8342 element = GFX_ELEMENT(element);
8344 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8347 int el2baseimg(int element)
8349 return element_info[element].graphic[ACTION_DEFAULT];
8352 int el2img(int element)
8354 element = GFX_ELEMENT(element);
8356 return element_info[element].graphic[ACTION_DEFAULT];
8359 int el2edimg(int element)
8361 element = GFX_ELEMENT(element);
8363 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8366 int el2preimg(int element)
8368 element = GFX_ELEMENT(element);
8370 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8373 int el2panelimg(int element)
8375 element = GFX_ELEMENT(element);
8377 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8380 int font2baseimg(int font_nr)
8382 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8385 int getBeltNrFromBeltElement(int element)
8387 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8388 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8389 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8392 int getBeltNrFromBeltActiveElement(int element)
8394 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8395 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8396 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8399 int getBeltNrFromBeltSwitchElement(int element)
8401 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8402 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8403 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8406 int getBeltDirNrFromBeltElement(int element)
8408 static int belt_base_element[4] =
8410 EL_CONVEYOR_BELT_1_LEFT,
8411 EL_CONVEYOR_BELT_2_LEFT,
8412 EL_CONVEYOR_BELT_3_LEFT,
8413 EL_CONVEYOR_BELT_4_LEFT
8416 int belt_nr = getBeltNrFromBeltElement(element);
8417 int belt_dir_nr = element - belt_base_element[belt_nr];
8419 return (belt_dir_nr % 3);
8422 int getBeltDirNrFromBeltSwitchElement(int element)
8424 static int belt_base_element[4] =
8426 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8427 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8428 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8429 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8432 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8433 int belt_dir_nr = element - belt_base_element[belt_nr];
8435 return (belt_dir_nr % 3);
8438 int getBeltDirFromBeltElement(int element)
8440 static int belt_move_dir[3] =
8447 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8449 return belt_move_dir[belt_dir_nr];
8452 int getBeltDirFromBeltSwitchElement(int element)
8454 static int belt_move_dir[3] =
8461 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8463 return belt_move_dir[belt_dir_nr];
8466 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8468 static int belt_base_element[4] =
8470 EL_CONVEYOR_BELT_1_LEFT,
8471 EL_CONVEYOR_BELT_2_LEFT,
8472 EL_CONVEYOR_BELT_3_LEFT,
8473 EL_CONVEYOR_BELT_4_LEFT
8476 return belt_base_element[belt_nr] + belt_dir_nr;
8479 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8481 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8483 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8486 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8488 static int belt_base_element[4] =
8490 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8491 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8492 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8493 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8496 return belt_base_element[belt_nr] + belt_dir_nr;
8499 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8501 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8503 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8506 boolean swapTiles_EM(boolean is_pre_emc_cave)
8508 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8511 boolean getTeamMode_EM(void)
8513 return game.team_mode || network_playing;
8516 boolean isActivePlayer_EM(int player_nr)
8518 return stored_player[player_nr].active;
8521 unsigned int InitRND(int seed)
8523 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8524 return InitEngineRandom_EM(seed);
8525 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8526 return InitEngineRandom_SP(seed);
8527 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8528 return InitEngineRandom_MM(seed);
8530 return InitEngineRandom_RND(seed);
8533 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8534 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8536 static int get_effective_element_EM(int tile, int frame_em)
8538 int element = object_mapping[tile].element_rnd;
8539 int action = object_mapping[tile].action;
8540 boolean is_backside = object_mapping[tile].is_backside;
8541 boolean action_removing = (action == ACTION_DIGGING ||
8542 action == ACTION_SNAPPING ||
8543 action == ACTION_COLLECTING);
8551 return (frame_em > 5 ? EL_EMPTY : element);
8557 else // frame_em == 7
8568 case Ydiamond_stone:
8572 case Xdrip_stretchB:
8588 case Ymagnify_blank:
8591 case Xsand_stonein_1:
8592 case Xsand_stonein_2:
8593 case Xsand_stonein_3:
8594 case Xsand_stonein_4:
8598 return (is_backside || action_removing ? EL_EMPTY : element);
8603 static boolean check_linear_animation_EM(int tile)
8607 case Xsand_stonesand_1:
8608 case Xsand_stonesand_quickout_1:
8609 case Xsand_sandstone_1:
8610 case Xsand_stonein_1:
8611 case Xsand_stoneout_1:
8639 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8640 boolean has_crumbled_graphics,
8641 int crumbled, int sync_frame)
8643 // if element can be crumbled, but certain action graphics are just empty
8644 // space (like instantly snapping sand to empty space in 1 frame), do not
8645 // treat these empty space graphics as crumbled graphics in EMC engine
8646 if (crumbled == IMG_EMPTY_SPACE)
8647 has_crumbled_graphics = FALSE;
8649 if (has_crumbled_graphics)
8651 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8652 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8653 g_crumbled->anim_delay,
8654 g_crumbled->anim_mode,
8655 g_crumbled->anim_start_frame,
8658 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8659 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8661 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8662 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8664 g_em->has_crumbled_graphics = TRUE;
8668 g_em->crumbled_bitmap = NULL;
8669 g_em->crumbled_src_x = 0;
8670 g_em->crumbled_src_y = 0;
8671 g_em->crumbled_border_size = 0;
8672 g_em->crumbled_tile_size = 0;
8674 g_em->has_crumbled_graphics = FALSE;
8679 void ResetGfxAnimation_EM(int x, int y, int tile)
8685 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8686 int tile, int frame_em, int x, int y)
8688 int action = object_mapping[tile].action;
8689 int direction = object_mapping[tile].direction;
8690 int effective_element = get_effective_element_EM(tile, frame_em);
8691 int graphic = (direction == MV_NONE ?
8692 el_act2img(effective_element, action) :
8693 el_act_dir2img(effective_element, action, direction));
8694 struct GraphicInfo *g = &graphic_info[graphic];
8696 boolean action_removing = (action == ACTION_DIGGING ||
8697 action == ACTION_SNAPPING ||
8698 action == ACTION_COLLECTING);
8699 boolean action_moving = (action == ACTION_FALLING ||
8700 action == ACTION_MOVING ||
8701 action == ACTION_PUSHING ||
8702 action == ACTION_EATING ||
8703 action == ACTION_FILLING ||
8704 action == ACTION_EMPTYING);
8705 boolean action_falling = (action == ACTION_FALLING ||
8706 action == ACTION_FILLING ||
8707 action == ACTION_EMPTYING);
8709 // special case: graphic uses "2nd movement tile" and has defined
8710 // 7 frames for movement animation (or less) => use default graphic
8711 // for last (8th) frame which ends the movement animation
8712 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8714 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8715 graphic = (direction == MV_NONE ?
8716 el_act2img(effective_element, action) :
8717 el_act_dir2img(effective_element, action, direction));
8719 g = &graphic_info[graphic];
8722 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8726 else if (action_moving)
8728 boolean is_backside = object_mapping[tile].is_backside;
8732 int direction = object_mapping[tile].direction;
8733 int move_dir = (action_falling ? MV_DOWN : direction);
8738 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8739 if (g->double_movement && frame_em == 0)
8743 if (move_dir == MV_LEFT)
8744 GfxFrame[x - 1][y] = GfxFrame[x][y];
8745 else if (move_dir == MV_RIGHT)
8746 GfxFrame[x + 1][y] = GfxFrame[x][y];
8747 else if (move_dir == MV_UP)
8748 GfxFrame[x][y - 1] = GfxFrame[x][y];
8749 else if (move_dir == MV_DOWN)
8750 GfxFrame[x][y + 1] = GfxFrame[x][y];
8757 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8758 if (tile == Xsand_stonesand_quickout_1 ||
8759 tile == Xsand_stonesand_quickout_2)
8763 if (graphic_info[graphic].anim_global_sync)
8764 sync_frame = FrameCounter;
8765 else if (graphic_info[graphic].anim_global_anim_sync)
8766 sync_frame = getGlobalAnimSyncFrame();
8767 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8768 sync_frame = GfxFrame[x][y];
8770 sync_frame = 0; // playfield border (pseudo steel)
8772 SetRandomAnimationValue(x, y);
8774 int frame = getAnimationFrame(g->anim_frames,
8777 g->anim_start_frame,
8780 g_em->unique_identifier =
8781 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8784 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8785 int tile, int frame_em, int x, int y)
8787 int action = object_mapping[tile].action;
8788 int direction = object_mapping[tile].direction;
8789 boolean is_backside = object_mapping[tile].is_backside;
8790 int effective_element = get_effective_element_EM(tile, frame_em);
8791 int effective_action = action;
8792 int graphic = (direction == MV_NONE ?
8793 el_act2img(effective_element, effective_action) :
8794 el_act_dir2img(effective_element, effective_action,
8796 int crumbled = (direction == MV_NONE ?
8797 el_act2crm(effective_element, effective_action) :
8798 el_act_dir2crm(effective_element, effective_action,
8800 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8801 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8802 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8803 struct GraphicInfo *g = &graphic_info[graphic];
8806 // special case: graphic uses "2nd movement tile" and has defined
8807 // 7 frames for movement animation (or less) => use default graphic
8808 // for last (8th) frame which ends the movement animation
8809 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8811 effective_action = ACTION_DEFAULT;
8812 graphic = (direction == MV_NONE ?
8813 el_act2img(effective_element, effective_action) :
8814 el_act_dir2img(effective_element, effective_action,
8816 crumbled = (direction == MV_NONE ?
8817 el_act2crm(effective_element, effective_action) :
8818 el_act_dir2crm(effective_element, effective_action,
8821 g = &graphic_info[graphic];
8824 if (graphic_info[graphic].anim_global_sync)
8825 sync_frame = FrameCounter;
8826 else if (graphic_info[graphic].anim_global_anim_sync)
8827 sync_frame = getGlobalAnimSyncFrame();
8828 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8829 sync_frame = GfxFrame[x][y];
8831 sync_frame = 0; // playfield border (pseudo steel)
8833 SetRandomAnimationValue(x, y);
8835 int frame = getAnimationFrame(g->anim_frames,
8838 g->anim_start_frame,
8841 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8842 g->double_movement && is_backside);
8844 // (updating the "crumbled" graphic definitions is probably not really needed,
8845 // as animations for crumbled graphics can't be longer than one EMC cycle)
8846 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8850 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8851 int player_nr, int anim, int frame_em)
8853 int element = player_mapping[player_nr][anim].element_rnd;
8854 int action = player_mapping[player_nr][anim].action;
8855 int direction = player_mapping[player_nr][anim].direction;
8856 int graphic = (direction == MV_NONE ?
8857 el_act2img(element, action) :
8858 el_act_dir2img(element, action, direction));
8859 struct GraphicInfo *g = &graphic_info[graphic];
8862 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8864 stored_player[player_nr].StepFrame = frame_em;
8866 sync_frame = stored_player[player_nr].Frame;
8868 int frame = getAnimationFrame(g->anim_frames,
8871 g->anim_start_frame,
8874 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8875 &g_em->src_x, &g_em->src_y, FALSE);
8878 void InitGraphicInfo_EM(void)
8882 // always start with reliable default values
8883 for (i = 0; i < GAME_TILE_MAX; i++)
8885 object_mapping[i].element_rnd = EL_UNKNOWN;
8886 object_mapping[i].is_backside = FALSE;
8887 object_mapping[i].action = ACTION_DEFAULT;
8888 object_mapping[i].direction = MV_NONE;
8891 // always start with reliable default values
8892 for (p = 0; p < MAX_PLAYERS; p++)
8894 for (i = 0; i < PLY_MAX; i++)
8896 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8897 player_mapping[p][i].action = ACTION_DEFAULT;
8898 player_mapping[p][i].direction = MV_NONE;
8902 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8904 int e = em_object_mapping_list[i].element_em;
8906 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8907 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8909 if (em_object_mapping_list[i].action != -1)
8910 object_mapping[e].action = em_object_mapping_list[i].action;
8912 if (em_object_mapping_list[i].direction != -1)
8913 object_mapping[e].direction =
8914 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8917 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8919 int a = em_player_mapping_list[i].action_em;
8920 int p = em_player_mapping_list[i].player_nr;
8922 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8924 if (em_player_mapping_list[i].action != -1)
8925 player_mapping[p][a].action = em_player_mapping_list[i].action;
8927 if (em_player_mapping_list[i].direction != -1)
8928 player_mapping[p][a].direction =
8929 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8932 for (i = 0; i < GAME_TILE_MAX; i++)
8934 int element = object_mapping[i].element_rnd;
8935 int action = object_mapping[i].action;
8936 int direction = object_mapping[i].direction;
8937 boolean is_backside = object_mapping[i].is_backside;
8938 boolean action_exploding = ((action == ACTION_EXPLODING ||
8939 action == ACTION_SMASHED_BY_ROCK ||
8940 action == ACTION_SMASHED_BY_SPRING) &&
8941 element != EL_DIAMOND);
8942 boolean action_active = (action == ACTION_ACTIVE);
8943 boolean action_other = (action == ACTION_OTHER);
8945 for (j = 0; j < 8; j++)
8947 int effective_element = get_effective_element_EM(i, j);
8948 int effective_action = (j < 7 ? action :
8949 i == Xdrip_stretch ? action :
8950 i == Xdrip_stretchB ? action :
8951 i == Ydrip_1_s ? action :
8952 i == Ydrip_1_sB ? action :
8953 i == Yball_1 ? action :
8954 i == Xball_2 ? action :
8955 i == Yball_2 ? action :
8956 i == Yball_blank ? action :
8957 i == Ykey_1_blank ? action :
8958 i == Ykey_2_blank ? action :
8959 i == Ykey_3_blank ? action :
8960 i == Ykey_4_blank ? action :
8961 i == Ykey_5_blank ? action :
8962 i == Ykey_6_blank ? action :
8963 i == Ykey_7_blank ? action :
8964 i == Ykey_8_blank ? action :
8965 i == Ylenses_blank ? action :
8966 i == Ymagnify_blank ? action :
8967 i == Ygrass_blank ? action :
8968 i == Ydirt_blank ? action :
8969 i == Xsand_stonein_1 ? action :
8970 i == Xsand_stonein_2 ? action :
8971 i == Xsand_stonein_3 ? action :
8972 i == Xsand_stonein_4 ? action :
8973 i == Xsand_stoneout_1 ? action :
8974 i == Xsand_stoneout_2 ? action :
8975 i == Xboom_android ? ACTION_EXPLODING :
8976 action_exploding ? ACTION_EXPLODING :
8977 action_active ? action :
8978 action_other ? action :
8980 int graphic = (el_act_dir2img(effective_element, effective_action,
8982 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8984 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8985 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8986 boolean has_action_graphics = (graphic != base_graphic);
8987 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8988 struct GraphicInfo *g = &graphic_info[graphic];
8989 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8992 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8993 boolean special_animation = (action != ACTION_DEFAULT &&
8994 g->anim_frames == 3 &&
8995 g->anim_delay == 2 &&
8996 g->anim_mode & ANIM_LINEAR);
8997 int sync_frame = (i == Xdrip_stretch ? 7 :
8998 i == Xdrip_stretchB ? 7 :
8999 i == Ydrip_2_s ? j + 8 :
9000 i == Ydrip_2_sB ? j + 8 :
9009 i == Xfake_acid_1 ? 0 :
9010 i == Xfake_acid_2 ? 10 :
9011 i == Xfake_acid_3 ? 20 :
9012 i == Xfake_acid_4 ? 30 :
9013 i == Xfake_acid_5 ? 40 :
9014 i == Xfake_acid_6 ? 50 :
9015 i == Xfake_acid_7 ? 60 :
9016 i == Xfake_acid_8 ? 70 :
9017 i == Xfake_acid_1_player ? 0 :
9018 i == Xfake_acid_2_player ? 10 :
9019 i == Xfake_acid_3_player ? 20 :
9020 i == Xfake_acid_4_player ? 30 :
9021 i == Xfake_acid_5_player ? 40 :
9022 i == Xfake_acid_6_player ? 50 :
9023 i == Xfake_acid_7_player ? 60 :
9024 i == Xfake_acid_8_player ? 70 :
9026 i == Yball_2 ? j + 8 :
9027 i == Yball_blank ? j + 1 :
9028 i == Ykey_1_blank ? j + 1 :
9029 i == Ykey_2_blank ? j + 1 :
9030 i == Ykey_3_blank ? j + 1 :
9031 i == Ykey_4_blank ? j + 1 :
9032 i == Ykey_5_blank ? j + 1 :
9033 i == Ykey_6_blank ? j + 1 :
9034 i == Ykey_7_blank ? j + 1 :
9035 i == Ykey_8_blank ? j + 1 :
9036 i == Ylenses_blank ? j + 1 :
9037 i == Ymagnify_blank ? j + 1 :
9038 i == Ygrass_blank ? j + 1 :
9039 i == Ydirt_blank ? j + 1 :
9040 i == Xamoeba_1 ? 0 :
9041 i == Xamoeba_2 ? 1 :
9042 i == Xamoeba_3 ? 2 :
9043 i == Xamoeba_4 ? 3 :
9044 i == Xamoeba_5 ? 0 :
9045 i == Xamoeba_6 ? 1 :
9046 i == Xamoeba_7 ? 2 :
9047 i == Xamoeba_8 ? 3 :
9048 i == Xexit_2 ? j + 8 :
9049 i == Xexit_3 ? j + 16 :
9050 i == Xdynamite_1 ? 0 :
9051 i == Xdynamite_2 ? 8 :
9052 i == Xdynamite_3 ? 16 :
9053 i == Xdynamite_4 ? 24 :
9054 i == Xsand_stonein_1 ? j + 1 :
9055 i == Xsand_stonein_2 ? j + 9 :
9056 i == Xsand_stonein_3 ? j + 17 :
9057 i == Xsand_stonein_4 ? j + 25 :
9058 i == Xsand_stoneout_1 && j == 0 ? 0 :
9059 i == Xsand_stoneout_1 && j == 1 ? 0 :
9060 i == Xsand_stoneout_1 && j == 2 ? 1 :
9061 i == Xsand_stoneout_1 && j == 3 ? 2 :
9062 i == Xsand_stoneout_1 && j == 4 ? 2 :
9063 i == Xsand_stoneout_1 && j == 5 ? 3 :
9064 i == Xsand_stoneout_1 && j == 6 ? 4 :
9065 i == Xsand_stoneout_1 && j == 7 ? 4 :
9066 i == Xsand_stoneout_2 && j == 0 ? 5 :
9067 i == Xsand_stoneout_2 && j == 1 ? 6 :
9068 i == Xsand_stoneout_2 && j == 2 ? 7 :
9069 i == Xsand_stoneout_2 && j == 3 ? 8 :
9070 i == Xsand_stoneout_2 && j == 4 ? 9 :
9071 i == Xsand_stoneout_2 && j == 5 ? 11 :
9072 i == Xsand_stoneout_2 && j == 6 ? 13 :
9073 i == Xsand_stoneout_2 && j == 7 ? 15 :
9074 i == Xboom_bug && j == 1 ? 2 :
9075 i == Xboom_bug && j == 2 ? 2 :
9076 i == Xboom_bug && j == 3 ? 4 :
9077 i == Xboom_bug && j == 4 ? 4 :
9078 i == Xboom_bug && j == 5 ? 2 :
9079 i == Xboom_bug && j == 6 ? 2 :
9080 i == Xboom_bug && j == 7 ? 0 :
9081 i == Xboom_tank && j == 1 ? 2 :
9082 i == Xboom_tank && j == 2 ? 2 :
9083 i == Xboom_tank && j == 3 ? 4 :
9084 i == Xboom_tank && j == 4 ? 4 :
9085 i == Xboom_tank && j == 5 ? 2 :
9086 i == Xboom_tank && j == 6 ? 2 :
9087 i == Xboom_tank && j == 7 ? 0 :
9088 i == Xboom_android && j == 7 ? 6 :
9089 i == Xboom_1 && j == 1 ? 2 :
9090 i == Xboom_1 && j == 2 ? 2 :
9091 i == Xboom_1 && j == 3 ? 4 :
9092 i == Xboom_1 && j == 4 ? 4 :
9093 i == Xboom_1 && j == 5 ? 6 :
9094 i == Xboom_1 && j == 6 ? 6 :
9095 i == Xboom_1 && j == 7 ? 8 :
9096 i == Xboom_2 && j == 0 ? 8 :
9097 i == Xboom_2 && j == 1 ? 8 :
9098 i == Xboom_2 && j == 2 ? 10 :
9099 i == Xboom_2 && j == 3 ? 10 :
9100 i == Xboom_2 && j == 4 ? 10 :
9101 i == Xboom_2 && j == 5 ? 12 :
9102 i == Xboom_2 && j == 6 ? 12 :
9103 i == Xboom_2 && j == 7 ? 12 :
9104 special_animation && j == 4 ? 3 :
9105 effective_action != action ? 0 :
9107 int frame = getAnimationFrame(g->anim_frames,
9110 g->anim_start_frame,
9113 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9114 g->double_movement && is_backside);
9116 g_em->bitmap = src_bitmap;
9117 g_em->src_x = src_x;
9118 g_em->src_y = src_y;
9119 g_em->src_offset_x = 0;
9120 g_em->src_offset_y = 0;
9121 g_em->dst_offset_x = 0;
9122 g_em->dst_offset_y = 0;
9123 g_em->width = TILEX;
9124 g_em->height = TILEY;
9126 g_em->preserve_background = FALSE;
9128 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9131 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9132 effective_action == ACTION_MOVING ||
9133 effective_action == ACTION_PUSHING ||
9134 effective_action == ACTION_EATING)) ||
9135 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9136 effective_action == ACTION_EMPTYING)))
9139 (effective_action == ACTION_FALLING ||
9140 effective_action == ACTION_FILLING ||
9141 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9142 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9143 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9144 int num_steps = (i == Ydrip_1_s ? 16 :
9145 i == Ydrip_1_sB ? 16 :
9146 i == Ydrip_2_s ? 16 :
9147 i == Ydrip_2_sB ? 16 :
9148 i == Xsand_stonein_1 ? 32 :
9149 i == Xsand_stonein_2 ? 32 :
9150 i == Xsand_stonein_3 ? 32 :
9151 i == Xsand_stonein_4 ? 32 :
9152 i == Xsand_stoneout_1 ? 16 :
9153 i == Xsand_stoneout_2 ? 16 : 8);
9154 int cx = ABS(dx) * (TILEX / num_steps);
9155 int cy = ABS(dy) * (TILEY / num_steps);
9156 int step_frame = (i == Ydrip_2_s ? j + 8 :
9157 i == Ydrip_2_sB ? j + 8 :
9158 i == Xsand_stonein_2 ? j + 8 :
9159 i == Xsand_stonein_3 ? j + 16 :
9160 i == Xsand_stonein_4 ? j + 24 :
9161 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9162 int step = (is_backside ? step_frame : num_steps - step_frame);
9164 if (is_backside) // tile where movement starts
9166 if (dx < 0 || dy < 0)
9168 g_em->src_offset_x = cx * step;
9169 g_em->src_offset_y = cy * step;
9173 g_em->dst_offset_x = cx * step;
9174 g_em->dst_offset_y = cy * step;
9177 else // tile where movement ends
9179 if (dx < 0 || dy < 0)
9181 g_em->dst_offset_x = cx * step;
9182 g_em->dst_offset_y = cy * step;
9186 g_em->src_offset_x = cx * step;
9187 g_em->src_offset_y = cy * step;
9191 g_em->width = TILEX - cx * step;
9192 g_em->height = TILEY - cy * step;
9195 // create unique graphic identifier to decide if tile must be redrawn
9196 /* bit 31 - 16 (16 bit): EM style graphic
9197 bit 15 - 12 ( 4 bit): EM style frame
9198 bit 11 - 6 ( 6 bit): graphic width
9199 bit 5 - 0 ( 6 bit): graphic height */
9200 g_em->unique_identifier =
9201 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9205 for (i = 0; i < GAME_TILE_MAX; i++)
9207 for (j = 0; j < 8; j++)
9209 int element = object_mapping[i].element_rnd;
9210 int action = object_mapping[i].action;
9211 int direction = object_mapping[i].direction;
9212 boolean is_backside = object_mapping[i].is_backside;
9213 int graphic_action = el_act_dir2img(element, action, direction);
9214 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9216 if ((action == ACTION_SMASHED_BY_ROCK ||
9217 action == ACTION_SMASHED_BY_SPRING ||
9218 action == ACTION_EATING) &&
9219 graphic_action == graphic_default)
9221 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9222 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9223 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9224 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9227 // no separate animation for "smashed by rock" -- use rock instead
9228 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9229 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9231 g_em->bitmap = g_xx->bitmap;
9232 g_em->src_x = g_xx->src_x;
9233 g_em->src_y = g_xx->src_y;
9234 g_em->src_offset_x = g_xx->src_offset_x;
9235 g_em->src_offset_y = g_xx->src_offset_y;
9236 g_em->dst_offset_x = g_xx->dst_offset_x;
9237 g_em->dst_offset_y = g_xx->dst_offset_y;
9238 g_em->width = g_xx->width;
9239 g_em->height = g_xx->height;
9240 g_em->unique_identifier = g_xx->unique_identifier;
9243 g_em->preserve_background = TRUE;
9248 for (p = 0; p < MAX_PLAYERS; p++)
9250 for (i = 0; i < PLY_MAX; i++)
9252 int element = player_mapping[p][i].element_rnd;
9253 int action = player_mapping[p][i].action;
9254 int direction = player_mapping[p][i].direction;
9256 for (j = 0; j < 8; j++)
9258 int effective_element = element;
9259 int effective_action = action;
9260 int graphic = (direction == MV_NONE ?
9261 el_act2img(effective_element, effective_action) :
9262 el_act_dir2img(effective_element, effective_action,
9264 struct GraphicInfo *g = &graphic_info[graphic];
9265 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9269 int frame = getAnimationFrame(g->anim_frames,
9272 g->anim_start_frame,
9275 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9277 g_em->bitmap = src_bitmap;
9278 g_em->src_x = src_x;
9279 g_em->src_y = src_y;
9280 g_em->src_offset_x = 0;
9281 g_em->src_offset_y = 0;
9282 g_em->dst_offset_x = 0;
9283 g_em->dst_offset_y = 0;
9284 g_em->width = TILEX;
9285 g_em->height = TILEY;
9291 static void CheckSaveEngineSnapshot_EM(int frame,
9292 boolean any_player_moving,
9293 boolean any_player_snapping,
9294 boolean any_player_dropping)
9296 if (frame == 7 && !any_player_dropping)
9298 if (!local_player->was_waiting)
9300 if (!CheckSaveEngineSnapshotToList())
9303 local_player->was_waiting = TRUE;
9306 else if (any_player_moving || any_player_snapping || any_player_dropping)
9308 local_player->was_waiting = FALSE;
9312 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9313 boolean murphy_is_dropping)
9315 if (murphy_is_waiting)
9317 if (!local_player->was_waiting)
9319 if (!CheckSaveEngineSnapshotToList())
9322 local_player->was_waiting = TRUE;
9327 local_player->was_waiting = FALSE;
9331 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9332 boolean button_released)
9334 if (button_released)
9336 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9337 CheckSaveEngineSnapshotToList();
9339 else if (element_clicked)
9341 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9342 CheckSaveEngineSnapshotToList();
9344 game.snapshot.changed_action = TRUE;
9348 boolean CheckSingleStepMode_EM(int frame,
9349 boolean any_player_moving,
9350 boolean any_player_snapping,
9351 boolean any_player_dropping)
9353 if (tape.single_step && tape.recording && !tape.pausing)
9354 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9355 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9357 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9358 any_player_snapping, any_player_dropping);
9360 return tape.pausing;
9363 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9364 boolean murphy_is_dropping)
9366 boolean murphy_starts_dropping = FALSE;
9369 for (i = 0; i < MAX_PLAYERS; i++)
9370 if (stored_player[i].force_dropping)
9371 murphy_starts_dropping = TRUE;
9373 if (tape.single_step && tape.recording && !tape.pausing)
9374 if (murphy_is_waiting && !murphy_starts_dropping)
9375 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9377 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9380 void CheckSingleStepMode_MM(boolean element_clicked,
9381 boolean button_released)
9383 if (tape.single_step && tape.recording && !tape.pausing)
9384 if (button_released)
9385 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9387 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9390 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9391 int graphic, int sync_frame)
9393 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9395 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9398 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9400 return (IS_NEXT_FRAME(sync_frame, graphic));
9403 int getGraphicInfo_Delay(int graphic)
9405 return graphic_info[graphic].anim_delay;
9408 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9410 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9413 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9419 void PlayMenuSoundExt(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);
9434 void PlayMenuSound(void)
9436 PlayMenuSoundExt(menu.sound[game_status]);
9439 void PlayMenuSoundStereo(int sound, int stereo_position)
9441 if (sound == SND_UNDEFINED)
9444 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9445 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9448 if (IS_LOOP_SOUND(sound))
9449 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9451 PlaySoundStereo(sound, stereo_position);
9454 void PlayMenuSoundIfLoopExt(int sound)
9456 if (sound == SND_UNDEFINED)
9459 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9460 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9463 if (IS_LOOP_SOUND(sound))
9464 PlaySoundLoop(sound);
9467 void PlayMenuSoundIfLoop(void)
9469 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9472 void PlayMenuMusicExt(int music)
9474 if (music == MUS_UNDEFINED)
9477 if (!setup.sound_music)
9480 if (IS_LOOP_MUSIC(music))
9481 PlayMusicLoop(music);
9486 void PlayMenuMusic(void)
9488 char *curr_music = getCurrentlyPlayingMusicFilename();
9489 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9491 if (!strEqual(curr_music, next_music))
9492 PlayMenuMusicExt(menu.music[game_status]);
9495 void PlayMenuSoundsAndMusic(void)
9501 static void FadeMenuSounds(void)
9506 static void FadeMenuMusic(void)
9508 char *curr_music = getCurrentlyPlayingMusicFilename();
9509 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9511 if (!strEqual(curr_music, next_music))
9515 void FadeMenuSoundsAndMusic(void)
9521 void PlaySoundActivating(void)
9524 PlaySound(SND_MENU_ITEM_ACTIVATING);
9528 void PlaySoundSelecting(void)
9531 PlaySound(SND_MENU_ITEM_SELECTING);
9535 void ToggleFullscreenIfNeeded(void)
9537 // if setup and video fullscreen state are already matching, nothing do do
9538 if (setup.fullscreen == video.fullscreen_enabled ||
9539 !video.fullscreen_available)
9542 SDLSetWindowFullscreen(setup.fullscreen);
9544 // set setup value according to successfully changed fullscreen mode
9545 setup.fullscreen = video.fullscreen_enabled;
9548 void ChangeWindowScalingIfNeeded(void)
9550 // if setup and video window scaling are already matching, nothing do do
9551 if (setup.window_scaling_percent == video.window_scaling_percent ||
9552 video.fullscreen_enabled)
9555 SDLSetWindowScaling(setup.window_scaling_percent);
9557 // set setup value according to successfully changed window scaling
9558 setup.window_scaling_percent = video.window_scaling_percent;
9561 void ChangeVsyncModeIfNeeded(void)
9563 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9564 int video_vsync_mode = video.vsync_mode;
9566 // if setup and video vsync mode are already matching, nothing do do
9567 if (setup_vsync_mode == video_vsync_mode)
9570 // if renderer is using OpenGL, vsync mode can directly be changed
9571 SDLSetScreenVsyncMode(setup.vsync_mode);
9573 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9574 if (video.vsync_mode == video_vsync_mode)
9576 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9578 // save backbuffer content which gets lost when re-creating screen
9579 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9581 // force re-creating screen and renderer to set new vsync mode
9582 video.fullscreen_enabled = !setup.fullscreen;
9584 // when creating new renderer, destroy textures linked to old renderer
9585 FreeAllImageTextures(); // needs old renderer to free the textures
9587 // re-create screen and renderer (including change of vsync mode)
9588 ChangeVideoModeIfNeeded(setup.fullscreen);
9590 // set setup value according to successfully changed fullscreen mode
9591 setup.fullscreen = video.fullscreen_enabled;
9593 // restore backbuffer content from temporary backbuffer backup bitmap
9594 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9595 FreeBitmap(tmp_backbuffer);
9597 // update visible window/screen
9598 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9600 // when changing vsync mode, re-create textures for new renderer
9601 InitImageTextures();
9604 // set setup value according to successfully changed vsync mode
9605 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9608 static void JoinRectangles(int *x, int *y, int *width, int *height,
9609 int x2, int y2, int width2, int height2)
9611 // do not join with "off-screen" rectangle
9612 if (x2 == -1 || y2 == -1)
9617 *width = MAX(*width, width2);
9618 *height = MAX(*height, height2);
9621 void SetAnimStatus(int anim_status_new)
9623 if (anim_status_new == GAME_MODE_MAIN)
9624 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9625 else if (anim_status_new == GAME_MODE_NAMES)
9626 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9627 else if (anim_status_new == GAME_MODE_SCORES)
9628 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9630 global.anim_status_next = anim_status_new;
9632 // directly set screen modes that are entered without fading
9633 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9634 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9635 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9636 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9637 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9638 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9639 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9640 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9641 global.anim_status = global.anim_status_next;
9644 void SetGameStatus(int game_status_new)
9646 if (game_status_new != game_status)
9647 game_status_last_screen = game_status;
9649 game_status = game_status_new;
9651 SetAnimStatus(game_status_new);
9654 void SetFontStatus(int game_status_new)
9656 static int last_game_status = -1;
9658 if (game_status_new != -1)
9660 // set game status for font use after storing last game status
9661 last_game_status = game_status;
9662 game_status = game_status_new;
9666 // reset game status after font use from last stored game status
9667 game_status = last_game_status;
9671 void ResetFontStatus(void)
9676 void SetLevelSetInfo(char *identifier, int level_nr)
9678 setString(&levelset.identifier, identifier);
9680 levelset.level_nr = level_nr;
9683 boolean CheckIfAllViewportsHaveChanged(void)
9685 // if game status has not changed, viewports have not changed either
9686 if (game_status == game_status_last)
9689 // check if all viewports have changed with current game status
9691 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9692 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9693 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9694 int new_real_sx = vp_playfield->x;
9695 int new_real_sy = vp_playfield->y;
9696 int new_full_sxsize = vp_playfield->width;
9697 int new_full_sysize = vp_playfield->height;
9698 int new_dx = vp_door_1->x;
9699 int new_dy = vp_door_1->y;
9700 int new_dxsize = vp_door_1->width;
9701 int new_dysize = vp_door_1->height;
9702 int new_vx = vp_door_2->x;
9703 int new_vy = vp_door_2->y;
9704 int new_vxsize = vp_door_2->width;
9705 int new_vysize = vp_door_2->height;
9707 boolean playfield_viewport_has_changed =
9708 (new_real_sx != REAL_SX ||
9709 new_real_sy != REAL_SY ||
9710 new_full_sxsize != FULL_SXSIZE ||
9711 new_full_sysize != FULL_SYSIZE);
9713 boolean door_1_viewport_has_changed =
9716 new_dxsize != DXSIZE ||
9717 new_dysize != DYSIZE);
9719 boolean door_2_viewport_has_changed =
9722 new_vxsize != VXSIZE ||
9723 new_vysize != VYSIZE ||
9724 game_status_last == GAME_MODE_EDITOR);
9726 return (playfield_viewport_has_changed &&
9727 door_1_viewport_has_changed &&
9728 door_2_viewport_has_changed);
9731 boolean CheckFadeAll(void)
9733 return (CheckIfGlobalBorderHasChanged() ||
9734 CheckIfAllViewportsHaveChanged());
9737 void ChangeViewportPropertiesIfNeeded(void)
9739 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9740 FALSE : setup.small_game_graphics);
9741 int gfx_game_mode = getGlobalGameStatus(game_status);
9742 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9744 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9745 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9746 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9747 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9748 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9749 int new_win_xsize = vp_window->width;
9750 int new_win_ysize = vp_window->height;
9751 int border_left = vp_playfield->border_left;
9752 int border_right = vp_playfield->border_right;
9753 int border_top = vp_playfield->border_top;
9754 int border_bottom = vp_playfield->border_bottom;
9755 int new_sx = vp_playfield->x + border_left;
9756 int new_sy = vp_playfield->y + border_top;
9757 int new_sxsize = vp_playfield->width - border_left - border_right;
9758 int new_sysize = vp_playfield->height - border_top - border_bottom;
9759 int new_real_sx = vp_playfield->x;
9760 int new_real_sy = vp_playfield->y;
9761 int new_full_sxsize = vp_playfield->width;
9762 int new_full_sysize = vp_playfield->height;
9763 int new_dx = vp_door_1->x;
9764 int new_dy = vp_door_1->y;
9765 int new_dxsize = vp_door_1->width;
9766 int new_dysize = vp_door_1->height;
9767 int new_vx = vp_door_2->x;
9768 int new_vy = vp_door_2->y;
9769 int new_vxsize = vp_door_2->width;
9770 int new_vysize = vp_door_2->height;
9771 int new_ex = vp_door_3->x;
9772 int new_ey = vp_door_3->y;
9773 int new_exsize = vp_door_3->width;
9774 int new_eysize = vp_door_3->height;
9775 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9776 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9777 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9778 int new_scr_fieldx = new_sxsize / tilesize;
9779 int new_scr_fieldy = new_sysize / tilesize;
9780 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9781 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9782 boolean init_gfx_buffers = FALSE;
9783 boolean init_video_buffer = FALSE;
9784 boolean init_gadgets_and_anims = FALSE;
9785 boolean init_em_graphics = FALSE;
9787 if (new_win_xsize != WIN_XSIZE ||
9788 new_win_ysize != WIN_YSIZE)
9790 WIN_XSIZE = new_win_xsize;
9791 WIN_YSIZE = new_win_ysize;
9793 init_video_buffer = TRUE;
9794 init_gfx_buffers = TRUE;
9795 init_gadgets_and_anims = TRUE;
9797 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9800 if (new_scr_fieldx != SCR_FIELDX ||
9801 new_scr_fieldy != SCR_FIELDY)
9803 // this always toggles between MAIN and GAME when using small tile size
9805 SCR_FIELDX = new_scr_fieldx;
9806 SCR_FIELDY = new_scr_fieldy;
9808 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9819 new_sxsize != SXSIZE ||
9820 new_sysize != SYSIZE ||
9821 new_dxsize != DXSIZE ||
9822 new_dysize != DYSIZE ||
9823 new_vxsize != VXSIZE ||
9824 new_vysize != VYSIZE ||
9825 new_exsize != EXSIZE ||
9826 new_eysize != EYSIZE ||
9827 new_real_sx != REAL_SX ||
9828 new_real_sy != REAL_SY ||
9829 new_full_sxsize != FULL_SXSIZE ||
9830 new_full_sysize != FULL_SYSIZE ||
9831 new_tilesize_var != TILESIZE_VAR
9834 // ------------------------------------------------------------------------
9835 // determine next fading area for changed viewport definitions
9836 // ------------------------------------------------------------------------
9838 // start with current playfield area (default fading area)
9841 FADE_SXSIZE = FULL_SXSIZE;
9842 FADE_SYSIZE = FULL_SYSIZE;
9844 // add new playfield area if position or size has changed
9845 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9846 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9848 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9849 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9852 // add current and new door 1 area if position or size has changed
9853 if (new_dx != DX || new_dy != DY ||
9854 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9856 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9857 DX, DY, DXSIZE, DYSIZE);
9858 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9859 new_dx, new_dy, new_dxsize, new_dysize);
9862 // add current and new door 2 area if position or size has changed
9863 if (new_vx != VX || new_vy != VY ||
9864 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9866 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9867 VX, VY, VXSIZE, VYSIZE);
9868 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9869 new_vx, new_vy, new_vxsize, new_vysize);
9872 // ------------------------------------------------------------------------
9873 // handle changed tile size
9874 // ------------------------------------------------------------------------
9876 if (new_tilesize_var != TILESIZE_VAR)
9878 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9880 // changing tile size invalidates scroll values of engine snapshots
9881 FreeEngineSnapshotSingle();
9883 // changing tile size requires update of graphic mapping for EM engine
9884 init_em_graphics = TRUE;
9895 SXSIZE = new_sxsize;
9896 SYSIZE = new_sysize;
9897 DXSIZE = new_dxsize;
9898 DYSIZE = new_dysize;
9899 VXSIZE = new_vxsize;
9900 VYSIZE = new_vysize;
9901 EXSIZE = new_exsize;
9902 EYSIZE = new_eysize;
9903 REAL_SX = new_real_sx;
9904 REAL_SY = new_real_sy;
9905 FULL_SXSIZE = new_full_sxsize;
9906 FULL_SYSIZE = new_full_sysize;
9907 TILESIZE_VAR = new_tilesize_var;
9909 init_gfx_buffers = TRUE;
9910 init_gadgets_and_anims = TRUE;
9912 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9913 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9916 if (init_gfx_buffers)
9918 // Debug("tools:viewport", "init_gfx_buffers");
9920 SCR_FIELDX = new_scr_fieldx_buffers;
9921 SCR_FIELDY = new_scr_fieldy_buffers;
9925 SCR_FIELDX = new_scr_fieldx;
9926 SCR_FIELDY = new_scr_fieldy;
9928 SetDrawDeactivationMask(REDRAW_NONE);
9929 SetDrawBackgroundMask(REDRAW_FIELD);
9932 if (init_video_buffer)
9934 // Debug("tools:viewport", "init_video_buffer");
9936 FreeAllImageTextures(); // needs old renderer to free the textures
9938 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9939 InitImageTextures();
9942 if (init_gadgets_and_anims)
9944 // Debug("tools:viewport", "init_gadgets_and_anims");
9947 InitGlobalAnimations();
9950 if (init_em_graphics)
9952 InitGraphicInfo_EM();
9956 void OpenURL(char *url)
9958 #if SDL_VERSION_ATLEAST(2,0,14)
9961 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9962 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9963 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9967 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9969 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9973 // ============================================================================
9975 // ============================================================================
9977 #if defined(PLATFORM_WINDOWS)
9978 /* FILETIME of Jan 1 1970 00:00:00. */
9979 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9982 * timezone information is stored outside the kernel so tzp isn't used anymore.
9984 * Note: this function is not for Win32 high precision timing purpose. See
9987 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9990 SYSTEMTIME system_time;
9991 ULARGE_INTEGER ularge;
9993 GetSystemTime(&system_time);
9994 SystemTimeToFileTime(&system_time, &file_time);
9995 ularge.LowPart = file_time.dwLowDateTime;
9996 ularge.HighPart = file_time.dwHighDateTime;
9998 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9999 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10005 static char *test_init_uuid_random_function_simple(void)
10007 static char seed_text[100];
10008 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10010 sprintf(seed_text, "%d", seed);
10015 static char *test_init_uuid_random_function_better(void)
10017 static char seed_text[100];
10018 struct timeval current_time;
10020 gettimeofday(¤t_time, NULL);
10022 prng_seed_bytes(¤t_time, sizeof(current_time));
10024 sprintf(seed_text, "%ld.%ld",
10025 (long)current_time.tv_sec,
10026 (long)current_time.tv_usec);
10031 #if defined(PLATFORM_WINDOWS)
10032 static char *test_init_uuid_random_function_better_windows(void)
10034 static char seed_text[100];
10035 struct timeval current_time;
10037 gettimeofday_windows(¤t_time, NULL);
10039 prng_seed_bytes(¤t_time, sizeof(current_time));
10041 sprintf(seed_text, "%ld.%ld",
10042 (long)current_time.tv_sec,
10043 (long)current_time.tv_usec);
10049 static unsigned int test_uuid_random_function_simple(int max)
10051 return GetSimpleRandom(max);
10054 static unsigned int test_uuid_random_function_better(int max)
10056 return (max > 0 ? prng_get_uint() % max : 0);
10059 #if defined(PLATFORM_WINDOWS)
10060 #define NUM_UUID_TESTS 3
10062 #define NUM_UUID_TESTS 2
10065 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10067 struct hashtable *hash_seeds =
10068 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10069 struct hashtable *hash_uuids =
10070 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10071 static char message[100];
10074 char *random_name = (nr == 0 ? "simple" : "better");
10075 char *random_type = (always_seed ? "always" : "only once");
10076 char *(*init_random_function)(void) =
10078 test_init_uuid_random_function_simple :
10079 test_init_uuid_random_function_better);
10080 unsigned int (*random_function)(int) =
10082 test_uuid_random_function_simple :
10083 test_uuid_random_function_better);
10086 #if defined(PLATFORM_WINDOWS)
10089 random_name = "windows";
10090 init_random_function = test_init_uuid_random_function_better_windows;
10096 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10097 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10099 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10100 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10101 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10103 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10107 // always initialize random number generator at least once
10108 init_random_function();
10110 unsigned int time_start = SDL_GetTicks();
10112 for (i = 0; i < num_uuids; i++)
10116 char *seed = getStringCopy(init_random_function());
10118 hashtable_remove(hash_seeds, seed);
10119 hashtable_insert(hash_seeds, seed, "1");
10122 char *uuid = getStringCopy(getUUIDExt(random_function));
10124 hashtable_remove(hash_uuids, uuid);
10125 hashtable_insert(hash_uuids, uuid, "1");
10128 int num_unique_seeds = hashtable_count(hash_seeds);
10129 int num_unique_uuids = hashtable_count(hash_uuids);
10131 unsigned int time_needed = SDL_GetTicks() - time_start;
10133 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10135 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10138 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10140 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10141 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10143 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10145 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10147 Request(message, REQ_CONFIRM);
10149 hashtable_destroy(hash_seeds, 0);
10150 hashtable_destroy(hash_uuids, 0);
10153 void TestGeneratingUUIDs(void)
10155 int num_uuids = 1000000;
10158 for (i = 0; i < NUM_UUID_TESTS; i++)
10159 for (j = 0; j < 2; j++)
10160 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10162 CloseAllAndExit(0);