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 if (maxWordLengthInRequestString(text) > line_length)
3181 font_nr = FONT_REQUEST_NARROW;
3182 font_width = getFontWidth(font_nr);
3183 line_length = max_text_width / font_width;
3186 text_door_style = checked_malloc(2 * strlen(text) + 1);
3188 src_text_ptr = text;
3189 dst_text_ptr = text_door_style;
3191 while (*src_text_ptr)
3193 if (*src_text_ptr == ' ' ||
3194 *src_text_ptr == '?' ||
3195 *src_text_ptr == '!')
3196 *dst_text_ptr++ = '\n';
3198 if (*src_text_ptr != ' ')
3199 *dst_text_ptr++ = *src_text_ptr;
3204 *dst_text_ptr = '\0';
3206 text_final = text_door_style;
3209 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3210 line_length, -1, max_lines, line_spacing, mask_mode,
3211 request.autowrap, request.centered, FALSE);
3213 if (text_door_style)
3214 free(text_door_style);
3219 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3221 DrawBuffer *drawto_last = drawto;
3222 int graphic = IMG_BACKGROUND_REQUEST;
3223 int width = request.width;
3224 int height = request.height;
3225 int tile_size = MAX(request.step_offset, 1);
3226 int x_steps = width / tile_size;
3227 int y_steps = height / tile_size;
3231 setRequestPosition(&sx, &sy, FALSE);
3233 // draw complete envelope request to temporary bitmap
3234 drawto = bitmap_db_store_1;
3236 ClearRectangle(drawto, sx, sy, width, height);
3238 for (y = 0; y < y_steps; y++)
3239 for (x = 0; x < x_steps; x++)
3240 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3241 x, y, x_steps, y_steps,
3242 tile_size, tile_size);
3244 // write text for request
3245 DrawEnvelopeRequestText(sx, sy, text);
3247 MapToolButtons(req_state);
3249 // restore pointer to drawing buffer
3250 drawto = drawto_last;
3252 // prepare complete envelope request from temporary bitmap
3253 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3256 static void AnimateEnvelopeRequest(int anim_mode, int action)
3258 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3259 int delay_value_normal = request.step_delay;
3260 int delay_value_fast = delay_value_normal / 2;
3261 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3262 boolean no_delay = (tape.warp_forward);
3263 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3264 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3265 DelayCounter anim_delay = { anim_delay_value };
3267 int tile_size = MAX(request.step_offset, 1);
3268 int max_xsize = request.width / tile_size;
3269 int max_ysize = request.height / tile_size;
3270 int max_xsize_inner = max_xsize - 2;
3271 int max_ysize_inner = max_ysize - 2;
3273 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3274 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3275 int xend = max_xsize_inner;
3276 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3277 int xstep = (xstart < xend ? 1 : 0);
3278 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3280 int end = MAX(xend - xstart, yend - ystart);
3283 if (setup.quick_doors)
3290 for (i = start; i <= end; i++)
3292 int last_frame = end; // last frame of this "for" loop
3293 int x = xstart + i * xstep;
3294 int y = ystart + i * ystep;
3295 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3296 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3297 int xsize_size_left = (xsize - 1) * tile_size;
3298 int ysize_size_top = (ysize - 1) * tile_size;
3299 int max_xsize_pos = (max_xsize - 1) * tile_size;
3300 int max_ysize_pos = (max_ysize - 1) * tile_size;
3301 int width = xsize * tile_size;
3302 int height = ysize * tile_size;
3308 HandleGameActions();
3310 setRequestPosition(&src_x, &src_y, FALSE);
3311 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3313 for (yy = 0; yy < 2; yy++)
3315 for (xx = 0; xx < 2; xx++)
3317 int src_xx = src_x + xx * max_xsize_pos;
3318 int src_yy = src_y + yy * max_ysize_pos;
3319 int dst_xx = dst_x + xx * xsize_size_left;
3320 int dst_yy = dst_y + yy * ysize_size_top;
3321 int xx_size = (xx ? tile_size : xsize_size_left);
3322 int yy_size = (yy ? tile_size : ysize_size_top);
3324 // draw partial (animated) envelope request to temporary bitmap
3325 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3326 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3330 // prepare partial (animated) envelope request from temporary bitmap
3331 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3334 redraw_mask |= REDRAW_FIELD;
3338 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3341 ClearAutoRepeatKeyEvents();
3344 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3346 int graphic = IMG_BACKGROUND_REQUEST;
3347 int sound_opening = SND_REQUEST_OPENING;
3348 int sound_closing = SND_REQUEST_CLOSING;
3349 int anim_mode_1 = request.anim_mode; // (higher priority)
3350 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3351 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3352 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3353 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3355 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3357 if (action == ACTION_OPENING)
3359 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3361 if (anim_mode == ANIM_DEFAULT)
3362 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3364 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3368 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3370 if (anim_mode != ANIM_NONE)
3371 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3373 if (anim_mode == ANIM_DEFAULT)
3374 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3377 game.envelope_active = FALSE;
3380 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3382 if (IS_MM_WALL(element))
3384 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3390 int graphic = el2preimg(element);
3392 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3393 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3398 void DrawLevel(int draw_background_mask)
3402 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3403 SetDrawBackgroundMask(draw_background_mask);
3407 for (x = BX1; x <= BX2; x++)
3408 for (y = BY1; y <= BY2; y++)
3409 DrawScreenField(x, y);
3411 redraw_mask |= REDRAW_FIELD;
3414 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3419 for (x = 0; x < size_x; x++)
3420 for (y = 0; y < size_y; y++)
3421 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3423 redraw_mask |= REDRAW_FIELD;
3426 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3430 for (x = 0; x < size_x; x++)
3431 for (y = 0; y < size_y; y++)
3432 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3434 redraw_mask |= REDRAW_FIELD;
3437 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3439 boolean show_level_border = (BorderElement != EL_EMPTY);
3440 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3441 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3442 int tile_size = preview.tile_size;
3443 int preview_width = preview.xsize * tile_size;
3444 int preview_height = preview.ysize * tile_size;
3445 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3446 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3447 int real_preview_width = real_preview_xsize * tile_size;
3448 int real_preview_height = real_preview_ysize * tile_size;
3449 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3450 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3453 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3456 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3458 dst_x += (preview_width - real_preview_width) / 2;
3459 dst_y += (preview_height - real_preview_height) / 2;
3461 for (x = 0; x < real_preview_xsize; x++)
3463 for (y = 0; y < real_preview_ysize; y++)
3465 int lx = from_x + x + (show_level_border ? -1 : 0);
3466 int ly = from_y + y + (show_level_border ? -1 : 0);
3467 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3468 getBorderElement(lx, ly));
3470 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3471 element, tile_size);
3475 redraw_mask |= REDRAW_FIELD;
3478 #define MICROLABEL_EMPTY 0
3479 #define MICROLABEL_LEVEL_NAME 1
3480 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3481 #define MICROLABEL_LEVEL_AUTHOR 3
3482 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3483 #define MICROLABEL_IMPORTED_FROM 5
3484 #define MICROLABEL_IMPORTED_BY_HEAD 6
3485 #define MICROLABEL_IMPORTED_BY 7
3487 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3489 int max_text_width = SXSIZE;
3490 int font_width = getFontWidth(font_nr);
3492 if (pos->align == ALIGN_CENTER)
3493 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3494 else if (pos->align == ALIGN_RIGHT)
3495 max_text_width = pos->x;
3497 max_text_width = SXSIZE - pos->x;
3499 return max_text_width / font_width;
3502 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3504 char label_text[MAX_OUTPUT_LINESIZE + 1];
3505 int max_len_label_text;
3506 int font_nr = pos->font;
3509 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3512 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3513 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3514 mode == MICROLABEL_IMPORTED_BY_HEAD)
3515 font_nr = pos->font_alt;
3517 max_len_label_text = getMaxTextLength(pos, font_nr);
3519 if (pos->size != -1)
3520 max_len_label_text = pos->size;
3522 for (i = 0; i < max_len_label_text; i++)
3523 label_text[i] = ' ';
3524 label_text[max_len_label_text] = '\0';
3526 if (strlen(label_text) > 0)
3527 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3530 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3531 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3532 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3533 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3534 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3535 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3536 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3537 max_len_label_text);
3538 label_text[max_len_label_text] = '\0';
3540 if (strlen(label_text) > 0)
3541 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3543 redraw_mask |= REDRAW_FIELD;
3546 static void DrawPreviewLevelLabel(int mode)
3548 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3551 static void DrawPreviewLevelInfo(int mode)
3553 if (mode == MICROLABEL_LEVEL_NAME)
3554 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3555 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3556 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3559 static void DrawPreviewLevelExt(boolean restart)
3561 static DelayCounter scroll_delay = { 0 };
3562 static DelayCounter label_delay = { 0 };
3563 static int from_x, from_y, scroll_direction;
3564 static int label_state, label_counter;
3565 boolean show_level_border = (BorderElement != EL_EMPTY);
3566 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3567 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3569 scroll_delay.value = preview.step_delay;
3570 label_delay.value = MICROLEVEL_LABEL_DELAY;
3577 if (preview.anim_mode == ANIM_CENTERED)
3579 if (level_xsize > preview.xsize)
3580 from_x = (level_xsize - preview.xsize) / 2;
3581 if (level_ysize > preview.ysize)
3582 from_y = (level_ysize - preview.ysize) / 2;
3585 from_x += preview.xoffset;
3586 from_y += preview.yoffset;
3588 scroll_direction = MV_RIGHT;
3592 DrawPreviewLevelPlayfield(from_x, from_y);
3593 DrawPreviewLevelLabel(label_state);
3595 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3596 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3598 // initialize delay counters
3599 ResetDelayCounter(&scroll_delay);
3600 ResetDelayCounter(&label_delay);
3602 if (leveldir_current->name)
3604 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3605 char label_text[MAX_OUTPUT_LINESIZE + 1];
3606 int font_nr = pos->font;
3607 int max_len_label_text = getMaxTextLength(pos, font_nr);
3609 if (pos->size != -1)
3610 max_len_label_text = pos->size;
3612 strncpy(label_text, leveldir_current->name, max_len_label_text);
3613 label_text[max_len_label_text] = '\0';
3615 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3616 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3622 // scroll preview level, if needed
3623 if (preview.anim_mode != ANIM_NONE &&
3624 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3625 DelayReached(&scroll_delay))
3627 switch (scroll_direction)
3632 from_x -= preview.step_offset;
3633 from_x = (from_x < 0 ? 0 : from_x);
3636 scroll_direction = MV_UP;
3640 if (from_x < level_xsize - preview.xsize)
3642 from_x += preview.step_offset;
3643 from_x = (from_x > level_xsize - preview.xsize ?
3644 level_xsize - preview.xsize : from_x);
3647 scroll_direction = MV_DOWN;
3653 from_y -= preview.step_offset;
3654 from_y = (from_y < 0 ? 0 : from_y);
3657 scroll_direction = MV_RIGHT;
3661 if (from_y < level_ysize - preview.ysize)
3663 from_y += preview.step_offset;
3664 from_y = (from_y > level_ysize - preview.ysize ?
3665 level_ysize - preview.ysize : from_y);
3668 scroll_direction = MV_LEFT;
3675 DrawPreviewLevelPlayfield(from_x, from_y);
3678 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3679 // redraw micro level label, if needed
3680 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3681 !strEqual(level.author, ANONYMOUS_NAME) &&
3682 !strEqual(level.author, leveldir_current->name) &&
3683 DelayReached(&label_delay))
3685 int max_label_counter = 23;
3687 if (leveldir_current->imported_from != NULL &&
3688 strlen(leveldir_current->imported_from) > 0)
3689 max_label_counter += 14;
3690 if (leveldir_current->imported_by != NULL &&
3691 strlen(leveldir_current->imported_by) > 0)
3692 max_label_counter += 14;
3694 label_counter = (label_counter + 1) % max_label_counter;
3695 label_state = (label_counter >= 0 && label_counter <= 7 ?
3696 MICROLABEL_LEVEL_NAME :
3697 label_counter >= 9 && label_counter <= 12 ?
3698 MICROLABEL_LEVEL_AUTHOR_HEAD :
3699 label_counter >= 14 && label_counter <= 21 ?
3700 MICROLABEL_LEVEL_AUTHOR :
3701 label_counter >= 23 && label_counter <= 26 ?
3702 MICROLABEL_IMPORTED_FROM_HEAD :
3703 label_counter >= 28 && label_counter <= 35 ?
3704 MICROLABEL_IMPORTED_FROM :
3705 label_counter >= 37 && label_counter <= 40 ?
3706 MICROLABEL_IMPORTED_BY_HEAD :
3707 label_counter >= 42 && label_counter <= 49 ?
3708 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3710 if (leveldir_current->imported_from == NULL &&
3711 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3712 label_state == MICROLABEL_IMPORTED_FROM))
3713 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3714 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3716 DrawPreviewLevelLabel(label_state);
3720 void DrawPreviewPlayers(void)
3722 if (game_status != GAME_MODE_MAIN)
3725 // do not draw preview players if level preview redefined, but players aren't
3726 if (preview.redefined && !menu.main.preview_players.redefined)
3729 boolean player_found[MAX_PLAYERS];
3730 int num_players = 0;
3733 for (i = 0; i < MAX_PLAYERS; i++)
3734 player_found[i] = FALSE;
3736 // check which players can be found in the level (simple approach)
3737 for (x = 0; x < lev_fieldx; x++)
3739 for (y = 0; y < lev_fieldy; y++)
3741 int element = level.field[x][y];
3743 if (IS_PLAYER_ELEMENT(element))
3745 int player_nr = GET_PLAYER_NR(element);
3747 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3749 if (!player_found[player_nr])
3752 player_found[player_nr] = TRUE;
3757 struct TextPosInfo *pos = &menu.main.preview_players;
3758 int tile_size = pos->tile_size;
3759 int border_size = pos->border_size;
3760 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3761 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3762 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3763 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3764 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3765 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3766 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3767 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3768 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3769 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3770 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3771 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3773 // clear area in which the players will be drawn
3774 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3775 max_players_width, max_players_height);
3777 if (!network.enabled && !setup.team_mode)
3780 // only draw players if level is suited for team mode
3781 if (num_players < 2)
3784 // draw all players that were found in the level
3785 for (i = 0; i < MAX_PLAYERS; i++)
3787 if (player_found[i])
3789 int graphic = el2img(EL_PLAYER_1 + i);
3791 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3793 xpos += player_xoffset;
3794 ypos += player_yoffset;
3799 void DrawPreviewLevelInitial(void)
3801 DrawPreviewLevelExt(TRUE);
3802 DrawPreviewPlayers();
3805 void DrawPreviewLevelAnimation(void)
3807 DrawPreviewLevelExt(FALSE);
3810 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3811 int border_size, int font_nr)
3813 int graphic = el2img(EL_PLAYER_1 + player_nr);
3814 int font_height = getFontHeight(font_nr);
3815 int player_height = MAX(tile_size, font_height);
3816 int xoffset_text = tile_size + border_size;
3817 int yoffset_text = (player_height - font_height) / 2;
3818 int yoffset_graphic = (player_height - tile_size) / 2;
3819 char *player_name = getNetworkPlayerName(player_nr + 1);
3821 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3823 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3826 static void DrawNetworkPlayersExt(boolean force)
3828 if (game_status != GAME_MODE_MAIN)
3831 if (!network.connected && !force)
3834 // do not draw network players if level preview redefined, but players aren't
3835 if (preview.redefined && !menu.main.network_players.redefined)
3838 int num_players = 0;
3841 for (i = 0; i < MAX_PLAYERS; i++)
3842 if (stored_player[i].connected_network)
3845 struct TextPosInfo *pos = &menu.main.network_players;
3846 int tile_size = pos->tile_size;
3847 int border_size = pos->border_size;
3848 int xoffset_text = tile_size + border_size;
3849 int font_nr = pos->font;
3850 int font_width = getFontWidth(font_nr);
3851 int font_height = getFontHeight(font_nr);
3852 int player_height = MAX(tile_size, font_height);
3853 int player_yoffset = player_height + border_size;
3854 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3855 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3856 int all_players_height = num_players * player_yoffset - border_size;
3857 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3858 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3859 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3861 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3862 max_players_width, max_players_height);
3864 // first draw local network player ...
3865 for (i = 0; i < MAX_PLAYERS; i++)
3867 if (stored_player[i].connected_network &&
3868 stored_player[i].connected_locally)
3870 char *player_name = getNetworkPlayerName(i + 1);
3871 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3872 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3874 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3876 ypos += player_yoffset;
3880 // ... then draw all other network players
3881 for (i = 0; i < MAX_PLAYERS; i++)
3883 if (stored_player[i].connected_network &&
3884 !stored_player[i].connected_locally)
3886 char *player_name = getNetworkPlayerName(i + 1);
3887 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3888 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3890 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3892 ypos += player_yoffset;
3897 void DrawNetworkPlayers(void)
3899 DrawNetworkPlayersExt(FALSE);
3902 void ClearNetworkPlayers(void)
3904 DrawNetworkPlayersExt(TRUE);
3907 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3908 int graphic, int lx, int ly,
3911 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3913 if (mask_mode == USE_MASKING)
3914 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3916 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3919 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3920 int graphic, int sync_frame, int mask_mode)
3922 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3924 if (mask_mode == USE_MASKING)
3925 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3927 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3930 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3931 int graphic, int sync_frame, int tilesize,
3934 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3936 if (mask_mode == USE_MASKING)
3937 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3939 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3942 static void DrawGraphicAnimation(int x, int y, int graphic)
3944 int lx = LEVELX(x), ly = LEVELY(y);
3945 int mask_mode = NO_MASKING;
3947 if (!IN_SCR_FIELD(x, y))
3950 if (game.use_masked_elements)
3952 if (Tile[lx][ly] != EL_EMPTY)
3954 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3956 mask_mode = USE_MASKING;
3960 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3961 graphic, lx, ly, mask_mode);
3963 MarkTileDirty(x, y);
3966 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3968 int lx = LEVELX(x), ly = LEVELY(y);
3969 int mask_mode = NO_MASKING;
3971 if (!IN_SCR_FIELD(x, y))
3974 if (game.use_masked_elements)
3976 if (Tile[lx][ly] != EL_EMPTY)
3978 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3980 mask_mode = USE_MASKING;
3984 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3985 graphic, lx, ly, mask_mode);
3987 MarkTileDirty(x, y);
3990 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3992 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3995 void DrawLevelElementAnimation(int x, int y, int element)
3997 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3999 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4002 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4004 int sx = SCREENX(x), sy = SCREENY(y);
4006 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4009 if (Tile[x][y] == EL_EMPTY)
4010 graphic = el2img(GfxElementEmpty[x][y]);
4012 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4015 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4018 DrawGraphicAnimation(sx, sy, graphic);
4021 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4022 DrawLevelFieldCrumbled(x, y);
4024 if (GFX_CRUMBLED(Tile[x][y]))
4025 DrawLevelFieldCrumbled(x, y);
4029 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4031 int sx = SCREENX(x), sy = SCREENY(y);
4034 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4037 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4039 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4042 DrawGraphicAnimation(sx, sy, graphic);
4044 if (GFX_CRUMBLED(element))
4045 DrawLevelFieldCrumbled(x, y);
4048 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4050 if (player->use_murphy)
4052 // this works only because currently only one player can be "murphy" ...
4053 static int last_horizontal_dir = MV_LEFT;
4054 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4056 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4057 last_horizontal_dir = move_dir;
4059 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4061 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4063 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4069 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4072 static boolean equalGraphics(int graphic1, int graphic2)
4074 struct GraphicInfo *g1 = &graphic_info[graphic1];
4075 struct GraphicInfo *g2 = &graphic_info[graphic2];
4077 return (g1->bitmap == g2->bitmap &&
4078 g1->src_x == g2->src_x &&
4079 g1->src_y == g2->src_y &&
4080 g1->anim_frames == g2->anim_frames &&
4081 g1->anim_delay == g2->anim_delay &&
4082 g1->anim_mode == g2->anim_mode);
4085 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4089 DRAW_PLAYER_STAGE_INIT = 0,
4090 DRAW_PLAYER_STAGE_LAST_FIELD,
4091 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4092 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4093 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4094 DRAW_PLAYER_STAGE_PLAYER,
4096 DRAW_PLAYER_STAGE_PLAYER,
4097 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4099 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4100 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4102 NUM_DRAW_PLAYER_STAGES
4105 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4107 static int static_last_player_graphic[MAX_PLAYERS];
4108 static int static_last_player_frame[MAX_PLAYERS];
4109 static boolean static_player_is_opaque[MAX_PLAYERS];
4110 static boolean draw_player[MAX_PLAYERS];
4111 int pnr = player->index_nr;
4113 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4115 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4116 static_last_player_frame[pnr] = player->Frame;
4117 static_player_is_opaque[pnr] = FALSE;
4119 draw_player[pnr] = TRUE;
4122 if (!draw_player[pnr])
4126 if (!IN_LEV_FIELD(player->jx, player->jy))
4128 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4129 Debug("draw:DrawPlayerExt", "This should never happen!");
4131 draw_player[pnr] = FALSE;
4137 int last_player_graphic = static_last_player_graphic[pnr];
4138 int last_player_frame = static_last_player_frame[pnr];
4139 boolean player_is_opaque = static_player_is_opaque[pnr];
4141 int jx = player->jx;
4142 int jy = player->jy;
4143 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4144 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4145 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4146 int last_jx = (player->is_moving ? jx - dx : jx);
4147 int last_jy = (player->is_moving ? jy - dy : jy);
4148 int next_jx = jx + dx;
4149 int next_jy = jy + dy;
4150 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4151 int sx = SCREENX(jx);
4152 int sy = SCREENY(jy);
4153 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4154 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4155 int element = Tile[jx][jy];
4156 int last_element = Tile[last_jx][last_jy];
4157 int action = (player->is_pushing ? ACTION_PUSHING :
4158 player->is_digging ? ACTION_DIGGING :
4159 player->is_collecting ? ACTION_COLLECTING :
4160 player->is_moving ? ACTION_MOVING :
4161 player->is_snapping ? ACTION_SNAPPING :
4162 player->is_dropping ? ACTION_DROPPING :
4163 player->is_waiting ? player->action_waiting :
4166 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4168 // ------------------------------------------------------------------------
4169 // initialize drawing the player
4170 // ------------------------------------------------------------------------
4172 draw_player[pnr] = FALSE;
4174 // GfxElement[][] is set to the element the player is digging or collecting;
4175 // remove also for off-screen player if the player is not moving anymore
4176 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4177 GfxElement[jx][jy] = EL_UNDEFINED;
4179 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4182 if (element == EL_EXPLOSION)
4185 InitPlayerGfxAnimation(player, action, move_dir);
4187 draw_player[pnr] = TRUE;
4189 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4191 // ------------------------------------------------------------------------
4192 // draw things in the field the player is leaving, if needed
4193 // ------------------------------------------------------------------------
4195 if (!IN_SCR_FIELD(sx, sy))
4196 draw_player[pnr] = FALSE;
4198 if (!player->is_moving)
4201 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4203 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4205 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 DrawLevelFieldThruMask(last_jx, last_jy);
4212 else if (last_element == EL_DYNAMITE_ACTIVE ||
4213 last_element == EL_EM_DYNAMITE_ACTIVE ||
4214 last_element == EL_SP_DISK_RED_ACTIVE)
4215 DrawDynamite(last_jx, last_jy);
4217 DrawLevelField(last_jx, last_jy);
4219 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4221 // ------------------------------------------------------------------------
4222 // draw things behind the player, if needed
4223 // ------------------------------------------------------------------------
4227 DrawLevelElement(jx, jy, Back[jx][jy]);
4232 if (IS_ACTIVE_BOMB(element))
4234 DrawLevelElement(jx, jy, EL_EMPTY);
4239 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4241 int old_element = GfxElement[jx][jy];
4242 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4243 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4245 if (GFX_CRUMBLED(old_element))
4246 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4248 DrawScreenGraphic(sx, sy, old_graphic, frame);
4250 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4251 static_player_is_opaque[pnr] = TRUE;
4255 GfxElement[jx][jy] = EL_UNDEFINED;
4257 // make sure that pushed elements are drawn with correct frame rate
4258 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4260 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4261 GfxFrame[jx][jy] = player->StepFrame;
4263 DrawLevelField(jx, jy);
4266 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4268 // ------------------------------------------------------------------------
4269 // draw things the player is pushing, if needed
4270 // ------------------------------------------------------------------------
4272 if (!player->is_pushing || !player->is_moving)
4275 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4278 int gfx_frame = GfxFrame[jx][jy];
4280 if (!IS_MOVING(jx, jy)) // push movement already finished
4282 element = Tile[next_jx][next_jy];
4283 gfx_frame = GfxFrame[next_jx][next_jy];
4286 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4287 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4288 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4290 // draw background element under pushed element (like the Sokoban field)
4291 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4293 // this allows transparent pushing animation over non-black background
4296 DrawLevelElement(jx, jy, Back[jx][jy]);
4298 DrawLevelElement(jx, jy, EL_EMPTY);
4301 if (Back[next_jx][next_jy])
4302 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4304 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4306 int px = SCREENX(jx), py = SCREENY(jy);
4307 int pxx = (TILEX - ABS(sxx)) * dx;
4308 int pyy = (TILEY - ABS(syy)) * dy;
4311 // do not draw (EM style) pushing animation when pushing is finished
4312 // (two-tile animations usually do not contain start and end frame)
4313 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4314 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4316 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4318 // masked drawing is needed for EMC style (double) movement graphics
4319 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4320 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4323 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4325 // ------------------------------------------------------------------------
4326 // draw player himself
4327 // ------------------------------------------------------------------------
4329 int graphic = getPlayerGraphic(player, move_dir);
4331 // in the case of changed player action or direction, prevent the current
4332 // animation frame from being restarted for identical animations
4333 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4334 player->Frame = last_player_frame;
4336 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4338 if (player_is_opaque)
4339 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4341 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4343 if (SHIELD_ON(player))
4345 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4346 IMG_SHIELD_NORMAL_ACTIVE);
4347 frame = getGraphicAnimationFrame(graphic, -1);
4349 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4352 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4354 // ------------------------------------------------------------------------
4355 // draw things in front of player (active dynamite or dynabombs)
4356 // ------------------------------------------------------------------------
4358 if (IS_ACTIVE_BOMB(element))
4360 int graphic = el2img(element);
4361 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4363 if (game.emulation == EMU_SUPAPLEX)
4364 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4366 DrawGraphicThruMask(sx, sy, graphic, frame);
4369 if (player_is_moving && last_element == EL_EXPLOSION)
4371 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4372 GfxElement[last_jx][last_jy] : EL_EMPTY);
4373 int graphic = el_act2img(element, ACTION_EXPLODING);
4374 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4375 int phase = ExplodePhase[last_jx][last_jy] - 1;
4376 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4379 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4382 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4384 // ------------------------------------------------------------------------
4385 // draw elements the player is just walking/passing through/under
4386 // ------------------------------------------------------------------------
4388 if (player_is_moving)
4390 // handle the field the player is leaving ...
4391 if (IS_ACCESSIBLE_INSIDE(last_element))
4392 DrawLevelField(last_jx, last_jy);
4393 else if (IS_ACCESSIBLE_UNDER(last_element))
4394 DrawLevelFieldThruMask(last_jx, last_jy);
4397 // do not redraw accessible elements if the player is just pushing them
4398 if (!player_is_moving || !player->is_pushing)
4400 // ... and the field the player is entering
4401 if (IS_ACCESSIBLE_INSIDE(element))
4402 DrawLevelField(jx, jy);
4403 else if (IS_ACCESSIBLE_UNDER(element))
4404 DrawLevelFieldThruMask(jx, jy);
4407 MarkTileDirty(sx, sy);
4411 void DrawPlayer(struct PlayerInfo *player)
4415 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4416 DrawPlayerExt(player, i);
4419 void DrawAllPlayers(void)
4423 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4424 for (j = 0; j < MAX_PLAYERS; j++)
4425 if (stored_player[j].active)
4426 DrawPlayerExt(&stored_player[j], i);
4429 void DrawPlayerField(int x, int y)
4431 if (!IS_PLAYER(x, y))
4434 DrawPlayer(PLAYERINFO(x, y));
4437 // ----------------------------------------------------------------------------
4439 void WaitForEventToContinue(void)
4441 boolean first_wait = TRUE;
4442 boolean still_wait = TRUE;
4444 if (program.headless)
4447 // simulate releasing mouse button over last gadget, if still pressed
4449 HandleGadgets(-1, -1, 0);
4451 button_status = MB_RELEASED;
4454 ClearPlayerAction();
4460 if (NextValidEvent(&event))
4464 case EVENT_BUTTONPRESS:
4465 case EVENT_FINGERPRESS:
4469 case EVENT_BUTTONRELEASE:
4470 case EVENT_FINGERRELEASE:
4471 still_wait = first_wait;
4474 case EVENT_KEYPRESS:
4475 case SDL_CONTROLLERBUTTONDOWN:
4476 case SDL_JOYBUTTONDOWN:
4481 HandleOtherEvents(&event);
4485 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4490 if (!PendingEvent())
4495 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4497 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4498 int draw_buffer_last = GetDrawtoField();
4499 int width = request.width;
4500 int height = request.height;
4504 setRequestPosition(&sx, &sy, FALSE);
4506 button_status = MB_RELEASED;
4508 request_gadget_id = -1;
4515 SetDrawtoField(draw_buffer_game);
4517 HandleGameActions();
4519 SetDrawtoField(DRAW_TO_BACKBUFFER);
4526 while (NextValidEvent(&event))
4530 case EVENT_BUTTONPRESS:
4531 case EVENT_BUTTONRELEASE:
4532 case EVENT_MOTIONNOTIFY:
4534 DrawBuffer *drawto_last = drawto;
4537 if (event.type == EVENT_MOTIONNOTIFY)
4542 motion_status = TRUE;
4543 mx = ((MotionEvent *) &event)->x;
4544 my = ((MotionEvent *) &event)->y;
4548 motion_status = FALSE;
4549 mx = ((ButtonEvent *) &event)->x;
4550 my = ((ButtonEvent *) &event)->y;
4551 if (event.type == EVENT_BUTTONPRESS)
4552 button_status = ((ButtonEvent *) &event)->button;
4554 button_status = MB_RELEASED;
4557 if (global.use_envelope_request)
4559 // draw changed button states to temporary bitmap
4560 drawto = bitmap_db_store_1;
4563 // this sets 'request_gadget_id'
4564 HandleGadgets(mx, my, button_status);
4566 if (global.use_envelope_request)
4568 // restore pointer to drawing buffer
4569 drawto = drawto_last;
4571 // prepare complete envelope request from temporary bitmap
4572 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4576 switch (request_gadget_id)
4578 case TOOL_CTRL_ID_YES:
4579 case TOOL_CTRL_ID_TOUCH_YES:
4582 case TOOL_CTRL_ID_NO:
4583 case TOOL_CTRL_ID_TOUCH_NO:
4586 case TOOL_CTRL_ID_CONFIRM:
4587 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4588 result = TRUE | FALSE;
4591 case TOOL_CTRL_ID_PLAYER_1:
4594 case TOOL_CTRL_ID_PLAYER_2:
4597 case TOOL_CTRL_ID_PLAYER_3:
4600 case TOOL_CTRL_ID_PLAYER_4:
4605 // only check clickable animations if no request gadget clicked
4606 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4613 case SDL_WINDOWEVENT:
4614 HandleWindowEvent((WindowEvent *) &event);
4617 case SDL_APP_WILLENTERBACKGROUND:
4618 case SDL_APP_DIDENTERBACKGROUND:
4619 case SDL_APP_WILLENTERFOREGROUND:
4620 case SDL_APP_DIDENTERFOREGROUND:
4621 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4624 case EVENT_KEYPRESS:
4626 Key key = GetEventKey((KeyEvent *)&event);
4631 if (req_state & REQ_CONFIRM)
4640 #if defined(KSYM_Rewind)
4641 case KSYM_Rewind: // for Amazon Fire TV remote
4650 #if defined(KSYM_FastForward)
4651 case KSYM_FastForward: // for Amazon Fire TV remote
4657 HandleKeysDebug(key, KEY_PRESSED);
4661 if (req_state & REQ_PLAYER)
4663 int old_player_nr = setup.network_player_nr;
4666 result = old_player_nr + 1;
4671 result = old_player_nr + 1;
4702 case EVENT_FINGERRELEASE:
4703 case EVENT_KEYRELEASE:
4704 ClearPlayerAction();
4707 case SDL_CONTROLLERBUTTONDOWN:
4708 switch (event.cbutton.button)
4710 case SDL_CONTROLLER_BUTTON_A:
4711 case SDL_CONTROLLER_BUTTON_X:
4712 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4713 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4717 case SDL_CONTROLLER_BUTTON_B:
4718 case SDL_CONTROLLER_BUTTON_Y:
4719 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4720 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4721 case SDL_CONTROLLER_BUTTON_BACK:
4726 if (req_state & REQ_PLAYER)
4728 int old_player_nr = setup.network_player_nr;
4731 result = old_player_nr + 1;
4733 switch (event.cbutton.button)
4735 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4736 case SDL_CONTROLLER_BUTTON_Y:
4740 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4741 case SDL_CONTROLLER_BUTTON_B:
4745 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4746 case SDL_CONTROLLER_BUTTON_A:
4750 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4751 case SDL_CONTROLLER_BUTTON_X:
4762 case SDL_CONTROLLERBUTTONUP:
4763 HandleJoystickEvent(&event);
4764 ClearPlayerAction();
4768 HandleOtherEvents(&event);
4773 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4775 int joy = AnyJoystick();
4777 if (joy & JOY_BUTTON_1)
4779 else if (joy & JOY_BUTTON_2)
4782 else if (AnyJoystick())
4784 int joy = AnyJoystick();
4786 if (req_state & REQ_PLAYER)
4790 else if (joy & JOY_RIGHT)
4792 else if (joy & JOY_DOWN)
4794 else if (joy & JOY_LEFT)
4802 SetDrawtoField(draw_buffer_last);
4807 static void DoRequestBefore(void)
4809 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4811 // when showing request dialog after game ended, deactivate game panel
4813 game.panel.active = FALSE;
4815 if (game_status == GAME_MODE_PLAYING)
4816 BlitScreenToBitmap(backbuffer);
4818 // disable deactivated drawing when quick-loading level tape recording
4819 if (tape.playing && tape.deactivate_display)
4820 TapeDeactivateDisplayOff(TRUE);
4822 SetMouseCursor(CURSOR_DEFAULT);
4824 // pause network game while waiting for request to answer
4825 if (network.enabled &&
4826 game_status == GAME_MODE_PLAYING &&
4827 !game.all_players_gone)
4828 SendToServer_PausePlaying();
4830 // simulate releasing mouse button over last gadget, if still pressed
4832 HandleGadgets(-1, -1, 0);
4837 static void DoRequestAfter(void)
4841 if (game_status == GAME_MODE_PLAYING)
4843 SetPanelBackground();
4844 SetDrawBackgroundMask(REDRAW_DOOR_1);
4848 SetDrawBackgroundMask(REDRAW_FIELD);
4851 // continue network game after request
4852 if (network.enabled &&
4853 game_status == GAME_MODE_PLAYING &&
4854 !game.all_players_gone)
4855 SendToServer_ContinuePlaying();
4857 // restore deactivated drawing when quick-loading level tape recording
4858 if (tape.playing && tape.deactivate_display)
4859 TapeDeactivateDisplayOn();
4862 static void setRequestDoorTextProperties(char *text,
4867 int *set_max_line_length)
4869 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4870 struct TextPosInfo *pos = &request.button.confirm;
4871 int button_ypos = pos->y;
4872 int font_nr = FONT_TEXT_2;
4873 int font_width = getFontWidth(font_nr);
4874 int font_height = getFontHeight(font_nr);
4875 int line_height = font_height + line_spacing;
4876 int max_text_width = vp_door_1->width;
4877 int max_text_height = button_ypos - 2 * text_spacing;
4878 int max_line_length = max_text_width / font_width;
4879 int max_lines = max_text_height / line_height;
4881 if (maxWordLengthInRequestString(text) > max_line_length)
4883 font_nr = FONT_TEXT_1;
4884 font_width = getFontWidth(font_nr);
4885 max_line_length = max_text_width / font_width;
4888 *set_font_nr = font_nr;
4889 *set_max_lines = max_lines;
4890 *set_max_line_length = max_line_length;
4893 static void DrawRequestDoorText(char *text)
4895 char *text_ptr = text;
4896 int text_spacing = 8;
4897 int line_spacing = 2;
4898 int max_request_lines;
4899 int max_request_line_len;
4903 // force DOOR font inside door area
4904 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4906 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4907 &max_request_lines, &max_request_line_len);
4909 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4911 char text_line[max_request_line_len + 1];
4917 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4919 tc = *(text_ptr + tx);
4920 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4924 if ((tc == '?' || tc == '!') && tl == 0)
4934 strncpy(text_line, text_ptr, tl);
4937 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4938 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4939 text_line, font_nr);
4941 text_ptr += tl + (tc == ' ' ? 1 : 0);
4947 static int RequestDoor(char *text, unsigned int req_state)
4949 unsigned int old_door_state = GetDoorState();
4950 int draw_buffer_last = GetDrawtoField();
4953 if (old_door_state & DOOR_OPEN_1)
4955 CloseDoor(DOOR_CLOSE_1);
4957 // save old door content
4958 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4959 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4962 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4963 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4965 // clear door drawing field
4966 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4968 // write text for request
4969 DrawRequestDoorText(text);
4971 MapToolButtons(req_state);
4973 // copy request gadgets to door backbuffer
4974 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4976 OpenDoor(DOOR_OPEN_1);
4978 // ---------- handle request buttons ----------
4979 result = RequestHandleEvents(req_state, draw_buffer_last);
4983 if (!(req_state & REQ_STAY_OPEN))
4985 CloseDoor(DOOR_CLOSE_1);
4987 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4988 (req_state & REQ_REOPEN))
4989 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4995 static int RequestEnvelope(char *text, unsigned int req_state)
4997 int draw_buffer_last = GetDrawtoField();
5000 DrawEnvelopeRequest(text, req_state);
5001 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5003 // ---------- handle request buttons ----------
5004 result = RequestHandleEvents(req_state, draw_buffer_last);
5008 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5013 int Request(char *text, unsigned int req_state)
5015 boolean overlay_enabled = GetOverlayEnabled();
5018 game.request_active = TRUE;
5020 SetOverlayEnabled(FALSE);
5024 if (global.use_envelope_request)
5025 result = RequestEnvelope(text, req_state);
5027 result = RequestDoor(text, req_state);
5031 SetOverlayEnabled(overlay_enabled);
5033 game.request_active = FALSE;
5038 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5040 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5041 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5044 if (dpo1->sort_priority != dpo2->sort_priority)
5045 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5047 compare_result = dpo1->nr - dpo2->nr;
5049 return compare_result;
5052 void InitGraphicCompatibilityInfo_Doors(void)
5058 struct DoorInfo *door;
5062 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5063 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5065 { -1, -1, -1, NULL }
5067 struct Rect door_rect_list[] =
5069 { DX, DY, DXSIZE, DYSIZE },
5070 { VX, VY, VXSIZE, VYSIZE }
5074 for (i = 0; doors[i].door_token != -1; i++)
5076 int door_token = doors[i].door_token;
5077 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5078 int part_1 = doors[i].part_1;
5079 int part_8 = doors[i].part_8;
5080 int part_2 = part_1 + 1;
5081 int part_3 = part_1 + 2;
5082 struct DoorInfo *door = doors[i].door;
5083 struct Rect *door_rect = &door_rect_list[door_index];
5084 boolean door_gfx_redefined = FALSE;
5086 // check if any door part graphic definitions have been redefined
5088 for (j = 0; door_part_controls[j].door_token != -1; j++)
5090 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5091 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5093 if (dpc->door_token == door_token && fi->redefined)
5094 door_gfx_redefined = TRUE;
5097 // check for old-style door graphic/animation modifications
5099 if (!door_gfx_redefined)
5101 if (door->anim_mode & ANIM_STATIC_PANEL)
5103 door->panel.step_xoffset = 0;
5104 door->panel.step_yoffset = 0;
5107 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5109 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5110 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5111 int num_door_steps, num_panel_steps;
5113 // remove door part graphics other than the two default wings
5115 for (j = 0; door_part_controls[j].door_token != -1; j++)
5117 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5118 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5120 if (dpc->graphic >= part_3 &&
5121 dpc->graphic <= part_8)
5125 // set graphics and screen positions of the default wings
5127 g_part_1->width = door_rect->width;
5128 g_part_1->height = door_rect->height;
5129 g_part_2->width = door_rect->width;
5130 g_part_2->height = door_rect->height;
5131 g_part_2->src_x = door_rect->width;
5132 g_part_2->src_y = g_part_1->src_y;
5134 door->part_2.x = door->part_1.x;
5135 door->part_2.y = door->part_1.y;
5137 if (door->width != -1)
5139 g_part_1->width = door->width;
5140 g_part_2->width = door->width;
5142 // special treatment for graphics and screen position of right wing
5143 g_part_2->src_x += door_rect->width - door->width;
5144 door->part_2.x += door_rect->width - door->width;
5147 if (door->height != -1)
5149 g_part_1->height = door->height;
5150 g_part_2->height = door->height;
5152 // special treatment for graphics and screen position of bottom wing
5153 g_part_2->src_y += door_rect->height - door->height;
5154 door->part_2.y += door_rect->height - door->height;
5157 // set animation delays for the default wings and panels
5159 door->part_1.step_delay = door->step_delay;
5160 door->part_2.step_delay = door->step_delay;
5161 door->panel.step_delay = door->step_delay;
5163 // set animation draw order for the default wings
5165 door->part_1.sort_priority = 2; // draw left wing over ...
5166 door->part_2.sort_priority = 1; // ... right wing
5168 // set animation draw offset for the default wings
5170 if (door->anim_mode & ANIM_HORIZONTAL)
5172 door->part_1.step_xoffset = door->step_offset;
5173 door->part_1.step_yoffset = 0;
5174 door->part_2.step_xoffset = door->step_offset * -1;
5175 door->part_2.step_yoffset = 0;
5177 num_door_steps = g_part_1->width / door->step_offset;
5179 else // ANIM_VERTICAL
5181 door->part_1.step_xoffset = 0;
5182 door->part_1.step_yoffset = door->step_offset;
5183 door->part_2.step_xoffset = 0;
5184 door->part_2.step_yoffset = door->step_offset * -1;
5186 num_door_steps = g_part_1->height / door->step_offset;
5189 // set animation draw offset for the default panels
5191 if (door->step_offset > 1)
5193 num_panel_steps = 2 * door_rect->height / door->step_offset;
5194 door->panel.start_step = num_panel_steps - num_door_steps;
5195 door->panel.start_step_closing = door->panel.start_step;
5199 num_panel_steps = door_rect->height / door->step_offset;
5200 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5201 door->panel.start_step_closing = door->panel.start_step;
5202 door->panel.step_delay *= 2;
5209 void InitDoors(void)
5213 for (i = 0; door_part_controls[i].door_token != -1; i++)
5215 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5216 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5218 // initialize "start_step_opening" and "start_step_closing", if needed
5219 if (dpc->pos->start_step_opening == 0 &&
5220 dpc->pos->start_step_closing == 0)
5222 // dpc->pos->start_step_opening = dpc->pos->start_step;
5223 dpc->pos->start_step_closing = dpc->pos->start_step;
5226 // fill structure for door part draw order (sorted below)
5228 dpo->sort_priority = dpc->pos->sort_priority;
5231 // sort door part controls according to sort_priority and graphic number
5232 qsort(door_part_order, MAX_DOOR_PARTS,
5233 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5236 unsigned int OpenDoor(unsigned int door_state)
5238 if (door_state & DOOR_COPY_BACK)
5240 if (door_state & DOOR_OPEN_1)
5241 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5242 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5244 if (door_state & DOOR_OPEN_2)
5245 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5246 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5248 door_state &= ~DOOR_COPY_BACK;
5251 return MoveDoor(door_state);
5254 unsigned int CloseDoor(unsigned int door_state)
5256 unsigned int old_door_state = GetDoorState();
5258 if (!(door_state & DOOR_NO_COPY_BACK))
5260 if (old_door_state & DOOR_OPEN_1)
5261 BlitBitmap(backbuffer, bitmap_db_door_1,
5262 DX, DY, DXSIZE, DYSIZE, 0, 0);
5264 if (old_door_state & DOOR_OPEN_2)
5265 BlitBitmap(backbuffer, bitmap_db_door_2,
5266 VX, VY, VXSIZE, VYSIZE, 0, 0);
5268 door_state &= ~DOOR_NO_COPY_BACK;
5271 return MoveDoor(door_state);
5274 unsigned int GetDoorState(void)
5276 return MoveDoor(DOOR_GET_STATE);
5279 unsigned int SetDoorState(unsigned int door_state)
5281 return MoveDoor(door_state | DOOR_SET_STATE);
5284 static int euclid(int a, int b)
5286 return (b ? euclid(b, a % b) : a);
5289 unsigned int MoveDoor(unsigned int door_state)
5291 struct Rect door_rect_list[] =
5293 { DX, DY, DXSIZE, DYSIZE },
5294 { VX, VY, VXSIZE, VYSIZE }
5296 static int door1 = DOOR_CLOSE_1;
5297 static int door2 = DOOR_CLOSE_2;
5298 DelayCounter door_delay = { 0 };
5301 if (door_state == DOOR_GET_STATE)
5302 return (door1 | door2);
5304 if (door_state & DOOR_SET_STATE)
5306 if (door_state & DOOR_ACTION_1)
5307 door1 = door_state & DOOR_ACTION_1;
5308 if (door_state & DOOR_ACTION_2)
5309 door2 = door_state & DOOR_ACTION_2;
5311 return (door1 | door2);
5314 if (!(door_state & DOOR_FORCE_REDRAW))
5316 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5317 door_state &= ~DOOR_OPEN_1;
5318 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5319 door_state &= ~DOOR_CLOSE_1;
5320 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5321 door_state &= ~DOOR_OPEN_2;
5322 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5323 door_state &= ~DOOR_CLOSE_2;
5326 if (global.autoplay_leveldir)
5328 door_state |= DOOR_NO_DELAY;
5329 door_state &= ~DOOR_CLOSE_ALL;
5332 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5333 door_state |= DOOR_NO_DELAY;
5335 if (door_state & DOOR_ACTION)
5337 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5338 boolean door_panel_drawn[NUM_DOORS];
5339 boolean panel_has_doors[NUM_DOORS];
5340 boolean door_part_skip[MAX_DOOR_PARTS];
5341 boolean door_part_done[MAX_DOOR_PARTS];
5342 boolean door_part_done_all;
5343 int num_steps[MAX_DOOR_PARTS];
5344 int max_move_delay = 0; // delay for complete animations of all doors
5345 int max_step_delay = 0; // delay (ms) between two animation frames
5346 int num_move_steps = 0; // number of animation steps for all doors
5347 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5348 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5352 for (i = 0; i < NUM_DOORS; i++)
5353 panel_has_doors[i] = FALSE;
5355 for (i = 0; i < MAX_DOOR_PARTS; i++)
5357 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5358 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5359 int door_token = dpc->door_token;
5361 door_part_done[i] = FALSE;
5362 door_part_skip[i] = (!(door_state & door_token) ||
5366 for (i = 0; i < MAX_DOOR_PARTS; i++)
5368 int nr = door_part_order[i].nr;
5369 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5370 struct DoorPartPosInfo *pos = dpc->pos;
5371 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5372 int door_token = dpc->door_token;
5373 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5374 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5375 int step_xoffset = ABS(pos->step_xoffset);
5376 int step_yoffset = ABS(pos->step_yoffset);
5377 int step_delay = pos->step_delay;
5378 int current_door_state = door_state & door_token;
5379 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5380 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5381 boolean part_opening = (is_panel ? door_closing : door_opening);
5382 int start_step = (part_opening ? pos->start_step_opening :
5383 pos->start_step_closing);
5384 float move_xsize = (step_xoffset ? g->width : 0);
5385 float move_ysize = (step_yoffset ? g->height : 0);
5386 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5387 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5388 int move_steps = (move_xsteps && move_ysteps ?
5389 MIN(move_xsteps, move_ysteps) :
5390 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5391 int move_delay = move_steps * step_delay;
5393 if (door_part_skip[nr])
5396 max_move_delay = MAX(max_move_delay, move_delay);
5397 max_step_delay = (max_step_delay == 0 ? step_delay :
5398 euclid(max_step_delay, step_delay));
5399 num_steps[nr] = move_steps;
5403 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5405 panel_has_doors[door_index] = TRUE;
5409 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5411 num_move_steps = max_move_delay / max_step_delay;
5412 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5414 door_delay.value = max_step_delay;
5416 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5418 start = num_move_steps - 1;
5422 // opening door sound has priority over simultaneously closing door
5423 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5425 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5427 if (door_state & DOOR_OPEN_1)
5428 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5429 if (door_state & DOOR_OPEN_2)
5430 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5432 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5434 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5436 if (door_state & DOOR_CLOSE_1)
5437 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5438 if (door_state & DOOR_CLOSE_2)
5439 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5443 for (k = start; k < num_move_steps; k++)
5445 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5447 door_part_done_all = TRUE;
5449 for (i = 0; i < NUM_DOORS; i++)
5450 door_panel_drawn[i] = FALSE;
5452 for (i = 0; i < MAX_DOOR_PARTS; i++)
5454 int nr = door_part_order[i].nr;
5455 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5456 struct DoorPartPosInfo *pos = dpc->pos;
5457 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5458 int door_token = dpc->door_token;
5459 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5460 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5461 boolean is_panel_and_door_has_closed = FALSE;
5462 struct Rect *door_rect = &door_rect_list[door_index];
5463 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5465 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5466 int current_door_state = door_state & door_token;
5467 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5468 boolean door_closing = !door_opening;
5469 boolean part_opening = (is_panel ? door_closing : door_opening);
5470 boolean part_closing = !part_opening;
5471 int start_step = (part_opening ? pos->start_step_opening :
5472 pos->start_step_closing);
5473 int step_delay = pos->step_delay;
5474 int step_factor = step_delay / max_step_delay;
5475 int k1 = (step_factor ? k / step_factor + 1 : k);
5476 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5477 int kk = MAX(0, k2);
5480 int src_x, src_y, src_xx, src_yy;
5481 int dst_x, dst_y, dst_xx, dst_yy;
5484 if (door_part_skip[nr])
5487 if (!(door_state & door_token))
5495 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5496 int kk_door = MAX(0, k2_door);
5497 int sync_frame = kk_door * door_delay.value;
5498 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5500 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5501 &g_src_x, &g_src_y);
5506 if (!door_panel_drawn[door_index])
5508 ClearRectangle(drawto, door_rect->x, door_rect->y,
5509 door_rect->width, door_rect->height);
5511 door_panel_drawn[door_index] = TRUE;
5514 // draw opening or closing door parts
5516 if (pos->step_xoffset < 0) // door part on right side
5519 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5522 if (dst_xx + width > door_rect->width)
5523 width = door_rect->width - dst_xx;
5525 else // door part on left side
5528 dst_xx = pos->x - kk * pos->step_xoffset;
5532 src_xx = ABS(dst_xx);
5536 width = g->width - src_xx;
5538 if (width > door_rect->width)
5539 width = door_rect->width;
5541 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5544 if (pos->step_yoffset < 0) // door part on bottom side
5547 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5550 if (dst_yy + height > door_rect->height)
5551 height = door_rect->height - dst_yy;
5553 else // door part on top side
5556 dst_yy = pos->y - kk * pos->step_yoffset;
5560 src_yy = ABS(dst_yy);
5564 height = g->height - src_yy;
5567 src_x = g_src_x + src_xx;
5568 src_y = g_src_y + src_yy;
5570 dst_x = door_rect->x + dst_xx;
5571 dst_y = door_rect->y + dst_yy;
5573 is_panel_and_door_has_closed =
5576 panel_has_doors[door_index] &&
5577 k >= num_move_steps_doors_only - 1);
5579 if (width >= 0 && width <= g->width &&
5580 height >= 0 && height <= g->height &&
5581 !is_panel_and_door_has_closed)
5583 if (is_panel || !pos->draw_masked)
5584 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5587 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5591 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5593 if ((part_opening && (width < 0 || height < 0)) ||
5594 (part_closing && (width >= g->width && height >= g->height)))
5595 door_part_done[nr] = TRUE;
5597 // continue door part animations, but not panel after door has closed
5598 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5599 door_part_done_all = FALSE;
5602 if (!(door_state & DOOR_NO_DELAY))
5605 HandleGameActions();
5609 SkipUntilDelayReached(&door_delay, &k, last_frame);
5611 // prevent OS (Windows) from complaining about program not responding
5615 if (door_part_done_all)
5619 if (!(door_state & DOOR_NO_DELAY))
5621 // wait for specified door action post delay
5622 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5623 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5624 else if (door_state & DOOR_ACTION_1)
5625 door_delay.value = door_1.post_delay;
5626 else if (door_state & DOOR_ACTION_2)
5627 door_delay.value = door_2.post_delay;
5629 while (!DelayReached(&door_delay))
5632 HandleGameActions();
5639 if (door_state & DOOR_ACTION_1)
5640 door1 = door_state & DOOR_ACTION_1;
5641 if (door_state & DOOR_ACTION_2)
5642 door2 = door_state & DOOR_ACTION_2;
5644 // draw masked border over door area
5645 DrawMaskedBorder(REDRAW_DOOR_1);
5646 DrawMaskedBorder(REDRAW_DOOR_2);
5648 ClearAutoRepeatKeyEvents();
5650 return (door1 | door2);
5653 static boolean useSpecialEditorDoor(void)
5655 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5656 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5658 // do not draw special editor door if editor border defined or redefined
5659 if (graphic_info[graphic].bitmap != NULL || redefined)
5662 // do not draw special editor door if global border defined to be empty
5663 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5666 // do not draw special editor door if viewport definitions do not match
5670 EY + EYSIZE != VY + VYSIZE)
5676 void DrawSpecialEditorDoor(void)
5678 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5679 int top_border_width = gfx1->width;
5680 int top_border_height = gfx1->height;
5681 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5682 int ex = EX - outer_border;
5683 int ey = EY - outer_border;
5684 int vy = VY - outer_border;
5685 int exsize = EXSIZE + 2 * outer_border;
5687 if (!useSpecialEditorDoor())
5690 // draw bigger level editor toolbox window
5691 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5692 top_border_width, top_border_height, ex, ey - top_border_height);
5693 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5694 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5696 redraw_mask |= REDRAW_ALL;
5699 void UndrawSpecialEditorDoor(void)
5701 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5702 int top_border_width = gfx1->width;
5703 int top_border_height = gfx1->height;
5704 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5705 int ex = EX - outer_border;
5706 int ey = EY - outer_border;
5707 int ey_top = ey - top_border_height;
5708 int exsize = EXSIZE + 2 * outer_border;
5709 int eysize = EYSIZE + 2 * outer_border;
5711 if (!useSpecialEditorDoor())
5714 // draw normal tape recorder window
5715 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5717 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5718 ex, ey_top, top_border_width, top_border_height,
5720 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5721 ex, ey, exsize, eysize, ex, ey);
5725 // if screen background is set to "[NONE]", clear editor toolbox window
5726 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5727 ClearRectangle(drawto, ex, ey, exsize, eysize);
5730 redraw_mask |= REDRAW_ALL;
5734 // ---------- new tool button stuff -------------------------------------------
5739 struct TextPosInfo *pos;
5741 boolean is_touch_button;
5743 } toolbutton_info[NUM_TOOL_BUTTONS] =
5746 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5747 TOOL_CTRL_ID_YES, FALSE, "yes"
5750 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5751 TOOL_CTRL_ID_NO, FALSE, "no"
5754 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5755 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5758 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5759 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5762 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5763 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5766 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5767 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5770 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5771 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5774 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5775 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5778 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5779 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5782 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5783 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5787 void CreateToolButtons(void)
5791 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5793 int graphic = toolbutton_info[i].graphic;
5794 struct GraphicInfo *gfx = &graphic_info[graphic];
5795 struct TextPosInfo *pos = toolbutton_info[i].pos;
5796 struct GadgetInfo *gi;
5797 Bitmap *deco_bitmap = None;
5798 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5799 unsigned int event_mask = GD_EVENT_RELEASED;
5800 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5801 int base_x = (is_touch_button ? 0 : DX);
5802 int base_y = (is_touch_button ? 0 : DY);
5803 int gd_x = gfx->src_x;
5804 int gd_y = gfx->src_y;
5805 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5806 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5811 // do not use touch buttons if overlay touch buttons are disabled
5812 if (is_touch_button && !setup.touch.overlay_buttons)
5815 if (global.use_envelope_request && !is_touch_button)
5817 setRequestPosition(&base_x, &base_y, TRUE);
5819 // check if request buttons are outside of envelope and fix, if needed
5820 if (x < 0 || x + gfx->width > request.width ||
5821 y < 0 || y + gfx->height > request.height)
5823 if (id == TOOL_CTRL_ID_YES)
5826 y = request.height - 2 * request.border_size - gfx->height;
5828 else if (id == TOOL_CTRL_ID_NO)
5830 x = request.width - 2 * request.border_size - gfx->width;
5831 y = request.height - 2 * request.border_size - gfx->height;
5833 else if (id == TOOL_CTRL_ID_CONFIRM)
5835 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5836 y = request.height - 2 * request.border_size - gfx->height;
5838 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5840 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5842 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5843 y = request.height - 2 * request.border_size - gfx->height * 2;
5845 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5846 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5851 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5854 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5856 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5857 pos->size, &deco_bitmap, &deco_x, &deco_y);
5858 deco_xpos = (gfx->width - pos->size) / 2;
5859 deco_ypos = (gfx->height - pos->size) / 2;
5862 gi = CreateGadget(GDI_CUSTOM_ID, id,
5863 GDI_IMAGE_ID, graphic,
5864 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5867 GDI_WIDTH, gfx->width,
5868 GDI_HEIGHT, gfx->height,
5869 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5870 GDI_STATE, GD_BUTTON_UNPRESSED,
5871 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5872 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5873 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5874 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5875 GDI_DECORATION_SIZE, pos->size, pos->size,
5876 GDI_DECORATION_SHIFTING, 1, 1,
5877 GDI_DIRECT_DRAW, FALSE,
5878 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5879 GDI_EVENT_MASK, event_mask,
5880 GDI_CALLBACK_ACTION, HandleToolButtons,
5884 Fail("cannot create gadget");
5886 tool_gadget[id] = gi;
5890 void FreeToolButtons(void)
5894 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5895 FreeGadget(tool_gadget[i]);
5898 static void MapToolButtons(unsigned int req_state)
5900 if (req_state & REQ_ASK)
5902 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5903 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5904 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5905 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5907 else if (req_state & REQ_CONFIRM)
5909 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5910 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5912 else if (req_state & REQ_PLAYER)
5914 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5915 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5916 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5917 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5921 static void UnmapToolButtons(void)
5925 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5926 UnmapGadget(tool_gadget[i]);
5929 static void HandleToolButtons(struct GadgetInfo *gi)
5931 request_gadget_id = gi->custom_id;
5934 static struct Mapping_EM_to_RND_object
5937 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5938 boolean is_backside; // backside of moving element
5944 em_object_mapping_list[GAME_TILE_MAX + 1] =
5947 Zborder, FALSE, FALSE,
5951 Zplayer, FALSE, FALSE,
5960 Ztank, FALSE, FALSE,
5964 Zeater, FALSE, FALSE,
5968 Zdynamite, FALSE, FALSE,
5972 Zboom, FALSE, FALSE,
5977 Xchain, FALSE, FALSE,
5978 EL_DEFAULT, ACTION_EXPLODING, -1
5981 Xboom_bug, FALSE, FALSE,
5982 EL_BUG, ACTION_EXPLODING, -1
5985 Xboom_tank, FALSE, FALSE,
5986 EL_SPACESHIP, ACTION_EXPLODING, -1
5989 Xboom_android, FALSE, FALSE,
5990 EL_EMC_ANDROID, ACTION_OTHER, -1
5993 Xboom_1, FALSE, FALSE,
5994 EL_DEFAULT, ACTION_EXPLODING, -1
5997 Xboom_2, FALSE, FALSE,
5998 EL_DEFAULT, ACTION_EXPLODING, -1
6002 Xblank, TRUE, FALSE,
6007 Xsplash_e, FALSE, FALSE,
6008 EL_ACID_SPLASH_RIGHT, -1, -1
6011 Xsplash_w, FALSE, FALSE,
6012 EL_ACID_SPLASH_LEFT, -1, -1
6016 Xplant, TRUE, FALSE,
6017 EL_EMC_PLANT, -1, -1
6020 Yplant, FALSE, FALSE,
6021 EL_EMC_PLANT, -1, -1
6025 Xacid_1, TRUE, FALSE,
6029 Xacid_2, FALSE, FALSE,
6033 Xacid_3, FALSE, FALSE,
6037 Xacid_4, FALSE, FALSE,
6041 Xacid_5, FALSE, FALSE,
6045 Xacid_6, FALSE, FALSE,
6049 Xacid_7, FALSE, FALSE,
6053 Xacid_8, FALSE, FALSE,
6058 Xfake_acid_1, TRUE, FALSE,
6059 EL_EMC_FAKE_ACID, -1, -1
6062 Xfake_acid_2, FALSE, FALSE,
6063 EL_EMC_FAKE_ACID, -1, -1
6066 Xfake_acid_3, FALSE, FALSE,
6067 EL_EMC_FAKE_ACID, -1, -1
6070 Xfake_acid_4, FALSE, FALSE,
6071 EL_EMC_FAKE_ACID, -1, -1
6074 Xfake_acid_5, FALSE, FALSE,
6075 EL_EMC_FAKE_ACID, -1, -1
6078 Xfake_acid_6, FALSE, FALSE,
6079 EL_EMC_FAKE_ACID, -1, -1
6082 Xfake_acid_7, FALSE, FALSE,
6083 EL_EMC_FAKE_ACID, -1, -1
6086 Xfake_acid_8, FALSE, FALSE,
6087 EL_EMC_FAKE_ACID, -1, -1
6091 Xfake_acid_1_player, FALSE, FALSE,
6092 EL_EMC_FAKE_ACID, -1, -1
6095 Xfake_acid_2_player, FALSE, FALSE,
6096 EL_EMC_FAKE_ACID, -1, -1
6099 Xfake_acid_3_player, FALSE, FALSE,
6100 EL_EMC_FAKE_ACID, -1, -1
6103 Xfake_acid_4_player, FALSE, FALSE,
6104 EL_EMC_FAKE_ACID, -1, -1
6107 Xfake_acid_5_player, FALSE, FALSE,
6108 EL_EMC_FAKE_ACID, -1, -1
6111 Xfake_acid_6_player, FALSE, FALSE,
6112 EL_EMC_FAKE_ACID, -1, -1
6115 Xfake_acid_7_player, FALSE, FALSE,
6116 EL_EMC_FAKE_ACID, -1, -1
6119 Xfake_acid_8_player, FALSE, FALSE,
6120 EL_EMC_FAKE_ACID, -1, -1
6124 Xgrass, TRUE, FALSE,
6125 EL_EMC_GRASS, -1, -1
6128 Ygrass_nB, FALSE, FALSE,
6129 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6132 Ygrass_eB, FALSE, FALSE,
6133 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6136 Ygrass_sB, FALSE, FALSE,
6137 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6140 Ygrass_wB, FALSE, FALSE,
6141 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6149 Ydirt_nB, FALSE, FALSE,
6150 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6153 Ydirt_eB, FALSE, FALSE,
6154 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6157 Ydirt_sB, FALSE, FALSE,
6158 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6161 Ydirt_wB, FALSE, FALSE,
6162 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6166 Xandroid, TRUE, FALSE,
6167 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6170 Xandroid_1_n, FALSE, FALSE,
6171 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6174 Xandroid_2_n, FALSE, FALSE,
6175 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6178 Xandroid_1_e, FALSE, FALSE,
6179 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6182 Xandroid_2_e, FALSE, FALSE,
6183 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6186 Xandroid_1_w, FALSE, FALSE,
6187 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6190 Xandroid_2_w, FALSE, FALSE,
6191 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6194 Xandroid_1_s, FALSE, FALSE,
6195 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6198 Xandroid_2_s, FALSE, FALSE,
6199 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6202 Yandroid_n, FALSE, FALSE,
6203 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6206 Yandroid_nB, FALSE, TRUE,
6207 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6210 Yandroid_ne, FALSE, FALSE,
6211 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6214 Yandroid_neB, FALSE, TRUE,
6215 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6218 Yandroid_e, FALSE, FALSE,
6219 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6222 Yandroid_eB, FALSE, TRUE,
6223 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6226 Yandroid_se, FALSE, FALSE,
6227 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6230 Yandroid_seB, FALSE, TRUE,
6231 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6234 Yandroid_s, FALSE, FALSE,
6235 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6238 Yandroid_sB, FALSE, TRUE,
6239 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6242 Yandroid_sw, FALSE, FALSE,
6243 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6246 Yandroid_swB, FALSE, TRUE,
6247 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6250 Yandroid_w, FALSE, FALSE,
6251 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6254 Yandroid_wB, FALSE, TRUE,
6255 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6258 Yandroid_nw, FALSE, FALSE,
6259 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6262 Yandroid_nwB, FALSE, TRUE,
6263 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6267 Xeater_n, TRUE, FALSE,
6268 EL_YAMYAM_UP, -1, -1
6271 Xeater_e, TRUE, FALSE,
6272 EL_YAMYAM_RIGHT, -1, -1
6275 Xeater_w, TRUE, FALSE,
6276 EL_YAMYAM_LEFT, -1, -1
6279 Xeater_s, TRUE, FALSE,
6280 EL_YAMYAM_DOWN, -1, -1
6283 Yeater_n, FALSE, FALSE,
6284 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6287 Yeater_nB, FALSE, TRUE,
6288 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6291 Yeater_e, FALSE, FALSE,
6292 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6295 Yeater_eB, FALSE, TRUE,
6296 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6299 Yeater_s, FALSE, FALSE,
6300 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6303 Yeater_sB, FALSE, TRUE,
6304 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6307 Yeater_w, FALSE, FALSE,
6308 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6311 Yeater_wB, FALSE, TRUE,
6312 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6315 Yeater_stone, FALSE, FALSE,
6316 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6319 Yeater_spring, FALSE, FALSE,
6320 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6324 Xalien, TRUE, FALSE,
6328 Xalien_pause, FALSE, FALSE,
6332 Yalien_n, FALSE, FALSE,
6333 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6336 Yalien_nB, FALSE, TRUE,
6337 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6340 Yalien_e, FALSE, FALSE,
6341 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6344 Yalien_eB, FALSE, TRUE,
6345 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6348 Yalien_s, FALSE, FALSE,
6349 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6352 Yalien_sB, FALSE, TRUE,
6353 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6356 Yalien_w, FALSE, FALSE,
6357 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6360 Yalien_wB, FALSE, TRUE,
6361 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6364 Yalien_stone, FALSE, FALSE,
6365 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6368 Yalien_spring, FALSE, FALSE,
6369 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6373 Xbug_1_n, TRUE, FALSE,
6377 Xbug_1_e, TRUE, FALSE,
6378 EL_BUG_RIGHT, -1, -1
6381 Xbug_1_s, TRUE, FALSE,
6385 Xbug_1_w, TRUE, FALSE,
6389 Xbug_2_n, FALSE, FALSE,
6393 Xbug_2_e, FALSE, FALSE,
6394 EL_BUG_RIGHT, -1, -1
6397 Xbug_2_s, FALSE, FALSE,
6401 Xbug_2_w, FALSE, FALSE,
6405 Ybug_n, FALSE, FALSE,
6406 EL_BUG, ACTION_MOVING, MV_BIT_UP
6409 Ybug_nB, FALSE, TRUE,
6410 EL_BUG, ACTION_MOVING, MV_BIT_UP
6413 Ybug_e, FALSE, FALSE,
6414 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6417 Ybug_eB, FALSE, TRUE,
6418 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6421 Ybug_s, FALSE, FALSE,
6422 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6425 Ybug_sB, FALSE, TRUE,
6426 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6429 Ybug_w, FALSE, FALSE,
6430 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6433 Ybug_wB, FALSE, TRUE,
6434 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6437 Ybug_w_n, FALSE, FALSE,
6438 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6441 Ybug_n_e, FALSE, FALSE,
6442 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6445 Ybug_e_s, FALSE, FALSE,
6446 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6449 Ybug_s_w, FALSE, FALSE,
6450 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6453 Ybug_e_n, FALSE, FALSE,
6454 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6457 Ybug_s_e, FALSE, FALSE,
6458 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6461 Ybug_w_s, FALSE, FALSE,
6462 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6465 Ybug_n_w, FALSE, FALSE,
6466 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6469 Ybug_stone, FALSE, FALSE,
6470 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6473 Ybug_spring, FALSE, FALSE,
6474 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6478 Xtank_1_n, TRUE, FALSE,
6479 EL_SPACESHIP_UP, -1, -1
6482 Xtank_1_e, TRUE, FALSE,
6483 EL_SPACESHIP_RIGHT, -1, -1
6486 Xtank_1_s, TRUE, FALSE,
6487 EL_SPACESHIP_DOWN, -1, -1
6490 Xtank_1_w, TRUE, FALSE,
6491 EL_SPACESHIP_LEFT, -1, -1
6494 Xtank_2_n, FALSE, FALSE,
6495 EL_SPACESHIP_UP, -1, -1
6498 Xtank_2_e, FALSE, FALSE,
6499 EL_SPACESHIP_RIGHT, -1, -1
6502 Xtank_2_s, FALSE, FALSE,
6503 EL_SPACESHIP_DOWN, -1, -1
6506 Xtank_2_w, FALSE, FALSE,
6507 EL_SPACESHIP_LEFT, -1, -1
6510 Ytank_n, FALSE, FALSE,
6511 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6514 Ytank_nB, FALSE, TRUE,
6515 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6518 Ytank_e, FALSE, FALSE,
6519 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6522 Ytank_eB, FALSE, TRUE,
6523 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6526 Ytank_s, FALSE, FALSE,
6527 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6530 Ytank_sB, FALSE, TRUE,
6531 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6534 Ytank_w, FALSE, FALSE,
6535 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6538 Ytank_wB, FALSE, TRUE,
6539 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6542 Ytank_w_n, FALSE, FALSE,
6543 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6546 Ytank_n_e, FALSE, FALSE,
6547 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6550 Ytank_e_s, FALSE, FALSE,
6551 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6554 Ytank_s_w, FALSE, FALSE,
6555 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6558 Ytank_e_n, FALSE, FALSE,
6559 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6562 Ytank_s_e, FALSE, FALSE,
6563 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6566 Ytank_w_s, FALSE, FALSE,
6567 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6570 Ytank_n_w, FALSE, FALSE,
6571 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6574 Ytank_stone, FALSE, FALSE,
6575 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6578 Ytank_spring, FALSE, FALSE,
6579 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6583 Xemerald, TRUE, FALSE,
6587 Xemerald_pause, FALSE, FALSE,
6591 Xemerald_fall, FALSE, FALSE,
6595 Xemerald_shine, FALSE, FALSE,
6596 EL_EMERALD, ACTION_TWINKLING, -1
6599 Yemerald_s, FALSE, FALSE,
6600 EL_EMERALD, ACTION_FALLING, -1
6603 Yemerald_sB, FALSE, TRUE,
6604 EL_EMERALD, ACTION_FALLING, -1
6607 Yemerald_e, FALSE, FALSE,
6608 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6611 Yemerald_eB, FALSE, TRUE,
6612 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6615 Yemerald_w, FALSE, FALSE,
6616 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6619 Yemerald_wB, FALSE, TRUE,
6620 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6623 Yemerald_blank, FALSE, FALSE,
6624 EL_EMERALD, ACTION_COLLECTING, -1
6628 Xdiamond, TRUE, FALSE,
6632 Xdiamond_pause, FALSE, FALSE,
6636 Xdiamond_fall, FALSE, FALSE,
6640 Xdiamond_shine, FALSE, FALSE,
6641 EL_DIAMOND, ACTION_TWINKLING, -1
6644 Ydiamond_s, FALSE, FALSE,
6645 EL_DIAMOND, ACTION_FALLING, -1
6648 Ydiamond_sB, FALSE, TRUE,
6649 EL_DIAMOND, ACTION_FALLING, -1
6652 Ydiamond_e, FALSE, FALSE,
6653 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6656 Ydiamond_eB, FALSE, TRUE,
6657 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6660 Ydiamond_w, FALSE, FALSE,
6661 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6664 Ydiamond_wB, FALSE, TRUE,
6665 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6668 Ydiamond_blank, FALSE, FALSE,
6669 EL_DIAMOND, ACTION_COLLECTING, -1
6672 Ydiamond_stone, FALSE, FALSE,
6673 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6677 Xstone, TRUE, FALSE,
6681 Xstone_pause, FALSE, FALSE,
6685 Xstone_fall, FALSE, FALSE,
6689 Ystone_s, FALSE, FALSE,
6690 EL_ROCK, ACTION_FALLING, -1
6693 Ystone_sB, FALSE, TRUE,
6694 EL_ROCK, ACTION_FALLING, -1
6697 Ystone_e, FALSE, FALSE,
6698 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6701 Ystone_eB, FALSE, TRUE,
6702 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6705 Ystone_w, FALSE, FALSE,
6706 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6709 Ystone_wB, FALSE, TRUE,
6710 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6718 Xbomb_pause, FALSE, FALSE,
6722 Xbomb_fall, FALSE, FALSE,
6726 Ybomb_s, FALSE, FALSE,
6727 EL_BOMB, ACTION_FALLING, -1
6730 Ybomb_sB, FALSE, TRUE,
6731 EL_BOMB, ACTION_FALLING, -1
6734 Ybomb_e, FALSE, FALSE,
6735 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6738 Ybomb_eB, FALSE, TRUE,
6739 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6742 Ybomb_w, FALSE, FALSE,
6743 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6746 Ybomb_wB, FALSE, TRUE,
6747 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6750 Ybomb_blank, FALSE, FALSE,
6751 EL_BOMB, ACTION_ACTIVATING, -1
6759 Xnut_pause, FALSE, FALSE,
6763 Xnut_fall, FALSE, FALSE,
6767 Ynut_s, FALSE, FALSE,
6768 EL_NUT, ACTION_FALLING, -1
6771 Ynut_sB, FALSE, TRUE,
6772 EL_NUT, ACTION_FALLING, -1
6775 Ynut_e, FALSE, FALSE,
6776 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6779 Ynut_eB, FALSE, TRUE,
6780 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6783 Ynut_w, FALSE, FALSE,
6784 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6787 Ynut_wB, FALSE, TRUE,
6788 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6791 Ynut_stone, FALSE, FALSE,
6792 EL_NUT, ACTION_BREAKING, -1
6796 Xspring, TRUE, FALSE,
6800 Xspring_pause, FALSE, FALSE,
6804 Xspring_e, TRUE, FALSE,
6805 EL_SPRING_RIGHT, -1, -1
6808 Xspring_w, TRUE, FALSE,
6809 EL_SPRING_LEFT, -1, -1
6812 Xspring_fall, FALSE, FALSE,
6816 Yspring_s, FALSE, FALSE,
6817 EL_SPRING, ACTION_FALLING, -1
6820 Yspring_sB, FALSE, TRUE,
6821 EL_SPRING, ACTION_FALLING, -1
6824 Yspring_e, FALSE, FALSE,
6825 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6828 Yspring_eB, FALSE, TRUE,
6829 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6832 Yspring_w, FALSE, FALSE,
6833 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6836 Yspring_wB, FALSE, TRUE,
6837 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6840 Yspring_alien_e, FALSE, FALSE,
6841 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6844 Yspring_alien_eB, FALSE, TRUE,
6845 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6848 Yspring_alien_w, FALSE, FALSE,
6849 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6852 Yspring_alien_wB, FALSE, TRUE,
6853 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6857 Xpush_emerald_e, FALSE, FALSE,
6858 EL_EMERALD, -1, MV_BIT_RIGHT
6861 Xpush_emerald_w, FALSE, FALSE,
6862 EL_EMERALD, -1, MV_BIT_LEFT
6865 Xpush_diamond_e, FALSE, FALSE,
6866 EL_DIAMOND, -1, MV_BIT_RIGHT
6869 Xpush_diamond_w, FALSE, FALSE,
6870 EL_DIAMOND, -1, MV_BIT_LEFT
6873 Xpush_stone_e, FALSE, FALSE,
6874 EL_ROCK, -1, MV_BIT_RIGHT
6877 Xpush_stone_w, FALSE, FALSE,
6878 EL_ROCK, -1, MV_BIT_LEFT
6881 Xpush_bomb_e, FALSE, FALSE,
6882 EL_BOMB, -1, MV_BIT_RIGHT
6885 Xpush_bomb_w, FALSE, FALSE,
6886 EL_BOMB, -1, MV_BIT_LEFT
6889 Xpush_nut_e, FALSE, FALSE,
6890 EL_NUT, -1, MV_BIT_RIGHT
6893 Xpush_nut_w, FALSE, FALSE,
6894 EL_NUT, -1, MV_BIT_LEFT
6897 Xpush_spring_e, FALSE, FALSE,
6898 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6901 Xpush_spring_w, FALSE, FALSE,
6902 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6906 Xdynamite, TRUE, FALSE,
6907 EL_EM_DYNAMITE, -1, -1
6910 Ydynamite_blank, FALSE, FALSE,
6911 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6914 Xdynamite_1, TRUE, FALSE,
6915 EL_EM_DYNAMITE_ACTIVE, -1, -1
6918 Xdynamite_2, FALSE, FALSE,
6919 EL_EM_DYNAMITE_ACTIVE, -1, -1
6922 Xdynamite_3, FALSE, FALSE,
6923 EL_EM_DYNAMITE_ACTIVE, -1, -1
6926 Xdynamite_4, FALSE, FALSE,
6927 EL_EM_DYNAMITE_ACTIVE, -1, -1
6931 Xkey_1, TRUE, FALSE,
6935 Xkey_2, TRUE, FALSE,
6939 Xkey_3, TRUE, FALSE,
6943 Xkey_4, TRUE, FALSE,
6947 Xkey_5, TRUE, FALSE,
6948 EL_EMC_KEY_5, -1, -1
6951 Xkey_6, TRUE, FALSE,
6952 EL_EMC_KEY_6, -1, -1
6955 Xkey_7, TRUE, FALSE,
6956 EL_EMC_KEY_7, -1, -1
6959 Xkey_8, TRUE, FALSE,
6960 EL_EMC_KEY_8, -1, -1
6964 Xdoor_1, TRUE, FALSE,
6965 EL_EM_GATE_1, -1, -1
6968 Xdoor_2, TRUE, FALSE,
6969 EL_EM_GATE_2, -1, -1
6972 Xdoor_3, TRUE, FALSE,
6973 EL_EM_GATE_3, -1, -1
6976 Xdoor_4, TRUE, FALSE,
6977 EL_EM_GATE_4, -1, -1
6980 Xdoor_5, TRUE, FALSE,
6981 EL_EMC_GATE_5, -1, -1
6984 Xdoor_6, TRUE, FALSE,
6985 EL_EMC_GATE_6, -1, -1
6988 Xdoor_7, TRUE, FALSE,
6989 EL_EMC_GATE_7, -1, -1
6992 Xdoor_8, TRUE, FALSE,
6993 EL_EMC_GATE_8, -1, -1
6997 Xfake_door_1, TRUE, FALSE,
6998 EL_EM_GATE_1_GRAY, -1, -1
7001 Xfake_door_2, TRUE, FALSE,
7002 EL_EM_GATE_2_GRAY, -1, -1
7005 Xfake_door_3, TRUE, FALSE,
7006 EL_EM_GATE_3_GRAY, -1, -1
7009 Xfake_door_4, TRUE, FALSE,
7010 EL_EM_GATE_4_GRAY, -1, -1
7013 Xfake_door_5, TRUE, FALSE,
7014 EL_EMC_GATE_5_GRAY, -1, -1
7017 Xfake_door_6, TRUE, FALSE,
7018 EL_EMC_GATE_6_GRAY, -1, -1
7021 Xfake_door_7, TRUE, FALSE,
7022 EL_EMC_GATE_7_GRAY, -1, -1
7025 Xfake_door_8, TRUE, FALSE,
7026 EL_EMC_GATE_8_GRAY, -1, -1
7030 Xballoon, TRUE, FALSE,
7034 Yballoon_n, FALSE, FALSE,
7035 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7038 Yballoon_nB, FALSE, TRUE,
7039 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7042 Yballoon_e, FALSE, FALSE,
7043 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7046 Yballoon_eB, FALSE, TRUE,
7047 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7050 Yballoon_s, FALSE, FALSE,
7051 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7054 Yballoon_sB, FALSE, TRUE,
7055 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7058 Yballoon_w, FALSE, FALSE,
7059 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7062 Yballoon_wB, FALSE, TRUE,
7063 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7067 Xball_1, TRUE, FALSE,
7068 EL_EMC_MAGIC_BALL, -1, -1
7071 Yball_1, FALSE, FALSE,
7072 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7075 Xball_2, FALSE, FALSE,
7076 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7079 Yball_2, FALSE, FALSE,
7080 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7083 Yball_blank, FALSE, FALSE,
7084 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7088 Xamoeba_1, TRUE, FALSE,
7089 EL_AMOEBA_DRY, ACTION_OTHER, -1
7092 Xamoeba_2, FALSE, FALSE,
7093 EL_AMOEBA_DRY, ACTION_OTHER, -1
7096 Xamoeba_3, FALSE, FALSE,
7097 EL_AMOEBA_DRY, ACTION_OTHER, -1
7100 Xamoeba_4, FALSE, FALSE,
7101 EL_AMOEBA_DRY, ACTION_OTHER, -1
7104 Xamoeba_5, TRUE, FALSE,
7105 EL_AMOEBA_WET, ACTION_OTHER, -1
7108 Xamoeba_6, FALSE, FALSE,
7109 EL_AMOEBA_WET, ACTION_OTHER, -1
7112 Xamoeba_7, FALSE, FALSE,
7113 EL_AMOEBA_WET, ACTION_OTHER, -1
7116 Xamoeba_8, FALSE, FALSE,
7117 EL_AMOEBA_WET, ACTION_OTHER, -1
7122 EL_AMOEBA_DROP, ACTION_GROWING, -1
7125 Xdrip_fall, FALSE, FALSE,
7126 EL_AMOEBA_DROP, -1, -1
7129 Xdrip_stretch, FALSE, FALSE,
7130 EL_AMOEBA_DROP, ACTION_FALLING, -1
7133 Xdrip_stretchB, FALSE, TRUE,
7134 EL_AMOEBA_DROP, ACTION_FALLING, -1
7137 Ydrip_1_s, FALSE, FALSE,
7138 EL_AMOEBA_DROP, ACTION_FALLING, -1
7141 Ydrip_1_sB, FALSE, TRUE,
7142 EL_AMOEBA_DROP, ACTION_FALLING, -1
7145 Ydrip_2_s, FALSE, FALSE,
7146 EL_AMOEBA_DROP, ACTION_FALLING, -1
7149 Ydrip_2_sB, FALSE, TRUE,
7150 EL_AMOEBA_DROP, ACTION_FALLING, -1
7154 Xwonderwall, TRUE, FALSE,
7155 EL_MAGIC_WALL, -1, -1
7158 Ywonderwall, FALSE, FALSE,
7159 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7163 Xwheel, TRUE, FALSE,
7164 EL_ROBOT_WHEEL, -1, -1
7167 Ywheel, FALSE, FALSE,
7168 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7172 Xswitch, TRUE, FALSE,
7173 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7176 Yswitch, FALSE, FALSE,
7177 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7181 Xbumper, TRUE, FALSE,
7182 EL_EMC_SPRING_BUMPER, -1, -1
7185 Ybumper, FALSE, FALSE,
7186 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7190 Xacid_nw, TRUE, FALSE,
7191 EL_ACID_POOL_TOPLEFT, -1, -1
7194 Xacid_ne, TRUE, FALSE,
7195 EL_ACID_POOL_TOPRIGHT, -1, -1
7198 Xacid_sw, TRUE, FALSE,
7199 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7202 Xacid_s, TRUE, FALSE,
7203 EL_ACID_POOL_BOTTOM, -1, -1
7206 Xacid_se, TRUE, FALSE,
7207 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7211 Xfake_blank, TRUE, FALSE,
7212 EL_INVISIBLE_WALL, -1, -1
7215 Yfake_blank, FALSE, FALSE,
7216 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7220 Xfake_grass, TRUE, FALSE,
7221 EL_EMC_FAKE_GRASS, -1, -1
7224 Yfake_grass, FALSE, FALSE,
7225 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7229 Xfake_amoeba, TRUE, FALSE,
7230 EL_EMC_DRIPPER, -1, -1
7233 Yfake_amoeba, FALSE, FALSE,
7234 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7238 Xlenses, TRUE, FALSE,
7239 EL_EMC_LENSES, -1, -1
7243 Xmagnify, TRUE, FALSE,
7244 EL_EMC_MAGNIFIER, -1, -1
7249 EL_QUICKSAND_EMPTY, -1, -1
7252 Xsand_stone, TRUE, FALSE,
7253 EL_QUICKSAND_FULL, -1, -1
7256 Xsand_stonein_1, FALSE, TRUE,
7257 EL_ROCK, ACTION_FILLING, -1
7260 Xsand_stonein_2, FALSE, TRUE,
7261 EL_ROCK, ACTION_FILLING, -1
7264 Xsand_stonein_3, FALSE, TRUE,
7265 EL_ROCK, ACTION_FILLING, -1
7268 Xsand_stonein_4, FALSE, TRUE,
7269 EL_ROCK, ACTION_FILLING, -1
7272 Xsand_sandstone_1, FALSE, FALSE,
7273 EL_QUICKSAND_FILLING, -1, -1
7276 Xsand_sandstone_2, FALSE, FALSE,
7277 EL_QUICKSAND_FILLING, -1, -1
7280 Xsand_sandstone_3, FALSE, FALSE,
7281 EL_QUICKSAND_FILLING, -1, -1
7284 Xsand_sandstone_4, FALSE, FALSE,
7285 EL_QUICKSAND_FILLING, -1, -1
7288 Xsand_stonesand_1, FALSE, FALSE,
7289 EL_QUICKSAND_EMPTYING, -1, -1
7292 Xsand_stonesand_2, FALSE, FALSE,
7293 EL_QUICKSAND_EMPTYING, -1, -1
7296 Xsand_stonesand_3, FALSE, FALSE,
7297 EL_QUICKSAND_EMPTYING, -1, -1
7300 Xsand_stonesand_4, FALSE, FALSE,
7301 EL_QUICKSAND_EMPTYING, -1, -1
7304 Xsand_stoneout_1, FALSE, FALSE,
7305 EL_ROCK, ACTION_EMPTYING, -1
7308 Xsand_stoneout_2, FALSE, FALSE,
7309 EL_ROCK, ACTION_EMPTYING, -1
7312 Xsand_stonesand_quickout_1, FALSE, FALSE,
7313 EL_QUICKSAND_EMPTYING, -1, -1
7316 Xsand_stonesand_quickout_2, FALSE, FALSE,
7317 EL_QUICKSAND_EMPTYING, -1, -1
7321 Xslide_ns, TRUE, FALSE,
7322 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7325 Yslide_ns_blank, FALSE, FALSE,
7326 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7329 Xslide_ew, TRUE, FALSE,
7330 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7333 Yslide_ew_blank, FALSE, FALSE,
7334 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7338 Xwind_n, TRUE, FALSE,
7339 EL_BALLOON_SWITCH_UP, -1, -1
7342 Xwind_e, TRUE, FALSE,
7343 EL_BALLOON_SWITCH_RIGHT, -1, -1
7346 Xwind_s, TRUE, FALSE,
7347 EL_BALLOON_SWITCH_DOWN, -1, -1
7350 Xwind_w, TRUE, FALSE,
7351 EL_BALLOON_SWITCH_LEFT, -1, -1
7354 Xwind_any, TRUE, FALSE,
7355 EL_BALLOON_SWITCH_ANY, -1, -1
7358 Xwind_stop, TRUE, FALSE,
7359 EL_BALLOON_SWITCH_NONE, -1, -1
7364 EL_EM_EXIT_CLOSED, -1, -1
7367 Xexit_1, TRUE, FALSE,
7368 EL_EM_EXIT_OPEN, -1, -1
7371 Xexit_2, FALSE, FALSE,
7372 EL_EM_EXIT_OPEN, -1, -1
7375 Xexit_3, FALSE, FALSE,
7376 EL_EM_EXIT_OPEN, -1, -1
7380 Xpause, FALSE, FALSE,
7385 Xwall_1, TRUE, FALSE,
7389 Xwall_2, TRUE, FALSE,
7390 EL_EMC_WALL_14, -1, -1
7393 Xwall_3, TRUE, FALSE,
7394 EL_EMC_WALL_15, -1, -1
7397 Xwall_4, TRUE, FALSE,
7398 EL_EMC_WALL_16, -1, -1
7402 Xroundwall_1, TRUE, FALSE,
7403 EL_WALL_SLIPPERY, -1, -1
7406 Xroundwall_2, TRUE, FALSE,
7407 EL_EMC_WALL_SLIPPERY_2, -1, -1
7410 Xroundwall_3, TRUE, FALSE,
7411 EL_EMC_WALL_SLIPPERY_3, -1, -1
7414 Xroundwall_4, TRUE, FALSE,
7415 EL_EMC_WALL_SLIPPERY_4, -1, -1
7419 Xsteel_1, TRUE, FALSE,
7420 EL_STEELWALL, -1, -1
7423 Xsteel_2, TRUE, FALSE,
7424 EL_EMC_STEELWALL_2, -1, -1
7427 Xsteel_3, TRUE, FALSE,
7428 EL_EMC_STEELWALL_3, -1, -1
7431 Xsteel_4, TRUE, FALSE,
7432 EL_EMC_STEELWALL_4, -1, -1
7436 Xdecor_1, TRUE, FALSE,
7437 EL_EMC_WALL_8, -1, -1
7440 Xdecor_2, TRUE, FALSE,
7441 EL_EMC_WALL_6, -1, -1
7444 Xdecor_3, TRUE, FALSE,
7445 EL_EMC_WALL_4, -1, -1
7448 Xdecor_4, TRUE, FALSE,
7449 EL_EMC_WALL_7, -1, -1
7452 Xdecor_5, TRUE, FALSE,
7453 EL_EMC_WALL_5, -1, -1
7456 Xdecor_6, TRUE, FALSE,
7457 EL_EMC_WALL_9, -1, -1
7460 Xdecor_7, TRUE, FALSE,
7461 EL_EMC_WALL_10, -1, -1
7464 Xdecor_8, TRUE, FALSE,
7465 EL_EMC_WALL_1, -1, -1
7468 Xdecor_9, TRUE, FALSE,
7469 EL_EMC_WALL_2, -1, -1
7472 Xdecor_10, TRUE, FALSE,
7473 EL_EMC_WALL_3, -1, -1
7476 Xdecor_11, TRUE, FALSE,
7477 EL_EMC_WALL_11, -1, -1
7480 Xdecor_12, TRUE, FALSE,
7481 EL_EMC_WALL_12, -1, -1
7485 Xalpha_0, TRUE, FALSE,
7486 EL_CHAR('0'), -1, -1
7489 Xalpha_1, TRUE, FALSE,
7490 EL_CHAR('1'), -1, -1
7493 Xalpha_2, TRUE, FALSE,
7494 EL_CHAR('2'), -1, -1
7497 Xalpha_3, TRUE, FALSE,
7498 EL_CHAR('3'), -1, -1
7501 Xalpha_4, TRUE, FALSE,
7502 EL_CHAR('4'), -1, -1
7505 Xalpha_5, TRUE, FALSE,
7506 EL_CHAR('5'), -1, -1
7509 Xalpha_6, TRUE, FALSE,
7510 EL_CHAR('6'), -1, -1
7513 Xalpha_7, TRUE, FALSE,
7514 EL_CHAR('7'), -1, -1
7517 Xalpha_8, TRUE, FALSE,
7518 EL_CHAR('8'), -1, -1
7521 Xalpha_9, TRUE, FALSE,
7522 EL_CHAR('9'), -1, -1
7525 Xalpha_excla, TRUE, FALSE,
7526 EL_CHAR('!'), -1, -1
7529 Xalpha_apost, TRUE, FALSE,
7530 EL_CHAR('\''), -1, -1
7533 Xalpha_comma, TRUE, FALSE,
7534 EL_CHAR(','), -1, -1
7537 Xalpha_minus, TRUE, FALSE,
7538 EL_CHAR('-'), -1, -1
7541 Xalpha_perio, TRUE, FALSE,
7542 EL_CHAR('.'), -1, -1
7545 Xalpha_colon, TRUE, FALSE,
7546 EL_CHAR(':'), -1, -1
7549 Xalpha_quest, TRUE, FALSE,
7550 EL_CHAR('?'), -1, -1
7553 Xalpha_a, TRUE, FALSE,
7554 EL_CHAR('A'), -1, -1
7557 Xalpha_b, TRUE, FALSE,
7558 EL_CHAR('B'), -1, -1
7561 Xalpha_c, TRUE, FALSE,
7562 EL_CHAR('C'), -1, -1
7565 Xalpha_d, TRUE, FALSE,
7566 EL_CHAR('D'), -1, -1
7569 Xalpha_e, TRUE, FALSE,
7570 EL_CHAR('E'), -1, -1
7573 Xalpha_f, TRUE, FALSE,
7574 EL_CHAR('F'), -1, -1
7577 Xalpha_g, TRUE, FALSE,
7578 EL_CHAR('G'), -1, -1
7581 Xalpha_h, TRUE, FALSE,
7582 EL_CHAR('H'), -1, -1
7585 Xalpha_i, TRUE, FALSE,
7586 EL_CHAR('I'), -1, -1
7589 Xalpha_j, TRUE, FALSE,
7590 EL_CHAR('J'), -1, -1
7593 Xalpha_k, TRUE, FALSE,
7594 EL_CHAR('K'), -1, -1
7597 Xalpha_l, TRUE, FALSE,
7598 EL_CHAR('L'), -1, -1
7601 Xalpha_m, TRUE, FALSE,
7602 EL_CHAR('M'), -1, -1
7605 Xalpha_n, TRUE, FALSE,
7606 EL_CHAR('N'), -1, -1
7609 Xalpha_o, TRUE, FALSE,
7610 EL_CHAR('O'), -1, -1
7613 Xalpha_p, TRUE, FALSE,
7614 EL_CHAR('P'), -1, -1
7617 Xalpha_q, TRUE, FALSE,
7618 EL_CHAR('Q'), -1, -1
7621 Xalpha_r, TRUE, FALSE,
7622 EL_CHAR('R'), -1, -1
7625 Xalpha_s, TRUE, FALSE,
7626 EL_CHAR('S'), -1, -1
7629 Xalpha_t, TRUE, FALSE,
7630 EL_CHAR('T'), -1, -1
7633 Xalpha_u, TRUE, FALSE,
7634 EL_CHAR('U'), -1, -1
7637 Xalpha_v, TRUE, FALSE,
7638 EL_CHAR('V'), -1, -1
7641 Xalpha_w, TRUE, FALSE,
7642 EL_CHAR('W'), -1, -1
7645 Xalpha_x, TRUE, FALSE,
7646 EL_CHAR('X'), -1, -1
7649 Xalpha_y, TRUE, FALSE,
7650 EL_CHAR('Y'), -1, -1
7653 Xalpha_z, TRUE, FALSE,
7654 EL_CHAR('Z'), -1, -1
7657 Xalpha_arrow_e, TRUE, FALSE,
7658 EL_CHAR('>'), -1, -1
7661 Xalpha_arrow_w, TRUE, FALSE,
7662 EL_CHAR('<'), -1, -1
7665 Xalpha_copyr, TRUE, FALSE,
7666 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7670 Ykey_1_blank, FALSE, FALSE,
7671 EL_EM_KEY_1, ACTION_COLLECTING, -1
7674 Ykey_2_blank, FALSE, FALSE,
7675 EL_EM_KEY_2, ACTION_COLLECTING, -1
7678 Ykey_3_blank, FALSE, FALSE,
7679 EL_EM_KEY_3, ACTION_COLLECTING, -1
7682 Ykey_4_blank, FALSE, FALSE,
7683 EL_EM_KEY_4, ACTION_COLLECTING, -1
7686 Ykey_5_blank, FALSE, FALSE,
7687 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7690 Ykey_6_blank, FALSE, FALSE,
7691 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7694 Ykey_7_blank, FALSE, FALSE,
7695 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7698 Ykey_8_blank, FALSE, FALSE,
7699 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7702 Ylenses_blank, FALSE, FALSE,
7703 EL_EMC_LENSES, ACTION_COLLECTING, -1
7706 Ymagnify_blank, FALSE, FALSE,
7707 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7710 Ygrass_blank, FALSE, FALSE,
7711 EL_EMC_GRASS, ACTION_SNAPPING, -1
7714 Ydirt_blank, FALSE, FALSE,
7715 EL_SAND, ACTION_SNAPPING, -1
7724 static struct Mapping_EM_to_RND_player
7733 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7737 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7741 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7745 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7749 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7753 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7757 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7761 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7765 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7769 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7773 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7777 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7781 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7785 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7789 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7793 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7797 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7801 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7805 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7809 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7813 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7817 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7821 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7825 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7829 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7833 EL_PLAYER_1, ACTION_DEFAULT, -1,
7837 EL_PLAYER_2, ACTION_DEFAULT, -1,
7841 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7845 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7849 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7853 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7857 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7861 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7865 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7869 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7873 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7877 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7881 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7885 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7889 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7893 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7897 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7901 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7905 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7909 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7913 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7917 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7921 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7925 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7929 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7933 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7937 EL_PLAYER_3, ACTION_DEFAULT, -1,
7941 EL_PLAYER_4, ACTION_DEFAULT, -1,
7950 int map_element_RND_to_EM_cave(int element_rnd)
7952 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7953 static boolean mapping_initialized = FALSE;
7955 if (!mapping_initialized)
7959 // return "Xalpha_quest" for all undefined elements in mapping array
7960 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7961 mapping_RND_to_EM[i] = Xalpha_quest;
7963 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7964 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7965 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7966 em_object_mapping_list[i].element_em;
7968 mapping_initialized = TRUE;
7971 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7973 Warn("invalid RND level element %d", element_rnd);
7978 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7981 int map_element_EM_to_RND_cave(int element_em_cave)
7983 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7984 static boolean mapping_initialized = FALSE;
7986 if (!mapping_initialized)
7990 // return "EL_UNKNOWN" for all undefined elements in mapping array
7991 for (i = 0; i < GAME_TILE_MAX; i++)
7992 mapping_EM_to_RND[i] = EL_UNKNOWN;
7994 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7995 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7996 em_object_mapping_list[i].element_rnd;
7998 mapping_initialized = TRUE;
8001 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8003 Warn("invalid EM cave element %d", element_em_cave);
8008 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8011 int map_element_EM_to_RND_game(int element_em_game)
8013 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8014 static boolean mapping_initialized = FALSE;
8016 if (!mapping_initialized)
8020 // return "EL_UNKNOWN" for all undefined elements in mapping array
8021 for (i = 0; i < GAME_TILE_MAX; i++)
8022 mapping_EM_to_RND[i] = EL_UNKNOWN;
8024 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8025 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8026 em_object_mapping_list[i].element_rnd;
8028 mapping_initialized = TRUE;
8031 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8033 Warn("invalid EM game element %d", element_em_game);
8038 return mapping_EM_to_RND[element_em_game];
8041 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8043 struct LevelInfo_EM *level_em = level->native_em_level;
8044 struct CAVE *cav = level_em->cav;
8047 for (i = 0; i < GAME_TILE_MAX; i++)
8048 cav->android_array[i] = Cblank;
8050 for (i = 0; i < level->num_android_clone_elements; i++)
8052 int element_rnd = level->android_clone_element[i];
8053 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8055 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8056 if (em_object_mapping_list[j].element_rnd == element_rnd)
8057 cav->android_array[em_object_mapping_list[j].element_em] =
8062 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8064 struct LevelInfo_EM *level_em = level->native_em_level;
8065 struct CAVE *cav = level_em->cav;
8068 level->num_android_clone_elements = 0;
8070 for (i = 0; i < GAME_TILE_MAX; i++)
8072 int element_em_cave = cav->android_array[i];
8074 boolean element_found = FALSE;
8076 if (element_em_cave == Cblank)
8079 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8081 for (j = 0; j < level->num_android_clone_elements; j++)
8082 if (level->android_clone_element[j] == element_rnd)
8083 element_found = TRUE;
8087 level->android_clone_element[level->num_android_clone_elements++] =
8090 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8095 if (level->num_android_clone_elements == 0)
8097 level->num_android_clone_elements = 1;
8098 level->android_clone_element[0] = EL_EMPTY;
8102 int map_direction_RND_to_EM(int direction)
8104 return (direction == MV_UP ? 0 :
8105 direction == MV_RIGHT ? 1 :
8106 direction == MV_DOWN ? 2 :
8107 direction == MV_LEFT ? 3 :
8111 int map_direction_EM_to_RND(int direction)
8113 return (direction == 0 ? MV_UP :
8114 direction == 1 ? MV_RIGHT :
8115 direction == 2 ? MV_DOWN :
8116 direction == 3 ? MV_LEFT :
8120 int map_element_RND_to_SP(int element_rnd)
8122 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8124 if (element_rnd >= EL_SP_START &&
8125 element_rnd <= EL_SP_END)
8126 element_sp = element_rnd - EL_SP_START;
8127 else if (element_rnd == EL_EMPTY_SPACE)
8129 else if (element_rnd == EL_INVISIBLE_WALL)
8135 int map_element_SP_to_RND(int element_sp)
8137 int element_rnd = EL_UNKNOWN;
8139 if (element_sp >= 0x00 &&
8141 element_rnd = EL_SP_START + element_sp;
8142 else if (element_sp == 0x28)
8143 element_rnd = EL_INVISIBLE_WALL;
8148 int map_action_SP_to_RND(int action_sp)
8152 case actActive: return ACTION_ACTIVE;
8153 case actImpact: return ACTION_IMPACT;
8154 case actExploding: return ACTION_EXPLODING;
8155 case actDigging: return ACTION_DIGGING;
8156 case actSnapping: return ACTION_SNAPPING;
8157 case actCollecting: return ACTION_COLLECTING;
8158 case actPassing: return ACTION_PASSING;
8159 case actPushing: return ACTION_PUSHING;
8160 case actDropping: return ACTION_DROPPING;
8162 default: return ACTION_DEFAULT;
8166 int map_element_RND_to_MM(int element_rnd)
8168 return (element_rnd >= EL_MM_START_1 &&
8169 element_rnd <= EL_MM_END_1 ?
8170 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8172 element_rnd >= EL_MM_START_2 &&
8173 element_rnd <= EL_MM_END_2 ?
8174 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8176 element_rnd >= EL_MM_START_3 &&
8177 element_rnd <= EL_MM_END_3 ?
8178 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8180 element_rnd >= EL_CHAR_START &&
8181 element_rnd <= EL_CHAR_END ?
8182 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8184 element_rnd >= EL_MM_RUNTIME_START &&
8185 element_rnd <= EL_MM_RUNTIME_END ?
8186 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8188 EL_MM_EMPTY_NATIVE);
8191 int map_element_MM_to_RND(int element_mm)
8193 return (element_mm == EL_MM_EMPTY_NATIVE ||
8194 element_mm == EL_DF_EMPTY_NATIVE ?
8197 element_mm >= EL_MM_START_1_NATIVE &&
8198 element_mm <= EL_MM_END_1_NATIVE ?
8199 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8201 element_mm >= EL_MM_START_2_NATIVE &&
8202 element_mm <= EL_MM_END_2_NATIVE ?
8203 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8205 element_mm >= EL_MM_START_3_NATIVE &&
8206 element_mm <= EL_MM_END_3_NATIVE ?
8207 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8209 element_mm >= EL_MM_CHAR_START_NATIVE &&
8210 element_mm <= EL_MM_CHAR_END_NATIVE ?
8211 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8213 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8214 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8215 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8220 int map_action_MM_to_RND(int action_mm)
8222 // all MM actions are defined to exactly match their RND counterparts
8226 int map_sound_MM_to_RND(int sound_mm)
8230 case SND_MM_GAME_LEVELTIME_CHARGING:
8231 return SND_GAME_LEVELTIME_CHARGING;
8233 case SND_MM_GAME_HEALTH_CHARGING:
8234 return SND_GAME_HEALTH_CHARGING;
8237 return SND_UNDEFINED;
8241 int map_mm_wall_element(int element)
8243 return (element >= EL_MM_STEEL_WALL_START &&
8244 element <= EL_MM_STEEL_WALL_END ?
8247 element >= EL_MM_WOODEN_WALL_START &&
8248 element <= EL_MM_WOODEN_WALL_END ?
8251 element >= EL_MM_ICE_WALL_START &&
8252 element <= EL_MM_ICE_WALL_END ?
8255 element >= EL_MM_AMOEBA_WALL_START &&
8256 element <= EL_MM_AMOEBA_WALL_END ?
8259 element >= EL_DF_STEEL_WALL_START &&
8260 element <= EL_DF_STEEL_WALL_END ?
8263 element >= EL_DF_WOODEN_WALL_START &&
8264 element <= EL_DF_WOODEN_WALL_END ?
8270 int map_mm_wall_element_editor(int element)
8274 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8275 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8276 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8277 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8278 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8279 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8281 default: return element;
8285 int get_next_element(int element)
8289 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8290 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8291 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8292 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8293 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8294 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8295 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8296 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8297 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8298 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8299 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8301 default: return element;
8305 int el2img_mm(int element_mm)
8307 return el2img(map_element_MM_to_RND(element_mm));
8310 int el_act2img_mm(int element_mm, int action)
8312 return el_act2img(map_element_MM_to_RND(element_mm), action);
8315 int el_act_dir2img(int element, int action, int direction)
8317 element = GFX_ELEMENT(element);
8318 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8320 // direction_graphic[][] == graphic[] for undefined direction graphics
8321 return element_info[element].direction_graphic[action][direction];
8324 static int el_act_dir2crm(int element, int action, int direction)
8326 element = GFX_ELEMENT(element);
8327 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8329 // direction_graphic[][] == graphic[] for undefined direction graphics
8330 return element_info[element].direction_crumbled[action][direction];
8333 int el_act2img(int element, int action)
8335 element = GFX_ELEMENT(element);
8337 return element_info[element].graphic[action];
8340 int el_act2crm(int element, int action)
8342 element = GFX_ELEMENT(element);
8344 return element_info[element].crumbled[action];
8347 int el_dir2img(int element, int direction)
8349 element = GFX_ELEMENT(element);
8351 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8354 int el2baseimg(int element)
8356 return element_info[element].graphic[ACTION_DEFAULT];
8359 int el2img(int element)
8361 element = GFX_ELEMENT(element);
8363 return element_info[element].graphic[ACTION_DEFAULT];
8366 int el2edimg(int element)
8368 element = GFX_ELEMENT(element);
8370 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8373 int el2preimg(int element)
8375 element = GFX_ELEMENT(element);
8377 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8380 int el2panelimg(int element)
8382 element = GFX_ELEMENT(element);
8384 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8387 int font2baseimg(int font_nr)
8389 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8392 int getBeltNrFromBeltElement(int element)
8394 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8395 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8396 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8399 int getBeltNrFromBeltActiveElement(int element)
8401 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8402 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8403 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8406 int getBeltNrFromBeltSwitchElement(int element)
8408 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8409 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8410 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8413 int getBeltDirNrFromBeltElement(int element)
8415 static int belt_base_element[4] =
8417 EL_CONVEYOR_BELT_1_LEFT,
8418 EL_CONVEYOR_BELT_2_LEFT,
8419 EL_CONVEYOR_BELT_3_LEFT,
8420 EL_CONVEYOR_BELT_4_LEFT
8423 int belt_nr = getBeltNrFromBeltElement(element);
8424 int belt_dir_nr = element - belt_base_element[belt_nr];
8426 return (belt_dir_nr % 3);
8429 int getBeltDirNrFromBeltSwitchElement(int element)
8431 static int belt_base_element[4] =
8433 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8434 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8435 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8436 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8439 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8440 int belt_dir_nr = element - belt_base_element[belt_nr];
8442 return (belt_dir_nr % 3);
8445 int getBeltDirFromBeltElement(int element)
8447 static int belt_move_dir[3] =
8454 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8456 return belt_move_dir[belt_dir_nr];
8459 int getBeltDirFromBeltSwitchElement(int element)
8461 static int belt_move_dir[3] =
8468 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8470 return belt_move_dir[belt_dir_nr];
8473 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8475 static int belt_base_element[4] =
8477 EL_CONVEYOR_BELT_1_LEFT,
8478 EL_CONVEYOR_BELT_2_LEFT,
8479 EL_CONVEYOR_BELT_3_LEFT,
8480 EL_CONVEYOR_BELT_4_LEFT
8483 return belt_base_element[belt_nr] + belt_dir_nr;
8486 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8488 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8490 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8493 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8495 static int belt_base_element[4] =
8497 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8498 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8499 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8500 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8503 return belt_base_element[belt_nr] + belt_dir_nr;
8506 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8508 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8510 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8513 boolean swapTiles_EM(boolean is_pre_emc_cave)
8515 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8518 boolean getTeamMode_EM(void)
8520 return game.team_mode || network_playing;
8523 boolean isActivePlayer_EM(int player_nr)
8525 return stored_player[player_nr].active;
8528 unsigned int InitRND(int seed)
8530 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8531 return InitEngineRandom_EM(seed);
8532 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8533 return InitEngineRandom_SP(seed);
8534 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8535 return InitEngineRandom_MM(seed);
8537 return InitEngineRandom_RND(seed);
8540 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8541 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8543 static int get_effective_element_EM(int tile, int frame_em)
8545 int element = object_mapping[tile].element_rnd;
8546 int action = object_mapping[tile].action;
8547 boolean is_backside = object_mapping[tile].is_backside;
8548 boolean action_removing = (action == ACTION_DIGGING ||
8549 action == ACTION_SNAPPING ||
8550 action == ACTION_COLLECTING);
8558 return (frame_em > 5 ? EL_EMPTY : element);
8564 else // frame_em == 7
8575 case Ydiamond_stone:
8579 case Xdrip_stretchB:
8595 case Ymagnify_blank:
8598 case Xsand_stonein_1:
8599 case Xsand_stonein_2:
8600 case Xsand_stonein_3:
8601 case Xsand_stonein_4:
8605 return (is_backside || action_removing ? EL_EMPTY : element);
8610 static boolean check_linear_animation_EM(int tile)
8614 case Xsand_stonesand_1:
8615 case Xsand_stonesand_quickout_1:
8616 case Xsand_sandstone_1:
8617 case Xsand_stonein_1:
8618 case Xsand_stoneout_1:
8646 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8647 boolean has_crumbled_graphics,
8648 int crumbled, int sync_frame)
8650 // if element can be crumbled, but certain action graphics are just empty
8651 // space (like instantly snapping sand to empty space in 1 frame), do not
8652 // treat these empty space graphics as crumbled graphics in EMC engine
8653 if (crumbled == IMG_EMPTY_SPACE)
8654 has_crumbled_graphics = FALSE;
8656 if (has_crumbled_graphics)
8658 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8659 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8660 g_crumbled->anim_delay,
8661 g_crumbled->anim_mode,
8662 g_crumbled->anim_start_frame,
8665 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8666 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8668 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8669 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8671 g_em->has_crumbled_graphics = TRUE;
8675 g_em->crumbled_bitmap = NULL;
8676 g_em->crumbled_src_x = 0;
8677 g_em->crumbled_src_y = 0;
8678 g_em->crumbled_border_size = 0;
8679 g_em->crumbled_tile_size = 0;
8681 g_em->has_crumbled_graphics = FALSE;
8686 void ResetGfxAnimation_EM(int x, int y, int tile)
8692 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8693 int tile, int frame_em, int x, int y)
8695 int action = object_mapping[tile].action;
8696 int direction = object_mapping[tile].direction;
8697 int effective_element = get_effective_element_EM(tile, frame_em);
8698 int graphic = (direction == MV_NONE ?
8699 el_act2img(effective_element, action) :
8700 el_act_dir2img(effective_element, action, direction));
8701 struct GraphicInfo *g = &graphic_info[graphic];
8703 boolean action_removing = (action == ACTION_DIGGING ||
8704 action == ACTION_SNAPPING ||
8705 action == ACTION_COLLECTING);
8706 boolean action_moving = (action == ACTION_FALLING ||
8707 action == ACTION_MOVING ||
8708 action == ACTION_PUSHING ||
8709 action == ACTION_EATING ||
8710 action == ACTION_FILLING ||
8711 action == ACTION_EMPTYING);
8712 boolean action_falling = (action == ACTION_FALLING ||
8713 action == ACTION_FILLING ||
8714 action == ACTION_EMPTYING);
8716 // special case: graphic uses "2nd movement tile" and has defined
8717 // 7 frames for movement animation (or less) => use default graphic
8718 // for last (8th) frame which ends the movement animation
8719 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8721 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8722 graphic = (direction == MV_NONE ?
8723 el_act2img(effective_element, action) :
8724 el_act_dir2img(effective_element, action, direction));
8726 g = &graphic_info[graphic];
8729 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8733 else if (action_moving)
8735 boolean is_backside = object_mapping[tile].is_backside;
8739 int direction = object_mapping[tile].direction;
8740 int move_dir = (action_falling ? MV_DOWN : direction);
8745 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8746 if (g->double_movement && frame_em == 0)
8750 if (move_dir == MV_LEFT)
8751 GfxFrame[x - 1][y] = GfxFrame[x][y];
8752 else if (move_dir == MV_RIGHT)
8753 GfxFrame[x + 1][y] = GfxFrame[x][y];
8754 else if (move_dir == MV_UP)
8755 GfxFrame[x][y - 1] = GfxFrame[x][y];
8756 else if (move_dir == MV_DOWN)
8757 GfxFrame[x][y + 1] = GfxFrame[x][y];
8764 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8765 if (tile == Xsand_stonesand_quickout_1 ||
8766 tile == Xsand_stonesand_quickout_2)
8770 if (graphic_info[graphic].anim_global_sync)
8771 sync_frame = FrameCounter;
8772 else if (graphic_info[graphic].anim_global_anim_sync)
8773 sync_frame = getGlobalAnimSyncFrame();
8774 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8775 sync_frame = GfxFrame[x][y];
8777 sync_frame = 0; // playfield border (pseudo steel)
8779 SetRandomAnimationValue(x, y);
8781 int frame = getAnimationFrame(g->anim_frames,
8784 g->anim_start_frame,
8787 g_em->unique_identifier =
8788 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8791 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8792 int tile, int frame_em, int x, int y)
8794 int action = object_mapping[tile].action;
8795 int direction = object_mapping[tile].direction;
8796 boolean is_backside = object_mapping[tile].is_backside;
8797 int effective_element = get_effective_element_EM(tile, frame_em);
8798 int effective_action = action;
8799 int graphic = (direction == MV_NONE ?
8800 el_act2img(effective_element, effective_action) :
8801 el_act_dir2img(effective_element, effective_action,
8803 int crumbled = (direction == MV_NONE ?
8804 el_act2crm(effective_element, effective_action) :
8805 el_act_dir2crm(effective_element, effective_action,
8807 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8808 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8809 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8810 struct GraphicInfo *g = &graphic_info[graphic];
8813 // special case: graphic uses "2nd movement tile" and has defined
8814 // 7 frames for movement animation (or less) => use default graphic
8815 // for last (8th) frame which ends the movement animation
8816 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8818 effective_action = ACTION_DEFAULT;
8819 graphic = (direction == MV_NONE ?
8820 el_act2img(effective_element, effective_action) :
8821 el_act_dir2img(effective_element, effective_action,
8823 crumbled = (direction == MV_NONE ?
8824 el_act2crm(effective_element, effective_action) :
8825 el_act_dir2crm(effective_element, effective_action,
8828 g = &graphic_info[graphic];
8831 if (graphic_info[graphic].anim_global_sync)
8832 sync_frame = FrameCounter;
8833 else if (graphic_info[graphic].anim_global_anim_sync)
8834 sync_frame = getGlobalAnimSyncFrame();
8835 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8836 sync_frame = GfxFrame[x][y];
8838 sync_frame = 0; // playfield border (pseudo steel)
8840 SetRandomAnimationValue(x, y);
8842 int frame = getAnimationFrame(g->anim_frames,
8845 g->anim_start_frame,
8848 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8849 g->double_movement && is_backside);
8851 // (updating the "crumbled" graphic definitions is probably not really needed,
8852 // as animations for crumbled graphics can't be longer than one EMC cycle)
8853 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8857 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8858 int player_nr, int anim, int frame_em)
8860 int element = player_mapping[player_nr][anim].element_rnd;
8861 int action = player_mapping[player_nr][anim].action;
8862 int direction = player_mapping[player_nr][anim].direction;
8863 int graphic = (direction == MV_NONE ?
8864 el_act2img(element, action) :
8865 el_act_dir2img(element, action, direction));
8866 struct GraphicInfo *g = &graphic_info[graphic];
8869 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8871 stored_player[player_nr].StepFrame = frame_em;
8873 sync_frame = stored_player[player_nr].Frame;
8875 int frame = getAnimationFrame(g->anim_frames,
8878 g->anim_start_frame,
8881 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8882 &g_em->src_x, &g_em->src_y, FALSE);
8885 void InitGraphicInfo_EM(void)
8889 // always start with reliable default values
8890 for (i = 0; i < GAME_TILE_MAX; i++)
8892 object_mapping[i].element_rnd = EL_UNKNOWN;
8893 object_mapping[i].is_backside = FALSE;
8894 object_mapping[i].action = ACTION_DEFAULT;
8895 object_mapping[i].direction = MV_NONE;
8898 // always start with reliable default values
8899 for (p = 0; p < MAX_PLAYERS; p++)
8901 for (i = 0; i < PLY_MAX; i++)
8903 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8904 player_mapping[p][i].action = ACTION_DEFAULT;
8905 player_mapping[p][i].direction = MV_NONE;
8909 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8911 int e = em_object_mapping_list[i].element_em;
8913 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8914 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8916 if (em_object_mapping_list[i].action != -1)
8917 object_mapping[e].action = em_object_mapping_list[i].action;
8919 if (em_object_mapping_list[i].direction != -1)
8920 object_mapping[e].direction =
8921 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8924 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8926 int a = em_player_mapping_list[i].action_em;
8927 int p = em_player_mapping_list[i].player_nr;
8929 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8931 if (em_player_mapping_list[i].action != -1)
8932 player_mapping[p][a].action = em_player_mapping_list[i].action;
8934 if (em_player_mapping_list[i].direction != -1)
8935 player_mapping[p][a].direction =
8936 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8939 for (i = 0; i < GAME_TILE_MAX; i++)
8941 int element = object_mapping[i].element_rnd;
8942 int action = object_mapping[i].action;
8943 int direction = object_mapping[i].direction;
8944 boolean is_backside = object_mapping[i].is_backside;
8945 boolean action_exploding = ((action == ACTION_EXPLODING ||
8946 action == ACTION_SMASHED_BY_ROCK ||
8947 action == ACTION_SMASHED_BY_SPRING) &&
8948 element != EL_DIAMOND);
8949 boolean action_active = (action == ACTION_ACTIVE);
8950 boolean action_other = (action == ACTION_OTHER);
8952 for (j = 0; j < 8; j++)
8954 int effective_element = get_effective_element_EM(i, j);
8955 int effective_action = (j < 7 ? action :
8956 i == Xdrip_stretch ? action :
8957 i == Xdrip_stretchB ? action :
8958 i == Ydrip_1_s ? action :
8959 i == Ydrip_1_sB ? action :
8960 i == Yball_1 ? action :
8961 i == Xball_2 ? action :
8962 i == Yball_2 ? action :
8963 i == Yball_blank ? action :
8964 i == Ykey_1_blank ? action :
8965 i == Ykey_2_blank ? action :
8966 i == Ykey_3_blank ? action :
8967 i == Ykey_4_blank ? action :
8968 i == Ykey_5_blank ? action :
8969 i == Ykey_6_blank ? action :
8970 i == Ykey_7_blank ? action :
8971 i == Ykey_8_blank ? action :
8972 i == Ylenses_blank ? action :
8973 i == Ymagnify_blank ? action :
8974 i == Ygrass_blank ? action :
8975 i == Ydirt_blank ? action :
8976 i == Xsand_stonein_1 ? action :
8977 i == Xsand_stonein_2 ? action :
8978 i == Xsand_stonein_3 ? action :
8979 i == Xsand_stonein_4 ? action :
8980 i == Xsand_stoneout_1 ? action :
8981 i == Xsand_stoneout_2 ? action :
8982 i == Xboom_android ? ACTION_EXPLODING :
8983 action_exploding ? ACTION_EXPLODING :
8984 action_active ? action :
8985 action_other ? action :
8987 int graphic = (el_act_dir2img(effective_element, effective_action,
8989 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8991 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8992 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8993 boolean has_action_graphics = (graphic != base_graphic);
8994 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8995 struct GraphicInfo *g = &graphic_info[graphic];
8996 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8999 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9000 boolean special_animation = (action != ACTION_DEFAULT &&
9001 g->anim_frames == 3 &&
9002 g->anim_delay == 2 &&
9003 g->anim_mode & ANIM_LINEAR);
9004 int sync_frame = (i == Xdrip_stretch ? 7 :
9005 i == Xdrip_stretchB ? 7 :
9006 i == Ydrip_2_s ? j + 8 :
9007 i == Ydrip_2_sB ? j + 8 :
9016 i == Xfake_acid_1 ? 0 :
9017 i == Xfake_acid_2 ? 10 :
9018 i == Xfake_acid_3 ? 20 :
9019 i == Xfake_acid_4 ? 30 :
9020 i == Xfake_acid_5 ? 40 :
9021 i == Xfake_acid_6 ? 50 :
9022 i == Xfake_acid_7 ? 60 :
9023 i == Xfake_acid_8 ? 70 :
9024 i == Xfake_acid_1_player ? 0 :
9025 i == Xfake_acid_2_player ? 10 :
9026 i == Xfake_acid_3_player ? 20 :
9027 i == Xfake_acid_4_player ? 30 :
9028 i == Xfake_acid_5_player ? 40 :
9029 i == Xfake_acid_6_player ? 50 :
9030 i == Xfake_acid_7_player ? 60 :
9031 i == Xfake_acid_8_player ? 70 :
9033 i == Yball_2 ? j + 8 :
9034 i == Yball_blank ? j + 1 :
9035 i == Ykey_1_blank ? j + 1 :
9036 i == Ykey_2_blank ? j + 1 :
9037 i == Ykey_3_blank ? j + 1 :
9038 i == Ykey_4_blank ? j + 1 :
9039 i == Ykey_5_blank ? j + 1 :
9040 i == Ykey_6_blank ? j + 1 :
9041 i == Ykey_7_blank ? j + 1 :
9042 i == Ykey_8_blank ? j + 1 :
9043 i == Ylenses_blank ? j + 1 :
9044 i == Ymagnify_blank ? j + 1 :
9045 i == Ygrass_blank ? j + 1 :
9046 i == Ydirt_blank ? j + 1 :
9047 i == Xamoeba_1 ? 0 :
9048 i == Xamoeba_2 ? 1 :
9049 i == Xamoeba_3 ? 2 :
9050 i == Xamoeba_4 ? 3 :
9051 i == Xamoeba_5 ? 0 :
9052 i == Xamoeba_6 ? 1 :
9053 i == Xamoeba_7 ? 2 :
9054 i == Xamoeba_8 ? 3 :
9055 i == Xexit_2 ? j + 8 :
9056 i == Xexit_3 ? j + 16 :
9057 i == Xdynamite_1 ? 0 :
9058 i == Xdynamite_2 ? 8 :
9059 i == Xdynamite_3 ? 16 :
9060 i == Xdynamite_4 ? 24 :
9061 i == Xsand_stonein_1 ? j + 1 :
9062 i == Xsand_stonein_2 ? j + 9 :
9063 i == Xsand_stonein_3 ? j + 17 :
9064 i == Xsand_stonein_4 ? j + 25 :
9065 i == Xsand_stoneout_1 && j == 0 ? 0 :
9066 i == Xsand_stoneout_1 && j == 1 ? 0 :
9067 i == Xsand_stoneout_1 && j == 2 ? 1 :
9068 i == Xsand_stoneout_1 && j == 3 ? 2 :
9069 i == Xsand_stoneout_1 && j == 4 ? 2 :
9070 i == Xsand_stoneout_1 && j == 5 ? 3 :
9071 i == Xsand_stoneout_1 && j == 6 ? 4 :
9072 i == Xsand_stoneout_1 && j == 7 ? 4 :
9073 i == Xsand_stoneout_2 && j == 0 ? 5 :
9074 i == Xsand_stoneout_2 && j == 1 ? 6 :
9075 i == Xsand_stoneout_2 && j == 2 ? 7 :
9076 i == Xsand_stoneout_2 && j == 3 ? 8 :
9077 i == Xsand_stoneout_2 && j == 4 ? 9 :
9078 i == Xsand_stoneout_2 && j == 5 ? 11 :
9079 i == Xsand_stoneout_2 && j == 6 ? 13 :
9080 i == Xsand_stoneout_2 && j == 7 ? 15 :
9081 i == Xboom_bug && j == 1 ? 2 :
9082 i == Xboom_bug && j == 2 ? 2 :
9083 i == Xboom_bug && j == 3 ? 4 :
9084 i == Xboom_bug && j == 4 ? 4 :
9085 i == Xboom_bug && j == 5 ? 2 :
9086 i == Xboom_bug && j == 6 ? 2 :
9087 i == Xboom_bug && j == 7 ? 0 :
9088 i == Xboom_tank && j == 1 ? 2 :
9089 i == Xboom_tank && j == 2 ? 2 :
9090 i == Xboom_tank && j == 3 ? 4 :
9091 i == Xboom_tank && j == 4 ? 4 :
9092 i == Xboom_tank && j == 5 ? 2 :
9093 i == Xboom_tank && j == 6 ? 2 :
9094 i == Xboom_tank && j == 7 ? 0 :
9095 i == Xboom_android && j == 7 ? 6 :
9096 i == Xboom_1 && j == 1 ? 2 :
9097 i == Xboom_1 && j == 2 ? 2 :
9098 i == Xboom_1 && j == 3 ? 4 :
9099 i == Xboom_1 && j == 4 ? 4 :
9100 i == Xboom_1 && j == 5 ? 6 :
9101 i == Xboom_1 && j == 6 ? 6 :
9102 i == Xboom_1 && j == 7 ? 8 :
9103 i == Xboom_2 && j == 0 ? 8 :
9104 i == Xboom_2 && j == 1 ? 8 :
9105 i == Xboom_2 && j == 2 ? 10 :
9106 i == Xboom_2 && j == 3 ? 10 :
9107 i == Xboom_2 && j == 4 ? 10 :
9108 i == Xboom_2 && j == 5 ? 12 :
9109 i == Xboom_2 && j == 6 ? 12 :
9110 i == Xboom_2 && j == 7 ? 12 :
9111 special_animation && j == 4 ? 3 :
9112 effective_action != action ? 0 :
9114 int frame = getAnimationFrame(g->anim_frames,
9117 g->anim_start_frame,
9120 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9121 g->double_movement && is_backside);
9123 g_em->bitmap = src_bitmap;
9124 g_em->src_x = src_x;
9125 g_em->src_y = src_y;
9126 g_em->src_offset_x = 0;
9127 g_em->src_offset_y = 0;
9128 g_em->dst_offset_x = 0;
9129 g_em->dst_offset_y = 0;
9130 g_em->width = TILEX;
9131 g_em->height = TILEY;
9133 g_em->preserve_background = FALSE;
9135 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9138 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9139 effective_action == ACTION_MOVING ||
9140 effective_action == ACTION_PUSHING ||
9141 effective_action == ACTION_EATING)) ||
9142 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9143 effective_action == ACTION_EMPTYING)))
9146 (effective_action == ACTION_FALLING ||
9147 effective_action == ACTION_FILLING ||
9148 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9149 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9150 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9151 int num_steps = (i == Ydrip_1_s ? 16 :
9152 i == Ydrip_1_sB ? 16 :
9153 i == Ydrip_2_s ? 16 :
9154 i == Ydrip_2_sB ? 16 :
9155 i == Xsand_stonein_1 ? 32 :
9156 i == Xsand_stonein_2 ? 32 :
9157 i == Xsand_stonein_3 ? 32 :
9158 i == Xsand_stonein_4 ? 32 :
9159 i == Xsand_stoneout_1 ? 16 :
9160 i == Xsand_stoneout_2 ? 16 : 8);
9161 int cx = ABS(dx) * (TILEX / num_steps);
9162 int cy = ABS(dy) * (TILEY / num_steps);
9163 int step_frame = (i == Ydrip_2_s ? j + 8 :
9164 i == Ydrip_2_sB ? j + 8 :
9165 i == Xsand_stonein_2 ? j + 8 :
9166 i == Xsand_stonein_3 ? j + 16 :
9167 i == Xsand_stonein_4 ? j + 24 :
9168 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9169 int step = (is_backside ? step_frame : num_steps - step_frame);
9171 if (is_backside) // tile where movement starts
9173 if (dx < 0 || dy < 0)
9175 g_em->src_offset_x = cx * step;
9176 g_em->src_offset_y = cy * step;
9180 g_em->dst_offset_x = cx * step;
9181 g_em->dst_offset_y = cy * step;
9184 else // tile where movement ends
9186 if (dx < 0 || dy < 0)
9188 g_em->dst_offset_x = cx * step;
9189 g_em->dst_offset_y = cy * step;
9193 g_em->src_offset_x = cx * step;
9194 g_em->src_offset_y = cy * step;
9198 g_em->width = TILEX - cx * step;
9199 g_em->height = TILEY - cy * step;
9202 // create unique graphic identifier to decide if tile must be redrawn
9203 /* bit 31 - 16 (16 bit): EM style graphic
9204 bit 15 - 12 ( 4 bit): EM style frame
9205 bit 11 - 6 ( 6 bit): graphic width
9206 bit 5 - 0 ( 6 bit): graphic height */
9207 g_em->unique_identifier =
9208 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9212 for (i = 0; i < GAME_TILE_MAX; i++)
9214 for (j = 0; j < 8; j++)
9216 int element = object_mapping[i].element_rnd;
9217 int action = object_mapping[i].action;
9218 int direction = object_mapping[i].direction;
9219 boolean is_backside = object_mapping[i].is_backside;
9220 int graphic_action = el_act_dir2img(element, action, direction);
9221 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9223 if ((action == ACTION_SMASHED_BY_ROCK ||
9224 action == ACTION_SMASHED_BY_SPRING ||
9225 action == ACTION_EATING) &&
9226 graphic_action == graphic_default)
9228 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9229 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9230 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9231 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9234 // no separate animation for "smashed by rock" -- use rock instead
9235 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9236 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9238 g_em->bitmap = g_xx->bitmap;
9239 g_em->src_x = g_xx->src_x;
9240 g_em->src_y = g_xx->src_y;
9241 g_em->src_offset_x = g_xx->src_offset_x;
9242 g_em->src_offset_y = g_xx->src_offset_y;
9243 g_em->dst_offset_x = g_xx->dst_offset_x;
9244 g_em->dst_offset_y = g_xx->dst_offset_y;
9245 g_em->width = g_xx->width;
9246 g_em->height = g_xx->height;
9247 g_em->unique_identifier = g_xx->unique_identifier;
9250 g_em->preserve_background = TRUE;
9255 for (p = 0; p < MAX_PLAYERS; p++)
9257 for (i = 0; i < PLY_MAX; i++)
9259 int element = player_mapping[p][i].element_rnd;
9260 int action = player_mapping[p][i].action;
9261 int direction = player_mapping[p][i].direction;
9263 for (j = 0; j < 8; j++)
9265 int effective_element = element;
9266 int effective_action = action;
9267 int graphic = (direction == MV_NONE ?
9268 el_act2img(effective_element, effective_action) :
9269 el_act_dir2img(effective_element, effective_action,
9271 struct GraphicInfo *g = &graphic_info[graphic];
9272 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9276 int frame = getAnimationFrame(g->anim_frames,
9279 g->anim_start_frame,
9282 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9284 g_em->bitmap = src_bitmap;
9285 g_em->src_x = src_x;
9286 g_em->src_y = src_y;
9287 g_em->src_offset_x = 0;
9288 g_em->src_offset_y = 0;
9289 g_em->dst_offset_x = 0;
9290 g_em->dst_offset_y = 0;
9291 g_em->width = TILEX;
9292 g_em->height = TILEY;
9298 static void CheckSaveEngineSnapshot_EM(int frame,
9299 boolean any_player_moving,
9300 boolean any_player_snapping,
9301 boolean any_player_dropping)
9303 if (frame == 7 && !any_player_dropping)
9305 if (!local_player->was_waiting)
9307 if (!CheckSaveEngineSnapshotToList())
9310 local_player->was_waiting = TRUE;
9313 else if (any_player_moving || any_player_snapping || any_player_dropping)
9315 local_player->was_waiting = FALSE;
9319 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9320 boolean murphy_is_dropping)
9322 if (murphy_is_waiting)
9324 if (!local_player->was_waiting)
9326 if (!CheckSaveEngineSnapshotToList())
9329 local_player->was_waiting = TRUE;
9334 local_player->was_waiting = FALSE;
9338 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9339 boolean button_released)
9341 if (button_released)
9343 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9344 CheckSaveEngineSnapshotToList();
9346 else if (element_clicked)
9348 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9349 CheckSaveEngineSnapshotToList();
9351 game.snapshot.changed_action = TRUE;
9355 boolean CheckSingleStepMode_EM(int frame,
9356 boolean any_player_moving,
9357 boolean any_player_snapping,
9358 boolean any_player_dropping)
9360 if (tape.single_step && tape.recording && !tape.pausing)
9361 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9362 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9364 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9365 any_player_snapping, any_player_dropping);
9367 return tape.pausing;
9370 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9371 boolean murphy_is_dropping)
9373 boolean murphy_starts_dropping = FALSE;
9376 for (i = 0; i < MAX_PLAYERS; i++)
9377 if (stored_player[i].force_dropping)
9378 murphy_starts_dropping = TRUE;
9380 if (tape.single_step && tape.recording && !tape.pausing)
9381 if (murphy_is_waiting && !murphy_starts_dropping)
9382 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9384 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9387 void CheckSingleStepMode_MM(boolean element_clicked,
9388 boolean button_released)
9390 if (tape.single_step && tape.recording && !tape.pausing)
9391 if (button_released)
9392 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9394 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9397 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9398 int graphic, int sync_frame)
9400 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9402 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9405 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9407 return (IS_NEXT_FRAME(sync_frame, graphic));
9410 int getGraphicInfo_Delay(int graphic)
9412 return graphic_info[graphic].anim_delay;
9415 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9417 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9420 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9426 void PlayMenuSoundExt(int sound)
9428 if (sound == SND_UNDEFINED)
9431 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9432 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9435 if (IS_LOOP_SOUND(sound))
9436 PlaySoundLoop(sound);
9441 void PlayMenuSound(void)
9443 PlayMenuSoundExt(menu.sound[game_status]);
9446 void PlayMenuSoundStereo(int sound, int stereo_position)
9448 if (sound == SND_UNDEFINED)
9451 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9452 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9455 if (IS_LOOP_SOUND(sound))
9456 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9458 PlaySoundStereo(sound, stereo_position);
9461 void PlayMenuSoundIfLoopExt(int sound)
9463 if (sound == SND_UNDEFINED)
9466 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9467 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9470 if (IS_LOOP_SOUND(sound))
9471 PlaySoundLoop(sound);
9474 void PlayMenuSoundIfLoop(void)
9476 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9479 void PlayMenuMusicExt(int music)
9481 if (music == MUS_UNDEFINED)
9484 if (!setup.sound_music)
9487 if (IS_LOOP_MUSIC(music))
9488 PlayMusicLoop(music);
9493 void PlayMenuMusic(void)
9495 char *curr_music = getCurrentlyPlayingMusicFilename();
9496 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9498 if (!strEqual(curr_music, next_music))
9499 PlayMenuMusicExt(menu.music[game_status]);
9502 void PlayMenuSoundsAndMusic(void)
9508 static void FadeMenuSounds(void)
9513 static void FadeMenuMusic(void)
9515 char *curr_music = getCurrentlyPlayingMusicFilename();
9516 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9518 if (!strEqual(curr_music, next_music))
9522 void FadeMenuSoundsAndMusic(void)
9528 void PlaySoundActivating(void)
9531 PlaySound(SND_MENU_ITEM_ACTIVATING);
9535 void PlaySoundSelecting(void)
9538 PlaySound(SND_MENU_ITEM_SELECTING);
9542 void ToggleFullscreenIfNeeded(void)
9544 // if setup and video fullscreen state are already matching, nothing do do
9545 if (setup.fullscreen == video.fullscreen_enabled ||
9546 !video.fullscreen_available)
9549 SDLSetWindowFullscreen(setup.fullscreen);
9551 // set setup value according to successfully changed fullscreen mode
9552 setup.fullscreen = video.fullscreen_enabled;
9555 void ChangeWindowScalingIfNeeded(void)
9557 // if setup and video window scaling are already matching, nothing do do
9558 if (setup.window_scaling_percent == video.window_scaling_percent ||
9559 video.fullscreen_enabled)
9562 SDLSetWindowScaling(setup.window_scaling_percent);
9564 // set setup value according to successfully changed window scaling
9565 setup.window_scaling_percent = video.window_scaling_percent;
9568 void ChangeVsyncModeIfNeeded(void)
9570 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9571 int video_vsync_mode = video.vsync_mode;
9573 // if setup and video vsync mode are already matching, nothing do do
9574 if (setup_vsync_mode == video_vsync_mode)
9577 // if renderer is using OpenGL, vsync mode can directly be changed
9578 SDLSetScreenVsyncMode(setup.vsync_mode);
9580 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9581 if (video.vsync_mode == video_vsync_mode)
9583 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9585 // save backbuffer content which gets lost when re-creating screen
9586 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9588 // force re-creating screen and renderer to set new vsync mode
9589 video.fullscreen_enabled = !setup.fullscreen;
9591 // when creating new renderer, destroy textures linked to old renderer
9592 FreeAllImageTextures(); // needs old renderer to free the textures
9594 // re-create screen and renderer (including change of vsync mode)
9595 ChangeVideoModeIfNeeded(setup.fullscreen);
9597 // set setup value according to successfully changed fullscreen mode
9598 setup.fullscreen = video.fullscreen_enabled;
9600 // restore backbuffer content from temporary backbuffer backup bitmap
9601 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9602 FreeBitmap(tmp_backbuffer);
9604 // update visible window/screen
9605 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9607 // when changing vsync mode, re-create textures for new renderer
9608 InitImageTextures();
9611 // set setup value according to successfully changed vsync mode
9612 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9615 static void JoinRectangles(int *x, int *y, int *width, int *height,
9616 int x2, int y2, int width2, int height2)
9618 // do not join with "off-screen" rectangle
9619 if (x2 == -1 || y2 == -1)
9624 *width = MAX(*width, width2);
9625 *height = MAX(*height, height2);
9628 void SetAnimStatus(int anim_status_new)
9630 if (anim_status_new == GAME_MODE_MAIN)
9631 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9632 else if (anim_status_new == GAME_MODE_NAMES)
9633 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9634 else if (anim_status_new == GAME_MODE_SCORES)
9635 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9637 global.anim_status_next = anim_status_new;
9639 // directly set screen modes that are entered without fading
9640 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9641 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9642 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9643 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9644 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9645 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9646 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9647 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9648 global.anim_status = global.anim_status_next;
9651 void SetGameStatus(int game_status_new)
9653 if (game_status_new != game_status)
9654 game_status_last_screen = game_status;
9656 game_status = game_status_new;
9658 SetAnimStatus(game_status_new);
9661 void SetFontStatus(int game_status_new)
9663 static int last_game_status = -1;
9665 if (game_status_new != -1)
9667 // set game status for font use after storing last game status
9668 last_game_status = game_status;
9669 game_status = game_status_new;
9673 // reset game status after font use from last stored game status
9674 game_status = last_game_status;
9678 void ResetFontStatus(void)
9683 void SetLevelSetInfo(char *identifier, int level_nr)
9685 setString(&levelset.identifier, identifier);
9687 levelset.level_nr = level_nr;
9690 boolean CheckIfAllViewportsHaveChanged(void)
9692 // if game status has not changed, viewports have not changed either
9693 if (game_status == game_status_last)
9696 // check if all viewports have changed with current game status
9698 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9699 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9700 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9701 int new_real_sx = vp_playfield->x;
9702 int new_real_sy = vp_playfield->y;
9703 int new_full_sxsize = vp_playfield->width;
9704 int new_full_sysize = vp_playfield->height;
9705 int new_dx = vp_door_1->x;
9706 int new_dy = vp_door_1->y;
9707 int new_dxsize = vp_door_1->width;
9708 int new_dysize = vp_door_1->height;
9709 int new_vx = vp_door_2->x;
9710 int new_vy = vp_door_2->y;
9711 int new_vxsize = vp_door_2->width;
9712 int new_vysize = vp_door_2->height;
9714 boolean playfield_viewport_has_changed =
9715 (new_real_sx != REAL_SX ||
9716 new_real_sy != REAL_SY ||
9717 new_full_sxsize != FULL_SXSIZE ||
9718 new_full_sysize != FULL_SYSIZE);
9720 boolean door_1_viewport_has_changed =
9723 new_dxsize != DXSIZE ||
9724 new_dysize != DYSIZE);
9726 boolean door_2_viewport_has_changed =
9729 new_vxsize != VXSIZE ||
9730 new_vysize != VYSIZE ||
9731 game_status_last == GAME_MODE_EDITOR);
9733 return (playfield_viewport_has_changed &&
9734 door_1_viewport_has_changed &&
9735 door_2_viewport_has_changed);
9738 boolean CheckFadeAll(void)
9740 return (CheckIfGlobalBorderHasChanged() ||
9741 CheckIfAllViewportsHaveChanged());
9744 void ChangeViewportPropertiesIfNeeded(void)
9746 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9747 FALSE : setup.small_game_graphics);
9748 int gfx_game_mode = getGlobalGameStatus(game_status);
9749 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9751 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9752 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9753 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9754 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9755 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9756 int new_win_xsize = vp_window->width;
9757 int new_win_ysize = vp_window->height;
9758 int border_left = vp_playfield->border_left;
9759 int border_right = vp_playfield->border_right;
9760 int border_top = vp_playfield->border_top;
9761 int border_bottom = vp_playfield->border_bottom;
9762 int new_sx = vp_playfield->x + border_left;
9763 int new_sy = vp_playfield->y + border_top;
9764 int new_sxsize = vp_playfield->width - border_left - border_right;
9765 int new_sysize = vp_playfield->height - border_top - border_bottom;
9766 int new_real_sx = vp_playfield->x;
9767 int new_real_sy = vp_playfield->y;
9768 int new_full_sxsize = vp_playfield->width;
9769 int new_full_sysize = vp_playfield->height;
9770 int new_dx = vp_door_1->x;
9771 int new_dy = vp_door_1->y;
9772 int new_dxsize = vp_door_1->width;
9773 int new_dysize = vp_door_1->height;
9774 int new_vx = vp_door_2->x;
9775 int new_vy = vp_door_2->y;
9776 int new_vxsize = vp_door_2->width;
9777 int new_vysize = vp_door_2->height;
9778 int new_ex = vp_door_3->x;
9779 int new_ey = vp_door_3->y;
9780 int new_exsize = vp_door_3->width;
9781 int new_eysize = vp_door_3->height;
9782 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9783 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9784 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9785 int new_scr_fieldx = new_sxsize / tilesize;
9786 int new_scr_fieldy = new_sysize / tilesize;
9787 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9788 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9789 boolean init_gfx_buffers = FALSE;
9790 boolean init_video_buffer = FALSE;
9791 boolean init_gadgets_and_anims = FALSE;
9792 boolean init_em_graphics = FALSE;
9794 if (new_win_xsize != WIN_XSIZE ||
9795 new_win_ysize != WIN_YSIZE)
9797 WIN_XSIZE = new_win_xsize;
9798 WIN_YSIZE = new_win_ysize;
9800 init_video_buffer = TRUE;
9801 init_gfx_buffers = TRUE;
9802 init_gadgets_and_anims = TRUE;
9804 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9807 if (new_scr_fieldx != SCR_FIELDX ||
9808 new_scr_fieldy != SCR_FIELDY)
9810 // this always toggles between MAIN and GAME when using small tile size
9812 SCR_FIELDX = new_scr_fieldx;
9813 SCR_FIELDY = new_scr_fieldy;
9815 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9826 new_sxsize != SXSIZE ||
9827 new_sysize != SYSIZE ||
9828 new_dxsize != DXSIZE ||
9829 new_dysize != DYSIZE ||
9830 new_vxsize != VXSIZE ||
9831 new_vysize != VYSIZE ||
9832 new_exsize != EXSIZE ||
9833 new_eysize != EYSIZE ||
9834 new_real_sx != REAL_SX ||
9835 new_real_sy != REAL_SY ||
9836 new_full_sxsize != FULL_SXSIZE ||
9837 new_full_sysize != FULL_SYSIZE ||
9838 new_tilesize_var != TILESIZE_VAR
9841 // ------------------------------------------------------------------------
9842 // determine next fading area for changed viewport definitions
9843 // ------------------------------------------------------------------------
9845 // start with current playfield area (default fading area)
9848 FADE_SXSIZE = FULL_SXSIZE;
9849 FADE_SYSIZE = FULL_SYSIZE;
9851 // add new playfield area if position or size has changed
9852 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9853 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9855 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9856 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9859 // add current and new door 1 area if position or size has changed
9860 if (new_dx != DX || new_dy != DY ||
9861 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9863 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9864 DX, DY, DXSIZE, DYSIZE);
9865 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9866 new_dx, new_dy, new_dxsize, new_dysize);
9869 // add current and new door 2 area if position or size has changed
9870 if (new_vx != VX || new_vy != VY ||
9871 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9873 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9874 VX, VY, VXSIZE, VYSIZE);
9875 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9876 new_vx, new_vy, new_vxsize, new_vysize);
9879 // ------------------------------------------------------------------------
9880 // handle changed tile size
9881 // ------------------------------------------------------------------------
9883 if (new_tilesize_var != TILESIZE_VAR)
9885 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9887 // changing tile size invalidates scroll values of engine snapshots
9888 FreeEngineSnapshotSingle();
9890 // changing tile size requires update of graphic mapping for EM engine
9891 init_em_graphics = TRUE;
9902 SXSIZE = new_sxsize;
9903 SYSIZE = new_sysize;
9904 DXSIZE = new_dxsize;
9905 DYSIZE = new_dysize;
9906 VXSIZE = new_vxsize;
9907 VYSIZE = new_vysize;
9908 EXSIZE = new_exsize;
9909 EYSIZE = new_eysize;
9910 REAL_SX = new_real_sx;
9911 REAL_SY = new_real_sy;
9912 FULL_SXSIZE = new_full_sxsize;
9913 FULL_SYSIZE = new_full_sysize;
9914 TILESIZE_VAR = new_tilesize_var;
9916 init_gfx_buffers = TRUE;
9917 init_gadgets_and_anims = TRUE;
9919 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9920 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9923 if (init_gfx_buffers)
9925 // Debug("tools:viewport", "init_gfx_buffers");
9927 SCR_FIELDX = new_scr_fieldx_buffers;
9928 SCR_FIELDY = new_scr_fieldy_buffers;
9932 SCR_FIELDX = new_scr_fieldx;
9933 SCR_FIELDY = new_scr_fieldy;
9935 SetDrawDeactivationMask(REDRAW_NONE);
9936 SetDrawBackgroundMask(REDRAW_FIELD);
9939 if (init_video_buffer)
9941 // Debug("tools:viewport", "init_video_buffer");
9943 FreeAllImageTextures(); // needs old renderer to free the textures
9945 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9946 InitImageTextures();
9949 if (init_gadgets_and_anims)
9951 // Debug("tools:viewport", "init_gadgets_and_anims");
9954 InitGlobalAnimations();
9957 if (init_em_graphics)
9959 InitGraphicInfo_EM();
9963 void OpenURL(char *url)
9965 #if SDL_VERSION_ATLEAST(2,0,14)
9968 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9969 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9970 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9974 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9976 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9980 // ============================================================================
9982 // ============================================================================
9984 #if defined(PLATFORM_WINDOWS)
9985 /* FILETIME of Jan 1 1970 00:00:00. */
9986 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9989 * timezone information is stored outside the kernel so tzp isn't used anymore.
9991 * Note: this function is not for Win32 high precision timing purpose. See
9994 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9997 SYSTEMTIME system_time;
9998 ULARGE_INTEGER ularge;
10000 GetSystemTime(&system_time);
10001 SystemTimeToFileTime(&system_time, &file_time);
10002 ularge.LowPart = file_time.dwLowDateTime;
10003 ularge.HighPart = file_time.dwHighDateTime;
10005 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10006 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10012 static char *test_init_uuid_random_function_simple(void)
10014 static char seed_text[100];
10015 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10017 sprintf(seed_text, "%d", seed);
10022 static char *test_init_uuid_random_function_better(void)
10024 static char seed_text[100];
10025 struct timeval current_time;
10027 gettimeofday(¤t_time, NULL);
10029 prng_seed_bytes(¤t_time, sizeof(current_time));
10031 sprintf(seed_text, "%ld.%ld",
10032 (long)current_time.tv_sec,
10033 (long)current_time.tv_usec);
10038 #if defined(PLATFORM_WINDOWS)
10039 static char *test_init_uuid_random_function_better_windows(void)
10041 static char seed_text[100];
10042 struct timeval current_time;
10044 gettimeofday_windows(¤t_time, NULL);
10046 prng_seed_bytes(¤t_time, sizeof(current_time));
10048 sprintf(seed_text, "%ld.%ld",
10049 (long)current_time.tv_sec,
10050 (long)current_time.tv_usec);
10056 static unsigned int test_uuid_random_function_simple(int max)
10058 return GetSimpleRandom(max);
10061 static unsigned int test_uuid_random_function_better(int max)
10063 return (max > 0 ? prng_get_uint() % max : 0);
10066 #if defined(PLATFORM_WINDOWS)
10067 #define NUM_UUID_TESTS 3
10069 #define NUM_UUID_TESTS 2
10072 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10074 struct hashtable *hash_seeds =
10075 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10076 struct hashtable *hash_uuids =
10077 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10078 static char message[100];
10081 char *random_name = (nr == 0 ? "simple" : "better");
10082 char *random_type = (always_seed ? "always" : "only once");
10083 char *(*init_random_function)(void) =
10085 test_init_uuid_random_function_simple :
10086 test_init_uuid_random_function_better);
10087 unsigned int (*random_function)(int) =
10089 test_uuid_random_function_simple :
10090 test_uuid_random_function_better);
10093 #if defined(PLATFORM_WINDOWS)
10096 random_name = "windows";
10097 init_random_function = test_init_uuid_random_function_better_windows;
10103 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10104 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10106 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10107 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10108 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10110 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10114 // always initialize random number generator at least once
10115 init_random_function();
10117 unsigned int time_start = SDL_GetTicks();
10119 for (i = 0; i < num_uuids; i++)
10123 char *seed = getStringCopy(init_random_function());
10125 hashtable_remove(hash_seeds, seed);
10126 hashtable_insert(hash_seeds, seed, "1");
10129 char *uuid = getStringCopy(getUUIDExt(random_function));
10131 hashtable_remove(hash_uuids, uuid);
10132 hashtable_insert(hash_uuids, uuid, "1");
10135 int num_unique_seeds = hashtable_count(hash_seeds);
10136 int num_unique_uuids = hashtable_count(hash_uuids);
10138 unsigned int time_needed = SDL_GetTicks() - time_start;
10140 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10142 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10145 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10147 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10148 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10150 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10152 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10154 Request(message, REQ_CONFIRM);
10156 hashtable_destroy(hash_seeds, 0);
10157 hashtable_destroy(hash_uuids, 0);
10160 void TestGeneratingUUIDs(void)
10162 int num_uuids = 1000000;
10165 for (i = 0; i < NUM_UUID_TESTS; i++)
10166 for (j = 0; j < 2; j++)
10167 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10169 CloseAllAndExit(0);