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:
4614 // only needed to handle clickable pointer animations here
4615 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4620 case SDL_WINDOWEVENT:
4621 HandleWindowEvent((WindowEvent *) &event);
4624 case SDL_APP_WILLENTERBACKGROUND:
4625 case SDL_APP_DIDENTERBACKGROUND:
4626 case SDL_APP_WILLENTERFOREGROUND:
4627 case SDL_APP_DIDENTERFOREGROUND:
4628 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4631 case EVENT_KEYPRESS:
4633 Key key = GetEventKey((KeyEvent *)&event);
4638 if (req_state & REQ_CONFIRM)
4647 #if defined(KSYM_Rewind)
4648 case KSYM_Rewind: // for Amazon Fire TV remote
4657 #if defined(KSYM_FastForward)
4658 case KSYM_FastForward: // for Amazon Fire TV remote
4664 HandleKeysDebug(key, KEY_PRESSED);
4668 if (req_state & REQ_PLAYER)
4670 int old_player_nr = setup.network_player_nr;
4673 result = old_player_nr + 1;
4678 result = old_player_nr + 1;
4709 case EVENT_FINGERRELEASE:
4710 case EVENT_KEYRELEASE:
4711 ClearPlayerAction();
4714 case SDL_CONTROLLERBUTTONDOWN:
4715 switch (event.cbutton.button)
4717 case SDL_CONTROLLER_BUTTON_A:
4718 case SDL_CONTROLLER_BUTTON_X:
4719 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4720 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4724 case SDL_CONTROLLER_BUTTON_B:
4725 case SDL_CONTROLLER_BUTTON_Y:
4726 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4727 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4728 case SDL_CONTROLLER_BUTTON_BACK:
4733 if (req_state & REQ_PLAYER)
4735 int old_player_nr = setup.network_player_nr;
4738 result = old_player_nr + 1;
4740 switch (event.cbutton.button)
4742 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4743 case SDL_CONTROLLER_BUTTON_Y:
4747 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4748 case SDL_CONTROLLER_BUTTON_B:
4752 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4753 case SDL_CONTROLLER_BUTTON_A:
4757 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4758 case SDL_CONTROLLER_BUTTON_X:
4769 case SDL_CONTROLLERBUTTONUP:
4770 HandleJoystickEvent(&event);
4771 ClearPlayerAction();
4775 HandleOtherEvents(&event);
4780 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4782 int joy = AnyJoystick();
4784 if (joy & JOY_BUTTON_1)
4786 else if (joy & JOY_BUTTON_2)
4789 else if (AnyJoystick())
4791 int joy = AnyJoystick();
4793 if (req_state & REQ_PLAYER)
4797 else if (joy & JOY_RIGHT)
4799 else if (joy & JOY_DOWN)
4801 else if (joy & JOY_LEFT)
4809 SetDrawtoField(draw_buffer_last);
4814 static void DoRequestBefore(void)
4816 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4818 // when showing request dialog after game ended, deactivate game panel
4820 game.panel.active = FALSE;
4822 if (game_status == GAME_MODE_PLAYING)
4823 BlitScreenToBitmap(backbuffer);
4825 // disable deactivated drawing when quick-loading level tape recording
4826 if (tape.playing && tape.deactivate_display)
4827 TapeDeactivateDisplayOff(TRUE);
4829 SetMouseCursor(CURSOR_DEFAULT);
4831 // pause network game while waiting for request to answer
4832 if (network.enabled &&
4833 game_status == GAME_MODE_PLAYING &&
4834 !game.all_players_gone)
4835 SendToServer_PausePlaying();
4837 // simulate releasing mouse button over last gadget, if still pressed
4839 HandleGadgets(-1, -1, 0);
4844 static void DoRequestAfter(void)
4848 if (game_status == GAME_MODE_PLAYING)
4850 SetPanelBackground();
4851 SetDrawBackgroundMask(REDRAW_DOOR_1);
4855 SetDrawBackgroundMask(REDRAW_FIELD);
4858 // continue network game after request
4859 if (network.enabled &&
4860 game_status == GAME_MODE_PLAYING &&
4861 !game.all_players_gone)
4862 SendToServer_ContinuePlaying();
4864 // restore deactivated drawing when quick-loading level tape recording
4865 if (tape.playing && tape.deactivate_display)
4866 TapeDeactivateDisplayOn();
4869 static void setRequestDoorTextProperties(char *text,
4874 int *set_max_line_length)
4876 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4877 struct TextPosInfo *pos = &request.button.confirm;
4878 int button_ypos = pos->y;
4879 int font_nr = FONT_TEXT_2;
4880 int font_width = getFontWidth(font_nr);
4881 int font_height = getFontHeight(font_nr);
4882 int line_height = font_height + line_spacing;
4883 int max_text_width = vp_door_1->width;
4884 int max_text_height = button_ypos - 2 * text_spacing;
4885 int max_line_length = max_text_width / font_width;
4886 int max_lines = max_text_height / line_height;
4888 if (maxWordLengthInRequestString(text) > max_line_length)
4890 font_nr = FONT_TEXT_1;
4891 font_width = getFontWidth(font_nr);
4892 max_line_length = max_text_width / font_width;
4895 *set_font_nr = font_nr;
4896 *set_max_lines = max_lines;
4897 *set_max_line_length = max_line_length;
4900 static void DrawRequestDoorText(char *text)
4902 char *text_ptr = text;
4903 int text_spacing = 8;
4904 int line_spacing = 2;
4905 int max_request_lines;
4906 int max_request_line_len;
4910 // force DOOR font inside door area
4911 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4913 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4914 &max_request_lines, &max_request_line_len);
4916 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4918 char text_line[max_request_line_len + 1];
4924 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4926 tc = *(text_ptr + tx);
4927 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4931 if ((tc == '?' || tc == '!') && tl == 0)
4941 strncpy(text_line, text_ptr, tl);
4944 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4945 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4946 text_line, font_nr);
4948 text_ptr += tl + (tc == ' ' ? 1 : 0);
4954 static int RequestDoor(char *text, unsigned int req_state)
4956 unsigned int old_door_state = GetDoorState();
4957 int draw_buffer_last = GetDrawtoField();
4960 if (old_door_state & DOOR_OPEN_1)
4962 CloseDoor(DOOR_CLOSE_1);
4964 // save old door content
4965 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4966 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4969 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4970 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4972 // clear door drawing field
4973 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4975 // write text for request
4976 DrawRequestDoorText(text);
4978 MapToolButtons(req_state);
4980 // copy request gadgets to door backbuffer
4981 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4983 OpenDoor(DOOR_OPEN_1);
4985 // ---------- handle request buttons ----------
4986 result = RequestHandleEvents(req_state, draw_buffer_last);
4990 if (!(req_state & REQ_STAY_OPEN))
4992 CloseDoor(DOOR_CLOSE_1);
4994 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4995 (req_state & REQ_REOPEN))
4996 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5002 static int RequestEnvelope(char *text, unsigned int req_state)
5004 int draw_buffer_last = GetDrawtoField();
5007 DrawEnvelopeRequest(text, req_state);
5008 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5010 // ---------- handle request buttons ----------
5011 result = RequestHandleEvents(req_state, draw_buffer_last);
5015 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5020 int Request(char *text, unsigned int req_state)
5022 boolean overlay_enabled = GetOverlayEnabled();
5025 game.request_active = TRUE;
5027 SetOverlayEnabled(FALSE);
5031 if (global.use_envelope_request)
5032 result = RequestEnvelope(text, req_state);
5034 result = RequestDoor(text, req_state);
5038 SetOverlayEnabled(overlay_enabled);
5040 game.request_active = FALSE;
5045 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5047 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5048 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5051 if (dpo1->sort_priority != dpo2->sort_priority)
5052 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5054 compare_result = dpo1->nr - dpo2->nr;
5056 return compare_result;
5059 void InitGraphicCompatibilityInfo_Doors(void)
5065 struct DoorInfo *door;
5069 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5070 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5072 { -1, -1, -1, NULL }
5074 struct Rect door_rect_list[] =
5076 { DX, DY, DXSIZE, DYSIZE },
5077 { VX, VY, VXSIZE, VYSIZE }
5081 for (i = 0; doors[i].door_token != -1; i++)
5083 int door_token = doors[i].door_token;
5084 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5085 int part_1 = doors[i].part_1;
5086 int part_8 = doors[i].part_8;
5087 int part_2 = part_1 + 1;
5088 int part_3 = part_1 + 2;
5089 struct DoorInfo *door = doors[i].door;
5090 struct Rect *door_rect = &door_rect_list[door_index];
5091 boolean door_gfx_redefined = FALSE;
5093 // check if any door part graphic definitions have been redefined
5095 for (j = 0; door_part_controls[j].door_token != -1; j++)
5097 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5098 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5100 if (dpc->door_token == door_token && fi->redefined)
5101 door_gfx_redefined = TRUE;
5104 // check for old-style door graphic/animation modifications
5106 if (!door_gfx_redefined)
5108 if (door->anim_mode & ANIM_STATIC_PANEL)
5110 door->panel.step_xoffset = 0;
5111 door->panel.step_yoffset = 0;
5114 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5116 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5117 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5118 int num_door_steps, num_panel_steps;
5120 // remove door part graphics other than the two default wings
5122 for (j = 0; door_part_controls[j].door_token != -1; j++)
5124 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5125 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5127 if (dpc->graphic >= part_3 &&
5128 dpc->graphic <= part_8)
5132 // set graphics and screen positions of the default wings
5134 g_part_1->width = door_rect->width;
5135 g_part_1->height = door_rect->height;
5136 g_part_2->width = door_rect->width;
5137 g_part_2->height = door_rect->height;
5138 g_part_2->src_x = door_rect->width;
5139 g_part_2->src_y = g_part_1->src_y;
5141 door->part_2.x = door->part_1.x;
5142 door->part_2.y = door->part_1.y;
5144 if (door->width != -1)
5146 g_part_1->width = door->width;
5147 g_part_2->width = door->width;
5149 // special treatment for graphics and screen position of right wing
5150 g_part_2->src_x += door_rect->width - door->width;
5151 door->part_2.x += door_rect->width - door->width;
5154 if (door->height != -1)
5156 g_part_1->height = door->height;
5157 g_part_2->height = door->height;
5159 // special treatment for graphics and screen position of bottom wing
5160 g_part_2->src_y += door_rect->height - door->height;
5161 door->part_2.y += door_rect->height - door->height;
5164 // set animation delays for the default wings and panels
5166 door->part_1.step_delay = door->step_delay;
5167 door->part_2.step_delay = door->step_delay;
5168 door->panel.step_delay = door->step_delay;
5170 // set animation draw order for the default wings
5172 door->part_1.sort_priority = 2; // draw left wing over ...
5173 door->part_2.sort_priority = 1; // ... right wing
5175 // set animation draw offset for the default wings
5177 if (door->anim_mode & ANIM_HORIZONTAL)
5179 door->part_1.step_xoffset = door->step_offset;
5180 door->part_1.step_yoffset = 0;
5181 door->part_2.step_xoffset = door->step_offset * -1;
5182 door->part_2.step_yoffset = 0;
5184 num_door_steps = g_part_1->width / door->step_offset;
5186 else // ANIM_VERTICAL
5188 door->part_1.step_xoffset = 0;
5189 door->part_1.step_yoffset = door->step_offset;
5190 door->part_2.step_xoffset = 0;
5191 door->part_2.step_yoffset = door->step_offset * -1;
5193 num_door_steps = g_part_1->height / door->step_offset;
5196 // set animation draw offset for the default panels
5198 if (door->step_offset > 1)
5200 num_panel_steps = 2 * door_rect->height / door->step_offset;
5201 door->panel.start_step = num_panel_steps - num_door_steps;
5202 door->panel.start_step_closing = door->panel.start_step;
5206 num_panel_steps = door_rect->height / door->step_offset;
5207 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5208 door->panel.start_step_closing = door->panel.start_step;
5209 door->panel.step_delay *= 2;
5216 void InitDoors(void)
5220 for (i = 0; door_part_controls[i].door_token != -1; i++)
5222 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5223 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5225 // initialize "start_step_opening" and "start_step_closing", if needed
5226 if (dpc->pos->start_step_opening == 0 &&
5227 dpc->pos->start_step_closing == 0)
5229 // dpc->pos->start_step_opening = dpc->pos->start_step;
5230 dpc->pos->start_step_closing = dpc->pos->start_step;
5233 // fill structure for door part draw order (sorted below)
5235 dpo->sort_priority = dpc->pos->sort_priority;
5238 // sort door part controls according to sort_priority and graphic number
5239 qsort(door_part_order, MAX_DOOR_PARTS,
5240 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5243 unsigned int OpenDoor(unsigned int door_state)
5245 if (door_state & DOOR_COPY_BACK)
5247 if (door_state & DOOR_OPEN_1)
5248 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5249 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5251 if (door_state & DOOR_OPEN_2)
5252 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5253 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5255 door_state &= ~DOOR_COPY_BACK;
5258 return MoveDoor(door_state);
5261 unsigned int CloseDoor(unsigned int door_state)
5263 unsigned int old_door_state = GetDoorState();
5265 if (!(door_state & DOOR_NO_COPY_BACK))
5267 if (old_door_state & DOOR_OPEN_1)
5268 BlitBitmap(backbuffer, bitmap_db_door_1,
5269 DX, DY, DXSIZE, DYSIZE, 0, 0);
5271 if (old_door_state & DOOR_OPEN_2)
5272 BlitBitmap(backbuffer, bitmap_db_door_2,
5273 VX, VY, VXSIZE, VYSIZE, 0, 0);
5275 door_state &= ~DOOR_NO_COPY_BACK;
5278 return MoveDoor(door_state);
5281 unsigned int GetDoorState(void)
5283 return MoveDoor(DOOR_GET_STATE);
5286 unsigned int SetDoorState(unsigned int door_state)
5288 return MoveDoor(door_state | DOOR_SET_STATE);
5291 static int euclid(int a, int b)
5293 return (b ? euclid(b, a % b) : a);
5296 unsigned int MoveDoor(unsigned int door_state)
5298 struct Rect door_rect_list[] =
5300 { DX, DY, DXSIZE, DYSIZE },
5301 { VX, VY, VXSIZE, VYSIZE }
5303 static int door1 = DOOR_CLOSE_1;
5304 static int door2 = DOOR_CLOSE_2;
5305 DelayCounter door_delay = { 0 };
5308 if (door_state == DOOR_GET_STATE)
5309 return (door1 | door2);
5311 if (door_state & DOOR_SET_STATE)
5313 if (door_state & DOOR_ACTION_1)
5314 door1 = door_state & DOOR_ACTION_1;
5315 if (door_state & DOOR_ACTION_2)
5316 door2 = door_state & DOOR_ACTION_2;
5318 return (door1 | door2);
5321 if (!(door_state & DOOR_FORCE_REDRAW))
5323 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5324 door_state &= ~DOOR_OPEN_1;
5325 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5326 door_state &= ~DOOR_CLOSE_1;
5327 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5328 door_state &= ~DOOR_OPEN_2;
5329 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5330 door_state &= ~DOOR_CLOSE_2;
5333 if (global.autoplay_leveldir)
5335 door_state |= DOOR_NO_DELAY;
5336 door_state &= ~DOOR_CLOSE_ALL;
5339 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5340 door_state |= DOOR_NO_DELAY;
5342 if (door_state & DOOR_ACTION)
5344 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5345 boolean door_panel_drawn[NUM_DOORS];
5346 boolean panel_has_doors[NUM_DOORS];
5347 boolean door_part_skip[MAX_DOOR_PARTS];
5348 boolean door_part_done[MAX_DOOR_PARTS];
5349 boolean door_part_done_all;
5350 int num_steps[MAX_DOOR_PARTS];
5351 int max_move_delay = 0; // delay for complete animations of all doors
5352 int max_step_delay = 0; // delay (ms) between two animation frames
5353 int num_move_steps = 0; // number of animation steps for all doors
5354 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5355 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5359 for (i = 0; i < NUM_DOORS; i++)
5360 panel_has_doors[i] = FALSE;
5362 for (i = 0; i < MAX_DOOR_PARTS; i++)
5364 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5365 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5366 int door_token = dpc->door_token;
5368 door_part_done[i] = FALSE;
5369 door_part_skip[i] = (!(door_state & door_token) ||
5373 for (i = 0; i < MAX_DOOR_PARTS; i++)
5375 int nr = door_part_order[i].nr;
5376 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5377 struct DoorPartPosInfo *pos = dpc->pos;
5378 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5379 int door_token = dpc->door_token;
5380 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5381 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5382 int step_xoffset = ABS(pos->step_xoffset);
5383 int step_yoffset = ABS(pos->step_yoffset);
5384 int step_delay = pos->step_delay;
5385 int current_door_state = door_state & door_token;
5386 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5387 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5388 boolean part_opening = (is_panel ? door_closing : door_opening);
5389 int start_step = (part_opening ? pos->start_step_opening :
5390 pos->start_step_closing);
5391 float move_xsize = (step_xoffset ? g->width : 0);
5392 float move_ysize = (step_yoffset ? g->height : 0);
5393 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5394 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5395 int move_steps = (move_xsteps && move_ysteps ?
5396 MIN(move_xsteps, move_ysteps) :
5397 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5398 int move_delay = move_steps * step_delay;
5400 if (door_part_skip[nr])
5403 max_move_delay = MAX(max_move_delay, move_delay);
5404 max_step_delay = (max_step_delay == 0 ? step_delay :
5405 euclid(max_step_delay, step_delay));
5406 num_steps[nr] = move_steps;
5410 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5412 panel_has_doors[door_index] = TRUE;
5416 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5418 num_move_steps = max_move_delay / max_step_delay;
5419 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5421 door_delay.value = max_step_delay;
5423 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5425 start = num_move_steps - 1;
5429 // opening door sound has priority over simultaneously closing door
5430 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5432 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5434 if (door_state & DOOR_OPEN_1)
5435 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5436 if (door_state & DOOR_OPEN_2)
5437 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5439 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5441 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5443 if (door_state & DOOR_CLOSE_1)
5444 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5445 if (door_state & DOOR_CLOSE_2)
5446 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5450 for (k = start; k < num_move_steps; k++)
5452 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5454 door_part_done_all = TRUE;
5456 for (i = 0; i < NUM_DOORS; i++)
5457 door_panel_drawn[i] = FALSE;
5459 for (i = 0; i < MAX_DOOR_PARTS; i++)
5461 int nr = door_part_order[i].nr;
5462 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5463 struct DoorPartPosInfo *pos = dpc->pos;
5464 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5465 int door_token = dpc->door_token;
5466 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5467 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5468 boolean is_panel_and_door_has_closed = FALSE;
5469 struct Rect *door_rect = &door_rect_list[door_index];
5470 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5472 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5473 int current_door_state = door_state & door_token;
5474 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5475 boolean door_closing = !door_opening;
5476 boolean part_opening = (is_panel ? door_closing : door_opening);
5477 boolean part_closing = !part_opening;
5478 int start_step = (part_opening ? pos->start_step_opening :
5479 pos->start_step_closing);
5480 int step_delay = pos->step_delay;
5481 int step_factor = step_delay / max_step_delay;
5482 int k1 = (step_factor ? k / step_factor + 1 : k);
5483 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5484 int kk = MAX(0, k2);
5487 int src_x, src_y, src_xx, src_yy;
5488 int dst_x, dst_y, dst_xx, dst_yy;
5491 if (door_part_skip[nr])
5494 if (!(door_state & door_token))
5502 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5503 int kk_door = MAX(0, k2_door);
5504 int sync_frame = kk_door * door_delay.value;
5505 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5507 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5508 &g_src_x, &g_src_y);
5513 if (!door_panel_drawn[door_index])
5515 ClearRectangle(drawto, door_rect->x, door_rect->y,
5516 door_rect->width, door_rect->height);
5518 door_panel_drawn[door_index] = TRUE;
5521 // draw opening or closing door parts
5523 if (pos->step_xoffset < 0) // door part on right side
5526 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5529 if (dst_xx + width > door_rect->width)
5530 width = door_rect->width - dst_xx;
5532 else // door part on left side
5535 dst_xx = pos->x - kk * pos->step_xoffset;
5539 src_xx = ABS(dst_xx);
5543 width = g->width - src_xx;
5545 if (width > door_rect->width)
5546 width = door_rect->width;
5548 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5551 if (pos->step_yoffset < 0) // door part on bottom side
5554 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5557 if (dst_yy + height > door_rect->height)
5558 height = door_rect->height - dst_yy;
5560 else // door part on top side
5563 dst_yy = pos->y - kk * pos->step_yoffset;
5567 src_yy = ABS(dst_yy);
5571 height = g->height - src_yy;
5574 src_x = g_src_x + src_xx;
5575 src_y = g_src_y + src_yy;
5577 dst_x = door_rect->x + dst_xx;
5578 dst_y = door_rect->y + dst_yy;
5580 is_panel_and_door_has_closed =
5583 panel_has_doors[door_index] &&
5584 k >= num_move_steps_doors_only - 1);
5586 if (width >= 0 && width <= g->width &&
5587 height >= 0 && height <= g->height &&
5588 !is_panel_and_door_has_closed)
5590 if (is_panel || !pos->draw_masked)
5591 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5594 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5598 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5600 if ((part_opening && (width < 0 || height < 0)) ||
5601 (part_closing && (width >= g->width && height >= g->height)))
5602 door_part_done[nr] = TRUE;
5604 // continue door part animations, but not panel after door has closed
5605 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5606 door_part_done_all = FALSE;
5609 if (!(door_state & DOOR_NO_DELAY))
5612 HandleGameActions();
5616 SkipUntilDelayReached(&door_delay, &k, last_frame);
5618 // prevent OS (Windows) from complaining about program not responding
5622 if (door_part_done_all)
5626 if (!(door_state & DOOR_NO_DELAY))
5628 // wait for specified door action post delay
5629 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5630 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5631 else if (door_state & DOOR_ACTION_1)
5632 door_delay.value = door_1.post_delay;
5633 else if (door_state & DOOR_ACTION_2)
5634 door_delay.value = door_2.post_delay;
5636 while (!DelayReached(&door_delay))
5639 HandleGameActions();
5646 if (door_state & DOOR_ACTION_1)
5647 door1 = door_state & DOOR_ACTION_1;
5648 if (door_state & DOOR_ACTION_2)
5649 door2 = door_state & DOOR_ACTION_2;
5651 // draw masked border over door area
5652 DrawMaskedBorder(REDRAW_DOOR_1);
5653 DrawMaskedBorder(REDRAW_DOOR_2);
5655 ClearAutoRepeatKeyEvents();
5657 return (door1 | door2);
5660 static boolean useSpecialEditorDoor(void)
5662 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5663 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5665 // do not draw special editor door if editor border defined or redefined
5666 if (graphic_info[graphic].bitmap != NULL || redefined)
5669 // do not draw special editor door if global border defined to be empty
5670 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5673 // do not draw special editor door if viewport definitions do not match
5677 EY + EYSIZE != VY + VYSIZE)
5683 void DrawSpecialEditorDoor(void)
5685 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5686 int top_border_width = gfx1->width;
5687 int top_border_height = gfx1->height;
5688 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5689 int ex = EX - outer_border;
5690 int ey = EY - outer_border;
5691 int vy = VY - outer_border;
5692 int exsize = EXSIZE + 2 * outer_border;
5694 if (!useSpecialEditorDoor())
5697 // draw bigger level editor toolbox window
5698 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5699 top_border_width, top_border_height, ex, ey - top_border_height);
5700 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5701 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5703 redraw_mask |= REDRAW_ALL;
5706 void UndrawSpecialEditorDoor(void)
5708 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5709 int top_border_width = gfx1->width;
5710 int top_border_height = gfx1->height;
5711 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5712 int ex = EX - outer_border;
5713 int ey = EY - outer_border;
5714 int ey_top = ey - top_border_height;
5715 int exsize = EXSIZE + 2 * outer_border;
5716 int eysize = EYSIZE + 2 * outer_border;
5718 if (!useSpecialEditorDoor())
5721 // draw normal tape recorder window
5722 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5724 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5725 ex, ey_top, top_border_width, top_border_height,
5727 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5728 ex, ey, exsize, eysize, ex, ey);
5732 // if screen background is set to "[NONE]", clear editor toolbox window
5733 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5734 ClearRectangle(drawto, ex, ey, exsize, eysize);
5737 redraw_mask |= REDRAW_ALL;
5741 // ---------- new tool button stuff -------------------------------------------
5746 struct TextPosInfo *pos;
5748 boolean is_touch_button;
5750 } toolbutton_info[NUM_TOOL_BUTTONS] =
5753 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5754 TOOL_CTRL_ID_YES, FALSE, "yes"
5757 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5758 TOOL_CTRL_ID_NO, FALSE, "no"
5761 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5762 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5765 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5766 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5769 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5770 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5773 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5774 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5777 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5778 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5781 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5782 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5785 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5786 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5789 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5790 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5794 void CreateToolButtons(void)
5798 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5800 int graphic = toolbutton_info[i].graphic;
5801 struct GraphicInfo *gfx = &graphic_info[graphic];
5802 struct TextPosInfo *pos = toolbutton_info[i].pos;
5803 struct GadgetInfo *gi;
5804 Bitmap *deco_bitmap = None;
5805 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5806 unsigned int event_mask = GD_EVENT_RELEASED;
5807 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5808 int base_x = (is_touch_button ? 0 : DX);
5809 int base_y = (is_touch_button ? 0 : DY);
5810 int gd_x = gfx->src_x;
5811 int gd_y = gfx->src_y;
5812 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5813 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5818 // do not use touch buttons if overlay touch buttons are disabled
5819 if (is_touch_button && !setup.touch.overlay_buttons)
5822 if (global.use_envelope_request && !is_touch_button)
5824 setRequestPosition(&base_x, &base_y, TRUE);
5826 // check if request buttons are outside of envelope and fix, if needed
5827 if (x < 0 || x + gfx->width > request.width ||
5828 y < 0 || y + gfx->height > request.height)
5830 if (id == TOOL_CTRL_ID_YES)
5833 y = request.height - 2 * request.border_size - gfx->height;
5835 else if (id == TOOL_CTRL_ID_NO)
5837 x = request.width - 2 * request.border_size - gfx->width;
5838 y = request.height - 2 * request.border_size - gfx->height;
5840 else if (id == TOOL_CTRL_ID_CONFIRM)
5842 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5843 y = request.height - 2 * request.border_size - gfx->height;
5845 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5847 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5849 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5850 y = request.height - 2 * request.border_size - gfx->height * 2;
5852 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5853 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5858 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5861 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5863 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5864 pos->size, &deco_bitmap, &deco_x, &deco_y);
5865 deco_xpos = (gfx->width - pos->size) / 2;
5866 deco_ypos = (gfx->height - pos->size) / 2;
5869 gi = CreateGadget(GDI_CUSTOM_ID, id,
5870 GDI_IMAGE_ID, graphic,
5871 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5874 GDI_WIDTH, gfx->width,
5875 GDI_HEIGHT, gfx->height,
5876 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5877 GDI_STATE, GD_BUTTON_UNPRESSED,
5878 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5879 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5880 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5881 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5882 GDI_DECORATION_SIZE, pos->size, pos->size,
5883 GDI_DECORATION_SHIFTING, 1, 1,
5884 GDI_DIRECT_DRAW, FALSE,
5885 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5886 GDI_EVENT_MASK, event_mask,
5887 GDI_CALLBACK_ACTION, HandleToolButtons,
5891 Fail("cannot create gadget");
5893 tool_gadget[id] = gi;
5897 void FreeToolButtons(void)
5901 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5902 FreeGadget(tool_gadget[i]);
5905 static void MapToolButtons(unsigned int req_state)
5907 if (req_state & REQ_ASK)
5909 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5910 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5911 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5912 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5914 else if (req_state & REQ_CONFIRM)
5916 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5917 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5919 else if (req_state & REQ_PLAYER)
5921 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5922 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5923 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5924 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5928 static void UnmapToolButtons(void)
5932 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5933 UnmapGadget(tool_gadget[i]);
5936 static void HandleToolButtons(struct GadgetInfo *gi)
5938 request_gadget_id = gi->custom_id;
5941 static struct Mapping_EM_to_RND_object
5944 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5945 boolean is_backside; // backside of moving element
5951 em_object_mapping_list[GAME_TILE_MAX + 1] =
5954 Zborder, FALSE, FALSE,
5958 Zplayer, FALSE, FALSE,
5967 Ztank, FALSE, FALSE,
5971 Zeater, FALSE, FALSE,
5975 Zdynamite, FALSE, FALSE,
5979 Zboom, FALSE, FALSE,
5984 Xchain, FALSE, FALSE,
5985 EL_DEFAULT, ACTION_EXPLODING, -1
5988 Xboom_bug, FALSE, FALSE,
5989 EL_BUG, ACTION_EXPLODING, -1
5992 Xboom_tank, FALSE, FALSE,
5993 EL_SPACESHIP, ACTION_EXPLODING, -1
5996 Xboom_android, FALSE, FALSE,
5997 EL_EMC_ANDROID, ACTION_OTHER, -1
6000 Xboom_1, FALSE, FALSE,
6001 EL_DEFAULT, ACTION_EXPLODING, -1
6004 Xboom_2, FALSE, FALSE,
6005 EL_DEFAULT, ACTION_EXPLODING, -1
6009 Xblank, TRUE, FALSE,
6014 Xsplash_e, FALSE, FALSE,
6015 EL_ACID_SPLASH_RIGHT, -1, -1
6018 Xsplash_w, FALSE, FALSE,
6019 EL_ACID_SPLASH_LEFT, -1, -1
6023 Xplant, TRUE, FALSE,
6024 EL_EMC_PLANT, -1, -1
6027 Yplant, FALSE, FALSE,
6028 EL_EMC_PLANT, -1, -1
6032 Xacid_1, TRUE, FALSE,
6036 Xacid_2, FALSE, FALSE,
6040 Xacid_3, FALSE, FALSE,
6044 Xacid_4, FALSE, FALSE,
6048 Xacid_5, FALSE, FALSE,
6052 Xacid_6, FALSE, FALSE,
6056 Xacid_7, FALSE, FALSE,
6060 Xacid_8, FALSE, FALSE,
6065 Xfake_acid_1, TRUE, FALSE,
6066 EL_EMC_FAKE_ACID, -1, -1
6069 Xfake_acid_2, FALSE, FALSE,
6070 EL_EMC_FAKE_ACID, -1, -1
6073 Xfake_acid_3, FALSE, FALSE,
6074 EL_EMC_FAKE_ACID, -1, -1
6077 Xfake_acid_4, FALSE, FALSE,
6078 EL_EMC_FAKE_ACID, -1, -1
6081 Xfake_acid_5, FALSE, FALSE,
6082 EL_EMC_FAKE_ACID, -1, -1
6085 Xfake_acid_6, FALSE, FALSE,
6086 EL_EMC_FAKE_ACID, -1, -1
6089 Xfake_acid_7, FALSE, FALSE,
6090 EL_EMC_FAKE_ACID, -1, -1
6093 Xfake_acid_8, FALSE, FALSE,
6094 EL_EMC_FAKE_ACID, -1, -1
6098 Xfake_acid_1_player, FALSE, FALSE,
6099 EL_EMC_FAKE_ACID, -1, -1
6102 Xfake_acid_2_player, FALSE, FALSE,
6103 EL_EMC_FAKE_ACID, -1, -1
6106 Xfake_acid_3_player, FALSE, FALSE,
6107 EL_EMC_FAKE_ACID, -1, -1
6110 Xfake_acid_4_player, FALSE, FALSE,
6111 EL_EMC_FAKE_ACID, -1, -1
6114 Xfake_acid_5_player, FALSE, FALSE,
6115 EL_EMC_FAKE_ACID, -1, -1
6118 Xfake_acid_6_player, FALSE, FALSE,
6119 EL_EMC_FAKE_ACID, -1, -1
6122 Xfake_acid_7_player, FALSE, FALSE,
6123 EL_EMC_FAKE_ACID, -1, -1
6126 Xfake_acid_8_player, FALSE, FALSE,
6127 EL_EMC_FAKE_ACID, -1, -1
6131 Xgrass, TRUE, FALSE,
6132 EL_EMC_GRASS, -1, -1
6135 Ygrass_nB, FALSE, FALSE,
6136 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6139 Ygrass_eB, FALSE, FALSE,
6140 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6143 Ygrass_sB, FALSE, FALSE,
6144 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6147 Ygrass_wB, FALSE, FALSE,
6148 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6156 Ydirt_nB, FALSE, FALSE,
6157 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6160 Ydirt_eB, FALSE, FALSE,
6161 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6164 Ydirt_sB, FALSE, FALSE,
6165 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6168 Ydirt_wB, FALSE, FALSE,
6169 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6173 Xandroid, TRUE, FALSE,
6174 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6177 Xandroid_1_n, FALSE, FALSE,
6178 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6181 Xandroid_2_n, FALSE, FALSE,
6182 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6185 Xandroid_1_e, FALSE, FALSE,
6186 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6189 Xandroid_2_e, FALSE, FALSE,
6190 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6193 Xandroid_1_w, FALSE, FALSE,
6194 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6197 Xandroid_2_w, FALSE, FALSE,
6198 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6201 Xandroid_1_s, FALSE, FALSE,
6202 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6205 Xandroid_2_s, FALSE, FALSE,
6206 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6209 Yandroid_n, FALSE, FALSE,
6210 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6213 Yandroid_nB, FALSE, TRUE,
6214 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6217 Yandroid_ne, FALSE, FALSE,
6218 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6221 Yandroid_neB, FALSE, TRUE,
6222 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6225 Yandroid_e, FALSE, FALSE,
6226 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6229 Yandroid_eB, FALSE, TRUE,
6230 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6233 Yandroid_se, FALSE, FALSE,
6234 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6237 Yandroid_seB, FALSE, TRUE,
6238 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6241 Yandroid_s, FALSE, FALSE,
6242 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6245 Yandroid_sB, FALSE, TRUE,
6246 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6249 Yandroid_sw, FALSE, FALSE,
6250 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6253 Yandroid_swB, FALSE, TRUE,
6254 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6257 Yandroid_w, FALSE, FALSE,
6258 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6261 Yandroid_wB, FALSE, TRUE,
6262 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6265 Yandroid_nw, FALSE, FALSE,
6266 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6269 Yandroid_nwB, FALSE, TRUE,
6270 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6274 Xeater_n, TRUE, FALSE,
6275 EL_YAMYAM_UP, -1, -1
6278 Xeater_e, TRUE, FALSE,
6279 EL_YAMYAM_RIGHT, -1, -1
6282 Xeater_w, TRUE, FALSE,
6283 EL_YAMYAM_LEFT, -1, -1
6286 Xeater_s, TRUE, FALSE,
6287 EL_YAMYAM_DOWN, -1, -1
6290 Yeater_n, FALSE, FALSE,
6291 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6294 Yeater_nB, FALSE, TRUE,
6295 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6298 Yeater_e, FALSE, FALSE,
6299 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6302 Yeater_eB, FALSE, TRUE,
6303 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6306 Yeater_s, FALSE, FALSE,
6307 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6310 Yeater_sB, FALSE, TRUE,
6311 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6314 Yeater_w, FALSE, FALSE,
6315 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6318 Yeater_wB, FALSE, TRUE,
6319 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6322 Yeater_stone, FALSE, FALSE,
6323 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6326 Yeater_spring, FALSE, FALSE,
6327 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6331 Xalien, TRUE, FALSE,
6335 Xalien_pause, FALSE, FALSE,
6339 Yalien_n, FALSE, FALSE,
6340 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6343 Yalien_nB, FALSE, TRUE,
6344 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6347 Yalien_e, FALSE, FALSE,
6348 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6351 Yalien_eB, FALSE, TRUE,
6352 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6355 Yalien_s, FALSE, FALSE,
6356 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6359 Yalien_sB, FALSE, TRUE,
6360 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6363 Yalien_w, FALSE, FALSE,
6364 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6367 Yalien_wB, FALSE, TRUE,
6368 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6371 Yalien_stone, FALSE, FALSE,
6372 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6375 Yalien_spring, FALSE, FALSE,
6376 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6380 Xbug_1_n, TRUE, FALSE,
6384 Xbug_1_e, TRUE, FALSE,
6385 EL_BUG_RIGHT, -1, -1
6388 Xbug_1_s, TRUE, FALSE,
6392 Xbug_1_w, TRUE, FALSE,
6396 Xbug_2_n, FALSE, FALSE,
6400 Xbug_2_e, FALSE, FALSE,
6401 EL_BUG_RIGHT, -1, -1
6404 Xbug_2_s, FALSE, FALSE,
6408 Xbug_2_w, FALSE, FALSE,
6412 Ybug_n, FALSE, FALSE,
6413 EL_BUG, ACTION_MOVING, MV_BIT_UP
6416 Ybug_nB, FALSE, TRUE,
6417 EL_BUG, ACTION_MOVING, MV_BIT_UP
6420 Ybug_e, FALSE, FALSE,
6421 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6424 Ybug_eB, FALSE, TRUE,
6425 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6428 Ybug_s, FALSE, FALSE,
6429 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6432 Ybug_sB, FALSE, TRUE,
6433 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6436 Ybug_w, FALSE, FALSE,
6437 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6440 Ybug_wB, FALSE, TRUE,
6441 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6444 Ybug_w_n, FALSE, FALSE,
6445 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6448 Ybug_n_e, FALSE, FALSE,
6449 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6452 Ybug_e_s, FALSE, FALSE,
6453 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6456 Ybug_s_w, FALSE, FALSE,
6457 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6460 Ybug_e_n, FALSE, FALSE,
6461 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6464 Ybug_s_e, FALSE, FALSE,
6465 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6468 Ybug_w_s, FALSE, FALSE,
6469 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6472 Ybug_n_w, FALSE, FALSE,
6473 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6476 Ybug_stone, FALSE, FALSE,
6477 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6480 Ybug_spring, FALSE, FALSE,
6481 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6485 Xtank_1_n, TRUE, FALSE,
6486 EL_SPACESHIP_UP, -1, -1
6489 Xtank_1_e, TRUE, FALSE,
6490 EL_SPACESHIP_RIGHT, -1, -1
6493 Xtank_1_s, TRUE, FALSE,
6494 EL_SPACESHIP_DOWN, -1, -1
6497 Xtank_1_w, TRUE, FALSE,
6498 EL_SPACESHIP_LEFT, -1, -1
6501 Xtank_2_n, FALSE, FALSE,
6502 EL_SPACESHIP_UP, -1, -1
6505 Xtank_2_e, FALSE, FALSE,
6506 EL_SPACESHIP_RIGHT, -1, -1
6509 Xtank_2_s, FALSE, FALSE,
6510 EL_SPACESHIP_DOWN, -1, -1
6513 Xtank_2_w, FALSE, FALSE,
6514 EL_SPACESHIP_LEFT, -1, -1
6517 Ytank_n, FALSE, FALSE,
6518 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6521 Ytank_nB, FALSE, TRUE,
6522 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6525 Ytank_e, FALSE, FALSE,
6526 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6529 Ytank_eB, FALSE, TRUE,
6530 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6533 Ytank_s, FALSE, FALSE,
6534 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6537 Ytank_sB, FALSE, TRUE,
6538 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6541 Ytank_w, FALSE, FALSE,
6542 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6545 Ytank_wB, FALSE, TRUE,
6546 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6549 Ytank_w_n, FALSE, FALSE,
6550 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6553 Ytank_n_e, FALSE, FALSE,
6554 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6557 Ytank_e_s, FALSE, FALSE,
6558 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6561 Ytank_s_w, FALSE, FALSE,
6562 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6565 Ytank_e_n, FALSE, FALSE,
6566 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6569 Ytank_s_e, FALSE, FALSE,
6570 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6573 Ytank_w_s, FALSE, FALSE,
6574 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6577 Ytank_n_w, FALSE, FALSE,
6578 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6581 Ytank_stone, FALSE, FALSE,
6582 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6585 Ytank_spring, FALSE, FALSE,
6586 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6590 Xemerald, TRUE, FALSE,
6594 Xemerald_pause, FALSE, FALSE,
6598 Xemerald_fall, FALSE, FALSE,
6602 Xemerald_shine, FALSE, FALSE,
6603 EL_EMERALD, ACTION_TWINKLING, -1
6606 Yemerald_s, FALSE, FALSE,
6607 EL_EMERALD, ACTION_FALLING, -1
6610 Yemerald_sB, FALSE, TRUE,
6611 EL_EMERALD, ACTION_FALLING, -1
6614 Yemerald_e, FALSE, FALSE,
6615 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6618 Yemerald_eB, FALSE, TRUE,
6619 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6622 Yemerald_w, FALSE, FALSE,
6623 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6626 Yemerald_wB, FALSE, TRUE,
6627 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6630 Yemerald_blank, FALSE, FALSE,
6631 EL_EMERALD, ACTION_COLLECTING, -1
6635 Xdiamond, TRUE, FALSE,
6639 Xdiamond_pause, FALSE, FALSE,
6643 Xdiamond_fall, FALSE, FALSE,
6647 Xdiamond_shine, FALSE, FALSE,
6648 EL_DIAMOND, ACTION_TWINKLING, -1
6651 Ydiamond_s, FALSE, FALSE,
6652 EL_DIAMOND, ACTION_FALLING, -1
6655 Ydiamond_sB, FALSE, TRUE,
6656 EL_DIAMOND, ACTION_FALLING, -1
6659 Ydiamond_e, FALSE, FALSE,
6660 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6663 Ydiamond_eB, FALSE, TRUE,
6664 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6667 Ydiamond_w, FALSE, FALSE,
6668 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6671 Ydiamond_wB, FALSE, TRUE,
6672 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6675 Ydiamond_blank, FALSE, FALSE,
6676 EL_DIAMOND, ACTION_COLLECTING, -1
6679 Ydiamond_stone, FALSE, FALSE,
6680 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6684 Xstone, TRUE, FALSE,
6688 Xstone_pause, FALSE, FALSE,
6692 Xstone_fall, FALSE, FALSE,
6696 Ystone_s, FALSE, FALSE,
6697 EL_ROCK, ACTION_FALLING, -1
6700 Ystone_sB, FALSE, TRUE,
6701 EL_ROCK, ACTION_FALLING, -1
6704 Ystone_e, FALSE, FALSE,
6705 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6708 Ystone_eB, FALSE, TRUE,
6709 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6712 Ystone_w, FALSE, FALSE,
6713 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6716 Ystone_wB, FALSE, TRUE,
6717 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6725 Xbomb_pause, FALSE, FALSE,
6729 Xbomb_fall, FALSE, FALSE,
6733 Ybomb_s, FALSE, FALSE,
6734 EL_BOMB, ACTION_FALLING, -1
6737 Ybomb_sB, FALSE, TRUE,
6738 EL_BOMB, ACTION_FALLING, -1
6741 Ybomb_e, FALSE, FALSE,
6742 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6745 Ybomb_eB, FALSE, TRUE,
6746 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6749 Ybomb_w, FALSE, FALSE,
6750 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6753 Ybomb_wB, FALSE, TRUE,
6754 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6757 Ybomb_blank, FALSE, FALSE,
6758 EL_BOMB, ACTION_ACTIVATING, -1
6766 Xnut_pause, FALSE, FALSE,
6770 Xnut_fall, FALSE, FALSE,
6774 Ynut_s, FALSE, FALSE,
6775 EL_NUT, ACTION_FALLING, -1
6778 Ynut_sB, FALSE, TRUE,
6779 EL_NUT, ACTION_FALLING, -1
6782 Ynut_e, FALSE, FALSE,
6783 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6786 Ynut_eB, FALSE, TRUE,
6787 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6790 Ynut_w, FALSE, FALSE,
6791 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6794 Ynut_wB, FALSE, TRUE,
6795 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6798 Ynut_stone, FALSE, FALSE,
6799 EL_NUT, ACTION_BREAKING, -1
6803 Xspring, TRUE, FALSE,
6807 Xspring_pause, FALSE, FALSE,
6811 Xspring_e, TRUE, FALSE,
6812 EL_SPRING_RIGHT, -1, -1
6815 Xspring_w, TRUE, FALSE,
6816 EL_SPRING_LEFT, -1, -1
6819 Xspring_fall, FALSE, FALSE,
6823 Yspring_s, FALSE, FALSE,
6824 EL_SPRING, ACTION_FALLING, -1
6827 Yspring_sB, FALSE, TRUE,
6828 EL_SPRING, ACTION_FALLING, -1
6831 Yspring_e, FALSE, FALSE,
6832 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6835 Yspring_eB, FALSE, TRUE,
6836 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6839 Yspring_w, FALSE, FALSE,
6840 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6843 Yspring_wB, FALSE, TRUE,
6844 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6847 Yspring_alien_e, FALSE, FALSE,
6848 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6851 Yspring_alien_eB, FALSE, TRUE,
6852 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6855 Yspring_alien_w, FALSE, FALSE,
6856 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6859 Yspring_alien_wB, FALSE, TRUE,
6860 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6864 Xpush_emerald_e, FALSE, FALSE,
6865 EL_EMERALD, -1, MV_BIT_RIGHT
6868 Xpush_emerald_w, FALSE, FALSE,
6869 EL_EMERALD, -1, MV_BIT_LEFT
6872 Xpush_diamond_e, FALSE, FALSE,
6873 EL_DIAMOND, -1, MV_BIT_RIGHT
6876 Xpush_diamond_w, FALSE, FALSE,
6877 EL_DIAMOND, -1, MV_BIT_LEFT
6880 Xpush_stone_e, FALSE, FALSE,
6881 EL_ROCK, -1, MV_BIT_RIGHT
6884 Xpush_stone_w, FALSE, FALSE,
6885 EL_ROCK, -1, MV_BIT_LEFT
6888 Xpush_bomb_e, FALSE, FALSE,
6889 EL_BOMB, -1, MV_BIT_RIGHT
6892 Xpush_bomb_w, FALSE, FALSE,
6893 EL_BOMB, -1, MV_BIT_LEFT
6896 Xpush_nut_e, FALSE, FALSE,
6897 EL_NUT, -1, MV_BIT_RIGHT
6900 Xpush_nut_w, FALSE, FALSE,
6901 EL_NUT, -1, MV_BIT_LEFT
6904 Xpush_spring_e, FALSE, FALSE,
6905 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6908 Xpush_spring_w, FALSE, FALSE,
6909 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6913 Xdynamite, TRUE, FALSE,
6914 EL_EM_DYNAMITE, -1, -1
6917 Ydynamite_blank, FALSE, FALSE,
6918 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6921 Xdynamite_1, TRUE, FALSE,
6922 EL_EM_DYNAMITE_ACTIVE, -1, -1
6925 Xdynamite_2, FALSE, FALSE,
6926 EL_EM_DYNAMITE_ACTIVE, -1, -1
6929 Xdynamite_3, FALSE, FALSE,
6930 EL_EM_DYNAMITE_ACTIVE, -1, -1
6933 Xdynamite_4, FALSE, FALSE,
6934 EL_EM_DYNAMITE_ACTIVE, -1, -1
6938 Xkey_1, TRUE, FALSE,
6942 Xkey_2, TRUE, FALSE,
6946 Xkey_3, TRUE, FALSE,
6950 Xkey_4, TRUE, FALSE,
6954 Xkey_5, TRUE, FALSE,
6955 EL_EMC_KEY_5, -1, -1
6958 Xkey_6, TRUE, FALSE,
6959 EL_EMC_KEY_6, -1, -1
6962 Xkey_7, TRUE, FALSE,
6963 EL_EMC_KEY_7, -1, -1
6966 Xkey_8, TRUE, FALSE,
6967 EL_EMC_KEY_8, -1, -1
6971 Xdoor_1, TRUE, FALSE,
6972 EL_EM_GATE_1, -1, -1
6975 Xdoor_2, TRUE, FALSE,
6976 EL_EM_GATE_2, -1, -1
6979 Xdoor_3, TRUE, FALSE,
6980 EL_EM_GATE_3, -1, -1
6983 Xdoor_4, TRUE, FALSE,
6984 EL_EM_GATE_4, -1, -1
6987 Xdoor_5, TRUE, FALSE,
6988 EL_EMC_GATE_5, -1, -1
6991 Xdoor_6, TRUE, FALSE,
6992 EL_EMC_GATE_6, -1, -1
6995 Xdoor_7, TRUE, FALSE,
6996 EL_EMC_GATE_7, -1, -1
6999 Xdoor_8, TRUE, FALSE,
7000 EL_EMC_GATE_8, -1, -1
7004 Xfake_door_1, TRUE, FALSE,
7005 EL_EM_GATE_1_GRAY, -1, -1
7008 Xfake_door_2, TRUE, FALSE,
7009 EL_EM_GATE_2_GRAY, -1, -1
7012 Xfake_door_3, TRUE, FALSE,
7013 EL_EM_GATE_3_GRAY, -1, -1
7016 Xfake_door_4, TRUE, FALSE,
7017 EL_EM_GATE_4_GRAY, -1, -1
7020 Xfake_door_5, TRUE, FALSE,
7021 EL_EMC_GATE_5_GRAY, -1, -1
7024 Xfake_door_6, TRUE, FALSE,
7025 EL_EMC_GATE_6_GRAY, -1, -1
7028 Xfake_door_7, TRUE, FALSE,
7029 EL_EMC_GATE_7_GRAY, -1, -1
7032 Xfake_door_8, TRUE, FALSE,
7033 EL_EMC_GATE_8_GRAY, -1, -1
7037 Xballoon, TRUE, FALSE,
7041 Yballoon_n, FALSE, FALSE,
7042 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7045 Yballoon_nB, FALSE, TRUE,
7046 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7049 Yballoon_e, FALSE, FALSE,
7050 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7053 Yballoon_eB, FALSE, TRUE,
7054 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7057 Yballoon_s, FALSE, FALSE,
7058 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7061 Yballoon_sB, FALSE, TRUE,
7062 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7065 Yballoon_w, FALSE, FALSE,
7066 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7069 Yballoon_wB, FALSE, TRUE,
7070 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7074 Xball_1, TRUE, FALSE,
7075 EL_EMC_MAGIC_BALL, -1, -1
7078 Yball_1, FALSE, FALSE,
7079 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7082 Xball_2, FALSE, FALSE,
7083 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7086 Yball_2, FALSE, FALSE,
7087 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7090 Yball_blank, FALSE, FALSE,
7091 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7095 Xamoeba_1, TRUE, FALSE,
7096 EL_AMOEBA_DRY, ACTION_OTHER, -1
7099 Xamoeba_2, FALSE, FALSE,
7100 EL_AMOEBA_DRY, ACTION_OTHER, -1
7103 Xamoeba_3, FALSE, FALSE,
7104 EL_AMOEBA_DRY, ACTION_OTHER, -1
7107 Xamoeba_4, FALSE, FALSE,
7108 EL_AMOEBA_DRY, ACTION_OTHER, -1
7111 Xamoeba_5, TRUE, FALSE,
7112 EL_AMOEBA_WET, ACTION_OTHER, -1
7115 Xamoeba_6, FALSE, FALSE,
7116 EL_AMOEBA_WET, ACTION_OTHER, -1
7119 Xamoeba_7, FALSE, FALSE,
7120 EL_AMOEBA_WET, ACTION_OTHER, -1
7123 Xamoeba_8, FALSE, FALSE,
7124 EL_AMOEBA_WET, ACTION_OTHER, -1
7129 EL_AMOEBA_DROP, ACTION_GROWING, -1
7132 Xdrip_fall, FALSE, FALSE,
7133 EL_AMOEBA_DROP, -1, -1
7136 Xdrip_stretch, FALSE, FALSE,
7137 EL_AMOEBA_DROP, ACTION_FALLING, -1
7140 Xdrip_stretchB, FALSE, TRUE,
7141 EL_AMOEBA_DROP, ACTION_FALLING, -1
7144 Ydrip_1_s, FALSE, FALSE,
7145 EL_AMOEBA_DROP, ACTION_FALLING, -1
7148 Ydrip_1_sB, FALSE, TRUE,
7149 EL_AMOEBA_DROP, ACTION_FALLING, -1
7152 Ydrip_2_s, FALSE, FALSE,
7153 EL_AMOEBA_DROP, ACTION_FALLING, -1
7156 Ydrip_2_sB, FALSE, TRUE,
7157 EL_AMOEBA_DROP, ACTION_FALLING, -1
7161 Xwonderwall, TRUE, FALSE,
7162 EL_MAGIC_WALL, -1, -1
7165 Ywonderwall, FALSE, FALSE,
7166 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7170 Xwheel, TRUE, FALSE,
7171 EL_ROBOT_WHEEL, -1, -1
7174 Ywheel, FALSE, FALSE,
7175 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7179 Xswitch, TRUE, FALSE,
7180 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7183 Yswitch, FALSE, FALSE,
7184 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7188 Xbumper, TRUE, FALSE,
7189 EL_EMC_SPRING_BUMPER, -1, -1
7192 Ybumper, FALSE, FALSE,
7193 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7197 Xacid_nw, TRUE, FALSE,
7198 EL_ACID_POOL_TOPLEFT, -1, -1
7201 Xacid_ne, TRUE, FALSE,
7202 EL_ACID_POOL_TOPRIGHT, -1, -1
7205 Xacid_sw, TRUE, FALSE,
7206 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7209 Xacid_s, TRUE, FALSE,
7210 EL_ACID_POOL_BOTTOM, -1, -1
7213 Xacid_se, TRUE, FALSE,
7214 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7218 Xfake_blank, TRUE, FALSE,
7219 EL_INVISIBLE_WALL, -1, -1
7222 Yfake_blank, FALSE, FALSE,
7223 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7227 Xfake_grass, TRUE, FALSE,
7228 EL_EMC_FAKE_GRASS, -1, -1
7231 Yfake_grass, FALSE, FALSE,
7232 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7236 Xfake_amoeba, TRUE, FALSE,
7237 EL_EMC_DRIPPER, -1, -1
7240 Yfake_amoeba, FALSE, FALSE,
7241 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7245 Xlenses, TRUE, FALSE,
7246 EL_EMC_LENSES, -1, -1
7250 Xmagnify, TRUE, FALSE,
7251 EL_EMC_MAGNIFIER, -1, -1
7256 EL_QUICKSAND_EMPTY, -1, -1
7259 Xsand_stone, TRUE, FALSE,
7260 EL_QUICKSAND_FULL, -1, -1
7263 Xsand_stonein_1, FALSE, TRUE,
7264 EL_ROCK, ACTION_FILLING, -1
7267 Xsand_stonein_2, FALSE, TRUE,
7268 EL_ROCK, ACTION_FILLING, -1
7271 Xsand_stonein_3, FALSE, TRUE,
7272 EL_ROCK, ACTION_FILLING, -1
7275 Xsand_stonein_4, FALSE, TRUE,
7276 EL_ROCK, ACTION_FILLING, -1
7279 Xsand_sandstone_1, FALSE, FALSE,
7280 EL_QUICKSAND_FILLING, -1, -1
7283 Xsand_sandstone_2, FALSE, FALSE,
7284 EL_QUICKSAND_FILLING, -1, -1
7287 Xsand_sandstone_3, FALSE, FALSE,
7288 EL_QUICKSAND_FILLING, -1, -1
7291 Xsand_sandstone_4, FALSE, FALSE,
7292 EL_QUICKSAND_FILLING, -1, -1
7295 Xsand_stonesand_1, FALSE, FALSE,
7296 EL_QUICKSAND_EMPTYING, -1, -1
7299 Xsand_stonesand_2, FALSE, FALSE,
7300 EL_QUICKSAND_EMPTYING, -1, -1
7303 Xsand_stonesand_3, FALSE, FALSE,
7304 EL_QUICKSAND_EMPTYING, -1, -1
7307 Xsand_stonesand_4, FALSE, FALSE,
7308 EL_QUICKSAND_EMPTYING, -1, -1
7311 Xsand_stoneout_1, FALSE, FALSE,
7312 EL_ROCK, ACTION_EMPTYING, -1
7315 Xsand_stoneout_2, FALSE, FALSE,
7316 EL_ROCK, ACTION_EMPTYING, -1
7319 Xsand_stonesand_quickout_1, FALSE, FALSE,
7320 EL_QUICKSAND_EMPTYING, -1, -1
7323 Xsand_stonesand_quickout_2, FALSE, FALSE,
7324 EL_QUICKSAND_EMPTYING, -1, -1
7328 Xslide_ns, TRUE, FALSE,
7329 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7332 Yslide_ns_blank, FALSE, FALSE,
7333 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7336 Xslide_ew, TRUE, FALSE,
7337 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7340 Yslide_ew_blank, FALSE, FALSE,
7341 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7345 Xwind_n, TRUE, FALSE,
7346 EL_BALLOON_SWITCH_UP, -1, -1
7349 Xwind_e, TRUE, FALSE,
7350 EL_BALLOON_SWITCH_RIGHT, -1, -1
7353 Xwind_s, TRUE, FALSE,
7354 EL_BALLOON_SWITCH_DOWN, -1, -1
7357 Xwind_w, TRUE, FALSE,
7358 EL_BALLOON_SWITCH_LEFT, -1, -1
7361 Xwind_any, TRUE, FALSE,
7362 EL_BALLOON_SWITCH_ANY, -1, -1
7365 Xwind_stop, TRUE, FALSE,
7366 EL_BALLOON_SWITCH_NONE, -1, -1
7371 EL_EM_EXIT_CLOSED, -1, -1
7374 Xexit_1, TRUE, FALSE,
7375 EL_EM_EXIT_OPEN, -1, -1
7378 Xexit_2, FALSE, FALSE,
7379 EL_EM_EXIT_OPEN, -1, -1
7382 Xexit_3, FALSE, FALSE,
7383 EL_EM_EXIT_OPEN, -1, -1
7387 Xpause, FALSE, FALSE,
7392 Xwall_1, TRUE, FALSE,
7396 Xwall_2, TRUE, FALSE,
7397 EL_EMC_WALL_14, -1, -1
7400 Xwall_3, TRUE, FALSE,
7401 EL_EMC_WALL_15, -1, -1
7404 Xwall_4, TRUE, FALSE,
7405 EL_EMC_WALL_16, -1, -1
7409 Xroundwall_1, TRUE, FALSE,
7410 EL_WALL_SLIPPERY, -1, -1
7413 Xroundwall_2, TRUE, FALSE,
7414 EL_EMC_WALL_SLIPPERY_2, -1, -1
7417 Xroundwall_3, TRUE, FALSE,
7418 EL_EMC_WALL_SLIPPERY_3, -1, -1
7421 Xroundwall_4, TRUE, FALSE,
7422 EL_EMC_WALL_SLIPPERY_4, -1, -1
7426 Xsteel_1, TRUE, FALSE,
7427 EL_STEELWALL, -1, -1
7430 Xsteel_2, TRUE, FALSE,
7431 EL_EMC_STEELWALL_2, -1, -1
7434 Xsteel_3, TRUE, FALSE,
7435 EL_EMC_STEELWALL_3, -1, -1
7438 Xsteel_4, TRUE, FALSE,
7439 EL_EMC_STEELWALL_4, -1, -1
7443 Xdecor_1, TRUE, FALSE,
7444 EL_EMC_WALL_8, -1, -1
7447 Xdecor_2, TRUE, FALSE,
7448 EL_EMC_WALL_6, -1, -1
7451 Xdecor_3, TRUE, FALSE,
7452 EL_EMC_WALL_4, -1, -1
7455 Xdecor_4, TRUE, FALSE,
7456 EL_EMC_WALL_7, -1, -1
7459 Xdecor_5, TRUE, FALSE,
7460 EL_EMC_WALL_5, -1, -1
7463 Xdecor_6, TRUE, FALSE,
7464 EL_EMC_WALL_9, -1, -1
7467 Xdecor_7, TRUE, FALSE,
7468 EL_EMC_WALL_10, -1, -1
7471 Xdecor_8, TRUE, FALSE,
7472 EL_EMC_WALL_1, -1, -1
7475 Xdecor_9, TRUE, FALSE,
7476 EL_EMC_WALL_2, -1, -1
7479 Xdecor_10, TRUE, FALSE,
7480 EL_EMC_WALL_3, -1, -1
7483 Xdecor_11, TRUE, FALSE,
7484 EL_EMC_WALL_11, -1, -1
7487 Xdecor_12, TRUE, FALSE,
7488 EL_EMC_WALL_12, -1, -1
7492 Xalpha_0, TRUE, FALSE,
7493 EL_CHAR('0'), -1, -1
7496 Xalpha_1, TRUE, FALSE,
7497 EL_CHAR('1'), -1, -1
7500 Xalpha_2, TRUE, FALSE,
7501 EL_CHAR('2'), -1, -1
7504 Xalpha_3, TRUE, FALSE,
7505 EL_CHAR('3'), -1, -1
7508 Xalpha_4, TRUE, FALSE,
7509 EL_CHAR('4'), -1, -1
7512 Xalpha_5, TRUE, FALSE,
7513 EL_CHAR('5'), -1, -1
7516 Xalpha_6, TRUE, FALSE,
7517 EL_CHAR('6'), -1, -1
7520 Xalpha_7, TRUE, FALSE,
7521 EL_CHAR('7'), -1, -1
7524 Xalpha_8, TRUE, FALSE,
7525 EL_CHAR('8'), -1, -1
7528 Xalpha_9, TRUE, FALSE,
7529 EL_CHAR('9'), -1, -1
7532 Xalpha_excla, TRUE, FALSE,
7533 EL_CHAR('!'), -1, -1
7536 Xalpha_apost, TRUE, FALSE,
7537 EL_CHAR('\''), -1, -1
7540 Xalpha_comma, TRUE, FALSE,
7541 EL_CHAR(','), -1, -1
7544 Xalpha_minus, TRUE, FALSE,
7545 EL_CHAR('-'), -1, -1
7548 Xalpha_perio, TRUE, FALSE,
7549 EL_CHAR('.'), -1, -1
7552 Xalpha_colon, TRUE, FALSE,
7553 EL_CHAR(':'), -1, -1
7556 Xalpha_quest, TRUE, FALSE,
7557 EL_CHAR('?'), -1, -1
7560 Xalpha_a, TRUE, FALSE,
7561 EL_CHAR('A'), -1, -1
7564 Xalpha_b, TRUE, FALSE,
7565 EL_CHAR('B'), -1, -1
7568 Xalpha_c, TRUE, FALSE,
7569 EL_CHAR('C'), -1, -1
7572 Xalpha_d, TRUE, FALSE,
7573 EL_CHAR('D'), -1, -1
7576 Xalpha_e, TRUE, FALSE,
7577 EL_CHAR('E'), -1, -1
7580 Xalpha_f, TRUE, FALSE,
7581 EL_CHAR('F'), -1, -1
7584 Xalpha_g, TRUE, FALSE,
7585 EL_CHAR('G'), -1, -1
7588 Xalpha_h, TRUE, FALSE,
7589 EL_CHAR('H'), -1, -1
7592 Xalpha_i, TRUE, FALSE,
7593 EL_CHAR('I'), -1, -1
7596 Xalpha_j, TRUE, FALSE,
7597 EL_CHAR('J'), -1, -1
7600 Xalpha_k, TRUE, FALSE,
7601 EL_CHAR('K'), -1, -1
7604 Xalpha_l, TRUE, FALSE,
7605 EL_CHAR('L'), -1, -1
7608 Xalpha_m, TRUE, FALSE,
7609 EL_CHAR('M'), -1, -1
7612 Xalpha_n, TRUE, FALSE,
7613 EL_CHAR('N'), -1, -1
7616 Xalpha_o, TRUE, FALSE,
7617 EL_CHAR('O'), -1, -1
7620 Xalpha_p, TRUE, FALSE,
7621 EL_CHAR('P'), -1, -1
7624 Xalpha_q, TRUE, FALSE,
7625 EL_CHAR('Q'), -1, -1
7628 Xalpha_r, TRUE, FALSE,
7629 EL_CHAR('R'), -1, -1
7632 Xalpha_s, TRUE, FALSE,
7633 EL_CHAR('S'), -1, -1
7636 Xalpha_t, TRUE, FALSE,
7637 EL_CHAR('T'), -1, -1
7640 Xalpha_u, TRUE, FALSE,
7641 EL_CHAR('U'), -1, -1
7644 Xalpha_v, TRUE, FALSE,
7645 EL_CHAR('V'), -1, -1
7648 Xalpha_w, TRUE, FALSE,
7649 EL_CHAR('W'), -1, -1
7652 Xalpha_x, TRUE, FALSE,
7653 EL_CHAR('X'), -1, -1
7656 Xalpha_y, TRUE, FALSE,
7657 EL_CHAR('Y'), -1, -1
7660 Xalpha_z, TRUE, FALSE,
7661 EL_CHAR('Z'), -1, -1
7664 Xalpha_arrow_e, TRUE, FALSE,
7665 EL_CHAR('>'), -1, -1
7668 Xalpha_arrow_w, TRUE, FALSE,
7669 EL_CHAR('<'), -1, -1
7672 Xalpha_copyr, TRUE, FALSE,
7673 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7677 Ykey_1_blank, FALSE, FALSE,
7678 EL_EM_KEY_1, ACTION_COLLECTING, -1
7681 Ykey_2_blank, FALSE, FALSE,
7682 EL_EM_KEY_2, ACTION_COLLECTING, -1
7685 Ykey_3_blank, FALSE, FALSE,
7686 EL_EM_KEY_3, ACTION_COLLECTING, -1
7689 Ykey_4_blank, FALSE, FALSE,
7690 EL_EM_KEY_4, ACTION_COLLECTING, -1
7693 Ykey_5_blank, FALSE, FALSE,
7694 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7697 Ykey_6_blank, FALSE, FALSE,
7698 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7701 Ykey_7_blank, FALSE, FALSE,
7702 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7705 Ykey_8_blank, FALSE, FALSE,
7706 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7709 Ylenses_blank, FALSE, FALSE,
7710 EL_EMC_LENSES, ACTION_COLLECTING, -1
7713 Ymagnify_blank, FALSE, FALSE,
7714 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7717 Ygrass_blank, FALSE, FALSE,
7718 EL_EMC_GRASS, ACTION_SNAPPING, -1
7721 Ydirt_blank, FALSE, FALSE,
7722 EL_SAND, ACTION_SNAPPING, -1
7731 static struct Mapping_EM_to_RND_player
7740 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7744 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7748 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7752 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7756 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7760 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7764 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7768 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7772 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7776 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7780 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7784 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7788 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7792 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7796 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7800 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7804 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7808 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7812 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7816 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7820 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7824 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7828 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7832 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7836 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7840 EL_PLAYER_1, ACTION_DEFAULT, -1,
7844 EL_PLAYER_2, ACTION_DEFAULT, -1,
7848 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7852 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7856 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7860 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7864 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7868 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7872 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7876 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7880 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7884 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7888 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7892 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7896 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7900 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7904 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7908 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7912 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7916 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7920 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7924 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7928 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7932 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7936 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7940 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7944 EL_PLAYER_3, ACTION_DEFAULT, -1,
7948 EL_PLAYER_4, ACTION_DEFAULT, -1,
7957 int map_element_RND_to_EM_cave(int element_rnd)
7959 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7960 static boolean mapping_initialized = FALSE;
7962 if (!mapping_initialized)
7966 // return "Xalpha_quest" for all undefined elements in mapping array
7967 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7968 mapping_RND_to_EM[i] = Xalpha_quest;
7970 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7971 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7972 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7973 em_object_mapping_list[i].element_em;
7975 mapping_initialized = TRUE;
7978 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7980 Warn("invalid RND level element %d", element_rnd);
7985 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7988 int map_element_EM_to_RND_cave(int element_em_cave)
7990 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7991 static boolean mapping_initialized = FALSE;
7993 if (!mapping_initialized)
7997 // return "EL_UNKNOWN" for all undefined elements in mapping array
7998 for (i = 0; i < GAME_TILE_MAX; i++)
7999 mapping_EM_to_RND[i] = EL_UNKNOWN;
8001 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8002 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8003 em_object_mapping_list[i].element_rnd;
8005 mapping_initialized = TRUE;
8008 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8010 Warn("invalid EM cave element %d", element_em_cave);
8015 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8018 int map_element_EM_to_RND_game(int element_em_game)
8020 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8021 static boolean mapping_initialized = FALSE;
8023 if (!mapping_initialized)
8027 // return "EL_UNKNOWN" for all undefined elements in mapping array
8028 for (i = 0; i < GAME_TILE_MAX; i++)
8029 mapping_EM_to_RND[i] = EL_UNKNOWN;
8031 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8032 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8033 em_object_mapping_list[i].element_rnd;
8035 mapping_initialized = TRUE;
8038 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8040 Warn("invalid EM game element %d", element_em_game);
8045 return mapping_EM_to_RND[element_em_game];
8048 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8050 struct LevelInfo_EM *level_em = level->native_em_level;
8051 struct CAVE *cav = level_em->cav;
8054 for (i = 0; i < GAME_TILE_MAX; i++)
8055 cav->android_array[i] = Cblank;
8057 for (i = 0; i < level->num_android_clone_elements; i++)
8059 int element_rnd = level->android_clone_element[i];
8060 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8062 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8063 if (em_object_mapping_list[j].element_rnd == element_rnd)
8064 cav->android_array[em_object_mapping_list[j].element_em] =
8069 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8071 struct LevelInfo_EM *level_em = level->native_em_level;
8072 struct CAVE *cav = level_em->cav;
8075 level->num_android_clone_elements = 0;
8077 for (i = 0; i < GAME_TILE_MAX; i++)
8079 int element_em_cave = cav->android_array[i];
8081 boolean element_found = FALSE;
8083 if (element_em_cave == Cblank)
8086 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8088 for (j = 0; j < level->num_android_clone_elements; j++)
8089 if (level->android_clone_element[j] == element_rnd)
8090 element_found = TRUE;
8094 level->android_clone_element[level->num_android_clone_elements++] =
8097 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8102 if (level->num_android_clone_elements == 0)
8104 level->num_android_clone_elements = 1;
8105 level->android_clone_element[0] = EL_EMPTY;
8109 int map_direction_RND_to_EM(int direction)
8111 return (direction == MV_UP ? 0 :
8112 direction == MV_RIGHT ? 1 :
8113 direction == MV_DOWN ? 2 :
8114 direction == MV_LEFT ? 3 :
8118 int map_direction_EM_to_RND(int direction)
8120 return (direction == 0 ? MV_UP :
8121 direction == 1 ? MV_RIGHT :
8122 direction == 2 ? MV_DOWN :
8123 direction == 3 ? MV_LEFT :
8127 int map_element_RND_to_SP(int element_rnd)
8129 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8131 if (element_rnd >= EL_SP_START &&
8132 element_rnd <= EL_SP_END)
8133 element_sp = element_rnd - EL_SP_START;
8134 else if (element_rnd == EL_EMPTY_SPACE)
8136 else if (element_rnd == EL_INVISIBLE_WALL)
8142 int map_element_SP_to_RND(int element_sp)
8144 int element_rnd = EL_UNKNOWN;
8146 if (element_sp >= 0x00 &&
8148 element_rnd = EL_SP_START + element_sp;
8149 else if (element_sp == 0x28)
8150 element_rnd = EL_INVISIBLE_WALL;
8155 int map_action_SP_to_RND(int action_sp)
8159 case actActive: return ACTION_ACTIVE;
8160 case actImpact: return ACTION_IMPACT;
8161 case actExploding: return ACTION_EXPLODING;
8162 case actDigging: return ACTION_DIGGING;
8163 case actSnapping: return ACTION_SNAPPING;
8164 case actCollecting: return ACTION_COLLECTING;
8165 case actPassing: return ACTION_PASSING;
8166 case actPushing: return ACTION_PUSHING;
8167 case actDropping: return ACTION_DROPPING;
8169 default: return ACTION_DEFAULT;
8173 int map_element_RND_to_MM(int element_rnd)
8175 return (element_rnd >= EL_MM_START_1 &&
8176 element_rnd <= EL_MM_END_1 ?
8177 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8179 element_rnd >= EL_MM_START_2 &&
8180 element_rnd <= EL_MM_END_2 ?
8181 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8183 element_rnd >= EL_MM_START_3 &&
8184 element_rnd <= EL_MM_END_3 ?
8185 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8187 element_rnd >= EL_CHAR_START &&
8188 element_rnd <= EL_CHAR_END ?
8189 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8191 element_rnd >= EL_MM_RUNTIME_START &&
8192 element_rnd <= EL_MM_RUNTIME_END ?
8193 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8195 EL_MM_EMPTY_NATIVE);
8198 int map_element_MM_to_RND(int element_mm)
8200 return (element_mm == EL_MM_EMPTY_NATIVE ||
8201 element_mm == EL_DF_EMPTY_NATIVE ?
8204 element_mm >= EL_MM_START_1_NATIVE &&
8205 element_mm <= EL_MM_END_1_NATIVE ?
8206 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8208 element_mm >= EL_MM_START_2_NATIVE &&
8209 element_mm <= EL_MM_END_2_NATIVE ?
8210 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8212 element_mm >= EL_MM_START_3_NATIVE &&
8213 element_mm <= EL_MM_END_3_NATIVE ?
8214 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8216 element_mm >= EL_MM_CHAR_START_NATIVE &&
8217 element_mm <= EL_MM_CHAR_END_NATIVE ?
8218 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8220 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8221 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8222 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8227 int map_action_MM_to_RND(int action_mm)
8229 // all MM actions are defined to exactly match their RND counterparts
8233 int map_sound_MM_to_RND(int sound_mm)
8237 case SND_MM_GAME_LEVELTIME_CHARGING:
8238 return SND_GAME_LEVELTIME_CHARGING;
8240 case SND_MM_GAME_HEALTH_CHARGING:
8241 return SND_GAME_HEALTH_CHARGING;
8244 return SND_UNDEFINED;
8248 int map_mm_wall_element(int element)
8250 return (element >= EL_MM_STEEL_WALL_START &&
8251 element <= EL_MM_STEEL_WALL_END ?
8254 element >= EL_MM_WOODEN_WALL_START &&
8255 element <= EL_MM_WOODEN_WALL_END ?
8258 element >= EL_MM_ICE_WALL_START &&
8259 element <= EL_MM_ICE_WALL_END ?
8262 element >= EL_MM_AMOEBA_WALL_START &&
8263 element <= EL_MM_AMOEBA_WALL_END ?
8266 element >= EL_DF_STEEL_WALL_START &&
8267 element <= EL_DF_STEEL_WALL_END ?
8270 element >= EL_DF_WOODEN_WALL_START &&
8271 element <= EL_DF_WOODEN_WALL_END ?
8277 int map_mm_wall_element_editor(int element)
8281 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8282 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8283 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8284 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8285 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8286 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8288 default: return element;
8292 int get_next_element(int element)
8296 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8297 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8298 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8299 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8300 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8301 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8302 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8303 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8304 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8305 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8306 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8308 default: return element;
8312 int el2img_mm(int element_mm)
8314 return el2img(map_element_MM_to_RND(element_mm));
8317 int el_act2img_mm(int element_mm, int action)
8319 return el_act2img(map_element_MM_to_RND(element_mm), action);
8322 int el_act_dir2img(int element, int action, int direction)
8324 element = GFX_ELEMENT(element);
8325 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8327 // direction_graphic[][] == graphic[] for undefined direction graphics
8328 return element_info[element].direction_graphic[action][direction];
8331 static int el_act_dir2crm(int element, int action, int direction)
8333 element = GFX_ELEMENT(element);
8334 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8336 // direction_graphic[][] == graphic[] for undefined direction graphics
8337 return element_info[element].direction_crumbled[action][direction];
8340 int el_act2img(int element, int action)
8342 element = GFX_ELEMENT(element);
8344 return element_info[element].graphic[action];
8347 int el_act2crm(int element, int action)
8349 element = GFX_ELEMENT(element);
8351 return element_info[element].crumbled[action];
8354 int el_dir2img(int element, int direction)
8356 element = GFX_ELEMENT(element);
8358 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8361 int el2baseimg(int element)
8363 return element_info[element].graphic[ACTION_DEFAULT];
8366 int el2img(int element)
8368 element = GFX_ELEMENT(element);
8370 return element_info[element].graphic[ACTION_DEFAULT];
8373 int el2edimg(int element)
8375 element = GFX_ELEMENT(element);
8377 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8380 int el2preimg(int element)
8382 element = GFX_ELEMENT(element);
8384 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8387 int el2panelimg(int element)
8389 element = GFX_ELEMENT(element);
8391 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8394 int font2baseimg(int font_nr)
8396 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8399 int getBeltNrFromBeltElement(int element)
8401 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8402 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8403 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8406 int getBeltNrFromBeltActiveElement(int element)
8408 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8409 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8410 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8413 int getBeltNrFromBeltSwitchElement(int element)
8415 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8416 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8417 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8420 int getBeltDirNrFromBeltElement(int element)
8422 static int belt_base_element[4] =
8424 EL_CONVEYOR_BELT_1_LEFT,
8425 EL_CONVEYOR_BELT_2_LEFT,
8426 EL_CONVEYOR_BELT_3_LEFT,
8427 EL_CONVEYOR_BELT_4_LEFT
8430 int belt_nr = getBeltNrFromBeltElement(element);
8431 int belt_dir_nr = element - belt_base_element[belt_nr];
8433 return (belt_dir_nr % 3);
8436 int getBeltDirNrFromBeltSwitchElement(int element)
8438 static int belt_base_element[4] =
8440 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8441 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8442 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8443 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8446 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8447 int belt_dir_nr = element - belt_base_element[belt_nr];
8449 return (belt_dir_nr % 3);
8452 int getBeltDirFromBeltElement(int element)
8454 static int belt_move_dir[3] =
8461 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8463 return belt_move_dir[belt_dir_nr];
8466 int getBeltDirFromBeltSwitchElement(int element)
8468 static int belt_move_dir[3] =
8475 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8477 return belt_move_dir[belt_dir_nr];
8480 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8482 static int belt_base_element[4] =
8484 EL_CONVEYOR_BELT_1_LEFT,
8485 EL_CONVEYOR_BELT_2_LEFT,
8486 EL_CONVEYOR_BELT_3_LEFT,
8487 EL_CONVEYOR_BELT_4_LEFT
8490 return belt_base_element[belt_nr] + belt_dir_nr;
8493 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8495 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8497 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8500 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8502 static int belt_base_element[4] =
8504 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8505 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8506 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8507 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8510 return belt_base_element[belt_nr] + belt_dir_nr;
8513 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8515 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8517 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8520 boolean swapTiles_EM(boolean is_pre_emc_cave)
8522 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8525 boolean getTeamMode_EM(void)
8527 return game.team_mode || network_playing;
8530 boolean isActivePlayer_EM(int player_nr)
8532 return stored_player[player_nr].active;
8535 unsigned int InitRND(int seed)
8537 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8538 return InitEngineRandom_EM(seed);
8539 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8540 return InitEngineRandom_SP(seed);
8541 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8542 return InitEngineRandom_MM(seed);
8544 return InitEngineRandom_RND(seed);
8547 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8548 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8550 static int get_effective_element_EM(int tile, int frame_em)
8552 int element = object_mapping[tile].element_rnd;
8553 int action = object_mapping[tile].action;
8554 boolean is_backside = object_mapping[tile].is_backside;
8555 boolean action_removing = (action == ACTION_DIGGING ||
8556 action == ACTION_SNAPPING ||
8557 action == ACTION_COLLECTING);
8565 return (frame_em > 5 ? EL_EMPTY : element);
8571 else // frame_em == 7
8582 case Ydiamond_stone:
8586 case Xdrip_stretchB:
8602 case Ymagnify_blank:
8605 case Xsand_stonein_1:
8606 case Xsand_stonein_2:
8607 case Xsand_stonein_3:
8608 case Xsand_stonein_4:
8612 return (is_backside || action_removing ? EL_EMPTY : element);
8617 static boolean check_linear_animation_EM(int tile)
8621 case Xsand_stonesand_1:
8622 case Xsand_stonesand_quickout_1:
8623 case Xsand_sandstone_1:
8624 case Xsand_stonein_1:
8625 case Xsand_stoneout_1:
8653 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8654 boolean has_crumbled_graphics,
8655 int crumbled, int sync_frame)
8657 // if element can be crumbled, but certain action graphics are just empty
8658 // space (like instantly snapping sand to empty space in 1 frame), do not
8659 // treat these empty space graphics as crumbled graphics in EMC engine
8660 if (crumbled == IMG_EMPTY_SPACE)
8661 has_crumbled_graphics = FALSE;
8663 if (has_crumbled_graphics)
8665 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8666 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8667 g_crumbled->anim_delay,
8668 g_crumbled->anim_mode,
8669 g_crumbled->anim_start_frame,
8672 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8673 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8675 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8676 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8678 g_em->has_crumbled_graphics = TRUE;
8682 g_em->crumbled_bitmap = NULL;
8683 g_em->crumbled_src_x = 0;
8684 g_em->crumbled_src_y = 0;
8685 g_em->crumbled_border_size = 0;
8686 g_em->crumbled_tile_size = 0;
8688 g_em->has_crumbled_graphics = FALSE;
8693 void ResetGfxAnimation_EM(int x, int y, int tile)
8699 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8700 int tile, int frame_em, int x, int y)
8702 int action = object_mapping[tile].action;
8703 int direction = object_mapping[tile].direction;
8704 int effective_element = get_effective_element_EM(tile, frame_em);
8705 int graphic = (direction == MV_NONE ?
8706 el_act2img(effective_element, action) :
8707 el_act_dir2img(effective_element, action, direction));
8708 struct GraphicInfo *g = &graphic_info[graphic];
8710 boolean action_removing = (action == ACTION_DIGGING ||
8711 action == ACTION_SNAPPING ||
8712 action == ACTION_COLLECTING);
8713 boolean action_moving = (action == ACTION_FALLING ||
8714 action == ACTION_MOVING ||
8715 action == ACTION_PUSHING ||
8716 action == ACTION_EATING ||
8717 action == ACTION_FILLING ||
8718 action == ACTION_EMPTYING);
8719 boolean action_falling = (action == ACTION_FALLING ||
8720 action == ACTION_FILLING ||
8721 action == ACTION_EMPTYING);
8723 // special case: graphic uses "2nd movement tile" and has defined
8724 // 7 frames for movement animation (or less) => use default graphic
8725 // for last (8th) frame which ends the movement animation
8726 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8728 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8729 graphic = (direction == MV_NONE ?
8730 el_act2img(effective_element, action) :
8731 el_act_dir2img(effective_element, action, direction));
8733 g = &graphic_info[graphic];
8736 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8740 else if (action_moving)
8742 boolean is_backside = object_mapping[tile].is_backside;
8746 int direction = object_mapping[tile].direction;
8747 int move_dir = (action_falling ? MV_DOWN : direction);
8752 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8753 if (g->double_movement && frame_em == 0)
8757 if (move_dir == MV_LEFT)
8758 GfxFrame[x - 1][y] = GfxFrame[x][y];
8759 else if (move_dir == MV_RIGHT)
8760 GfxFrame[x + 1][y] = GfxFrame[x][y];
8761 else if (move_dir == MV_UP)
8762 GfxFrame[x][y - 1] = GfxFrame[x][y];
8763 else if (move_dir == MV_DOWN)
8764 GfxFrame[x][y + 1] = GfxFrame[x][y];
8771 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8772 if (tile == Xsand_stonesand_quickout_1 ||
8773 tile == Xsand_stonesand_quickout_2)
8777 if (graphic_info[graphic].anim_global_sync)
8778 sync_frame = FrameCounter;
8779 else if (graphic_info[graphic].anim_global_anim_sync)
8780 sync_frame = getGlobalAnimSyncFrame();
8781 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8782 sync_frame = GfxFrame[x][y];
8784 sync_frame = 0; // playfield border (pseudo steel)
8786 SetRandomAnimationValue(x, y);
8788 int frame = getAnimationFrame(g->anim_frames,
8791 g->anim_start_frame,
8794 g_em->unique_identifier =
8795 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8798 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8799 int tile, int frame_em, int x, int y)
8801 int action = object_mapping[tile].action;
8802 int direction = object_mapping[tile].direction;
8803 boolean is_backside = object_mapping[tile].is_backside;
8804 int effective_element = get_effective_element_EM(tile, frame_em);
8805 int effective_action = action;
8806 int graphic = (direction == MV_NONE ?
8807 el_act2img(effective_element, effective_action) :
8808 el_act_dir2img(effective_element, effective_action,
8810 int crumbled = (direction == MV_NONE ?
8811 el_act2crm(effective_element, effective_action) :
8812 el_act_dir2crm(effective_element, effective_action,
8814 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8815 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8816 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8817 struct GraphicInfo *g = &graphic_info[graphic];
8820 // special case: graphic uses "2nd movement tile" and has defined
8821 // 7 frames for movement animation (or less) => use default graphic
8822 // for last (8th) frame which ends the movement animation
8823 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8825 effective_action = ACTION_DEFAULT;
8826 graphic = (direction == MV_NONE ?
8827 el_act2img(effective_element, effective_action) :
8828 el_act_dir2img(effective_element, effective_action,
8830 crumbled = (direction == MV_NONE ?
8831 el_act2crm(effective_element, effective_action) :
8832 el_act_dir2crm(effective_element, effective_action,
8835 g = &graphic_info[graphic];
8838 if (graphic_info[graphic].anim_global_sync)
8839 sync_frame = FrameCounter;
8840 else if (graphic_info[graphic].anim_global_anim_sync)
8841 sync_frame = getGlobalAnimSyncFrame();
8842 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8843 sync_frame = GfxFrame[x][y];
8845 sync_frame = 0; // playfield border (pseudo steel)
8847 SetRandomAnimationValue(x, y);
8849 int frame = getAnimationFrame(g->anim_frames,
8852 g->anim_start_frame,
8855 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8856 g->double_movement && is_backside);
8858 // (updating the "crumbled" graphic definitions is probably not really needed,
8859 // as animations for crumbled graphics can't be longer than one EMC cycle)
8860 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8864 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8865 int player_nr, int anim, int frame_em)
8867 int element = player_mapping[player_nr][anim].element_rnd;
8868 int action = player_mapping[player_nr][anim].action;
8869 int direction = player_mapping[player_nr][anim].direction;
8870 int graphic = (direction == MV_NONE ?
8871 el_act2img(element, action) :
8872 el_act_dir2img(element, action, direction));
8873 struct GraphicInfo *g = &graphic_info[graphic];
8876 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8878 stored_player[player_nr].StepFrame = frame_em;
8880 sync_frame = stored_player[player_nr].Frame;
8882 int frame = getAnimationFrame(g->anim_frames,
8885 g->anim_start_frame,
8888 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8889 &g_em->src_x, &g_em->src_y, FALSE);
8892 void InitGraphicInfo_EM(void)
8896 // always start with reliable default values
8897 for (i = 0; i < GAME_TILE_MAX; i++)
8899 object_mapping[i].element_rnd = EL_UNKNOWN;
8900 object_mapping[i].is_backside = FALSE;
8901 object_mapping[i].action = ACTION_DEFAULT;
8902 object_mapping[i].direction = MV_NONE;
8905 // always start with reliable default values
8906 for (p = 0; p < MAX_PLAYERS; p++)
8908 for (i = 0; i < PLY_MAX; i++)
8910 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8911 player_mapping[p][i].action = ACTION_DEFAULT;
8912 player_mapping[p][i].direction = MV_NONE;
8916 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8918 int e = em_object_mapping_list[i].element_em;
8920 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8921 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8923 if (em_object_mapping_list[i].action != -1)
8924 object_mapping[e].action = em_object_mapping_list[i].action;
8926 if (em_object_mapping_list[i].direction != -1)
8927 object_mapping[e].direction =
8928 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8931 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8933 int a = em_player_mapping_list[i].action_em;
8934 int p = em_player_mapping_list[i].player_nr;
8936 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8938 if (em_player_mapping_list[i].action != -1)
8939 player_mapping[p][a].action = em_player_mapping_list[i].action;
8941 if (em_player_mapping_list[i].direction != -1)
8942 player_mapping[p][a].direction =
8943 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8946 for (i = 0; i < GAME_TILE_MAX; i++)
8948 int element = object_mapping[i].element_rnd;
8949 int action = object_mapping[i].action;
8950 int direction = object_mapping[i].direction;
8951 boolean is_backside = object_mapping[i].is_backside;
8952 boolean action_exploding = ((action == ACTION_EXPLODING ||
8953 action == ACTION_SMASHED_BY_ROCK ||
8954 action == ACTION_SMASHED_BY_SPRING) &&
8955 element != EL_DIAMOND);
8956 boolean action_active = (action == ACTION_ACTIVE);
8957 boolean action_other = (action == ACTION_OTHER);
8959 for (j = 0; j < 8; j++)
8961 int effective_element = get_effective_element_EM(i, j);
8962 int effective_action = (j < 7 ? action :
8963 i == Xdrip_stretch ? action :
8964 i == Xdrip_stretchB ? action :
8965 i == Ydrip_1_s ? action :
8966 i == Ydrip_1_sB ? action :
8967 i == Yball_1 ? action :
8968 i == Xball_2 ? action :
8969 i == Yball_2 ? action :
8970 i == Yball_blank ? action :
8971 i == Ykey_1_blank ? action :
8972 i == Ykey_2_blank ? action :
8973 i == Ykey_3_blank ? action :
8974 i == Ykey_4_blank ? action :
8975 i == Ykey_5_blank ? action :
8976 i == Ykey_6_blank ? action :
8977 i == Ykey_7_blank ? action :
8978 i == Ykey_8_blank ? action :
8979 i == Ylenses_blank ? action :
8980 i == Ymagnify_blank ? action :
8981 i == Ygrass_blank ? action :
8982 i == Ydirt_blank ? action :
8983 i == Xsand_stonein_1 ? action :
8984 i == Xsand_stonein_2 ? action :
8985 i == Xsand_stonein_3 ? action :
8986 i == Xsand_stonein_4 ? action :
8987 i == Xsand_stoneout_1 ? action :
8988 i == Xsand_stoneout_2 ? action :
8989 i == Xboom_android ? ACTION_EXPLODING :
8990 action_exploding ? ACTION_EXPLODING :
8991 action_active ? action :
8992 action_other ? action :
8994 int graphic = (el_act_dir2img(effective_element, effective_action,
8996 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8998 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8999 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9000 boolean has_action_graphics = (graphic != base_graphic);
9001 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9002 struct GraphicInfo *g = &graphic_info[graphic];
9003 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9006 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9007 boolean special_animation = (action != ACTION_DEFAULT &&
9008 g->anim_frames == 3 &&
9009 g->anim_delay == 2 &&
9010 g->anim_mode & ANIM_LINEAR);
9011 int sync_frame = (i == Xdrip_stretch ? 7 :
9012 i == Xdrip_stretchB ? 7 :
9013 i == Ydrip_2_s ? j + 8 :
9014 i == Ydrip_2_sB ? j + 8 :
9023 i == Xfake_acid_1 ? 0 :
9024 i == Xfake_acid_2 ? 10 :
9025 i == Xfake_acid_3 ? 20 :
9026 i == Xfake_acid_4 ? 30 :
9027 i == Xfake_acid_5 ? 40 :
9028 i == Xfake_acid_6 ? 50 :
9029 i == Xfake_acid_7 ? 60 :
9030 i == Xfake_acid_8 ? 70 :
9031 i == Xfake_acid_1_player ? 0 :
9032 i == Xfake_acid_2_player ? 10 :
9033 i == Xfake_acid_3_player ? 20 :
9034 i == Xfake_acid_4_player ? 30 :
9035 i == Xfake_acid_5_player ? 40 :
9036 i == Xfake_acid_6_player ? 50 :
9037 i == Xfake_acid_7_player ? 60 :
9038 i == Xfake_acid_8_player ? 70 :
9040 i == Yball_2 ? j + 8 :
9041 i == Yball_blank ? j + 1 :
9042 i == Ykey_1_blank ? j + 1 :
9043 i == Ykey_2_blank ? j + 1 :
9044 i == Ykey_3_blank ? j + 1 :
9045 i == Ykey_4_blank ? j + 1 :
9046 i == Ykey_5_blank ? j + 1 :
9047 i == Ykey_6_blank ? j + 1 :
9048 i == Ykey_7_blank ? j + 1 :
9049 i == Ykey_8_blank ? j + 1 :
9050 i == Ylenses_blank ? j + 1 :
9051 i == Ymagnify_blank ? j + 1 :
9052 i == Ygrass_blank ? j + 1 :
9053 i == Ydirt_blank ? j + 1 :
9054 i == Xamoeba_1 ? 0 :
9055 i == Xamoeba_2 ? 1 :
9056 i == Xamoeba_3 ? 2 :
9057 i == Xamoeba_4 ? 3 :
9058 i == Xamoeba_5 ? 0 :
9059 i == Xamoeba_6 ? 1 :
9060 i == Xamoeba_7 ? 2 :
9061 i == Xamoeba_8 ? 3 :
9062 i == Xexit_2 ? j + 8 :
9063 i == Xexit_3 ? j + 16 :
9064 i == Xdynamite_1 ? 0 :
9065 i == Xdynamite_2 ? 8 :
9066 i == Xdynamite_3 ? 16 :
9067 i == Xdynamite_4 ? 24 :
9068 i == Xsand_stonein_1 ? j + 1 :
9069 i == Xsand_stonein_2 ? j + 9 :
9070 i == Xsand_stonein_3 ? j + 17 :
9071 i == Xsand_stonein_4 ? j + 25 :
9072 i == Xsand_stoneout_1 && j == 0 ? 0 :
9073 i == Xsand_stoneout_1 && j == 1 ? 0 :
9074 i == Xsand_stoneout_1 && j == 2 ? 1 :
9075 i == Xsand_stoneout_1 && j == 3 ? 2 :
9076 i == Xsand_stoneout_1 && j == 4 ? 2 :
9077 i == Xsand_stoneout_1 && j == 5 ? 3 :
9078 i == Xsand_stoneout_1 && j == 6 ? 4 :
9079 i == Xsand_stoneout_1 && j == 7 ? 4 :
9080 i == Xsand_stoneout_2 && j == 0 ? 5 :
9081 i == Xsand_stoneout_2 && j == 1 ? 6 :
9082 i == Xsand_stoneout_2 && j == 2 ? 7 :
9083 i == Xsand_stoneout_2 && j == 3 ? 8 :
9084 i == Xsand_stoneout_2 && j == 4 ? 9 :
9085 i == Xsand_stoneout_2 && j == 5 ? 11 :
9086 i == Xsand_stoneout_2 && j == 6 ? 13 :
9087 i == Xsand_stoneout_2 && j == 7 ? 15 :
9088 i == Xboom_bug && j == 1 ? 2 :
9089 i == Xboom_bug && j == 2 ? 2 :
9090 i == Xboom_bug && j == 3 ? 4 :
9091 i == Xboom_bug && j == 4 ? 4 :
9092 i == Xboom_bug && j == 5 ? 2 :
9093 i == Xboom_bug && j == 6 ? 2 :
9094 i == Xboom_bug && j == 7 ? 0 :
9095 i == Xboom_tank && j == 1 ? 2 :
9096 i == Xboom_tank && j == 2 ? 2 :
9097 i == Xboom_tank && j == 3 ? 4 :
9098 i == Xboom_tank && j == 4 ? 4 :
9099 i == Xboom_tank && j == 5 ? 2 :
9100 i == Xboom_tank && j == 6 ? 2 :
9101 i == Xboom_tank && j == 7 ? 0 :
9102 i == Xboom_android && j == 7 ? 6 :
9103 i == Xboom_1 && j == 1 ? 2 :
9104 i == Xboom_1 && j == 2 ? 2 :
9105 i == Xboom_1 && j == 3 ? 4 :
9106 i == Xboom_1 && j == 4 ? 4 :
9107 i == Xboom_1 && j == 5 ? 6 :
9108 i == Xboom_1 && j == 6 ? 6 :
9109 i == Xboom_1 && j == 7 ? 8 :
9110 i == Xboom_2 && j == 0 ? 8 :
9111 i == Xboom_2 && j == 1 ? 8 :
9112 i == Xboom_2 && j == 2 ? 10 :
9113 i == Xboom_2 && j == 3 ? 10 :
9114 i == Xboom_2 && j == 4 ? 10 :
9115 i == Xboom_2 && j == 5 ? 12 :
9116 i == Xboom_2 && j == 6 ? 12 :
9117 i == Xboom_2 && j == 7 ? 12 :
9118 special_animation && j == 4 ? 3 :
9119 effective_action != action ? 0 :
9121 int frame = getAnimationFrame(g->anim_frames,
9124 g->anim_start_frame,
9127 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9128 g->double_movement && is_backside);
9130 g_em->bitmap = src_bitmap;
9131 g_em->src_x = src_x;
9132 g_em->src_y = src_y;
9133 g_em->src_offset_x = 0;
9134 g_em->src_offset_y = 0;
9135 g_em->dst_offset_x = 0;
9136 g_em->dst_offset_y = 0;
9137 g_em->width = TILEX;
9138 g_em->height = TILEY;
9140 g_em->preserve_background = FALSE;
9142 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9145 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9146 effective_action == ACTION_MOVING ||
9147 effective_action == ACTION_PUSHING ||
9148 effective_action == ACTION_EATING)) ||
9149 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9150 effective_action == ACTION_EMPTYING)))
9153 (effective_action == ACTION_FALLING ||
9154 effective_action == ACTION_FILLING ||
9155 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9156 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9157 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9158 int num_steps = (i == Ydrip_1_s ? 16 :
9159 i == Ydrip_1_sB ? 16 :
9160 i == Ydrip_2_s ? 16 :
9161 i == Ydrip_2_sB ? 16 :
9162 i == Xsand_stonein_1 ? 32 :
9163 i == Xsand_stonein_2 ? 32 :
9164 i == Xsand_stonein_3 ? 32 :
9165 i == Xsand_stonein_4 ? 32 :
9166 i == Xsand_stoneout_1 ? 16 :
9167 i == Xsand_stoneout_2 ? 16 : 8);
9168 int cx = ABS(dx) * (TILEX / num_steps);
9169 int cy = ABS(dy) * (TILEY / num_steps);
9170 int step_frame = (i == Ydrip_2_s ? j + 8 :
9171 i == Ydrip_2_sB ? j + 8 :
9172 i == Xsand_stonein_2 ? j + 8 :
9173 i == Xsand_stonein_3 ? j + 16 :
9174 i == Xsand_stonein_4 ? j + 24 :
9175 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9176 int step = (is_backside ? step_frame : num_steps - step_frame);
9178 if (is_backside) // tile where movement starts
9180 if (dx < 0 || dy < 0)
9182 g_em->src_offset_x = cx * step;
9183 g_em->src_offset_y = cy * step;
9187 g_em->dst_offset_x = cx * step;
9188 g_em->dst_offset_y = cy * step;
9191 else // tile where movement ends
9193 if (dx < 0 || dy < 0)
9195 g_em->dst_offset_x = cx * step;
9196 g_em->dst_offset_y = cy * step;
9200 g_em->src_offset_x = cx * step;
9201 g_em->src_offset_y = cy * step;
9205 g_em->width = TILEX - cx * step;
9206 g_em->height = TILEY - cy * step;
9209 // create unique graphic identifier to decide if tile must be redrawn
9210 /* bit 31 - 16 (16 bit): EM style graphic
9211 bit 15 - 12 ( 4 bit): EM style frame
9212 bit 11 - 6 ( 6 bit): graphic width
9213 bit 5 - 0 ( 6 bit): graphic height */
9214 g_em->unique_identifier =
9215 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9219 for (i = 0; i < GAME_TILE_MAX; i++)
9221 for (j = 0; j < 8; j++)
9223 int element = object_mapping[i].element_rnd;
9224 int action = object_mapping[i].action;
9225 int direction = object_mapping[i].direction;
9226 boolean is_backside = object_mapping[i].is_backside;
9227 int graphic_action = el_act_dir2img(element, action, direction);
9228 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9230 if ((action == ACTION_SMASHED_BY_ROCK ||
9231 action == ACTION_SMASHED_BY_SPRING ||
9232 action == ACTION_EATING) &&
9233 graphic_action == graphic_default)
9235 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9236 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9237 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9238 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9241 // no separate animation for "smashed by rock" -- use rock instead
9242 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9243 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9245 g_em->bitmap = g_xx->bitmap;
9246 g_em->src_x = g_xx->src_x;
9247 g_em->src_y = g_xx->src_y;
9248 g_em->src_offset_x = g_xx->src_offset_x;
9249 g_em->src_offset_y = g_xx->src_offset_y;
9250 g_em->dst_offset_x = g_xx->dst_offset_x;
9251 g_em->dst_offset_y = g_xx->dst_offset_y;
9252 g_em->width = g_xx->width;
9253 g_em->height = g_xx->height;
9254 g_em->unique_identifier = g_xx->unique_identifier;
9257 g_em->preserve_background = TRUE;
9262 for (p = 0; p < MAX_PLAYERS; p++)
9264 for (i = 0; i < PLY_MAX; i++)
9266 int element = player_mapping[p][i].element_rnd;
9267 int action = player_mapping[p][i].action;
9268 int direction = player_mapping[p][i].direction;
9270 for (j = 0; j < 8; j++)
9272 int effective_element = element;
9273 int effective_action = action;
9274 int graphic = (direction == MV_NONE ?
9275 el_act2img(effective_element, effective_action) :
9276 el_act_dir2img(effective_element, effective_action,
9278 struct GraphicInfo *g = &graphic_info[graphic];
9279 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9283 int frame = getAnimationFrame(g->anim_frames,
9286 g->anim_start_frame,
9289 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9291 g_em->bitmap = src_bitmap;
9292 g_em->src_x = src_x;
9293 g_em->src_y = src_y;
9294 g_em->src_offset_x = 0;
9295 g_em->src_offset_y = 0;
9296 g_em->dst_offset_x = 0;
9297 g_em->dst_offset_y = 0;
9298 g_em->width = TILEX;
9299 g_em->height = TILEY;
9305 static void CheckSaveEngineSnapshot_EM(int frame,
9306 boolean any_player_moving,
9307 boolean any_player_snapping,
9308 boolean any_player_dropping)
9310 if (frame == 7 && !any_player_dropping)
9312 if (!local_player->was_waiting)
9314 if (!CheckSaveEngineSnapshotToList())
9317 local_player->was_waiting = TRUE;
9320 else if (any_player_moving || any_player_snapping || any_player_dropping)
9322 local_player->was_waiting = FALSE;
9326 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9327 boolean murphy_is_dropping)
9329 if (murphy_is_waiting)
9331 if (!local_player->was_waiting)
9333 if (!CheckSaveEngineSnapshotToList())
9336 local_player->was_waiting = TRUE;
9341 local_player->was_waiting = FALSE;
9345 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9346 boolean button_released)
9348 if (button_released)
9350 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9351 CheckSaveEngineSnapshotToList();
9353 else if (element_clicked)
9355 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9356 CheckSaveEngineSnapshotToList();
9358 game.snapshot.changed_action = TRUE;
9362 boolean CheckSingleStepMode_EM(int frame,
9363 boolean any_player_moving,
9364 boolean any_player_snapping,
9365 boolean any_player_dropping)
9367 if (tape.single_step && tape.recording && !tape.pausing)
9368 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9369 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9371 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9372 any_player_snapping, any_player_dropping);
9374 return tape.pausing;
9377 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9378 boolean murphy_is_dropping)
9380 boolean murphy_starts_dropping = FALSE;
9383 for (i = 0; i < MAX_PLAYERS; i++)
9384 if (stored_player[i].force_dropping)
9385 murphy_starts_dropping = TRUE;
9387 if (tape.single_step && tape.recording && !tape.pausing)
9388 if (murphy_is_waiting && !murphy_starts_dropping)
9389 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9391 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9394 void CheckSingleStepMode_MM(boolean element_clicked,
9395 boolean button_released)
9397 if (tape.single_step && tape.recording && !tape.pausing)
9398 if (button_released)
9399 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9401 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9404 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9405 int graphic, int sync_frame)
9407 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9409 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9412 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9414 return (IS_NEXT_FRAME(sync_frame, graphic));
9417 int getGraphicInfo_Delay(int graphic)
9419 return graphic_info[graphic].anim_delay;
9422 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9424 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9427 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9433 void PlayMenuSoundExt(int sound)
9435 if (sound == SND_UNDEFINED)
9438 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9439 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9442 if (IS_LOOP_SOUND(sound))
9443 PlaySoundLoop(sound);
9448 void PlayMenuSound(void)
9450 PlayMenuSoundExt(menu.sound[game_status]);
9453 void PlayMenuSoundStereo(int sound, int stereo_position)
9455 if (sound == SND_UNDEFINED)
9458 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9459 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9462 if (IS_LOOP_SOUND(sound))
9463 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9465 PlaySoundStereo(sound, stereo_position);
9468 void PlayMenuSoundIfLoopExt(int sound)
9470 if (sound == SND_UNDEFINED)
9473 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9474 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9477 if (IS_LOOP_SOUND(sound))
9478 PlaySoundLoop(sound);
9481 void PlayMenuSoundIfLoop(void)
9483 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9486 void PlayMenuMusicExt(int music)
9488 if (music == MUS_UNDEFINED)
9491 if (!setup.sound_music)
9494 if (IS_LOOP_MUSIC(music))
9495 PlayMusicLoop(music);
9500 void PlayMenuMusic(void)
9502 char *curr_music = getCurrentlyPlayingMusicFilename();
9503 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9505 if (!strEqual(curr_music, next_music))
9506 PlayMenuMusicExt(menu.music[game_status]);
9509 void PlayMenuSoundsAndMusic(void)
9515 static void FadeMenuSounds(void)
9520 static void FadeMenuMusic(void)
9522 char *curr_music = getCurrentlyPlayingMusicFilename();
9523 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9525 if (!strEqual(curr_music, next_music))
9529 void FadeMenuSoundsAndMusic(void)
9535 void PlaySoundActivating(void)
9538 PlaySound(SND_MENU_ITEM_ACTIVATING);
9542 void PlaySoundSelecting(void)
9545 PlaySound(SND_MENU_ITEM_SELECTING);
9549 void ToggleFullscreenIfNeeded(void)
9551 // if setup and video fullscreen state are already matching, nothing do do
9552 if (setup.fullscreen == video.fullscreen_enabled ||
9553 !video.fullscreen_available)
9556 SDLSetWindowFullscreen(setup.fullscreen);
9558 // set setup value according to successfully changed fullscreen mode
9559 setup.fullscreen = video.fullscreen_enabled;
9562 void ChangeWindowScalingIfNeeded(void)
9564 // if setup and video window scaling are already matching, nothing do do
9565 if (setup.window_scaling_percent == video.window_scaling_percent ||
9566 video.fullscreen_enabled)
9569 SDLSetWindowScaling(setup.window_scaling_percent);
9571 // set setup value according to successfully changed window scaling
9572 setup.window_scaling_percent = video.window_scaling_percent;
9575 void ChangeVsyncModeIfNeeded(void)
9577 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9578 int video_vsync_mode = video.vsync_mode;
9580 // if setup and video vsync mode are already matching, nothing do do
9581 if (setup_vsync_mode == video_vsync_mode)
9584 // if renderer is using OpenGL, vsync mode can directly be changed
9585 SDLSetScreenVsyncMode(setup.vsync_mode);
9587 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9588 if (video.vsync_mode == video_vsync_mode)
9590 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9592 // save backbuffer content which gets lost when re-creating screen
9593 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9595 // force re-creating screen and renderer to set new vsync mode
9596 video.fullscreen_enabled = !setup.fullscreen;
9598 // when creating new renderer, destroy textures linked to old renderer
9599 FreeAllImageTextures(); // needs old renderer to free the textures
9601 // re-create screen and renderer (including change of vsync mode)
9602 ChangeVideoModeIfNeeded(setup.fullscreen);
9604 // set setup value according to successfully changed fullscreen mode
9605 setup.fullscreen = video.fullscreen_enabled;
9607 // restore backbuffer content from temporary backbuffer backup bitmap
9608 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9609 FreeBitmap(tmp_backbuffer);
9611 // update visible window/screen
9612 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9614 // when changing vsync mode, re-create textures for new renderer
9615 InitImageTextures();
9618 // set setup value according to successfully changed vsync mode
9619 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9622 static void JoinRectangles(int *x, int *y, int *width, int *height,
9623 int x2, int y2, int width2, int height2)
9625 // do not join with "off-screen" rectangle
9626 if (x2 == -1 || y2 == -1)
9631 *width = MAX(*width, width2);
9632 *height = MAX(*height, height2);
9635 void SetAnimStatus(int anim_status_new)
9637 if (anim_status_new == GAME_MODE_MAIN)
9638 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9639 else if (anim_status_new == GAME_MODE_NAMES)
9640 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9641 else if (anim_status_new == GAME_MODE_SCORES)
9642 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9644 global.anim_status_next = anim_status_new;
9646 // directly set screen modes that are entered without fading
9647 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9648 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9649 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9650 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9651 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9652 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9653 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9654 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9655 global.anim_status = global.anim_status_next;
9658 void SetGameStatus(int game_status_new)
9660 if (game_status_new != game_status)
9661 game_status_last_screen = game_status;
9663 game_status = game_status_new;
9665 SetAnimStatus(game_status_new);
9668 void SetFontStatus(int game_status_new)
9670 static int last_game_status = -1;
9672 if (game_status_new != -1)
9674 // set game status for font use after storing last game status
9675 last_game_status = game_status;
9676 game_status = game_status_new;
9680 // reset game status after font use from last stored game status
9681 game_status = last_game_status;
9685 void ResetFontStatus(void)
9690 void SetLevelSetInfo(char *identifier, int level_nr)
9692 setString(&levelset.identifier, identifier);
9694 levelset.level_nr = level_nr;
9697 boolean CheckIfAllViewportsHaveChanged(void)
9699 // if game status has not changed, viewports have not changed either
9700 if (game_status == game_status_last)
9703 // check if all viewports have changed with current game status
9705 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9706 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9707 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9708 int new_real_sx = vp_playfield->x;
9709 int new_real_sy = vp_playfield->y;
9710 int new_full_sxsize = vp_playfield->width;
9711 int new_full_sysize = vp_playfield->height;
9712 int new_dx = vp_door_1->x;
9713 int new_dy = vp_door_1->y;
9714 int new_dxsize = vp_door_1->width;
9715 int new_dysize = vp_door_1->height;
9716 int new_vx = vp_door_2->x;
9717 int new_vy = vp_door_2->y;
9718 int new_vxsize = vp_door_2->width;
9719 int new_vysize = vp_door_2->height;
9721 boolean playfield_viewport_has_changed =
9722 (new_real_sx != REAL_SX ||
9723 new_real_sy != REAL_SY ||
9724 new_full_sxsize != FULL_SXSIZE ||
9725 new_full_sysize != FULL_SYSIZE);
9727 boolean door_1_viewport_has_changed =
9730 new_dxsize != DXSIZE ||
9731 new_dysize != DYSIZE);
9733 boolean door_2_viewport_has_changed =
9736 new_vxsize != VXSIZE ||
9737 new_vysize != VYSIZE ||
9738 game_status_last == GAME_MODE_EDITOR);
9740 return (playfield_viewport_has_changed &&
9741 door_1_viewport_has_changed &&
9742 door_2_viewport_has_changed);
9745 boolean CheckFadeAll(void)
9747 return (CheckIfGlobalBorderHasChanged() ||
9748 CheckIfAllViewportsHaveChanged());
9751 void ChangeViewportPropertiesIfNeeded(void)
9753 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9754 FALSE : setup.small_game_graphics);
9755 int gfx_game_mode = getGlobalGameStatus(game_status);
9756 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9758 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9759 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9760 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9761 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9762 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9763 int new_win_xsize = vp_window->width;
9764 int new_win_ysize = vp_window->height;
9765 int border_left = vp_playfield->border_left;
9766 int border_right = vp_playfield->border_right;
9767 int border_top = vp_playfield->border_top;
9768 int border_bottom = vp_playfield->border_bottom;
9769 int new_sx = vp_playfield->x + border_left;
9770 int new_sy = vp_playfield->y + border_top;
9771 int new_sxsize = vp_playfield->width - border_left - border_right;
9772 int new_sysize = vp_playfield->height - border_top - border_bottom;
9773 int new_real_sx = vp_playfield->x;
9774 int new_real_sy = vp_playfield->y;
9775 int new_full_sxsize = vp_playfield->width;
9776 int new_full_sysize = vp_playfield->height;
9777 int new_dx = vp_door_1->x;
9778 int new_dy = vp_door_1->y;
9779 int new_dxsize = vp_door_1->width;
9780 int new_dysize = vp_door_1->height;
9781 int new_vx = vp_door_2->x;
9782 int new_vy = vp_door_2->y;
9783 int new_vxsize = vp_door_2->width;
9784 int new_vysize = vp_door_2->height;
9785 int new_ex = vp_door_3->x;
9786 int new_ey = vp_door_3->y;
9787 int new_exsize = vp_door_3->width;
9788 int new_eysize = vp_door_3->height;
9789 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9790 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9791 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9792 int new_scr_fieldx = new_sxsize / tilesize;
9793 int new_scr_fieldy = new_sysize / tilesize;
9794 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9795 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9796 boolean init_gfx_buffers = FALSE;
9797 boolean init_video_buffer = FALSE;
9798 boolean init_gadgets_and_anims = FALSE;
9799 boolean init_em_graphics = FALSE;
9801 if (new_win_xsize != WIN_XSIZE ||
9802 new_win_ysize != WIN_YSIZE)
9804 WIN_XSIZE = new_win_xsize;
9805 WIN_YSIZE = new_win_ysize;
9807 init_video_buffer = TRUE;
9808 init_gfx_buffers = TRUE;
9809 init_gadgets_and_anims = TRUE;
9811 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9814 if (new_scr_fieldx != SCR_FIELDX ||
9815 new_scr_fieldy != SCR_FIELDY)
9817 // this always toggles between MAIN and GAME when using small tile size
9819 SCR_FIELDX = new_scr_fieldx;
9820 SCR_FIELDY = new_scr_fieldy;
9822 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9833 new_sxsize != SXSIZE ||
9834 new_sysize != SYSIZE ||
9835 new_dxsize != DXSIZE ||
9836 new_dysize != DYSIZE ||
9837 new_vxsize != VXSIZE ||
9838 new_vysize != VYSIZE ||
9839 new_exsize != EXSIZE ||
9840 new_eysize != EYSIZE ||
9841 new_real_sx != REAL_SX ||
9842 new_real_sy != REAL_SY ||
9843 new_full_sxsize != FULL_SXSIZE ||
9844 new_full_sysize != FULL_SYSIZE ||
9845 new_tilesize_var != TILESIZE_VAR
9848 // ------------------------------------------------------------------------
9849 // determine next fading area for changed viewport definitions
9850 // ------------------------------------------------------------------------
9852 // start with current playfield area (default fading area)
9855 FADE_SXSIZE = FULL_SXSIZE;
9856 FADE_SYSIZE = FULL_SYSIZE;
9858 // add new playfield area if position or size has changed
9859 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9860 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9862 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9863 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9866 // add current and new door 1 area if position or size has changed
9867 if (new_dx != DX || new_dy != DY ||
9868 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9870 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9871 DX, DY, DXSIZE, DYSIZE);
9872 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9873 new_dx, new_dy, new_dxsize, new_dysize);
9876 // add current and new door 2 area if position or size has changed
9877 if (new_vx != VX || new_vy != VY ||
9878 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9880 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9881 VX, VY, VXSIZE, VYSIZE);
9882 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9883 new_vx, new_vy, new_vxsize, new_vysize);
9886 // ------------------------------------------------------------------------
9887 // handle changed tile size
9888 // ------------------------------------------------------------------------
9890 if (new_tilesize_var != TILESIZE_VAR)
9892 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9894 // changing tile size invalidates scroll values of engine snapshots
9895 FreeEngineSnapshotSingle();
9897 // changing tile size requires update of graphic mapping for EM engine
9898 init_em_graphics = TRUE;
9909 SXSIZE = new_sxsize;
9910 SYSIZE = new_sysize;
9911 DXSIZE = new_dxsize;
9912 DYSIZE = new_dysize;
9913 VXSIZE = new_vxsize;
9914 VYSIZE = new_vysize;
9915 EXSIZE = new_exsize;
9916 EYSIZE = new_eysize;
9917 REAL_SX = new_real_sx;
9918 REAL_SY = new_real_sy;
9919 FULL_SXSIZE = new_full_sxsize;
9920 FULL_SYSIZE = new_full_sysize;
9921 TILESIZE_VAR = new_tilesize_var;
9923 init_gfx_buffers = TRUE;
9924 init_gadgets_and_anims = TRUE;
9926 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9927 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9930 if (init_gfx_buffers)
9932 // Debug("tools:viewport", "init_gfx_buffers");
9934 SCR_FIELDX = new_scr_fieldx_buffers;
9935 SCR_FIELDY = new_scr_fieldy_buffers;
9939 SCR_FIELDX = new_scr_fieldx;
9940 SCR_FIELDY = new_scr_fieldy;
9942 SetDrawDeactivationMask(REDRAW_NONE);
9943 SetDrawBackgroundMask(REDRAW_FIELD);
9946 if (init_video_buffer)
9948 // Debug("tools:viewport", "init_video_buffer");
9950 FreeAllImageTextures(); // needs old renderer to free the textures
9952 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9953 InitImageTextures();
9956 if (init_gadgets_and_anims)
9958 // Debug("tools:viewport", "init_gadgets_and_anims");
9961 InitGlobalAnimations();
9964 if (init_em_graphics)
9966 InitGraphicInfo_EM();
9970 void OpenURL(char *url)
9972 #if SDL_VERSION_ATLEAST(2,0,14)
9975 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9976 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9977 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9981 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9983 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9987 // ============================================================================
9989 // ============================================================================
9991 #if defined(PLATFORM_WINDOWS)
9992 /* FILETIME of Jan 1 1970 00:00:00. */
9993 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9996 * timezone information is stored outside the kernel so tzp isn't used anymore.
9998 * Note: this function is not for Win32 high precision timing purpose. See
10001 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10003 FILETIME file_time;
10004 SYSTEMTIME system_time;
10005 ULARGE_INTEGER ularge;
10007 GetSystemTime(&system_time);
10008 SystemTimeToFileTime(&system_time, &file_time);
10009 ularge.LowPart = file_time.dwLowDateTime;
10010 ularge.HighPart = file_time.dwHighDateTime;
10012 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10013 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10019 static char *test_init_uuid_random_function_simple(void)
10021 static char seed_text[100];
10022 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10024 sprintf(seed_text, "%d", seed);
10029 static char *test_init_uuid_random_function_better(void)
10031 static char seed_text[100];
10032 struct timeval current_time;
10034 gettimeofday(¤t_time, NULL);
10036 prng_seed_bytes(¤t_time, sizeof(current_time));
10038 sprintf(seed_text, "%ld.%ld",
10039 (long)current_time.tv_sec,
10040 (long)current_time.tv_usec);
10045 #if defined(PLATFORM_WINDOWS)
10046 static char *test_init_uuid_random_function_better_windows(void)
10048 static char seed_text[100];
10049 struct timeval current_time;
10051 gettimeofday_windows(¤t_time, NULL);
10053 prng_seed_bytes(¤t_time, sizeof(current_time));
10055 sprintf(seed_text, "%ld.%ld",
10056 (long)current_time.tv_sec,
10057 (long)current_time.tv_usec);
10063 static unsigned int test_uuid_random_function_simple(int max)
10065 return GetSimpleRandom(max);
10068 static unsigned int test_uuid_random_function_better(int max)
10070 return (max > 0 ? prng_get_uint() % max : 0);
10073 #if defined(PLATFORM_WINDOWS)
10074 #define NUM_UUID_TESTS 3
10076 #define NUM_UUID_TESTS 2
10079 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10081 struct hashtable *hash_seeds =
10082 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10083 struct hashtable *hash_uuids =
10084 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10085 static char message[100];
10088 char *random_name = (nr == 0 ? "simple" : "better");
10089 char *random_type = (always_seed ? "always" : "only once");
10090 char *(*init_random_function)(void) =
10092 test_init_uuid_random_function_simple :
10093 test_init_uuid_random_function_better);
10094 unsigned int (*random_function)(int) =
10096 test_uuid_random_function_simple :
10097 test_uuid_random_function_better);
10100 #if defined(PLATFORM_WINDOWS)
10103 random_name = "windows";
10104 init_random_function = test_init_uuid_random_function_better_windows;
10110 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10111 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10113 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10114 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10115 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10117 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10121 // always initialize random number generator at least once
10122 init_random_function();
10124 unsigned int time_start = SDL_GetTicks();
10126 for (i = 0; i < num_uuids; i++)
10130 char *seed = getStringCopy(init_random_function());
10132 hashtable_remove(hash_seeds, seed);
10133 hashtable_insert(hash_seeds, seed, "1");
10136 char *uuid = getStringCopy(getUUIDExt(random_function));
10138 hashtable_remove(hash_uuids, uuid);
10139 hashtable_insert(hash_uuids, uuid, "1");
10142 int num_unique_seeds = hashtable_count(hash_seeds);
10143 int num_unique_uuids = hashtable_count(hash_uuids);
10145 unsigned int time_needed = SDL_GetTicks() - time_start;
10147 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10149 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10152 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10154 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10155 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10157 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10159 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10161 Request(message, REQ_CONFIRM);
10163 hashtable_destroy(hash_seeds, 0);
10164 hashtable_destroy(hash_uuids, 0);
10167 void TestGeneratingUUIDs(void)
10169 int num_uuids = 1000000;
10172 for (i = 0; i < NUM_UUID_TESTS; i++)
10173 for (j = 0; j < 2; j++)
10174 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10176 CloseAllAndExit(0);