1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void MapToolButtons(unsigned int);
179 static void UnmapToolButtons(void);
180 static void HandleToolButtons(struct GadgetInfo *);
181 static int el_act_dir2crm(int, int, int);
182 static int el_act2crm(int, int);
184 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
185 static int request_gadget_id = -1;
187 static char *print_if_not_empty(int element)
189 static char *s = NULL;
190 char *token_name = element_info[element].token_name;
195 s = checked_malloc(strlen(token_name) + 10 + 1);
197 if (element != EL_EMPTY)
198 sprintf(s, "%d\t['%s']", element, token_name);
200 sprintf(s, "%d", element);
205 int getFieldbufferOffsetX_RND(int dir, int pos)
207 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
208 int dx = (dir & MV_HORIZONTAL ? pos : 0);
209 int dx_var = dx * TILESIZE_VAR / TILESIZE;
212 if (EVEN(SCR_FIELDX))
214 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
215 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
217 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
218 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
220 fx += (dx_var > 0 ? TILEX_VAR : 0);
227 if (full_lev_fieldx <= SCR_FIELDX)
229 if (EVEN(SCR_FIELDX))
230 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
232 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 int getFieldbufferOffsetY_RND(int dir, int pos)
240 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
241 int dy = (dir & MV_VERTICAL ? pos : 0);
242 int dy_var = dy * TILESIZE_VAR / TILESIZE;
245 if (EVEN(SCR_FIELDY))
247 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
248 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
250 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
251 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
253 fy += (dy_var > 0 ? TILEY_VAR : 0);
260 if (full_lev_fieldy <= SCR_FIELDY)
262 if (EVEN(SCR_FIELDY))
263 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
265 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 static int getLevelFromScreenX_RND(int sx)
273 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
276 int lx = LEVELX((px + dx) / TILESIZE_VAR);
281 static int getLevelFromScreenY_RND(int sy)
283 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
286 int ly = LEVELY((py + dy) / TILESIZE_VAR);
291 static int getLevelFromScreenX_EM(int sx)
293 int level_xsize = level.native_em_level->cav->width;
294 int full_xsize = level_xsize * TILESIZE_VAR;
296 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
298 int fx = getFieldbufferOffsetX_EM();
301 int lx = LEVELX((px + dx) / TILESIZE_VAR);
306 static int getLevelFromScreenY_EM(int sy)
308 int level_ysize = level.native_em_level->cav->height;
309 int full_ysize = level_ysize * TILESIZE_VAR;
311 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
313 int fy = getFieldbufferOffsetY_EM();
316 int ly = LEVELY((py + dy) / TILESIZE_VAR);
321 static int getLevelFromScreenX_SP(int sx)
323 int menBorder = setup.sp_show_border_elements;
324 int level_xsize = level.native_sp_level->width;
325 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
327 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
329 int fx = getFieldbufferOffsetX_SP();
332 int lx = LEVELX((px + dx) / TILESIZE_VAR);
337 static int getLevelFromScreenY_SP(int sy)
339 int menBorder = setup.sp_show_border_elements;
340 int level_ysize = level.native_sp_level->height;
341 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
343 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
345 int fy = getFieldbufferOffsetY_SP();
348 int ly = LEVELY((py + dy) / TILESIZE_VAR);
353 static int getLevelFromScreenX_MM(int sx)
355 int level_xsize = level.native_mm_level->fieldx;
356 int full_xsize = level_xsize * TILESIZE_VAR;
358 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
361 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
366 static int getLevelFromScreenY_MM(int sy)
368 int level_ysize = level.native_mm_level->fieldy;
369 int full_ysize = level_ysize * TILESIZE_VAR;
371 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
374 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
379 int getLevelFromScreenX(int x)
381 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
382 return getLevelFromScreenX_EM(x);
383 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
384 return getLevelFromScreenX_SP(x);
385 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
386 return getLevelFromScreenX_MM(x);
388 return getLevelFromScreenX_RND(x);
391 int getLevelFromScreenY(int y)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenY_EM(y);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenY_SP(y);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenY_MM(y);
400 return getLevelFromScreenY_RND(y);
403 int getScreenFieldSizeX(void)
405 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
408 int getScreenFieldSizeY(void)
410 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
413 void DumpTile(int x, int y)
420 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
423 if (!IN_LEV_FIELD(x, y))
425 Info("(not in level field)");
431 token_name = element_info[Tile[x][y]].token_name;
433 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
434 Info("Back: %s", print_if_not_empty(Back[x][y]));
435 Info("Store: %s", print_if_not_empty(Store[x][y]));
436 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
437 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
438 Info("MovPos: %d", MovPos[x][y]);
439 Info("MovDir: %d", MovDir[x][y]);
440 Info("MovDelay: %d", MovDelay[x][y]);
441 Info("ChangeDelay: %d", ChangeDelay[x][y]);
442 Info("CustomValue: %d", CustomValue[x][y]);
443 Info("GfxElement: %d", GfxElement[x][y]);
444 Info("GfxAction: %d", GfxAction[x][y]);
445 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
446 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 int GetDrawtoField(void)
486 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
489 static void RedrawPlayfield_RND(void)
491 if (game.envelope_active)
494 DrawLevel(REDRAW_ALL);
498 void RedrawPlayfield(void)
500 if (game_status != GAME_MODE_PLAYING)
503 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
504 RedrawPlayfield_EM(TRUE);
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
506 RedrawPlayfield_SP(TRUE);
507 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
508 RedrawPlayfield_MM();
509 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
510 RedrawPlayfield_RND();
512 BlitScreenToBitmap(backbuffer);
514 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
518 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
521 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
522 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
524 // may happen for "border.draw_masked.*" with undefined "global.border.*"
525 if (src_bitmap == NULL)
528 if (x == -1 && y == -1)
531 if (draw_target == DRAW_TO_SCREEN)
532 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
534 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
537 static void DrawMaskedBorderExt_FIELD(int draw_target)
539 if (global.border_status >= GAME_MODE_MAIN &&
540 global.border_status <= GAME_MODE_PLAYING &&
541 border.draw_masked[global.border_status])
542 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
546 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_1))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 (global.border_status != GAME_MODE_EDITOR ||
555 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
556 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
559 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
561 // when drawing to backbuffer, never draw border over open doors
562 if (draw_target == DRAW_TO_BACKBUFFER &&
563 (GetDoorState() & DOOR_OPEN_2))
566 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
567 global.border_status != GAME_MODE_EDITOR)
568 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
571 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
573 // currently not available
576 static void DrawMaskedBorderExt_ALL(int draw_target)
578 DrawMaskedBorderExt_FIELD(draw_target);
579 DrawMaskedBorderExt_DOOR_1(draw_target);
580 DrawMaskedBorderExt_DOOR_2(draw_target);
581 DrawMaskedBorderExt_DOOR_3(draw_target);
584 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
586 // never draw masked screen borders on borderless screens
587 if (global.border_status == GAME_MODE_LOADING ||
588 global.border_status == GAME_MODE_TITLE)
591 if (redraw_mask & REDRAW_ALL)
592 DrawMaskedBorderExt_ALL(draw_target);
595 if (redraw_mask & REDRAW_FIELD)
596 DrawMaskedBorderExt_FIELD(draw_target);
597 if (redraw_mask & REDRAW_DOOR_1)
598 DrawMaskedBorderExt_DOOR_1(draw_target);
599 if (redraw_mask & REDRAW_DOOR_2)
600 DrawMaskedBorderExt_DOOR_2(draw_target);
601 if (redraw_mask & REDRAW_DOOR_3)
602 DrawMaskedBorderExt_DOOR_3(draw_target);
606 void DrawMaskedBorder_FIELD(void)
608 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
611 void DrawMaskedBorder(int redraw_mask)
613 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
616 void DrawMaskedBorderToTarget(int draw_target)
618 if (draw_target == DRAW_TO_BACKBUFFER ||
619 draw_target == DRAW_TO_SCREEN)
621 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
625 int last_border_status = global.border_status;
627 if (draw_target == DRAW_TO_FADE_SOURCE)
629 global.border_status = gfx.fade_border_source_status;
630 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
632 else if (draw_target == DRAW_TO_FADE_TARGET)
634 global.border_status = gfx.fade_border_target_status;
635 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
638 // always use global border for PLAYING when restarting the game
639 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
640 global.border_status = GAME_MODE_PLAYING;
642 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
644 global.border_status = last_border_status;
645 gfx.masked_border_bitmap_ptr = backbuffer;
649 void DrawTileCursor(int draw_target, int drawing_stage)
651 int tile_cursor_active = (game_status == GAME_MODE_PLAYING);
653 DrawTileCursor_MM(draw_target, drawing_stage, tile_cursor_active);
656 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
658 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
661 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
663 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
664 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
666 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
669 void BlitScreenToBitmap(Bitmap *target_bitmap)
671 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
672 BlitScreenToBitmap_EM(target_bitmap);
673 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
674 BlitScreenToBitmap_SP(target_bitmap);
675 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
676 BlitScreenToBitmap_MM(target_bitmap);
677 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
678 BlitScreenToBitmap_RND(target_bitmap);
680 redraw_mask |= REDRAW_FIELD;
683 static void DrawFramesPerSecond(void)
686 int font_nr = FONT_TEXT_2;
687 int font_width = getFontWidth(font_nr);
688 int draw_deactivation_mask = GetDrawDeactivationMask();
689 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
691 // draw FPS with leading space (needed if field buffer deactivated)
692 sprintf(text, " %04.1f fps", global.frames_per_second);
694 // override draw deactivation mask (required for invisible warp mode)
695 SetDrawDeactivationMask(REDRAW_NONE);
697 // draw opaque FPS if field buffer deactivated, else draw masked FPS
698 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
699 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
701 // set draw deactivation mask to previous value
702 SetDrawDeactivationMask(draw_deactivation_mask);
704 // force full-screen redraw in this frame
705 redraw_mask = REDRAW_ALL;
709 static void PrintFrameTimeDebugging(void)
711 static unsigned int last_counter = 0;
712 unsigned int counter = Counter();
713 int diff_1 = counter - last_counter;
714 int diff_2 = diff_1 - GAME_FRAME_DELAY;
716 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
717 char diff_bar[2 * diff_2_max + 5];
721 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
723 for (i = 0; i < diff_2_max; i++)
724 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
725 i >= diff_2_max - diff_2_cut ? '-' : ' ');
727 diff_bar[pos++] = '|';
729 for (i = 0; i < diff_2_max; i++)
730 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
732 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
734 diff_bar[pos++] = '\0';
736 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
739 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
742 last_counter = counter;
746 static int unifiedRedrawMask(int mask)
748 if (mask & REDRAW_ALL)
751 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
757 static boolean equalRedrawMasks(int mask_1, int mask_2)
759 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
762 void BackToFront(void)
764 static int last_redraw_mask = REDRAW_NONE;
766 // force screen redraw in every frame to continue drawing global animations
767 // (but always use the last redraw mask to prevent unwanted side effects)
768 if (redraw_mask == REDRAW_NONE)
769 redraw_mask = last_redraw_mask;
771 last_redraw_mask = redraw_mask;
774 // masked border now drawn immediately when blitting backbuffer to window
776 // draw masked border to all viewports, if defined
777 DrawMaskedBorder(redraw_mask);
780 // draw frames per second (only if debug mode is enabled)
781 if (redraw_mask & REDRAW_FPS)
782 DrawFramesPerSecond();
784 // remove playfield redraw before potentially merging with doors redraw
785 if (DrawingDeactivated(REAL_SX, REAL_SY))
786 redraw_mask &= ~REDRAW_FIELD;
788 // redraw complete window if both playfield and (some) doors need redraw
789 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
790 redraw_mask = REDRAW_ALL;
792 /* although redrawing the whole window would be fine for normal gameplay,
793 being able to only redraw the playfield is required for deactivating
794 certain drawing areas (mainly playfield) to work, which is needed for
795 warp-forward to be fast enough (by skipping redraw of most frames) */
797 if (redraw_mask & REDRAW_ALL)
799 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
801 else if (redraw_mask & REDRAW_FIELD)
803 BlitBitmap(backbuffer, window,
804 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
806 else if (redraw_mask & REDRAW_DOORS)
808 // merge door areas to prevent calling screen redraw more than once
814 if (redraw_mask & REDRAW_DOOR_1)
818 x2 = MAX(x2, DX + DXSIZE);
819 y2 = MAX(y2, DY + DYSIZE);
822 if (redraw_mask & REDRAW_DOOR_2)
826 x2 = MAX(x2, VX + VXSIZE);
827 y2 = MAX(y2, VY + VYSIZE);
830 if (redraw_mask & REDRAW_DOOR_3)
834 x2 = MAX(x2, EX + EXSIZE);
835 y2 = MAX(y2, EY + EYSIZE);
838 // make sure that at least one pixel is blitted, and inside the screen
839 // (else nothing is blitted, causing the animations not to be updated)
840 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
841 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
842 x2 = MIN(MAX(1, x2), WIN_XSIZE);
843 y2 = MIN(MAX(1, y2), WIN_YSIZE);
845 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
848 redraw_mask = REDRAW_NONE;
851 PrintFrameTimeDebugging();
855 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
857 unsigned int frame_delay_value_old = GetVideoFrameDelay();
859 SetVideoFrameDelay(frame_delay_value);
863 SetVideoFrameDelay(frame_delay_value_old);
866 static int fade_type_skip = FADE_TYPE_NONE;
868 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
870 void (*draw_border_function)(void) = NULL;
871 int x, y, width, height;
872 int fade_delay, post_delay;
874 if (fade_type == FADE_TYPE_FADE_OUT)
876 if (fade_type_skip != FADE_TYPE_NONE)
878 // skip all fade operations until specified fade operation
879 if (fade_type & fade_type_skip)
880 fade_type_skip = FADE_TYPE_NONE;
885 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
889 redraw_mask |= fade_mask;
891 if (fade_type == FADE_TYPE_SKIP)
893 fade_type_skip = fade_mode;
898 fade_delay = fading.fade_delay;
899 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
901 if (fade_type_skip != FADE_TYPE_NONE)
903 // skip all fade operations until specified fade operation
904 if (fade_type & fade_type_skip)
905 fade_type_skip = FADE_TYPE_NONE;
910 if (global.autoplay_leveldir)
915 if (fade_mask == REDRAW_FIELD)
920 height = FADE_SYSIZE;
922 if (border.draw_masked_when_fading)
923 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
925 DrawMaskedBorder_FIELD(); // draw once
935 // when switching screens without fading, set fade delay to zero
936 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
939 // do not display black frame when fading out without fade delay
940 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
943 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
944 draw_border_function);
946 redraw_mask &= ~fade_mask;
948 ClearAutoRepeatKeyEvents();
951 static void SetScreenStates_BeforeFadingIn(void)
953 // temporarily set screen mode for animations to screen after fading in
954 global.anim_status = global.anim_status_next;
956 // store backbuffer with all animations that will be started after fading in
957 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
959 // set screen mode for animations back to fading
960 global.anim_status = GAME_MODE_PSEUDO_FADING;
963 static void SetScreenStates_AfterFadingIn(void)
965 // store new source screen (to use correct masked border for fading)
966 gfx.fade_border_source_status = global.border_status;
968 global.anim_status = global.anim_status_next;
971 static void SetScreenStates_BeforeFadingOut(void)
973 // store new target screen (to use correct masked border for fading)
974 gfx.fade_border_target_status = game_status;
976 // set screen mode for animations to fading
977 global.anim_status = GAME_MODE_PSEUDO_FADING;
979 // store backbuffer with all animations that will be stopped for fading out
980 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
983 static void SetScreenStates_AfterFadingOut(void)
985 global.border_status = game_status;
987 // always use global border for PLAYING when restarting the game
988 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
989 global.border_status = GAME_MODE_PLAYING;
992 void FadeIn(int fade_mask)
994 SetScreenStates_BeforeFadingIn();
997 DrawMaskedBorder(REDRAW_ALL);
1000 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1001 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1003 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1007 FADE_SXSIZE = FULL_SXSIZE;
1008 FADE_SYSIZE = FULL_SYSIZE;
1010 // activate virtual buttons depending on upcoming game status
1011 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1012 game_status == GAME_MODE_PLAYING && !tape.playing)
1013 SetOverlayActive(TRUE);
1015 SetScreenStates_AfterFadingIn();
1017 // force update of global animation status in case of rapid screen changes
1018 redraw_mask = REDRAW_ALL;
1022 void FadeOut(int fade_mask)
1024 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1025 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1026 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1029 SetScreenStates_BeforeFadingOut();
1031 SetTileCursorActive(FALSE);
1032 SetOverlayActive(FALSE);
1035 DrawMaskedBorder(REDRAW_ALL);
1038 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1039 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1041 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1043 SetScreenStates_AfterFadingOut();
1046 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1048 static struct TitleFadingInfo fading_leave_stored;
1051 fading_leave_stored = fading_leave;
1053 fading = fading_leave_stored;
1056 void FadeSetEnterMenu(void)
1058 fading = menu.enter_menu;
1060 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1063 void FadeSetLeaveMenu(void)
1065 fading = menu.leave_menu;
1067 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1070 void FadeSetEnterScreen(void)
1072 fading = menu.enter_screen[game_status];
1074 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1077 void FadeSetNextScreen(void)
1079 fading = menu.next_screen[game_status];
1081 // (do not overwrite fade mode set by FadeSetEnterScreen)
1082 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1085 void FadeSetLeaveScreen(void)
1087 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1090 void FadeSetFromType(int type)
1092 if (type & TYPE_ENTER_SCREEN)
1093 FadeSetEnterScreen();
1094 else if (type & TYPE_ENTER)
1096 else if (type & TYPE_LEAVE)
1100 void FadeSetDisabled(void)
1102 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1104 fading = fading_none;
1107 void FadeSkipNextFadeIn(void)
1109 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1112 void FadeSkipNextFadeOut(void)
1114 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1117 static int getGlobalGameStatus(int status)
1119 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1120 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1124 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1126 if (graphic == IMG_UNDEFINED)
1127 return IMG_UNDEFINED;
1129 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1131 return (graphic_info[graphic].bitmap != NULL || redefined ?
1132 graphic : default_graphic);
1135 static int getBackgroundImage(int graphic)
1137 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1140 static int getGlobalBorderImage(int graphic)
1142 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1145 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1147 int status = getGlobalGameStatus(status_raw);
1149 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1150 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1151 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1152 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1154 int graphic_final = getGlobalBorderImage(graphic);
1156 return graphic_info[graphic_final].bitmap;
1159 void SetBackgroundImage(int graphic, int redraw_mask)
1161 struct GraphicInfo *g = &graphic_info[graphic];
1162 struct GraphicInfo g_undefined = { 0 };
1164 if (graphic == IMG_UNDEFINED)
1167 // always use original size bitmap for backgrounds, if existing
1168 Bitmap *bitmap = (g->bitmaps != NULL &&
1169 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1170 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1172 // remove every mask before setting mask for window, and
1173 // remove window area mask before setting mask for main or door area
1174 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1176 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1177 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1178 SetBackgroundBitmap(bitmap, redraw_mask,
1180 g->width, g->height);
1183 void SetWindowBackgroundImageIfDefined(int graphic)
1185 if (graphic_info[graphic].bitmap)
1186 SetBackgroundImage(graphic, REDRAW_ALL);
1189 void SetMainBackgroundImageIfDefined(int graphic)
1191 if (graphic_info[graphic].bitmap)
1192 SetBackgroundImage(graphic, REDRAW_FIELD);
1195 void SetDoorBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1201 void SetWindowBackgroundImage(int graphic)
1203 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1206 void SetMainBackgroundImage(int graphic)
1208 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1211 void SetDoorBackgroundImage(int graphic)
1213 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1216 void SetPanelBackground(void)
1218 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1221 void DrawBackground(int x, int y, int width, int height)
1223 // "drawto" might still point to playfield buffer here (hall of fame)
1224 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1226 if (IN_GFX_FIELD_FULL(x, y))
1227 redraw_mask |= REDRAW_FIELD;
1228 else if (IN_GFX_DOOR_1(x, y))
1229 redraw_mask |= REDRAW_DOOR_1;
1230 else if (IN_GFX_DOOR_2(x, y))
1231 redraw_mask |= REDRAW_DOOR_2;
1232 else if (IN_GFX_DOOR_3(x, y))
1233 redraw_mask |= REDRAW_DOOR_3;
1236 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1238 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1240 if (font->bitmap == NULL)
1243 DrawBackground(x, y, width, height);
1246 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1248 struct GraphicInfo *g = &graphic_info[graphic];
1250 if (g->bitmap == NULL)
1253 DrawBackground(x, y, width, height);
1256 static int game_status_last = -1;
1257 static Bitmap *global_border_bitmap_last = NULL;
1258 static Bitmap *global_border_bitmap = NULL;
1259 static int real_sx_last = -1, real_sy_last = -1;
1260 static int full_sxsize_last = -1, full_sysize_last = -1;
1261 static int dx_last = -1, dy_last = -1;
1262 static int dxsize_last = -1, dysize_last = -1;
1263 static int vx_last = -1, vy_last = -1;
1264 static int vxsize_last = -1, vysize_last = -1;
1265 static int ex_last = -1, ey_last = -1;
1266 static int exsize_last = -1, eysize_last = -1;
1268 boolean CheckIfGlobalBorderHasChanged(void)
1270 // if game status has not changed, global border has not changed either
1271 if (game_status == game_status_last)
1274 // determine and store new global border bitmap for current game status
1275 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1277 return (global_border_bitmap_last != global_border_bitmap);
1280 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1282 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1283 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1285 // if game status has not changed, nothing has to be redrawn
1286 if (game_status == game_status_last)
1289 // redraw if last screen was title screen
1290 if (game_status_last == GAME_MODE_TITLE)
1293 // redraw if global screen border has changed
1294 if (CheckIfGlobalBorderHasChanged())
1297 // redraw if position or size of playfield area has changed
1298 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1299 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1302 // redraw if position or size of door area has changed
1303 if (dx_last != DX || dy_last != DY ||
1304 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1307 // redraw if position or size of tape area has changed
1308 if (vx_last != VX || vy_last != VY ||
1309 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1312 // redraw if position or size of editor area has changed
1313 if (ex_last != EX || ey_last != EY ||
1314 exsize_last != EXSIZE || eysize_last != EYSIZE)
1321 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1324 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1326 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1329 void RedrawGlobalBorder(void)
1331 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1333 RedrawGlobalBorderFromBitmap(bitmap);
1335 redraw_mask = REDRAW_ALL;
1338 static void RedrawGlobalBorderIfNeeded(void)
1340 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1341 if (game_status == game_status_last)
1345 // copy current draw buffer to later copy back areas that have not changed
1346 if (game_status_last != GAME_MODE_TITLE)
1347 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1349 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1350 if (CheckIfGlobalBorderRedrawIsNeeded())
1352 // determine and store new global border bitmap for current game status
1353 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1356 // redraw global screen border (or clear, if defined to be empty)
1357 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1359 if (game_status == GAME_MODE_EDITOR)
1360 DrawSpecialEditorDoor();
1362 // copy previous playfield and door areas, if they are defined on both
1363 // previous and current screen and if they still have the same size
1365 if (real_sx_last != -1 && real_sy_last != -1 &&
1366 REAL_SX != -1 && REAL_SY != -1 &&
1367 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1368 BlitBitmap(bitmap_db_store_1, backbuffer,
1369 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1372 if (dx_last != -1 && dy_last != -1 &&
1373 DX != -1 && DY != -1 &&
1374 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1378 if (game_status != GAME_MODE_EDITOR)
1380 if (vx_last != -1 && vy_last != -1 &&
1381 VX != -1 && VY != -1 &&
1382 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1388 if (ex_last != -1 && ey_last != -1 &&
1389 EX != -1 && EY != -1 &&
1390 exsize_last == EXSIZE && eysize_last == EYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1395 redraw_mask = REDRAW_ALL;
1398 game_status_last = game_status;
1400 global_border_bitmap_last = global_border_bitmap;
1402 real_sx_last = REAL_SX;
1403 real_sy_last = REAL_SY;
1404 full_sxsize_last = FULL_SXSIZE;
1405 full_sysize_last = FULL_SYSIZE;
1408 dxsize_last = DXSIZE;
1409 dysize_last = DYSIZE;
1412 vxsize_last = VXSIZE;
1413 vysize_last = VYSIZE;
1416 exsize_last = EXSIZE;
1417 eysize_last = EYSIZE;
1420 void ClearField(void)
1422 RedrawGlobalBorderIfNeeded();
1424 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1425 // (when entering hall of fame after playing)
1426 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1428 // !!! maybe this should be done before clearing the background !!!
1429 if (game_status == GAME_MODE_PLAYING)
1431 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1432 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1436 SetDrawtoField(DRAW_TO_BACKBUFFER);
1440 void MarkTileDirty(int x, int y)
1442 redraw_mask |= REDRAW_FIELD;
1445 void SetBorderElement(void)
1449 BorderElement = EL_EMPTY;
1451 // only the R'n'D game engine may use an additional steelwall border
1452 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1455 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1457 for (x = 0; x < lev_fieldx; x++)
1459 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1460 BorderElement = EL_STEELWALL;
1462 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1468 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1469 int max_array_fieldx, int max_array_fieldy,
1470 short field[max_array_fieldx][max_array_fieldy],
1471 int max_fieldx, int max_fieldy)
1473 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1474 struct XY *check = xy_topdown;
1475 int old_element = field[start_x][start_y];
1478 // do nothing if start field already has the desired content
1479 if (old_element == fill_element)
1482 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1484 while (stack_pos > 0)
1486 struct XY current = stack_buffer[--stack_pos];
1489 field[current.x][current.y] = fill_element;
1491 for (i = 0; i < 4; i++)
1493 int x = current.x + check[i].x;
1494 int y = current.y + check[i].y;
1496 // check for stack buffer overflow (should not happen)
1497 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1498 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1500 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1501 stack_buffer[stack_pos++] = (struct XY){ x, y };
1506 void FloodFillLevel(int from_x, int from_y, int fill_element,
1507 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1508 int max_fieldx, int max_fieldy)
1510 FloodFillLevelExt(from_x, from_y, fill_element,
1511 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1512 max_fieldx, max_fieldy);
1515 void SetRandomAnimationValue(int x, int y)
1517 gfx.anim_random_frame = GfxRandom[x][y];
1520 int getGraphicAnimationFrame(int graphic, int sync_frame)
1522 // animation synchronized with global frame counter, not move position
1523 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1524 sync_frame = FrameCounter;
1525 else if (graphic_info[graphic].anim_global_anim_sync)
1526 sync_frame = getGlobalAnimSyncFrame();
1528 return getAnimationFrame(graphic_info[graphic].anim_frames,
1529 graphic_info[graphic].anim_delay,
1530 graphic_info[graphic].anim_mode,
1531 graphic_info[graphic].anim_start_frame,
1535 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1537 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1539 struct GraphicInfo *g = &graphic_info[graphic];
1540 int xsize = MAX(1, g->anim_frames_per_line);
1541 int ysize = MAX(1, g->anim_frames / xsize);
1542 int xoffset = g->anim_start_frame % xsize;
1543 int yoffset = g->anim_start_frame % ysize;
1544 // may be needed if screen field is significantly larger than playfield
1545 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1546 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1547 int sync_frame = y * xsize + x;
1549 return sync_frame % g->anim_frames;
1551 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1553 struct GraphicInfo *g = &graphic_info[graphic];
1554 // may be needed if screen field is significantly larger than playfield
1555 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1556 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1557 int sync_frame = GfxRandomStatic[x][y];
1559 return sync_frame % g->anim_frames;
1563 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1565 return getGraphicAnimationFrame(graphic, sync_frame);
1569 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1571 struct GraphicInfo *g = &graphic_info[graphic];
1572 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1574 if (tilesize == gfx.standard_tile_size)
1575 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1576 else if (tilesize == game.tile_size)
1577 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1579 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1582 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1583 boolean get_backside)
1585 struct GraphicInfo *g = &graphic_info[graphic];
1586 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1587 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1589 if (g->offset_y == 0) // frames are ordered horizontally
1591 int max_width = g->anim_frames_per_line * g->width;
1592 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1594 *x = pos % max_width;
1595 *y = src_y % g->height + pos / max_width * g->height;
1597 else if (g->offset_x == 0) // frames are ordered vertically
1599 int max_height = g->anim_frames_per_line * g->height;
1600 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1602 *x = src_x % g->width + pos / max_height * g->width;
1603 *y = pos % max_height;
1605 else // frames are ordered diagonally
1607 *x = src_x + frame * g->offset_x;
1608 *y = src_y + frame * g->offset_y;
1612 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1613 Bitmap **bitmap, int *x, int *y,
1614 boolean get_backside)
1616 struct GraphicInfo *g = &graphic_info[graphic];
1618 // if no graphics defined at all, use fallback graphics
1619 if (g->bitmaps == NULL)
1620 *g = graphic_info[IMG_CHAR_EXCLAM];
1622 // if no in-game graphics defined, always use standard graphic size
1623 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1624 tilesize = TILESIZE;
1626 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1627 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1629 *x = *x * tilesize / g->tile_size;
1630 *y = *y * tilesize / g->tile_size;
1633 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1634 Bitmap **bitmap, int *x, int *y)
1636 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1639 void getFixedGraphicSource(int graphic, int frame,
1640 Bitmap **bitmap, int *x, int *y)
1642 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1645 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1647 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1650 void getGlobalAnimGraphicSource(int graphic, int frame,
1651 Bitmap **bitmap, int *x, int *y)
1653 struct GraphicInfo *g = &graphic_info[graphic];
1655 // if no graphics defined at all, use fallback graphics
1656 if (g->bitmaps == NULL)
1657 *g = graphic_info[IMG_CHAR_EXCLAM];
1659 // use original size graphics, if existing, else use standard size graphics
1660 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1661 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1663 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1665 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1668 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1669 int *x, int *y, boolean get_backside)
1671 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1675 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1677 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1680 void DrawGraphic(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1686 Debug("draw:DrawGraphic", "This should never happen!");
1692 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1695 MarkTileDirty(x, y);
1698 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1701 if (!IN_SCR_FIELD(x, y))
1703 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1705 Debug("draw:DrawFixedGraphic", "This should never happen!");
1711 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1713 MarkTileDirty(x, y);
1716 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1722 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1724 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1727 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1733 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1734 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1737 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1740 if (!IN_SCR_FIELD(x, y))
1742 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1744 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1750 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1753 MarkTileDirty(x, y);
1756 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1759 if (!IN_SCR_FIELD(x, y))
1761 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1763 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1769 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1771 MarkTileDirty(x, y);
1774 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1780 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1782 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1786 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1787 int graphic, int frame)
1792 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1794 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1798 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1800 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1802 MarkTileDirty(x / tilesize, y / tilesize);
1805 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1808 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1809 graphic, frame, tilesize);
1810 MarkTileDirty(x / tilesize, y / tilesize);
1813 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1819 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1820 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1823 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1824 int frame, int tilesize)
1829 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1830 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1833 void DrawMiniGraphic(int x, int y, int graphic)
1835 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1836 MarkTileDirty(x / 2, y / 2);
1839 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1844 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1845 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1848 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1849 int graphic, int frame,
1850 int cut_mode, int mask_mode)
1855 int width = TILEX, height = TILEY;
1858 if (dx || dy) // shifted graphic
1860 if (x < BX1) // object enters playfield from the left
1867 else if (x > BX2) // object enters playfield from the right
1873 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1879 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1881 else if (dx) // general horizontal movement
1882 MarkTileDirty(x + SIGN(dx), y);
1884 if (y < BY1) // object enters playfield from the top
1886 if (cut_mode == CUT_BELOW) // object completely above top border
1894 else if (y > BY2) // object enters playfield from the bottom
1900 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1906 else if (dy > 0 && cut_mode == CUT_ABOVE)
1908 if (y == BY2) // object completely above bottom border
1914 MarkTileDirty(x, y + 1);
1915 } // object leaves playfield to the bottom
1916 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1918 else if (dy) // general vertical movement
1919 MarkTileDirty(x, y + SIGN(dy));
1923 if (!IN_SCR_FIELD(x, y))
1925 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1927 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1933 width = width * TILESIZE_VAR / TILESIZE;
1934 height = height * TILESIZE_VAR / TILESIZE;
1935 cx = cx * TILESIZE_VAR / TILESIZE;
1936 cy = cy * TILESIZE_VAR / TILESIZE;
1937 dx = dx * TILESIZE_VAR / TILESIZE;
1938 dy = dy * TILESIZE_VAR / TILESIZE;
1940 if (width > 0 && height > 0)
1942 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1947 dst_x = FX + x * TILEX_VAR + dx;
1948 dst_y = FY + y * TILEY_VAR + dy;
1950 if (mask_mode == USE_MASKING)
1951 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1957 MarkTileDirty(x, y);
1961 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1962 int graphic, int frame,
1963 int cut_mode, int mask_mode)
1968 int width = TILEX_VAR, height = TILEY_VAR;
1971 int x2 = x + SIGN(dx);
1972 int y2 = y + SIGN(dy);
1974 // movement with two-tile animations must be sync'ed with movement position,
1975 // not with current GfxFrame (which can be higher when using slow movement)
1976 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1977 int anim_frames = graphic_info[graphic].anim_frames;
1979 // (we also need anim_delay here for movement animations with less frames)
1980 int anim_delay = graphic_info[graphic].anim_delay;
1981 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1983 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1984 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1986 // re-calculate animation frame for two-tile movement animation
1987 frame = getGraphicAnimationFrame(graphic, sync_frame);
1989 // check if movement start graphic inside screen area and should be drawn
1990 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1992 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1994 dst_x = FX + x1 * TILEX_VAR;
1995 dst_y = FY + y1 * TILEY_VAR;
1997 if (mask_mode == USE_MASKING)
1998 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2001 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2004 MarkTileDirty(x1, y1);
2007 // check if movement end graphic inside screen area and should be drawn
2008 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2010 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2012 dst_x = FX + x2 * TILEX_VAR;
2013 dst_y = FY + y2 * TILEY_VAR;
2015 if (mask_mode == USE_MASKING)
2016 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2019 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2022 MarkTileDirty(x2, y2);
2026 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2027 int graphic, int frame,
2028 int cut_mode, int mask_mode)
2032 DrawGraphic(x, y, graphic, frame);
2037 if (graphic_info[graphic].double_movement) // EM style movement images
2038 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2043 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2044 int graphic, int frame, int cut_mode)
2046 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2049 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2050 int cut_mode, int mask_mode)
2052 int lx = LEVELX(x), ly = LEVELY(y);
2056 if (IN_LEV_FIELD(lx, ly))
2058 if (element == EL_EMPTY)
2059 element = GfxElementEmpty[lx][ly];
2061 SetRandomAnimationValue(lx, ly);
2063 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2064 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2066 // do not use double (EM style) movement graphic when not moving
2067 if (graphic_info[graphic].double_movement && !dx && !dy)
2069 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2070 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2073 if (game.use_masked_elements && (dx || dy))
2074 mask_mode = USE_MASKING;
2076 else // border element
2078 graphic = el2img(element);
2079 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2082 if (element == EL_EXPANDABLE_WALL)
2084 boolean left_stopped = FALSE, right_stopped = FALSE;
2086 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2087 left_stopped = TRUE;
2088 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2089 right_stopped = TRUE;
2091 if (left_stopped && right_stopped)
2093 else if (left_stopped)
2095 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2096 frame = graphic_info[graphic].anim_frames - 1;
2098 else if (right_stopped)
2100 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2101 frame = graphic_info[graphic].anim_frames - 1;
2106 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2107 else if (mask_mode == USE_MASKING)
2108 DrawGraphicThruMask(x, y, graphic, frame);
2110 DrawGraphic(x, y, graphic, frame);
2113 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2114 int cut_mode, int mask_mode)
2116 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2117 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2118 cut_mode, mask_mode);
2121 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2124 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2127 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2130 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2133 void DrawLevelElementThruMask(int x, int y, int element)
2135 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2138 void DrawLevelFieldThruMask(int x, int y)
2140 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2143 // !!! implementation of quicksand is totally broken !!!
2144 #define IS_CRUMBLED_TILE(x, y, e) \
2145 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2146 !IS_MOVING(x, y) || \
2147 (e) == EL_QUICKSAND_EMPTYING || \
2148 (e) == EL_QUICKSAND_FAST_EMPTYING))
2150 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2155 int width, height, cx, cy;
2156 int sx = SCREENX(x), sy = SCREENY(y);
2157 int crumbled_border_size = graphic_info[graphic].border_size;
2158 int crumbled_tile_size = graphic_info[graphic].tile_size;
2159 int crumbled_border_size_var =
2160 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2163 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2165 for (i = 1; i < 4; i++)
2167 int dxx = (i & 1 ? dx : 0);
2168 int dyy = (i & 2 ? dy : 0);
2171 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2174 // check if neighbour field is of same crumble type
2175 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2176 graphic_info[graphic].class ==
2177 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2179 // return if check prevents inner corner
2180 if (same == (dxx == dx && dyy == dy))
2184 // if we reach this point, we have an inner corner
2186 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2188 width = crumbled_border_size_var;
2189 height = crumbled_border_size_var;
2190 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2191 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2193 if (game.use_masked_elements)
2195 int graphic0 = el2img(EL_EMPTY);
2196 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2197 Bitmap *src_bitmap0;
2200 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2202 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2204 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2206 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2208 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2211 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2213 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2216 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2221 int width, height, bx, by, cx, cy;
2222 int sx = SCREENX(x), sy = SCREENY(y);
2223 int crumbled_border_size = graphic_info[graphic].border_size;
2224 int crumbled_tile_size = graphic_info[graphic].tile_size;
2225 int crumbled_border_size_var =
2226 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2227 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2230 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2232 // only needed when using masked elements
2233 int graphic0 = el2img(EL_EMPTY);
2234 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2235 Bitmap *src_bitmap0;
2238 if (game.use_masked_elements)
2239 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2241 // draw simple, sloppy, non-corner-accurate crumbled border
2243 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2244 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2245 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2246 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2248 if (game.use_masked_elements)
2250 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2252 FX + sx * TILEX_VAR + cx,
2253 FY + sy * TILEY_VAR + cy);
2255 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2257 FX + sx * TILEX_VAR + cx,
2258 FY + sy * TILEY_VAR + cy);
2261 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2263 FX + sx * TILEX_VAR + cx,
2264 FY + sy * TILEY_VAR + cy);
2266 // (remaining middle border part must be at least as big as corner part)
2267 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2268 crumbled_border_size_var >= TILESIZE_VAR / 3)
2271 // correct corners of crumbled border, if needed
2273 for (i = -1; i <= 1; i += 2)
2275 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2276 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2277 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2280 // check if neighbour field is of same crumble type
2281 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2282 graphic_info[graphic].class ==
2283 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2285 // no crumbled corner, but continued crumbled border
2287 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2288 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2289 int b1 = (i == 1 ? crumbled_border_size_var :
2290 TILESIZE_VAR - 2 * crumbled_border_size_var);
2292 width = crumbled_border_size_var;
2293 height = crumbled_border_size_var;
2295 if (dir == 1 || dir == 2)
2310 if (game.use_masked_elements)
2312 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2314 FX + sx * TILEX_VAR + cx,
2315 FY + sy * TILEY_VAR + cy);
2317 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2319 FX + sx * TILEX_VAR + cx,
2320 FY + sy * TILEY_VAR + cy);
2323 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2325 FX + sx * TILEX_VAR + cx,
2326 FY + sy * TILEY_VAR + cy);
2331 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2333 int sx = SCREENX(x), sy = SCREENY(y);
2336 struct XY *xy = xy_topdown;
2338 if (!IN_LEV_FIELD(x, y))
2341 element = TILE_GFX_ELEMENT(x, y);
2343 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2345 if (!IN_SCR_FIELD(sx, sy))
2348 // crumble field borders towards direct neighbour fields
2349 for (i = 0; i < 4; i++)
2351 int xx = x + xy[i].x;
2352 int yy = y + xy[i].y;
2354 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2357 // check if neighbour field is of same crumble type
2358 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2359 graphic_info[graphic].class ==
2360 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2363 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2366 // crumble inner field corners towards corner neighbour fields
2367 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2368 graphic_info[graphic].anim_frames == 2)
2370 for (i = 0; i < 4; i++)
2372 int dx = (i & 1 ? +1 : -1);
2373 int dy = (i & 2 ? +1 : -1);
2375 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2379 MarkTileDirty(sx, sy);
2381 else // center field is not crumbled -- crumble neighbour fields
2383 // crumble field borders of direct neighbour fields
2384 for (i = 0; i < 4; i++)
2386 int xx = x + xy[i].x;
2387 int yy = y + xy[i].y;
2388 int sxx = sx + xy[i].x;
2389 int syy = sy + xy[i].y;
2391 if (!IN_LEV_FIELD(xx, yy) ||
2392 !IN_SCR_FIELD(sxx, syy))
2395 // do not crumble fields that are being digged or snapped
2396 if (Tile[xx][yy] == EL_EMPTY ||
2397 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2400 element = TILE_GFX_ELEMENT(xx, yy);
2402 if (!IS_CRUMBLED_TILE(xx, yy, element))
2405 graphic = el_act2crm(element, ACTION_DEFAULT);
2407 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2409 MarkTileDirty(sxx, syy);
2412 // crumble inner field corners of corner neighbour fields
2413 for (i = 0; i < 4; i++)
2415 int dx = (i & 1 ? +1 : -1);
2416 int dy = (i & 2 ? +1 : -1);
2422 if (!IN_LEV_FIELD(xx, yy) ||
2423 !IN_SCR_FIELD(sxx, syy))
2426 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2429 element = TILE_GFX_ELEMENT(xx, yy);
2431 if (!IS_CRUMBLED_TILE(xx, yy, element))
2434 graphic = el_act2crm(element, ACTION_DEFAULT);
2436 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2437 graphic_info[graphic].anim_frames == 2)
2438 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2440 MarkTileDirty(sxx, syy);
2445 void DrawLevelFieldCrumbled(int x, int y)
2449 if (!IN_LEV_FIELD(x, y))
2452 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2453 GfxElement[x][y] != EL_UNDEFINED &&
2454 GFX_CRUMBLED(GfxElement[x][y]))
2456 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2461 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2463 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2466 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2469 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2470 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2471 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2472 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2473 int sx = SCREENX(x), sy = SCREENY(y);
2475 DrawScreenGraphic(sx, sy, graphic1, frame1);
2476 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2479 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2481 int sx = SCREENX(x), sy = SCREENY(y);
2482 struct XY *xy = xy_topdown;
2485 // crumble direct neighbour fields (required for field borders)
2486 for (i = 0; i < 4; i++)
2488 int xx = x + xy[i].x;
2489 int yy = y + xy[i].y;
2490 int sxx = sx + xy[i].x;
2491 int syy = sy + xy[i].y;
2493 if (!IN_LEV_FIELD(xx, yy) ||
2494 !IN_SCR_FIELD(sxx, syy) ||
2495 !GFX_CRUMBLED(Tile[xx][yy]) ||
2499 DrawLevelField(xx, yy);
2502 // crumble corner neighbour fields (required for inner field corners)
2503 for (i = 0; i < 4; i++)
2505 int dx = (i & 1 ? +1 : -1);
2506 int dy = (i & 2 ? +1 : -1);
2512 if (!IN_LEV_FIELD(xx, yy) ||
2513 !IN_SCR_FIELD(sxx, syy) ||
2514 !GFX_CRUMBLED(Tile[xx][yy]) ||
2518 int element = TILE_GFX_ELEMENT(xx, yy);
2519 int graphic = el_act2crm(element, ACTION_DEFAULT);
2521 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2522 graphic_info[graphic].anim_frames == 2)
2523 DrawLevelField(xx, yy);
2527 static int getBorderElement(int x, int y)
2531 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2532 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2533 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2534 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2535 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2536 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2537 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2539 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2540 int steel_position = (x == -1 && y == -1 ? 0 :
2541 x == lev_fieldx && y == -1 ? 1 :
2542 x == -1 && y == lev_fieldy ? 2 :
2543 x == lev_fieldx && y == lev_fieldy ? 3 :
2544 x == -1 || x == lev_fieldx ? 4 :
2545 y == -1 || y == lev_fieldy ? 5 : 6);
2547 return border[steel_position][steel_type];
2550 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2552 if (game.use_masked_elements)
2554 if (graphic != el2img(EL_EMPTY))
2555 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2557 DrawGraphicThruMask(x, y, graphic, frame);
2561 DrawGraphic(x, y, graphic, frame);
2565 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2567 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2570 void DrawScreenElement(int x, int y, int element)
2572 int mask_mode = NO_MASKING;
2574 if (game.use_masked_elements)
2576 int lx = LEVELX(x), ly = LEVELY(y);
2578 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2580 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2582 mask_mode = USE_MASKING;
2586 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2587 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2590 void DrawLevelElement(int x, int y, int element)
2592 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2593 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2596 void DrawScreenField(int x, int y)
2598 int lx = LEVELX(x), ly = LEVELY(y);
2599 int element, content;
2601 if (!IN_LEV_FIELD(lx, ly))
2603 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2606 element = getBorderElement(lx, ly);
2608 DrawScreenElement(x, y, element);
2613 element = Tile[lx][ly];
2614 content = Store[lx][ly];
2616 if (IS_MOVING(lx, ly))
2618 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2619 boolean cut_mode = NO_CUTTING;
2621 if (element == EL_QUICKSAND_EMPTYING ||
2622 element == EL_QUICKSAND_FAST_EMPTYING ||
2623 element == EL_MAGIC_WALL_EMPTYING ||
2624 element == EL_BD_MAGIC_WALL_EMPTYING ||
2625 element == EL_DC_MAGIC_WALL_EMPTYING ||
2626 element == EL_AMOEBA_DROPPING)
2627 cut_mode = CUT_ABOVE;
2628 else if (element == EL_QUICKSAND_FILLING ||
2629 element == EL_QUICKSAND_FAST_FILLING ||
2630 element == EL_MAGIC_WALL_FILLING ||
2631 element == EL_BD_MAGIC_WALL_FILLING ||
2632 element == EL_DC_MAGIC_WALL_FILLING)
2633 cut_mode = CUT_BELOW;
2635 if (cut_mode == CUT_ABOVE)
2636 DrawScreenElement(x, y, element);
2638 DrawScreenElement(x, y, EL_EMPTY);
2640 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2642 int dir = MovDir[lx][ly];
2643 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2644 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2646 if (IN_SCR_FIELD(newx, newy))
2647 DrawScreenElement(newx, newy, EL_EMPTY);
2651 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2652 else if (cut_mode == NO_CUTTING)
2653 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2656 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2658 if (cut_mode == CUT_BELOW &&
2659 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2660 DrawLevelElement(lx, ly + 1, element);
2663 if (content == EL_ACID)
2665 int dir = MovDir[lx][ly];
2666 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2667 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2669 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2671 // prevent target field from being drawn again (but without masking)
2672 // (this would happen if target field is scanned after moving element)
2673 Stop[newlx][newly] = TRUE;
2676 else if (IS_BLOCKED(lx, ly))
2681 boolean cut_mode = NO_CUTTING;
2682 int element_old, content_old;
2684 Blocked2Moving(lx, ly, &oldx, &oldy);
2687 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2688 MovDir[oldx][oldy] == MV_RIGHT);
2690 element_old = Tile[oldx][oldy];
2691 content_old = Store[oldx][oldy];
2693 if (element_old == EL_QUICKSAND_EMPTYING ||
2694 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2695 element_old == EL_MAGIC_WALL_EMPTYING ||
2696 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2697 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2698 element_old == EL_AMOEBA_DROPPING)
2699 cut_mode = CUT_ABOVE;
2701 DrawScreenElement(x, y, EL_EMPTY);
2704 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2706 else if (cut_mode == NO_CUTTING)
2707 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2710 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2713 else if (IS_DRAWABLE(element))
2714 DrawScreenElement(x, y, element);
2716 DrawScreenElement(x, y, EL_EMPTY);
2719 void DrawLevelField(int x, int y)
2721 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2722 DrawScreenField(SCREENX(x), SCREENY(y));
2723 else if (IS_MOVING(x, y))
2727 Moving2Blocked(x, y, &newx, &newy);
2728 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2729 DrawScreenField(SCREENX(newx), SCREENY(newy));
2731 else if (IS_BLOCKED(x, y))
2735 Blocked2Moving(x, y, &oldx, &oldy);
2736 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2737 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2741 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2742 int (*el2img_function)(int), boolean masked,
2743 int element_bits_draw)
2745 int element_base = map_mm_wall_element(element);
2746 int element_bits = (IS_DF_WALL(element) ?
2747 element - EL_DF_WALL_START :
2748 IS_MM_WALL(element) ?
2749 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2750 int graphic = el2img_function(element_base);
2751 int tilesize_draw = tilesize / 2;
2756 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2758 for (i = 0; i < 4; i++)
2760 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2761 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2763 if (!(element_bits_draw & (1 << i)))
2766 if (element_bits & (1 << i))
2769 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2770 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2772 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2773 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2778 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2779 tilesize_draw, tilesize_draw);
2784 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2785 boolean masked, int element_bits_draw)
2787 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2788 element, tilesize, el2edimg, masked, element_bits_draw);
2791 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2792 int (*el2img_function)(int))
2794 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2798 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2801 if (IS_MM_WALL(element))
2803 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2804 element, tilesize, el2edimg, masked, 0x000f);
2808 int graphic = el2edimg(element);
2811 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2813 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2817 void DrawSizedElement(int x, int y, int element, int tilesize)
2819 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2822 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2824 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2827 void DrawMiniElement(int x, int y, int element)
2831 graphic = el2edimg(element);
2832 DrawMiniGraphic(x, y, graphic);
2835 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2838 int x = sx + scroll_x, y = sy + scroll_y;
2840 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2841 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2842 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2843 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2845 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2848 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2850 int x = sx + scroll_x, y = sy + scroll_y;
2852 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2853 DrawMiniElement(sx, sy, EL_EMPTY);
2854 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2855 DrawMiniElement(sx, sy, Tile[x][y]);
2857 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2860 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2861 int x, int y, int xsize, int ysize,
2862 int tile_width, int tile_height)
2866 int dst_x = startx + x * tile_width;
2867 int dst_y = starty + y * tile_height;
2868 int width = graphic_info[graphic].width;
2869 int height = graphic_info[graphic].height;
2870 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2871 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2872 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2873 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2874 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2875 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2876 boolean draw_masked = graphic_info[graphic].draw_masked;
2878 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2880 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2882 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2886 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2887 inner_sx + (x - 1) * tile_width % inner_width);
2888 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2889 inner_sy + (y - 1) * tile_height % inner_height);
2892 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2895 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2899 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2900 int x, int y, int xsize, int ysize,
2903 int font_width = getFontWidth(font_nr);
2904 int font_height = getFontHeight(font_nr);
2906 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2907 font_width, font_height);
2910 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2912 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2913 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2914 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2915 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2916 boolean no_delay = (tape.warp_forward);
2917 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2918 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2919 DelayCounter anim_delay = { anim_delay_value };
2920 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2921 int font_width = getFontWidth(font_nr);
2922 int font_height = getFontHeight(font_nr);
2923 int max_xsize = level.envelope[envelope_nr].xsize;
2924 int max_ysize = level.envelope[envelope_nr].ysize;
2925 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2926 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2927 int xend = max_xsize;
2928 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2929 int xstep = (xstart < xend ? 1 : 0);
2930 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2932 int end = MAX(xend - xstart, yend - ystart);
2935 for (i = start; i <= end; i++)
2937 int last_frame = end; // last frame of this "for" loop
2938 int x = xstart + i * xstep;
2939 int y = ystart + i * ystep;
2940 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2941 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2942 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2943 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2946 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2948 BlitScreenToBitmap(backbuffer);
2950 SetDrawtoField(DRAW_TO_BACKBUFFER);
2952 for (yy = 0; yy < ysize; yy++)
2953 for (xx = 0; xx < xsize; xx++)
2954 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2956 DrawTextArea(sx + font_width, sy + font_height,
2957 level.envelope[envelope_nr].text, font_nr, max_xsize,
2958 xsize - 2, ysize - 2, 0, mask_mode,
2959 level.envelope[envelope_nr].autowrap,
2960 level.envelope[envelope_nr].centered, FALSE);
2962 redraw_mask |= REDRAW_FIELD;
2965 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2968 ClearAutoRepeatKeyEvents();
2971 void ShowEnvelope(int envelope_nr)
2973 int element = EL_ENVELOPE_1 + envelope_nr;
2974 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2975 int sound_opening = element_info[element].sound[ACTION_OPENING];
2976 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2977 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2978 boolean no_delay = (tape.warp_forward);
2979 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2980 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2981 int anim_mode = graphic_info[graphic].anim_mode;
2982 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2983 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2984 boolean overlay_enabled = GetOverlayEnabled();
2986 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2988 SetOverlayEnabled(FALSE);
2991 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2993 if (anim_mode == ANIM_DEFAULT)
2994 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2996 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2999 Delay_WithScreenUpdates(wait_delay_value);
3001 WaitForEventToContinue();
3004 SetOverlayEnabled(overlay_enabled);
3006 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3008 if (anim_mode != ANIM_NONE)
3009 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3011 if (anim_mode == ANIM_DEFAULT)
3012 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3014 game.envelope_active = FALSE;
3016 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3018 redraw_mask |= REDRAW_FIELD;
3022 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3023 int xsize, int ysize)
3025 if (!global.use_envelope_request)
3028 if (request.bitmap == NULL ||
3029 xsize > request.xsize ||
3030 ysize > request.ysize)
3032 if (request.bitmap != NULL)
3033 FreeBitmap(request.bitmap);
3035 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3037 SDL_Surface *surface = request.bitmap->surface;
3039 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3040 Fail("SDLGetNativeSurface() failed");
3043 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3045 // create masked surface for request bitmap, if needed
3046 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3048 SDL_Surface *surface = request.bitmap->surface;
3049 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3051 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3052 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3053 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3056 SDLFreeBitmapTextures(request.bitmap);
3057 SDLCreateBitmapTextures(request.bitmap);
3059 ResetBitmapAlpha(request.bitmap);
3061 // set envelope request run-time values
3064 request.xsize = xsize;
3065 request.ysize = ysize;
3068 void DrawEnvelopeRequestToScreen(int drawing_target)
3070 if (global.use_envelope_request &&
3071 game.request_active &&
3072 drawing_target == DRAW_TO_SCREEN)
3074 struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST];
3076 SetBitmapAlphaNextBlit(request.bitmap, g->alpha);
3079 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3080 request.sx, request.sy);
3082 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3083 request.sx, request.sy);
3087 static void setRequestBasePosition(int *x, int *y)
3089 int sx_base, sy_base;
3091 if (request.x != -1)
3092 sx_base = request.x;
3093 else if (request.align == ALIGN_LEFT)
3095 else if (request.align == ALIGN_RIGHT)
3096 sx_base = SX + SXSIZE;
3098 sx_base = SX + SXSIZE / 2;
3100 if (request.y != -1)
3101 sy_base = request.y;
3102 else if (request.valign == VALIGN_TOP)
3104 else if (request.valign == VALIGN_BOTTOM)
3105 sy_base = SY + SYSIZE;
3107 sy_base = SY + SYSIZE / 2;
3113 static void setRequestPositionExt(int *x, int *y, int width, int height,
3114 boolean add_border_size)
3116 int border_size = request.border_size;
3117 int sx_base, sy_base;
3120 setRequestBasePosition(&sx_base, &sy_base);
3122 if (request.align == ALIGN_LEFT)
3124 else if (request.align == ALIGN_RIGHT)
3125 sx = sx_base - width;
3127 sx = sx_base - width / 2;
3129 if (request.valign == VALIGN_TOP)
3131 else if (request.valign == VALIGN_BOTTOM)
3132 sy = sy_base - height;
3134 sy = sy_base - height / 2;
3136 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3137 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3139 if (add_border_size)
3149 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3151 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3154 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3156 char *text_final = text;
3157 char *text_door_style = NULL;
3158 int graphic = IMG_BACKGROUND_REQUEST;
3159 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3160 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3161 int font_nr = FONT_REQUEST;
3162 int font_width = getFontWidth(font_nr);
3163 int font_height = getFontHeight(font_nr);
3164 int border_size = request.border_size;
3165 int line_spacing = request.line_spacing;
3166 int line_height = font_height + line_spacing;
3167 int max_text_width = request.width - 2 * border_size;
3168 int max_text_height = request.height - 2 * border_size;
3169 int line_length = max_text_width / font_width;
3170 int max_lines = max_text_height / line_height;
3171 int text_width = line_length * font_width;
3172 int sx_offset = border_size;
3173 int sy_offset = border_size;
3175 // force DOOR font inside door area
3176 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3178 if (request.centered)
3179 sx_offset = (request.width - text_width) / 2;
3181 if (request.wrap_single_words && !request.autowrap)
3183 char *src_text_ptr, *dst_text_ptr;
3185 if (maxWordLengthInRequestString(text) > line_length)
3187 font_nr = FONT_REQUEST_NARROW;
3188 font_width = getFontWidth(font_nr);
3189 line_length = max_text_width / font_width;
3192 text_door_style = checked_malloc(2 * strlen(text) + 1);
3194 src_text_ptr = text;
3195 dst_text_ptr = text_door_style;
3197 while (*src_text_ptr)
3199 if (*src_text_ptr == ' ' ||
3200 *src_text_ptr == '?' ||
3201 *src_text_ptr == '!')
3202 *dst_text_ptr++ = '\n';
3204 if (*src_text_ptr != ' ')
3205 *dst_text_ptr++ = *src_text_ptr;
3210 *dst_text_ptr = '\0';
3212 text_final = text_door_style;
3215 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3216 line_length, -1, max_lines, line_spacing, mask_mode,
3217 request.autowrap, request.centered, FALSE);
3219 if (text_door_style)
3220 free(text_door_style);
3225 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3227 DrawBuffer *drawto_last = drawto;
3228 int graphic = IMG_BACKGROUND_REQUEST;
3229 int width = request.width;
3230 int height = request.height;
3231 int tile_size = MAX(request.step_offset, 1);
3232 int x_steps = width / tile_size;
3233 int y_steps = height / tile_size;
3237 setRequestPosition(&sx, &sy, FALSE);
3239 // draw complete envelope request to temporary bitmap
3240 drawto = bitmap_db_store_1;
3242 ClearRectangle(drawto, sx, sy, width, height);
3244 for (y = 0; y < y_steps; y++)
3245 for (x = 0; x < x_steps; x++)
3246 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3247 x, y, x_steps, y_steps,
3248 tile_size, tile_size);
3250 // write text for request
3251 DrawEnvelopeRequestText(sx, sy, text);
3253 MapToolButtons(req_state);
3255 // restore pointer to drawing buffer
3256 drawto = drawto_last;
3258 // prepare complete envelope request from temporary bitmap
3259 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3262 static void AnimateEnvelopeRequest(int anim_mode, int action)
3264 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3265 int delay_value_normal = request.step_delay;
3266 int delay_value_fast = delay_value_normal / 2;
3267 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3268 boolean no_delay = (tape.warp_forward);
3269 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3270 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3271 DelayCounter anim_delay = { anim_delay_value };
3273 int tile_size = MAX(request.step_offset, 1);
3274 int max_xsize = request.width / tile_size;
3275 int max_ysize = request.height / tile_size;
3276 int max_xsize_inner = max_xsize - 2;
3277 int max_ysize_inner = max_ysize - 2;
3279 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3280 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3281 int xend = max_xsize_inner;
3282 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3283 int xstep = (xstart < xend ? 1 : 0);
3284 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3286 int end = MAX(xend - xstart, yend - ystart);
3289 if (setup.quick_doors)
3296 for (i = start; i <= end; i++)
3298 int last_frame = end; // last frame of this "for" loop
3299 int x = xstart + i * xstep;
3300 int y = ystart + i * ystep;
3301 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3302 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3303 int xsize_size_left = (xsize - 1) * tile_size;
3304 int ysize_size_top = (ysize - 1) * tile_size;
3305 int max_xsize_pos = (max_xsize - 1) * tile_size;
3306 int max_ysize_pos = (max_ysize - 1) * tile_size;
3307 int width = xsize * tile_size;
3308 int height = ysize * tile_size;
3314 HandleGameActions();
3316 setRequestPosition(&src_x, &src_y, FALSE);
3317 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3319 for (yy = 0; yy < 2; yy++)
3321 for (xx = 0; xx < 2; xx++)
3323 int src_xx = src_x + xx * max_xsize_pos;
3324 int src_yy = src_y + yy * max_ysize_pos;
3325 int dst_xx = dst_x + xx * xsize_size_left;
3326 int dst_yy = dst_y + yy * ysize_size_top;
3327 int xx_size = (xx ? tile_size : xsize_size_left);
3328 int yy_size = (yy ? tile_size : ysize_size_top);
3330 // draw partial (animated) envelope request to temporary bitmap
3331 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3332 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3336 // prepare partial (animated) envelope request from temporary bitmap
3337 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3340 redraw_mask |= REDRAW_FIELD;
3344 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3347 ClearAutoRepeatKeyEvents();
3350 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3352 int graphic = IMG_BACKGROUND_REQUEST;
3353 int sound_opening = SND_REQUEST_OPENING;
3354 int sound_closing = SND_REQUEST_CLOSING;
3355 int anim_mode_1 = request.anim_mode; // (higher priority)
3356 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3357 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3358 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3359 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3361 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3363 if (action == ACTION_OPENING)
3365 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3367 if (anim_mode == ANIM_DEFAULT)
3368 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3370 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3374 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3376 if (anim_mode != ANIM_NONE)
3377 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3379 if (anim_mode == ANIM_DEFAULT)
3380 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3383 game.envelope_active = FALSE;
3386 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3388 if (IS_MM_WALL(element))
3390 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3396 int graphic = el2preimg(element);
3398 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3399 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3404 void DrawLevel(int draw_background_mask)
3408 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3409 SetDrawBackgroundMask(draw_background_mask);
3413 for (x = BX1; x <= BX2; x++)
3414 for (y = BY1; y <= BY2; y++)
3415 DrawScreenField(x, y);
3417 redraw_mask |= REDRAW_FIELD;
3420 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3425 for (x = 0; x < size_x; x++)
3426 for (y = 0; y < size_y; y++)
3427 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3429 redraw_mask |= REDRAW_FIELD;
3432 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3436 for (x = 0; x < size_x; x++)
3437 for (y = 0; y < size_y; y++)
3438 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3440 redraw_mask |= REDRAW_FIELD;
3443 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3445 boolean show_level_border = (BorderElement != EL_EMPTY);
3446 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3447 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3448 int tile_size = preview.tile_size;
3449 int preview_width = preview.xsize * tile_size;
3450 int preview_height = preview.ysize * tile_size;
3451 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3452 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3453 int real_preview_width = real_preview_xsize * tile_size;
3454 int real_preview_height = real_preview_ysize * tile_size;
3455 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3456 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3459 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3462 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3464 dst_x += (preview_width - real_preview_width) / 2;
3465 dst_y += (preview_height - real_preview_height) / 2;
3467 for (x = 0; x < real_preview_xsize; x++)
3469 for (y = 0; y < real_preview_ysize; y++)
3471 int lx = from_x + x + (show_level_border ? -1 : 0);
3472 int ly = from_y + y + (show_level_border ? -1 : 0);
3473 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3474 getBorderElement(lx, ly));
3476 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3477 element, tile_size);
3481 redraw_mask |= REDRAW_FIELD;
3484 #define MICROLABEL_EMPTY 0
3485 #define MICROLABEL_LEVEL_NAME 1
3486 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3487 #define MICROLABEL_LEVEL_AUTHOR 3
3488 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3489 #define MICROLABEL_IMPORTED_FROM 5
3490 #define MICROLABEL_IMPORTED_BY_HEAD 6
3491 #define MICROLABEL_IMPORTED_BY 7
3493 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3495 int max_text_width = SXSIZE;
3496 int font_width = getFontWidth(font_nr);
3498 if (pos->align == ALIGN_CENTER)
3499 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3500 else if (pos->align == ALIGN_RIGHT)
3501 max_text_width = pos->x;
3503 max_text_width = SXSIZE - pos->x;
3505 return max_text_width / font_width;
3508 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3510 char label_text[MAX_OUTPUT_LINESIZE + 1];
3511 int max_len_label_text;
3512 int font_nr = pos->font;
3515 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3518 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3519 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3520 mode == MICROLABEL_IMPORTED_BY_HEAD)
3521 font_nr = pos->font_alt;
3523 max_len_label_text = getMaxTextLength(pos, font_nr);
3525 if (pos->size != -1)
3526 max_len_label_text = pos->size;
3528 for (i = 0; i < max_len_label_text; i++)
3529 label_text[i] = ' ';
3530 label_text[max_len_label_text] = '\0';
3532 if (strlen(label_text) > 0)
3533 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3536 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3537 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3538 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3539 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3540 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3541 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3542 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3543 max_len_label_text);
3544 label_text[max_len_label_text] = '\0';
3546 if (strlen(label_text) > 0)
3547 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3549 redraw_mask |= REDRAW_FIELD;
3552 static void DrawPreviewLevelLabel(int mode)
3554 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3557 static void DrawPreviewLevelInfo(int mode)
3559 if (mode == MICROLABEL_LEVEL_NAME)
3560 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3561 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3562 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3565 static void DrawPreviewLevelExt(boolean restart)
3567 static DelayCounter scroll_delay = { 0 };
3568 static DelayCounter label_delay = { 0 };
3569 static int from_x, from_y, scroll_direction;
3570 static int label_state, label_counter;
3571 boolean show_level_border = (BorderElement != EL_EMPTY);
3572 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3573 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3575 scroll_delay.value = preview.step_delay;
3576 label_delay.value = MICROLEVEL_LABEL_DELAY;
3583 if (preview.anim_mode == ANIM_CENTERED)
3585 if (level_xsize > preview.xsize)
3586 from_x = (level_xsize - preview.xsize) / 2;
3587 if (level_ysize > preview.ysize)
3588 from_y = (level_ysize - preview.ysize) / 2;
3591 from_x += preview.xoffset;
3592 from_y += preview.yoffset;
3594 scroll_direction = MV_RIGHT;
3598 DrawPreviewLevelPlayfield(from_x, from_y);
3599 DrawPreviewLevelLabel(label_state);
3601 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3602 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3604 // initialize delay counters
3605 ResetDelayCounter(&scroll_delay);
3606 ResetDelayCounter(&label_delay);
3608 if (leveldir_current->name)
3610 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3611 char label_text[MAX_OUTPUT_LINESIZE + 1];
3612 int font_nr = pos->font;
3613 int max_len_label_text = getMaxTextLength(pos, font_nr);
3615 if (pos->size != -1)
3616 max_len_label_text = pos->size;
3618 strncpy(label_text, leveldir_current->name, max_len_label_text);
3619 label_text[max_len_label_text] = '\0';
3621 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3622 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3628 // scroll preview level, if needed
3629 if (preview.anim_mode != ANIM_NONE &&
3630 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3631 DelayReached(&scroll_delay))
3633 switch (scroll_direction)
3638 from_x -= preview.step_offset;
3639 from_x = (from_x < 0 ? 0 : from_x);
3642 scroll_direction = MV_UP;
3646 if (from_x < level_xsize - preview.xsize)
3648 from_x += preview.step_offset;
3649 from_x = (from_x > level_xsize - preview.xsize ?
3650 level_xsize - preview.xsize : from_x);
3653 scroll_direction = MV_DOWN;
3659 from_y -= preview.step_offset;
3660 from_y = (from_y < 0 ? 0 : from_y);
3663 scroll_direction = MV_RIGHT;
3667 if (from_y < level_ysize - preview.ysize)
3669 from_y += preview.step_offset;
3670 from_y = (from_y > level_ysize - preview.ysize ?
3671 level_ysize - preview.ysize : from_y);
3674 scroll_direction = MV_LEFT;
3681 DrawPreviewLevelPlayfield(from_x, from_y);
3684 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3685 // redraw micro level label, if needed
3686 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3687 !strEqual(level.author, ANONYMOUS_NAME) &&
3688 !strEqual(level.author, leveldir_current->name) &&
3689 DelayReached(&label_delay))
3691 int max_label_counter = 23;
3693 if (leveldir_current->imported_from != NULL &&
3694 strlen(leveldir_current->imported_from) > 0)
3695 max_label_counter += 14;
3696 if (leveldir_current->imported_by != NULL &&
3697 strlen(leveldir_current->imported_by) > 0)
3698 max_label_counter += 14;
3700 label_counter = (label_counter + 1) % max_label_counter;
3701 label_state = (label_counter >= 0 && label_counter <= 7 ?
3702 MICROLABEL_LEVEL_NAME :
3703 label_counter >= 9 && label_counter <= 12 ?
3704 MICROLABEL_LEVEL_AUTHOR_HEAD :
3705 label_counter >= 14 && label_counter <= 21 ?
3706 MICROLABEL_LEVEL_AUTHOR :
3707 label_counter >= 23 && label_counter <= 26 ?
3708 MICROLABEL_IMPORTED_FROM_HEAD :
3709 label_counter >= 28 && label_counter <= 35 ?
3710 MICROLABEL_IMPORTED_FROM :
3711 label_counter >= 37 && label_counter <= 40 ?
3712 MICROLABEL_IMPORTED_BY_HEAD :
3713 label_counter >= 42 && label_counter <= 49 ?
3714 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3716 if (leveldir_current->imported_from == NULL &&
3717 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3718 label_state == MICROLABEL_IMPORTED_FROM))
3719 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3720 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3722 DrawPreviewLevelLabel(label_state);
3726 void DrawPreviewPlayers(void)
3728 if (game_status != GAME_MODE_MAIN)
3731 // do not draw preview players if level preview redefined, but players aren't
3732 if (preview.redefined && !menu.main.preview_players.redefined)
3735 boolean player_found[MAX_PLAYERS];
3736 int num_players = 0;
3739 for (i = 0; i < MAX_PLAYERS; i++)
3740 player_found[i] = FALSE;
3742 // check which players can be found in the level (simple approach)
3743 for (x = 0; x < lev_fieldx; x++)
3745 for (y = 0; y < lev_fieldy; y++)
3747 int element = level.field[x][y];
3749 if (IS_PLAYER_ELEMENT(element))
3751 int player_nr = GET_PLAYER_NR(element);
3753 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3755 if (!player_found[player_nr])
3758 player_found[player_nr] = TRUE;
3763 struct TextPosInfo *pos = &menu.main.preview_players;
3764 int tile_size = pos->tile_size;
3765 int border_size = pos->border_size;
3766 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3767 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3768 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3769 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3770 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3771 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3772 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3773 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3774 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3775 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3776 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3777 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3779 // clear area in which the players will be drawn
3780 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3781 max_players_width, max_players_height);
3783 if (!network.enabled && !setup.team_mode)
3786 // only draw players if level is suited for team mode
3787 if (num_players < 2)
3790 // draw all players that were found in the level
3791 for (i = 0; i < MAX_PLAYERS; i++)
3793 if (player_found[i])
3795 int graphic = el2img(EL_PLAYER_1 + i);
3797 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3799 xpos += player_xoffset;
3800 ypos += player_yoffset;
3805 void DrawPreviewLevelInitial(void)
3807 DrawPreviewLevelExt(TRUE);
3808 DrawPreviewPlayers();
3811 void DrawPreviewLevelAnimation(void)
3813 DrawPreviewLevelExt(FALSE);
3816 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3817 int border_size, int font_nr)
3819 int graphic = el2img(EL_PLAYER_1 + player_nr);
3820 int font_height = getFontHeight(font_nr);
3821 int player_height = MAX(tile_size, font_height);
3822 int xoffset_text = tile_size + border_size;
3823 int yoffset_text = (player_height - font_height) / 2;
3824 int yoffset_graphic = (player_height - tile_size) / 2;
3825 char *player_name = getNetworkPlayerName(player_nr + 1);
3827 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3829 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3832 static void DrawNetworkPlayersExt(boolean force)
3834 if (game_status != GAME_MODE_MAIN)
3837 if (!network.connected && !force)
3840 // do not draw network players if level preview redefined, but players aren't
3841 if (preview.redefined && !menu.main.network_players.redefined)
3844 int num_players = 0;
3847 for (i = 0; i < MAX_PLAYERS; i++)
3848 if (stored_player[i].connected_network)
3851 struct TextPosInfo *pos = &menu.main.network_players;
3852 int tile_size = pos->tile_size;
3853 int border_size = pos->border_size;
3854 int xoffset_text = tile_size + border_size;
3855 int font_nr = pos->font;
3856 int font_width = getFontWidth(font_nr);
3857 int font_height = getFontHeight(font_nr);
3858 int player_height = MAX(tile_size, font_height);
3859 int player_yoffset = player_height + border_size;
3860 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3861 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3862 int all_players_height = num_players * player_yoffset - border_size;
3863 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3864 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3865 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3867 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3868 max_players_width, max_players_height);
3870 // first draw local network player ...
3871 for (i = 0; i < MAX_PLAYERS; i++)
3873 if (stored_player[i].connected_network &&
3874 stored_player[i].connected_locally)
3876 char *player_name = getNetworkPlayerName(i + 1);
3877 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3878 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3880 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3882 ypos += player_yoffset;
3886 // ... then draw all other network players
3887 for (i = 0; i < MAX_PLAYERS; i++)
3889 if (stored_player[i].connected_network &&
3890 !stored_player[i].connected_locally)
3892 char *player_name = getNetworkPlayerName(i + 1);
3893 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3894 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3896 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3898 ypos += player_yoffset;
3903 void DrawNetworkPlayers(void)
3905 DrawNetworkPlayersExt(FALSE);
3908 void ClearNetworkPlayers(void)
3910 DrawNetworkPlayersExt(TRUE);
3913 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3914 int graphic, int lx, int ly,
3917 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3919 if (mask_mode == USE_MASKING)
3920 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3922 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3925 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3926 int graphic, int sync_frame, int mask_mode)
3928 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3930 if (mask_mode == USE_MASKING)
3931 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3933 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3936 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3937 int graphic, int sync_frame, int tilesize,
3940 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3942 if (mask_mode == USE_MASKING)
3943 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3945 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3948 static void DrawGraphicAnimation(int x, int y, int graphic)
3950 int lx = LEVELX(x), ly = LEVELY(y);
3951 int mask_mode = NO_MASKING;
3953 if (!IN_SCR_FIELD(x, y))
3956 if (game.use_masked_elements)
3958 if (Tile[lx][ly] != EL_EMPTY)
3960 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3962 mask_mode = USE_MASKING;
3966 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3967 graphic, lx, ly, mask_mode);
3969 MarkTileDirty(x, y);
3972 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3974 int lx = LEVELX(x), ly = LEVELY(y);
3975 int mask_mode = NO_MASKING;
3977 if (!IN_SCR_FIELD(x, y))
3980 if (game.use_masked_elements)
3982 if (Tile[lx][ly] != EL_EMPTY)
3984 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3986 mask_mode = USE_MASKING;
3990 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3991 graphic, lx, ly, mask_mode);
3993 MarkTileDirty(x, y);
3996 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3998 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4001 void DrawLevelElementAnimation(int x, int y, int element)
4003 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4005 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4008 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4010 int sx = SCREENX(x), sy = SCREENY(y);
4012 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4015 if (Tile[x][y] == EL_EMPTY)
4016 graphic = el2img(GfxElementEmpty[x][y]);
4018 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4021 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4024 DrawGraphicAnimation(sx, sy, graphic);
4027 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4028 DrawLevelFieldCrumbled(x, y);
4030 if (GFX_CRUMBLED(Tile[x][y]))
4031 DrawLevelFieldCrumbled(x, y);
4035 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4037 int sx = SCREENX(x), sy = SCREENY(y);
4040 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4043 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4045 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4048 DrawGraphicAnimation(sx, sy, graphic);
4050 if (GFX_CRUMBLED(element))
4051 DrawLevelFieldCrumbled(x, y);
4054 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4056 if (player->use_murphy)
4058 // this works only because currently only one player can be "murphy" ...
4059 static int last_horizontal_dir = MV_LEFT;
4060 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4062 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4063 last_horizontal_dir = move_dir;
4065 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4067 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4069 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4075 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4078 static boolean equalGraphics(int graphic1, int graphic2)
4080 struct GraphicInfo *g1 = &graphic_info[graphic1];
4081 struct GraphicInfo *g2 = &graphic_info[graphic2];
4083 return (g1->bitmap == g2->bitmap &&
4084 g1->src_x == g2->src_x &&
4085 g1->src_y == g2->src_y &&
4086 g1->anim_frames == g2->anim_frames &&
4087 g1->anim_delay == g2->anim_delay &&
4088 g1->anim_mode == g2->anim_mode);
4091 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4095 DRAW_PLAYER_STAGE_INIT = 0,
4096 DRAW_PLAYER_STAGE_LAST_FIELD,
4097 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4098 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4099 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4100 DRAW_PLAYER_STAGE_PLAYER,
4102 DRAW_PLAYER_STAGE_PLAYER,
4103 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4105 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4106 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4108 NUM_DRAW_PLAYER_STAGES
4111 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4113 static int static_last_player_graphic[MAX_PLAYERS];
4114 static int static_last_player_frame[MAX_PLAYERS];
4115 static boolean static_player_is_opaque[MAX_PLAYERS];
4116 static boolean draw_player[MAX_PLAYERS];
4117 int pnr = player->index_nr;
4119 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4121 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4122 static_last_player_frame[pnr] = player->Frame;
4123 static_player_is_opaque[pnr] = FALSE;
4125 draw_player[pnr] = TRUE;
4128 if (!draw_player[pnr])
4132 if (!IN_LEV_FIELD(player->jx, player->jy))
4134 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4135 Debug("draw:DrawPlayerExt", "This should never happen!");
4137 draw_player[pnr] = FALSE;
4143 int last_player_graphic = static_last_player_graphic[pnr];
4144 int last_player_frame = static_last_player_frame[pnr];
4145 boolean player_is_opaque = static_player_is_opaque[pnr];
4147 int jx = player->jx;
4148 int jy = player->jy;
4149 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4150 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4151 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4152 int last_jx = (player->is_moving ? jx - dx : jx);
4153 int last_jy = (player->is_moving ? jy - dy : jy);
4154 int next_jx = jx + dx;
4155 int next_jy = jy + dy;
4156 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4157 int sx = SCREENX(jx);
4158 int sy = SCREENY(jy);
4159 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4160 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4161 int element = Tile[jx][jy];
4162 int last_element = Tile[last_jx][last_jy];
4163 int action = (player->is_pushing ? ACTION_PUSHING :
4164 player->is_digging ? ACTION_DIGGING :
4165 player->is_collecting ? ACTION_COLLECTING :
4166 player->is_moving ? ACTION_MOVING :
4167 player->is_snapping ? ACTION_SNAPPING :
4168 player->is_dropping ? ACTION_DROPPING :
4169 player->is_waiting ? player->action_waiting :
4172 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4174 // ------------------------------------------------------------------------
4175 // initialize drawing the player
4176 // ------------------------------------------------------------------------
4178 draw_player[pnr] = FALSE;
4180 // GfxElement[][] is set to the element the player is digging or collecting;
4181 // remove also for off-screen player if the player is not moving anymore
4182 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4183 GfxElement[jx][jy] = EL_UNDEFINED;
4185 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4188 if (element == EL_EXPLOSION)
4191 InitPlayerGfxAnimation(player, action, move_dir);
4193 draw_player[pnr] = TRUE;
4195 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4197 // ------------------------------------------------------------------------
4198 // draw things in the field the player is leaving, if needed
4199 // ------------------------------------------------------------------------
4201 if (!IN_SCR_FIELD(sx, sy))
4202 draw_player[pnr] = FALSE;
4204 if (!player->is_moving)
4207 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4209 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4211 if (last_element == EL_DYNAMITE_ACTIVE ||
4212 last_element == EL_EM_DYNAMITE_ACTIVE ||
4213 last_element == EL_SP_DISK_RED_ACTIVE)
4214 DrawDynamite(last_jx, last_jy);
4216 DrawLevelFieldThruMask(last_jx, last_jy);
4218 else if (last_element == EL_DYNAMITE_ACTIVE ||
4219 last_element == EL_EM_DYNAMITE_ACTIVE ||
4220 last_element == EL_SP_DISK_RED_ACTIVE)
4221 DrawDynamite(last_jx, last_jy);
4223 DrawLevelField(last_jx, last_jy);
4225 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4227 // ------------------------------------------------------------------------
4228 // draw things behind the player, if needed
4229 // ------------------------------------------------------------------------
4233 DrawLevelElement(jx, jy, Back[jx][jy]);
4238 if (IS_ACTIVE_BOMB(element))
4240 DrawLevelElement(jx, jy, EL_EMPTY);
4245 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4247 int old_element = GfxElement[jx][jy];
4248 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4249 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4251 if (GFX_CRUMBLED(old_element))
4252 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4254 DrawScreenGraphic(sx, sy, old_graphic, frame);
4256 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4257 static_player_is_opaque[pnr] = TRUE;
4261 GfxElement[jx][jy] = EL_UNDEFINED;
4263 // make sure that pushed elements are drawn with correct frame rate
4264 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4266 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4267 GfxFrame[jx][jy] = player->StepFrame;
4269 DrawLevelField(jx, jy);
4272 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4274 // ------------------------------------------------------------------------
4275 // draw things the player is pushing, if needed
4276 // ------------------------------------------------------------------------
4278 if (!player->is_pushing || !player->is_moving)
4281 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4284 int gfx_frame = GfxFrame[jx][jy];
4286 if (!IS_MOVING(jx, jy)) // push movement already finished
4288 element = Tile[next_jx][next_jy];
4289 gfx_frame = GfxFrame[next_jx][next_jy];
4292 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4293 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4294 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4296 // draw background element under pushed element (like the Sokoban field)
4297 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4299 // this allows transparent pushing animation over non-black background
4302 DrawLevelElement(jx, jy, Back[jx][jy]);
4304 DrawLevelElement(jx, jy, EL_EMPTY);
4307 if (Back[next_jx][next_jy])
4308 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4310 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4312 int px = SCREENX(jx), py = SCREENY(jy);
4313 int pxx = (TILEX - ABS(sxx)) * dx;
4314 int pyy = (TILEY - ABS(syy)) * dy;
4317 // do not draw (EM style) pushing animation when pushing is finished
4318 // (two-tile animations usually do not contain start and end frame)
4319 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4320 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4322 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4324 // masked drawing is needed for EMC style (double) movement graphics
4325 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4326 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4329 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4331 // ------------------------------------------------------------------------
4332 // draw player himself
4333 // ------------------------------------------------------------------------
4335 int graphic = getPlayerGraphic(player, move_dir);
4337 // in the case of changed player action or direction, prevent the current
4338 // animation frame from being restarted for identical animations
4339 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4340 player->Frame = last_player_frame;
4342 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4344 if (player_is_opaque)
4345 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4347 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4349 if (SHIELD_ON(player))
4351 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4352 IMG_SHIELD_NORMAL_ACTIVE);
4353 frame = getGraphicAnimationFrame(graphic, -1);
4355 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4358 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4360 // ------------------------------------------------------------------------
4361 // draw things in front of player (active dynamite or dynabombs)
4362 // ------------------------------------------------------------------------
4364 if (IS_ACTIVE_BOMB(element))
4366 int graphic = el2img(element);
4367 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4369 if (game.emulation == EMU_SUPAPLEX)
4370 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4372 DrawGraphicThruMask(sx, sy, graphic, frame);
4375 if (player_is_moving && last_element == EL_EXPLOSION)
4377 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4378 GfxElement[last_jx][last_jy] : EL_EMPTY);
4379 int graphic = el_act2img(element, ACTION_EXPLODING);
4380 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4381 int phase = ExplodePhase[last_jx][last_jy] - 1;
4382 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4385 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4388 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4390 // ------------------------------------------------------------------------
4391 // draw elements the player is just walking/passing through/under
4392 // ------------------------------------------------------------------------
4394 if (player_is_moving)
4396 // handle the field the player is leaving ...
4397 if (IS_ACCESSIBLE_INSIDE(last_element))
4398 DrawLevelField(last_jx, last_jy);
4399 else if (IS_ACCESSIBLE_UNDER(last_element))
4400 DrawLevelFieldThruMask(last_jx, last_jy);
4403 // do not redraw accessible elements if the player is just pushing them
4404 if (!player_is_moving || !player->is_pushing)
4406 // ... and the field the player is entering
4407 if (IS_ACCESSIBLE_INSIDE(element))
4408 DrawLevelField(jx, jy);
4409 else if (IS_ACCESSIBLE_UNDER(element))
4410 DrawLevelFieldThruMask(jx, jy);
4413 MarkTileDirty(sx, sy);
4417 void DrawPlayer(struct PlayerInfo *player)
4421 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4422 DrawPlayerExt(player, i);
4425 void DrawAllPlayers(void)
4429 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4430 for (j = 0; j < MAX_PLAYERS; j++)
4431 if (stored_player[j].active)
4432 DrawPlayerExt(&stored_player[j], i);
4435 void DrawPlayerField(int x, int y)
4437 if (!IS_PLAYER(x, y))
4440 DrawPlayer(PLAYERINFO(x, y));
4443 // ----------------------------------------------------------------------------
4445 void WaitForEventToContinue(void)
4447 boolean first_wait = TRUE;
4448 boolean still_wait = TRUE;
4450 if (program.headless)
4453 // simulate releasing mouse button over last gadget, if still pressed
4455 HandleGadgets(-1, -1, 0);
4457 button_status = MB_RELEASED;
4460 ClearPlayerAction();
4466 if (NextValidEvent(&event))
4470 case EVENT_BUTTONPRESS:
4471 case EVENT_FINGERPRESS:
4475 case EVENT_BUTTONRELEASE:
4476 case EVENT_FINGERRELEASE:
4477 still_wait = first_wait;
4480 case EVENT_KEYPRESS:
4481 case SDL_CONTROLLERBUTTONDOWN:
4482 case SDL_JOYBUTTONDOWN:
4487 HandleOtherEvents(&event);
4491 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4496 if (!PendingEvent())
4501 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4503 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4504 int draw_buffer_last = GetDrawtoField();
4505 int width = request.width;
4506 int height = request.height;
4510 setRequestPosition(&sx, &sy, FALSE);
4512 button_status = MB_RELEASED;
4514 request_gadget_id = -1;
4521 SetDrawtoField(draw_buffer_game);
4523 HandleGameActions();
4525 SetDrawtoField(DRAW_TO_BACKBUFFER);
4532 while (NextValidEvent(&event))
4536 case EVENT_BUTTONPRESS:
4537 case EVENT_BUTTONRELEASE:
4538 case EVENT_MOTIONNOTIFY:
4540 DrawBuffer *drawto_last = drawto;
4543 if (event.type == EVENT_MOTIONNOTIFY)
4548 motion_status = TRUE;
4549 mx = ((MotionEvent *) &event)->x;
4550 my = ((MotionEvent *) &event)->y;
4554 motion_status = FALSE;
4555 mx = ((ButtonEvent *) &event)->x;
4556 my = ((ButtonEvent *) &event)->y;
4557 if (event.type == EVENT_BUTTONPRESS)
4558 button_status = ((ButtonEvent *) &event)->button;
4560 button_status = MB_RELEASED;
4563 if (global.use_envelope_request)
4565 // draw changed button states to temporary bitmap
4566 drawto = bitmap_db_store_1;
4569 // this sets 'request_gadget_id'
4570 HandleGadgets(mx, my, button_status);
4572 if (global.use_envelope_request)
4574 // restore pointer to drawing buffer
4575 drawto = drawto_last;
4577 // prepare complete envelope request from temporary bitmap
4578 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4582 switch (request_gadget_id)
4584 case TOOL_CTRL_ID_YES:
4585 case TOOL_CTRL_ID_TOUCH_YES:
4588 case TOOL_CTRL_ID_NO:
4589 case TOOL_CTRL_ID_TOUCH_NO:
4592 case TOOL_CTRL_ID_CONFIRM:
4593 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4594 result = TRUE | FALSE;
4597 case TOOL_CTRL_ID_PLAYER_1:
4600 case TOOL_CTRL_ID_PLAYER_2:
4603 case TOOL_CTRL_ID_PLAYER_3:
4606 case TOOL_CTRL_ID_PLAYER_4:
4611 // only check clickable animations if no request gadget clicked
4612 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4619 case SDL_WINDOWEVENT:
4620 HandleWindowEvent((WindowEvent *) &event);
4623 case SDL_APP_WILLENTERBACKGROUND:
4624 case SDL_APP_DIDENTERBACKGROUND:
4625 case SDL_APP_WILLENTERFOREGROUND:
4626 case SDL_APP_DIDENTERFOREGROUND:
4627 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4630 case EVENT_KEYPRESS:
4632 Key key = GetEventKey((KeyEvent *)&event);
4637 if (req_state & REQ_CONFIRM)
4646 #if defined(KSYM_Rewind)
4647 case KSYM_Rewind: // for Amazon Fire TV remote
4656 #if defined(KSYM_FastForward)
4657 case KSYM_FastForward: // for Amazon Fire TV remote
4663 HandleKeysDebug(key, KEY_PRESSED);
4667 if (req_state & REQ_PLAYER)
4669 int old_player_nr = setup.network_player_nr;
4672 result = old_player_nr + 1;
4677 result = old_player_nr + 1;
4708 case EVENT_FINGERRELEASE:
4709 case EVENT_KEYRELEASE:
4710 ClearPlayerAction();
4713 case SDL_CONTROLLERBUTTONDOWN:
4714 switch (event.cbutton.button)
4716 case SDL_CONTROLLER_BUTTON_A:
4717 case SDL_CONTROLLER_BUTTON_X:
4718 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4719 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4723 case SDL_CONTROLLER_BUTTON_B:
4724 case SDL_CONTROLLER_BUTTON_Y:
4725 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4726 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4727 case SDL_CONTROLLER_BUTTON_BACK:
4732 if (req_state & REQ_PLAYER)
4734 int old_player_nr = setup.network_player_nr;
4737 result = old_player_nr + 1;
4739 switch (event.cbutton.button)
4741 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4742 case SDL_CONTROLLER_BUTTON_Y:
4746 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4747 case SDL_CONTROLLER_BUTTON_B:
4751 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4752 case SDL_CONTROLLER_BUTTON_A:
4756 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4757 case SDL_CONTROLLER_BUTTON_X:
4768 case SDL_CONTROLLERBUTTONUP:
4769 HandleJoystickEvent(&event);
4770 ClearPlayerAction();
4774 HandleOtherEvents(&event);
4779 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4781 int joy = AnyJoystick();
4783 if (joy & JOY_BUTTON_1)
4785 else if (joy & JOY_BUTTON_2)
4788 else if (AnyJoystick())
4790 int joy = AnyJoystick();
4792 if (req_state & REQ_PLAYER)
4796 else if (joy & JOY_RIGHT)
4798 else if (joy & JOY_DOWN)
4800 else if (joy & JOY_LEFT)
4808 SetDrawtoField(draw_buffer_last);
4813 static void DoRequestBefore(void)
4815 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4817 // when showing request dialog after game ended, deactivate game panel
4819 game.panel.active = FALSE;
4821 if (game_status == GAME_MODE_PLAYING)
4822 BlitScreenToBitmap(backbuffer);
4824 // disable deactivated drawing when quick-loading level tape recording
4825 if (tape.playing && tape.deactivate_display)
4826 TapeDeactivateDisplayOff(TRUE);
4828 SetMouseCursor(CURSOR_DEFAULT);
4830 // pause network game while waiting for request to answer
4831 if (network.enabled &&
4832 game_status == GAME_MODE_PLAYING &&
4833 !game.all_players_gone)
4834 SendToServer_PausePlaying();
4836 // simulate releasing mouse button over last gadget, if still pressed
4838 HandleGadgets(-1, -1, 0);
4843 static void DoRequestAfter(void)
4847 if (game_status == GAME_MODE_PLAYING)
4849 SetPanelBackground();
4850 SetDrawBackgroundMask(REDRAW_DOOR_1);
4854 SetDrawBackgroundMask(REDRAW_FIELD);
4857 // continue network game after request
4858 if (network.enabled &&
4859 game_status == GAME_MODE_PLAYING &&
4860 !game.all_players_gone)
4861 SendToServer_ContinuePlaying();
4863 // restore deactivated drawing when quick-loading level tape recording
4864 if (tape.playing && tape.deactivate_display)
4865 TapeDeactivateDisplayOn();
4868 static void setRequestDoorTextProperties(char *text,
4873 int *set_max_line_length)
4875 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4876 struct TextPosInfo *pos = &request.button.confirm;
4877 int button_ypos = pos->y;
4878 int font_nr = FONT_TEXT_2;
4879 int font_width = getFontWidth(font_nr);
4880 int font_height = getFontHeight(font_nr);
4881 int line_height = font_height + line_spacing;
4882 int max_text_width = vp_door_1->width;
4883 int max_text_height = button_ypos - 2 * text_spacing;
4884 int max_line_length = max_text_width / font_width;
4885 int max_lines = max_text_height / line_height;
4887 if (maxWordLengthInRequestString(text) > max_line_length)
4889 font_nr = FONT_TEXT_1;
4890 font_width = getFontWidth(font_nr);
4891 max_line_length = max_text_width / font_width;
4894 *set_font_nr = font_nr;
4895 *set_max_lines = max_lines;
4896 *set_max_line_length = max_line_length;
4899 static void DrawRequestDoorText(char *text)
4901 char *text_ptr = text;
4902 int text_spacing = 8;
4903 int line_spacing = 2;
4904 int max_request_lines;
4905 int max_request_line_len;
4909 // force DOOR font inside door area
4910 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4912 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4913 &max_request_lines, &max_request_line_len);
4915 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4917 char text_line[max_request_line_len + 1];
4923 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4925 tc = *(text_ptr + tx);
4926 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4930 if ((tc == '?' || tc == '!') && tl == 0)
4940 strncpy(text_line, text_ptr, tl);
4943 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4944 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4945 text_line, font_nr);
4947 text_ptr += tl + (tc == ' ' ? 1 : 0);
4953 static int RequestDoor(char *text, unsigned int req_state)
4955 unsigned int old_door_state = GetDoorState();
4956 int draw_buffer_last = GetDrawtoField();
4959 if (old_door_state & DOOR_OPEN_1)
4961 CloseDoor(DOOR_CLOSE_1);
4963 // save old door content
4964 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4965 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4968 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4969 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4971 // clear door drawing field
4972 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4974 // write text for request
4975 DrawRequestDoorText(text);
4977 MapToolButtons(req_state);
4979 // copy request gadgets to door backbuffer
4980 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4982 OpenDoor(DOOR_OPEN_1);
4984 // ---------- handle request buttons ----------
4985 result = RequestHandleEvents(req_state, draw_buffer_last);
4989 if (!(req_state & REQ_STAY_OPEN))
4991 CloseDoor(DOOR_CLOSE_1);
4993 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4994 (req_state & REQ_REOPEN))
4995 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5001 static int RequestEnvelope(char *text, unsigned int req_state)
5003 int draw_buffer_last = GetDrawtoField();
5006 DrawEnvelopeRequest(text, req_state);
5007 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5009 // ---------- handle request buttons ----------
5010 result = RequestHandleEvents(req_state, draw_buffer_last);
5014 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5019 int Request(char *text, unsigned int req_state)
5021 boolean overlay_enabled = GetOverlayEnabled();
5024 game.request_active = TRUE;
5026 SetOverlayEnabled(FALSE);
5030 if (global.use_envelope_request)
5031 result = RequestEnvelope(text, req_state);
5033 result = RequestDoor(text, req_state);
5037 SetOverlayEnabled(overlay_enabled);
5039 game.request_active = FALSE;
5044 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5046 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5047 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5050 if (dpo1->sort_priority != dpo2->sort_priority)
5051 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5053 compare_result = dpo1->nr - dpo2->nr;
5055 return compare_result;
5058 void InitGraphicCompatibilityInfo_Doors(void)
5064 struct DoorInfo *door;
5068 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5069 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5071 { -1, -1, -1, NULL }
5073 struct Rect door_rect_list[] =
5075 { DX, DY, DXSIZE, DYSIZE },
5076 { VX, VY, VXSIZE, VYSIZE }
5080 for (i = 0; doors[i].door_token != -1; i++)
5082 int door_token = doors[i].door_token;
5083 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5084 int part_1 = doors[i].part_1;
5085 int part_8 = doors[i].part_8;
5086 int part_2 = part_1 + 1;
5087 int part_3 = part_1 + 2;
5088 struct DoorInfo *door = doors[i].door;
5089 struct Rect *door_rect = &door_rect_list[door_index];
5090 boolean door_gfx_redefined = FALSE;
5092 // check if any door part graphic definitions have been redefined
5094 for (j = 0; door_part_controls[j].door_token != -1; j++)
5096 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5097 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5099 if (dpc->door_token == door_token && fi->redefined)
5100 door_gfx_redefined = TRUE;
5103 // check for old-style door graphic/animation modifications
5105 if (!door_gfx_redefined)
5107 if (door->anim_mode & ANIM_STATIC_PANEL)
5109 door->panel.step_xoffset = 0;
5110 door->panel.step_yoffset = 0;
5113 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5115 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5116 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5117 int num_door_steps, num_panel_steps;
5119 // remove door part graphics other than the two default wings
5121 for (j = 0; door_part_controls[j].door_token != -1; j++)
5123 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5124 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5126 if (dpc->graphic >= part_3 &&
5127 dpc->graphic <= part_8)
5131 // set graphics and screen positions of the default wings
5133 g_part_1->width = door_rect->width;
5134 g_part_1->height = door_rect->height;
5135 g_part_2->width = door_rect->width;
5136 g_part_2->height = door_rect->height;
5137 g_part_2->src_x = door_rect->width;
5138 g_part_2->src_y = g_part_1->src_y;
5140 door->part_2.x = door->part_1.x;
5141 door->part_2.y = door->part_1.y;
5143 if (door->width != -1)
5145 g_part_1->width = door->width;
5146 g_part_2->width = door->width;
5148 // special treatment for graphics and screen position of right wing
5149 g_part_2->src_x += door_rect->width - door->width;
5150 door->part_2.x += door_rect->width - door->width;
5153 if (door->height != -1)
5155 g_part_1->height = door->height;
5156 g_part_2->height = door->height;
5158 // special treatment for graphics and screen position of bottom wing
5159 g_part_2->src_y += door_rect->height - door->height;
5160 door->part_2.y += door_rect->height - door->height;
5163 // set animation delays for the default wings and panels
5165 door->part_1.step_delay = door->step_delay;
5166 door->part_2.step_delay = door->step_delay;
5167 door->panel.step_delay = door->step_delay;
5169 // set animation draw order for the default wings
5171 door->part_1.sort_priority = 2; // draw left wing over ...
5172 door->part_2.sort_priority = 1; // ... right wing
5174 // set animation draw offset for the default wings
5176 if (door->anim_mode & ANIM_HORIZONTAL)
5178 door->part_1.step_xoffset = door->step_offset;
5179 door->part_1.step_yoffset = 0;
5180 door->part_2.step_xoffset = door->step_offset * -1;
5181 door->part_2.step_yoffset = 0;
5183 num_door_steps = g_part_1->width / door->step_offset;
5185 else // ANIM_VERTICAL
5187 door->part_1.step_xoffset = 0;
5188 door->part_1.step_yoffset = door->step_offset;
5189 door->part_2.step_xoffset = 0;
5190 door->part_2.step_yoffset = door->step_offset * -1;
5192 num_door_steps = g_part_1->height / door->step_offset;
5195 // set animation draw offset for the default panels
5197 if (door->step_offset > 1)
5199 num_panel_steps = 2 * door_rect->height / door->step_offset;
5200 door->panel.start_step = num_panel_steps - num_door_steps;
5201 door->panel.start_step_closing = door->panel.start_step;
5205 num_panel_steps = door_rect->height / door->step_offset;
5206 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5207 door->panel.start_step_closing = door->panel.start_step;
5208 door->panel.step_delay *= 2;
5215 void InitDoors(void)
5219 for (i = 0; door_part_controls[i].door_token != -1; i++)
5221 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5222 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5224 // initialize "start_step_opening" and "start_step_closing", if needed
5225 if (dpc->pos->start_step_opening == 0 &&
5226 dpc->pos->start_step_closing == 0)
5228 // dpc->pos->start_step_opening = dpc->pos->start_step;
5229 dpc->pos->start_step_closing = dpc->pos->start_step;
5232 // fill structure for door part draw order (sorted below)
5234 dpo->sort_priority = dpc->pos->sort_priority;
5237 // sort door part controls according to sort_priority and graphic number
5238 qsort(door_part_order, MAX_DOOR_PARTS,
5239 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5242 unsigned int OpenDoor(unsigned int door_state)
5244 if (door_state & DOOR_COPY_BACK)
5246 if (door_state & DOOR_OPEN_1)
5247 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5248 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5250 if (door_state & DOOR_OPEN_2)
5251 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5252 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5254 door_state &= ~DOOR_COPY_BACK;
5257 return MoveDoor(door_state);
5260 unsigned int CloseDoor(unsigned int door_state)
5262 unsigned int old_door_state = GetDoorState();
5264 if (!(door_state & DOOR_NO_COPY_BACK))
5266 if (old_door_state & DOOR_OPEN_1)
5267 BlitBitmap(backbuffer, bitmap_db_door_1,
5268 DX, DY, DXSIZE, DYSIZE, 0, 0);
5270 if (old_door_state & DOOR_OPEN_2)
5271 BlitBitmap(backbuffer, bitmap_db_door_2,
5272 VX, VY, VXSIZE, VYSIZE, 0, 0);
5274 door_state &= ~DOOR_NO_COPY_BACK;
5277 return MoveDoor(door_state);
5280 unsigned int GetDoorState(void)
5282 return MoveDoor(DOOR_GET_STATE);
5285 unsigned int SetDoorState(unsigned int door_state)
5287 return MoveDoor(door_state | DOOR_SET_STATE);
5290 static int euclid(int a, int b)
5292 return (b ? euclid(b, a % b) : a);
5295 unsigned int MoveDoor(unsigned int door_state)
5297 struct Rect door_rect_list[] =
5299 { DX, DY, DXSIZE, DYSIZE },
5300 { VX, VY, VXSIZE, VYSIZE }
5302 static int door1 = DOOR_CLOSE_1;
5303 static int door2 = DOOR_CLOSE_2;
5304 DelayCounter door_delay = { 0 };
5307 if (door_state == DOOR_GET_STATE)
5308 return (door1 | door2);
5310 if (door_state & DOOR_SET_STATE)
5312 if (door_state & DOOR_ACTION_1)
5313 door1 = door_state & DOOR_ACTION_1;
5314 if (door_state & DOOR_ACTION_2)
5315 door2 = door_state & DOOR_ACTION_2;
5317 return (door1 | door2);
5320 if (!(door_state & DOOR_FORCE_REDRAW))
5322 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5323 door_state &= ~DOOR_OPEN_1;
5324 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5325 door_state &= ~DOOR_CLOSE_1;
5326 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5327 door_state &= ~DOOR_OPEN_2;
5328 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5329 door_state &= ~DOOR_CLOSE_2;
5332 if (global.autoplay_leveldir)
5334 door_state |= DOOR_NO_DELAY;
5335 door_state &= ~DOOR_CLOSE_ALL;
5338 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5339 door_state |= DOOR_NO_DELAY;
5341 if (door_state & DOOR_ACTION)
5343 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5344 boolean door_panel_drawn[NUM_DOORS];
5345 boolean panel_has_doors[NUM_DOORS];
5346 boolean door_part_skip[MAX_DOOR_PARTS];
5347 boolean door_part_done[MAX_DOOR_PARTS];
5348 boolean door_part_done_all;
5349 int num_steps[MAX_DOOR_PARTS];
5350 int max_move_delay = 0; // delay for complete animations of all doors
5351 int max_step_delay = 0; // delay (ms) between two animation frames
5352 int num_move_steps = 0; // number of animation steps for all doors
5353 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5354 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5358 for (i = 0; i < NUM_DOORS; i++)
5359 panel_has_doors[i] = FALSE;
5361 for (i = 0; i < MAX_DOOR_PARTS; i++)
5363 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5364 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5365 int door_token = dpc->door_token;
5367 door_part_done[i] = FALSE;
5368 door_part_skip[i] = (!(door_state & door_token) ||
5372 for (i = 0; i < MAX_DOOR_PARTS; i++)
5374 int nr = door_part_order[i].nr;
5375 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5376 struct DoorPartPosInfo *pos = dpc->pos;
5377 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5378 int door_token = dpc->door_token;
5379 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5380 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5381 int step_xoffset = ABS(pos->step_xoffset);
5382 int step_yoffset = ABS(pos->step_yoffset);
5383 int step_delay = pos->step_delay;
5384 int current_door_state = door_state & door_token;
5385 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5386 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5387 boolean part_opening = (is_panel ? door_closing : door_opening);
5388 int start_step = (part_opening ? pos->start_step_opening :
5389 pos->start_step_closing);
5390 float move_xsize = (step_xoffset ? g->width : 0);
5391 float move_ysize = (step_yoffset ? g->height : 0);
5392 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5393 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5394 int move_steps = (move_xsteps && move_ysteps ?
5395 MIN(move_xsteps, move_ysteps) :
5396 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5397 int move_delay = move_steps * step_delay;
5399 if (door_part_skip[nr])
5402 max_move_delay = MAX(max_move_delay, move_delay);
5403 max_step_delay = (max_step_delay == 0 ? step_delay :
5404 euclid(max_step_delay, step_delay));
5405 num_steps[nr] = move_steps;
5409 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5411 panel_has_doors[door_index] = TRUE;
5415 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5417 num_move_steps = max_move_delay / max_step_delay;
5418 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5420 door_delay.value = max_step_delay;
5422 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5424 start = num_move_steps - 1;
5428 // opening door sound has priority over simultaneously closing door
5429 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5431 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5433 if (door_state & DOOR_OPEN_1)
5434 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5435 if (door_state & DOOR_OPEN_2)
5436 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5438 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5440 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5442 if (door_state & DOOR_CLOSE_1)
5443 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5444 if (door_state & DOOR_CLOSE_2)
5445 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5449 for (k = start; k < num_move_steps; k++)
5451 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5453 door_part_done_all = TRUE;
5455 for (i = 0; i < NUM_DOORS; i++)
5456 door_panel_drawn[i] = FALSE;
5458 for (i = 0; i < MAX_DOOR_PARTS; i++)
5460 int nr = door_part_order[i].nr;
5461 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5462 struct DoorPartPosInfo *pos = dpc->pos;
5463 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5464 int door_token = dpc->door_token;
5465 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5466 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5467 boolean is_panel_and_door_has_closed = FALSE;
5468 struct Rect *door_rect = &door_rect_list[door_index];
5469 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5471 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5472 int current_door_state = door_state & door_token;
5473 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5474 boolean door_closing = !door_opening;
5475 boolean part_opening = (is_panel ? door_closing : door_opening);
5476 boolean part_closing = !part_opening;
5477 int start_step = (part_opening ? pos->start_step_opening :
5478 pos->start_step_closing);
5479 int step_delay = pos->step_delay;
5480 int step_factor = step_delay / max_step_delay;
5481 int k1 = (step_factor ? k / step_factor + 1 : k);
5482 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5483 int kk = MAX(0, k2);
5486 int src_x, src_y, src_xx, src_yy;
5487 int dst_x, dst_y, dst_xx, dst_yy;
5490 if (door_part_skip[nr])
5493 if (!(door_state & door_token))
5501 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5502 int kk_door = MAX(0, k2_door);
5503 int sync_frame = kk_door * door_delay.value;
5504 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5506 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5507 &g_src_x, &g_src_y);
5512 if (!door_panel_drawn[door_index])
5514 ClearRectangle(drawto, door_rect->x, door_rect->y,
5515 door_rect->width, door_rect->height);
5517 door_panel_drawn[door_index] = TRUE;
5520 // draw opening or closing door parts
5522 if (pos->step_xoffset < 0) // door part on right side
5525 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5528 if (dst_xx + width > door_rect->width)
5529 width = door_rect->width - dst_xx;
5531 else // door part on left side
5534 dst_xx = pos->x - kk * pos->step_xoffset;
5538 src_xx = ABS(dst_xx);
5542 width = g->width - src_xx;
5544 if (width > door_rect->width)
5545 width = door_rect->width;
5547 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5550 if (pos->step_yoffset < 0) // door part on bottom side
5553 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5556 if (dst_yy + height > door_rect->height)
5557 height = door_rect->height - dst_yy;
5559 else // door part on top side
5562 dst_yy = pos->y - kk * pos->step_yoffset;
5566 src_yy = ABS(dst_yy);
5570 height = g->height - src_yy;
5573 src_x = g_src_x + src_xx;
5574 src_y = g_src_y + src_yy;
5576 dst_x = door_rect->x + dst_xx;
5577 dst_y = door_rect->y + dst_yy;
5579 is_panel_and_door_has_closed =
5582 panel_has_doors[door_index] &&
5583 k >= num_move_steps_doors_only - 1);
5585 if (width >= 0 && width <= g->width &&
5586 height >= 0 && height <= g->height &&
5587 !is_panel_and_door_has_closed)
5589 if (is_panel || !pos->draw_masked)
5590 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5593 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5597 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5599 if ((part_opening && (width < 0 || height < 0)) ||
5600 (part_closing && (width >= g->width && height >= g->height)))
5601 door_part_done[nr] = TRUE;
5603 // continue door part animations, but not panel after door has closed
5604 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5605 door_part_done_all = FALSE;
5608 if (!(door_state & DOOR_NO_DELAY))
5611 HandleGameActions();
5615 SkipUntilDelayReached(&door_delay, &k, last_frame);
5617 // prevent OS (Windows) from complaining about program not responding
5621 if (door_part_done_all)
5625 if (!(door_state & DOOR_NO_DELAY))
5627 // wait for specified door action post delay
5628 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5629 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5630 else if (door_state & DOOR_ACTION_1)
5631 door_delay.value = door_1.post_delay;
5632 else if (door_state & DOOR_ACTION_2)
5633 door_delay.value = door_2.post_delay;
5635 while (!DelayReached(&door_delay))
5638 HandleGameActions();
5645 if (door_state & DOOR_ACTION_1)
5646 door1 = door_state & DOOR_ACTION_1;
5647 if (door_state & DOOR_ACTION_2)
5648 door2 = door_state & DOOR_ACTION_2;
5650 // draw masked border over door area
5651 DrawMaskedBorder(REDRAW_DOOR_1);
5652 DrawMaskedBorder(REDRAW_DOOR_2);
5654 ClearAutoRepeatKeyEvents();
5656 return (door1 | door2);
5659 static boolean useSpecialEditorDoor(void)
5661 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5662 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5664 // do not draw special editor door if editor border defined or redefined
5665 if (graphic_info[graphic].bitmap != NULL || redefined)
5668 // do not draw special editor door if global border defined to be empty
5669 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5672 // do not draw special editor door if viewport definitions do not match
5676 EY + EYSIZE != VY + VYSIZE)
5682 void DrawSpecialEditorDoor(void)
5684 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5685 int top_border_width = gfx1->width;
5686 int top_border_height = gfx1->height;
5687 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5688 int ex = EX - outer_border;
5689 int ey = EY - outer_border;
5690 int vy = VY - outer_border;
5691 int exsize = EXSIZE + 2 * outer_border;
5693 if (!useSpecialEditorDoor())
5696 // draw bigger level editor toolbox window
5697 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5698 top_border_width, top_border_height, ex, ey - top_border_height);
5699 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5700 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5702 redraw_mask |= REDRAW_ALL;
5705 void UndrawSpecialEditorDoor(void)
5707 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5708 int top_border_width = gfx1->width;
5709 int top_border_height = gfx1->height;
5710 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5711 int ex = EX - outer_border;
5712 int ey = EY - outer_border;
5713 int ey_top = ey - top_border_height;
5714 int exsize = EXSIZE + 2 * outer_border;
5715 int eysize = EYSIZE + 2 * outer_border;
5717 if (!useSpecialEditorDoor())
5720 // draw normal tape recorder window
5721 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5723 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5724 ex, ey_top, top_border_width, top_border_height,
5726 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5727 ex, ey, exsize, eysize, ex, ey);
5731 // if screen background is set to "[NONE]", clear editor toolbox window
5732 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5733 ClearRectangle(drawto, ex, ey, exsize, eysize);
5736 redraw_mask |= REDRAW_ALL;
5740 // ---------- new tool button stuff -------------------------------------------
5745 struct TextPosInfo *pos;
5747 boolean is_touch_button;
5749 } toolbutton_info[NUM_TOOL_BUTTONS] =
5752 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5753 TOOL_CTRL_ID_YES, FALSE, "yes"
5756 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5757 TOOL_CTRL_ID_NO, FALSE, "no"
5760 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5761 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5764 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5765 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5768 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5769 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5772 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5773 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5776 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5777 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5780 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5781 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5784 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5785 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5788 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5789 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5793 void CreateToolButtons(void)
5797 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5799 int graphic = toolbutton_info[i].graphic;
5800 struct GraphicInfo *gfx = &graphic_info[graphic];
5801 struct TextPosInfo *pos = toolbutton_info[i].pos;
5802 struct GadgetInfo *gi;
5803 Bitmap *deco_bitmap = None;
5804 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5805 unsigned int event_mask = GD_EVENT_RELEASED;
5806 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5807 int base_x = (is_touch_button ? 0 : DX);
5808 int base_y = (is_touch_button ? 0 : DY);
5809 int gd_x = gfx->src_x;
5810 int gd_y = gfx->src_y;
5811 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5812 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5817 // do not use touch buttons if overlay touch buttons are disabled
5818 if (is_touch_button && !setup.touch.overlay_buttons)
5821 if (global.use_envelope_request && !is_touch_button)
5823 setRequestPosition(&base_x, &base_y, TRUE);
5825 // check if request buttons are outside of envelope and fix, if needed
5826 if (x < 0 || x + gfx->width > request.width ||
5827 y < 0 || y + gfx->height > request.height)
5829 if (id == TOOL_CTRL_ID_YES)
5832 y = request.height - 2 * request.border_size - gfx->height;
5834 else if (id == TOOL_CTRL_ID_NO)
5836 x = request.width - 2 * request.border_size - gfx->width;
5837 y = request.height - 2 * request.border_size - gfx->height;
5839 else if (id == TOOL_CTRL_ID_CONFIRM)
5841 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5842 y = request.height - 2 * request.border_size - gfx->height;
5844 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5846 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5848 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5849 y = request.height - 2 * request.border_size - gfx->height * 2;
5851 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5852 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5857 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5860 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5862 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5863 pos->size, &deco_bitmap, &deco_x, &deco_y);
5864 deco_xpos = (gfx->width - pos->size) / 2;
5865 deco_ypos = (gfx->height - pos->size) / 2;
5868 gi = CreateGadget(GDI_CUSTOM_ID, id,
5869 GDI_IMAGE_ID, graphic,
5870 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5873 GDI_WIDTH, gfx->width,
5874 GDI_HEIGHT, gfx->height,
5875 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5876 GDI_STATE, GD_BUTTON_UNPRESSED,
5877 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5878 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5879 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5880 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5881 GDI_DECORATION_SIZE, pos->size, pos->size,
5882 GDI_DECORATION_SHIFTING, 1, 1,
5883 GDI_DIRECT_DRAW, FALSE,
5884 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5885 GDI_EVENT_MASK, event_mask,
5886 GDI_CALLBACK_ACTION, HandleToolButtons,
5890 Fail("cannot create gadget");
5892 tool_gadget[id] = gi;
5896 void FreeToolButtons(void)
5900 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5901 FreeGadget(tool_gadget[i]);
5904 static void MapToolButtons(unsigned int req_state)
5906 if (req_state & REQ_ASK)
5908 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5909 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5910 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5911 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5913 else if (req_state & REQ_CONFIRM)
5915 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5916 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5918 else if (req_state & REQ_PLAYER)
5920 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5921 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5922 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5923 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5927 static void UnmapToolButtons(void)
5931 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5932 UnmapGadget(tool_gadget[i]);
5935 static void HandleToolButtons(struct GadgetInfo *gi)
5937 request_gadget_id = gi->custom_id;
5940 static struct Mapping_EM_to_RND_object
5943 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5944 boolean is_backside; // backside of moving element
5950 em_object_mapping_list[GAME_TILE_MAX + 1] =
5953 Zborder, FALSE, FALSE,
5957 Zplayer, FALSE, FALSE,
5966 Ztank, FALSE, FALSE,
5970 Zeater, FALSE, FALSE,
5974 Zdynamite, FALSE, FALSE,
5978 Zboom, FALSE, FALSE,
5983 Xchain, FALSE, FALSE,
5984 EL_DEFAULT, ACTION_EXPLODING, -1
5987 Xboom_bug, FALSE, FALSE,
5988 EL_BUG, ACTION_EXPLODING, -1
5991 Xboom_tank, FALSE, FALSE,
5992 EL_SPACESHIP, ACTION_EXPLODING, -1
5995 Xboom_android, FALSE, FALSE,
5996 EL_EMC_ANDROID, ACTION_OTHER, -1
5999 Xboom_1, FALSE, FALSE,
6000 EL_DEFAULT, ACTION_EXPLODING, -1
6003 Xboom_2, FALSE, FALSE,
6004 EL_DEFAULT, ACTION_EXPLODING, -1
6008 Xblank, TRUE, FALSE,
6013 Xsplash_e, FALSE, FALSE,
6014 EL_ACID_SPLASH_RIGHT, -1, -1
6017 Xsplash_w, FALSE, FALSE,
6018 EL_ACID_SPLASH_LEFT, -1, -1
6022 Xplant, TRUE, FALSE,
6023 EL_EMC_PLANT, -1, -1
6026 Yplant, FALSE, FALSE,
6027 EL_EMC_PLANT, -1, -1
6031 Xacid_1, TRUE, FALSE,
6035 Xacid_2, FALSE, FALSE,
6039 Xacid_3, FALSE, FALSE,
6043 Xacid_4, FALSE, FALSE,
6047 Xacid_5, FALSE, FALSE,
6051 Xacid_6, FALSE, FALSE,
6055 Xacid_7, FALSE, FALSE,
6059 Xacid_8, FALSE, FALSE,
6064 Xfake_acid_1, TRUE, FALSE,
6065 EL_EMC_FAKE_ACID, -1, -1
6068 Xfake_acid_2, FALSE, FALSE,
6069 EL_EMC_FAKE_ACID, -1, -1
6072 Xfake_acid_3, FALSE, FALSE,
6073 EL_EMC_FAKE_ACID, -1, -1
6076 Xfake_acid_4, FALSE, FALSE,
6077 EL_EMC_FAKE_ACID, -1, -1
6080 Xfake_acid_5, FALSE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_6, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_7, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6092 Xfake_acid_8, FALSE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6097 Xfake_acid_1_player, FALSE, FALSE,
6098 EL_EMC_FAKE_ACID, -1, -1
6101 Xfake_acid_2_player, FALSE, FALSE,
6102 EL_EMC_FAKE_ACID, -1, -1
6105 Xfake_acid_3_player, FALSE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_4_player, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_5_player, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_6_player, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_7_player, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_8_player, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6130 Xgrass, TRUE, FALSE,
6131 EL_EMC_GRASS, -1, -1
6134 Ygrass_nB, FALSE, FALSE,
6135 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6138 Ygrass_eB, FALSE, FALSE,
6139 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6142 Ygrass_sB, FALSE, FALSE,
6143 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6146 Ygrass_wB, FALSE, FALSE,
6147 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6155 Ydirt_nB, FALSE, FALSE,
6156 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6159 Ydirt_eB, FALSE, FALSE,
6160 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6163 Ydirt_sB, FALSE, FALSE,
6164 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6167 Ydirt_wB, FALSE, FALSE,
6168 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6172 Xandroid, TRUE, FALSE,
6173 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6176 Xandroid_1_n, FALSE, FALSE,
6177 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6180 Xandroid_2_n, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6184 Xandroid_1_e, FALSE, FALSE,
6185 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6188 Xandroid_2_e, FALSE, FALSE,
6189 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6192 Xandroid_1_w, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6196 Xandroid_2_w, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6200 Xandroid_1_s, FALSE, FALSE,
6201 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6204 Xandroid_2_s, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6208 Yandroid_n, FALSE, FALSE,
6209 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6212 Yandroid_nB, FALSE, TRUE,
6213 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6216 Yandroid_ne, FALSE, FALSE,
6217 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6220 Yandroid_neB, FALSE, TRUE,
6221 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6224 Yandroid_e, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6228 Yandroid_eB, FALSE, TRUE,
6229 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6232 Yandroid_se, FALSE, FALSE,
6233 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6236 Yandroid_seB, FALSE, TRUE,
6237 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6240 Yandroid_s, FALSE, FALSE,
6241 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6244 Yandroid_sB, FALSE, TRUE,
6245 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6248 Yandroid_sw, FALSE, FALSE,
6249 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6252 Yandroid_swB, FALSE, TRUE,
6253 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6256 Yandroid_w, FALSE, FALSE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6260 Yandroid_wB, FALSE, TRUE,
6261 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6264 Yandroid_nw, FALSE, FALSE,
6265 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6268 Yandroid_nwB, FALSE, TRUE,
6269 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6273 Xeater_n, TRUE, FALSE,
6274 EL_YAMYAM_UP, -1, -1
6277 Xeater_e, TRUE, FALSE,
6278 EL_YAMYAM_RIGHT, -1, -1
6281 Xeater_w, TRUE, FALSE,
6282 EL_YAMYAM_LEFT, -1, -1
6285 Xeater_s, TRUE, FALSE,
6286 EL_YAMYAM_DOWN, -1, -1
6289 Yeater_n, FALSE, FALSE,
6290 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6293 Yeater_nB, FALSE, TRUE,
6294 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6297 Yeater_e, FALSE, FALSE,
6298 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6301 Yeater_eB, FALSE, TRUE,
6302 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6305 Yeater_s, FALSE, FALSE,
6306 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6309 Yeater_sB, FALSE, TRUE,
6310 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6313 Yeater_w, FALSE, FALSE,
6314 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6317 Yeater_wB, FALSE, TRUE,
6318 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6321 Yeater_stone, FALSE, FALSE,
6322 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6325 Yeater_spring, FALSE, FALSE,
6326 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6330 Xalien, TRUE, FALSE,
6334 Xalien_pause, FALSE, FALSE,
6338 Yalien_n, FALSE, FALSE,
6339 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6342 Yalien_nB, FALSE, TRUE,
6343 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6346 Yalien_e, FALSE, FALSE,
6347 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6350 Yalien_eB, FALSE, TRUE,
6351 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6354 Yalien_s, FALSE, FALSE,
6355 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6358 Yalien_sB, FALSE, TRUE,
6359 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6362 Yalien_w, FALSE, FALSE,
6363 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6366 Yalien_wB, FALSE, TRUE,
6367 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6370 Yalien_stone, FALSE, FALSE,
6371 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6374 Yalien_spring, FALSE, FALSE,
6375 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6379 Xbug_1_n, TRUE, FALSE,
6383 Xbug_1_e, TRUE, FALSE,
6384 EL_BUG_RIGHT, -1, -1
6387 Xbug_1_s, TRUE, FALSE,
6391 Xbug_1_w, TRUE, FALSE,
6395 Xbug_2_n, FALSE, FALSE,
6399 Xbug_2_e, FALSE, FALSE,
6400 EL_BUG_RIGHT, -1, -1
6403 Xbug_2_s, FALSE, FALSE,
6407 Xbug_2_w, FALSE, FALSE,
6411 Ybug_n, FALSE, FALSE,
6412 EL_BUG, ACTION_MOVING, MV_BIT_UP
6415 Ybug_nB, FALSE, TRUE,
6416 EL_BUG, ACTION_MOVING, MV_BIT_UP
6419 Ybug_e, FALSE, FALSE,
6420 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6423 Ybug_eB, FALSE, TRUE,
6424 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6427 Ybug_s, FALSE, FALSE,
6428 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6431 Ybug_sB, FALSE, TRUE,
6432 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6435 Ybug_w, FALSE, FALSE,
6436 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6439 Ybug_wB, FALSE, TRUE,
6440 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6443 Ybug_w_n, FALSE, FALSE,
6444 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6447 Ybug_n_e, FALSE, FALSE,
6448 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6451 Ybug_e_s, FALSE, FALSE,
6452 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6455 Ybug_s_w, FALSE, FALSE,
6456 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6459 Ybug_e_n, FALSE, FALSE,
6460 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6463 Ybug_s_e, FALSE, FALSE,
6464 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6467 Ybug_w_s, FALSE, FALSE,
6468 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6471 Ybug_n_w, FALSE, FALSE,
6472 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6475 Ybug_stone, FALSE, FALSE,
6476 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6479 Ybug_spring, FALSE, FALSE,
6480 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6484 Xtank_1_n, TRUE, FALSE,
6485 EL_SPACESHIP_UP, -1, -1
6488 Xtank_1_e, TRUE, FALSE,
6489 EL_SPACESHIP_RIGHT, -1, -1
6492 Xtank_1_s, TRUE, FALSE,
6493 EL_SPACESHIP_DOWN, -1, -1
6496 Xtank_1_w, TRUE, FALSE,
6497 EL_SPACESHIP_LEFT, -1, -1
6500 Xtank_2_n, FALSE, FALSE,
6501 EL_SPACESHIP_UP, -1, -1
6504 Xtank_2_e, FALSE, FALSE,
6505 EL_SPACESHIP_RIGHT, -1, -1
6508 Xtank_2_s, FALSE, FALSE,
6509 EL_SPACESHIP_DOWN, -1, -1
6512 Xtank_2_w, FALSE, FALSE,
6513 EL_SPACESHIP_LEFT, -1, -1
6516 Ytank_n, FALSE, FALSE,
6517 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6520 Ytank_nB, FALSE, TRUE,
6521 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6524 Ytank_e, FALSE, FALSE,
6525 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6528 Ytank_eB, FALSE, TRUE,
6529 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6532 Ytank_s, FALSE, FALSE,
6533 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6536 Ytank_sB, FALSE, TRUE,
6537 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6540 Ytank_w, FALSE, FALSE,
6541 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6544 Ytank_wB, FALSE, TRUE,
6545 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6548 Ytank_w_n, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6552 Ytank_n_e, FALSE, FALSE,
6553 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6556 Ytank_e_s, FALSE, FALSE,
6557 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6560 Ytank_s_w, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6564 Ytank_e_n, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6568 Ytank_s_e, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6572 Ytank_w_s, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6576 Ytank_n_w, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6580 Ytank_stone, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6584 Ytank_spring, FALSE, FALSE,
6585 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6589 Xemerald, TRUE, FALSE,
6593 Xemerald_pause, FALSE, FALSE,
6597 Xemerald_fall, FALSE, FALSE,
6601 Xemerald_shine, FALSE, FALSE,
6602 EL_EMERALD, ACTION_TWINKLING, -1
6605 Yemerald_s, FALSE, FALSE,
6606 EL_EMERALD, ACTION_FALLING, -1
6609 Yemerald_sB, FALSE, TRUE,
6610 EL_EMERALD, ACTION_FALLING, -1
6613 Yemerald_e, FALSE, FALSE,
6614 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6617 Yemerald_eB, FALSE, TRUE,
6618 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6621 Yemerald_w, FALSE, FALSE,
6622 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6625 Yemerald_wB, FALSE, TRUE,
6626 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6629 Yemerald_blank, FALSE, FALSE,
6630 EL_EMERALD, ACTION_COLLECTING, -1
6634 Xdiamond, TRUE, FALSE,
6638 Xdiamond_pause, FALSE, FALSE,
6642 Xdiamond_fall, FALSE, FALSE,
6646 Xdiamond_shine, FALSE, FALSE,
6647 EL_DIAMOND, ACTION_TWINKLING, -1
6650 Ydiamond_s, FALSE, FALSE,
6651 EL_DIAMOND, ACTION_FALLING, -1
6654 Ydiamond_sB, FALSE, TRUE,
6655 EL_DIAMOND, ACTION_FALLING, -1
6658 Ydiamond_e, FALSE, FALSE,
6659 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6662 Ydiamond_eB, FALSE, TRUE,
6663 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6666 Ydiamond_w, FALSE, FALSE,
6667 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6670 Ydiamond_wB, FALSE, TRUE,
6671 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6674 Ydiamond_blank, FALSE, FALSE,
6675 EL_DIAMOND, ACTION_COLLECTING, -1
6678 Ydiamond_stone, FALSE, FALSE,
6679 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6683 Xstone, TRUE, FALSE,
6687 Xstone_pause, FALSE, FALSE,
6691 Xstone_fall, FALSE, FALSE,
6695 Ystone_s, FALSE, FALSE,
6696 EL_ROCK, ACTION_FALLING, -1
6699 Ystone_sB, FALSE, TRUE,
6700 EL_ROCK, ACTION_FALLING, -1
6703 Ystone_e, FALSE, FALSE,
6704 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6707 Ystone_eB, FALSE, TRUE,
6708 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6711 Ystone_w, FALSE, FALSE,
6712 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6715 Ystone_wB, FALSE, TRUE,
6716 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6724 Xbomb_pause, FALSE, FALSE,
6728 Xbomb_fall, FALSE, FALSE,
6732 Ybomb_s, FALSE, FALSE,
6733 EL_BOMB, ACTION_FALLING, -1
6736 Ybomb_sB, FALSE, TRUE,
6737 EL_BOMB, ACTION_FALLING, -1
6740 Ybomb_e, FALSE, FALSE,
6741 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6744 Ybomb_eB, FALSE, TRUE,
6745 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6748 Ybomb_w, FALSE, FALSE,
6749 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6752 Ybomb_wB, FALSE, TRUE,
6753 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6756 Ybomb_blank, FALSE, FALSE,
6757 EL_BOMB, ACTION_ACTIVATING, -1
6765 Xnut_pause, FALSE, FALSE,
6769 Xnut_fall, FALSE, FALSE,
6773 Ynut_s, FALSE, FALSE,
6774 EL_NUT, ACTION_FALLING, -1
6777 Ynut_sB, FALSE, TRUE,
6778 EL_NUT, ACTION_FALLING, -1
6781 Ynut_e, FALSE, FALSE,
6782 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6785 Ynut_eB, FALSE, TRUE,
6786 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6789 Ynut_w, FALSE, FALSE,
6790 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6793 Ynut_wB, FALSE, TRUE,
6794 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6797 Ynut_stone, FALSE, FALSE,
6798 EL_NUT, ACTION_BREAKING, -1
6802 Xspring, TRUE, FALSE,
6806 Xspring_pause, FALSE, FALSE,
6810 Xspring_e, TRUE, FALSE,
6811 EL_SPRING_RIGHT, -1, -1
6814 Xspring_w, TRUE, FALSE,
6815 EL_SPRING_LEFT, -1, -1
6818 Xspring_fall, FALSE, FALSE,
6822 Yspring_s, FALSE, FALSE,
6823 EL_SPRING, ACTION_FALLING, -1
6826 Yspring_sB, FALSE, TRUE,
6827 EL_SPRING, ACTION_FALLING, -1
6830 Yspring_e, FALSE, FALSE,
6831 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6834 Yspring_eB, FALSE, TRUE,
6835 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6838 Yspring_w, FALSE, FALSE,
6839 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6842 Yspring_wB, FALSE, TRUE,
6843 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6846 Yspring_alien_e, FALSE, FALSE,
6847 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6850 Yspring_alien_eB, FALSE, TRUE,
6851 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6854 Yspring_alien_w, FALSE, FALSE,
6855 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6858 Yspring_alien_wB, FALSE, TRUE,
6859 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6863 Xpush_emerald_e, FALSE, FALSE,
6864 EL_EMERALD, -1, MV_BIT_RIGHT
6867 Xpush_emerald_w, FALSE, FALSE,
6868 EL_EMERALD, -1, MV_BIT_LEFT
6871 Xpush_diamond_e, FALSE, FALSE,
6872 EL_DIAMOND, -1, MV_BIT_RIGHT
6875 Xpush_diamond_w, FALSE, FALSE,
6876 EL_DIAMOND, -1, MV_BIT_LEFT
6879 Xpush_stone_e, FALSE, FALSE,
6880 EL_ROCK, -1, MV_BIT_RIGHT
6883 Xpush_stone_w, FALSE, FALSE,
6884 EL_ROCK, -1, MV_BIT_LEFT
6887 Xpush_bomb_e, FALSE, FALSE,
6888 EL_BOMB, -1, MV_BIT_RIGHT
6891 Xpush_bomb_w, FALSE, FALSE,
6892 EL_BOMB, -1, MV_BIT_LEFT
6895 Xpush_nut_e, FALSE, FALSE,
6896 EL_NUT, -1, MV_BIT_RIGHT
6899 Xpush_nut_w, FALSE, FALSE,
6900 EL_NUT, -1, MV_BIT_LEFT
6903 Xpush_spring_e, FALSE, FALSE,
6904 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6907 Xpush_spring_w, FALSE, FALSE,
6908 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6912 Xdynamite, TRUE, FALSE,
6913 EL_EM_DYNAMITE, -1, -1
6916 Ydynamite_blank, FALSE, FALSE,
6917 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6920 Xdynamite_1, TRUE, FALSE,
6921 EL_EM_DYNAMITE_ACTIVE, -1, -1
6924 Xdynamite_2, FALSE, FALSE,
6925 EL_EM_DYNAMITE_ACTIVE, -1, -1
6928 Xdynamite_3, FALSE, FALSE,
6929 EL_EM_DYNAMITE_ACTIVE, -1, -1
6932 Xdynamite_4, FALSE, FALSE,
6933 EL_EM_DYNAMITE_ACTIVE, -1, -1
6937 Xkey_1, TRUE, FALSE,
6941 Xkey_2, TRUE, FALSE,
6945 Xkey_3, TRUE, FALSE,
6949 Xkey_4, TRUE, FALSE,
6953 Xkey_5, TRUE, FALSE,
6954 EL_EMC_KEY_5, -1, -1
6957 Xkey_6, TRUE, FALSE,
6958 EL_EMC_KEY_6, -1, -1
6961 Xkey_7, TRUE, FALSE,
6962 EL_EMC_KEY_7, -1, -1
6965 Xkey_8, TRUE, FALSE,
6966 EL_EMC_KEY_8, -1, -1
6970 Xdoor_1, TRUE, FALSE,
6971 EL_EM_GATE_1, -1, -1
6974 Xdoor_2, TRUE, FALSE,
6975 EL_EM_GATE_2, -1, -1
6978 Xdoor_3, TRUE, FALSE,
6979 EL_EM_GATE_3, -1, -1
6982 Xdoor_4, TRUE, FALSE,
6983 EL_EM_GATE_4, -1, -1
6986 Xdoor_5, TRUE, FALSE,
6987 EL_EMC_GATE_5, -1, -1
6990 Xdoor_6, TRUE, FALSE,
6991 EL_EMC_GATE_6, -1, -1
6994 Xdoor_7, TRUE, FALSE,
6995 EL_EMC_GATE_7, -1, -1
6998 Xdoor_8, TRUE, FALSE,
6999 EL_EMC_GATE_8, -1, -1
7003 Xfake_door_1, TRUE, FALSE,
7004 EL_EM_GATE_1_GRAY, -1, -1
7007 Xfake_door_2, TRUE, FALSE,
7008 EL_EM_GATE_2_GRAY, -1, -1
7011 Xfake_door_3, TRUE, FALSE,
7012 EL_EM_GATE_3_GRAY, -1, -1
7015 Xfake_door_4, TRUE, FALSE,
7016 EL_EM_GATE_4_GRAY, -1, -1
7019 Xfake_door_5, TRUE, FALSE,
7020 EL_EMC_GATE_5_GRAY, -1, -1
7023 Xfake_door_6, TRUE, FALSE,
7024 EL_EMC_GATE_6_GRAY, -1, -1
7027 Xfake_door_7, TRUE, FALSE,
7028 EL_EMC_GATE_7_GRAY, -1, -1
7031 Xfake_door_8, TRUE, FALSE,
7032 EL_EMC_GATE_8_GRAY, -1, -1
7036 Xballoon, TRUE, FALSE,
7040 Yballoon_n, FALSE, FALSE,
7041 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7044 Yballoon_nB, FALSE, TRUE,
7045 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7048 Yballoon_e, FALSE, FALSE,
7049 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7052 Yballoon_eB, FALSE, TRUE,
7053 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7056 Yballoon_s, FALSE, FALSE,
7057 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7060 Yballoon_sB, FALSE, TRUE,
7061 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7064 Yballoon_w, FALSE, FALSE,
7065 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7068 Yballoon_wB, FALSE, TRUE,
7069 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7073 Xball_1, TRUE, FALSE,
7074 EL_EMC_MAGIC_BALL, -1, -1
7077 Yball_1, FALSE, FALSE,
7078 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7081 Xball_2, FALSE, FALSE,
7082 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7085 Yball_2, FALSE, FALSE,
7086 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7089 Yball_blank, FALSE, FALSE,
7090 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7094 Xamoeba_1, TRUE, FALSE,
7095 EL_AMOEBA_DRY, ACTION_OTHER, -1
7098 Xamoeba_2, FALSE, FALSE,
7099 EL_AMOEBA_DRY, ACTION_OTHER, -1
7102 Xamoeba_3, FALSE, FALSE,
7103 EL_AMOEBA_DRY, ACTION_OTHER, -1
7106 Xamoeba_4, FALSE, FALSE,
7107 EL_AMOEBA_DRY, ACTION_OTHER, -1
7110 Xamoeba_5, TRUE, FALSE,
7111 EL_AMOEBA_WET, ACTION_OTHER, -1
7114 Xamoeba_6, FALSE, FALSE,
7115 EL_AMOEBA_WET, ACTION_OTHER, -1
7118 Xamoeba_7, FALSE, FALSE,
7119 EL_AMOEBA_WET, ACTION_OTHER, -1
7122 Xamoeba_8, FALSE, FALSE,
7123 EL_AMOEBA_WET, ACTION_OTHER, -1
7128 EL_AMOEBA_DROP, ACTION_GROWING, -1
7131 Xdrip_fall, FALSE, FALSE,
7132 EL_AMOEBA_DROP, -1, -1
7135 Xdrip_stretch, FALSE, FALSE,
7136 EL_AMOEBA_DROP, ACTION_FALLING, -1
7139 Xdrip_stretchB, FALSE, TRUE,
7140 EL_AMOEBA_DROP, ACTION_FALLING, -1
7143 Ydrip_1_s, FALSE, FALSE,
7144 EL_AMOEBA_DROP, ACTION_FALLING, -1
7147 Ydrip_1_sB, FALSE, TRUE,
7148 EL_AMOEBA_DROP, ACTION_FALLING, -1
7151 Ydrip_2_s, FALSE, FALSE,
7152 EL_AMOEBA_DROP, ACTION_FALLING, -1
7155 Ydrip_2_sB, FALSE, TRUE,
7156 EL_AMOEBA_DROP, ACTION_FALLING, -1
7160 Xwonderwall, TRUE, FALSE,
7161 EL_MAGIC_WALL, -1, -1
7164 Ywonderwall, FALSE, FALSE,
7165 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7169 Xwheel, TRUE, FALSE,
7170 EL_ROBOT_WHEEL, -1, -1
7173 Ywheel, FALSE, FALSE,
7174 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7178 Xswitch, TRUE, FALSE,
7179 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7182 Yswitch, FALSE, FALSE,
7183 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7187 Xbumper, TRUE, FALSE,
7188 EL_EMC_SPRING_BUMPER, -1, -1
7191 Ybumper, FALSE, FALSE,
7192 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7196 Xacid_nw, TRUE, FALSE,
7197 EL_ACID_POOL_TOPLEFT, -1, -1
7200 Xacid_ne, TRUE, FALSE,
7201 EL_ACID_POOL_TOPRIGHT, -1, -1
7204 Xacid_sw, TRUE, FALSE,
7205 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7208 Xacid_s, TRUE, FALSE,
7209 EL_ACID_POOL_BOTTOM, -1, -1
7212 Xacid_se, TRUE, FALSE,
7213 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7217 Xfake_blank, TRUE, FALSE,
7218 EL_INVISIBLE_WALL, -1, -1
7221 Yfake_blank, FALSE, FALSE,
7222 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7226 Xfake_grass, TRUE, FALSE,
7227 EL_EMC_FAKE_GRASS, -1, -1
7230 Yfake_grass, FALSE, FALSE,
7231 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7235 Xfake_amoeba, TRUE, FALSE,
7236 EL_EMC_DRIPPER, -1, -1
7239 Yfake_amoeba, FALSE, FALSE,
7240 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7244 Xlenses, TRUE, FALSE,
7245 EL_EMC_LENSES, -1, -1
7249 Xmagnify, TRUE, FALSE,
7250 EL_EMC_MAGNIFIER, -1, -1
7255 EL_QUICKSAND_EMPTY, -1, -1
7258 Xsand_stone, TRUE, FALSE,
7259 EL_QUICKSAND_FULL, -1, -1
7262 Xsand_stonein_1, FALSE, TRUE,
7263 EL_ROCK, ACTION_FILLING, -1
7266 Xsand_stonein_2, FALSE, TRUE,
7267 EL_ROCK, ACTION_FILLING, -1
7270 Xsand_stonein_3, FALSE, TRUE,
7271 EL_ROCK, ACTION_FILLING, -1
7274 Xsand_stonein_4, FALSE, TRUE,
7275 EL_ROCK, ACTION_FILLING, -1
7278 Xsand_sandstone_1, FALSE, FALSE,
7279 EL_QUICKSAND_FILLING, -1, -1
7282 Xsand_sandstone_2, FALSE, FALSE,
7283 EL_QUICKSAND_FILLING, -1, -1
7286 Xsand_sandstone_3, FALSE, FALSE,
7287 EL_QUICKSAND_FILLING, -1, -1
7290 Xsand_sandstone_4, FALSE, FALSE,
7291 EL_QUICKSAND_FILLING, -1, -1
7294 Xsand_stonesand_1, FALSE, FALSE,
7295 EL_QUICKSAND_EMPTYING, -1, -1
7298 Xsand_stonesand_2, FALSE, FALSE,
7299 EL_QUICKSAND_EMPTYING, -1, -1
7302 Xsand_stonesand_3, FALSE, FALSE,
7303 EL_QUICKSAND_EMPTYING, -1, -1
7306 Xsand_stonesand_4, FALSE, FALSE,
7307 EL_QUICKSAND_EMPTYING, -1, -1
7310 Xsand_stoneout_1, FALSE, FALSE,
7311 EL_ROCK, ACTION_EMPTYING, -1
7314 Xsand_stoneout_2, FALSE, FALSE,
7315 EL_ROCK, ACTION_EMPTYING, -1
7318 Xsand_stonesand_quickout_1, FALSE, FALSE,
7319 EL_QUICKSAND_EMPTYING, -1, -1
7322 Xsand_stonesand_quickout_2, FALSE, FALSE,
7323 EL_QUICKSAND_EMPTYING, -1, -1
7327 Xslide_ns, TRUE, FALSE,
7328 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7331 Yslide_ns_blank, FALSE, FALSE,
7332 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7335 Xslide_ew, TRUE, FALSE,
7336 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7339 Yslide_ew_blank, FALSE, FALSE,
7340 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7344 Xwind_n, TRUE, FALSE,
7345 EL_BALLOON_SWITCH_UP, -1, -1
7348 Xwind_e, TRUE, FALSE,
7349 EL_BALLOON_SWITCH_RIGHT, -1, -1
7352 Xwind_s, TRUE, FALSE,
7353 EL_BALLOON_SWITCH_DOWN, -1, -1
7356 Xwind_w, TRUE, FALSE,
7357 EL_BALLOON_SWITCH_LEFT, -1, -1
7360 Xwind_any, TRUE, FALSE,
7361 EL_BALLOON_SWITCH_ANY, -1, -1
7364 Xwind_stop, TRUE, FALSE,
7365 EL_BALLOON_SWITCH_NONE, -1, -1
7370 EL_EM_EXIT_CLOSED, -1, -1
7373 Xexit_1, TRUE, FALSE,
7374 EL_EM_EXIT_OPEN, -1, -1
7377 Xexit_2, FALSE, FALSE,
7378 EL_EM_EXIT_OPEN, -1, -1
7381 Xexit_3, FALSE, FALSE,
7382 EL_EM_EXIT_OPEN, -1, -1
7386 Xpause, FALSE, FALSE,
7391 Xwall_1, TRUE, FALSE,
7395 Xwall_2, TRUE, FALSE,
7396 EL_EMC_WALL_14, -1, -1
7399 Xwall_3, TRUE, FALSE,
7400 EL_EMC_WALL_15, -1, -1
7403 Xwall_4, TRUE, FALSE,
7404 EL_EMC_WALL_16, -1, -1
7408 Xroundwall_1, TRUE, FALSE,
7409 EL_WALL_SLIPPERY, -1, -1
7412 Xroundwall_2, TRUE, FALSE,
7413 EL_EMC_WALL_SLIPPERY_2, -1, -1
7416 Xroundwall_3, TRUE, FALSE,
7417 EL_EMC_WALL_SLIPPERY_3, -1, -1
7420 Xroundwall_4, TRUE, FALSE,
7421 EL_EMC_WALL_SLIPPERY_4, -1, -1
7425 Xsteel_1, TRUE, FALSE,
7426 EL_STEELWALL, -1, -1
7429 Xsteel_2, TRUE, FALSE,
7430 EL_EMC_STEELWALL_2, -1, -1
7433 Xsteel_3, TRUE, FALSE,
7434 EL_EMC_STEELWALL_3, -1, -1
7437 Xsteel_4, TRUE, FALSE,
7438 EL_EMC_STEELWALL_4, -1, -1
7442 Xdecor_1, TRUE, FALSE,
7443 EL_EMC_WALL_8, -1, -1
7446 Xdecor_2, TRUE, FALSE,
7447 EL_EMC_WALL_6, -1, -1
7450 Xdecor_3, TRUE, FALSE,
7451 EL_EMC_WALL_4, -1, -1
7454 Xdecor_4, TRUE, FALSE,
7455 EL_EMC_WALL_7, -1, -1
7458 Xdecor_5, TRUE, FALSE,
7459 EL_EMC_WALL_5, -1, -1
7462 Xdecor_6, TRUE, FALSE,
7463 EL_EMC_WALL_9, -1, -1
7466 Xdecor_7, TRUE, FALSE,
7467 EL_EMC_WALL_10, -1, -1
7470 Xdecor_8, TRUE, FALSE,
7471 EL_EMC_WALL_1, -1, -1
7474 Xdecor_9, TRUE, FALSE,
7475 EL_EMC_WALL_2, -1, -1
7478 Xdecor_10, TRUE, FALSE,
7479 EL_EMC_WALL_3, -1, -1
7482 Xdecor_11, TRUE, FALSE,
7483 EL_EMC_WALL_11, -1, -1
7486 Xdecor_12, TRUE, FALSE,
7487 EL_EMC_WALL_12, -1, -1
7491 Xalpha_0, TRUE, FALSE,
7492 EL_CHAR('0'), -1, -1
7495 Xalpha_1, TRUE, FALSE,
7496 EL_CHAR('1'), -1, -1
7499 Xalpha_2, TRUE, FALSE,
7500 EL_CHAR('2'), -1, -1
7503 Xalpha_3, TRUE, FALSE,
7504 EL_CHAR('3'), -1, -1
7507 Xalpha_4, TRUE, FALSE,
7508 EL_CHAR('4'), -1, -1
7511 Xalpha_5, TRUE, FALSE,
7512 EL_CHAR('5'), -1, -1
7515 Xalpha_6, TRUE, FALSE,
7516 EL_CHAR('6'), -1, -1
7519 Xalpha_7, TRUE, FALSE,
7520 EL_CHAR('7'), -1, -1
7523 Xalpha_8, TRUE, FALSE,
7524 EL_CHAR('8'), -1, -1
7527 Xalpha_9, TRUE, FALSE,
7528 EL_CHAR('9'), -1, -1
7531 Xalpha_excla, TRUE, FALSE,
7532 EL_CHAR('!'), -1, -1
7535 Xalpha_apost, TRUE, FALSE,
7536 EL_CHAR('\''), -1, -1
7539 Xalpha_comma, TRUE, FALSE,
7540 EL_CHAR(','), -1, -1
7543 Xalpha_minus, TRUE, FALSE,
7544 EL_CHAR('-'), -1, -1
7547 Xalpha_perio, TRUE, FALSE,
7548 EL_CHAR('.'), -1, -1
7551 Xalpha_colon, TRUE, FALSE,
7552 EL_CHAR(':'), -1, -1
7555 Xalpha_quest, TRUE, FALSE,
7556 EL_CHAR('?'), -1, -1
7559 Xalpha_a, TRUE, FALSE,
7560 EL_CHAR('A'), -1, -1
7563 Xalpha_b, TRUE, FALSE,
7564 EL_CHAR('B'), -1, -1
7567 Xalpha_c, TRUE, FALSE,
7568 EL_CHAR('C'), -1, -1
7571 Xalpha_d, TRUE, FALSE,
7572 EL_CHAR('D'), -1, -1
7575 Xalpha_e, TRUE, FALSE,
7576 EL_CHAR('E'), -1, -1
7579 Xalpha_f, TRUE, FALSE,
7580 EL_CHAR('F'), -1, -1
7583 Xalpha_g, TRUE, FALSE,
7584 EL_CHAR('G'), -1, -1
7587 Xalpha_h, TRUE, FALSE,
7588 EL_CHAR('H'), -1, -1
7591 Xalpha_i, TRUE, FALSE,
7592 EL_CHAR('I'), -1, -1
7595 Xalpha_j, TRUE, FALSE,
7596 EL_CHAR('J'), -1, -1
7599 Xalpha_k, TRUE, FALSE,
7600 EL_CHAR('K'), -1, -1
7603 Xalpha_l, TRUE, FALSE,
7604 EL_CHAR('L'), -1, -1
7607 Xalpha_m, TRUE, FALSE,
7608 EL_CHAR('M'), -1, -1
7611 Xalpha_n, TRUE, FALSE,
7612 EL_CHAR('N'), -1, -1
7615 Xalpha_o, TRUE, FALSE,
7616 EL_CHAR('O'), -1, -1
7619 Xalpha_p, TRUE, FALSE,
7620 EL_CHAR('P'), -1, -1
7623 Xalpha_q, TRUE, FALSE,
7624 EL_CHAR('Q'), -1, -1
7627 Xalpha_r, TRUE, FALSE,
7628 EL_CHAR('R'), -1, -1
7631 Xalpha_s, TRUE, FALSE,
7632 EL_CHAR('S'), -1, -1
7635 Xalpha_t, TRUE, FALSE,
7636 EL_CHAR('T'), -1, -1
7639 Xalpha_u, TRUE, FALSE,
7640 EL_CHAR('U'), -1, -1
7643 Xalpha_v, TRUE, FALSE,
7644 EL_CHAR('V'), -1, -1
7647 Xalpha_w, TRUE, FALSE,
7648 EL_CHAR('W'), -1, -1
7651 Xalpha_x, TRUE, FALSE,
7652 EL_CHAR('X'), -1, -1
7655 Xalpha_y, TRUE, FALSE,
7656 EL_CHAR('Y'), -1, -1
7659 Xalpha_z, TRUE, FALSE,
7660 EL_CHAR('Z'), -1, -1
7663 Xalpha_arrow_e, TRUE, FALSE,
7664 EL_CHAR('>'), -1, -1
7667 Xalpha_arrow_w, TRUE, FALSE,
7668 EL_CHAR('<'), -1, -1
7671 Xalpha_copyr, TRUE, FALSE,
7672 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7676 Ykey_1_blank, FALSE, FALSE,
7677 EL_EM_KEY_1, ACTION_COLLECTING, -1
7680 Ykey_2_blank, FALSE, FALSE,
7681 EL_EM_KEY_2, ACTION_COLLECTING, -1
7684 Ykey_3_blank, FALSE, FALSE,
7685 EL_EM_KEY_3, ACTION_COLLECTING, -1
7688 Ykey_4_blank, FALSE, FALSE,
7689 EL_EM_KEY_4, ACTION_COLLECTING, -1
7692 Ykey_5_blank, FALSE, FALSE,
7693 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7696 Ykey_6_blank, FALSE, FALSE,
7697 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7700 Ykey_7_blank, FALSE, FALSE,
7701 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7704 Ykey_8_blank, FALSE, FALSE,
7705 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7708 Ylenses_blank, FALSE, FALSE,
7709 EL_EMC_LENSES, ACTION_COLLECTING, -1
7712 Ymagnify_blank, FALSE, FALSE,
7713 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7716 Ygrass_blank, FALSE, FALSE,
7717 EL_EMC_GRASS, ACTION_SNAPPING, -1
7720 Ydirt_blank, FALSE, FALSE,
7721 EL_SAND, ACTION_SNAPPING, -1
7730 static struct Mapping_EM_to_RND_player
7739 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7743 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7747 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7751 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7755 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7759 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7763 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7767 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7771 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7775 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7779 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7783 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7787 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7791 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7795 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7799 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7803 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7807 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7811 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7815 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7819 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7823 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7827 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7831 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7835 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7839 EL_PLAYER_1, ACTION_DEFAULT, -1,
7843 EL_PLAYER_2, ACTION_DEFAULT, -1,
7847 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7851 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7855 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7859 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7863 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7867 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7871 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7875 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7879 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7883 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7887 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7891 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7895 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7899 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7903 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7907 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7911 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7915 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7919 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7923 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7927 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7931 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7935 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7939 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7943 EL_PLAYER_3, ACTION_DEFAULT, -1,
7947 EL_PLAYER_4, ACTION_DEFAULT, -1,
7956 int map_element_RND_to_EM_cave(int element_rnd)
7958 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7959 static boolean mapping_initialized = FALSE;
7961 if (!mapping_initialized)
7965 // return "Xalpha_quest" for all undefined elements in mapping array
7966 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7967 mapping_RND_to_EM[i] = Xalpha_quest;
7969 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7970 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7971 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7972 em_object_mapping_list[i].element_em;
7974 mapping_initialized = TRUE;
7977 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7979 Warn("invalid RND level element %d", element_rnd);
7984 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7987 int map_element_EM_to_RND_cave(int element_em_cave)
7989 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7990 static boolean mapping_initialized = FALSE;
7992 if (!mapping_initialized)
7996 // return "EL_UNKNOWN" for all undefined elements in mapping array
7997 for (i = 0; i < GAME_TILE_MAX; i++)
7998 mapping_EM_to_RND[i] = EL_UNKNOWN;
8000 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8001 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8002 em_object_mapping_list[i].element_rnd;
8004 mapping_initialized = TRUE;
8007 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8009 Warn("invalid EM cave element %d", element_em_cave);
8014 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8017 int map_element_EM_to_RND_game(int element_em_game)
8019 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8020 static boolean mapping_initialized = FALSE;
8022 if (!mapping_initialized)
8026 // return "EL_UNKNOWN" for all undefined elements in mapping array
8027 for (i = 0; i < GAME_TILE_MAX; i++)
8028 mapping_EM_to_RND[i] = EL_UNKNOWN;
8030 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8031 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8032 em_object_mapping_list[i].element_rnd;
8034 mapping_initialized = TRUE;
8037 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8039 Warn("invalid EM game element %d", element_em_game);
8044 return mapping_EM_to_RND[element_em_game];
8047 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8049 struct LevelInfo_EM *level_em = level->native_em_level;
8050 struct CAVE *cav = level_em->cav;
8053 for (i = 0; i < GAME_TILE_MAX; i++)
8054 cav->android_array[i] = Cblank;
8056 for (i = 0; i < level->num_android_clone_elements; i++)
8058 int element_rnd = level->android_clone_element[i];
8059 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8061 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8062 if (em_object_mapping_list[j].element_rnd == element_rnd)
8063 cav->android_array[em_object_mapping_list[j].element_em] =
8068 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8070 struct LevelInfo_EM *level_em = level->native_em_level;
8071 struct CAVE *cav = level_em->cav;
8074 level->num_android_clone_elements = 0;
8076 for (i = 0; i < GAME_TILE_MAX; i++)
8078 int element_em_cave = cav->android_array[i];
8080 boolean element_found = FALSE;
8082 if (element_em_cave == Cblank)
8085 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8087 for (j = 0; j < level->num_android_clone_elements; j++)
8088 if (level->android_clone_element[j] == element_rnd)
8089 element_found = TRUE;
8093 level->android_clone_element[level->num_android_clone_elements++] =
8096 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8101 if (level->num_android_clone_elements == 0)
8103 level->num_android_clone_elements = 1;
8104 level->android_clone_element[0] = EL_EMPTY;
8108 int map_direction_RND_to_EM(int direction)
8110 return (direction == MV_UP ? 0 :
8111 direction == MV_RIGHT ? 1 :
8112 direction == MV_DOWN ? 2 :
8113 direction == MV_LEFT ? 3 :
8117 int map_direction_EM_to_RND(int direction)
8119 return (direction == 0 ? MV_UP :
8120 direction == 1 ? MV_RIGHT :
8121 direction == 2 ? MV_DOWN :
8122 direction == 3 ? MV_LEFT :
8126 int map_element_RND_to_SP(int element_rnd)
8128 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8130 if (element_rnd >= EL_SP_START &&
8131 element_rnd <= EL_SP_END)
8132 element_sp = element_rnd - EL_SP_START;
8133 else if (element_rnd == EL_EMPTY_SPACE)
8135 else if (element_rnd == EL_INVISIBLE_WALL)
8141 int map_element_SP_to_RND(int element_sp)
8143 int element_rnd = EL_UNKNOWN;
8145 if (element_sp >= 0x00 &&
8147 element_rnd = EL_SP_START + element_sp;
8148 else if (element_sp == 0x28)
8149 element_rnd = EL_INVISIBLE_WALL;
8154 int map_action_SP_to_RND(int action_sp)
8158 case actActive: return ACTION_ACTIVE;
8159 case actImpact: return ACTION_IMPACT;
8160 case actExploding: return ACTION_EXPLODING;
8161 case actDigging: return ACTION_DIGGING;
8162 case actSnapping: return ACTION_SNAPPING;
8163 case actCollecting: return ACTION_COLLECTING;
8164 case actPassing: return ACTION_PASSING;
8165 case actPushing: return ACTION_PUSHING;
8166 case actDropping: return ACTION_DROPPING;
8168 default: return ACTION_DEFAULT;
8172 int map_element_RND_to_MM(int element_rnd)
8174 return (element_rnd >= EL_MM_START_1 &&
8175 element_rnd <= EL_MM_END_1 ?
8176 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8178 element_rnd >= EL_MM_START_2 &&
8179 element_rnd <= EL_MM_END_2 ?
8180 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8182 element_rnd >= EL_MM_START_3 &&
8183 element_rnd <= EL_MM_END_3 ?
8184 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8186 element_rnd >= EL_CHAR_START &&
8187 element_rnd <= EL_CHAR_END ?
8188 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8190 element_rnd >= EL_MM_RUNTIME_START &&
8191 element_rnd <= EL_MM_RUNTIME_END ?
8192 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8194 EL_MM_EMPTY_NATIVE);
8197 int map_element_MM_to_RND(int element_mm)
8199 return (element_mm == EL_MM_EMPTY_NATIVE ||
8200 element_mm == EL_DF_EMPTY_NATIVE ?
8203 element_mm >= EL_MM_START_1_NATIVE &&
8204 element_mm <= EL_MM_END_1_NATIVE ?
8205 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8207 element_mm >= EL_MM_START_2_NATIVE &&
8208 element_mm <= EL_MM_END_2_NATIVE ?
8209 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8211 element_mm >= EL_MM_START_3_NATIVE &&
8212 element_mm <= EL_MM_END_3_NATIVE ?
8213 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8215 element_mm >= EL_MM_CHAR_START_NATIVE &&
8216 element_mm <= EL_MM_CHAR_END_NATIVE ?
8217 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8219 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8220 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8221 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8226 int map_action_MM_to_RND(int action_mm)
8228 // all MM actions are defined to exactly match their RND counterparts
8232 int map_sound_MM_to_RND(int sound_mm)
8236 case SND_MM_GAME_LEVELTIME_CHARGING:
8237 return SND_GAME_LEVELTIME_CHARGING;
8239 case SND_MM_GAME_HEALTH_CHARGING:
8240 return SND_GAME_HEALTH_CHARGING;
8243 return SND_UNDEFINED;
8247 int map_mm_wall_element(int element)
8249 return (element >= EL_MM_STEEL_WALL_START &&
8250 element <= EL_MM_STEEL_WALL_END ?
8253 element >= EL_MM_WOODEN_WALL_START &&
8254 element <= EL_MM_WOODEN_WALL_END ?
8257 element >= EL_MM_ICE_WALL_START &&
8258 element <= EL_MM_ICE_WALL_END ?
8261 element >= EL_MM_AMOEBA_WALL_START &&
8262 element <= EL_MM_AMOEBA_WALL_END ?
8265 element >= EL_DF_STEEL_WALL_START &&
8266 element <= EL_DF_STEEL_WALL_END ?
8269 element >= EL_DF_WOODEN_WALL_START &&
8270 element <= EL_DF_WOODEN_WALL_END ?
8276 int map_mm_wall_element_editor(int element)
8280 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8281 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8282 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8283 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8284 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8285 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8287 default: return element;
8291 int get_next_element(int element)
8295 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8296 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8297 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8298 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8299 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8300 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8301 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8302 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8303 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8304 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8305 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8307 default: return element;
8311 int el2img_mm(int element_mm)
8313 return el2img(map_element_MM_to_RND(element_mm));
8316 int el_act2img_mm(int element_mm, int action)
8318 return el_act2img(map_element_MM_to_RND(element_mm), action);
8321 int el_act_dir2img(int element, int action, int direction)
8323 element = GFX_ELEMENT(element);
8324 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8326 // direction_graphic[][] == graphic[] for undefined direction graphics
8327 return element_info[element].direction_graphic[action][direction];
8330 static int el_act_dir2crm(int element, int action, int direction)
8332 element = GFX_ELEMENT(element);
8333 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8335 // direction_graphic[][] == graphic[] for undefined direction graphics
8336 return element_info[element].direction_crumbled[action][direction];
8339 int el_act2img(int element, int action)
8341 element = GFX_ELEMENT(element);
8343 return element_info[element].graphic[action];
8346 int el_act2crm(int element, int action)
8348 element = GFX_ELEMENT(element);
8350 return element_info[element].crumbled[action];
8353 int el_dir2img(int element, int direction)
8355 element = GFX_ELEMENT(element);
8357 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8360 int el2baseimg(int element)
8362 return element_info[element].graphic[ACTION_DEFAULT];
8365 int el2img(int element)
8367 element = GFX_ELEMENT(element);
8369 return element_info[element].graphic[ACTION_DEFAULT];
8372 int el2edimg(int element)
8374 element = GFX_ELEMENT(element);
8376 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8379 int el2preimg(int element)
8381 element = GFX_ELEMENT(element);
8383 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8386 int el2panelimg(int element)
8388 element = GFX_ELEMENT(element);
8390 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8393 int font2baseimg(int font_nr)
8395 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8398 int getBeltNrFromBeltElement(int element)
8400 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8401 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8402 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8405 int getBeltNrFromBeltActiveElement(int element)
8407 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8408 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8409 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8412 int getBeltNrFromBeltSwitchElement(int element)
8414 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8415 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8416 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8419 int getBeltDirNrFromBeltElement(int element)
8421 static int belt_base_element[4] =
8423 EL_CONVEYOR_BELT_1_LEFT,
8424 EL_CONVEYOR_BELT_2_LEFT,
8425 EL_CONVEYOR_BELT_3_LEFT,
8426 EL_CONVEYOR_BELT_4_LEFT
8429 int belt_nr = getBeltNrFromBeltElement(element);
8430 int belt_dir_nr = element - belt_base_element[belt_nr];
8432 return (belt_dir_nr % 3);
8435 int getBeltDirNrFromBeltSwitchElement(int element)
8437 static int belt_base_element[4] =
8439 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8440 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8441 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8442 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8445 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8446 int belt_dir_nr = element - belt_base_element[belt_nr];
8448 return (belt_dir_nr % 3);
8451 int getBeltDirFromBeltElement(int element)
8453 static int belt_move_dir[3] =
8460 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8462 return belt_move_dir[belt_dir_nr];
8465 int getBeltDirFromBeltSwitchElement(int element)
8467 static int belt_move_dir[3] =
8474 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8476 return belt_move_dir[belt_dir_nr];
8479 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8481 static int belt_base_element[4] =
8483 EL_CONVEYOR_BELT_1_LEFT,
8484 EL_CONVEYOR_BELT_2_LEFT,
8485 EL_CONVEYOR_BELT_3_LEFT,
8486 EL_CONVEYOR_BELT_4_LEFT
8489 return belt_base_element[belt_nr] + belt_dir_nr;
8492 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8494 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8496 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8499 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8501 static int belt_base_element[4] =
8503 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8504 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8505 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8506 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8509 return belt_base_element[belt_nr] + belt_dir_nr;
8512 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8514 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8516 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8519 boolean swapTiles_EM(boolean is_pre_emc_cave)
8521 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8524 boolean getTeamMode_EM(void)
8526 return game.team_mode || network_playing;
8529 boolean isActivePlayer_EM(int player_nr)
8531 return stored_player[player_nr].active;
8534 unsigned int InitRND(int seed)
8536 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8537 return InitEngineRandom_EM(seed);
8538 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8539 return InitEngineRandom_SP(seed);
8540 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8541 return InitEngineRandom_MM(seed);
8543 return InitEngineRandom_RND(seed);
8546 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8547 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8549 static int get_effective_element_EM(int tile, int frame_em)
8551 int element = object_mapping[tile].element_rnd;
8552 int action = object_mapping[tile].action;
8553 boolean is_backside = object_mapping[tile].is_backside;
8554 boolean action_removing = (action == ACTION_DIGGING ||
8555 action == ACTION_SNAPPING ||
8556 action == ACTION_COLLECTING);
8564 return (frame_em > 5 ? EL_EMPTY : element);
8570 else // frame_em == 7
8581 case Ydiamond_stone:
8585 case Xdrip_stretchB:
8601 case Ymagnify_blank:
8604 case Xsand_stonein_1:
8605 case Xsand_stonein_2:
8606 case Xsand_stonein_3:
8607 case Xsand_stonein_4:
8611 return (is_backside || action_removing ? EL_EMPTY : element);
8616 static boolean check_linear_animation_EM(int tile)
8620 case Xsand_stonesand_1:
8621 case Xsand_stonesand_quickout_1:
8622 case Xsand_sandstone_1:
8623 case Xsand_stonein_1:
8624 case Xsand_stoneout_1:
8652 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8653 boolean has_crumbled_graphics,
8654 int crumbled, int sync_frame)
8656 // if element can be crumbled, but certain action graphics are just empty
8657 // space (like instantly snapping sand to empty space in 1 frame), do not
8658 // treat these empty space graphics as crumbled graphics in EMC engine
8659 if (crumbled == IMG_EMPTY_SPACE)
8660 has_crumbled_graphics = FALSE;
8662 if (has_crumbled_graphics)
8664 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8665 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8666 g_crumbled->anim_delay,
8667 g_crumbled->anim_mode,
8668 g_crumbled->anim_start_frame,
8671 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8672 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8674 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8675 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8677 g_em->has_crumbled_graphics = TRUE;
8681 g_em->crumbled_bitmap = NULL;
8682 g_em->crumbled_src_x = 0;
8683 g_em->crumbled_src_y = 0;
8684 g_em->crumbled_border_size = 0;
8685 g_em->crumbled_tile_size = 0;
8687 g_em->has_crumbled_graphics = FALSE;
8692 void ResetGfxAnimation_EM(int x, int y, int tile)
8698 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8699 int tile, int frame_em, int x, int y)
8701 int action = object_mapping[tile].action;
8702 int direction = object_mapping[tile].direction;
8703 int effective_element = get_effective_element_EM(tile, frame_em);
8704 int graphic = (direction == MV_NONE ?
8705 el_act2img(effective_element, action) :
8706 el_act_dir2img(effective_element, action, direction));
8707 struct GraphicInfo *g = &graphic_info[graphic];
8709 boolean action_removing = (action == ACTION_DIGGING ||
8710 action == ACTION_SNAPPING ||
8711 action == ACTION_COLLECTING);
8712 boolean action_moving = (action == ACTION_FALLING ||
8713 action == ACTION_MOVING ||
8714 action == ACTION_PUSHING ||
8715 action == ACTION_EATING ||
8716 action == ACTION_FILLING ||
8717 action == ACTION_EMPTYING);
8718 boolean action_falling = (action == ACTION_FALLING ||
8719 action == ACTION_FILLING ||
8720 action == ACTION_EMPTYING);
8722 // special case: graphic uses "2nd movement tile" and has defined
8723 // 7 frames for movement animation (or less) => use default graphic
8724 // for last (8th) frame which ends the movement animation
8725 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8727 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8728 graphic = (direction == MV_NONE ?
8729 el_act2img(effective_element, action) :
8730 el_act_dir2img(effective_element, action, direction));
8732 g = &graphic_info[graphic];
8735 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8739 else if (action_moving)
8741 boolean is_backside = object_mapping[tile].is_backside;
8745 int direction = object_mapping[tile].direction;
8746 int move_dir = (action_falling ? MV_DOWN : direction);
8751 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8752 if (g->double_movement && frame_em == 0)
8756 if (move_dir == MV_LEFT)
8757 GfxFrame[x - 1][y] = GfxFrame[x][y];
8758 else if (move_dir == MV_RIGHT)
8759 GfxFrame[x + 1][y] = GfxFrame[x][y];
8760 else if (move_dir == MV_UP)
8761 GfxFrame[x][y - 1] = GfxFrame[x][y];
8762 else if (move_dir == MV_DOWN)
8763 GfxFrame[x][y + 1] = GfxFrame[x][y];
8770 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8771 if (tile == Xsand_stonesand_quickout_1 ||
8772 tile == Xsand_stonesand_quickout_2)
8776 if (graphic_info[graphic].anim_global_sync)
8777 sync_frame = FrameCounter;
8778 else if (graphic_info[graphic].anim_global_anim_sync)
8779 sync_frame = getGlobalAnimSyncFrame();
8780 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8781 sync_frame = GfxFrame[x][y];
8783 sync_frame = 0; // playfield border (pseudo steel)
8785 SetRandomAnimationValue(x, y);
8787 int frame = getAnimationFrame(g->anim_frames,
8790 g->anim_start_frame,
8793 g_em->unique_identifier =
8794 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8797 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8798 int tile, int frame_em, int x, int y)
8800 int action = object_mapping[tile].action;
8801 int direction = object_mapping[tile].direction;
8802 boolean is_backside = object_mapping[tile].is_backside;
8803 int effective_element = get_effective_element_EM(tile, frame_em);
8804 int effective_action = action;
8805 int graphic = (direction == MV_NONE ?
8806 el_act2img(effective_element, effective_action) :
8807 el_act_dir2img(effective_element, effective_action,
8809 int crumbled = (direction == MV_NONE ?
8810 el_act2crm(effective_element, effective_action) :
8811 el_act_dir2crm(effective_element, effective_action,
8813 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8814 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8815 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8816 struct GraphicInfo *g = &graphic_info[graphic];
8819 // special case: graphic uses "2nd movement tile" and has defined
8820 // 7 frames for movement animation (or less) => use default graphic
8821 // for last (8th) frame which ends the movement animation
8822 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8824 effective_action = ACTION_DEFAULT;
8825 graphic = (direction == MV_NONE ?
8826 el_act2img(effective_element, effective_action) :
8827 el_act_dir2img(effective_element, effective_action,
8829 crumbled = (direction == MV_NONE ?
8830 el_act2crm(effective_element, effective_action) :
8831 el_act_dir2crm(effective_element, effective_action,
8834 g = &graphic_info[graphic];
8837 if (graphic_info[graphic].anim_global_sync)
8838 sync_frame = FrameCounter;
8839 else if (graphic_info[graphic].anim_global_anim_sync)
8840 sync_frame = getGlobalAnimSyncFrame();
8841 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8842 sync_frame = GfxFrame[x][y];
8844 sync_frame = 0; // playfield border (pseudo steel)
8846 SetRandomAnimationValue(x, y);
8848 int frame = getAnimationFrame(g->anim_frames,
8851 g->anim_start_frame,
8854 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8855 g->double_movement && is_backside);
8857 // (updating the "crumbled" graphic definitions is probably not really needed,
8858 // as animations for crumbled graphics can't be longer than one EMC cycle)
8859 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8863 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8864 int player_nr, int anim, int frame_em)
8866 int element = player_mapping[player_nr][anim].element_rnd;
8867 int action = player_mapping[player_nr][anim].action;
8868 int direction = player_mapping[player_nr][anim].direction;
8869 int graphic = (direction == MV_NONE ?
8870 el_act2img(element, action) :
8871 el_act_dir2img(element, action, direction));
8872 struct GraphicInfo *g = &graphic_info[graphic];
8875 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8877 stored_player[player_nr].StepFrame = frame_em;
8879 sync_frame = stored_player[player_nr].Frame;
8881 int frame = getAnimationFrame(g->anim_frames,
8884 g->anim_start_frame,
8887 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8888 &g_em->src_x, &g_em->src_y, FALSE);
8891 void InitGraphicInfo_EM(void)
8895 // always start with reliable default values
8896 for (i = 0; i < GAME_TILE_MAX; i++)
8898 object_mapping[i].element_rnd = EL_UNKNOWN;
8899 object_mapping[i].is_backside = FALSE;
8900 object_mapping[i].action = ACTION_DEFAULT;
8901 object_mapping[i].direction = MV_NONE;
8904 // always start with reliable default values
8905 for (p = 0; p < MAX_PLAYERS; p++)
8907 for (i = 0; i < PLY_MAX; i++)
8909 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8910 player_mapping[p][i].action = ACTION_DEFAULT;
8911 player_mapping[p][i].direction = MV_NONE;
8915 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8917 int e = em_object_mapping_list[i].element_em;
8919 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8920 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8922 if (em_object_mapping_list[i].action != -1)
8923 object_mapping[e].action = em_object_mapping_list[i].action;
8925 if (em_object_mapping_list[i].direction != -1)
8926 object_mapping[e].direction =
8927 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8930 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8932 int a = em_player_mapping_list[i].action_em;
8933 int p = em_player_mapping_list[i].player_nr;
8935 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8937 if (em_player_mapping_list[i].action != -1)
8938 player_mapping[p][a].action = em_player_mapping_list[i].action;
8940 if (em_player_mapping_list[i].direction != -1)
8941 player_mapping[p][a].direction =
8942 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8945 for (i = 0; i < GAME_TILE_MAX; i++)
8947 int element = object_mapping[i].element_rnd;
8948 int action = object_mapping[i].action;
8949 int direction = object_mapping[i].direction;
8950 boolean is_backside = object_mapping[i].is_backside;
8951 boolean action_exploding = ((action == ACTION_EXPLODING ||
8952 action == ACTION_SMASHED_BY_ROCK ||
8953 action == ACTION_SMASHED_BY_SPRING) &&
8954 element != EL_DIAMOND);
8955 boolean action_active = (action == ACTION_ACTIVE);
8956 boolean action_other = (action == ACTION_OTHER);
8958 for (j = 0; j < 8; j++)
8960 int effective_element = get_effective_element_EM(i, j);
8961 int effective_action = (j < 7 ? action :
8962 i == Xdrip_stretch ? action :
8963 i == Xdrip_stretchB ? action :
8964 i == Ydrip_1_s ? action :
8965 i == Ydrip_1_sB ? action :
8966 i == Yball_1 ? action :
8967 i == Xball_2 ? action :
8968 i == Yball_2 ? action :
8969 i == Yball_blank ? action :
8970 i == Ykey_1_blank ? action :
8971 i == Ykey_2_blank ? action :
8972 i == Ykey_3_blank ? action :
8973 i == Ykey_4_blank ? action :
8974 i == Ykey_5_blank ? action :
8975 i == Ykey_6_blank ? action :
8976 i == Ykey_7_blank ? action :
8977 i == Ykey_8_blank ? action :
8978 i == Ylenses_blank ? action :
8979 i == Ymagnify_blank ? action :
8980 i == Ygrass_blank ? action :
8981 i == Ydirt_blank ? action :
8982 i == Xsand_stonein_1 ? action :
8983 i == Xsand_stonein_2 ? action :
8984 i == Xsand_stonein_3 ? action :
8985 i == Xsand_stonein_4 ? action :
8986 i == Xsand_stoneout_1 ? action :
8987 i == Xsand_stoneout_2 ? action :
8988 i == Xboom_android ? ACTION_EXPLODING :
8989 action_exploding ? ACTION_EXPLODING :
8990 action_active ? action :
8991 action_other ? action :
8993 int graphic = (el_act_dir2img(effective_element, effective_action,
8995 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8997 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8998 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8999 boolean has_action_graphics = (graphic != base_graphic);
9000 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9001 struct GraphicInfo *g = &graphic_info[graphic];
9002 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9005 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9006 boolean special_animation = (action != ACTION_DEFAULT &&
9007 g->anim_frames == 3 &&
9008 g->anim_delay == 2 &&
9009 g->anim_mode & ANIM_LINEAR);
9010 int sync_frame = (i == Xdrip_stretch ? 7 :
9011 i == Xdrip_stretchB ? 7 :
9012 i == Ydrip_2_s ? j + 8 :
9013 i == Ydrip_2_sB ? j + 8 :
9022 i == Xfake_acid_1 ? 0 :
9023 i == Xfake_acid_2 ? 10 :
9024 i == Xfake_acid_3 ? 20 :
9025 i == Xfake_acid_4 ? 30 :
9026 i == Xfake_acid_5 ? 40 :
9027 i == Xfake_acid_6 ? 50 :
9028 i == Xfake_acid_7 ? 60 :
9029 i == Xfake_acid_8 ? 70 :
9030 i == Xfake_acid_1_player ? 0 :
9031 i == Xfake_acid_2_player ? 10 :
9032 i == Xfake_acid_3_player ? 20 :
9033 i == Xfake_acid_4_player ? 30 :
9034 i == Xfake_acid_5_player ? 40 :
9035 i == Xfake_acid_6_player ? 50 :
9036 i == Xfake_acid_7_player ? 60 :
9037 i == Xfake_acid_8_player ? 70 :
9039 i == Yball_2 ? j + 8 :
9040 i == Yball_blank ? j + 1 :
9041 i == Ykey_1_blank ? j + 1 :
9042 i == Ykey_2_blank ? j + 1 :
9043 i == Ykey_3_blank ? j + 1 :
9044 i == Ykey_4_blank ? j + 1 :
9045 i == Ykey_5_blank ? j + 1 :
9046 i == Ykey_6_blank ? j + 1 :
9047 i == Ykey_7_blank ? j + 1 :
9048 i == Ykey_8_blank ? j + 1 :
9049 i == Ylenses_blank ? j + 1 :
9050 i == Ymagnify_blank ? j + 1 :
9051 i == Ygrass_blank ? j + 1 :
9052 i == Ydirt_blank ? j + 1 :
9053 i == Xamoeba_1 ? 0 :
9054 i == Xamoeba_2 ? 1 :
9055 i == Xamoeba_3 ? 2 :
9056 i == Xamoeba_4 ? 3 :
9057 i == Xamoeba_5 ? 0 :
9058 i == Xamoeba_6 ? 1 :
9059 i == Xamoeba_7 ? 2 :
9060 i == Xamoeba_8 ? 3 :
9061 i == Xexit_2 ? j + 8 :
9062 i == Xexit_3 ? j + 16 :
9063 i == Xdynamite_1 ? 0 :
9064 i == Xdynamite_2 ? 8 :
9065 i == Xdynamite_3 ? 16 :
9066 i == Xdynamite_4 ? 24 :
9067 i == Xsand_stonein_1 ? j + 1 :
9068 i == Xsand_stonein_2 ? j + 9 :
9069 i == Xsand_stonein_3 ? j + 17 :
9070 i == Xsand_stonein_4 ? j + 25 :
9071 i == Xsand_stoneout_1 && j == 0 ? 0 :
9072 i == Xsand_stoneout_1 && j == 1 ? 0 :
9073 i == Xsand_stoneout_1 && j == 2 ? 1 :
9074 i == Xsand_stoneout_1 && j == 3 ? 2 :
9075 i == Xsand_stoneout_1 && j == 4 ? 2 :
9076 i == Xsand_stoneout_1 && j == 5 ? 3 :
9077 i == Xsand_stoneout_1 && j == 6 ? 4 :
9078 i == Xsand_stoneout_1 && j == 7 ? 4 :
9079 i == Xsand_stoneout_2 && j == 0 ? 5 :
9080 i == Xsand_stoneout_2 && j == 1 ? 6 :
9081 i == Xsand_stoneout_2 && j == 2 ? 7 :
9082 i == Xsand_stoneout_2 && j == 3 ? 8 :
9083 i == Xsand_stoneout_2 && j == 4 ? 9 :
9084 i == Xsand_stoneout_2 && j == 5 ? 11 :
9085 i == Xsand_stoneout_2 && j == 6 ? 13 :
9086 i == Xsand_stoneout_2 && j == 7 ? 15 :
9087 i == Xboom_bug && j == 1 ? 2 :
9088 i == Xboom_bug && j == 2 ? 2 :
9089 i == Xboom_bug && j == 3 ? 4 :
9090 i == Xboom_bug && j == 4 ? 4 :
9091 i == Xboom_bug && j == 5 ? 2 :
9092 i == Xboom_bug && j == 6 ? 2 :
9093 i == Xboom_bug && j == 7 ? 0 :
9094 i == Xboom_tank && j == 1 ? 2 :
9095 i == Xboom_tank && j == 2 ? 2 :
9096 i == Xboom_tank && j == 3 ? 4 :
9097 i == Xboom_tank && j == 4 ? 4 :
9098 i == Xboom_tank && j == 5 ? 2 :
9099 i == Xboom_tank && j == 6 ? 2 :
9100 i == Xboom_tank && j == 7 ? 0 :
9101 i == Xboom_android && j == 7 ? 6 :
9102 i == Xboom_1 && j == 1 ? 2 :
9103 i == Xboom_1 && j == 2 ? 2 :
9104 i == Xboom_1 && j == 3 ? 4 :
9105 i == Xboom_1 && j == 4 ? 4 :
9106 i == Xboom_1 && j == 5 ? 6 :
9107 i == Xboom_1 && j == 6 ? 6 :
9108 i == Xboom_1 && j == 7 ? 8 :
9109 i == Xboom_2 && j == 0 ? 8 :
9110 i == Xboom_2 && j == 1 ? 8 :
9111 i == Xboom_2 && j == 2 ? 10 :
9112 i == Xboom_2 && j == 3 ? 10 :
9113 i == Xboom_2 && j == 4 ? 10 :
9114 i == Xboom_2 && j == 5 ? 12 :
9115 i == Xboom_2 && j == 6 ? 12 :
9116 i == Xboom_2 && j == 7 ? 12 :
9117 special_animation && j == 4 ? 3 :
9118 effective_action != action ? 0 :
9120 int frame = getAnimationFrame(g->anim_frames,
9123 g->anim_start_frame,
9126 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9127 g->double_movement && is_backside);
9129 g_em->bitmap = src_bitmap;
9130 g_em->src_x = src_x;
9131 g_em->src_y = src_y;
9132 g_em->src_offset_x = 0;
9133 g_em->src_offset_y = 0;
9134 g_em->dst_offset_x = 0;
9135 g_em->dst_offset_y = 0;
9136 g_em->width = TILEX;
9137 g_em->height = TILEY;
9139 g_em->preserve_background = FALSE;
9141 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9144 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9145 effective_action == ACTION_MOVING ||
9146 effective_action == ACTION_PUSHING ||
9147 effective_action == ACTION_EATING)) ||
9148 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9149 effective_action == ACTION_EMPTYING)))
9152 (effective_action == ACTION_FALLING ||
9153 effective_action == ACTION_FILLING ||
9154 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9155 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9156 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9157 int num_steps = (i == Ydrip_1_s ? 16 :
9158 i == Ydrip_1_sB ? 16 :
9159 i == Ydrip_2_s ? 16 :
9160 i == Ydrip_2_sB ? 16 :
9161 i == Xsand_stonein_1 ? 32 :
9162 i == Xsand_stonein_2 ? 32 :
9163 i == Xsand_stonein_3 ? 32 :
9164 i == Xsand_stonein_4 ? 32 :
9165 i == Xsand_stoneout_1 ? 16 :
9166 i == Xsand_stoneout_2 ? 16 : 8);
9167 int cx = ABS(dx) * (TILEX / num_steps);
9168 int cy = ABS(dy) * (TILEY / num_steps);
9169 int step_frame = (i == Ydrip_2_s ? j + 8 :
9170 i == Ydrip_2_sB ? j + 8 :
9171 i == Xsand_stonein_2 ? j + 8 :
9172 i == Xsand_stonein_3 ? j + 16 :
9173 i == Xsand_stonein_4 ? j + 24 :
9174 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9175 int step = (is_backside ? step_frame : num_steps - step_frame);
9177 if (is_backside) // tile where movement starts
9179 if (dx < 0 || dy < 0)
9181 g_em->src_offset_x = cx * step;
9182 g_em->src_offset_y = cy * step;
9186 g_em->dst_offset_x = cx * step;
9187 g_em->dst_offset_y = cy * step;
9190 else // tile where movement ends
9192 if (dx < 0 || dy < 0)
9194 g_em->dst_offset_x = cx * step;
9195 g_em->dst_offset_y = cy * step;
9199 g_em->src_offset_x = cx * step;
9200 g_em->src_offset_y = cy * step;
9204 g_em->width = TILEX - cx * step;
9205 g_em->height = TILEY - cy * step;
9208 // create unique graphic identifier to decide if tile must be redrawn
9209 /* bit 31 - 16 (16 bit): EM style graphic
9210 bit 15 - 12 ( 4 bit): EM style frame
9211 bit 11 - 6 ( 6 bit): graphic width
9212 bit 5 - 0 ( 6 bit): graphic height */
9213 g_em->unique_identifier =
9214 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9218 for (i = 0; i < GAME_TILE_MAX; i++)
9220 for (j = 0; j < 8; j++)
9222 int element = object_mapping[i].element_rnd;
9223 int action = object_mapping[i].action;
9224 int direction = object_mapping[i].direction;
9225 boolean is_backside = object_mapping[i].is_backside;
9226 int graphic_action = el_act_dir2img(element, action, direction);
9227 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9229 if ((action == ACTION_SMASHED_BY_ROCK ||
9230 action == ACTION_SMASHED_BY_SPRING ||
9231 action == ACTION_EATING) &&
9232 graphic_action == graphic_default)
9234 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9235 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9236 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9237 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9240 // no separate animation for "smashed by rock" -- use rock instead
9241 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9242 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9244 g_em->bitmap = g_xx->bitmap;
9245 g_em->src_x = g_xx->src_x;
9246 g_em->src_y = g_xx->src_y;
9247 g_em->src_offset_x = g_xx->src_offset_x;
9248 g_em->src_offset_y = g_xx->src_offset_y;
9249 g_em->dst_offset_x = g_xx->dst_offset_x;
9250 g_em->dst_offset_y = g_xx->dst_offset_y;
9251 g_em->width = g_xx->width;
9252 g_em->height = g_xx->height;
9253 g_em->unique_identifier = g_xx->unique_identifier;
9256 g_em->preserve_background = TRUE;
9261 for (p = 0; p < MAX_PLAYERS; p++)
9263 for (i = 0; i < PLY_MAX; i++)
9265 int element = player_mapping[p][i].element_rnd;
9266 int action = player_mapping[p][i].action;
9267 int direction = player_mapping[p][i].direction;
9269 for (j = 0; j < 8; j++)
9271 int effective_element = element;
9272 int effective_action = action;
9273 int graphic = (direction == MV_NONE ?
9274 el_act2img(effective_element, effective_action) :
9275 el_act_dir2img(effective_element, effective_action,
9277 struct GraphicInfo *g = &graphic_info[graphic];
9278 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9282 int frame = getAnimationFrame(g->anim_frames,
9285 g->anim_start_frame,
9288 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9290 g_em->bitmap = src_bitmap;
9291 g_em->src_x = src_x;
9292 g_em->src_y = src_y;
9293 g_em->src_offset_x = 0;
9294 g_em->src_offset_y = 0;
9295 g_em->dst_offset_x = 0;
9296 g_em->dst_offset_y = 0;
9297 g_em->width = TILEX;
9298 g_em->height = TILEY;
9304 static void CheckSaveEngineSnapshot_EM(int frame,
9305 boolean any_player_moving,
9306 boolean any_player_snapping,
9307 boolean any_player_dropping)
9309 if (frame == 7 && !any_player_dropping)
9311 if (!local_player->was_waiting)
9313 if (!CheckSaveEngineSnapshotToList())
9316 local_player->was_waiting = TRUE;
9319 else if (any_player_moving || any_player_snapping || any_player_dropping)
9321 local_player->was_waiting = FALSE;
9325 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9326 boolean murphy_is_dropping)
9328 if (murphy_is_waiting)
9330 if (!local_player->was_waiting)
9332 if (!CheckSaveEngineSnapshotToList())
9335 local_player->was_waiting = TRUE;
9340 local_player->was_waiting = FALSE;
9344 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9345 boolean button_released)
9347 if (button_released)
9349 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9350 CheckSaveEngineSnapshotToList();
9352 else if (element_clicked)
9354 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9355 CheckSaveEngineSnapshotToList();
9357 game.snapshot.changed_action = TRUE;
9361 boolean CheckSingleStepMode_EM(int frame,
9362 boolean any_player_moving,
9363 boolean any_player_snapping,
9364 boolean any_player_dropping)
9366 if (tape.single_step && tape.recording && !tape.pausing)
9367 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9368 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9370 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9371 any_player_snapping, any_player_dropping);
9373 return tape.pausing;
9376 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9377 boolean murphy_is_dropping)
9379 boolean murphy_starts_dropping = FALSE;
9382 for (i = 0; i < MAX_PLAYERS; i++)
9383 if (stored_player[i].force_dropping)
9384 murphy_starts_dropping = TRUE;
9386 if (tape.single_step && tape.recording && !tape.pausing)
9387 if (murphy_is_waiting && !murphy_starts_dropping)
9388 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9390 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9393 void CheckSingleStepMode_MM(boolean element_clicked,
9394 boolean button_released)
9396 if (tape.single_step && tape.recording && !tape.pausing)
9397 if (button_released)
9398 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9400 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9403 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9404 int graphic, int sync_frame)
9406 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9408 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9411 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9413 return (IS_NEXT_FRAME(sync_frame, graphic));
9416 int getGraphicInfo_Delay(int graphic)
9418 return graphic_info[graphic].anim_delay;
9421 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9423 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9426 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9432 void PlayMenuSoundExt(int sound)
9434 if (sound == SND_UNDEFINED)
9437 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9438 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9441 if (IS_LOOP_SOUND(sound))
9442 PlaySoundLoop(sound);
9447 void PlayMenuSound(void)
9449 PlayMenuSoundExt(menu.sound[game_status]);
9452 void PlayMenuSoundStereo(int sound, int stereo_position)
9454 if (sound == SND_UNDEFINED)
9457 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9458 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9461 if (IS_LOOP_SOUND(sound))
9462 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9464 PlaySoundStereo(sound, stereo_position);
9467 void PlayMenuSoundIfLoopExt(int sound)
9469 if (sound == SND_UNDEFINED)
9472 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9473 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9476 if (IS_LOOP_SOUND(sound))
9477 PlaySoundLoop(sound);
9480 void PlayMenuSoundIfLoop(void)
9482 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9485 void PlayMenuMusicExt(int music)
9487 if (music == MUS_UNDEFINED)
9490 if (!setup.sound_music)
9493 if (IS_LOOP_MUSIC(music))
9494 PlayMusicLoop(music);
9499 void PlayMenuMusic(void)
9501 char *curr_music = getCurrentlyPlayingMusicFilename();
9502 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9504 if (!strEqual(curr_music, next_music))
9505 PlayMenuMusicExt(menu.music[game_status]);
9508 void PlayMenuSoundsAndMusic(void)
9514 static void FadeMenuSounds(void)
9519 static void FadeMenuMusic(void)
9521 char *curr_music = getCurrentlyPlayingMusicFilename();
9522 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9524 if (!strEqual(curr_music, next_music))
9528 void FadeMenuSoundsAndMusic(void)
9534 void PlaySoundActivating(void)
9537 PlaySound(SND_MENU_ITEM_ACTIVATING);
9541 void PlaySoundSelecting(void)
9544 PlaySound(SND_MENU_ITEM_SELECTING);
9548 void ToggleFullscreenIfNeeded(void)
9550 // if setup and video fullscreen state are already matching, nothing do do
9551 if (setup.fullscreen == video.fullscreen_enabled ||
9552 !video.fullscreen_available)
9555 SDLSetWindowFullscreen(setup.fullscreen);
9557 // set setup value according to successfully changed fullscreen mode
9558 setup.fullscreen = video.fullscreen_enabled;
9561 void ChangeWindowScalingIfNeeded(void)
9563 // if setup and video window scaling are already matching, nothing do do
9564 if (setup.window_scaling_percent == video.window_scaling_percent ||
9565 video.fullscreen_enabled)
9568 SDLSetWindowScaling(setup.window_scaling_percent);
9570 // set setup value according to successfully changed window scaling
9571 setup.window_scaling_percent = video.window_scaling_percent;
9574 void ChangeVsyncModeIfNeeded(void)
9576 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9577 int video_vsync_mode = video.vsync_mode;
9579 // if setup and video vsync mode are already matching, nothing do do
9580 if (setup_vsync_mode == video_vsync_mode)
9583 // if renderer is using OpenGL, vsync mode can directly be changed
9584 SDLSetScreenVsyncMode(setup.vsync_mode);
9586 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9587 if (video.vsync_mode == video_vsync_mode)
9589 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9591 // save backbuffer content which gets lost when re-creating screen
9592 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9594 // force re-creating screen and renderer to set new vsync mode
9595 video.fullscreen_enabled = !setup.fullscreen;
9597 // when creating new renderer, destroy textures linked to old renderer
9598 FreeAllImageTextures(); // needs old renderer to free the textures
9600 // re-create screen and renderer (including change of vsync mode)
9601 ChangeVideoModeIfNeeded(setup.fullscreen);
9603 // set setup value according to successfully changed fullscreen mode
9604 setup.fullscreen = video.fullscreen_enabled;
9606 // restore backbuffer content from temporary backbuffer backup bitmap
9607 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9608 FreeBitmap(tmp_backbuffer);
9610 // update visible window/screen
9611 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9613 // when changing vsync mode, re-create textures for new renderer
9614 InitImageTextures();
9617 // set setup value according to successfully changed vsync mode
9618 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9621 static void JoinRectangles(int *x, int *y, int *width, int *height,
9622 int x2, int y2, int width2, int height2)
9624 // do not join with "off-screen" rectangle
9625 if (x2 == -1 || y2 == -1)
9630 *width = MAX(*width, width2);
9631 *height = MAX(*height, height2);
9634 void SetAnimStatus(int anim_status_new)
9636 if (anim_status_new == GAME_MODE_MAIN)
9637 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9638 else if (anim_status_new == GAME_MODE_NAMES)
9639 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9640 else if (anim_status_new == GAME_MODE_SCORES)
9641 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9643 global.anim_status_next = anim_status_new;
9645 // directly set screen modes that are entered without fading
9646 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9647 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9648 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9649 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9650 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9651 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9652 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9653 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9654 global.anim_status = global.anim_status_next;
9657 void SetGameStatus(int game_status_new)
9659 if (game_status_new != game_status)
9660 game_status_last_screen = game_status;
9662 game_status = game_status_new;
9664 SetAnimStatus(game_status_new);
9667 void SetFontStatus(int game_status_new)
9669 static int last_game_status = -1;
9671 if (game_status_new != -1)
9673 // set game status for font use after storing last game status
9674 last_game_status = game_status;
9675 game_status = game_status_new;
9679 // reset game status after font use from last stored game status
9680 game_status = last_game_status;
9684 void ResetFontStatus(void)
9689 void SetLevelSetInfo(char *identifier, int level_nr)
9691 setString(&levelset.identifier, identifier);
9693 levelset.level_nr = level_nr;
9696 boolean CheckIfAllViewportsHaveChanged(void)
9698 // if game status has not changed, viewports have not changed either
9699 if (game_status == game_status_last)
9702 // check if all viewports have changed with current game status
9704 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9705 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9706 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9707 int new_real_sx = vp_playfield->x;
9708 int new_real_sy = vp_playfield->y;
9709 int new_full_sxsize = vp_playfield->width;
9710 int new_full_sysize = vp_playfield->height;
9711 int new_dx = vp_door_1->x;
9712 int new_dy = vp_door_1->y;
9713 int new_dxsize = vp_door_1->width;
9714 int new_dysize = vp_door_1->height;
9715 int new_vx = vp_door_2->x;
9716 int new_vy = vp_door_2->y;
9717 int new_vxsize = vp_door_2->width;
9718 int new_vysize = vp_door_2->height;
9720 boolean playfield_viewport_has_changed =
9721 (new_real_sx != REAL_SX ||
9722 new_real_sy != REAL_SY ||
9723 new_full_sxsize != FULL_SXSIZE ||
9724 new_full_sysize != FULL_SYSIZE);
9726 boolean door_1_viewport_has_changed =
9729 new_dxsize != DXSIZE ||
9730 new_dysize != DYSIZE);
9732 boolean door_2_viewport_has_changed =
9735 new_vxsize != VXSIZE ||
9736 new_vysize != VYSIZE ||
9737 game_status_last == GAME_MODE_EDITOR);
9739 return (playfield_viewport_has_changed &&
9740 door_1_viewport_has_changed &&
9741 door_2_viewport_has_changed);
9744 boolean CheckFadeAll(void)
9746 return (CheckIfGlobalBorderHasChanged() ||
9747 CheckIfAllViewportsHaveChanged());
9750 void ChangeViewportPropertiesIfNeeded(void)
9752 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9753 FALSE : setup.small_game_graphics);
9754 int gfx_game_mode = getGlobalGameStatus(game_status);
9755 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9757 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9758 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9759 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9760 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9761 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9762 int new_win_xsize = vp_window->width;
9763 int new_win_ysize = vp_window->height;
9764 int border_left = vp_playfield->border_left;
9765 int border_right = vp_playfield->border_right;
9766 int border_top = vp_playfield->border_top;
9767 int border_bottom = vp_playfield->border_bottom;
9768 int new_sx = vp_playfield->x + border_left;
9769 int new_sy = vp_playfield->y + border_top;
9770 int new_sxsize = vp_playfield->width - border_left - border_right;
9771 int new_sysize = vp_playfield->height - border_top - border_bottom;
9772 int new_real_sx = vp_playfield->x;
9773 int new_real_sy = vp_playfield->y;
9774 int new_full_sxsize = vp_playfield->width;
9775 int new_full_sysize = vp_playfield->height;
9776 int new_dx = vp_door_1->x;
9777 int new_dy = vp_door_1->y;
9778 int new_dxsize = vp_door_1->width;
9779 int new_dysize = vp_door_1->height;
9780 int new_vx = vp_door_2->x;
9781 int new_vy = vp_door_2->y;
9782 int new_vxsize = vp_door_2->width;
9783 int new_vysize = vp_door_2->height;
9784 int new_ex = vp_door_3->x;
9785 int new_ey = vp_door_3->y;
9786 int new_exsize = vp_door_3->width;
9787 int new_eysize = vp_door_3->height;
9788 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9789 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9790 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9791 int new_scr_fieldx = new_sxsize / tilesize;
9792 int new_scr_fieldy = new_sysize / tilesize;
9793 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9794 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9795 boolean init_gfx_buffers = FALSE;
9796 boolean init_video_buffer = FALSE;
9797 boolean init_gadgets_and_anims = FALSE;
9798 boolean init_em_graphics = FALSE;
9800 if (new_win_xsize != WIN_XSIZE ||
9801 new_win_ysize != WIN_YSIZE)
9803 WIN_XSIZE = new_win_xsize;
9804 WIN_YSIZE = new_win_ysize;
9806 init_video_buffer = TRUE;
9807 init_gfx_buffers = TRUE;
9808 init_gadgets_and_anims = TRUE;
9810 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9813 if (new_scr_fieldx != SCR_FIELDX ||
9814 new_scr_fieldy != SCR_FIELDY)
9816 // this always toggles between MAIN and GAME when using small tile size
9818 SCR_FIELDX = new_scr_fieldx;
9819 SCR_FIELDY = new_scr_fieldy;
9821 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9832 new_sxsize != SXSIZE ||
9833 new_sysize != SYSIZE ||
9834 new_dxsize != DXSIZE ||
9835 new_dysize != DYSIZE ||
9836 new_vxsize != VXSIZE ||
9837 new_vysize != VYSIZE ||
9838 new_exsize != EXSIZE ||
9839 new_eysize != EYSIZE ||
9840 new_real_sx != REAL_SX ||
9841 new_real_sy != REAL_SY ||
9842 new_full_sxsize != FULL_SXSIZE ||
9843 new_full_sysize != FULL_SYSIZE ||
9844 new_tilesize_var != TILESIZE_VAR
9847 // ------------------------------------------------------------------------
9848 // determine next fading area for changed viewport definitions
9849 // ------------------------------------------------------------------------
9851 // start with current playfield area (default fading area)
9854 FADE_SXSIZE = FULL_SXSIZE;
9855 FADE_SYSIZE = FULL_SYSIZE;
9857 // add new playfield area if position or size has changed
9858 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9859 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9861 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9862 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9865 // add current and new door 1 area if position or size has changed
9866 if (new_dx != DX || new_dy != DY ||
9867 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9869 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9870 DX, DY, DXSIZE, DYSIZE);
9871 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9872 new_dx, new_dy, new_dxsize, new_dysize);
9875 // add current and new door 2 area if position or size has changed
9876 if (new_vx != VX || new_vy != VY ||
9877 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9879 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9880 VX, VY, VXSIZE, VYSIZE);
9881 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9882 new_vx, new_vy, new_vxsize, new_vysize);
9885 // ------------------------------------------------------------------------
9886 // handle changed tile size
9887 // ------------------------------------------------------------------------
9889 if (new_tilesize_var != TILESIZE_VAR)
9891 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9893 // changing tile size invalidates scroll values of engine snapshots
9894 FreeEngineSnapshotSingle();
9896 // changing tile size requires update of graphic mapping for EM engine
9897 init_em_graphics = TRUE;
9908 SXSIZE = new_sxsize;
9909 SYSIZE = new_sysize;
9910 DXSIZE = new_dxsize;
9911 DYSIZE = new_dysize;
9912 VXSIZE = new_vxsize;
9913 VYSIZE = new_vysize;
9914 EXSIZE = new_exsize;
9915 EYSIZE = new_eysize;
9916 REAL_SX = new_real_sx;
9917 REAL_SY = new_real_sy;
9918 FULL_SXSIZE = new_full_sxsize;
9919 FULL_SYSIZE = new_full_sysize;
9920 TILESIZE_VAR = new_tilesize_var;
9922 init_gfx_buffers = TRUE;
9923 init_gadgets_and_anims = TRUE;
9925 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9926 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9929 if (init_gfx_buffers)
9931 // Debug("tools:viewport", "init_gfx_buffers");
9933 SCR_FIELDX = new_scr_fieldx_buffers;
9934 SCR_FIELDY = new_scr_fieldy_buffers;
9938 SCR_FIELDX = new_scr_fieldx;
9939 SCR_FIELDY = new_scr_fieldy;
9941 SetDrawDeactivationMask(REDRAW_NONE);
9942 SetDrawBackgroundMask(REDRAW_FIELD);
9945 if (init_video_buffer)
9947 // Debug("tools:viewport", "init_video_buffer");
9949 FreeAllImageTextures(); // needs old renderer to free the textures
9951 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9952 InitImageTextures();
9955 if (init_gadgets_and_anims)
9957 // Debug("tools:viewport", "init_gadgets_and_anims");
9960 InitGlobalAnimations();
9963 if (init_em_graphics)
9965 InitGraphicInfo_EM();
9969 void OpenURL(char *url)
9971 #if SDL_VERSION_ATLEAST(2,0,14)
9974 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9975 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9976 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9980 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9982 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9986 // ============================================================================
9988 // ============================================================================
9990 #if defined(PLATFORM_WINDOWS)
9991 /* FILETIME of Jan 1 1970 00:00:00. */
9992 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9995 * timezone information is stored outside the kernel so tzp isn't used anymore.
9997 * Note: this function is not for Win32 high precision timing purpose. See
10000 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10002 FILETIME file_time;
10003 SYSTEMTIME system_time;
10004 ULARGE_INTEGER ularge;
10006 GetSystemTime(&system_time);
10007 SystemTimeToFileTime(&system_time, &file_time);
10008 ularge.LowPart = file_time.dwLowDateTime;
10009 ularge.HighPart = file_time.dwHighDateTime;
10011 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10012 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10018 static char *test_init_uuid_random_function_simple(void)
10020 static char seed_text[100];
10021 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10023 sprintf(seed_text, "%d", seed);
10028 static char *test_init_uuid_random_function_better(void)
10030 static char seed_text[100];
10031 struct timeval current_time;
10033 gettimeofday(¤t_time, NULL);
10035 prng_seed_bytes(¤t_time, sizeof(current_time));
10037 sprintf(seed_text, "%ld.%ld",
10038 (long)current_time.tv_sec,
10039 (long)current_time.tv_usec);
10044 #if defined(PLATFORM_WINDOWS)
10045 static char *test_init_uuid_random_function_better_windows(void)
10047 static char seed_text[100];
10048 struct timeval current_time;
10050 gettimeofday_windows(¤t_time, NULL);
10052 prng_seed_bytes(¤t_time, sizeof(current_time));
10054 sprintf(seed_text, "%ld.%ld",
10055 (long)current_time.tv_sec,
10056 (long)current_time.tv_usec);
10062 static unsigned int test_uuid_random_function_simple(int max)
10064 return GetSimpleRandom(max);
10067 static unsigned int test_uuid_random_function_better(int max)
10069 return (max > 0 ? prng_get_uint() % max : 0);
10072 #if defined(PLATFORM_WINDOWS)
10073 #define NUM_UUID_TESTS 3
10075 #define NUM_UUID_TESTS 2
10078 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10080 struct hashtable *hash_seeds =
10081 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10082 struct hashtable *hash_uuids =
10083 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10084 static char message[100];
10087 char *random_name = (nr == 0 ? "simple" : "better");
10088 char *random_type = (always_seed ? "always" : "only once");
10089 char *(*init_random_function)(void) =
10091 test_init_uuid_random_function_simple :
10092 test_init_uuid_random_function_better);
10093 unsigned int (*random_function)(int) =
10095 test_uuid_random_function_simple :
10096 test_uuid_random_function_better);
10099 #if defined(PLATFORM_WINDOWS)
10102 random_name = "windows";
10103 init_random_function = test_init_uuid_random_function_better_windows;
10109 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10110 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10112 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10113 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10114 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10116 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10120 // always initialize random number generator at least once
10121 init_random_function();
10123 unsigned int time_start = SDL_GetTicks();
10125 for (i = 0; i < num_uuids; i++)
10129 char *seed = getStringCopy(init_random_function());
10131 hashtable_remove(hash_seeds, seed);
10132 hashtable_insert(hash_seeds, seed, "1");
10135 char *uuid = getStringCopy(getUUIDExt(random_function));
10137 hashtable_remove(hash_uuids, uuid);
10138 hashtable_insert(hash_uuids, uuid, "1");
10141 int num_unique_seeds = hashtable_count(hash_seeds);
10142 int num_unique_uuids = hashtable_count(hash_uuids);
10144 unsigned int time_needed = SDL_GetTicks() - time_start;
10146 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10148 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10151 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10153 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10154 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10156 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10158 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10160 Request(message, REQ_CONFIRM);
10162 hashtable_destroy(hash_seeds, 0);
10163 hashtable_destroy(hash_uuids, 0);
10166 void TestGeneratingUUIDs(void)
10168 int num_uuids = 1000000;
10171 for (i = 0; i < NUM_UUID_TESTS; i++)
10172 for (j = 0; j < 2; j++)
10173 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10175 CloseAllAndExit(0);