1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void MapToolButtons(unsigned int);
179 static void UnmapToolButtons(void);
180 static void HandleToolButtons(struct GadgetInfo *);
181 static int el_act_dir2crm(int, int, int);
182 static int el_act2crm(int, int);
184 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
185 static int request_gadget_id = -1;
187 static char *print_if_not_empty(int element)
189 static char *s = NULL;
190 char *token_name = element_info[element].token_name;
195 s = checked_malloc(strlen(token_name) + 10 + 1);
197 if (element != EL_EMPTY)
198 sprintf(s, "%d\t['%s']", element, token_name);
200 sprintf(s, "%d", element);
205 int getFieldbufferOffsetX_RND(int dir, int pos)
207 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
208 int dx = (dir & MV_HORIZONTAL ? pos : 0);
209 int dx_var = dx * TILESIZE_VAR / TILESIZE;
212 if (EVEN(SCR_FIELDX))
214 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
215 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
217 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
218 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
220 fx += (dx_var > 0 ? TILEX_VAR : 0);
227 if (full_lev_fieldx <= SCR_FIELDX)
229 if (EVEN(SCR_FIELDX))
230 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
232 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 int getFieldbufferOffsetY_RND(int dir, int pos)
240 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
241 int dy = (dir & MV_VERTICAL ? pos : 0);
242 int dy_var = dy * TILESIZE_VAR / TILESIZE;
245 if (EVEN(SCR_FIELDY))
247 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
248 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
250 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
251 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
253 fy += (dy_var > 0 ? TILEY_VAR : 0);
260 if (full_lev_fieldy <= SCR_FIELDY)
262 if (EVEN(SCR_FIELDY))
263 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
265 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 static int getLevelFromScreenX_RND(int sx)
273 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
276 int lx = LEVELX((px + dx) / TILESIZE_VAR);
281 static int getLevelFromScreenY_RND(int sy)
283 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
286 int ly = LEVELY((py + dy) / TILESIZE_VAR);
291 static int getLevelFromScreenX_EM(int sx)
293 int level_xsize = level.native_em_level->cav->width;
294 int full_xsize = level_xsize * TILESIZE_VAR;
296 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
298 int fx = getFieldbufferOffsetX_EM();
301 int lx = LEVELX((px + dx) / TILESIZE_VAR);
306 static int getLevelFromScreenY_EM(int sy)
308 int level_ysize = level.native_em_level->cav->height;
309 int full_ysize = level_ysize * TILESIZE_VAR;
311 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
313 int fy = getFieldbufferOffsetY_EM();
316 int ly = LEVELY((py + dy) / TILESIZE_VAR);
321 static int getLevelFromScreenX_SP(int sx)
323 int menBorder = setup.sp_show_border_elements;
324 int level_xsize = level.native_sp_level->width;
325 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
327 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
329 int fx = getFieldbufferOffsetX_SP();
332 int lx = LEVELX((px + dx) / TILESIZE_VAR);
337 static int getLevelFromScreenY_SP(int sy)
339 int menBorder = setup.sp_show_border_elements;
340 int level_ysize = level.native_sp_level->height;
341 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
343 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
345 int fy = getFieldbufferOffsetY_SP();
348 int ly = LEVELY((py + dy) / TILESIZE_VAR);
353 static int getLevelFromScreenX_MM(int sx)
355 int level_xsize = level.native_mm_level->fieldx;
356 int full_xsize = level_xsize * TILESIZE_VAR;
358 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
361 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
366 static int getLevelFromScreenY_MM(int sy)
368 int level_ysize = level.native_mm_level->fieldy;
369 int full_ysize = level_ysize * TILESIZE_VAR;
371 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
374 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
379 int getLevelFromScreenX(int x)
381 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
382 return getLevelFromScreenX_EM(x);
383 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
384 return getLevelFromScreenX_SP(x);
385 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
386 return getLevelFromScreenX_MM(x);
388 return getLevelFromScreenX_RND(x);
391 int getLevelFromScreenY(int y)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenY_EM(y);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenY_SP(y);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenY_MM(y);
400 return getLevelFromScreenY_RND(y);
403 int getScreenFieldSizeX(void)
405 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
408 int getScreenFieldSizeY(void)
410 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
413 void DumpTile(int x, int y)
420 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
423 if (!IN_LEV_FIELD(x, y))
425 Info("(not in level field)");
431 token_name = element_info[Tile[x][y]].token_name;
433 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
434 Info("Back: %s", print_if_not_empty(Back[x][y]));
435 Info("Store: %s", print_if_not_empty(Store[x][y]));
436 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
437 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
438 Info("MovPos: %d", MovPos[x][y]);
439 Info("MovDir: %d", MovDir[x][y]);
440 Info("MovDelay: %d", MovDelay[x][y]);
441 Info("ChangeDelay: %d", ChangeDelay[x][y]);
442 Info("CustomValue: %d", CustomValue[x][y]);
443 Info("GfxElement: %d", GfxElement[x][y]);
444 Info("GfxAction: %d", GfxAction[x][y]);
445 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
446 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 int GetDrawtoField(void)
486 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
489 static void RedrawPlayfield_RND(void)
491 if (game.envelope_active)
494 DrawLevel(REDRAW_ALL);
498 void RedrawPlayfield(void)
500 if (game_status != GAME_MODE_PLAYING)
503 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
504 RedrawPlayfield_EM(TRUE);
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
506 RedrawPlayfield_SP(TRUE);
507 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
508 RedrawPlayfield_MM();
509 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
510 RedrawPlayfield_RND();
512 BlitScreenToBitmap(backbuffer);
514 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
518 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
521 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
522 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
524 // may happen for "border.draw_masked.*" with undefined "global.border.*"
525 if (src_bitmap == NULL)
528 if (x == -1 && y == -1)
531 if (draw_target == DRAW_TO_SCREEN)
532 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
534 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
537 static void DrawMaskedBorderExt_FIELD(int draw_target)
539 if (global.border_status >= GAME_MODE_MAIN &&
540 global.border_status <= GAME_MODE_PLAYING &&
541 border.draw_masked[global.border_status])
542 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
546 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_1))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 (global.border_status != GAME_MODE_EDITOR ||
555 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
556 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
559 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
561 // when drawing to backbuffer, never draw border over open doors
562 if (draw_target == DRAW_TO_BACKBUFFER &&
563 (GetDoorState() & DOOR_OPEN_2))
566 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
567 global.border_status != GAME_MODE_EDITOR)
568 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
571 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
573 // currently not available
576 static void DrawMaskedBorderExt_ALL(int draw_target)
578 DrawMaskedBorderExt_FIELD(draw_target);
579 DrawMaskedBorderExt_DOOR_1(draw_target);
580 DrawMaskedBorderExt_DOOR_2(draw_target);
581 DrawMaskedBorderExt_DOOR_3(draw_target);
584 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
586 // never draw masked screen borders on borderless screens
587 if (global.border_status == GAME_MODE_LOADING ||
588 global.border_status == GAME_MODE_TITLE)
591 if (redraw_mask & REDRAW_ALL)
592 DrawMaskedBorderExt_ALL(draw_target);
595 if (redraw_mask & REDRAW_FIELD)
596 DrawMaskedBorderExt_FIELD(draw_target);
597 if (redraw_mask & REDRAW_DOOR_1)
598 DrawMaskedBorderExt_DOOR_1(draw_target);
599 if (redraw_mask & REDRAW_DOOR_2)
600 DrawMaskedBorderExt_DOOR_2(draw_target);
601 if (redraw_mask & REDRAW_DOOR_3)
602 DrawMaskedBorderExt_DOOR_3(draw_target);
606 void DrawMaskedBorder_FIELD(void)
608 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
611 void DrawMaskedBorder(int redraw_mask)
613 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
616 void DrawMaskedBorderToTarget(int draw_target)
618 if (draw_target == DRAW_TO_BACKBUFFER ||
619 draw_target == DRAW_TO_SCREEN)
621 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
625 int last_border_status = global.border_status;
627 if (draw_target == DRAW_TO_FADE_SOURCE)
629 global.border_status = gfx.fade_border_source_status;
630 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
632 else if (draw_target == DRAW_TO_FADE_TARGET)
634 global.border_status = gfx.fade_border_target_status;
635 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
638 // always use global border for PLAYING when restarting the game
639 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
640 global.border_status = GAME_MODE_PLAYING;
642 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
644 global.border_status = last_border_status;
645 gfx.masked_border_bitmap_ptr = backbuffer;
649 void DrawTileCursor(int draw_target, int drawing_stage)
651 int tile_cursor_active = (game_status == GAME_MODE_PLAYING);
653 DrawTileCursor_MM(draw_target, drawing_stage, tile_cursor_active);
656 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
658 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
661 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
663 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
664 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
666 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
669 void BlitScreenToBitmap(Bitmap *target_bitmap)
671 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
672 BlitScreenToBitmap_EM(target_bitmap);
673 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
674 BlitScreenToBitmap_SP(target_bitmap);
675 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
676 BlitScreenToBitmap_MM(target_bitmap);
677 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
678 BlitScreenToBitmap_RND(target_bitmap);
680 redraw_mask |= REDRAW_FIELD;
683 static void DrawFramesPerSecond(void)
686 int font_nr = FONT_TEXT_2;
687 int font_width = getFontWidth(font_nr);
688 int draw_deactivation_mask = GetDrawDeactivationMask();
689 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
691 // draw FPS with leading space (needed if field buffer deactivated)
692 sprintf(text, " %04.1f fps", global.frames_per_second);
694 // override draw deactivation mask (required for invisible warp mode)
695 SetDrawDeactivationMask(REDRAW_NONE);
697 // draw opaque FPS if field buffer deactivated, else draw masked FPS
698 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
699 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
701 // set draw deactivation mask to previous value
702 SetDrawDeactivationMask(draw_deactivation_mask);
704 // force full-screen redraw in this frame
705 redraw_mask = REDRAW_ALL;
709 static void PrintFrameTimeDebugging(void)
711 static unsigned int last_counter = 0;
712 unsigned int counter = Counter();
713 int diff_1 = counter - last_counter;
714 int diff_2 = diff_1 - GAME_FRAME_DELAY;
716 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
717 char diff_bar[2 * diff_2_max + 5];
721 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
723 for (i = 0; i < diff_2_max; i++)
724 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
725 i >= diff_2_max - diff_2_cut ? '-' : ' ');
727 diff_bar[pos++] = '|';
729 for (i = 0; i < diff_2_max; i++)
730 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
732 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
734 diff_bar[pos++] = '\0';
736 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
739 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
742 last_counter = counter;
746 static int unifiedRedrawMask(int mask)
748 if (mask & REDRAW_ALL)
751 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
757 static boolean equalRedrawMasks(int mask_1, int mask_2)
759 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
762 void BackToFront(void)
764 static int last_redraw_mask = REDRAW_NONE;
766 // force screen redraw in every frame to continue drawing global animations
767 // (but always use the last redraw mask to prevent unwanted side effects)
768 if (redraw_mask == REDRAW_NONE)
769 redraw_mask = last_redraw_mask;
771 last_redraw_mask = redraw_mask;
774 // masked border now drawn immediately when blitting backbuffer to window
776 // draw masked border to all viewports, if defined
777 DrawMaskedBorder(redraw_mask);
780 // draw frames per second (only if debug mode is enabled)
781 if (redraw_mask & REDRAW_FPS)
782 DrawFramesPerSecond();
784 // remove playfield redraw before potentially merging with doors redraw
785 if (DrawingDeactivated(REAL_SX, REAL_SY))
786 redraw_mask &= ~REDRAW_FIELD;
788 // redraw complete window if both playfield and (some) doors need redraw
789 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
790 redraw_mask = REDRAW_ALL;
792 /* although redrawing the whole window would be fine for normal gameplay,
793 being able to only redraw the playfield is required for deactivating
794 certain drawing areas (mainly playfield) to work, which is needed for
795 warp-forward to be fast enough (by skipping redraw of most frames) */
797 if (redraw_mask & REDRAW_ALL)
799 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
801 else if (redraw_mask & REDRAW_FIELD)
803 BlitBitmap(backbuffer, window,
804 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
806 else if (redraw_mask & REDRAW_DOORS)
808 // merge door areas to prevent calling screen redraw more than once
814 if (redraw_mask & REDRAW_DOOR_1)
818 x2 = MAX(x2, DX + DXSIZE);
819 y2 = MAX(y2, DY + DYSIZE);
822 if (redraw_mask & REDRAW_DOOR_2)
826 x2 = MAX(x2, VX + VXSIZE);
827 y2 = MAX(y2, VY + VYSIZE);
830 if (redraw_mask & REDRAW_DOOR_3)
834 x2 = MAX(x2, EX + EXSIZE);
835 y2 = MAX(y2, EY + EYSIZE);
838 // make sure that at least one pixel is blitted, and inside the screen
839 // (else nothing is blitted, causing the animations not to be updated)
840 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
841 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
842 x2 = MIN(MAX(1, x2), WIN_XSIZE);
843 y2 = MIN(MAX(1, y2), WIN_YSIZE);
845 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
848 redraw_mask = REDRAW_NONE;
851 PrintFrameTimeDebugging();
855 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
857 unsigned int frame_delay_value_old = GetVideoFrameDelay();
859 SetVideoFrameDelay(frame_delay_value);
863 SetVideoFrameDelay(frame_delay_value_old);
866 static int fade_type_skip = FADE_TYPE_NONE;
868 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
870 void (*draw_border_function)(void) = NULL;
871 int x, y, width, height;
872 int fade_delay, post_delay;
874 if (fade_type == FADE_TYPE_FADE_OUT)
876 if (fade_type_skip != FADE_TYPE_NONE)
878 // skip all fade operations until specified fade operation
879 if (fade_type & fade_type_skip)
880 fade_type_skip = FADE_TYPE_NONE;
885 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
889 redraw_mask |= fade_mask;
891 if (fade_type == FADE_TYPE_SKIP)
893 fade_type_skip = fade_mode;
898 fade_delay = fading.fade_delay;
899 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
901 if (fade_type_skip != FADE_TYPE_NONE)
903 // skip all fade operations until specified fade operation
904 if (fade_type & fade_type_skip)
905 fade_type_skip = FADE_TYPE_NONE;
910 if (global.autoplay_leveldir)
915 if (fade_mask == REDRAW_FIELD)
920 height = FADE_SYSIZE;
922 if (border.draw_masked_when_fading)
923 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
925 DrawMaskedBorder_FIELD(); // draw once
935 // when switching screens without fading, set fade delay to zero
936 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
939 // do not display black frame when fading out without fade delay
940 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
943 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
944 draw_border_function);
946 redraw_mask &= ~fade_mask;
948 ClearAutoRepeatKeyEvents();
951 static void SetScreenStates_BeforeFadingIn(void)
953 // temporarily set screen mode for animations to screen after fading in
954 global.anim_status = global.anim_status_next;
956 // store backbuffer with all animations that will be started after fading in
957 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
959 // set screen mode for animations back to fading
960 global.anim_status = GAME_MODE_PSEUDO_FADING;
963 static void SetScreenStates_AfterFadingIn(void)
965 // store new source screen (to use correct masked border for fading)
966 gfx.fade_border_source_status = global.border_status;
968 global.anim_status = global.anim_status_next;
971 static void SetScreenStates_BeforeFadingOut(void)
973 // store new target screen (to use correct masked border for fading)
974 gfx.fade_border_target_status = game_status;
976 // set screen mode for animations to fading
977 global.anim_status = GAME_MODE_PSEUDO_FADING;
979 // store backbuffer with all animations that will be stopped for fading out
980 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
983 static void SetScreenStates_AfterFadingOut(void)
985 global.border_status = game_status;
987 // always use global border for PLAYING when restarting the game
988 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
989 global.border_status = GAME_MODE_PLAYING;
992 void FadeIn(int fade_mask)
994 SetScreenStates_BeforeFadingIn();
997 DrawMaskedBorder(REDRAW_ALL);
1000 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1001 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1003 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1007 FADE_SXSIZE = FULL_SXSIZE;
1008 FADE_SYSIZE = FULL_SYSIZE;
1010 // activate virtual buttons depending on upcoming game status
1011 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1012 game_status == GAME_MODE_PLAYING && !tape.playing)
1013 SetOverlayActive(TRUE);
1015 SetScreenStates_AfterFadingIn();
1017 // force update of global animation status in case of rapid screen changes
1018 redraw_mask = REDRAW_ALL;
1022 void FadeOut(int fade_mask)
1024 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1025 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1026 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1029 SetScreenStates_BeforeFadingOut();
1031 SetTileCursorActive(FALSE);
1032 SetOverlayActive(FALSE);
1035 DrawMaskedBorder(REDRAW_ALL);
1038 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1039 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1041 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1043 SetScreenStates_AfterFadingOut();
1046 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1048 static struct TitleFadingInfo fading_leave_stored;
1051 fading_leave_stored = fading_leave;
1053 fading = fading_leave_stored;
1056 void FadeSetEnterMenu(void)
1058 fading = menu.enter_menu;
1060 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1063 void FadeSetLeaveMenu(void)
1065 fading = menu.leave_menu;
1067 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1070 void FadeSetEnterScreen(void)
1072 fading = menu.enter_screen[game_status];
1074 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1077 void FadeSetNextScreen(void)
1079 fading = menu.next_screen[game_status];
1081 // (do not overwrite fade mode set by FadeSetEnterScreen)
1082 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1085 void FadeSetLeaveScreen(void)
1087 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1090 void FadeSetFromType(int type)
1092 if (type & TYPE_ENTER_SCREEN)
1093 FadeSetEnterScreen();
1094 else if (type & TYPE_ENTER)
1096 else if (type & TYPE_LEAVE)
1100 void FadeSetDisabled(void)
1102 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1104 fading = fading_none;
1107 void FadeSkipNextFadeIn(void)
1109 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1112 void FadeSkipNextFadeOut(void)
1114 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1117 static int getGlobalGameStatus(int status)
1119 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1120 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1124 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1126 if (graphic == IMG_UNDEFINED)
1127 return IMG_UNDEFINED;
1129 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1131 return (graphic_info[graphic].bitmap != NULL || redefined ?
1132 graphic : default_graphic);
1135 static int getBackgroundImage(int graphic)
1137 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1140 static int getGlobalBorderImage(int graphic)
1142 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1145 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1147 int status = getGlobalGameStatus(status_raw);
1149 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1150 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1151 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1152 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1154 int graphic_final = getGlobalBorderImage(graphic);
1156 return graphic_info[graphic_final].bitmap;
1159 void SetBackgroundImage(int graphic, int redraw_mask)
1161 struct GraphicInfo *g = &graphic_info[graphic];
1162 struct GraphicInfo g_undefined = { 0 };
1164 if (graphic == IMG_UNDEFINED)
1167 // always use original size bitmap for backgrounds, if existing
1168 Bitmap *bitmap = (g->bitmaps != NULL &&
1169 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1170 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1172 // remove every mask before setting mask for window, and
1173 // remove window area mask before setting mask for main or door area
1174 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1176 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1177 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1178 SetBackgroundBitmap(bitmap, redraw_mask,
1180 g->width, g->height);
1183 void SetWindowBackgroundImageIfDefined(int graphic)
1185 if (graphic_info[graphic].bitmap)
1186 SetBackgroundImage(graphic, REDRAW_ALL);
1189 void SetMainBackgroundImageIfDefined(int graphic)
1191 if (graphic_info[graphic].bitmap)
1192 SetBackgroundImage(graphic, REDRAW_FIELD);
1195 void SetDoorBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1201 void SetWindowBackgroundImage(int graphic)
1203 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1206 void SetMainBackgroundImage(int graphic)
1208 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1211 void SetDoorBackgroundImage(int graphic)
1213 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1216 void SetPanelBackground(void)
1218 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1221 void DrawBackground(int x, int y, int width, int height)
1223 // "drawto" might still point to playfield buffer here (hall of fame)
1224 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1226 if (IN_GFX_FIELD_FULL(x, y))
1227 redraw_mask |= REDRAW_FIELD;
1228 else if (IN_GFX_DOOR_1(x, y))
1229 redraw_mask |= REDRAW_DOOR_1;
1230 else if (IN_GFX_DOOR_2(x, y))
1231 redraw_mask |= REDRAW_DOOR_2;
1232 else if (IN_GFX_DOOR_3(x, y))
1233 redraw_mask |= REDRAW_DOOR_3;
1236 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1238 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1240 if (font->bitmap == NULL)
1243 DrawBackground(x, y, width, height);
1246 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1248 struct GraphicInfo *g = &graphic_info[graphic];
1250 if (g->bitmap == NULL)
1253 DrawBackground(x, y, width, height);
1256 static int game_status_last = -1;
1257 static Bitmap *global_border_bitmap_last = NULL;
1258 static Bitmap *global_border_bitmap = NULL;
1259 static int real_sx_last = -1, real_sy_last = -1;
1260 static int full_sxsize_last = -1, full_sysize_last = -1;
1261 static int dx_last = -1, dy_last = -1;
1262 static int dxsize_last = -1, dysize_last = -1;
1263 static int vx_last = -1, vy_last = -1;
1264 static int vxsize_last = -1, vysize_last = -1;
1265 static int ex_last = -1, ey_last = -1;
1266 static int exsize_last = -1, eysize_last = -1;
1268 boolean CheckIfGlobalBorderHasChanged(void)
1270 // if game status has not changed, global border has not changed either
1271 if (game_status == game_status_last)
1274 // determine and store new global border bitmap for current game status
1275 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1277 return (global_border_bitmap_last != global_border_bitmap);
1280 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1282 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1283 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1285 // if game status has not changed, nothing has to be redrawn
1286 if (game_status == game_status_last)
1289 // redraw if last screen was title screen
1290 if (game_status_last == GAME_MODE_TITLE)
1293 // redraw if global screen border has changed
1294 if (CheckIfGlobalBorderHasChanged())
1297 // redraw if position or size of playfield area has changed
1298 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1299 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1302 // redraw if position or size of door area has changed
1303 if (dx_last != DX || dy_last != DY ||
1304 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1307 // redraw if position or size of tape area has changed
1308 if (vx_last != VX || vy_last != VY ||
1309 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1312 // redraw if position or size of editor area has changed
1313 if (ex_last != EX || ey_last != EY ||
1314 exsize_last != EXSIZE || eysize_last != EYSIZE)
1321 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1324 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1326 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1329 void RedrawGlobalBorder(void)
1331 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1333 RedrawGlobalBorderFromBitmap(bitmap);
1335 redraw_mask = REDRAW_ALL;
1338 static void RedrawGlobalBorderIfNeeded(void)
1340 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1341 if (game_status == game_status_last)
1345 // copy current draw buffer to later copy back areas that have not changed
1346 if (game_status_last != GAME_MODE_TITLE)
1347 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1349 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1350 if (CheckIfGlobalBorderRedrawIsNeeded())
1352 // determine and store new global border bitmap for current game status
1353 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1356 // redraw global screen border (or clear, if defined to be empty)
1357 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1359 if (game_status == GAME_MODE_EDITOR)
1360 DrawSpecialEditorDoor();
1362 // copy previous playfield and door areas, if they are defined on both
1363 // previous and current screen and if they still have the same size
1365 if (real_sx_last != -1 && real_sy_last != -1 &&
1366 REAL_SX != -1 && REAL_SY != -1 &&
1367 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1368 BlitBitmap(bitmap_db_store_1, backbuffer,
1369 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1372 if (dx_last != -1 && dy_last != -1 &&
1373 DX != -1 && DY != -1 &&
1374 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1378 if (game_status != GAME_MODE_EDITOR)
1380 if (vx_last != -1 && vy_last != -1 &&
1381 VX != -1 && VY != -1 &&
1382 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1388 if (ex_last != -1 && ey_last != -1 &&
1389 EX != -1 && EY != -1 &&
1390 exsize_last == EXSIZE && eysize_last == EYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1395 redraw_mask = REDRAW_ALL;
1398 game_status_last = game_status;
1400 global_border_bitmap_last = global_border_bitmap;
1402 real_sx_last = REAL_SX;
1403 real_sy_last = REAL_SY;
1404 full_sxsize_last = FULL_SXSIZE;
1405 full_sysize_last = FULL_SYSIZE;
1408 dxsize_last = DXSIZE;
1409 dysize_last = DYSIZE;
1412 vxsize_last = VXSIZE;
1413 vysize_last = VYSIZE;
1416 exsize_last = EXSIZE;
1417 eysize_last = EYSIZE;
1420 void ClearField(void)
1422 RedrawGlobalBorderIfNeeded();
1424 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1425 // (when entering hall of fame after playing)
1426 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1428 // !!! maybe this should be done before clearing the background !!!
1429 if (game_status == GAME_MODE_PLAYING)
1431 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1432 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1436 SetDrawtoField(DRAW_TO_BACKBUFFER);
1440 void MarkTileDirty(int x, int y)
1442 redraw_mask |= REDRAW_FIELD;
1445 void SetBorderElement(void)
1449 BorderElement = EL_EMPTY;
1451 // only the R'n'D game engine may use an additional steelwall border
1452 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1455 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1457 for (x = 0; x < lev_fieldx; x++)
1459 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1460 BorderElement = EL_STEELWALL;
1462 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1468 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1469 int max_array_fieldx, int max_array_fieldy,
1470 short field[max_array_fieldx][max_array_fieldy],
1471 int max_fieldx, int max_fieldy)
1473 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1474 struct XY *check = xy_topdown;
1475 int old_element = field[start_x][start_y];
1478 // do nothing if start field already has the desired content
1479 if (old_element == fill_element)
1482 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1484 while (stack_pos > 0)
1486 struct XY current = stack_buffer[--stack_pos];
1489 field[current.x][current.y] = fill_element;
1491 for (i = 0; i < 4; i++)
1493 int x = current.x + check[i].x;
1494 int y = current.y + check[i].y;
1496 // check for stack buffer overflow (should not happen)
1497 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1498 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1500 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1501 stack_buffer[stack_pos++] = (struct XY){ x, y };
1506 void FloodFillLevel(int from_x, int from_y, int fill_element,
1507 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1508 int max_fieldx, int max_fieldy)
1510 FloodFillLevelExt(from_x, from_y, fill_element,
1511 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1512 max_fieldx, max_fieldy);
1515 void SetRandomAnimationValue(int x, int y)
1517 gfx.anim_random_frame = GfxRandom[x][y];
1520 int getGraphicAnimationFrame(int graphic, int sync_frame)
1522 // animation synchronized with global frame counter, not move position
1523 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1524 sync_frame = FrameCounter;
1525 else if (graphic_info[graphic].anim_global_anim_sync)
1526 sync_frame = getGlobalAnimSyncFrame();
1528 return getAnimationFrame(graphic_info[graphic].anim_frames,
1529 graphic_info[graphic].anim_delay,
1530 graphic_info[graphic].anim_mode,
1531 graphic_info[graphic].anim_start_frame,
1535 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1537 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1539 struct GraphicInfo *g = &graphic_info[graphic];
1540 int xsize = MAX(1, g->anim_frames_per_line);
1541 int ysize = MAX(1, g->anim_frames / xsize);
1542 int xoffset = g->anim_start_frame % xsize;
1543 int yoffset = g->anim_start_frame % ysize;
1544 // may be needed if screen field is significantly larger than playfield
1545 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1546 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1547 int sync_frame = y * xsize + x;
1549 return sync_frame % g->anim_frames;
1551 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1553 struct GraphicInfo *g = &graphic_info[graphic];
1554 // may be needed if screen field is significantly larger than playfield
1555 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1556 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1557 int sync_frame = GfxRandomStatic[x][y];
1559 return sync_frame % g->anim_frames;
1563 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1565 return getGraphicAnimationFrame(graphic, sync_frame);
1569 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1571 struct GraphicInfo *g = &graphic_info[graphic];
1572 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1574 if (tilesize == gfx.standard_tile_size)
1575 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1576 else if (tilesize == game.tile_size)
1577 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1579 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1582 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1583 boolean get_backside)
1585 struct GraphicInfo *g = &graphic_info[graphic];
1586 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1587 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1589 if (g->offset_y == 0) // frames are ordered horizontally
1591 int max_width = g->anim_frames_per_line * g->width;
1592 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1594 *x = pos % max_width;
1595 *y = src_y % g->height + pos / max_width * g->height;
1597 else if (g->offset_x == 0) // frames are ordered vertically
1599 int max_height = g->anim_frames_per_line * g->height;
1600 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1602 *x = src_x % g->width + pos / max_height * g->width;
1603 *y = pos % max_height;
1605 else // frames are ordered diagonally
1607 *x = src_x + frame * g->offset_x;
1608 *y = src_y + frame * g->offset_y;
1612 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1613 Bitmap **bitmap, int *x, int *y,
1614 boolean get_backside)
1616 struct GraphicInfo *g = &graphic_info[graphic];
1618 // if no graphics defined at all, use fallback graphics
1619 if (g->bitmaps == NULL)
1620 *g = graphic_info[IMG_CHAR_EXCLAM];
1622 // if no in-game graphics defined, always use standard graphic size
1623 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1624 tilesize = TILESIZE;
1626 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1627 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1629 *x = *x * tilesize / g->tile_size;
1630 *y = *y * tilesize / g->tile_size;
1633 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1634 Bitmap **bitmap, int *x, int *y)
1636 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1639 void getFixedGraphicSource(int graphic, int frame,
1640 Bitmap **bitmap, int *x, int *y)
1642 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1645 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1647 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1650 void getGlobalAnimGraphicSource(int graphic, int frame,
1651 Bitmap **bitmap, int *x, int *y)
1653 struct GraphicInfo *g = &graphic_info[graphic];
1655 // if no graphics defined at all, use fallback graphics
1656 if (g->bitmaps == NULL)
1657 *g = graphic_info[IMG_CHAR_EXCLAM];
1659 // use original size graphics, if existing, else use standard size graphics
1660 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1661 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1663 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1665 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1668 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1669 int *x, int *y, boolean get_backside)
1671 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1675 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1677 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1680 void DrawGraphic(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1686 Debug("draw:DrawGraphic", "This should never happen!");
1692 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1695 MarkTileDirty(x, y);
1698 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1701 if (!IN_SCR_FIELD(x, y))
1703 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1705 Debug("draw:DrawFixedGraphic", "This should never happen!");
1711 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1713 MarkTileDirty(x, y);
1716 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1722 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1724 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1727 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1733 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1734 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1737 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1740 if (!IN_SCR_FIELD(x, y))
1742 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1744 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1750 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1753 MarkTileDirty(x, y);
1756 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1759 if (!IN_SCR_FIELD(x, y))
1761 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1763 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1769 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1771 MarkTileDirty(x, y);
1774 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1780 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1782 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1786 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1787 int graphic, int frame)
1792 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1794 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1798 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1800 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1802 MarkTileDirty(x / tilesize, y / tilesize);
1805 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1808 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1809 graphic, frame, tilesize);
1810 MarkTileDirty(x / tilesize, y / tilesize);
1813 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1819 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1820 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1823 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1824 int frame, int tilesize)
1829 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1830 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1833 void DrawMiniGraphic(int x, int y, int graphic)
1835 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1836 MarkTileDirty(x / 2, y / 2);
1839 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1844 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1845 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1848 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1849 int graphic, int frame,
1850 int cut_mode, int mask_mode)
1855 int width = TILEX, height = TILEY;
1858 if (dx || dy) // shifted graphic
1860 if (x < BX1) // object enters playfield from the left
1867 else if (x > BX2) // object enters playfield from the right
1873 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1879 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1881 else if (dx) // general horizontal movement
1882 MarkTileDirty(x + SIGN(dx), y);
1884 if (y < BY1) // object enters playfield from the top
1886 if (cut_mode == CUT_BELOW) // object completely above top border
1894 else if (y > BY2) // object enters playfield from the bottom
1900 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1906 else if (dy > 0 && cut_mode == CUT_ABOVE)
1908 if (y == BY2) // object completely above bottom border
1914 MarkTileDirty(x, y + 1);
1915 } // object leaves playfield to the bottom
1916 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1918 else if (dy) // general vertical movement
1919 MarkTileDirty(x, y + SIGN(dy));
1923 if (!IN_SCR_FIELD(x, y))
1925 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1927 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1933 width = width * TILESIZE_VAR / TILESIZE;
1934 height = height * TILESIZE_VAR / TILESIZE;
1935 cx = cx * TILESIZE_VAR / TILESIZE;
1936 cy = cy * TILESIZE_VAR / TILESIZE;
1937 dx = dx * TILESIZE_VAR / TILESIZE;
1938 dy = dy * TILESIZE_VAR / TILESIZE;
1940 if (width > 0 && height > 0)
1942 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1947 dst_x = FX + x * TILEX_VAR + dx;
1948 dst_y = FY + y * TILEY_VAR + dy;
1950 if (mask_mode == USE_MASKING)
1951 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1957 MarkTileDirty(x, y);
1961 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1962 int graphic, int frame,
1963 int cut_mode, int mask_mode)
1968 int width = TILEX_VAR, height = TILEY_VAR;
1971 int x2 = x + SIGN(dx);
1972 int y2 = y + SIGN(dy);
1974 // movement with two-tile animations must be sync'ed with movement position,
1975 // not with current GfxFrame (which can be higher when using slow movement)
1976 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1977 int anim_frames = graphic_info[graphic].anim_frames;
1979 // (we also need anim_delay here for movement animations with less frames)
1980 int anim_delay = graphic_info[graphic].anim_delay;
1981 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1983 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1984 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1986 // re-calculate animation frame for two-tile movement animation
1987 frame = getGraphicAnimationFrame(graphic, sync_frame);
1989 // check if movement start graphic inside screen area and should be drawn
1990 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1992 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1994 dst_x = FX + x1 * TILEX_VAR;
1995 dst_y = FY + y1 * TILEY_VAR;
1997 if (mask_mode == USE_MASKING)
1998 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2001 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2004 MarkTileDirty(x1, y1);
2007 // check if movement end graphic inside screen area and should be drawn
2008 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2010 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2012 dst_x = FX + x2 * TILEX_VAR;
2013 dst_y = FY + y2 * TILEY_VAR;
2015 if (mask_mode == USE_MASKING)
2016 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2019 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2022 MarkTileDirty(x2, y2);
2026 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2027 int graphic, int frame,
2028 int cut_mode, int mask_mode)
2032 DrawGraphic(x, y, graphic, frame);
2037 if (graphic_info[graphic].double_movement) // EM style movement images
2038 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2043 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2044 int graphic, int frame, int cut_mode)
2046 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2049 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2050 int cut_mode, int mask_mode)
2052 int lx = LEVELX(x), ly = LEVELY(y);
2056 if (IN_LEV_FIELD(lx, ly))
2058 if (element == EL_EMPTY)
2059 element = GfxElementEmpty[lx][ly];
2061 SetRandomAnimationValue(lx, ly);
2063 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2064 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2066 // do not use double (EM style) movement graphic when not moving
2067 if (graphic_info[graphic].double_movement && !dx && !dy)
2069 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2070 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2073 if (game.use_masked_elements && (dx || dy))
2074 mask_mode = USE_MASKING;
2076 else // border element
2078 graphic = el2img(element);
2079 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2082 if (element == EL_EXPANDABLE_WALL)
2084 boolean left_stopped = FALSE, right_stopped = FALSE;
2086 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2087 left_stopped = TRUE;
2088 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2089 right_stopped = TRUE;
2091 if (left_stopped && right_stopped)
2093 else if (left_stopped)
2095 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2096 frame = graphic_info[graphic].anim_frames - 1;
2098 else if (right_stopped)
2100 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2101 frame = graphic_info[graphic].anim_frames - 1;
2106 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2107 else if (mask_mode == USE_MASKING)
2108 DrawGraphicThruMask(x, y, graphic, frame);
2110 DrawGraphic(x, y, graphic, frame);
2113 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2114 int cut_mode, int mask_mode)
2116 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2117 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2118 cut_mode, mask_mode);
2121 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2124 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2127 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2130 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2133 void DrawLevelElementThruMask(int x, int y, int element)
2135 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2138 void DrawLevelFieldThruMask(int x, int y)
2140 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2143 // !!! implementation of quicksand is totally broken !!!
2144 #define IS_CRUMBLED_TILE(x, y, e) \
2145 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2146 !IS_MOVING(x, y) || \
2147 (e) == EL_QUICKSAND_EMPTYING || \
2148 (e) == EL_QUICKSAND_FAST_EMPTYING))
2150 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2155 int width, height, cx, cy;
2156 int sx = SCREENX(x), sy = SCREENY(y);
2157 int crumbled_border_size = graphic_info[graphic].border_size;
2158 int crumbled_tile_size = graphic_info[graphic].tile_size;
2159 int crumbled_border_size_var =
2160 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2163 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2165 for (i = 1; i < 4; i++)
2167 int dxx = (i & 1 ? dx : 0);
2168 int dyy = (i & 2 ? dy : 0);
2171 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2174 // check if neighbour field is of same crumble type
2175 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2176 graphic_info[graphic].class ==
2177 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2179 // return if check prevents inner corner
2180 if (same == (dxx == dx && dyy == dy))
2184 // if we reach this point, we have an inner corner
2186 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2188 width = crumbled_border_size_var;
2189 height = crumbled_border_size_var;
2190 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2191 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2193 if (game.use_masked_elements)
2195 int graphic0 = el2img(EL_EMPTY);
2196 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2197 Bitmap *src_bitmap0;
2200 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2202 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2204 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2206 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2208 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2211 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2213 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2216 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2221 int width, height, bx, by, cx, cy;
2222 int sx = SCREENX(x), sy = SCREENY(y);
2223 int crumbled_border_size = graphic_info[graphic].border_size;
2224 int crumbled_tile_size = graphic_info[graphic].tile_size;
2225 int crumbled_border_size_var =
2226 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2227 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2230 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2232 // only needed when using masked elements
2233 int graphic0 = el2img(EL_EMPTY);
2234 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2235 Bitmap *src_bitmap0;
2238 if (game.use_masked_elements)
2239 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2241 // draw simple, sloppy, non-corner-accurate crumbled border
2243 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2244 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2245 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2246 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2248 if (game.use_masked_elements)
2250 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2252 FX + sx * TILEX_VAR + cx,
2253 FY + sy * TILEY_VAR + cy);
2255 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2257 FX + sx * TILEX_VAR + cx,
2258 FY + sy * TILEY_VAR + cy);
2261 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2263 FX + sx * TILEX_VAR + cx,
2264 FY + sy * TILEY_VAR + cy);
2266 // (remaining middle border part must be at least as big as corner part)
2267 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2268 crumbled_border_size_var >= TILESIZE_VAR / 3)
2271 // correct corners of crumbled border, if needed
2273 for (i = -1; i <= 1; i += 2)
2275 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2276 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2277 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2280 // check if neighbour field is of same crumble type
2281 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2282 graphic_info[graphic].class ==
2283 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2285 // no crumbled corner, but continued crumbled border
2287 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2288 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2289 int b1 = (i == 1 ? crumbled_border_size_var :
2290 TILESIZE_VAR - 2 * crumbled_border_size_var);
2292 width = crumbled_border_size_var;
2293 height = crumbled_border_size_var;
2295 if (dir == 1 || dir == 2)
2310 if (game.use_masked_elements)
2312 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2314 FX + sx * TILEX_VAR + cx,
2315 FY + sy * TILEY_VAR + cy);
2317 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2319 FX + sx * TILEX_VAR + cx,
2320 FY + sy * TILEY_VAR + cy);
2323 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2325 FX + sx * TILEX_VAR + cx,
2326 FY + sy * TILEY_VAR + cy);
2331 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2333 int sx = SCREENX(x), sy = SCREENY(y);
2336 struct XY *xy = xy_topdown;
2338 if (!IN_LEV_FIELD(x, y))
2341 element = TILE_GFX_ELEMENT(x, y);
2343 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2345 if (!IN_SCR_FIELD(sx, sy))
2348 // crumble field borders towards direct neighbour fields
2349 for (i = 0; i < 4; i++)
2351 int xx = x + xy[i].x;
2352 int yy = y + xy[i].y;
2354 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2357 // check if neighbour field is of same crumble type
2358 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2359 graphic_info[graphic].class ==
2360 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2363 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2366 // crumble inner field corners towards corner neighbour fields
2367 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2368 graphic_info[graphic].anim_frames == 2)
2370 for (i = 0; i < 4; i++)
2372 int dx = (i & 1 ? +1 : -1);
2373 int dy = (i & 2 ? +1 : -1);
2375 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2379 MarkTileDirty(sx, sy);
2381 else // center field is not crumbled -- crumble neighbour fields
2383 // crumble field borders of direct neighbour fields
2384 for (i = 0; i < 4; i++)
2386 int xx = x + xy[i].x;
2387 int yy = y + xy[i].y;
2388 int sxx = sx + xy[i].x;
2389 int syy = sy + xy[i].y;
2391 if (!IN_LEV_FIELD(xx, yy) ||
2392 !IN_SCR_FIELD(sxx, syy))
2395 // do not crumble fields that are being digged or snapped
2396 if (Tile[xx][yy] == EL_EMPTY ||
2397 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2400 element = TILE_GFX_ELEMENT(xx, yy);
2402 if (!IS_CRUMBLED_TILE(xx, yy, element))
2405 graphic = el_act2crm(element, ACTION_DEFAULT);
2407 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2409 MarkTileDirty(sxx, syy);
2412 // crumble inner field corners of corner neighbour fields
2413 for (i = 0; i < 4; i++)
2415 int dx = (i & 1 ? +1 : -1);
2416 int dy = (i & 2 ? +1 : -1);
2422 if (!IN_LEV_FIELD(xx, yy) ||
2423 !IN_SCR_FIELD(sxx, syy))
2426 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2429 element = TILE_GFX_ELEMENT(xx, yy);
2431 if (!IS_CRUMBLED_TILE(xx, yy, element))
2434 graphic = el_act2crm(element, ACTION_DEFAULT);
2436 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2437 graphic_info[graphic].anim_frames == 2)
2438 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2440 MarkTileDirty(sxx, syy);
2445 void DrawLevelFieldCrumbled(int x, int y)
2449 if (!IN_LEV_FIELD(x, y))
2452 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2453 GfxElement[x][y] != EL_UNDEFINED &&
2454 GFX_CRUMBLED(GfxElement[x][y]))
2456 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2461 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2463 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2466 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2469 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2470 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2471 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2472 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2473 int sx = SCREENX(x), sy = SCREENY(y);
2475 DrawScreenGraphic(sx, sy, graphic1, frame1);
2476 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2479 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2481 int sx = SCREENX(x), sy = SCREENY(y);
2482 struct XY *xy = xy_topdown;
2485 // crumble direct neighbour fields (required for field borders)
2486 for (i = 0; i < 4; i++)
2488 int xx = x + xy[i].x;
2489 int yy = y + xy[i].y;
2490 int sxx = sx + xy[i].x;
2491 int syy = sy + xy[i].y;
2493 if (!IN_LEV_FIELD(xx, yy) ||
2494 !IN_SCR_FIELD(sxx, syy) ||
2495 !GFX_CRUMBLED(Tile[xx][yy]) ||
2499 DrawLevelField(xx, yy);
2502 // crumble corner neighbour fields (required for inner field corners)
2503 for (i = 0; i < 4; i++)
2505 int dx = (i & 1 ? +1 : -1);
2506 int dy = (i & 2 ? +1 : -1);
2512 if (!IN_LEV_FIELD(xx, yy) ||
2513 !IN_SCR_FIELD(sxx, syy) ||
2514 !GFX_CRUMBLED(Tile[xx][yy]) ||
2518 int element = TILE_GFX_ELEMENT(xx, yy);
2519 int graphic = el_act2crm(element, ACTION_DEFAULT);
2521 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2522 graphic_info[graphic].anim_frames == 2)
2523 DrawLevelField(xx, yy);
2527 static int getBorderElement(int x, int y)
2531 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2532 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2533 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2534 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2535 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2536 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2537 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2539 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2540 int steel_position = (x == -1 && y == -1 ? 0 :
2541 x == lev_fieldx && y == -1 ? 1 :
2542 x == -1 && y == lev_fieldy ? 2 :
2543 x == lev_fieldx && y == lev_fieldy ? 3 :
2544 x == -1 || x == lev_fieldx ? 4 :
2545 y == -1 || y == lev_fieldy ? 5 : 6);
2547 return border[steel_position][steel_type];
2550 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2552 if (game.use_masked_elements)
2554 if (graphic != el2img(EL_EMPTY))
2555 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2557 DrawGraphicThruMask(x, y, graphic, frame);
2561 DrawGraphic(x, y, graphic, frame);
2565 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2567 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2570 void DrawScreenElement(int x, int y, int element)
2572 int mask_mode = NO_MASKING;
2574 if (game.use_masked_elements)
2576 int lx = LEVELX(x), ly = LEVELY(y);
2578 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2580 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2582 mask_mode = USE_MASKING;
2586 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2587 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2590 void DrawLevelElement(int x, int y, int element)
2592 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2593 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2596 void DrawScreenField(int x, int y)
2598 int lx = LEVELX(x), ly = LEVELY(y);
2599 int element, content;
2601 if (!IN_LEV_FIELD(lx, ly))
2603 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2606 element = getBorderElement(lx, ly);
2608 DrawScreenElement(x, y, element);
2613 element = Tile[lx][ly];
2614 content = Store[lx][ly];
2616 if (IS_MOVING(lx, ly))
2618 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2619 boolean cut_mode = NO_CUTTING;
2621 if (element == EL_QUICKSAND_EMPTYING ||
2622 element == EL_QUICKSAND_FAST_EMPTYING ||
2623 element == EL_MAGIC_WALL_EMPTYING ||
2624 element == EL_BD_MAGIC_WALL_EMPTYING ||
2625 element == EL_DC_MAGIC_WALL_EMPTYING ||
2626 element == EL_AMOEBA_DROPPING)
2627 cut_mode = CUT_ABOVE;
2628 else if (element == EL_QUICKSAND_FILLING ||
2629 element == EL_QUICKSAND_FAST_FILLING ||
2630 element == EL_MAGIC_WALL_FILLING ||
2631 element == EL_BD_MAGIC_WALL_FILLING ||
2632 element == EL_DC_MAGIC_WALL_FILLING)
2633 cut_mode = CUT_BELOW;
2635 if (cut_mode == CUT_ABOVE)
2636 DrawScreenElement(x, y, element);
2638 DrawScreenElement(x, y, EL_EMPTY);
2640 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2642 int dir = MovDir[lx][ly];
2643 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2644 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2646 if (IN_SCR_FIELD(newx, newy))
2647 DrawScreenElement(newx, newy, EL_EMPTY);
2651 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2652 else if (cut_mode == NO_CUTTING)
2653 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2656 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2658 if (cut_mode == CUT_BELOW &&
2659 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2660 DrawLevelElement(lx, ly + 1, element);
2663 if (content == EL_ACID)
2665 int dir = MovDir[lx][ly];
2666 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2667 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2669 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2671 // prevent target field from being drawn again (but without masking)
2672 // (this would happen if target field is scanned after moving element)
2673 Stop[newlx][newly] = TRUE;
2676 else if (IS_BLOCKED(lx, ly))
2681 boolean cut_mode = NO_CUTTING;
2682 int element_old, content_old;
2684 Blocked2Moving(lx, ly, &oldx, &oldy);
2687 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2688 MovDir[oldx][oldy] == MV_RIGHT);
2690 element_old = Tile[oldx][oldy];
2691 content_old = Store[oldx][oldy];
2693 if (element_old == EL_QUICKSAND_EMPTYING ||
2694 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2695 element_old == EL_MAGIC_WALL_EMPTYING ||
2696 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2697 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2698 element_old == EL_AMOEBA_DROPPING)
2699 cut_mode = CUT_ABOVE;
2701 DrawScreenElement(x, y, EL_EMPTY);
2704 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2706 else if (cut_mode == NO_CUTTING)
2707 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2710 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2713 else if (IS_DRAWABLE(element))
2714 DrawScreenElement(x, y, element);
2716 DrawScreenElement(x, y, EL_EMPTY);
2719 void DrawLevelField(int x, int y)
2721 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2722 DrawScreenField(SCREENX(x), SCREENY(y));
2723 else if (IS_MOVING(x, y))
2727 Moving2Blocked(x, y, &newx, &newy);
2728 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2729 DrawScreenField(SCREENX(newx), SCREENY(newy));
2731 else if (IS_BLOCKED(x, y))
2735 Blocked2Moving(x, y, &oldx, &oldy);
2736 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2737 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2741 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2742 int (*el2img_function)(int), boolean masked,
2743 int element_bits_draw)
2745 int element_base = map_mm_wall_element(element);
2746 int element_bits = (IS_DF_WALL(element) ?
2747 element - EL_DF_WALL_START :
2748 IS_MM_WALL(element) ?
2749 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2750 int graphic = el2img_function(element_base);
2751 int tilesize_draw = tilesize / 2;
2756 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2758 for (i = 0; i < 4; i++)
2760 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2761 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2763 if (!(element_bits_draw & (1 << i)))
2766 if (element_bits & (1 << i))
2769 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2770 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2772 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2773 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2778 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2779 tilesize_draw, tilesize_draw);
2784 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2785 boolean masked, int element_bits_draw)
2787 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2788 element, tilesize, el2edimg, masked, element_bits_draw);
2791 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2792 int (*el2img_function)(int))
2794 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2798 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2801 if (IS_MM_WALL(element))
2803 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2804 element, tilesize, el2edimg, masked, 0x000f);
2808 int graphic = el2edimg(element);
2811 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2813 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2817 void DrawSizedElement(int x, int y, int element, int tilesize)
2819 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2822 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2824 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2827 void DrawMiniElement(int x, int y, int element)
2831 graphic = el2edimg(element);
2832 DrawMiniGraphic(x, y, graphic);
2835 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2838 int x = sx + scroll_x, y = sy + scroll_y;
2840 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2841 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2842 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2843 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2845 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2848 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2850 int x = sx + scroll_x, y = sy + scroll_y;
2852 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2853 DrawMiniElement(sx, sy, EL_EMPTY);
2854 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2855 DrawMiniElement(sx, sy, Tile[x][y]);
2857 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2860 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2861 int x, int y, int xsize, int ysize,
2862 int tile_width, int tile_height)
2866 int dst_x = startx + x * tile_width;
2867 int dst_y = starty + y * tile_height;
2868 int width = graphic_info[graphic].width;
2869 int height = graphic_info[graphic].height;
2870 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2871 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2872 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2873 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2874 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2875 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2876 boolean draw_masked = graphic_info[graphic].draw_masked;
2878 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2880 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2882 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2886 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2887 inner_sx + (x - 1) * tile_width % inner_width);
2888 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2889 inner_sy + (y - 1) * tile_height % inner_height);
2892 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2895 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2899 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2900 int x, int y, int xsize, int ysize,
2903 int font_width = getFontWidth(font_nr);
2904 int font_height = getFontHeight(font_nr);
2906 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2907 font_width, font_height);
2910 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2912 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2913 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2914 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2915 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2916 boolean no_delay = (tape.warp_forward);
2917 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2918 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2919 DelayCounter anim_delay = { anim_delay_value };
2920 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2921 int font_width = getFontWidth(font_nr);
2922 int font_height = getFontHeight(font_nr);
2923 int max_xsize = level.envelope[envelope_nr].xsize;
2924 int max_ysize = level.envelope[envelope_nr].ysize;
2925 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2926 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2927 int xend = max_xsize;
2928 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2929 int xstep = (xstart < xend ? 1 : 0);
2930 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2932 int end = MAX(xend - xstart, yend - ystart);
2935 for (i = start; i <= end; i++)
2937 int last_frame = end; // last frame of this "for" loop
2938 int x = xstart + i * xstep;
2939 int y = ystart + i * ystep;
2940 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2941 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2942 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2943 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2946 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2948 BlitScreenToBitmap(backbuffer);
2950 SetDrawtoField(DRAW_TO_BACKBUFFER);
2952 for (yy = 0; yy < ysize; yy++)
2953 for (xx = 0; xx < xsize; xx++)
2954 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2956 DrawTextArea(sx + font_width, sy + font_height,
2957 level.envelope[envelope_nr].text, font_nr, max_xsize,
2958 xsize - 2, ysize - 2, 0, mask_mode,
2959 level.envelope[envelope_nr].autowrap,
2960 level.envelope[envelope_nr].centered, FALSE);
2962 redraw_mask |= REDRAW_FIELD;
2965 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2968 ClearAutoRepeatKeyEvents();
2971 void ShowEnvelope(int envelope_nr)
2973 int element = EL_ENVELOPE_1 + envelope_nr;
2974 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2975 int sound_opening = element_info[element].sound[ACTION_OPENING];
2976 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2977 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2978 boolean no_delay = (tape.warp_forward);
2979 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2980 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2981 int anim_mode = graphic_info[graphic].anim_mode;
2982 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2983 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2984 boolean overlay_enabled = GetOverlayEnabled();
2986 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2988 SetOverlayEnabled(FALSE);
2991 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2993 if (anim_mode == ANIM_DEFAULT)
2994 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2996 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2999 Delay_WithScreenUpdates(wait_delay_value);
3001 WaitForEventToContinue();
3004 SetOverlayEnabled(overlay_enabled);
3006 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3008 if (anim_mode != ANIM_NONE)
3009 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3011 if (anim_mode == ANIM_DEFAULT)
3012 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3014 game.envelope_active = FALSE;
3016 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3018 redraw_mask |= REDRAW_FIELD;
3022 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3023 int xsize, int ysize)
3025 if (!global.use_envelope_request)
3028 if (request.bitmap == NULL ||
3029 xsize > request.xsize ||
3030 ysize > request.ysize)
3032 if (request.bitmap != NULL)
3033 FreeBitmap(request.bitmap);
3035 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3037 SDL_Surface *surface = request.bitmap->surface;
3039 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3040 Fail("SDLGetNativeSurface() failed");
3043 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3045 // create masked surface for request bitmap, if needed
3046 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3048 SDL_Surface *surface = request.bitmap->surface;
3049 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3051 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3052 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3053 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3056 SDLFreeBitmapTextures(request.bitmap);
3057 SDLCreateBitmapTextures(request.bitmap);
3059 // set envelope request run-time values
3062 request.xsize = xsize;
3063 request.ysize = ysize;
3066 void DrawEnvelopeRequestToScreen(int drawing_target)
3068 if (global.use_envelope_request &&
3069 game.request_active &&
3070 drawing_target == DRAW_TO_SCREEN)
3072 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3073 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3074 request.sx, request.sy);
3076 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3077 request.sx, request.sy);
3081 static void setRequestBasePosition(int *x, int *y)
3083 int sx_base, sy_base;
3085 if (request.x != -1)
3086 sx_base = request.x;
3087 else if (request.align == ALIGN_LEFT)
3089 else if (request.align == ALIGN_RIGHT)
3090 sx_base = SX + SXSIZE;
3092 sx_base = SX + SXSIZE / 2;
3094 if (request.y != -1)
3095 sy_base = request.y;
3096 else if (request.valign == VALIGN_TOP)
3098 else if (request.valign == VALIGN_BOTTOM)
3099 sy_base = SY + SYSIZE;
3101 sy_base = SY + SYSIZE / 2;
3107 static void setRequestPositionExt(int *x, int *y, int width, int height,
3108 boolean add_border_size)
3110 int border_size = request.border_size;
3111 int sx_base, sy_base;
3114 setRequestBasePosition(&sx_base, &sy_base);
3116 if (request.align == ALIGN_LEFT)
3118 else if (request.align == ALIGN_RIGHT)
3119 sx = sx_base - width;
3121 sx = sx_base - width / 2;
3123 if (request.valign == VALIGN_TOP)
3125 else if (request.valign == VALIGN_BOTTOM)
3126 sy = sy_base - height;
3128 sy = sy_base - height / 2;
3130 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3131 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3133 if (add_border_size)
3143 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3145 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3148 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3150 DrawBuffer *drawto_last = drawto;
3151 char *text_final = text;
3152 char *text_door_style = NULL;
3153 int graphic = IMG_BACKGROUND_REQUEST;
3154 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3155 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3156 int font_nr = FONT_REQUEST;
3157 int font_width = getFontWidth(font_nr);
3158 int font_height = getFontHeight(font_nr);
3159 int border_size = request.border_size;
3160 int line_spacing = request.line_spacing;
3161 int line_height = font_height + line_spacing;
3162 int max_text_width = request.width - 2 * border_size;
3163 int max_text_height = request.height - 2 * border_size;
3164 int line_length = max_text_width / font_width;
3165 int max_lines = max_text_height / line_height;
3166 int text_width = line_length * font_width;
3167 int width = request.width;
3168 int height = request.height;
3169 int tile_size = MAX(request.step_offset, 1);
3170 int x_steps = width / tile_size;
3171 int y_steps = height / tile_size;
3172 int sx_offset = border_size;
3173 int sy_offset = border_size;
3177 if (request.centered)
3178 sx_offset = (request.width - text_width) / 2;
3180 if (request.wrap_single_words && !request.autowrap)
3182 char *src_text_ptr, *dst_text_ptr;
3184 text_door_style = checked_malloc(2 * strlen(text) + 1);
3186 src_text_ptr = text;
3187 dst_text_ptr = text_door_style;
3189 while (*src_text_ptr)
3191 if (*src_text_ptr == ' ' ||
3192 *src_text_ptr == '?' ||
3193 *src_text_ptr == '!')
3194 *dst_text_ptr++ = '\n';
3196 if (*src_text_ptr != ' ')
3197 *dst_text_ptr++ = *src_text_ptr;
3202 *dst_text_ptr = '\0';
3204 text_final = text_door_style;
3207 setRequestPosition(&sx, &sy, FALSE);
3209 // draw complete envelope request to temporary bitmap
3210 drawto = bitmap_db_store_1;
3212 ClearRectangle(drawto, sx, sy, width, height);
3214 for (y = 0; y < y_steps; y++)
3215 for (x = 0; x < x_steps; x++)
3216 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3217 x, y, x_steps, y_steps,
3218 tile_size, tile_size);
3220 // force DOOR font inside door area
3221 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3223 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3224 line_length, -1, max_lines, line_spacing, mask_mode,
3225 request.autowrap, request.centered, FALSE);
3229 MapToolButtons(req_state);
3231 // restore pointer to drawing buffer
3232 drawto = drawto_last;
3234 // prepare complete envelope request from temporary bitmap
3235 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3237 if (text_door_style)
3238 free(text_door_style);
3241 static void AnimateEnvelopeRequest(int anim_mode, int action)
3243 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3244 int delay_value_normal = request.step_delay;
3245 int delay_value_fast = delay_value_normal / 2;
3246 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3247 boolean no_delay = (tape.warp_forward);
3248 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3249 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3250 DelayCounter anim_delay = { anim_delay_value };
3252 int tile_size = MAX(request.step_offset, 1);
3253 int max_xsize = request.width / tile_size;
3254 int max_ysize = request.height / tile_size;
3255 int max_xsize_inner = max_xsize - 2;
3256 int max_ysize_inner = max_ysize - 2;
3258 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3259 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3260 int xend = max_xsize_inner;
3261 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3262 int xstep = (xstart < xend ? 1 : 0);
3263 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3265 int end = MAX(xend - xstart, yend - ystart);
3268 if (setup.quick_doors)
3275 for (i = start; i <= end; i++)
3277 int last_frame = end; // last frame of this "for" loop
3278 int x = xstart + i * xstep;
3279 int y = ystart + i * ystep;
3280 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3281 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3282 int xsize_size_left = (xsize - 1) * tile_size;
3283 int ysize_size_top = (ysize - 1) * tile_size;
3284 int max_xsize_pos = (max_xsize - 1) * tile_size;
3285 int max_ysize_pos = (max_ysize - 1) * tile_size;
3286 int width = xsize * tile_size;
3287 int height = ysize * tile_size;
3293 HandleGameActions();
3295 setRequestPosition(&src_x, &src_y, FALSE);
3296 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3298 for (yy = 0; yy < 2; yy++)
3300 for (xx = 0; xx < 2; xx++)
3302 int src_xx = src_x + xx * max_xsize_pos;
3303 int src_yy = src_y + yy * max_ysize_pos;
3304 int dst_xx = dst_x + xx * xsize_size_left;
3305 int dst_yy = dst_y + yy * ysize_size_top;
3306 int xx_size = (xx ? tile_size : xsize_size_left);
3307 int yy_size = (yy ? tile_size : ysize_size_top);
3309 // draw partial (animated) envelope request to temporary bitmap
3310 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3311 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3315 // prepare partial (animated) envelope request from temporary bitmap
3316 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3319 redraw_mask |= REDRAW_FIELD;
3323 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3326 ClearAutoRepeatKeyEvents();
3329 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3331 int graphic = IMG_BACKGROUND_REQUEST;
3332 int sound_opening = SND_REQUEST_OPENING;
3333 int sound_closing = SND_REQUEST_CLOSING;
3334 int anim_mode_1 = request.anim_mode; // (higher priority)
3335 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3336 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3337 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3338 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3340 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3342 if (action == ACTION_OPENING)
3344 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3346 if (anim_mode == ANIM_DEFAULT)
3347 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3349 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3353 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3355 if (anim_mode != ANIM_NONE)
3356 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3358 if (anim_mode == ANIM_DEFAULT)
3359 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3362 game.envelope_active = FALSE;
3365 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3367 if (IS_MM_WALL(element))
3369 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3375 int graphic = el2preimg(element);
3377 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3378 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3383 void DrawLevel(int draw_background_mask)
3387 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3388 SetDrawBackgroundMask(draw_background_mask);
3392 for (x = BX1; x <= BX2; x++)
3393 for (y = BY1; y <= BY2; y++)
3394 DrawScreenField(x, y);
3396 redraw_mask |= REDRAW_FIELD;
3399 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3404 for (x = 0; x < size_x; x++)
3405 for (y = 0; y < size_y; y++)
3406 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3408 redraw_mask |= REDRAW_FIELD;
3411 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3415 for (x = 0; x < size_x; x++)
3416 for (y = 0; y < size_y; y++)
3417 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3419 redraw_mask |= REDRAW_FIELD;
3422 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3424 boolean show_level_border = (BorderElement != EL_EMPTY);
3425 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3426 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3427 int tile_size = preview.tile_size;
3428 int preview_width = preview.xsize * tile_size;
3429 int preview_height = preview.ysize * tile_size;
3430 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3431 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3432 int real_preview_width = real_preview_xsize * tile_size;
3433 int real_preview_height = real_preview_ysize * tile_size;
3434 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3435 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3438 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3441 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3443 dst_x += (preview_width - real_preview_width) / 2;
3444 dst_y += (preview_height - real_preview_height) / 2;
3446 for (x = 0; x < real_preview_xsize; x++)
3448 for (y = 0; y < real_preview_ysize; y++)
3450 int lx = from_x + x + (show_level_border ? -1 : 0);
3451 int ly = from_y + y + (show_level_border ? -1 : 0);
3452 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3453 getBorderElement(lx, ly));
3455 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3456 element, tile_size);
3460 redraw_mask |= REDRAW_FIELD;
3463 #define MICROLABEL_EMPTY 0
3464 #define MICROLABEL_LEVEL_NAME 1
3465 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3466 #define MICROLABEL_LEVEL_AUTHOR 3
3467 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3468 #define MICROLABEL_IMPORTED_FROM 5
3469 #define MICROLABEL_IMPORTED_BY_HEAD 6
3470 #define MICROLABEL_IMPORTED_BY 7
3472 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3474 int max_text_width = SXSIZE;
3475 int font_width = getFontWidth(font_nr);
3477 if (pos->align == ALIGN_CENTER)
3478 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3479 else if (pos->align == ALIGN_RIGHT)
3480 max_text_width = pos->x;
3482 max_text_width = SXSIZE - pos->x;
3484 return max_text_width / font_width;
3487 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3489 char label_text[MAX_OUTPUT_LINESIZE + 1];
3490 int max_len_label_text;
3491 int font_nr = pos->font;
3494 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3497 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3498 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3499 mode == MICROLABEL_IMPORTED_BY_HEAD)
3500 font_nr = pos->font_alt;
3502 max_len_label_text = getMaxTextLength(pos, font_nr);
3504 if (pos->size != -1)
3505 max_len_label_text = pos->size;
3507 for (i = 0; i < max_len_label_text; i++)
3508 label_text[i] = ' ';
3509 label_text[max_len_label_text] = '\0';
3511 if (strlen(label_text) > 0)
3512 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3515 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3516 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3517 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3518 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3519 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3520 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3521 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3522 max_len_label_text);
3523 label_text[max_len_label_text] = '\0';
3525 if (strlen(label_text) > 0)
3526 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3528 redraw_mask |= REDRAW_FIELD;
3531 static void DrawPreviewLevelLabel(int mode)
3533 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3536 static void DrawPreviewLevelInfo(int mode)
3538 if (mode == MICROLABEL_LEVEL_NAME)
3539 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3540 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3541 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3544 static void DrawPreviewLevelExt(boolean restart)
3546 static DelayCounter scroll_delay = { 0 };
3547 static DelayCounter label_delay = { 0 };
3548 static int from_x, from_y, scroll_direction;
3549 static int label_state, label_counter;
3550 boolean show_level_border = (BorderElement != EL_EMPTY);
3551 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3552 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3554 scroll_delay.value = preview.step_delay;
3555 label_delay.value = MICROLEVEL_LABEL_DELAY;
3562 if (preview.anim_mode == ANIM_CENTERED)
3564 if (level_xsize > preview.xsize)
3565 from_x = (level_xsize - preview.xsize) / 2;
3566 if (level_ysize > preview.ysize)
3567 from_y = (level_ysize - preview.ysize) / 2;
3570 from_x += preview.xoffset;
3571 from_y += preview.yoffset;
3573 scroll_direction = MV_RIGHT;
3577 DrawPreviewLevelPlayfield(from_x, from_y);
3578 DrawPreviewLevelLabel(label_state);
3580 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3581 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3583 // initialize delay counters
3584 ResetDelayCounter(&scroll_delay);
3585 ResetDelayCounter(&label_delay);
3587 if (leveldir_current->name)
3589 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3590 char label_text[MAX_OUTPUT_LINESIZE + 1];
3591 int font_nr = pos->font;
3592 int max_len_label_text = getMaxTextLength(pos, font_nr);
3594 if (pos->size != -1)
3595 max_len_label_text = pos->size;
3597 strncpy(label_text, leveldir_current->name, max_len_label_text);
3598 label_text[max_len_label_text] = '\0';
3600 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3601 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3607 // scroll preview level, if needed
3608 if (preview.anim_mode != ANIM_NONE &&
3609 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3610 DelayReached(&scroll_delay))
3612 switch (scroll_direction)
3617 from_x -= preview.step_offset;
3618 from_x = (from_x < 0 ? 0 : from_x);
3621 scroll_direction = MV_UP;
3625 if (from_x < level_xsize - preview.xsize)
3627 from_x += preview.step_offset;
3628 from_x = (from_x > level_xsize - preview.xsize ?
3629 level_xsize - preview.xsize : from_x);
3632 scroll_direction = MV_DOWN;
3638 from_y -= preview.step_offset;
3639 from_y = (from_y < 0 ? 0 : from_y);
3642 scroll_direction = MV_RIGHT;
3646 if (from_y < level_ysize - preview.ysize)
3648 from_y += preview.step_offset;
3649 from_y = (from_y > level_ysize - preview.ysize ?
3650 level_ysize - preview.ysize : from_y);
3653 scroll_direction = MV_LEFT;
3660 DrawPreviewLevelPlayfield(from_x, from_y);
3663 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3664 // redraw micro level label, if needed
3665 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3666 !strEqual(level.author, ANONYMOUS_NAME) &&
3667 !strEqual(level.author, leveldir_current->name) &&
3668 DelayReached(&label_delay))
3670 int max_label_counter = 23;
3672 if (leveldir_current->imported_from != NULL &&
3673 strlen(leveldir_current->imported_from) > 0)
3674 max_label_counter += 14;
3675 if (leveldir_current->imported_by != NULL &&
3676 strlen(leveldir_current->imported_by) > 0)
3677 max_label_counter += 14;
3679 label_counter = (label_counter + 1) % max_label_counter;
3680 label_state = (label_counter >= 0 && label_counter <= 7 ?
3681 MICROLABEL_LEVEL_NAME :
3682 label_counter >= 9 && label_counter <= 12 ?
3683 MICROLABEL_LEVEL_AUTHOR_HEAD :
3684 label_counter >= 14 && label_counter <= 21 ?
3685 MICROLABEL_LEVEL_AUTHOR :
3686 label_counter >= 23 && label_counter <= 26 ?
3687 MICROLABEL_IMPORTED_FROM_HEAD :
3688 label_counter >= 28 && label_counter <= 35 ?
3689 MICROLABEL_IMPORTED_FROM :
3690 label_counter >= 37 && label_counter <= 40 ?
3691 MICROLABEL_IMPORTED_BY_HEAD :
3692 label_counter >= 42 && label_counter <= 49 ?
3693 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3695 if (leveldir_current->imported_from == NULL &&
3696 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3697 label_state == MICROLABEL_IMPORTED_FROM))
3698 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3699 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3701 DrawPreviewLevelLabel(label_state);
3705 void DrawPreviewPlayers(void)
3707 if (game_status != GAME_MODE_MAIN)
3710 // do not draw preview players if level preview redefined, but players aren't
3711 if (preview.redefined && !menu.main.preview_players.redefined)
3714 boolean player_found[MAX_PLAYERS];
3715 int num_players = 0;
3718 for (i = 0; i < MAX_PLAYERS; i++)
3719 player_found[i] = FALSE;
3721 // check which players can be found in the level (simple approach)
3722 for (x = 0; x < lev_fieldx; x++)
3724 for (y = 0; y < lev_fieldy; y++)
3726 int element = level.field[x][y];
3728 if (IS_PLAYER_ELEMENT(element))
3730 int player_nr = GET_PLAYER_NR(element);
3732 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3734 if (!player_found[player_nr])
3737 player_found[player_nr] = TRUE;
3742 struct TextPosInfo *pos = &menu.main.preview_players;
3743 int tile_size = pos->tile_size;
3744 int border_size = pos->border_size;
3745 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3746 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3747 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3748 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3749 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3750 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3751 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3752 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3753 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3754 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3755 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3756 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3758 // clear area in which the players will be drawn
3759 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3760 max_players_width, max_players_height);
3762 if (!network.enabled && !setup.team_mode)
3765 // only draw players if level is suited for team mode
3766 if (num_players < 2)
3769 // draw all players that were found in the level
3770 for (i = 0; i < MAX_PLAYERS; i++)
3772 if (player_found[i])
3774 int graphic = el2img(EL_PLAYER_1 + i);
3776 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3778 xpos += player_xoffset;
3779 ypos += player_yoffset;
3784 void DrawPreviewLevelInitial(void)
3786 DrawPreviewLevelExt(TRUE);
3787 DrawPreviewPlayers();
3790 void DrawPreviewLevelAnimation(void)
3792 DrawPreviewLevelExt(FALSE);
3795 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3796 int border_size, int font_nr)
3798 int graphic = el2img(EL_PLAYER_1 + player_nr);
3799 int font_height = getFontHeight(font_nr);
3800 int player_height = MAX(tile_size, font_height);
3801 int xoffset_text = tile_size + border_size;
3802 int yoffset_text = (player_height - font_height) / 2;
3803 int yoffset_graphic = (player_height - tile_size) / 2;
3804 char *player_name = getNetworkPlayerName(player_nr + 1);
3806 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3808 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3811 static void DrawNetworkPlayersExt(boolean force)
3813 if (game_status != GAME_MODE_MAIN)
3816 if (!network.connected && !force)
3819 // do not draw network players if level preview redefined, but players aren't
3820 if (preview.redefined && !menu.main.network_players.redefined)
3823 int num_players = 0;
3826 for (i = 0; i < MAX_PLAYERS; i++)
3827 if (stored_player[i].connected_network)
3830 struct TextPosInfo *pos = &menu.main.network_players;
3831 int tile_size = pos->tile_size;
3832 int border_size = pos->border_size;
3833 int xoffset_text = tile_size + border_size;
3834 int font_nr = pos->font;
3835 int font_width = getFontWidth(font_nr);
3836 int font_height = getFontHeight(font_nr);
3837 int player_height = MAX(tile_size, font_height);
3838 int player_yoffset = player_height + border_size;
3839 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3840 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3841 int all_players_height = num_players * player_yoffset - border_size;
3842 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3843 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3844 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3846 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3847 max_players_width, max_players_height);
3849 // first draw local network player ...
3850 for (i = 0; i < MAX_PLAYERS; i++)
3852 if (stored_player[i].connected_network &&
3853 stored_player[i].connected_locally)
3855 char *player_name = getNetworkPlayerName(i + 1);
3856 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3857 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3859 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3861 ypos += player_yoffset;
3865 // ... then draw all other network players
3866 for (i = 0; i < MAX_PLAYERS; i++)
3868 if (stored_player[i].connected_network &&
3869 !stored_player[i].connected_locally)
3871 char *player_name = getNetworkPlayerName(i + 1);
3872 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3873 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3875 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3877 ypos += player_yoffset;
3882 void DrawNetworkPlayers(void)
3884 DrawNetworkPlayersExt(FALSE);
3887 void ClearNetworkPlayers(void)
3889 DrawNetworkPlayersExt(TRUE);
3892 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3893 int graphic, int lx, int ly,
3896 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3898 if (mask_mode == USE_MASKING)
3899 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3901 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3904 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3905 int graphic, int sync_frame, int mask_mode)
3907 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3909 if (mask_mode == USE_MASKING)
3910 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3912 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3915 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3916 int graphic, int sync_frame, int tilesize,
3919 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3921 if (mask_mode == USE_MASKING)
3922 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3924 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3927 static void DrawGraphicAnimation(int x, int y, int graphic)
3929 int lx = LEVELX(x), ly = LEVELY(y);
3930 int mask_mode = NO_MASKING;
3932 if (!IN_SCR_FIELD(x, y))
3935 if (game.use_masked_elements)
3937 if (Tile[lx][ly] != EL_EMPTY)
3939 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3941 mask_mode = USE_MASKING;
3945 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3946 graphic, lx, ly, mask_mode);
3948 MarkTileDirty(x, y);
3951 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3953 int lx = LEVELX(x), ly = LEVELY(y);
3954 int mask_mode = NO_MASKING;
3956 if (!IN_SCR_FIELD(x, y))
3959 if (game.use_masked_elements)
3961 if (Tile[lx][ly] != EL_EMPTY)
3963 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3965 mask_mode = USE_MASKING;
3969 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3970 graphic, lx, ly, mask_mode);
3972 MarkTileDirty(x, y);
3975 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3977 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3980 void DrawLevelElementAnimation(int x, int y, int element)
3982 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3984 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3987 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3989 int sx = SCREENX(x), sy = SCREENY(y);
3991 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3994 if (Tile[x][y] == EL_EMPTY)
3995 graphic = el2img(GfxElementEmpty[x][y]);
3997 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4000 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4003 DrawGraphicAnimation(sx, sy, graphic);
4006 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4007 DrawLevelFieldCrumbled(x, y);
4009 if (GFX_CRUMBLED(Tile[x][y]))
4010 DrawLevelFieldCrumbled(x, y);
4014 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4016 int sx = SCREENX(x), sy = SCREENY(y);
4019 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4022 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4024 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4027 DrawGraphicAnimation(sx, sy, graphic);
4029 if (GFX_CRUMBLED(element))
4030 DrawLevelFieldCrumbled(x, y);
4033 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4035 if (player->use_murphy)
4037 // this works only because currently only one player can be "murphy" ...
4038 static int last_horizontal_dir = MV_LEFT;
4039 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4041 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4042 last_horizontal_dir = move_dir;
4044 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4046 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4048 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4054 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4057 static boolean equalGraphics(int graphic1, int graphic2)
4059 struct GraphicInfo *g1 = &graphic_info[graphic1];
4060 struct GraphicInfo *g2 = &graphic_info[graphic2];
4062 return (g1->bitmap == g2->bitmap &&
4063 g1->src_x == g2->src_x &&
4064 g1->src_y == g2->src_y &&
4065 g1->anim_frames == g2->anim_frames &&
4066 g1->anim_delay == g2->anim_delay &&
4067 g1->anim_mode == g2->anim_mode);
4070 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4074 DRAW_PLAYER_STAGE_INIT = 0,
4075 DRAW_PLAYER_STAGE_LAST_FIELD,
4076 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4077 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4078 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4079 DRAW_PLAYER_STAGE_PLAYER,
4081 DRAW_PLAYER_STAGE_PLAYER,
4082 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4084 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4085 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4087 NUM_DRAW_PLAYER_STAGES
4090 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4092 static int static_last_player_graphic[MAX_PLAYERS];
4093 static int static_last_player_frame[MAX_PLAYERS];
4094 static boolean static_player_is_opaque[MAX_PLAYERS];
4095 static boolean draw_player[MAX_PLAYERS];
4096 int pnr = player->index_nr;
4098 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4100 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4101 static_last_player_frame[pnr] = player->Frame;
4102 static_player_is_opaque[pnr] = FALSE;
4104 draw_player[pnr] = TRUE;
4107 if (!draw_player[pnr])
4111 if (!IN_LEV_FIELD(player->jx, player->jy))
4113 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4114 Debug("draw:DrawPlayerExt", "This should never happen!");
4116 draw_player[pnr] = FALSE;
4122 int last_player_graphic = static_last_player_graphic[pnr];
4123 int last_player_frame = static_last_player_frame[pnr];
4124 boolean player_is_opaque = static_player_is_opaque[pnr];
4126 int jx = player->jx;
4127 int jy = player->jy;
4128 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4129 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4130 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4131 int last_jx = (player->is_moving ? jx - dx : jx);
4132 int last_jy = (player->is_moving ? jy - dy : jy);
4133 int next_jx = jx + dx;
4134 int next_jy = jy + dy;
4135 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4136 int sx = SCREENX(jx);
4137 int sy = SCREENY(jy);
4138 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4139 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4140 int element = Tile[jx][jy];
4141 int last_element = Tile[last_jx][last_jy];
4142 int action = (player->is_pushing ? ACTION_PUSHING :
4143 player->is_digging ? ACTION_DIGGING :
4144 player->is_collecting ? ACTION_COLLECTING :
4145 player->is_moving ? ACTION_MOVING :
4146 player->is_snapping ? ACTION_SNAPPING :
4147 player->is_dropping ? ACTION_DROPPING :
4148 player->is_waiting ? player->action_waiting :
4151 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4153 // ------------------------------------------------------------------------
4154 // initialize drawing the player
4155 // ------------------------------------------------------------------------
4157 draw_player[pnr] = FALSE;
4159 // GfxElement[][] is set to the element the player is digging or collecting;
4160 // remove also for off-screen player if the player is not moving anymore
4161 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4162 GfxElement[jx][jy] = EL_UNDEFINED;
4164 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4167 if (element == EL_EXPLOSION)
4170 InitPlayerGfxAnimation(player, action, move_dir);
4172 draw_player[pnr] = TRUE;
4174 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4176 // ------------------------------------------------------------------------
4177 // draw things in the field the player is leaving, if needed
4178 // ------------------------------------------------------------------------
4180 if (!IN_SCR_FIELD(sx, sy))
4181 draw_player[pnr] = FALSE;
4183 if (!player->is_moving)
4186 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4188 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4190 if (last_element == EL_DYNAMITE_ACTIVE ||
4191 last_element == EL_EM_DYNAMITE_ACTIVE ||
4192 last_element == EL_SP_DISK_RED_ACTIVE)
4193 DrawDynamite(last_jx, last_jy);
4195 DrawLevelFieldThruMask(last_jx, last_jy);
4197 else if (last_element == EL_DYNAMITE_ACTIVE ||
4198 last_element == EL_EM_DYNAMITE_ACTIVE ||
4199 last_element == EL_SP_DISK_RED_ACTIVE)
4200 DrawDynamite(last_jx, last_jy);
4202 DrawLevelField(last_jx, last_jy);
4204 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4206 // ------------------------------------------------------------------------
4207 // draw things behind the player, if needed
4208 // ------------------------------------------------------------------------
4212 DrawLevelElement(jx, jy, Back[jx][jy]);
4217 if (IS_ACTIVE_BOMB(element))
4219 DrawLevelElement(jx, jy, EL_EMPTY);
4224 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4226 int old_element = GfxElement[jx][jy];
4227 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4228 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4230 if (GFX_CRUMBLED(old_element))
4231 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4233 DrawScreenGraphic(sx, sy, old_graphic, frame);
4235 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4236 static_player_is_opaque[pnr] = TRUE;
4240 GfxElement[jx][jy] = EL_UNDEFINED;
4242 // make sure that pushed elements are drawn with correct frame rate
4243 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4245 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4246 GfxFrame[jx][jy] = player->StepFrame;
4248 DrawLevelField(jx, jy);
4251 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4253 // ------------------------------------------------------------------------
4254 // draw things the player is pushing, if needed
4255 // ------------------------------------------------------------------------
4257 if (!player->is_pushing || !player->is_moving)
4260 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4263 int gfx_frame = GfxFrame[jx][jy];
4265 if (!IS_MOVING(jx, jy)) // push movement already finished
4267 element = Tile[next_jx][next_jy];
4268 gfx_frame = GfxFrame[next_jx][next_jy];
4271 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4272 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4273 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4275 // draw background element under pushed element (like the Sokoban field)
4276 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4278 // this allows transparent pushing animation over non-black background
4281 DrawLevelElement(jx, jy, Back[jx][jy]);
4283 DrawLevelElement(jx, jy, EL_EMPTY);
4286 if (Back[next_jx][next_jy])
4287 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4289 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4291 int px = SCREENX(jx), py = SCREENY(jy);
4292 int pxx = (TILEX - ABS(sxx)) * dx;
4293 int pyy = (TILEY - ABS(syy)) * dy;
4296 // do not draw (EM style) pushing animation when pushing is finished
4297 // (two-tile animations usually do not contain start and end frame)
4298 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4299 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4301 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4303 // masked drawing is needed for EMC style (double) movement graphics
4304 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4305 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4308 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4310 // ------------------------------------------------------------------------
4311 // draw player himself
4312 // ------------------------------------------------------------------------
4314 int graphic = getPlayerGraphic(player, move_dir);
4316 // in the case of changed player action or direction, prevent the current
4317 // animation frame from being restarted for identical animations
4318 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4319 player->Frame = last_player_frame;
4321 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4323 if (player_is_opaque)
4324 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4326 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4328 if (SHIELD_ON(player))
4330 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4331 IMG_SHIELD_NORMAL_ACTIVE);
4332 frame = getGraphicAnimationFrame(graphic, -1);
4334 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4337 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4339 // ------------------------------------------------------------------------
4340 // draw things in front of player (active dynamite or dynabombs)
4341 // ------------------------------------------------------------------------
4343 if (IS_ACTIVE_BOMB(element))
4345 int graphic = el2img(element);
4346 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4348 if (game.emulation == EMU_SUPAPLEX)
4349 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4351 DrawGraphicThruMask(sx, sy, graphic, frame);
4354 if (player_is_moving && last_element == EL_EXPLOSION)
4356 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4357 GfxElement[last_jx][last_jy] : EL_EMPTY);
4358 int graphic = el_act2img(element, ACTION_EXPLODING);
4359 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4360 int phase = ExplodePhase[last_jx][last_jy] - 1;
4361 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4364 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4367 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4369 // ------------------------------------------------------------------------
4370 // draw elements the player is just walking/passing through/under
4371 // ------------------------------------------------------------------------
4373 if (player_is_moving)
4375 // handle the field the player is leaving ...
4376 if (IS_ACCESSIBLE_INSIDE(last_element))
4377 DrawLevelField(last_jx, last_jy);
4378 else if (IS_ACCESSIBLE_UNDER(last_element))
4379 DrawLevelFieldThruMask(last_jx, last_jy);
4382 // do not redraw accessible elements if the player is just pushing them
4383 if (!player_is_moving || !player->is_pushing)
4385 // ... and the field the player is entering
4386 if (IS_ACCESSIBLE_INSIDE(element))
4387 DrawLevelField(jx, jy);
4388 else if (IS_ACCESSIBLE_UNDER(element))
4389 DrawLevelFieldThruMask(jx, jy);
4392 MarkTileDirty(sx, sy);
4396 void DrawPlayer(struct PlayerInfo *player)
4400 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4401 DrawPlayerExt(player, i);
4404 void DrawAllPlayers(void)
4408 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4409 for (j = 0; j < MAX_PLAYERS; j++)
4410 if (stored_player[j].active)
4411 DrawPlayerExt(&stored_player[j], i);
4414 void DrawPlayerField(int x, int y)
4416 if (!IS_PLAYER(x, y))
4419 DrawPlayer(PLAYERINFO(x, y));
4422 // ----------------------------------------------------------------------------
4424 void WaitForEventToContinue(void)
4426 boolean first_wait = TRUE;
4427 boolean still_wait = TRUE;
4429 if (program.headless)
4432 // simulate releasing mouse button over last gadget, if still pressed
4434 HandleGadgets(-1, -1, 0);
4436 button_status = MB_RELEASED;
4439 ClearPlayerAction();
4445 if (NextValidEvent(&event))
4449 case EVENT_BUTTONPRESS:
4450 case EVENT_FINGERPRESS:
4454 case EVENT_BUTTONRELEASE:
4455 case EVENT_FINGERRELEASE:
4456 still_wait = first_wait;
4459 case EVENT_KEYPRESS:
4460 case SDL_CONTROLLERBUTTONDOWN:
4461 case SDL_JOYBUTTONDOWN:
4466 HandleOtherEvents(&event);
4470 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4475 if (!PendingEvent())
4480 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4482 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4483 int draw_buffer_last = GetDrawtoField();
4484 int width = request.width;
4485 int height = request.height;
4489 setRequestPosition(&sx, &sy, FALSE);
4491 button_status = MB_RELEASED;
4493 request_gadget_id = -1;
4500 SetDrawtoField(draw_buffer_game);
4502 HandleGameActions();
4504 SetDrawtoField(DRAW_TO_BACKBUFFER);
4511 while (NextValidEvent(&event))
4515 case EVENT_BUTTONPRESS:
4516 case EVENT_BUTTONRELEASE:
4517 case EVENT_MOTIONNOTIFY:
4519 DrawBuffer *drawto_last = drawto;
4522 if (event.type == EVENT_MOTIONNOTIFY)
4527 motion_status = TRUE;
4528 mx = ((MotionEvent *) &event)->x;
4529 my = ((MotionEvent *) &event)->y;
4533 motion_status = FALSE;
4534 mx = ((ButtonEvent *) &event)->x;
4535 my = ((ButtonEvent *) &event)->y;
4536 if (event.type == EVENT_BUTTONPRESS)
4537 button_status = ((ButtonEvent *) &event)->button;
4539 button_status = MB_RELEASED;
4542 if (global.use_envelope_request)
4544 // draw changed button states to temporary bitmap
4545 drawto = bitmap_db_store_1;
4548 // this sets 'request_gadget_id'
4549 HandleGadgets(mx, my, button_status);
4551 if (global.use_envelope_request)
4553 // restore pointer to drawing buffer
4554 drawto = drawto_last;
4556 // prepare complete envelope request from temporary bitmap
4557 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4561 switch (request_gadget_id)
4563 case TOOL_CTRL_ID_YES:
4564 case TOOL_CTRL_ID_TOUCH_YES:
4567 case TOOL_CTRL_ID_NO:
4568 case TOOL_CTRL_ID_TOUCH_NO:
4571 case TOOL_CTRL_ID_CONFIRM:
4572 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4573 result = TRUE | FALSE;
4576 case TOOL_CTRL_ID_PLAYER_1:
4579 case TOOL_CTRL_ID_PLAYER_2:
4582 case TOOL_CTRL_ID_PLAYER_3:
4585 case TOOL_CTRL_ID_PLAYER_4:
4590 // only check clickable animations if no request gadget clicked
4591 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4598 case SDL_WINDOWEVENT:
4599 HandleWindowEvent((WindowEvent *) &event);
4602 case SDL_APP_WILLENTERBACKGROUND:
4603 case SDL_APP_DIDENTERBACKGROUND:
4604 case SDL_APP_WILLENTERFOREGROUND:
4605 case SDL_APP_DIDENTERFOREGROUND:
4606 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4609 case EVENT_KEYPRESS:
4611 Key key = GetEventKey((KeyEvent *)&event);
4616 if (req_state & REQ_CONFIRM)
4625 #if defined(KSYM_Rewind)
4626 case KSYM_Rewind: // for Amazon Fire TV remote
4635 #if defined(KSYM_FastForward)
4636 case KSYM_FastForward: // for Amazon Fire TV remote
4642 HandleKeysDebug(key, KEY_PRESSED);
4646 if (req_state & REQ_PLAYER)
4648 int old_player_nr = setup.network_player_nr;
4651 result = old_player_nr + 1;
4656 result = old_player_nr + 1;
4687 case EVENT_FINGERRELEASE:
4688 case EVENT_KEYRELEASE:
4689 ClearPlayerAction();
4692 case SDL_CONTROLLERBUTTONDOWN:
4693 switch (event.cbutton.button)
4695 case SDL_CONTROLLER_BUTTON_A:
4696 case SDL_CONTROLLER_BUTTON_X:
4697 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4698 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4702 case SDL_CONTROLLER_BUTTON_B:
4703 case SDL_CONTROLLER_BUTTON_Y:
4704 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4705 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4706 case SDL_CONTROLLER_BUTTON_BACK:
4711 if (req_state & REQ_PLAYER)
4713 int old_player_nr = setup.network_player_nr;
4716 result = old_player_nr + 1;
4718 switch (event.cbutton.button)
4720 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4721 case SDL_CONTROLLER_BUTTON_Y:
4725 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4726 case SDL_CONTROLLER_BUTTON_B:
4730 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4731 case SDL_CONTROLLER_BUTTON_A:
4735 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4736 case SDL_CONTROLLER_BUTTON_X:
4747 case SDL_CONTROLLERBUTTONUP:
4748 HandleJoystickEvent(&event);
4749 ClearPlayerAction();
4753 HandleOtherEvents(&event);
4758 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4760 int joy = AnyJoystick();
4762 if (joy & JOY_BUTTON_1)
4764 else if (joy & JOY_BUTTON_2)
4767 else if (AnyJoystick())
4769 int joy = AnyJoystick();
4771 if (req_state & REQ_PLAYER)
4775 else if (joy & JOY_RIGHT)
4777 else if (joy & JOY_DOWN)
4779 else if (joy & JOY_LEFT)
4787 SetDrawtoField(draw_buffer_last);
4792 static void DoRequestBefore(void)
4794 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4796 // when showing request dialog after game ended, deactivate game panel
4798 game.panel.active = FALSE;
4800 if (game_status == GAME_MODE_PLAYING)
4801 BlitScreenToBitmap(backbuffer);
4803 // disable deactivated drawing when quick-loading level tape recording
4804 if (tape.playing && tape.deactivate_display)
4805 TapeDeactivateDisplayOff(TRUE);
4807 SetMouseCursor(CURSOR_DEFAULT);
4809 // pause network game while waiting for request to answer
4810 if (network.enabled &&
4811 game_status == GAME_MODE_PLAYING &&
4812 !game.all_players_gone)
4813 SendToServer_PausePlaying();
4815 // simulate releasing mouse button over last gadget, if still pressed
4817 HandleGadgets(-1, -1, 0);
4822 static void DoRequestAfter(void)
4826 if (game_status == GAME_MODE_PLAYING)
4828 SetPanelBackground();
4829 SetDrawBackgroundMask(REDRAW_DOOR_1);
4833 SetDrawBackgroundMask(REDRAW_FIELD);
4836 // continue network game after request
4837 if (network.enabled &&
4838 game_status == GAME_MODE_PLAYING &&
4839 !game.all_players_gone)
4840 SendToServer_ContinuePlaying();
4842 // restore deactivated drawing when quick-loading level tape recording
4843 if (tape.playing && tape.deactivate_display)
4844 TapeDeactivateDisplayOn();
4847 static void setRequestDoorTextProperties(char *text,
4852 int *set_max_line_length)
4854 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4855 struct TextPosInfo *pos = &request.button.confirm;
4856 int button_ypos = pos->y;
4857 int font_nr = FONT_TEXT_2;
4858 int font_width = getFontWidth(font_nr);
4859 int font_height = getFontHeight(font_nr);
4860 int line_height = font_height + line_spacing;
4861 int max_text_width = vp_door_1->width;
4862 int max_text_height = button_ypos - 2 * text_spacing;
4863 int max_line_length = max_text_width / font_width;
4864 int max_lines = max_text_height / line_height;
4866 if (maxWordLengthInRequestString(text) > max_line_length)
4868 font_nr = FONT_TEXT_1;
4869 font_width = getFontWidth(font_nr);
4870 max_line_length = max_text_width / font_width;
4873 *set_font_nr = font_nr;
4874 *set_max_lines = max_lines;
4875 *set_max_line_length = max_line_length;
4878 static void DrawRequestDoorText(char *text)
4880 char *text_ptr = text;
4881 int text_spacing = 8;
4882 int line_spacing = 2;
4883 int max_request_lines;
4884 int max_request_line_len;
4888 // force DOOR font inside door area
4889 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4891 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4892 &max_request_lines, &max_request_line_len);
4894 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4896 char text_line[max_request_line_len + 1];
4902 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4904 tc = *(text_ptr + tx);
4905 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4909 if ((tc == '?' || tc == '!') && tl == 0)
4919 strncpy(text_line, text_ptr, tl);
4922 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4923 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4924 text_line, font_nr);
4926 text_ptr += tl + (tc == ' ' ? 1 : 0);
4932 static int RequestDoor(char *text, unsigned int req_state)
4934 unsigned int old_door_state = GetDoorState();
4935 int draw_buffer_last = GetDrawtoField();
4938 if (old_door_state & DOOR_OPEN_1)
4940 CloseDoor(DOOR_CLOSE_1);
4942 // save old door content
4943 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4944 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4947 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4948 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4950 // clear door drawing field
4951 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4953 // write text for request
4954 DrawRequestDoorText(text);
4956 MapToolButtons(req_state);
4958 // copy request gadgets to door backbuffer
4959 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4961 OpenDoor(DOOR_OPEN_1);
4963 // ---------- handle request buttons ----------
4964 result = RequestHandleEvents(req_state, draw_buffer_last);
4968 if (!(req_state & REQ_STAY_OPEN))
4970 CloseDoor(DOOR_CLOSE_1);
4972 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4973 (req_state & REQ_REOPEN))
4974 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4980 static int RequestEnvelope(char *text, unsigned int req_state)
4982 int draw_buffer_last = GetDrawtoField();
4985 DrawEnvelopeRequest(text, req_state);
4986 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4988 // ---------- handle request buttons ----------
4989 result = RequestHandleEvents(req_state, draw_buffer_last);
4993 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4998 int Request(char *text, unsigned int req_state)
5000 boolean overlay_enabled = GetOverlayEnabled();
5003 game.request_active = TRUE;
5005 SetOverlayEnabled(FALSE);
5009 if (global.use_envelope_request)
5010 result = RequestEnvelope(text, req_state);
5012 result = RequestDoor(text, req_state);
5016 SetOverlayEnabled(overlay_enabled);
5018 game.request_active = FALSE;
5023 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5025 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5026 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5029 if (dpo1->sort_priority != dpo2->sort_priority)
5030 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5032 compare_result = dpo1->nr - dpo2->nr;
5034 return compare_result;
5037 void InitGraphicCompatibilityInfo_Doors(void)
5043 struct DoorInfo *door;
5047 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5048 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5050 { -1, -1, -1, NULL }
5052 struct Rect door_rect_list[] =
5054 { DX, DY, DXSIZE, DYSIZE },
5055 { VX, VY, VXSIZE, VYSIZE }
5059 for (i = 0; doors[i].door_token != -1; i++)
5061 int door_token = doors[i].door_token;
5062 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5063 int part_1 = doors[i].part_1;
5064 int part_8 = doors[i].part_8;
5065 int part_2 = part_1 + 1;
5066 int part_3 = part_1 + 2;
5067 struct DoorInfo *door = doors[i].door;
5068 struct Rect *door_rect = &door_rect_list[door_index];
5069 boolean door_gfx_redefined = FALSE;
5071 // check if any door part graphic definitions have been redefined
5073 for (j = 0; door_part_controls[j].door_token != -1; j++)
5075 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5076 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5078 if (dpc->door_token == door_token && fi->redefined)
5079 door_gfx_redefined = TRUE;
5082 // check for old-style door graphic/animation modifications
5084 if (!door_gfx_redefined)
5086 if (door->anim_mode & ANIM_STATIC_PANEL)
5088 door->panel.step_xoffset = 0;
5089 door->panel.step_yoffset = 0;
5092 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5094 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5095 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5096 int num_door_steps, num_panel_steps;
5098 // remove door part graphics other than the two default wings
5100 for (j = 0; door_part_controls[j].door_token != -1; j++)
5102 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5103 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5105 if (dpc->graphic >= part_3 &&
5106 dpc->graphic <= part_8)
5110 // set graphics and screen positions of the default wings
5112 g_part_1->width = door_rect->width;
5113 g_part_1->height = door_rect->height;
5114 g_part_2->width = door_rect->width;
5115 g_part_2->height = door_rect->height;
5116 g_part_2->src_x = door_rect->width;
5117 g_part_2->src_y = g_part_1->src_y;
5119 door->part_2.x = door->part_1.x;
5120 door->part_2.y = door->part_1.y;
5122 if (door->width != -1)
5124 g_part_1->width = door->width;
5125 g_part_2->width = door->width;
5127 // special treatment for graphics and screen position of right wing
5128 g_part_2->src_x += door_rect->width - door->width;
5129 door->part_2.x += door_rect->width - door->width;
5132 if (door->height != -1)
5134 g_part_1->height = door->height;
5135 g_part_2->height = door->height;
5137 // special treatment for graphics and screen position of bottom wing
5138 g_part_2->src_y += door_rect->height - door->height;
5139 door->part_2.y += door_rect->height - door->height;
5142 // set animation delays for the default wings and panels
5144 door->part_1.step_delay = door->step_delay;
5145 door->part_2.step_delay = door->step_delay;
5146 door->panel.step_delay = door->step_delay;
5148 // set animation draw order for the default wings
5150 door->part_1.sort_priority = 2; // draw left wing over ...
5151 door->part_2.sort_priority = 1; // ... right wing
5153 // set animation draw offset for the default wings
5155 if (door->anim_mode & ANIM_HORIZONTAL)
5157 door->part_1.step_xoffset = door->step_offset;
5158 door->part_1.step_yoffset = 0;
5159 door->part_2.step_xoffset = door->step_offset * -1;
5160 door->part_2.step_yoffset = 0;
5162 num_door_steps = g_part_1->width / door->step_offset;
5164 else // ANIM_VERTICAL
5166 door->part_1.step_xoffset = 0;
5167 door->part_1.step_yoffset = door->step_offset;
5168 door->part_2.step_xoffset = 0;
5169 door->part_2.step_yoffset = door->step_offset * -1;
5171 num_door_steps = g_part_1->height / door->step_offset;
5174 // set animation draw offset for the default panels
5176 if (door->step_offset > 1)
5178 num_panel_steps = 2 * door_rect->height / door->step_offset;
5179 door->panel.start_step = num_panel_steps - num_door_steps;
5180 door->panel.start_step_closing = door->panel.start_step;
5184 num_panel_steps = door_rect->height / door->step_offset;
5185 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5186 door->panel.start_step_closing = door->panel.start_step;
5187 door->panel.step_delay *= 2;
5194 void InitDoors(void)
5198 for (i = 0; door_part_controls[i].door_token != -1; i++)
5200 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5201 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5203 // initialize "start_step_opening" and "start_step_closing", if needed
5204 if (dpc->pos->start_step_opening == 0 &&
5205 dpc->pos->start_step_closing == 0)
5207 // dpc->pos->start_step_opening = dpc->pos->start_step;
5208 dpc->pos->start_step_closing = dpc->pos->start_step;
5211 // fill structure for door part draw order (sorted below)
5213 dpo->sort_priority = dpc->pos->sort_priority;
5216 // sort door part controls according to sort_priority and graphic number
5217 qsort(door_part_order, MAX_DOOR_PARTS,
5218 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5221 unsigned int OpenDoor(unsigned int door_state)
5223 if (door_state & DOOR_COPY_BACK)
5225 if (door_state & DOOR_OPEN_1)
5226 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5227 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5229 if (door_state & DOOR_OPEN_2)
5230 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5231 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5233 door_state &= ~DOOR_COPY_BACK;
5236 return MoveDoor(door_state);
5239 unsigned int CloseDoor(unsigned int door_state)
5241 unsigned int old_door_state = GetDoorState();
5243 if (!(door_state & DOOR_NO_COPY_BACK))
5245 if (old_door_state & DOOR_OPEN_1)
5246 BlitBitmap(backbuffer, bitmap_db_door_1,
5247 DX, DY, DXSIZE, DYSIZE, 0, 0);
5249 if (old_door_state & DOOR_OPEN_2)
5250 BlitBitmap(backbuffer, bitmap_db_door_2,
5251 VX, VY, VXSIZE, VYSIZE, 0, 0);
5253 door_state &= ~DOOR_NO_COPY_BACK;
5256 return MoveDoor(door_state);
5259 unsigned int GetDoorState(void)
5261 return MoveDoor(DOOR_GET_STATE);
5264 unsigned int SetDoorState(unsigned int door_state)
5266 return MoveDoor(door_state | DOOR_SET_STATE);
5269 static int euclid(int a, int b)
5271 return (b ? euclid(b, a % b) : a);
5274 unsigned int MoveDoor(unsigned int door_state)
5276 struct Rect door_rect_list[] =
5278 { DX, DY, DXSIZE, DYSIZE },
5279 { VX, VY, VXSIZE, VYSIZE }
5281 static int door1 = DOOR_CLOSE_1;
5282 static int door2 = DOOR_CLOSE_2;
5283 DelayCounter door_delay = { 0 };
5286 if (door_state == DOOR_GET_STATE)
5287 return (door1 | door2);
5289 if (door_state & DOOR_SET_STATE)
5291 if (door_state & DOOR_ACTION_1)
5292 door1 = door_state & DOOR_ACTION_1;
5293 if (door_state & DOOR_ACTION_2)
5294 door2 = door_state & DOOR_ACTION_2;
5296 return (door1 | door2);
5299 if (!(door_state & DOOR_FORCE_REDRAW))
5301 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5302 door_state &= ~DOOR_OPEN_1;
5303 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5304 door_state &= ~DOOR_CLOSE_1;
5305 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5306 door_state &= ~DOOR_OPEN_2;
5307 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5308 door_state &= ~DOOR_CLOSE_2;
5311 if (global.autoplay_leveldir)
5313 door_state |= DOOR_NO_DELAY;
5314 door_state &= ~DOOR_CLOSE_ALL;
5317 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5318 door_state |= DOOR_NO_DELAY;
5320 if (door_state & DOOR_ACTION)
5322 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5323 boolean door_panel_drawn[NUM_DOORS];
5324 boolean panel_has_doors[NUM_DOORS];
5325 boolean door_part_skip[MAX_DOOR_PARTS];
5326 boolean door_part_done[MAX_DOOR_PARTS];
5327 boolean door_part_done_all;
5328 int num_steps[MAX_DOOR_PARTS];
5329 int max_move_delay = 0; // delay for complete animations of all doors
5330 int max_step_delay = 0; // delay (ms) between two animation frames
5331 int num_move_steps = 0; // number of animation steps for all doors
5332 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5333 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5337 for (i = 0; i < NUM_DOORS; i++)
5338 panel_has_doors[i] = FALSE;
5340 for (i = 0; i < MAX_DOOR_PARTS; i++)
5342 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5343 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5344 int door_token = dpc->door_token;
5346 door_part_done[i] = FALSE;
5347 door_part_skip[i] = (!(door_state & door_token) ||
5351 for (i = 0; i < MAX_DOOR_PARTS; i++)
5353 int nr = door_part_order[i].nr;
5354 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5355 struct DoorPartPosInfo *pos = dpc->pos;
5356 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5357 int door_token = dpc->door_token;
5358 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5359 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5360 int step_xoffset = ABS(pos->step_xoffset);
5361 int step_yoffset = ABS(pos->step_yoffset);
5362 int step_delay = pos->step_delay;
5363 int current_door_state = door_state & door_token;
5364 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5365 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5366 boolean part_opening = (is_panel ? door_closing : door_opening);
5367 int start_step = (part_opening ? pos->start_step_opening :
5368 pos->start_step_closing);
5369 float move_xsize = (step_xoffset ? g->width : 0);
5370 float move_ysize = (step_yoffset ? g->height : 0);
5371 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5372 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5373 int move_steps = (move_xsteps && move_ysteps ?
5374 MIN(move_xsteps, move_ysteps) :
5375 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5376 int move_delay = move_steps * step_delay;
5378 if (door_part_skip[nr])
5381 max_move_delay = MAX(max_move_delay, move_delay);
5382 max_step_delay = (max_step_delay == 0 ? step_delay :
5383 euclid(max_step_delay, step_delay));
5384 num_steps[nr] = move_steps;
5388 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5390 panel_has_doors[door_index] = TRUE;
5394 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5396 num_move_steps = max_move_delay / max_step_delay;
5397 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5399 door_delay.value = max_step_delay;
5401 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5403 start = num_move_steps - 1;
5407 // opening door sound has priority over simultaneously closing door
5408 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5410 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5412 if (door_state & DOOR_OPEN_1)
5413 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5414 if (door_state & DOOR_OPEN_2)
5415 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5417 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5419 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5421 if (door_state & DOOR_CLOSE_1)
5422 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5423 if (door_state & DOOR_CLOSE_2)
5424 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5428 for (k = start; k < num_move_steps; k++)
5430 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5432 door_part_done_all = TRUE;
5434 for (i = 0; i < NUM_DOORS; i++)
5435 door_panel_drawn[i] = FALSE;
5437 for (i = 0; i < MAX_DOOR_PARTS; i++)
5439 int nr = door_part_order[i].nr;
5440 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5441 struct DoorPartPosInfo *pos = dpc->pos;
5442 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5443 int door_token = dpc->door_token;
5444 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5445 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5446 boolean is_panel_and_door_has_closed = FALSE;
5447 struct Rect *door_rect = &door_rect_list[door_index];
5448 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5450 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5451 int current_door_state = door_state & door_token;
5452 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5453 boolean door_closing = !door_opening;
5454 boolean part_opening = (is_panel ? door_closing : door_opening);
5455 boolean part_closing = !part_opening;
5456 int start_step = (part_opening ? pos->start_step_opening :
5457 pos->start_step_closing);
5458 int step_delay = pos->step_delay;
5459 int step_factor = step_delay / max_step_delay;
5460 int k1 = (step_factor ? k / step_factor + 1 : k);
5461 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5462 int kk = MAX(0, k2);
5465 int src_x, src_y, src_xx, src_yy;
5466 int dst_x, dst_y, dst_xx, dst_yy;
5469 if (door_part_skip[nr])
5472 if (!(door_state & door_token))
5480 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5481 int kk_door = MAX(0, k2_door);
5482 int sync_frame = kk_door * door_delay.value;
5483 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5485 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5486 &g_src_x, &g_src_y);
5491 if (!door_panel_drawn[door_index])
5493 ClearRectangle(drawto, door_rect->x, door_rect->y,
5494 door_rect->width, door_rect->height);
5496 door_panel_drawn[door_index] = TRUE;
5499 // draw opening or closing door parts
5501 if (pos->step_xoffset < 0) // door part on right side
5504 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5507 if (dst_xx + width > door_rect->width)
5508 width = door_rect->width - dst_xx;
5510 else // door part on left side
5513 dst_xx = pos->x - kk * pos->step_xoffset;
5517 src_xx = ABS(dst_xx);
5521 width = g->width - src_xx;
5523 if (width > door_rect->width)
5524 width = door_rect->width;
5526 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5529 if (pos->step_yoffset < 0) // door part on bottom side
5532 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5535 if (dst_yy + height > door_rect->height)
5536 height = door_rect->height - dst_yy;
5538 else // door part on top side
5541 dst_yy = pos->y - kk * pos->step_yoffset;
5545 src_yy = ABS(dst_yy);
5549 height = g->height - src_yy;
5552 src_x = g_src_x + src_xx;
5553 src_y = g_src_y + src_yy;
5555 dst_x = door_rect->x + dst_xx;
5556 dst_y = door_rect->y + dst_yy;
5558 is_panel_and_door_has_closed =
5561 panel_has_doors[door_index] &&
5562 k >= num_move_steps_doors_only - 1);
5564 if (width >= 0 && width <= g->width &&
5565 height >= 0 && height <= g->height &&
5566 !is_panel_and_door_has_closed)
5568 if (is_panel || !pos->draw_masked)
5569 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5572 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5576 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5578 if ((part_opening && (width < 0 || height < 0)) ||
5579 (part_closing && (width >= g->width && height >= g->height)))
5580 door_part_done[nr] = TRUE;
5582 // continue door part animations, but not panel after door has closed
5583 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5584 door_part_done_all = FALSE;
5587 if (!(door_state & DOOR_NO_DELAY))
5590 HandleGameActions();
5594 SkipUntilDelayReached(&door_delay, &k, last_frame);
5596 // prevent OS (Windows) from complaining about program not responding
5600 if (door_part_done_all)
5604 if (!(door_state & DOOR_NO_DELAY))
5606 // wait for specified door action post delay
5607 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5608 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5609 else if (door_state & DOOR_ACTION_1)
5610 door_delay.value = door_1.post_delay;
5611 else if (door_state & DOOR_ACTION_2)
5612 door_delay.value = door_2.post_delay;
5614 while (!DelayReached(&door_delay))
5617 HandleGameActions();
5624 if (door_state & DOOR_ACTION_1)
5625 door1 = door_state & DOOR_ACTION_1;
5626 if (door_state & DOOR_ACTION_2)
5627 door2 = door_state & DOOR_ACTION_2;
5629 // draw masked border over door area
5630 DrawMaskedBorder(REDRAW_DOOR_1);
5631 DrawMaskedBorder(REDRAW_DOOR_2);
5633 ClearAutoRepeatKeyEvents();
5635 return (door1 | door2);
5638 static boolean useSpecialEditorDoor(void)
5640 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5641 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5643 // do not draw special editor door if editor border defined or redefined
5644 if (graphic_info[graphic].bitmap != NULL || redefined)
5647 // do not draw special editor door if global border defined to be empty
5648 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5651 // do not draw special editor door if viewport definitions do not match
5655 EY + EYSIZE != VY + VYSIZE)
5661 void DrawSpecialEditorDoor(void)
5663 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5664 int top_border_width = gfx1->width;
5665 int top_border_height = gfx1->height;
5666 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5667 int ex = EX - outer_border;
5668 int ey = EY - outer_border;
5669 int vy = VY - outer_border;
5670 int exsize = EXSIZE + 2 * outer_border;
5672 if (!useSpecialEditorDoor())
5675 // draw bigger level editor toolbox window
5676 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5677 top_border_width, top_border_height, ex, ey - top_border_height);
5678 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5679 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5681 redraw_mask |= REDRAW_ALL;
5684 void UndrawSpecialEditorDoor(void)
5686 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5687 int top_border_width = gfx1->width;
5688 int top_border_height = gfx1->height;
5689 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5690 int ex = EX - outer_border;
5691 int ey = EY - outer_border;
5692 int ey_top = ey - top_border_height;
5693 int exsize = EXSIZE + 2 * outer_border;
5694 int eysize = EYSIZE + 2 * outer_border;
5696 if (!useSpecialEditorDoor())
5699 // draw normal tape recorder window
5700 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5702 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5703 ex, ey_top, top_border_width, top_border_height,
5705 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5706 ex, ey, exsize, eysize, ex, ey);
5710 // if screen background is set to "[NONE]", clear editor toolbox window
5711 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5712 ClearRectangle(drawto, ex, ey, exsize, eysize);
5715 redraw_mask |= REDRAW_ALL;
5719 // ---------- new tool button stuff -------------------------------------------
5724 struct TextPosInfo *pos;
5726 boolean is_touch_button;
5728 } toolbutton_info[NUM_TOOL_BUTTONS] =
5731 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5732 TOOL_CTRL_ID_YES, FALSE, "yes"
5735 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5736 TOOL_CTRL_ID_NO, FALSE, "no"
5739 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5740 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5743 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5744 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5747 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5748 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5751 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5752 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5755 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5756 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5759 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5760 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5763 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5764 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5767 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5768 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5772 void CreateToolButtons(void)
5776 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5778 int graphic = toolbutton_info[i].graphic;
5779 struct GraphicInfo *gfx = &graphic_info[graphic];
5780 struct TextPosInfo *pos = toolbutton_info[i].pos;
5781 struct GadgetInfo *gi;
5782 Bitmap *deco_bitmap = None;
5783 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5784 unsigned int event_mask = GD_EVENT_RELEASED;
5785 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5786 int base_x = (is_touch_button ? 0 : DX);
5787 int base_y = (is_touch_button ? 0 : DY);
5788 int gd_x = gfx->src_x;
5789 int gd_y = gfx->src_y;
5790 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5791 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5796 // do not use touch buttons if overlay touch buttons are disabled
5797 if (is_touch_button && !setup.touch.overlay_buttons)
5800 if (global.use_envelope_request && !is_touch_button)
5802 setRequestPosition(&base_x, &base_y, TRUE);
5804 // check if request buttons are outside of envelope and fix, if needed
5805 if (x < 0 || x + gfx->width > request.width ||
5806 y < 0 || y + gfx->height > request.height)
5808 if (id == TOOL_CTRL_ID_YES)
5811 y = request.height - 2 * request.border_size - gfx->height;
5813 else if (id == TOOL_CTRL_ID_NO)
5815 x = request.width - 2 * request.border_size - gfx->width;
5816 y = request.height - 2 * request.border_size - gfx->height;
5818 else if (id == TOOL_CTRL_ID_CONFIRM)
5820 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5821 y = request.height - 2 * request.border_size - gfx->height;
5823 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5825 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5827 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5828 y = request.height - 2 * request.border_size - gfx->height * 2;
5830 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5831 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5836 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5839 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5841 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5842 pos->size, &deco_bitmap, &deco_x, &deco_y);
5843 deco_xpos = (gfx->width - pos->size) / 2;
5844 deco_ypos = (gfx->height - pos->size) / 2;
5847 gi = CreateGadget(GDI_CUSTOM_ID, id,
5848 GDI_IMAGE_ID, graphic,
5849 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5852 GDI_WIDTH, gfx->width,
5853 GDI_HEIGHT, gfx->height,
5854 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5855 GDI_STATE, GD_BUTTON_UNPRESSED,
5856 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5857 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5858 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5859 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5860 GDI_DECORATION_SIZE, pos->size, pos->size,
5861 GDI_DECORATION_SHIFTING, 1, 1,
5862 GDI_DIRECT_DRAW, FALSE,
5863 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5864 GDI_EVENT_MASK, event_mask,
5865 GDI_CALLBACK_ACTION, HandleToolButtons,
5869 Fail("cannot create gadget");
5871 tool_gadget[id] = gi;
5875 void FreeToolButtons(void)
5879 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5880 FreeGadget(tool_gadget[i]);
5883 static void MapToolButtons(unsigned int req_state)
5885 if (req_state & REQ_ASK)
5887 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5888 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5889 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5890 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5892 else if (req_state & REQ_CONFIRM)
5894 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5895 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5897 else if (req_state & REQ_PLAYER)
5899 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5900 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5901 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5902 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5906 static void UnmapToolButtons(void)
5910 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5911 UnmapGadget(tool_gadget[i]);
5914 static void HandleToolButtons(struct GadgetInfo *gi)
5916 request_gadget_id = gi->custom_id;
5919 static struct Mapping_EM_to_RND_object
5922 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5923 boolean is_backside; // backside of moving element
5929 em_object_mapping_list[GAME_TILE_MAX + 1] =
5932 Zborder, FALSE, FALSE,
5936 Zplayer, FALSE, FALSE,
5945 Ztank, FALSE, FALSE,
5949 Zeater, FALSE, FALSE,
5953 Zdynamite, FALSE, FALSE,
5957 Zboom, FALSE, FALSE,
5962 Xchain, FALSE, FALSE,
5963 EL_DEFAULT, ACTION_EXPLODING, -1
5966 Xboom_bug, FALSE, FALSE,
5967 EL_BUG, ACTION_EXPLODING, -1
5970 Xboom_tank, FALSE, FALSE,
5971 EL_SPACESHIP, ACTION_EXPLODING, -1
5974 Xboom_android, FALSE, FALSE,
5975 EL_EMC_ANDROID, ACTION_OTHER, -1
5978 Xboom_1, FALSE, FALSE,
5979 EL_DEFAULT, ACTION_EXPLODING, -1
5982 Xboom_2, FALSE, FALSE,
5983 EL_DEFAULT, ACTION_EXPLODING, -1
5987 Xblank, TRUE, FALSE,
5992 Xsplash_e, FALSE, FALSE,
5993 EL_ACID_SPLASH_RIGHT, -1, -1
5996 Xsplash_w, FALSE, FALSE,
5997 EL_ACID_SPLASH_LEFT, -1, -1
6001 Xplant, TRUE, FALSE,
6002 EL_EMC_PLANT, -1, -1
6005 Yplant, FALSE, FALSE,
6006 EL_EMC_PLANT, -1, -1
6010 Xacid_1, TRUE, FALSE,
6014 Xacid_2, FALSE, FALSE,
6018 Xacid_3, FALSE, FALSE,
6022 Xacid_4, FALSE, FALSE,
6026 Xacid_5, FALSE, FALSE,
6030 Xacid_6, FALSE, FALSE,
6034 Xacid_7, FALSE, FALSE,
6038 Xacid_8, FALSE, FALSE,
6043 Xfake_acid_1, TRUE, FALSE,
6044 EL_EMC_FAKE_ACID, -1, -1
6047 Xfake_acid_2, FALSE, FALSE,
6048 EL_EMC_FAKE_ACID, -1, -1
6051 Xfake_acid_3, FALSE, FALSE,
6052 EL_EMC_FAKE_ACID, -1, -1
6055 Xfake_acid_4, FALSE, FALSE,
6056 EL_EMC_FAKE_ACID, -1, -1
6059 Xfake_acid_5, FALSE, FALSE,
6060 EL_EMC_FAKE_ACID, -1, -1
6063 Xfake_acid_6, FALSE, FALSE,
6064 EL_EMC_FAKE_ACID, -1, -1
6067 Xfake_acid_7, FALSE, FALSE,
6068 EL_EMC_FAKE_ACID, -1, -1
6071 Xfake_acid_8, FALSE, FALSE,
6072 EL_EMC_FAKE_ACID, -1, -1
6076 Xfake_acid_1_player, FALSE, FALSE,
6077 EL_EMC_FAKE_ACID, -1, -1
6080 Xfake_acid_2_player, FALSE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_3_player, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_4_player, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6092 Xfake_acid_5_player, FALSE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_6_player, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_7_player, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_8_player, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6109 Xgrass, TRUE, FALSE,
6110 EL_EMC_GRASS, -1, -1
6113 Ygrass_nB, FALSE, FALSE,
6114 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6117 Ygrass_eB, FALSE, FALSE,
6118 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6121 Ygrass_sB, FALSE, FALSE,
6122 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6125 Ygrass_wB, FALSE, FALSE,
6126 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6134 Ydirt_nB, FALSE, FALSE,
6135 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6138 Ydirt_eB, FALSE, FALSE,
6139 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6142 Ydirt_sB, FALSE, FALSE,
6143 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6146 Ydirt_wB, FALSE, FALSE,
6147 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6151 Xandroid, TRUE, FALSE,
6152 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6155 Xandroid_1_n, FALSE, FALSE,
6156 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6159 Xandroid_2_n, FALSE, FALSE,
6160 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6163 Xandroid_1_e, FALSE, FALSE,
6164 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6167 Xandroid_2_e, FALSE, FALSE,
6168 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6171 Xandroid_1_w, FALSE, FALSE,
6172 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6175 Xandroid_2_w, FALSE, FALSE,
6176 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6179 Xandroid_1_s, FALSE, FALSE,
6180 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6183 Xandroid_2_s, FALSE, FALSE,
6184 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6187 Yandroid_n, FALSE, FALSE,
6188 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6191 Yandroid_nB, FALSE, TRUE,
6192 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6195 Yandroid_ne, FALSE, FALSE,
6196 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6199 Yandroid_neB, FALSE, TRUE,
6200 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6203 Yandroid_e, FALSE, FALSE,
6204 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6207 Yandroid_eB, FALSE, TRUE,
6208 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6211 Yandroid_se, FALSE, FALSE,
6212 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6215 Yandroid_seB, FALSE, TRUE,
6216 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6219 Yandroid_s, FALSE, FALSE,
6220 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6223 Yandroid_sB, FALSE, TRUE,
6224 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6227 Yandroid_sw, FALSE, FALSE,
6228 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6231 Yandroid_swB, FALSE, TRUE,
6232 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6235 Yandroid_w, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6239 Yandroid_wB, FALSE, TRUE,
6240 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6243 Yandroid_nw, FALSE, FALSE,
6244 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6247 Yandroid_nwB, FALSE, TRUE,
6248 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6252 Xeater_n, TRUE, FALSE,
6253 EL_YAMYAM_UP, -1, -1
6256 Xeater_e, TRUE, FALSE,
6257 EL_YAMYAM_RIGHT, -1, -1
6260 Xeater_w, TRUE, FALSE,
6261 EL_YAMYAM_LEFT, -1, -1
6264 Xeater_s, TRUE, FALSE,
6265 EL_YAMYAM_DOWN, -1, -1
6268 Yeater_n, FALSE, FALSE,
6269 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6272 Yeater_nB, FALSE, TRUE,
6273 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6276 Yeater_e, FALSE, FALSE,
6277 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6280 Yeater_eB, FALSE, TRUE,
6281 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6284 Yeater_s, FALSE, FALSE,
6285 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6288 Yeater_sB, FALSE, TRUE,
6289 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6292 Yeater_w, FALSE, FALSE,
6293 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6296 Yeater_wB, FALSE, TRUE,
6297 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6300 Yeater_stone, FALSE, FALSE,
6301 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6304 Yeater_spring, FALSE, FALSE,
6305 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6309 Xalien, TRUE, FALSE,
6313 Xalien_pause, FALSE, FALSE,
6317 Yalien_n, FALSE, FALSE,
6318 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6321 Yalien_nB, FALSE, TRUE,
6322 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6325 Yalien_e, FALSE, FALSE,
6326 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6329 Yalien_eB, FALSE, TRUE,
6330 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6333 Yalien_s, FALSE, FALSE,
6334 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6337 Yalien_sB, FALSE, TRUE,
6338 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6341 Yalien_w, FALSE, FALSE,
6342 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6345 Yalien_wB, FALSE, TRUE,
6346 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6349 Yalien_stone, FALSE, FALSE,
6350 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6353 Yalien_spring, FALSE, FALSE,
6354 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6358 Xbug_1_n, TRUE, FALSE,
6362 Xbug_1_e, TRUE, FALSE,
6363 EL_BUG_RIGHT, -1, -1
6366 Xbug_1_s, TRUE, FALSE,
6370 Xbug_1_w, TRUE, FALSE,
6374 Xbug_2_n, FALSE, FALSE,
6378 Xbug_2_e, FALSE, FALSE,
6379 EL_BUG_RIGHT, -1, -1
6382 Xbug_2_s, FALSE, FALSE,
6386 Xbug_2_w, FALSE, FALSE,
6390 Ybug_n, FALSE, FALSE,
6391 EL_BUG, ACTION_MOVING, MV_BIT_UP
6394 Ybug_nB, FALSE, TRUE,
6395 EL_BUG, ACTION_MOVING, MV_BIT_UP
6398 Ybug_e, FALSE, FALSE,
6399 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6402 Ybug_eB, FALSE, TRUE,
6403 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6406 Ybug_s, FALSE, FALSE,
6407 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6410 Ybug_sB, FALSE, TRUE,
6411 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6414 Ybug_w, FALSE, FALSE,
6415 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6418 Ybug_wB, FALSE, TRUE,
6419 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6422 Ybug_w_n, FALSE, FALSE,
6423 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6426 Ybug_n_e, FALSE, FALSE,
6427 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6430 Ybug_e_s, FALSE, FALSE,
6431 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6434 Ybug_s_w, FALSE, FALSE,
6435 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6438 Ybug_e_n, FALSE, FALSE,
6439 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6442 Ybug_s_e, FALSE, FALSE,
6443 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6446 Ybug_w_s, FALSE, FALSE,
6447 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6450 Ybug_n_w, FALSE, FALSE,
6451 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6454 Ybug_stone, FALSE, FALSE,
6455 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6458 Ybug_spring, FALSE, FALSE,
6459 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6463 Xtank_1_n, TRUE, FALSE,
6464 EL_SPACESHIP_UP, -1, -1
6467 Xtank_1_e, TRUE, FALSE,
6468 EL_SPACESHIP_RIGHT, -1, -1
6471 Xtank_1_s, TRUE, FALSE,
6472 EL_SPACESHIP_DOWN, -1, -1
6475 Xtank_1_w, TRUE, FALSE,
6476 EL_SPACESHIP_LEFT, -1, -1
6479 Xtank_2_n, FALSE, FALSE,
6480 EL_SPACESHIP_UP, -1, -1
6483 Xtank_2_e, FALSE, FALSE,
6484 EL_SPACESHIP_RIGHT, -1, -1
6487 Xtank_2_s, FALSE, FALSE,
6488 EL_SPACESHIP_DOWN, -1, -1
6491 Xtank_2_w, FALSE, FALSE,
6492 EL_SPACESHIP_LEFT, -1, -1
6495 Ytank_n, FALSE, FALSE,
6496 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6499 Ytank_nB, FALSE, TRUE,
6500 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6503 Ytank_e, FALSE, FALSE,
6504 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6507 Ytank_eB, FALSE, TRUE,
6508 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6511 Ytank_s, FALSE, FALSE,
6512 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6515 Ytank_sB, FALSE, TRUE,
6516 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6519 Ytank_w, FALSE, FALSE,
6520 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6523 Ytank_wB, FALSE, TRUE,
6524 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6527 Ytank_w_n, FALSE, FALSE,
6528 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6531 Ytank_n_e, FALSE, FALSE,
6532 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6535 Ytank_e_s, FALSE, FALSE,
6536 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6539 Ytank_s_w, FALSE, FALSE,
6540 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6543 Ytank_e_n, FALSE, FALSE,
6544 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6547 Ytank_s_e, FALSE, FALSE,
6548 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6551 Ytank_w_s, FALSE, FALSE,
6552 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6555 Ytank_n_w, FALSE, FALSE,
6556 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6559 Ytank_stone, FALSE, FALSE,
6560 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6563 Ytank_spring, FALSE, FALSE,
6564 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6568 Xemerald, TRUE, FALSE,
6572 Xemerald_pause, FALSE, FALSE,
6576 Xemerald_fall, FALSE, FALSE,
6580 Xemerald_shine, FALSE, FALSE,
6581 EL_EMERALD, ACTION_TWINKLING, -1
6584 Yemerald_s, FALSE, FALSE,
6585 EL_EMERALD, ACTION_FALLING, -1
6588 Yemerald_sB, FALSE, TRUE,
6589 EL_EMERALD, ACTION_FALLING, -1
6592 Yemerald_e, FALSE, FALSE,
6593 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6596 Yemerald_eB, FALSE, TRUE,
6597 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6600 Yemerald_w, FALSE, FALSE,
6601 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6604 Yemerald_wB, FALSE, TRUE,
6605 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6608 Yemerald_blank, FALSE, FALSE,
6609 EL_EMERALD, ACTION_COLLECTING, -1
6613 Xdiamond, TRUE, FALSE,
6617 Xdiamond_pause, FALSE, FALSE,
6621 Xdiamond_fall, FALSE, FALSE,
6625 Xdiamond_shine, FALSE, FALSE,
6626 EL_DIAMOND, ACTION_TWINKLING, -1
6629 Ydiamond_s, FALSE, FALSE,
6630 EL_DIAMOND, ACTION_FALLING, -1
6633 Ydiamond_sB, FALSE, TRUE,
6634 EL_DIAMOND, ACTION_FALLING, -1
6637 Ydiamond_e, FALSE, FALSE,
6638 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6641 Ydiamond_eB, FALSE, TRUE,
6642 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6645 Ydiamond_w, FALSE, FALSE,
6646 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6649 Ydiamond_wB, FALSE, TRUE,
6650 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6653 Ydiamond_blank, FALSE, FALSE,
6654 EL_DIAMOND, ACTION_COLLECTING, -1
6657 Ydiamond_stone, FALSE, FALSE,
6658 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6662 Xstone, TRUE, FALSE,
6666 Xstone_pause, FALSE, FALSE,
6670 Xstone_fall, FALSE, FALSE,
6674 Ystone_s, FALSE, FALSE,
6675 EL_ROCK, ACTION_FALLING, -1
6678 Ystone_sB, FALSE, TRUE,
6679 EL_ROCK, ACTION_FALLING, -1
6682 Ystone_e, FALSE, FALSE,
6683 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6686 Ystone_eB, FALSE, TRUE,
6687 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6690 Ystone_w, FALSE, FALSE,
6691 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6694 Ystone_wB, FALSE, TRUE,
6695 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6703 Xbomb_pause, FALSE, FALSE,
6707 Xbomb_fall, FALSE, FALSE,
6711 Ybomb_s, FALSE, FALSE,
6712 EL_BOMB, ACTION_FALLING, -1
6715 Ybomb_sB, FALSE, TRUE,
6716 EL_BOMB, ACTION_FALLING, -1
6719 Ybomb_e, FALSE, FALSE,
6720 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6723 Ybomb_eB, FALSE, TRUE,
6724 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6727 Ybomb_w, FALSE, FALSE,
6728 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6731 Ybomb_wB, FALSE, TRUE,
6732 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6735 Ybomb_blank, FALSE, FALSE,
6736 EL_BOMB, ACTION_ACTIVATING, -1
6744 Xnut_pause, FALSE, FALSE,
6748 Xnut_fall, FALSE, FALSE,
6752 Ynut_s, FALSE, FALSE,
6753 EL_NUT, ACTION_FALLING, -1
6756 Ynut_sB, FALSE, TRUE,
6757 EL_NUT, ACTION_FALLING, -1
6760 Ynut_e, FALSE, FALSE,
6761 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6764 Ynut_eB, FALSE, TRUE,
6765 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6768 Ynut_w, FALSE, FALSE,
6769 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6772 Ynut_wB, FALSE, TRUE,
6773 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6776 Ynut_stone, FALSE, FALSE,
6777 EL_NUT, ACTION_BREAKING, -1
6781 Xspring, TRUE, FALSE,
6785 Xspring_pause, FALSE, FALSE,
6789 Xspring_e, TRUE, FALSE,
6790 EL_SPRING_RIGHT, -1, -1
6793 Xspring_w, TRUE, FALSE,
6794 EL_SPRING_LEFT, -1, -1
6797 Xspring_fall, FALSE, FALSE,
6801 Yspring_s, FALSE, FALSE,
6802 EL_SPRING, ACTION_FALLING, -1
6805 Yspring_sB, FALSE, TRUE,
6806 EL_SPRING, ACTION_FALLING, -1
6809 Yspring_e, FALSE, FALSE,
6810 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6813 Yspring_eB, FALSE, TRUE,
6814 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6817 Yspring_w, FALSE, FALSE,
6818 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6821 Yspring_wB, FALSE, TRUE,
6822 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6825 Yspring_alien_e, FALSE, FALSE,
6826 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6829 Yspring_alien_eB, FALSE, TRUE,
6830 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6833 Yspring_alien_w, FALSE, FALSE,
6834 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6837 Yspring_alien_wB, FALSE, TRUE,
6838 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6842 Xpush_emerald_e, FALSE, FALSE,
6843 EL_EMERALD, -1, MV_BIT_RIGHT
6846 Xpush_emerald_w, FALSE, FALSE,
6847 EL_EMERALD, -1, MV_BIT_LEFT
6850 Xpush_diamond_e, FALSE, FALSE,
6851 EL_DIAMOND, -1, MV_BIT_RIGHT
6854 Xpush_diamond_w, FALSE, FALSE,
6855 EL_DIAMOND, -1, MV_BIT_LEFT
6858 Xpush_stone_e, FALSE, FALSE,
6859 EL_ROCK, -1, MV_BIT_RIGHT
6862 Xpush_stone_w, FALSE, FALSE,
6863 EL_ROCK, -1, MV_BIT_LEFT
6866 Xpush_bomb_e, FALSE, FALSE,
6867 EL_BOMB, -1, MV_BIT_RIGHT
6870 Xpush_bomb_w, FALSE, FALSE,
6871 EL_BOMB, -1, MV_BIT_LEFT
6874 Xpush_nut_e, FALSE, FALSE,
6875 EL_NUT, -1, MV_BIT_RIGHT
6878 Xpush_nut_w, FALSE, FALSE,
6879 EL_NUT, -1, MV_BIT_LEFT
6882 Xpush_spring_e, FALSE, FALSE,
6883 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6886 Xpush_spring_w, FALSE, FALSE,
6887 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6891 Xdynamite, TRUE, FALSE,
6892 EL_EM_DYNAMITE, -1, -1
6895 Ydynamite_blank, FALSE, FALSE,
6896 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6899 Xdynamite_1, TRUE, FALSE,
6900 EL_EM_DYNAMITE_ACTIVE, -1, -1
6903 Xdynamite_2, FALSE, FALSE,
6904 EL_EM_DYNAMITE_ACTIVE, -1, -1
6907 Xdynamite_3, FALSE, FALSE,
6908 EL_EM_DYNAMITE_ACTIVE, -1, -1
6911 Xdynamite_4, FALSE, FALSE,
6912 EL_EM_DYNAMITE_ACTIVE, -1, -1
6916 Xkey_1, TRUE, FALSE,
6920 Xkey_2, TRUE, FALSE,
6924 Xkey_3, TRUE, FALSE,
6928 Xkey_4, TRUE, FALSE,
6932 Xkey_5, TRUE, FALSE,
6933 EL_EMC_KEY_5, -1, -1
6936 Xkey_6, TRUE, FALSE,
6937 EL_EMC_KEY_6, -1, -1
6940 Xkey_7, TRUE, FALSE,
6941 EL_EMC_KEY_7, -1, -1
6944 Xkey_8, TRUE, FALSE,
6945 EL_EMC_KEY_8, -1, -1
6949 Xdoor_1, TRUE, FALSE,
6950 EL_EM_GATE_1, -1, -1
6953 Xdoor_2, TRUE, FALSE,
6954 EL_EM_GATE_2, -1, -1
6957 Xdoor_3, TRUE, FALSE,
6958 EL_EM_GATE_3, -1, -1
6961 Xdoor_4, TRUE, FALSE,
6962 EL_EM_GATE_4, -1, -1
6965 Xdoor_5, TRUE, FALSE,
6966 EL_EMC_GATE_5, -1, -1
6969 Xdoor_6, TRUE, FALSE,
6970 EL_EMC_GATE_6, -1, -1
6973 Xdoor_7, TRUE, FALSE,
6974 EL_EMC_GATE_7, -1, -1
6977 Xdoor_8, TRUE, FALSE,
6978 EL_EMC_GATE_8, -1, -1
6982 Xfake_door_1, TRUE, FALSE,
6983 EL_EM_GATE_1_GRAY, -1, -1
6986 Xfake_door_2, TRUE, FALSE,
6987 EL_EM_GATE_2_GRAY, -1, -1
6990 Xfake_door_3, TRUE, FALSE,
6991 EL_EM_GATE_3_GRAY, -1, -1
6994 Xfake_door_4, TRUE, FALSE,
6995 EL_EM_GATE_4_GRAY, -1, -1
6998 Xfake_door_5, TRUE, FALSE,
6999 EL_EMC_GATE_5_GRAY, -1, -1
7002 Xfake_door_6, TRUE, FALSE,
7003 EL_EMC_GATE_6_GRAY, -1, -1
7006 Xfake_door_7, TRUE, FALSE,
7007 EL_EMC_GATE_7_GRAY, -1, -1
7010 Xfake_door_8, TRUE, FALSE,
7011 EL_EMC_GATE_8_GRAY, -1, -1
7015 Xballoon, TRUE, FALSE,
7019 Yballoon_n, FALSE, FALSE,
7020 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7023 Yballoon_nB, FALSE, TRUE,
7024 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7027 Yballoon_e, FALSE, FALSE,
7028 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7031 Yballoon_eB, FALSE, TRUE,
7032 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7035 Yballoon_s, FALSE, FALSE,
7036 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7039 Yballoon_sB, FALSE, TRUE,
7040 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7043 Yballoon_w, FALSE, FALSE,
7044 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7047 Yballoon_wB, FALSE, TRUE,
7048 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7052 Xball_1, TRUE, FALSE,
7053 EL_EMC_MAGIC_BALL, -1, -1
7056 Yball_1, FALSE, FALSE,
7057 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7060 Xball_2, FALSE, FALSE,
7061 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7064 Yball_2, FALSE, FALSE,
7065 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7068 Yball_blank, FALSE, FALSE,
7069 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7073 Xamoeba_1, TRUE, FALSE,
7074 EL_AMOEBA_DRY, ACTION_OTHER, -1
7077 Xamoeba_2, FALSE, FALSE,
7078 EL_AMOEBA_DRY, ACTION_OTHER, -1
7081 Xamoeba_3, FALSE, FALSE,
7082 EL_AMOEBA_DRY, ACTION_OTHER, -1
7085 Xamoeba_4, FALSE, FALSE,
7086 EL_AMOEBA_DRY, ACTION_OTHER, -1
7089 Xamoeba_5, TRUE, FALSE,
7090 EL_AMOEBA_WET, ACTION_OTHER, -1
7093 Xamoeba_6, FALSE, FALSE,
7094 EL_AMOEBA_WET, ACTION_OTHER, -1
7097 Xamoeba_7, FALSE, FALSE,
7098 EL_AMOEBA_WET, ACTION_OTHER, -1
7101 Xamoeba_8, FALSE, FALSE,
7102 EL_AMOEBA_WET, ACTION_OTHER, -1
7107 EL_AMOEBA_DROP, ACTION_GROWING, -1
7110 Xdrip_fall, FALSE, FALSE,
7111 EL_AMOEBA_DROP, -1, -1
7114 Xdrip_stretch, FALSE, FALSE,
7115 EL_AMOEBA_DROP, ACTION_FALLING, -1
7118 Xdrip_stretchB, FALSE, TRUE,
7119 EL_AMOEBA_DROP, ACTION_FALLING, -1
7122 Ydrip_1_s, FALSE, FALSE,
7123 EL_AMOEBA_DROP, ACTION_FALLING, -1
7126 Ydrip_1_sB, FALSE, TRUE,
7127 EL_AMOEBA_DROP, ACTION_FALLING, -1
7130 Ydrip_2_s, FALSE, FALSE,
7131 EL_AMOEBA_DROP, ACTION_FALLING, -1
7134 Ydrip_2_sB, FALSE, TRUE,
7135 EL_AMOEBA_DROP, ACTION_FALLING, -1
7139 Xwonderwall, TRUE, FALSE,
7140 EL_MAGIC_WALL, -1, -1
7143 Ywonderwall, FALSE, FALSE,
7144 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7148 Xwheel, TRUE, FALSE,
7149 EL_ROBOT_WHEEL, -1, -1
7152 Ywheel, FALSE, FALSE,
7153 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7157 Xswitch, TRUE, FALSE,
7158 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7161 Yswitch, FALSE, FALSE,
7162 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7166 Xbumper, TRUE, FALSE,
7167 EL_EMC_SPRING_BUMPER, -1, -1
7170 Ybumper, FALSE, FALSE,
7171 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7175 Xacid_nw, TRUE, FALSE,
7176 EL_ACID_POOL_TOPLEFT, -1, -1
7179 Xacid_ne, TRUE, FALSE,
7180 EL_ACID_POOL_TOPRIGHT, -1, -1
7183 Xacid_sw, TRUE, FALSE,
7184 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7187 Xacid_s, TRUE, FALSE,
7188 EL_ACID_POOL_BOTTOM, -1, -1
7191 Xacid_se, TRUE, FALSE,
7192 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7196 Xfake_blank, TRUE, FALSE,
7197 EL_INVISIBLE_WALL, -1, -1
7200 Yfake_blank, FALSE, FALSE,
7201 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7205 Xfake_grass, TRUE, FALSE,
7206 EL_EMC_FAKE_GRASS, -1, -1
7209 Yfake_grass, FALSE, FALSE,
7210 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7214 Xfake_amoeba, TRUE, FALSE,
7215 EL_EMC_DRIPPER, -1, -1
7218 Yfake_amoeba, FALSE, FALSE,
7219 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7223 Xlenses, TRUE, FALSE,
7224 EL_EMC_LENSES, -1, -1
7228 Xmagnify, TRUE, FALSE,
7229 EL_EMC_MAGNIFIER, -1, -1
7234 EL_QUICKSAND_EMPTY, -1, -1
7237 Xsand_stone, TRUE, FALSE,
7238 EL_QUICKSAND_FULL, -1, -1
7241 Xsand_stonein_1, FALSE, TRUE,
7242 EL_ROCK, ACTION_FILLING, -1
7245 Xsand_stonein_2, FALSE, TRUE,
7246 EL_ROCK, ACTION_FILLING, -1
7249 Xsand_stonein_3, FALSE, TRUE,
7250 EL_ROCK, ACTION_FILLING, -1
7253 Xsand_stonein_4, FALSE, TRUE,
7254 EL_ROCK, ACTION_FILLING, -1
7257 Xsand_sandstone_1, FALSE, FALSE,
7258 EL_QUICKSAND_FILLING, -1, -1
7261 Xsand_sandstone_2, FALSE, FALSE,
7262 EL_QUICKSAND_FILLING, -1, -1
7265 Xsand_sandstone_3, FALSE, FALSE,
7266 EL_QUICKSAND_FILLING, -1, -1
7269 Xsand_sandstone_4, FALSE, FALSE,
7270 EL_QUICKSAND_FILLING, -1, -1
7273 Xsand_stonesand_1, FALSE, FALSE,
7274 EL_QUICKSAND_EMPTYING, -1, -1
7277 Xsand_stonesand_2, FALSE, FALSE,
7278 EL_QUICKSAND_EMPTYING, -1, -1
7281 Xsand_stonesand_3, FALSE, FALSE,
7282 EL_QUICKSAND_EMPTYING, -1, -1
7285 Xsand_stonesand_4, FALSE, FALSE,
7286 EL_QUICKSAND_EMPTYING, -1, -1
7289 Xsand_stoneout_1, FALSE, FALSE,
7290 EL_ROCK, ACTION_EMPTYING, -1
7293 Xsand_stoneout_2, FALSE, FALSE,
7294 EL_ROCK, ACTION_EMPTYING, -1
7297 Xsand_stonesand_quickout_1, FALSE, FALSE,
7298 EL_QUICKSAND_EMPTYING, -1, -1
7301 Xsand_stonesand_quickout_2, FALSE, FALSE,
7302 EL_QUICKSAND_EMPTYING, -1, -1
7306 Xslide_ns, TRUE, FALSE,
7307 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7310 Yslide_ns_blank, FALSE, FALSE,
7311 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7314 Xslide_ew, TRUE, FALSE,
7315 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7318 Yslide_ew_blank, FALSE, FALSE,
7319 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7323 Xwind_n, TRUE, FALSE,
7324 EL_BALLOON_SWITCH_UP, -1, -1
7327 Xwind_e, TRUE, FALSE,
7328 EL_BALLOON_SWITCH_RIGHT, -1, -1
7331 Xwind_s, TRUE, FALSE,
7332 EL_BALLOON_SWITCH_DOWN, -1, -1
7335 Xwind_w, TRUE, FALSE,
7336 EL_BALLOON_SWITCH_LEFT, -1, -1
7339 Xwind_any, TRUE, FALSE,
7340 EL_BALLOON_SWITCH_ANY, -1, -1
7343 Xwind_stop, TRUE, FALSE,
7344 EL_BALLOON_SWITCH_NONE, -1, -1
7349 EL_EM_EXIT_CLOSED, -1, -1
7352 Xexit_1, TRUE, FALSE,
7353 EL_EM_EXIT_OPEN, -1, -1
7356 Xexit_2, FALSE, FALSE,
7357 EL_EM_EXIT_OPEN, -1, -1
7360 Xexit_3, FALSE, FALSE,
7361 EL_EM_EXIT_OPEN, -1, -1
7365 Xpause, FALSE, FALSE,
7370 Xwall_1, TRUE, FALSE,
7374 Xwall_2, TRUE, FALSE,
7375 EL_EMC_WALL_14, -1, -1
7378 Xwall_3, TRUE, FALSE,
7379 EL_EMC_WALL_15, -1, -1
7382 Xwall_4, TRUE, FALSE,
7383 EL_EMC_WALL_16, -1, -1
7387 Xroundwall_1, TRUE, FALSE,
7388 EL_WALL_SLIPPERY, -1, -1
7391 Xroundwall_2, TRUE, FALSE,
7392 EL_EMC_WALL_SLIPPERY_2, -1, -1
7395 Xroundwall_3, TRUE, FALSE,
7396 EL_EMC_WALL_SLIPPERY_3, -1, -1
7399 Xroundwall_4, TRUE, FALSE,
7400 EL_EMC_WALL_SLIPPERY_4, -1, -1
7404 Xsteel_1, TRUE, FALSE,
7405 EL_STEELWALL, -1, -1
7408 Xsteel_2, TRUE, FALSE,
7409 EL_EMC_STEELWALL_2, -1, -1
7412 Xsteel_3, TRUE, FALSE,
7413 EL_EMC_STEELWALL_3, -1, -1
7416 Xsteel_4, TRUE, FALSE,
7417 EL_EMC_STEELWALL_4, -1, -1
7421 Xdecor_1, TRUE, FALSE,
7422 EL_EMC_WALL_8, -1, -1
7425 Xdecor_2, TRUE, FALSE,
7426 EL_EMC_WALL_6, -1, -1
7429 Xdecor_3, TRUE, FALSE,
7430 EL_EMC_WALL_4, -1, -1
7433 Xdecor_4, TRUE, FALSE,
7434 EL_EMC_WALL_7, -1, -1
7437 Xdecor_5, TRUE, FALSE,
7438 EL_EMC_WALL_5, -1, -1
7441 Xdecor_6, TRUE, FALSE,
7442 EL_EMC_WALL_9, -1, -1
7445 Xdecor_7, TRUE, FALSE,
7446 EL_EMC_WALL_10, -1, -1
7449 Xdecor_8, TRUE, FALSE,
7450 EL_EMC_WALL_1, -1, -1
7453 Xdecor_9, TRUE, FALSE,
7454 EL_EMC_WALL_2, -1, -1
7457 Xdecor_10, TRUE, FALSE,
7458 EL_EMC_WALL_3, -1, -1
7461 Xdecor_11, TRUE, FALSE,
7462 EL_EMC_WALL_11, -1, -1
7465 Xdecor_12, TRUE, FALSE,
7466 EL_EMC_WALL_12, -1, -1
7470 Xalpha_0, TRUE, FALSE,
7471 EL_CHAR('0'), -1, -1
7474 Xalpha_1, TRUE, FALSE,
7475 EL_CHAR('1'), -1, -1
7478 Xalpha_2, TRUE, FALSE,
7479 EL_CHAR('2'), -1, -1
7482 Xalpha_3, TRUE, FALSE,
7483 EL_CHAR('3'), -1, -1
7486 Xalpha_4, TRUE, FALSE,
7487 EL_CHAR('4'), -1, -1
7490 Xalpha_5, TRUE, FALSE,
7491 EL_CHAR('5'), -1, -1
7494 Xalpha_6, TRUE, FALSE,
7495 EL_CHAR('6'), -1, -1
7498 Xalpha_7, TRUE, FALSE,
7499 EL_CHAR('7'), -1, -1
7502 Xalpha_8, TRUE, FALSE,
7503 EL_CHAR('8'), -1, -1
7506 Xalpha_9, TRUE, FALSE,
7507 EL_CHAR('9'), -1, -1
7510 Xalpha_excla, TRUE, FALSE,
7511 EL_CHAR('!'), -1, -1
7514 Xalpha_apost, TRUE, FALSE,
7515 EL_CHAR('\''), -1, -1
7518 Xalpha_comma, TRUE, FALSE,
7519 EL_CHAR(','), -1, -1
7522 Xalpha_minus, TRUE, FALSE,
7523 EL_CHAR('-'), -1, -1
7526 Xalpha_perio, TRUE, FALSE,
7527 EL_CHAR('.'), -1, -1
7530 Xalpha_colon, TRUE, FALSE,
7531 EL_CHAR(':'), -1, -1
7534 Xalpha_quest, TRUE, FALSE,
7535 EL_CHAR('?'), -1, -1
7538 Xalpha_a, TRUE, FALSE,
7539 EL_CHAR('A'), -1, -1
7542 Xalpha_b, TRUE, FALSE,
7543 EL_CHAR('B'), -1, -1
7546 Xalpha_c, TRUE, FALSE,
7547 EL_CHAR('C'), -1, -1
7550 Xalpha_d, TRUE, FALSE,
7551 EL_CHAR('D'), -1, -1
7554 Xalpha_e, TRUE, FALSE,
7555 EL_CHAR('E'), -1, -1
7558 Xalpha_f, TRUE, FALSE,
7559 EL_CHAR('F'), -1, -1
7562 Xalpha_g, TRUE, FALSE,
7563 EL_CHAR('G'), -1, -1
7566 Xalpha_h, TRUE, FALSE,
7567 EL_CHAR('H'), -1, -1
7570 Xalpha_i, TRUE, FALSE,
7571 EL_CHAR('I'), -1, -1
7574 Xalpha_j, TRUE, FALSE,
7575 EL_CHAR('J'), -1, -1
7578 Xalpha_k, TRUE, FALSE,
7579 EL_CHAR('K'), -1, -1
7582 Xalpha_l, TRUE, FALSE,
7583 EL_CHAR('L'), -1, -1
7586 Xalpha_m, TRUE, FALSE,
7587 EL_CHAR('M'), -1, -1
7590 Xalpha_n, TRUE, FALSE,
7591 EL_CHAR('N'), -1, -1
7594 Xalpha_o, TRUE, FALSE,
7595 EL_CHAR('O'), -1, -1
7598 Xalpha_p, TRUE, FALSE,
7599 EL_CHAR('P'), -1, -1
7602 Xalpha_q, TRUE, FALSE,
7603 EL_CHAR('Q'), -1, -1
7606 Xalpha_r, TRUE, FALSE,
7607 EL_CHAR('R'), -1, -1
7610 Xalpha_s, TRUE, FALSE,
7611 EL_CHAR('S'), -1, -1
7614 Xalpha_t, TRUE, FALSE,
7615 EL_CHAR('T'), -1, -1
7618 Xalpha_u, TRUE, FALSE,
7619 EL_CHAR('U'), -1, -1
7622 Xalpha_v, TRUE, FALSE,
7623 EL_CHAR('V'), -1, -1
7626 Xalpha_w, TRUE, FALSE,
7627 EL_CHAR('W'), -1, -1
7630 Xalpha_x, TRUE, FALSE,
7631 EL_CHAR('X'), -1, -1
7634 Xalpha_y, TRUE, FALSE,
7635 EL_CHAR('Y'), -1, -1
7638 Xalpha_z, TRUE, FALSE,
7639 EL_CHAR('Z'), -1, -1
7642 Xalpha_arrow_e, TRUE, FALSE,
7643 EL_CHAR('>'), -1, -1
7646 Xalpha_arrow_w, TRUE, FALSE,
7647 EL_CHAR('<'), -1, -1
7650 Xalpha_copyr, TRUE, FALSE,
7651 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7655 Ykey_1_blank, FALSE, FALSE,
7656 EL_EM_KEY_1, ACTION_COLLECTING, -1
7659 Ykey_2_blank, FALSE, FALSE,
7660 EL_EM_KEY_2, ACTION_COLLECTING, -1
7663 Ykey_3_blank, FALSE, FALSE,
7664 EL_EM_KEY_3, ACTION_COLLECTING, -1
7667 Ykey_4_blank, FALSE, FALSE,
7668 EL_EM_KEY_4, ACTION_COLLECTING, -1
7671 Ykey_5_blank, FALSE, FALSE,
7672 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7675 Ykey_6_blank, FALSE, FALSE,
7676 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7679 Ykey_7_blank, FALSE, FALSE,
7680 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7683 Ykey_8_blank, FALSE, FALSE,
7684 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7687 Ylenses_blank, FALSE, FALSE,
7688 EL_EMC_LENSES, ACTION_COLLECTING, -1
7691 Ymagnify_blank, FALSE, FALSE,
7692 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7695 Ygrass_blank, FALSE, FALSE,
7696 EL_EMC_GRASS, ACTION_SNAPPING, -1
7699 Ydirt_blank, FALSE, FALSE,
7700 EL_SAND, ACTION_SNAPPING, -1
7709 static struct Mapping_EM_to_RND_player
7718 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7722 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7726 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7730 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7734 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7738 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7742 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7746 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7750 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7754 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7758 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7762 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7766 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7770 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7774 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7778 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7782 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7786 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7790 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7794 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7798 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7802 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7806 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7810 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7814 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7818 EL_PLAYER_1, ACTION_DEFAULT, -1,
7822 EL_PLAYER_2, ACTION_DEFAULT, -1,
7826 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7830 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7834 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7838 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7842 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7846 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7850 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7854 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7858 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7862 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7866 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7870 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7874 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7878 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7882 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7886 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7890 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7894 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7898 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7902 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7906 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7910 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7914 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7918 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7922 EL_PLAYER_3, ACTION_DEFAULT, -1,
7926 EL_PLAYER_4, ACTION_DEFAULT, -1,
7935 int map_element_RND_to_EM_cave(int element_rnd)
7937 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7938 static boolean mapping_initialized = FALSE;
7940 if (!mapping_initialized)
7944 // return "Xalpha_quest" for all undefined elements in mapping array
7945 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7946 mapping_RND_to_EM[i] = Xalpha_quest;
7948 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7949 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7950 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7951 em_object_mapping_list[i].element_em;
7953 mapping_initialized = TRUE;
7956 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7958 Warn("invalid RND level element %d", element_rnd);
7963 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7966 int map_element_EM_to_RND_cave(int element_em_cave)
7968 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7969 static boolean mapping_initialized = FALSE;
7971 if (!mapping_initialized)
7975 // return "EL_UNKNOWN" for all undefined elements in mapping array
7976 for (i = 0; i < GAME_TILE_MAX; i++)
7977 mapping_EM_to_RND[i] = EL_UNKNOWN;
7979 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7980 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7981 em_object_mapping_list[i].element_rnd;
7983 mapping_initialized = TRUE;
7986 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7988 Warn("invalid EM cave element %d", element_em_cave);
7993 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7996 int map_element_EM_to_RND_game(int element_em_game)
7998 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7999 static boolean mapping_initialized = FALSE;
8001 if (!mapping_initialized)
8005 // return "EL_UNKNOWN" for all undefined elements in mapping array
8006 for (i = 0; i < GAME_TILE_MAX; i++)
8007 mapping_EM_to_RND[i] = EL_UNKNOWN;
8009 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8010 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8011 em_object_mapping_list[i].element_rnd;
8013 mapping_initialized = TRUE;
8016 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8018 Warn("invalid EM game element %d", element_em_game);
8023 return mapping_EM_to_RND[element_em_game];
8026 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8028 struct LevelInfo_EM *level_em = level->native_em_level;
8029 struct CAVE *cav = level_em->cav;
8032 for (i = 0; i < GAME_TILE_MAX; i++)
8033 cav->android_array[i] = Cblank;
8035 for (i = 0; i < level->num_android_clone_elements; i++)
8037 int element_rnd = level->android_clone_element[i];
8038 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8040 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8041 if (em_object_mapping_list[j].element_rnd == element_rnd)
8042 cav->android_array[em_object_mapping_list[j].element_em] =
8047 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8049 struct LevelInfo_EM *level_em = level->native_em_level;
8050 struct CAVE *cav = level_em->cav;
8053 level->num_android_clone_elements = 0;
8055 for (i = 0; i < GAME_TILE_MAX; i++)
8057 int element_em_cave = cav->android_array[i];
8059 boolean element_found = FALSE;
8061 if (element_em_cave == Cblank)
8064 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8066 for (j = 0; j < level->num_android_clone_elements; j++)
8067 if (level->android_clone_element[j] == element_rnd)
8068 element_found = TRUE;
8072 level->android_clone_element[level->num_android_clone_elements++] =
8075 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8080 if (level->num_android_clone_elements == 0)
8082 level->num_android_clone_elements = 1;
8083 level->android_clone_element[0] = EL_EMPTY;
8087 int map_direction_RND_to_EM(int direction)
8089 return (direction == MV_UP ? 0 :
8090 direction == MV_RIGHT ? 1 :
8091 direction == MV_DOWN ? 2 :
8092 direction == MV_LEFT ? 3 :
8096 int map_direction_EM_to_RND(int direction)
8098 return (direction == 0 ? MV_UP :
8099 direction == 1 ? MV_RIGHT :
8100 direction == 2 ? MV_DOWN :
8101 direction == 3 ? MV_LEFT :
8105 int map_element_RND_to_SP(int element_rnd)
8107 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8109 if (element_rnd >= EL_SP_START &&
8110 element_rnd <= EL_SP_END)
8111 element_sp = element_rnd - EL_SP_START;
8112 else if (element_rnd == EL_EMPTY_SPACE)
8114 else if (element_rnd == EL_INVISIBLE_WALL)
8120 int map_element_SP_to_RND(int element_sp)
8122 int element_rnd = EL_UNKNOWN;
8124 if (element_sp >= 0x00 &&
8126 element_rnd = EL_SP_START + element_sp;
8127 else if (element_sp == 0x28)
8128 element_rnd = EL_INVISIBLE_WALL;
8133 int map_action_SP_to_RND(int action_sp)
8137 case actActive: return ACTION_ACTIVE;
8138 case actImpact: return ACTION_IMPACT;
8139 case actExploding: return ACTION_EXPLODING;
8140 case actDigging: return ACTION_DIGGING;
8141 case actSnapping: return ACTION_SNAPPING;
8142 case actCollecting: return ACTION_COLLECTING;
8143 case actPassing: return ACTION_PASSING;
8144 case actPushing: return ACTION_PUSHING;
8145 case actDropping: return ACTION_DROPPING;
8147 default: return ACTION_DEFAULT;
8151 int map_element_RND_to_MM(int element_rnd)
8153 return (element_rnd >= EL_MM_START_1 &&
8154 element_rnd <= EL_MM_END_1 ?
8155 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8157 element_rnd >= EL_MM_START_2 &&
8158 element_rnd <= EL_MM_END_2 ?
8159 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8161 element_rnd >= EL_MM_START_3 &&
8162 element_rnd <= EL_MM_END_3 ?
8163 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8165 element_rnd >= EL_CHAR_START &&
8166 element_rnd <= EL_CHAR_END ?
8167 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8169 element_rnd >= EL_MM_RUNTIME_START &&
8170 element_rnd <= EL_MM_RUNTIME_END ?
8171 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8173 EL_MM_EMPTY_NATIVE);
8176 int map_element_MM_to_RND(int element_mm)
8178 return (element_mm == EL_MM_EMPTY_NATIVE ||
8179 element_mm == EL_DF_EMPTY_NATIVE ?
8182 element_mm >= EL_MM_START_1_NATIVE &&
8183 element_mm <= EL_MM_END_1_NATIVE ?
8184 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8186 element_mm >= EL_MM_START_2_NATIVE &&
8187 element_mm <= EL_MM_END_2_NATIVE ?
8188 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8190 element_mm >= EL_MM_START_3_NATIVE &&
8191 element_mm <= EL_MM_END_3_NATIVE ?
8192 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8194 element_mm >= EL_MM_CHAR_START_NATIVE &&
8195 element_mm <= EL_MM_CHAR_END_NATIVE ?
8196 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8198 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8199 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8200 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8205 int map_action_MM_to_RND(int action_mm)
8207 // all MM actions are defined to exactly match their RND counterparts
8211 int map_sound_MM_to_RND(int sound_mm)
8215 case SND_MM_GAME_LEVELTIME_CHARGING:
8216 return SND_GAME_LEVELTIME_CHARGING;
8218 case SND_MM_GAME_HEALTH_CHARGING:
8219 return SND_GAME_HEALTH_CHARGING;
8222 return SND_UNDEFINED;
8226 int map_mm_wall_element(int element)
8228 return (element >= EL_MM_STEEL_WALL_START &&
8229 element <= EL_MM_STEEL_WALL_END ?
8232 element >= EL_MM_WOODEN_WALL_START &&
8233 element <= EL_MM_WOODEN_WALL_END ?
8236 element >= EL_MM_ICE_WALL_START &&
8237 element <= EL_MM_ICE_WALL_END ?
8240 element >= EL_MM_AMOEBA_WALL_START &&
8241 element <= EL_MM_AMOEBA_WALL_END ?
8244 element >= EL_DF_STEEL_WALL_START &&
8245 element <= EL_DF_STEEL_WALL_END ?
8248 element >= EL_DF_WOODEN_WALL_START &&
8249 element <= EL_DF_WOODEN_WALL_END ?
8255 int map_mm_wall_element_editor(int element)
8259 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8260 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8261 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8262 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8263 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8264 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8266 default: return element;
8270 int get_next_element(int element)
8274 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8275 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8276 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8277 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8278 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8279 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8280 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8281 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8282 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8283 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8284 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8286 default: return element;
8290 int el2img_mm(int element_mm)
8292 return el2img(map_element_MM_to_RND(element_mm));
8295 int el_act2img_mm(int element_mm, int action)
8297 return el_act2img(map_element_MM_to_RND(element_mm), action);
8300 int el_act_dir2img(int element, int action, int direction)
8302 element = GFX_ELEMENT(element);
8303 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8305 // direction_graphic[][] == graphic[] for undefined direction graphics
8306 return element_info[element].direction_graphic[action][direction];
8309 static int el_act_dir2crm(int element, int action, int direction)
8311 element = GFX_ELEMENT(element);
8312 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8314 // direction_graphic[][] == graphic[] for undefined direction graphics
8315 return element_info[element].direction_crumbled[action][direction];
8318 int el_act2img(int element, int action)
8320 element = GFX_ELEMENT(element);
8322 return element_info[element].graphic[action];
8325 int el_act2crm(int element, int action)
8327 element = GFX_ELEMENT(element);
8329 return element_info[element].crumbled[action];
8332 int el_dir2img(int element, int direction)
8334 element = GFX_ELEMENT(element);
8336 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8339 int el2baseimg(int element)
8341 return element_info[element].graphic[ACTION_DEFAULT];
8344 int el2img(int element)
8346 element = GFX_ELEMENT(element);
8348 return element_info[element].graphic[ACTION_DEFAULT];
8351 int el2edimg(int element)
8353 element = GFX_ELEMENT(element);
8355 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8358 int el2preimg(int element)
8360 element = GFX_ELEMENT(element);
8362 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8365 int el2panelimg(int element)
8367 element = GFX_ELEMENT(element);
8369 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8372 int font2baseimg(int font_nr)
8374 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8377 int getBeltNrFromBeltElement(int element)
8379 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8380 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8381 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8384 int getBeltNrFromBeltActiveElement(int element)
8386 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8387 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8388 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8391 int getBeltNrFromBeltSwitchElement(int element)
8393 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8394 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8395 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8398 int getBeltDirNrFromBeltElement(int element)
8400 static int belt_base_element[4] =
8402 EL_CONVEYOR_BELT_1_LEFT,
8403 EL_CONVEYOR_BELT_2_LEFT,
8404 EL_CONVEYOR_BELT_3_LEFT,
8405 EL_CONVEYOR_BELT_4_LEFT
8408 int belt_nr = getBeltNrFromBeltElement(element);
8409 int belt_dir_nr = element - belt_base_element[belt_nr];
8411 return (belt_dir_nr % 3);
8414 int getBeltDirNrFromBeltSwitchElement(int element)
8416 static int belt_base_element[4] =
8418 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8419 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8420 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8421 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8424 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8425 int belt_dir_nr = element - belt_base_element[belt_nr];
8427 return (belt_dir_nr % 3);
8430 int getBeltDirFromBeltElement(int element)
8432 static int belt_move_dir[3] =
8439 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8441 return belt_move_dir[belt_dir_nr];
8444 int getBeltDirFromBeltSwitchElement(int element)
8446 static int belt_move_dir[3] =
8453 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8455 return belt_move_dir[belt_dir_nr];
8458 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8460 static int belt_base_element[4] =
8462 EL_CONVEYOR_BELT_1_LEFT,
8463 EL_CONVEYOR_BELT_2_LEFT,
8464 EL_CONVEYOR_BELT_3_LEFT,
8465 EL_CONVEYOR_BELT_4_LEFT
8468 return belt_base_element[belt_nr] + belt_dir_nr;
8471 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8473 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8475 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8478 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8480 static int belt_base_element[4] =
8482 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8483 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8484 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8485 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8488 return belt_base_element[belt_nr] + belt_dir_nr;
8491 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8493 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8495 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8498 boolean swapTiles_EM(boolean is_pre_emc_cave)
8500 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8503 boolean getTeamMode_EM(void)
8505 return game.team_mode || network_playing;
8508 boolean isActivePlayer_EM(int player_nr)
8510 return stored_player[player_nr].active;
8513 unsigned int InitRND(int seed)
8515 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8516 return InitEngineRandom_EM(seed);
8517 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8518 return InitEngineRandom_SP(seed);
8519 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8520 return InitEngineRandom_MM(seed);
8522 return InitEngineRandom_RND(seed);
8525 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8526 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8528 static int get_effective_element_EM(int tile, int frame_em)
8530 int element = object_mapping[tile].element_rnd;
8531 int action = object_mapping[tile].action;
8532 boolean is_backside = object_mapping[tile].is_backside;
8533 boolean action_removing = (action == ACTION_DIGGING ||
8534 action == ACTION_SNAPPING ||
8535 action == ACTION_COLLECTING);
8543 return (frame_em > 5 ? EL_EMPTY : element);
8549 else // frame_em == 7
8560 case Ydiamond_stone:
8564 case Xdrip_stretchB:
8580 case Ymagnify_blank:
8583 case Xsand_stonein_1:
8584 case Xsand_stonein_2:
8585 case Xsand_stonein_3:
8586 case Xsand_stonein_4:
8590 return (is_backside || action_removing ? EL_EMPTY : element);
8595 static boolean check_linear_animation_EM(int tile)
8599 case Xsand_stonesand_1:
8600 case Xsand_stonesand_quickout_1:
8601 case Xsand_sandstone_1:
8602 case Xsand_stonein_1:
8603 case Xsand_stoneout_1:
8631 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8632 boolean has_crumbled_graphics,
8633 int crumbled, int sync_frame)
8635 // if element can be crumbled, but certain action graphics are just empty
8636 // space (like instantly snapping sand to empty space in 1 frame), do not
8637 // treat these empty space graphics as crumbled graphics in EMC engine
8638 if (crumbled == IMG_EMPTY_SPACE)
8639 has_crumbled_graphics = FALSE;
8641 if (has_crumbled_graphics)
8643 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8644 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8645 g_crumbled->anim_delay,
8646 g_crumbled->anim_mode,
8647 g_crumbled->anim_start_frame,
8650 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8651 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8653 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8654 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8656 g_em->has_crumbled_graphics = TRUE;
8660 g_em->crumbled_bitmap = NULL;
8661 g_em->crumbled_src_x = 0;
8662 g_em->crumbled_src_y = 0;
8663 g_em->crumbled_border_size = 0;
8664 g_em->crumbled_tile_size = 0;
8666 g_em->has_crumbled_graphics = FALSE;
8671 void ResetGfxAnimation_EM(int x, int y, int tile)
8677 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8678 int tile, int frame_em, int x, int y)
8680 int action = object_mapping[tile].action;
8681 int direction = object_mapping[tile].direction;
8682 int effective_element = get_effective_element_EM(tile, frame_em);
8683 int graphic = (direction == MV_NONE ?
8684 el_act2img(effective_element, action) :
8685 el_act_dir2img(effective_element, action, direction));
8686 struct GraphicInfo *g = &graphic_info[graphic];
8688 boolean action_removing = (action == ACTION_DIGGING ||
8689 action == ACTION_SNAPPING ||
8690 action == ACTION_COLLECTING);
8691 boolean action_moving = (action == ACTION_FALLING ||
8692 action == ACTION_MOVING ||
8693 action == ACTION_PUSHING ||
8694 action == ACTION_EATING ||
8695 action == ACTION_FILLING ||
8696 action == ACTION_EMPTYING);
8697 boolean action_falling = (action == ACTION_FALLING ||
8698 action == ACTION_FILLING ||
8699 action == ACTION_EMPTYING);
8701 // special case: graphic uses "2nd movement tile" and has defined
8702 // 7 frames for movement animation (or less) => use default graphic
8703 // for last (8th) frame which ends the movement animation
8704 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8706 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8707 graphic = (direction == MV_NONE ?
8708 el_act2img(effective_element, action) :
8709 el_act_dir2img(effective_element, action, direction));
8711 g = &graphic_info[graphic];
8714 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8718 else if (action_moving)
8720 boolean is_backside = object_mapping[tile].is_backside;
8724 int direction = object_mapping[tile].direction;
8725 int move_dir = (action_falling ? MV_DOWN : direction);
8730 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8731 if (g->double_movement && frame_em == 0)
8735 if (move_dir == MV_LEFT)
8736 GfxFrame[x - 1][y] = GfxFrame[x][y];
8737 else if (move_dir == MV_RIGHT)
8738 GfxFrame[x + 1][y] = GfxFrame[x][y];
8739 else if (move_dir == MV_UP)
8740 GfxFrame[x][y - 1] = GfxFrame[x][y];
8741 else if (move_dir == MV_DOWN)
8742 GfxFrame[x][y + 1] = GfxFrame[x][y];
8749 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8750 if (tile == Xsand_stonesand_quickout_1 ||
8751 tile == Xsand_stonesand_quickout_2)
8755 if (graphic_info[graphic].anim_global_sync)
8756 sync_frame = FrameCounter;
8757 else if (graphic_info[graphic].anim_global_anim_sync)
8758 sync_frame = getGlobalAnimSyncFrame();
8759 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8760 sync_frame = GfxFrame[x][y];
8762 sync_frame = 0; // playfield border (pseudo steel)
8764 SetRandomAnimationValue(x, y);
8766 int frame = getAnimationFrame(g->anim_frames,
8769 g->anim_start_frame,
8772 g_em->unique_identifier =
8773 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8776 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8777 int tile, int frame_em, int x, int y)
8779 int action = object_mapping[tile].action;
8780 int direction = object_mapping[tile].direction;
8781 boolean is_backside = object_mapping[tile].is_backside;
8782 int effective_element = get_effective_element_EM(tile, frame_em);
8783 int effective_action = action;
8784 int graphic = (direction == MV_NONE ?
8785 el_act2img(effective_element, effective_action) :
8786 el_act_dir2img(effective_element, effective_action,
8788 int crumbled = (direction == MV_NONE ?
8789 el_act2crm(effective_element, effective_action) :
8790 el_act_dir2crm(effective_element, effective_action,
8792 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8793 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8794 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8795 struct GraphicInfo *g = &graphic_info[graphic];
8798 // special case: graphic uses "2nd movement tile" and has defined
8799 // 7 frames for movement animation (or less) => use default graphic
8800 // for last (8th) frame which ends the movement animation
8801 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8803 effective_action = ACTION_DEFAULT;
8804 graphic = (direction == MV_NONE ?
8805 el_act2img(effective_element, effective_action) :
8806 el_act_dir2img(effective_element, effective_action,
8808 crumbled = (direction == MV_NONE ?
8809 el_act2crm(effective_element, effective_action) :
8810 el_act_dir2crm(effective_element, effective_action,
8813 g = &graphic_info[graphic];
8816 if (graphic_info[graphic].anim_global_sync)
8817 sync_frame = FrameCounter;
8818 else if (graphic_info[graphic].anim_global_anim_sync)
8819 sync_frame = getGlobalAnimSyncFrame();
8820 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8821 sync_frame = GfxFrame[x][y];
8823 sync_frame = 0; // playfield border (pseudo steel)
8825 SetRandomAnimationValue(x, y);
8827 int frame = getAnimationFrame(g->anim_frames,
8830 g->anim_start_frame,
8833 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8834 g->double_movement && is_backside);
8836 // (updating the "crumbled" graphic definitions is probably not really needed,
8837 // as animations for crumbled graphics can't be longer than one EMC cycle)
8838 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8842 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8843 int player_nr, int anim, int frame_em)
8845 int element = player_mapping[player_nr][anim].element_rnd;
8846 int action = player_mapping[player_nr][anim].action;
8847 int direction = player_mapping[player_nr][anim].direction;
8848 int graphic = (direction == MV_NONE ?
8849 el_act2img(element, action) :
8850 el_act_dir2img(element, action, direction));
8851 struct GraphicInfo *g = &graphic_info[graphic];
8854 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8856 stored_player[player_nr].StepFrame = frame_em;
8858 sync_frame = stored_player[player_nr].Frame;
8860 int frame = getAnimationFrame(g->anim_frames,
8863 g->anim_start_frame,
8866 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8867 &g_em->src_x, &g_em->src_y, FALSE);
8870 void InitGraphicInfo_EM(void)
8874 // always start with reliable default values
8875 for (i = 0; i < GAME_TILE_MAX; i++)
8877 object_mapping[i].element_rnd = EL_UNKNOWN;
8878 object_mapping[i].is_backside = FALSE;
8879 object_mapping[i].action = ACTION_DEFAULT;
8880 object_mapping[i].direction = MV_NONE;
8883 // always start with reliable default values
8884 for (p = 0; p < MAX_PLAYERS; p++)
8886 for (i = 0; i < PLY_MAX; i++)
8888 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8889 player_mapping[p][i].action = ACTION_DEFAULT;
8890 player_mapping[p][i].direction = MV_NONE;
8894 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8896 int e = em_object_mapping_list[i].element_em;
8898 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8899 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8901 if (em_object_mapping_list[i].action != -1)
8902 object_mapping[e].action = em_object_mapping_list[i].action;
8904 if (em_object_mapping_list[i].direction != -1)
8905 object_mapping[e].direction =
8906 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8909 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8911 int a = em_player_mapping_list[i].action_em;
8912 int p = em_player_mapping_list[i].player_nr;
8914 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8916 if (em_player_mapping_list[i].action != -1)
8917 player_mapping[p][a].action = em_player_mapping_list[i].action;
8919 if (em_player_mapping_list[i].direction != -1)
8920 player_mapping[p][a].direction =
8921 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8924 for (i = 0; i < GAME_TILE_MAX; i++)
8926 int element = object_mapping[i].element_rnd;
8927 int action = object_mapping[i].action;
8928 int direction = object_mapping[i].direction;
8929 boolean is_backside = object_mapping[i].is_backside;
8930 boolean action_exploding = ((action == ACTION_EXPLODING ||
8931 action == ACTION_SMASHED_BY_ROCK ||
8932 action == ACTION_SMASHED_BY_SPRING) &&
8933 element != EL_DIAMOND);
8934 boolean action_active = (action == ACTION_ACTIVE);
8935 boolean action_other = (action == ACTION_OTHER);
8937 for (j = 0; j < 8; j++)
8939 int effective_element = get_effective_element_EM(i, j);
8940 int effective_action = (j < 7 ? action :
8941 i == Xdrip_stretch ? action :
8942 i == Xdrip_stretchB ? action :
8943 i == Ydrip_1_s ? action :
8944 i == Ydrip_1_sB ? action :
8945 i == Yball_1 ? action :
8946 i == Xball_2 ? action :
8947 i == Yball_2 ? action :
8948 i == Yball_blank ? action :
8949 i == Ykey_1_blank ? action :
8950 i == Ykey_2_blank ? action :
8951 i == Ykey_3_blank ? action :
8952 i == Ykey_4_blank ? action :
8953 i == Ykey_5_blank ? action :
8954 i == Ykey_6_blank ? action :
8955 i == Ykey_7_blank ? action :
8956 i == Ykey_8_blank ? action :
8957 i == Ylenses_blank ? action :
8958 i == Ymagnify_blank ? action :
8959 i == Ygrass_blank ? action :
8960 i == Ydirt_blank ? action :
8961 i == Xsand_stonein_1 ? action :
8962 i == Xsand_stonein_2 ? action :
8963 i == Xsand_stonein_3 ? action :
8964 i == Xsand_stonein_4 ? action :
8965 i == Xsand_stoneout_1 ? action :
8966 i == Xsand_stoneout_2 ? action :
8967 i == Xboom_android ? ACTION_EXPLODING :
8968 action_exploding ? ACTION_EXPLODING :
8969 action_active ? action :
8970 action_other ? action :
8972 int graphic = (el_act_dir2img(effective_element, effective_action,
8974 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8976 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8977 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8978 boolean has_action_graphics = (graphic != base_graphic);
8979 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8980 struct GraphicInfo *g = &graphic_info[graphic];
8981 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8984 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8985 boolean special_animation = (action != ACTION_DEFAULT &&
8986 g->anim_frames == 3 &&
8987 g->anim_delay == 2 &&
8988 g->anim_mode & ANIM_LINEAR);
8989 int sync_frame = (i == Xdrip_stretch ? 7 :
8990 i == Xdrip_stretchB ? 7 :
8991 i == Ydrip_2_s ? j + 8 :
8992 i == Ydrip_2_sB ? j + 8 :
9001 i == Xfake_acid_1 ? 0 :
9002 i == Xfake_acid_2 ? 10 :
9003 i == Xfake_acid_3 ? 20 :
9004 i == Xfake_acid_4 ? 30 :
9005 i == Xfake_acid_5 ? 40 :
9006 i == Xfake_acid_6 ? 50 :
9007 i == Xfake_acid_7 ? 60 :
9008 i == Xfake_acid_8 ? 70 :
9009 i == Xfake_acid_1_player ? 0 :
9010 i == Xfake_acid_2_player ? 10 :
9011 i == Xfake_acid_3_player ? 20 :
9012 i == Xfake_acid_4_player ? 30 :
9013 i == Xfake_acid_5_player ? 40 :
9014 i == Xfake_acid_6_player ? 50 :
9015 i == Xfake_acid_7_player ? 60 :
9016 i == Xfake_acid_8_player ? 70 :
9018 i == Yball_2 ? j + 8 :
9019 i == Yball_blank ? j + 1 :
9020 i == Ykey_1_blank ? j + 1 :
9021 i == Ykey_2_blank ? j + 1 :
9022 i == Ykey_3_blank ? j + 1 :
9023 i == Ykey_4_blank ? j + 1 :
9024 i == Ykey_5_blank ? j + 1 :
9025 i == Ykey_6_blank ? j + 1 :
9026 i == Ykey_7_blank ? j + 1 :
9027 i == Ykey_8_blank ? j + 1 :
9028 i == Ylenses_blank ? j + 1 :
9029 i == Ymagnify_blank ? j + 1 :
9030 i == Ygrass_blank ? j + 1 :
9031 i == Ydirt_blank ? j + 1 :
9032 i == Xamoeba_1 ? 0 :
9033 i == Xamoeba_2 ? 1 :
9034 i == Xamoeba_3 ? 2 :
9035 i == Xamoeba_4 ? 3 :
9036 i == Xamoeba_5 ? 0 :
9037 i == Xamoeba_6 ? 1 :
9038 i == Xamoeba_7 ? 2 :
9039 i == Xamoeba_8 ? 3 :
9040 i == Xexit_2 ? j + 8 :
9041 i == Xexit_3 ? j + 16 :
9042 i == Xdynamite_1 ? 0 :
9043 i == Xdynamite_2 ? 8 :
9044 i == Xdynamite_3 ? 16 :
9045 i == Xdynamite_4 ? 24 :
9046 i == Xsand_stonein_1 ? j + 1 :
9047 i == Xsand_stonein_2 ? j + 9 :
9048 i == Xsand_stonein_3 ? j + 17 :
9049 i == Xsand_stonein_4 ? j + 25 :
9050 i == Xsand_stoneout_1 && j == 0 ? 0 :
9051 i == Xsand_stoneout_1 && j == 1 ? 0 :
9052 i == Xsand_stoneout_1 && j == 2 ? 1 :
9053 i == Xsand_stoneout_1 && j == 3 ? 2 :
9054 i == Xsand_stoneout_1 && j == 4 ? 2 :
9055 i == Xsand_stoneout_1 && j == 5 ? 3 :
9056 i == Xsand_stoneout_1 && j == 6 ? 4 :
9057 i == Xsand_stoneout_1 && j == 7 ? 4 :
9058 i == Xsand_stoneout_2 && j == 0 ? 5 :
9059 i == Xsand_stoneout_2 && j == 1 ? 6 :
9060 i == Xsand_stoneout_2 && j == 2 ? 7 :
9061 i == Xsand_stoneout_2 && j == 3 ? 8 :
9062 i == Xsand_stoneout_2 && j == 4 ? 9 :
9063 i == Xsand_stoneout_2 && j == 5 ? 11 :
9064 i == Xsand_stoneout_2 && j == 6 ? 13 :
9065 i == Xsand_stoneout_2 && j == 7 ? 15 :
9066 i == Xboom_bug && j == 1 ? 2 :
9067 i == Xboom_bug && j == 2 ? 2 :
9068 i == Xboom_bug && j == 3 ? 4 :
9069 i == Xboom_bug && j == 4 ? 4 :
9070 i == Xboom_bug && j == 5 ? 2 :
9071 i == Xboom_bug && j == 6 ? 2 :
9072 i == Xboom_bug && j == 7 ? 0 :
9073 i == Xboom_tank && j == 1 ? 2 :
9074 i == Xboom_tank && j == 2 ? 2 :
9075 i == Xboom_tank && j == 3 ? 4 :
9076 i == Xboom_tank && j == 4 ? 4 :
9077 i == Xboom_tank && j == 5 ? 2 :
9078 i == Xboom_tank && j == 6 ? 2 :
9079 i == Xboom_tank && j == 7 ? 0 :
9080 i == Xboom_android && j == 7 ? 6 :
9081 i == Xboom_1 && j == 1 ? 2 :
9082 i == Xboom_1 && j == 2 ? 2 :
9083 i == Xboom_1 && j == 3 ? 4 :
9084 i == Xboom_1 && j == 4 ? 4 :
9085 i == Xboom_1 && j == 5 ? 6 :
9086 i == Xboom_1 && j == 6 ? 6 :
9087 i == Xboom_1 && j == 7 ? 8 :
9088 i == Xboom_2 && j == 0 ? 8 :
9089 i == Xboom_2 && j == 1 ? 8 :
9090 i == Xboom_2 && j == 2 ? 10 :
9091 i == Xboom_2 && j == 3 ? 10 :
9092 i == Xboom_2 && j == 4 ? 10 :
9093 i == Xboom_2 && j == 5 ? 12 :
9094 i == Xboom_2 && j == 6 ? 12 :
9095 i == Xboom_2 && j == 7 ? 12 :
9096 special_animation && j == 4 ? 3 :
9097 effective_action != action ? 0 :
9099 int frame = getAnimationFrame(g->anim_frames,
9102 g->anim_start_frame,
9105 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9106 g->double_movement && is_backside);
9108 g_em->bitmap = src_bitmap;
9109 g_em->src_x = src_x;
9110 g_em->src_y = src_y;
9111 g_em->src_offset_x = 0;
9112 g_em->src_offset_y = 0;
9113 g_em->dst_offset_x = 0;
9114 g_em->dst_offset_y = 0;
9115 g_em->width = TILEX;
9116 g_em->height = TILEY;
9118 g_em->preserve_background = FALSE;
9120 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9123 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9124 effective_action == ACTION_MOVING ||
9125 effective_action == ACTION_PUSHING ||
9126 effective_action == ACTION_EATING)) ||
9127 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9128 effective_action == ACTION_EMPTYING)))
9131 (effective_action == ACTION_FALLING ||
9132 effective_action == ACTION_FILLING ||
9133 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9134 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9135 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9136 int num_steps = (i == Ydrip_1_s ? 16 :
9137 i == Ydrip_1_sB ? 16 :
9138 i == Ydrip_2_s ? 16 :
9139 i == Ydrip_2_sB ? 16 :
9140 i == Xsand_stonein_1 ? 32 :
9141 i == Xsand_stonein_2 ? 32 :
9142 i == Xsand_stonein_3 ? 32 :
9143 i == Xsand_stonein_4 ? 32 :
9144 i == Xsand_stoneout_1 ? 16 :
9145 i == Xsand_stoneout_2 ? 16 : 8);
9146 int cx = ABS(dx) * (TILEX / num_steps);
9147 int cy = ABS(dy) * (TILEY / num_steps);
9148 int step_frame = (i == Ydrip_2_s ? j + 8 :
9149 i == Ydrip_2_sB ? j + 8 :
9150 i == Xsand_stonein_2 ? j + 8 :
9151 i == Xsand_stonein_3 ? j + 16 :
9152 i == Xsand_stonein_4 ? j + 24 :
9153 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9154 int step = (is_backside ? step_frame : num_steps - step_frame);
9156 if (is_backside) // tile where movement starts
9158 if (dx < 0 || dy < 0)
9160 g_em->src_offset_x = cx * step;
9161 g_em->src_offset_y = cy * step;
9165 g_em->dst_offset_x = cx * step;
9166 g_em->dst_offset_y = cy * step;
9169 else // tile where movement ends
9171 if (dx < 0 || dy < 0)
9173 g_em->dst_offset_x = cx * step;
9174 g_em->dst_offset_y = cy * step;
9178 g_em->src_offset_x = cx * step;
9179 g_em->src_offset_y = cy * step;
9183 g_em->width = TILEX - cx * step;
9184 g_em->height = TILEY - cy * step;
9187 // create unique graphic identifier to decide if tile must be redrawn
9188 /* bit 31 - 16 (16 bit): EM style graphic
9189 bit 15 - 12 ( 4 bit): EM style frame
9190 bit 11 - 6 ( 6 bit): graphic width
9191 bit 5 - 0 ( 6 bit): graphic height */
9192 g_em->unique_identifier =
9193 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9197 for (i = 0; i < GAME_TILE_MAX; i++)
9199 for (j = 0; j < 8; j++)
9201 int element = object_mapping[i].element_rnd;
9202 int action = object_mapping[i].action;
9203 int direction = object_mapping[i].direction;
9204 boolean is_backside = object_mapping[i].is_backside;
9205 int graphic_action = el_act_dir2img(element, action, direction);
9206 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9208 if ((action == ACTION_SMASHED_BY_ROCK ||
9209 action == ACTION_SMASHED_BY_SPRING ||
9210 action == ACTION_EATING) &&
9211 graphic_action == graphic_default)
9213 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9214 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9215 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9216 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9219 // no separate animation for "smashed by rock" -- use rock instead
9220 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9221 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9223 g_em->bitmap = g_xx->bitmap;
9224 g_em->src_x = g_xx->src_x;
9225 g_em->src_y = g_xx->src_y;
9226 g_em->src_offset_x = g_xx->src_offset_x;
9227 g_em->src_offset_y = g_xx->src_offset_y;
9228 g_em->dst_offset_x = g_xx->dst_offset_x;
9229 g_em->dst_offset_y = g_xx->dst_offset_y;
9230 g_em->width = g_xx->width;
9231 g_em->height = g_xx->height;
9232 g_em->unique_identifier = g_xx->unique_identifier;
9235 g_em->preserve_background = TRUE;
9240 for (p = 0; p < MAX_PLAYERS; p++)
9242 for (i = 0; i < PLY_MAX; i++)
9244 int element = player_mapping[p][i].element_rnd;
9245 int action = player_mapping[p][i].action;
9246 int direction = player_mapping[p][i].direction;
9248 for (j = 0; j < 8; j++)
9250 int effective_element = element;
9251 int effective_action = action;
9252 int graphic = (direction == MV_NONE ?
9253 el_act2img(effective_element, effective_action) :
9254 el_act_dir2img(effective_element, effective_action,
9256 struct GraphicInfo *g = &graphic_info[graphic];
9257 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9261 int frame = getAnimationFrame(g->anim_frames,
9264 g->anim_start_frame,
9267 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9269 g_em->bitmap = src_bitmap;
9270 g_em->src_x = src_x;
9271 g_em->src_y = src_y;
9272 g_em->src_offset_x = 0;
9273 g_em->src_offset_y = 0;
9274 g_em->dst_offset_x = 0;
9275 g_em->dst_offset_y = 0;
9276 g_em->width = TILEX;
9277 g_em->height = TILEY;
9283 static void CheckSaveEngineSnapshot_EM(int frame,
9284 boolean any_player_moving,
9285 boolean any_player_snapping,
9286 boolean any_player_dropping)
9288 if (frame == 7 && !any_player_dropping)
9290 if (!local_player->was_waiting)
9292 if (!CheckSaveEngineSnapshotToList())
9295 local_player->was_waiting = TRUE;
9298 else if (any_player_moving || any_player_snapping || any_player_dropping)
9300 local_player->was_waiting = FALSE;
9304 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9305 boolean murphy_is_dropping)
9307 if (murphy_is_waiting)
9309 if (!local_player->was_waiting)
9311 if (!CheckSaveEngineSnapshotToList())
9314 local_player->was_waiting = TRUE;
9319 local_player->was_waiting = FALSE;
9323 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9324 boolean button_released)
9326 if (button_released)
9328 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9329 CheckSaveEngineSnapshotToList();
9331 else if (element_clicked)
9333 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9334 CheckSaveEngineSnapshotToList();
9336 game.snapshot.changed_action = TRUE;
9340 boolean CheckSingleStepMode_EM(int frame,
9341 boolean any_player_moving,
9342 boolean any_player_snapping,
9343 boolean any_player_dropping)
9345 if (tape.single_step && tape.recording && !tape.pausing)
9346 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9347 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9349 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9350 any_player_snapping, any_player_dropping);
9352 return tape.pausing;
9355 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9356 boolean murphy_is_dropping)
9358 boolean murphy_starts_dropping = FALSE;
9361 for (i = 0; i < MAX_PLAYERS; i++)
9362 if (stored_player[i].force_dropping)
9363 murphy_starts_dropping = TRUE;
9365 if (tape.single_step && tape.recording && !tape.pausing)
9366 if (murphy_is_waiting && !murphy_starts_dropping)
9367 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9369 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9372 void CheckSingleStepMode_MM(boolean element_clicked,
9373 boolean button_released)
9375 if (tape.single_step && tape.recording && !tape.pausing)
9376 if (button_released)
9377 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9379 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9382 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9383 int graphic, int sync_frame)
9385 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9387 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9390 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9392 return (IS_NEXT_FRAME(sync_frame, graphic));
9395 int getGraphicInfo_Delay(int graphic)
9397 return graphic_info[graphic].anim_delay;
9400 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9402 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9405 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9411 void PlayMenuSoundExt(int sound)
9413 if (sound == SND_UNDEFINED)
9416 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9417 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9420 if (IS_LOOP_SOUND(sound))
9421 PlaySoundLoop(sound);
9426 void PlayMenuSound(void)
9428 PlayMenuSoundExt(menu.sound[game_status]);
9431 void PlayMenuSoundStereo(int sound, int stereo_position)
9433 if (sound == SND_UNDEFINED)
9436 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9437 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9440 if (IS_LOOP_SOUND(sound))
9441 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9443 PlaySoundStereo(sound, stereo_position);
9446 void PlayMenuSoundIfLoopExt(int sound)
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 PlaySoundLoop(sound);
9459 void PlayMenuSoundIfLoop(void)
9461 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9464 void PlayMenuMusicExt(int music)
9466 if (music == MUS_UNDEFINED)
9469 if (!setup.sound_music)
9472 if (IS_LOOP_MUSIC(music))
9473 PlayMusicLoop(music);
9478 void PlayMenuMusic(void)
9480 char *curr_music = getCurrentlyPlayingMusicFilename();
9481 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9483 if (!strEqual(curr_music, next_music))
9484 PlayMenuMusicExt(menu.music[game_status]);
9487 void PlayMenuSoundsAndMusic(void)
9493 static void FadeMenuSounds(void)
9498 static void FadeMenuMusic(void)
9500 char *curr_music = getCurrentlyPlayingMusicFilename();
9501 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9503 if (!strEqual(curr_music, next_music))
9507 void FadeMenuSoundsAndMusic(void)
9513 void PlaySoundActivating(void)
9516 PlaySound(SND_MENU_ITEM_ACTIVATING);
9520 void PlaySoundSelecting(void)
9523 PlaySound(SND_MENU_ITEM_SELECTING);
9527 void ToggleFullscreenIfNeeded(void)
9529 // if setup and video fullscreen state are already matching, nothing do do
9530 if (setup.fullscreen == video.fullscreen_enabled ||
9531 !video.fullscreen_available)
9534 SDLSetWindowFullscreen(setup.fullscreen);
9536 // set setup value according to successfully changed fullscreen mode
9537 setup.fullscreen = video.fullscreen_enabled;
9540 void ChangeWindowScalingIfNeeded(void)
9542 // if setup and video window scaling are already matching, nothing do do
9543 if (setup.window_scaling_percent == video.window_scaling_percent ||
9544 video.fullscreen_enabled)
9547 SDLSetWindowScaling(setup.window_scaling_percent);
9549 // set setup value according to successfully changed window scaling
9550 setup.window_scaling_percent = video.window_scaling_percent;
9553 void ChangeVsyncModeIfNeeded(void)
9555 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9556 int video_vsync_mode = video.vsync_mode;
9558 // if setup and video vsync mode are already matching, nothing do do
9559 if (setup_vsync_mode == video_vsync_mode)
9562 // if renderer is using OpenGL, vsync mode can directly be changed
9563 SDLSetScreenVsyncMode(setup.vsync_mode);
9565 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9566 if (video.vsync_mode == video_vsync_mode)
9568 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9570 // save backbuffer content which gets lost when re-creating screen
9571 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9573 // force re-creating screen and renderer to set new vsync mode
9574 video.fullscreen_enabled = !setup.fullscreen;
9576 // when creating new renderer, destroy textures linked to old renderer
9577 FreeAllImageTextures(); // needs old renderer to free the textures
9579 // re-create screen and renderer (including change of vsync mode)
9580 ChangeVideoModeIfNeeded(setup.fullscreen);
9582 // set setup value according to successfully changed fullscreen mode
9583 setup.fullscreen = video.fullscreen_enabled;
9585 // restore backbuffer content from temporary backbuffer backup bitmap
9586 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9587 FreeBitmap(tmp_backbuffer);
9589 // update visible window/screen
9590 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9592 // when changing vsync mode, re-create textures for new renderer
9593 InitImageTextures();
9596 // set setup value according to successfully changed vsync mode
9597 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9600 static void JoinRectangles(int *x, int *y, int *width, int *height,
9601 int x2, int y2, int width2, int height2)
9603 // do not join with "off-screen" rectangle
9604 if (x2 == -1 || y2 == -1)
9609 *width = MAX(*width, width2);
9610 *height = MAX(*height, height2);
9613 void SetAnimStatus(int anim_status_new)
9615 if (anim_status_new == GAME_MODE_MAIN)
9616 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9617 else if (anim_status_new == GAME_MODE_NAMES)
9618 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9619 else if (anim_status_new == GAME_MODE_SCORES)
9620 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9622 global.anim_status_next = anim_status_new;
9624 // directly set screen modes that are entered without fading
9625 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9626 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9627 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9628 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9629 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9630 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9631 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9632 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9633 global.anim_status = global.anim_status_next;
9636 void SetGameStatus(int game_status_new)
9638 if (game_status_new != game_status)
9639 game_status_last_screen = game_status;
9641 game_status = game_status_new;
9643 SetAnimStatus(game_status_new);
9646 void SetFontStatus(int game_status_new)
9648 static int last_game_status = -1;
9650 if (game_status_new != -1)
9652 // set game status for font use after storing last game status
9653 last_game_status = game_status;
9654 game_status = game_status_new;
9658 // reset game status after font use from last stored game status
9659 game_status = last_game_status;
9663 void ResetFontStatus(void)
9668 void SetLevelSetInfo(char *identifier, int level_nr)
9670 setString(&levelset.identifier, identifier);
9672 levelset.level_nr = level_nr;
9675 boolean CheckIfAllViewportsHaveChanged(void)
9677 // if game status has not changed, viewports have not changed either
9678 if (game_status == game_status_last)
9681 // check if all viewports have changed with current game status
9683 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9684 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9685 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9686 int new_real_sx = vp_playfield->x;
9687 int new_real_sy = vp_playfield->y;
9688 int new_full_sxsize = vp_playfield->width;
9689 int new_full_sysize = vp_playfield->height;
9690 int new_dx = vp_door_1->x;
9691 int new_dy = vp_door_1->y;
9692 int new_dxsize = vp_door_1->width;
9693 int new_dysize = vp_door_1->height;
9694 int new_vx = vp_door_2->x;
9695 int new_vy = vp_door_2->y;
9696 int new_vxsize = vp_door_2->width;
9697 int new_vysize = vp_door_2->height;
9699 boolean playfield_viewport_has_changed =
9700 (new_real_sx != REAL_SX ||
9701 new_real_sy != REAL_SY ||
9702 new_full_sxsize != FULL_SXSIZE ||
9703 new_full_sysize != FULL_SYSIZE);
9705 boolean door_1_viewport_has_changed =
9708 new_dxsize != DXSIZE ||
9709 new_dysize != DYSIZE);
9711 boolean door_2_viewport_has_changed =
9714 new_vxsize != VXSIZE ||
9715 new_vysize != VYSIZE ||
9716 game_status_last == GAME_MODE_EDITOR);
9718 return (playfield_viewport_has_changed &&
9719 door_1_viewport_has_changed &&
9720 door_2_viewport_has_changed);
9723 boolean CheckFadeAll(void)
9725 return (CheckIfGlobalBorderHasChanged() ||
9726 CheckIfAllViewportsHaveChanged());
9729 void ChangeViewportPropertiesIfNeeded(void)
9731 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9732 FALSE : setup.small_game_graphics);
9733 int gfx_game_mode = getGlobalGameStatus(game_status);
9734 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9736 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9737 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9738 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9739 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9740 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9741 int new_win_xsize = vp_window->width;
9742 int new_win_ysize = vp_window->height;
9743 int border_left = vp_playfield->border_left;
9744 int border_right = vp_playfield->border_right;
9745 int border_top = vp_playfield->border_top;
9746 int border_bottom = vp_playfield->border_bottom;
9747 int new_sx = vp_playfield->x + border_left;
9748 int new_sy = vp_playfield->y + border_top;
9749 int new_sxsize = vp_playfield->width - border_left - border_right;
9750 int new_sysize = vp_playfield->height - border_top - border_bottom;
9751 int new_real_sx = vp_playfield->x;
9752 int new_real_sy = vp_playfield->y;
9753 int new_full_sxsize = vp_playfield->width;
9754 int new_full_sysize = vp_playfield->height;
9755 int new_dx = vp_door_1->x;
9756 int new_dy = vp_door_1->y;
9757 int new_dxsize = vp_door_1->width;
9758 int new_dysize = vp_door_1->height;
9759 int new_vx = vp_door_2->x;
9760 int new_vy = vp_door_2->y;
9761 int new_vxsize = vp_door_2->width;
9762 int new_vysize = vp_door_2->height;
9763 int new_ex = vp_door_3->x;
9764 int new_ey = vp_door_3->y;
9765 int new_exsize = vp_door_3->width;
9766 int new_eysize = vp_door_3->height;
9767 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9768 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9769 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9770 int new_scr_fieldx = new_sxsize / tilesize;
9771 int new_scr_fieldy = new_sysize / tilesize;
9772 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9773 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9774 boolean init_gfx_buffers = FALSE;
9775 boolean init_video_buffer = FALSE;
9776 boolean init_gadgets_and_anims = FALSE;
9777 boolean init_em_graphics = FALSE;
9779 if (new_win_xsize != WIN_XSIZE ||
9780 new_win_ysize != WIN_YSIZE)
9782 WIN_XSIZE = new_win_xsize;
9783 WIN_YSIZE = new_win_ysize;
9785 init_video_buffer = TRUE;
9786 init_gfx_buffers = TRUE;
9787 init_gadgets_and_anims = TRUE;
9789 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9792 if (new_scr_fieldx != SCR_FIELDX ||
9793 new_scr_fieldy != SCR_FIELDY)
9795 // this always toggles between MAIN and GAME when using small tile size
9797 SCR_FIELDX = new_scr_fieldx;
9798 SCR_FIELDY = new_scr_fieldy;
9800 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9811 new_sxsize != SXSIZE ||
9812 new_sysize != SYSIZE ||
9813 new_dxsize != DXSIZE ||
9814 new_dysize != DYSIZE ||
9815 new_vxsize != VXSIZE ||
9816 new_vysize != VYSIZE ||
9817 new_exsize != EXSIZE ||
9818 new_eysize != EYSIZE ||
9819 new_real_sx != REAL_SX ||
9820 new_real_sy != REAL_SY ||
9821 new_full_sxsize != FULL_SXSIZE ||
9822 new_full_sysize != FULL_SYSIZE ||
9823 new_tilesize_var != TILESIZE_VAR
9826 // ------------------------------------------------------------------------
9827 // determine next fading area for changed viewport definitions
9828 // ------------------------------------------------------------------------
9830 // start with current playfield area (default fading area)
9833 FADE_SXSIZE = FULL_SXSIZE;
9834 FADE_SYSIZE = FULL_SYSIZE;
9836 // add new playfield area if position or size has changed
9837 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9838 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9840 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9841 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9844 // add current and new door 1 area if position or size has changed
9845 if (new_dx != DX || new_dy != DY ||
9846 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9848 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9849 DX, DY, DXSIZE, DYSIZE);
9850 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9851 new_dx, new_dy, new_dxsize, new_dysize);
9854 // add current and new door 2 area if position or size has changed
9855 if (new_vx != VX || new_vy != VY ||
9856 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9858 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9859 VX, VY, VXSIZE, VYSIZE);
9860 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9861 new_vx, new_vy, new_vxsize, new_vysize);
9864 // ------------------------------------------------------------------------
9865 // handle changed tile size
9866 // ------------------------------------------------------------------------
9868 if (new_tilesize_var != TILESIZE_VAR)
9870 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9872 // changing tile size invalidates scroll values of engine snapshots
9873 FreeEngineSnapshotSingle();
9875 // changing tile size requires update of graphic mapping for EM engine
9876 init_em_graphics = TRUE;
9887 SXSIZE = new_sxsize;
9888 SYSIZE = new_sysize;
9889 DXSIZE = new_dxsize;
9890 DYSIZE = new_dysize;
9891 VXSIZE = new_vxsize;
9892 VYSIZE = new_vysize;
9893 EXSIZE = new_exsize;
9894 EYSIZE = new_eysize;
9895 REAL_SX = new_real_sx;
9896 REAL_SY = new_real_sy;
9897 FULL_SXSIZE = new_full_sxsize;
9898 FULL_SYSIZE = new_full_sysize;
9899 TILESIZE_VAR = new_tilesize_var;
9901 init_gfx_buffers = TRUE;
9902 init_gadgets_and_anims = TRUE;
9904 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9905 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9908 if (init_gfx_buffers)
9910 // Debug("tools:viewport", "init_gfx_buffers");
9912 SCR_FIELDX = new_scr_fieldx_buffers;
9913 SCR_FIELDY = new_scr_fieldy_buffers;
9917 SCR_FIELDX = new_scr_fieldx;
9918 SCR_FIELDY = new_scr_fieldy;
9920 SetDrawDeactivationMask(REDRAW_NONE);
9921 SetDrawBackgroundMask(REDRAW_FIELD);
9924 if (init_video_buffer)
9926 // Debug("tools:viewport", "init_video_buffer");
9928 FreeAllImageTextures(); // needs old renderer to free the textures
9930 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9931 InitImageTextures();
9934 if (init_gadgets_and_anims)
9936 // Debug("tools:viewport", "init_gadgets_and_anims");
9939 InitGlobalAnimations();
9942 if (init_em_graphics)
9944 InitGraphicInfo_EM();
9948 void OpenURL(char *url)
9950 #if SDL_VERSION_ATLEAST(2,0,14)
9953 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9954 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9955 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9959 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9961 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9965 // ============================================================================
9967 // ============================================================================
9969 #if defined(PLATFORM_WINDOWS)
9970 /* FILETIME of Jan 1 1970 00:00:00. */
9971 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9974 * timezone information is stored outside the kernel so tzp isn't used anymore.
9976 * Note: this function is not for Win32 high precision timing purpose. See
9979 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9982 SYSTEMTIME system_time;
9983 ULARGE_INTEGER ularge;
9985 GetSystemTime(&system_time);
9986 SystemTimeToFileTime(&system_time, &file_time);
9987 ularge.LowPart = file_time.dwLowDateTime;
9988 ularge.HighPart = file_time.dwHighDateTime;
9990 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9991 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9997 static char *test_init_uuid_random_function_simple(void)
9999 static char seed_text[100];
10000 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10002 sprintf(seed_text, "%d", seed);
10007 static char *test_init_uuid_random_function_better(void)
10009 static char seed_text[100];
10010 struct timeval current_time;
10012 gettimeofday(¤t_time, NULL);
10014 prng_seed_bytes(¤t_time, sizeof(current_time));
10016 sprintf(seed_text, "%ld.%ld",
10017 (long)current_time.tv_sec,
10018 (long)current_time.tv_usec);
10023 #if defined(PLATFORM_WINDOWS)
10024 static char *test_init_uuid_random_function_better_windows(void)
10026 static char seed_text[100];
10027 struct timeval current_time;
10029 gettimeofday_windows(¤t_time, NULL);
10031 prng_seed_bytes(¤t_time, sizeof(current_time));
10033 sprintf(seed_text, "%ld.%ld",
10034 (long)current_time.tv_sec,
10035 (long)current_time.tv_usec);
10041 static unsigned int test_uuid_random_function_simple(int max)
10043 return GetSimpleRandom(max);
10046 static unsigned int test_uuid_random_function_better(int max)
10048 return (max > 0 ? prng_get_uint() % max : 0);
10051 #if defined(PLATFORM_WINDOWS)
10052 #define NUM_UUID_TESTS 3
10054 #define NUM_UUID_TESTS 2
10057 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10059 struct hashtable *hash_seeds =
10060 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10061 struct hashtable *hash_uuids =
10062 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10063 static char message[100];
10066 char *random_name = (nr == 0 ? "simple" : "better");
10067 char *random_type = (always_seed ? "always" : "only once");
10068 char *(*init_random_function)(void) =
10070 test_init_uuid_random_function_simple :
10071 test_init_uuid_random_function_better);
10072 unsigned int (*random_function)(int) =
10074 test_uuid_random_function_simple :
10075 test_uuid_random_function_better);
10078 #if defined(PLATFORM_WINDOWS)
10081 random_name = "windows";
10082 init_random_function = test_init_uuid_random_function_better_windows;
10088 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10089 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10091 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10092 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10093 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10095 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10099 // always initialize random number generator at least once
10100 init_random_function();
10102 unsigned int time_start = SDL_GetTicks();
10104 for (i = 0; i < num_uuids; i++)
10108 char *seed = getStringCopy(init_random_function());
10110 hashtable_remove(hash_seeds, seed);
10111 hashtable_insert(hash_seeds, seed, "1");
10114 char *uuid = getStringCopy(getUUIDExt(random_function));
10116 hashtable_remove(hash_uuids, uuid);
10117 hashtable_insert(hash_uuids, uuid, "1");
10120 int num_unique_seeds = hashtable_count(hash_seeds);
10121 int num_unique_uuids = hashtable_count(hash_uuids);
10123 unsigned int time_needed = SDL_GetTicks() - time_start;
10125 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10127 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10130 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10132 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10133 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10135 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10137 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10139 Request(message, REQ_CONFIRM);
10141 hashtable_destroy(hash_seeds, 0);
10142 hashtable_destroy(hash_uuids, 0);
10145 void TestGeneratingUUIDs(void)
10147 int num_uuids = 1000000;
10150 for (i = 0; i < NUM_UUID_TESTS; i++)
10151 for (j = 0; j < 2; j++)
10152 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10154 CloseAllAndExit(0);