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,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(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_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 // currently not available
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 // never draw masked screen borders on borderless screens
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD(void)
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void DrawTileCursor(int draw_target)
634 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
639 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
644 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
647 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
652 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653 BlitScreenToBitmap_EM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655 BlitScreenToBitmap_SP(target_bitmap);
656 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657 BlitScreenToBitmap_MM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659 BlitScreenToBitmap_RND(target_bitmap);
661 redraw_mask |= REDRAW_FIELD;
664 static void DrawFramesPerSecond(void)
667 int font_nr = FONT_TEXT_2;
668 int font_width = getFontWidth(font_nr);
669 int draw_deactivation_mask = GetDrawDeactivationMask();
670 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
672 // draw FPS with leading space (needed if field buffer deactivated)
673 sprintf(text, " %04.1f fps", global.frames_per_second);
675 // override draw deactivation mask (required for invisible warp mode)
676 SetDrawDeactivationMask(REDRAW_NONE);
678 // draw opaque FPS if field buffer deactivated, else draw masked FPS
679 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
682 // set draw deactivation mask to previous value
683 SetDrawDeactivationMask(draw_deactivation_mask);
685 // force full-screen redraw in this frame
686 redraw_mask = REDRAW_ALL;
690 static void PrintFrameTimeDebugging(void)
692 static unsigned int last_counter = 0;
693 unsigned int counter = Counter();
694 int diff_1 = counter - last_counter;
695 int diff_2 = diff_1 - GAME_FRAME_DELAY;
697 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698 char diff_bar[2 * diff_2_max + 5];
702 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
704 for (i = 0; i < diff_2_max; i++)
705 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706 i >= diff_2_max - diff_2_cut ? '-' : ' ');
708 diff_bar[pos++] = '|';
710 for (i = 0; i < diff_2_max; i++)
711 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
713 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
715 diff_bar[pos++] = '\0';
717 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
720 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
723 last_counter = counter;
727 static int unifiedRedrawMask(int mask)
729 if (mask & REDRAW_ALL)
732 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
740 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
743 void BackToFront(void)
745 static int last_redraw_mask = REDRAW_NONE;
747 // force screen redraw in every frame to continue drawing global animations
748 // (but always use the last redraw mask to prevent unwanted side effects)
749 if (redraw_mask == REDRAW_NONE)
750 redraw_mask = last_redraw_mask;
752 last_redraw_mask = redraw_mask;
755 // masked border now drawn immediately when blitting backbuffer to window
757 // draw masked border to all viewports, if defined
758 DrawMaskedBorder(redraw_mask);
761 // draw frames per second (only if debug mode is enabled)
762 if (redraw_mask & REDRAW_FPS)
763 DrawFramesPerSecond();
765 // remove playfield redraw before potentially merging with doors redraw
766 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767 redraw_mask &= ~REDRAW_FIELD;
769 // redraw complete window if both playfield and (some) doors need redraw
770 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771 redraw_mask = REDRAW_ALL;
773 /* although redrawing the whole window would be fine for normal gameplay,
774 being able to only redraw the playfield is required for deactivating
775 certain drawing areas (mainly playfield) to work, which is needed for
776 warp-forward to be fast enough (by skipping redraw of most frames) */
778 if (redraw_mask & REDRAW_ALL)
780 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
782 else if (redraw_mask & REDRAW_FIELD)
784 BlitBitmap(backbuffer, window,
785 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
787 else if (redraw_mask & REDRAW_DOORS)
789 // merge door areas to prevent calling screen redraw more than once
795 if (redraw_mask & REDRAW_DOOR_1)
799 x2 = MAX(x2, DX + DXSIZE);
800 y2 = MAX(y2, DY + DYSIZE);
803 if (redraw_mask & REDRAW_DOOR_2)
807 x2 = MAX(x2, VX + VXSIZE);
808 y2 = MAX(y2, VY + VYSIZE);
811 if (redraw_mask & REDRAW_DOOR_3)
815 x2 = MAX(x2, EX + EXSIZE);
816 y2 = MAX(y2, EY + EYSIZE);
819 // make sure that at least one pixel is blitted, and inside the screen
820 // (else nothing is blitted, causing the animations not to be updated)
821 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823 x2 = MIN(MAX(1, x2), WIN_XSIZE);
824 y2 = MIN(MAX(1, y2), WIN_YSIZE);
826 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
829 redraw_mask = REDRAW_NONE;
832 PrintFrameTimeDebugging();
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
838 unsigned int frame_delay_value_old = GetVideoFrameDelay();
840 SetVideoFrameDelay(frame_delay_value);
844 SetVideoFrameDelay(frame_delay_value_old);
847 static int fade_type_skip = FADE_TYPE_NONE;
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
851 void (*draw_border_function)(void) = NULL;
852 int x, y, width, height;
853 int fade_delay, post_delay;
855 if (fade_type == FADE_TYPE_FADE_OUT)
857 if (fade_type_skip != FADE_TYPE_NONE)
859 // skip all fade operations until specified fade operation
860 if (fade_type & fade_type_skip)
861 fade_type_skip = FADE_TYPE_NONE;
866 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
870 redraw_mask |= fade_mask;
872 if (fade_type == FADE_TYPE_SKIP)
874 fade_type_skip = fade_mode;
879 fade_delay = fading.fade_delay;
880 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
882 if (fade_type_skip != FADE_TYPE_NONE)
884 // skip all fade operations until specified fade operation
885 if (fade_type & fade_type_skip)
886 fade_type_skip = FADE_TYPE_NONE;
891 if (global.autoplay_leveldir)
896 if (fade_mask == REDRAW_FIELD)
901 height = FADE_SYSIZE;
903 if (border.draw_masked_when_fading)
904 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
906 DrawMaskedBorder_FIELD(); // draw once
916 // when switching screens without fading, set fade delay to zero
917 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
920 // do not display black frame when fading out without fade delay
921 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
924 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925 draw_border_function);
927 redraw_mask &= ~fade_mask;
929 ClearAutoRepeatKeyEvents();
932 static void SetScreenStates_BeforeFadingIn(void)
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
940 // set screen mode for animations back to fading
941 global.anim_status = GAME_MODE_PSEUDO_FADING;
944 static void SetScreenStates_AfterFadingIn(void)
946 // store new source screen (to use correct masked border for fading)
947 gfx.fade_border_source_status = global.border_status;
949 global.anim_status = global.anim_status_next;
952 static void SetScreenStates_BeforeFadingOut(void)
954 // store new target screen (to use correct masked border for fading)
955 gfx.fade_border_target_status = game_status;
957 // set screen mode for animations to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 // store backbuffer with all animations that will be stopped for fading out
961 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
964 static void SetScreenStates_AfterFadingOut(void)
966 global.border_status = game_status;
969 void FadeIn(int fade_mask)
971 SetScreenStates_BeforeFadingIn();
974 DrawMaskedBorder(REDRAW_ALL);
977 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
980 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
984 FADE_SXSIZE = FULL_SXSIZE;
985 FADE_SYSIZE = FULL_SYSIZE;
987 // activate virtual buttons depending on upcoming game status
988 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989 game_status == GAME_MODE_PLAYING && !tape.playing)
990 SetOverlayActive(TRUE);
992 SetScreenStates_AfterFadingIn();
994 // force update of global animation status in case of rapid screen changes
995 redraw_mask = REDRAW_ALL;
999 void FadeOut(int fade_mask)
1001 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1006 SetScreenStates_BeforeFadingOut();
1008 SetTileCursorActive(FALSE);
1009 SetOverlayActive(FALSE);
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1018 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1020 SetScreenStates_AfterFadingOut();
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1025 static struct TitleFadingInfo fading_leave_stored;
1028 fading_leave_stored = fading_leave;
1030 fading = fading_leave_stored;
1033 void FadeSetEnterMenu(void)
1035 fading = menu.enter_menu;
1037 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1040 void FadeSetLeaveMenu(void)
1042 fading = menu.leave_menu;
1044 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetEnterScreen(void)
1049 fading = menu.enter_screen[game_status];
1051 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1054 void FadeSetNextScreen(void)
1056 fading = menu.next_screen[game_status];
1058 // (do not overwrite fade mode set by FadeSetEnterScreen)
1059 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1062 void FadeSetLeaveScreen(void)
1064 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1067 void FadeSetFromType(int type)
1069 if (type & TYPE_ENTER_SCREEN)
1070 FadeSetEnterScreen();
1071 else if (type & TYPE_ENTER)
1073 else if (type & TYPE_LEAVE)
1077 void FadeSetDisabled(void)
1079 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1081 fading = fading_none;
1084 void FadeSkipNextFadeIn(void)
1086 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1089 void FadeSkipNextFadeOut(void)
1091 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1096 if (graphic == IMG_UNDEFINED)
1099 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1101 return (graphic_info[graphic].bitmap != NULL || redefined ?
1102 graphic_info[graphic].bitmap :
1103 graphic_info[default_graphic].bitmap);
1106 static Bitmap *getBackgroundBitmap(int graphic)
1108 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1113 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1119 (status == GAME_MODE_MAIN ||
1120 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1121 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1122 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1123 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1126 return getGlobalBorderBitmap(graphic);
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1131 if (graphic_info[graphic].bitmap)
1132 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1135 void SetMainBackgroundImageIfDefined(int graphic)
1137 if (graphic_info[graphic].bitmap)
1138 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1143 if (graphic_info[graphic].bitmap)
1144 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1147 void SetWindowBackgroundImage(int graphic)
1149 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetMainBackgroundImage(int graphic)
1154 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetDoorBackgroundImage(int graphic)
1159 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetPanelBackground(void)
1164 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1166 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1169 SetDoorBackgroundBitmap(bitmap_db_panel);
1172 void DrawBackground(int x, int y, int width, int height)
1174 // "drawto" might still point to playfield buffer here (hall of fame)
1175 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1177 if (IN_GFX_FIELD_FULL(x, y))
1178 redraw_mask |= REDRAW_FIELD;
1179 else if (IN_GFX_DOOR_1(x, y))
1180 redraw_mask |= REDRAW_DOOR_1;
1181 else if (IN_GFX_DOOR_2(x, y))
1182 redraw_mask |= REDRAW_DOOR_2;
1183 else if (IN_GFX_DOOR_3(x, y))
1184 redraw_mask |= REDRAW_DOOR_3;
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1189 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1191 if (font->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1199 struct GraphicInfo *g = &graphic_info[graphic];
1201 if (g->bitmap == NULL)
1204 DrawBackground(x, y, width, height);
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1219 boolean CheckIfGlobalBorderHasChanged(void)
1221 // if game status has not changed, global border has not changed either
1222 if (game_status == game_status_last)
1225 // determine and store new global border bitmap for current game status
1226 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1228 return (global_border_bitmap_last != global_border_bitmap);
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1236 // if game status has not changed, nothing has to be redrawn
1237 if (game_status == game_status_last)
1240 // redraw if last screen was title screen
1241 if (game_status_last == GAME_MODE_TITLE)
1244 // redraw if global screen border has changed
1245 if (CheckIfGlobalBorderHasChanged())
1248 // redraw if position or size of playfield area has changed
1249 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1253 // redraw if position or size of door area has changed
1254 if (dx_last != DX || dy_last != DY ||
1255 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1258 // redraw if position or size of tape area has changed
1259 if (vx_last != VX || vy_last != VY ||
1260 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1263 // redraw if position or size of editor area has changed
1264 if (ex_last != EX || ey_last != EY ||
1265 exsize_last != EXSIZE || eysize_last != EYSIZE)
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1275 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1277 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1280 void RedrawGlobalBorder(void)
1282 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 RedrawGlobalBorderFromBitmap(bitmap);
1286 redraw_mask = REDRAW_ALL;
1289 static void RedrawGlobalBorderIfNeeded(void)
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 if (game_status == game_status_last)
1296 // copy current draw buffer to later copy back areas that have not changed
1297 if (game_status_last != GAME_MODE_TITLE)
1298 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301 if (CheckIfGlobalBorderRedrawIsNeeded())
1303 // determine and store new global border bitmap for current game status
1304 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1307 // redraw global screen border (or clear, if defined to be empty)
1308 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1310 if (game_status == GAME_MODE_EDITOR)
1311 DrawSpecialEditorDoor();
1313 // copy previous playfield and door areas, if they are defined on both
1314 // previous and current screen and if they still have the same size
1316 if (real_sx_last != -1 && real_sy_last != -1 &&
1317 REAL_SX != -1 && REAL_SY != -1 &&
1318 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1323 if (dx_last != -1 && dy_last != -1 &&
1324 DX != -1 && DY != -1 &&
1325 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326 BlitBitmap(bitmap_db_store_1, backbuffer,
1327 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1329 if (game_status != GAME_MODE_EDITOR)
1331 if (vx_last != -1 && vy_last != -1 &&
1332 VX != -1 && VY != -1 &&
1333 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334 BlitBitmap(bitmap_db_store_1, backbuffer,
1335 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1339 if (ex_last != -1 && ey_last != -1 &&
1340 EX != -1 && EY != -1 &&
1341 exsize_last == EXSIZE && eysize_last == EYSIZE)
1342 BlitBitmap(bitmap_db_store_1, backbuffer,
1343 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1346 redraw_mask = REDRAW_ALL;
1349 game_status_last = game_status;
1351 global_border_bitmap_last = global_border_bitmap;
1353 real_sx_last = REAL_SX;
1354 real_sy_last = REAL_SY;
1355 full_sxsize_last = FULL_SXSIZE;
1356 full_sysize_last = FULL_SYSIZE;
1359 dxsize_last = DXSIZE;
1360 dysize_last = DYSIZE;
1363 vxsize_last = VXSIZE;
1364 vysize_last = VYSIZE;
1367 exsize_last = EXSIZE;
1368 eysize_last = EYSIZE;
1371 void ClearField(void)
1373 RedrawGlobalBorderIfNeeded();
1375 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376 // (when entering hall of fame after playing)
1377 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1379 // !!! maybe this should be done before clearing the background !!!
1380 if (game_status == GAME_MODE_PLAYING)
1382 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1387 SetDrawtoField(DRAW_TO_BACKBUFFER);
1391 void MarkTileDirty(int x, int y)
1393 redraw_mask |= REDRAW_FIELD;
1396 void SetBorderElement(void)
1400 BorderElement = EL_EMPTY;
1402 // only the R'n'D game engine may use an additional steelwall border
1403 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1406 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1408 for (x = 0; x < lev_fieldx; x++)
1410 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411 BorderElement = EL_STEELWALL;
1413 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1419 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1420 int max_array_fieldx, int max_array_fieldy,
1421 short field[max_array_fieldx][max_array_fieldy],
1422 int max_fieldx, int max_fieldy)
1424 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1425 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1426 int old_element = field[start_x][start_y];
1429 // do nothing if start field already has the desired content
1430 if (old_element == fill_element)
1433 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1435 while (stack_pos > 0)
1437 struct XY current = stack_buffer[--stack_pos];
1440 field[current.x][current.y] = fill_element;
1442 for (i = 0; i < 4; i++)
1444 int x = current.x + check[i].x;
1445 int y = current.y + check[i].y;
1447 // check for stack buffer overflow (should not happen)
1448 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1449 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1451 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1452 stack_buffer[stack_pos++] = (struct XY){ x, y };
1457 void FloodFillLevel(int from_x, int from_y, int fill_element,
1458 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1459 int max_fieldx, int max_fieldy)
1461 FloodFillLevelExt(from_x, from_y, fill_element,
1462 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1463 max_fieldx, max_fieldy);
1466 void SetRandomAnimationValue(int x, int y)
1468 gfx.anim_random_frame = GfxRandom[x][y];
1471 int getGraphicAnimationFrame(int graphic, int sync_frame)
1473 // animation synchronized with global frame counter, not move position
1474 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1475 sync_frame = FrameCounter;
1477 return getAnimationFrame(graphic_info[graphic].anim_frames,
1478 graphic_info[graphic].anim_delay,
1479 graphic_info[graphic].anim_mode,
1480 graphic_info[graphic].anim_start_frame,
1484 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1486 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1488 struct GraphicInfo *g = &graphic_info[graphic];
1489 int xsize = MAX(1, g->anim_frames_per_line);
1490 int ysize = MAX(1, g->anim_frames / xsize);
1491 int xoffset = g->anim_start_frame % xsize;
1492 int yoffset = g->anim_start_frame % ysize;
1493 int x = (lx + xoffset + xsize) % xsize;
1494 int y = (ly + yoffset + ysize) % ysize;
1495 int sync_frame = y * xsize + x;
1497 return sync_frame % g->anim_frames;
1499 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1501 struct GraphicInfo *g = &graphic_info[graphic];
1502 int x = (lx + lev_fieldx) % lev_fieldx;
1503 int y = (ly + lev_fieldy) % lev_fieldy;
1504 int sync_frame = GfxRandomStatic[x][y];
1506 return sync_frame % g->anim_frames;
1509 return getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1512 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1514 struct GraphicInfo *g = &graphic_info[graphic];
1515 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1517 if (tilesize == gfx.standard_tile_size)
1518 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1519 else if (tilesize == game.tile_size)
1520 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1522 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1525 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1526 boolean get_backside)
1528 struct GraphicInfo *g = &graphic_info[graphic];
1529 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1530 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1532 if (g->offset_y == 0) // frames are ordered horizontally
1534 int max_width = g->anim_frames_per_line * g->width;
1535 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1537 *x = pos % max_width;
1538 *y = src_y % g->height + pos / max_width * g->height;
1540 else if (g->offset_x == 0) // frames are ordered vertically
1542 int max_height = g->anim_frames_per_line * g->height;
1543 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1545 *x = src_x % g->width + pos / max_height * g->width;
1546 *y = pos % max_height;
1548 else // frames are ordered diagonally
1550 *x = src_x + frame * g->offset_x;
1551 *y = src_y + frame * g->offset_y;
1555 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1556 Bitmap **bitmap, int *x, int *y,
1557 boolean get_backside)
1559 struct GraphicInfo *g = &graphic_info[graphic];
1561 // if no graphics defined at all, use fallback graphics
1562 if (g->bitmaps == NULL)
1563 *g = graphic_info[IMG_CHAR_EXCLAM];
1565 // if no in-game graphics defined, always use standard graphic size
1566 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1567 tilesize = TILESIZE;
1569 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1570 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1572 *x = *x * tilesize / g->tile_size;
1573 *y = *y * tilesize / g->tile_size;
1576 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1577 Bitmap **bitmap, int *x, int *y)
1579 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1582 void getFixedGraphicSource(int graphic, int frame,
1583 Bitmap **bitmap, int *x, int *y)
1585 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1588 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1590 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1593 void getGlobalAnimGraphicSource(int graphic, int frame,
1594 Bitmap **bitmap, int *x, int *y)
1596 struct GraphicInfo *g = &graphic_info[graphic];
1598 // if no graphics defined at all, use fallback graphics
1599 if (g->bitmaps == NULL)
1600 *g = graphic_info[IMG_CHAR_EXCLAM];
1602 // use original size graphics, if existing, else use standard size graphics
1603 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1604 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1606 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1608 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1611 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1612 int *x, int *y, boolean get_backside)
1614 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1618 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1620 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1623 void DrawGraphic(int x, int y, int graphic, int frame)
1626 if (!IN_SCR_FIELD(x, y))
1628 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1629 Debug("draw:DrawGraphic", "This should never happen!");
1635 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1638 MarkTileDirty(x, y);
1641 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1644 if (!IN_SCR_FIELD(x, y))
1646 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1648 Debug("draw:DrawFixedGraphic", "This should never happen!");
1654 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1656 MarkTileDirty(x, y);
1659 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1665 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1667 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1670 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1676 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1680 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1687 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1693 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1696 MarkTileDirty(x, y);
1699 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1702 if (!IN_SCR_FIELD(x, y))
1704 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1706 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1712 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1714 MarkTileDirty(x, y);
1717 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1723 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1725 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1729 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1730 int graphic, int frame)
1735 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1737 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1741 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1743 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1745 MarkTileDirty(x / tilesize, y / tilesize);
1748 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1751 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1752 graphic, frame, tilesize);
1753 MarkTileDirty(x / tilesize, y / tilesize);
1756 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1762 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1763 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1766 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1767 int frame, int tilesize)
1772 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1773 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1776 void DrawMiniGraphic(int x, int y, int graphic)
1778 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1779 MarkTileDirty(x / 2, y / 2);
1782 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1787 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1788 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1791 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1792 int graphic, int frame,
1793 int cut_mode, int mask_mode)
1798 int width = TILEX, height = TILEY;
1801 if (dx || dy) // shifted graphic
1803 if (x < BX1) // object enters playfield from the left
1810 else if (x > BX2) // object enters playfield from the right
1816 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1822 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1824 else if (dx) // general horizontal movement
1825 MarkTileDirty(x + SIGN(dx), y);
1827 if (y < BY1) // object enters playfield from the top
1829 if (cut_mode == CUT_BELOW) // object completely above top border
1837 else if (y > BY2) // object enters playfield from the bottom
1843 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1849 else if (dy > 0 && cut_mode == CUT_ABOVE)
1851 if (y == BY2) // object completely above bottom border
1857 MarkTileDirty(x, y + 1);
1858 } // object leaves playfield to the bottom
1859 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1861 else if (dy) // general vertical movement
1862 MarkTileDirty(x, y + SIGN(dy));
1866 if (!IN_SCR_FIELD(x, y))
1868 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1870 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1876 width = width * TILESIZE_VAR / TILESIZE;
1877 height = height * TILESIZE_VAR / TILESIZE;
1878 cx = cx * TILESIZE_VAR / TILESIZE;
1879 cy = cy * TILESIZE_VAR / TILESIZE;
1880 dx = dx * TILESIZE_VAR / TILESIZE;
1881 dy = dy * TILESIZE_VAR / TILESIZE;
1883 if (width > 0 && height > 0)
1885 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1890 dst_x = FX + x * TILEX_VAR + dx;
1891 dst_y = FY + y * TILEY_VAR + dy;
1893 if (mask_mode == USE_MASKING)
1894 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1897 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1900 MarkTileDirty(x, y);
1904 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1905 int graphic, int frame,
1906 int cut_mode, int mask_mode)
1911 int width = TILEX_VAR, height = TILEY_VAR;
1914 int x2 = x + SIGN(dx);
1915 int y2 = y + SIGN(dy);
1917 // movement with two-tile animations must be sync'ed with movement position,
1918 // not with current GfxFrame (which can be higher when using slow movement)
1919 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1920 int anim_frames = graphic_info[graphic].anim_frames;
1922 // (we also need anim_delay here for movement animations with less frames)
1923 int anim_delay = graphic_info[graphic].anim_delay;
1924 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1926 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1927 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1929 // re-calculate animation frame for two-tile movement animation
1930 frame = getGraphicAnimationFrame(graphic, sync_frame);
1932 // check if movement start graphic inside screen area and should be drawn
1933 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1935 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1937 dst_x = FX + x1 * TILEX_VAR;
1938 dst_y = FY + y1 * TILEY_VAR;
1940 if (mask_mode == USE_MASKING)
1941 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1944 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1947 MarkTileDirty(x1, y1);
1950 // check if movement end graphic inside screen area and should be drawn
1951 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1953 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1955 dst_x = FX + x2 * TILEX_VAR;
1956 dst_y = FY + y2 * TILEY_VAR;
1958 if (mask_mode == USE_MASKING)
1959 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1962 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1965 MarkTileDirty(x2, y2);
1969 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1970 int graphic, int frame,
1971 int cut_mode, int mask_mode)
1975 DrawGraphic(x, y, graphic, frame);
1980 if (graphic_info[graphic].double_movement) // EM style movement images
1981 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1983 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1986 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1987 int graphic, int frame, int cut_mode)
1989 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1992 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1993 int cut_mode, int mask_mode)
1995 int lx = LEVELX(x), ly = LEVELY(y);
1999 if (IN_LEV_FIELD(lx, ly))
2001 SetRandomAnimationValue(lx, ly);
2003 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2004 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2006 // do not use double (EM style) movement graphic when not moving
2007 if (graphic_info[graphic].double_movement && !dx && !dy)
2009 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2010 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2013 if (game.use_masked_elements && (dx || dy))
2014 mask_mode = USE_MASKING;
2016 else // border element
2018 graphic = el2img(element);
2019 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2022 if (element == EL_EXPANDABLE_WALL)
2024 boolean left_stopped = FALSE, right_stopped = FALSE;
2026 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2027 left_stopped = TRUE;
2028 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2029 right_stopped = TRUE;
2031 if (left_stopped && right_stopped)
2033 else if (left_stopped)
2035 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2036 frame = graphic_info[graphic].anim_frames - 1;
2038 else if (right_stopped)
2040 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2041 frame = graphic_info[graphic].anim_frames - 1;
2046 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2047 else if (mask_mode == USE_MASKING)
2048 DrawGraphicThruMask(x, y, graphic, frame);
2050 DrawGraphic(x, y, graphic, frame);
2053 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2054 int cut_mode, int mask_mode)
2056 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2057 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2058 cut_mode, mask_mode);
2061 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2064 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2067 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2070 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2073 void DrawLevelElementThruMask(int x, int y, int element)
2075 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2078 void DrawLevelFieldThruMask(int x, int y)
2080 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2083 // !!! implementation of quicksand is totally broken !!!
2084 #define IS_CRUMBLED_TILE(x, y, e) \
2085 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2086 !IS_MOVING(x, y) || \
2087 (e) == EL_QUICKSAND_EMPTYING || \
2088 (e) == EL_QUICKSAND_FAST_EMPTYING))
2090 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2095 int width, height, cx, cy;
2096 int sx = SCREENX(x), sy = SCREENY(y);
2097 int crumbled_border_size = graphic_info[graphic].border_size;
2098 int crumbled_tile_size = graphic_info[graphic].tile_size;
2099 int crumbled_border_size_var =
2100 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2103 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2105 for (i = 1; i < 4; i++)
2107 int dxx = (i & 1 ? dx : 0);
2108 int dyy = (i & 2 ? dy : 0);
2111 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2114 // check if neighbour field is of same crumble type
2115 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2116 graphic_info[graphic].class ==
2117 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2119 // return if check prevents inner corner
2120 if (same == (dxx == dx && dyy == dy))
2124 // if we reach this point, we have an inner corner
2126 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2128 width = crumbled_border_size_var;
2129 height = crumbled_border_size_var;
2130 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2131 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2133 if (game.use_masked_elements)
2135 int graphic0 = el2img(EL_EMPTY);
2136 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2137 Bitmap *src_bitmap0;
2140 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2142 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2144 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2146 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2148 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2151 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2153 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2156 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2161 int width, height, bx, by, cx, cy;
2162 int sx = SCREENX(x), sy = SCREENY(y);
2163 int crumbled_border_size = graphic_info[graphic].border_size;
2164 int crumbled_tile_size = graphic_info[graphic].tile_size;
2165 int crumbled_border_size_var =
2166 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2167 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2170 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2172 // only needed when using masked elements
2173 int graphic0 = el2img(EL_EMPTY);
2174 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2175 Bitmap *src_bitmap0;
2178 if (game.use_masked_elements)
2179 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2181 // draw simple, sloppy, non-corner-accurate crumbled border
2183 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2184 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2185 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2186 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2188 if (game.use_masked_elements)
2190 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2192 FX + sx * TILEX_VAR + cx,
2193 FY + sy * TILEY_VAR + cy);
2195 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2197 FX + sx * TILEX_VAR + cx,
2198 FY + sy * TILEY_VAR + cy);
2201 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2203 FX + sx * TILEX_VAR + cx,
2204 FY + sy * TILEY_VAR + cy);
2206 // (remaining middle border part must be at least as big as corner part)
2207 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2208 crumbled_border_size_var >= TILESIZE_VAR / 3)
2211 // correct corners of crumbled border, if needed
2213 for (i = -1; i <= 1; i += 2)
2215 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2216 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2217 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2220 // check if neighbour field is of same crumble type
2221 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2222 graphic_info[graphic].class ==
2223 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2225 // no crumbled corner, but continued crumbled border
2227 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2228 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2229 int b1 = (i == 1 ? crumbled_border_size_var :
2230 TILESIZE_VAR - 2 * crumbled_border_size_var);
2232 width = crumbled_border_size_var;
2233 height = crumbled_border_size_var;
2235 if (dir == 1 || dir == 2)
2250 if (game.use_masked_elements)
2252 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2254 FX + sx * TILEX_VAR + cx,
2255 FY + sy * TILEY_VAR + cy);
2257 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2259 FX + sx * TILEX_VAR + cx,
2260 FY + sy * TILEY_VAR + cy);
2263 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2265 FX + sx * TILEX_VAR + cx,
2266 FY + sy * TILEY_VAR + cy);
2271 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2273 int sx = SCREENX(x), sy = SCREENY(y);
2276 static int xy[4][2] =
2284 if (!IN_LEV_FIELD(x, y))
2287 element = TILE_GFX_ELEMENT(x, y);
2289 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2291 if (!IN_SCR_FIELD(sx, sy))
2294 // crumble field borders towards direct neighbour fields
2295 for (i = 0; i < 4; i++)
2297 int xx = x + xy[i][0];
2298 int yy = y + xy[i][1];
2300 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2303 // check if neighbour field is of same crumble type
2304 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2305 graphic_info[graphic].class ==
2306 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2309 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2312 // crumble inner field corners towards corner neighbour fields
2313 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2314 graphic_info[graphic].anim_frames == 2)
2316 for (i = 0; i < 4; i++)
2318 int dx = (i & 1 ? +1 : -1);
2319 int dy = (i & 2 ? +1 : -1);
2321 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2325 MarkTileDirty(sx, sy);
2327 else // center field is not crumbled -- crumble neighbour fields
2329 // crumble field borders of direct neighbour fields
2330 for (i = 0; i < 4; i++)
2332 int xx = x + xy[i][0];
2333 int yy = y + xy[i][1];
2334 int sxx = sx + xy[i][0];
2335 int syy = sy + xy[i][1];
2337 if (!IN_LEV_FIELD(xx, yy) ||
2338 !IN_SCR_FIELD(sxx, syy))
2341 // do not crumble fields that are being digged or snapped
2342 if (Tile[xx][yy] == EL_EMPTY ||
2343 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2346 element = TILE_GFX_ELEMENT(xx, yy);
2348 if (!IS_CRUMBLED_TILE(xx, yy, element))
2351 graphic = el_act2crm(element, ACTION_DEFAULT);
2353 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2355 MarkTileDirty(sxx, syy);
2358 // crumble inner field corners of corner neighbour fields
2359 for (i = 0; i < 4; i++)
2361 int dx = (i & 1 ? +1 : -1);
2362 int dy = (i & 2 ? +1 : -1);
2368 if (!IN_LEV_FIELD(xx, yy) ||
2369 !IN_SCR_FIELD(sxx, syy))
2372 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2375 element = TILE_GFX_ELEMENT(xx, yy);
2377 if (!IS_CRUMBLED_TILE(xx, yy, element))
2380 graphic = el_act2crm(element, ACTION_DEFAULT);
2382 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2383 graphic_info[graphic].anim_frames == 2)
2384 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2386 MarkTileDirty(sxx, syy);
2391 void DrawLevelFieldCrumbled(int x, int y)
2395 if (!IN_LEV_FIELD(x, y))
2398 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2399 GfxElement[x][y] != EL_UNDEFINED &&
2400 GFX_CRUMBLED(GfxElement[x][y]))
2402 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2407 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2409 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2412 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2415 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2416 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2417 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2418 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2419 int sx = SCREENX(x), sy = SCREENY(y);
2421 DrawScreenGraphic(sx, sy, graphic1, frame1);
2422 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2425 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2427 int sx = SCREENX(x), sy = SCREENY(y);
2428 static int xy[4][2] =
2437 // crumble direct neighbour fields (required for field borders)
2438 for (i = 0; i < 4; i++)
2440 int xx = x + xy[i][0];
2441 int yy = y + xy[i][1];
2442 int sxx = sx + xy[i][0];
2443 int syy = sy + xy[i][1];
2445 if (!IN_LEV_FIELD(xx, yy) ||
2446 !IN_SCR_FIELD(sxx, syy) ||
2447 !GFX_CRUMBLED(Tile[xx][yy]) ||
2451 DrawLevelField(xx, yy);
2454 // crumble corner neighbour fields (required for inner field corners)
2455 for (i = 0; i < 4; i++)
2457 int dx = (i & 1 ? +1 : -1);
2458 int dy = (i & 2 ? +1 : -1);
2464 if (!IN_LEV_FIELD(xx, yy) ||
2465 !IN_SCR_FIELD(sxx, syy) ||
2466 !GFX_CRUMBLED(Tile[xx][yy]) ||
2470 int element = TILE_GFX_ELEMENT(xx, yy);
2471 int graphic = el_act2crm(element, ACTION_DEFAULT);
2473 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2474 graphic_info[graphic].anim_frames == 2)
2475 DrawLevelField(xx, yy);
2479 static int getBorderElement(int x, int y)
2483 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2484 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2485 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2486 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2487 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2488 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2489 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2491 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2492 int steel_position = (x == -1 && y == -1 ? 0 :
2493 x == lev_fieldx && y == -1 ? 1 :
2494 x == -1 && y == lev_fieldy ? 2 :
2495 x == lev_fieldx && y == lev_fieldy ? 3 :
2496 x == -1 || x == lev_fieldx ? 4 :
2497 y == -1 || y == lev_fieldy ? 5 : 6);
2499 return border[steel_position][steel_type];
2502 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2504 if (game.use_masked_elements)
2506 if (graphic != el2img(EL_EMPTY))
2507 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2509 DrawGraphicThruMask(x, y, graphic, frame);
2513 DrawGraphic(x, y, graphic, frame);
2517 void DrawScreenElement(int x, int y, int element)
2519 int mask_mode = NO_MASKING;
2521 if (game.use_masked_elements)
2523 int lx = LEVELX(x), ly = LEVELY(y);
2525 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2527 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2529 mask_mode = USE_MASKING;
2533 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2534 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2537 void DrawLevelElement(int x, int y, int element)
2539 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2540 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2543 void DrawScreenField(int x, int y)
2545 int lx = LEVELX(x), ly = LEVELY(y);
2546 int element, content;
2548 if (!IN_LEV_FIELD(lx, ly))
2550 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2553 element = getBorderElement(lx, ly);
2555 DrawScreenElement(x, y, element);
2560 element = Tile[lx][ly];
2561 content = Store[lx][ly];
2563 if (IS_MOVING(lx, ly))
2565 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2566 boolean cut_mode = NO_CUTTING;
2568 if (element == EL_QUICKSAND_EMPTYING ||
2569 element == EL_QUICKSAND_FAST_EMPTYING ||
2570 element == EL_MAGIC_WALL_EMPTYING ||
2571 element == EL_BD_MAGIC_WALL_EMPTYING ||
2572 element == EL_DC_MAGIC_WALL_EMPTYING ||
2573 element == EL_AMOEBA_DROPPING)
2574 cut_mode = CUT_ABOVE;
2575 else if (element == EL_QUICKSAND_FILLING ||
2576 element == EL_QUICKSAND_FAST_FILLING ||
2577 element == EL_MAGIC_WALL_FILLING ||
2578 element == EL_BD_MAGIC_WALL_FILLING ||
2579 element == EL_DC_MAGIC_WALL_FILLING)
2580 cut_mode = CUT_BELOW;
2582 if (cut_mode == CUT_ABOVE)
2583 DrawScreenElement(x, y, element);
2585 DrawScreenElement(x, y, EL_EMPTY);
2587 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2589 int dir = MovDir[lx][ly];
2590 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2591 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2593 if (IN_SCR_FIELD(newx, newy))
2594 DrawScreenElement(newx, newy, EL_EMPTY);
2598 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2599 else if (cut_mode == NO_CUTTING)
2600 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2603 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2605 if (cut_mode == CUT_BELOW &&
2606 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2607 DrawLevelElement(lx, ly + 1, element);
2610 if (content == EL_ACID)
2612 int dir = MovDir[lx][ly];
2613 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2614 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2616 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2618 // prevent target field from being drawn again (but without masking)
2619 // (this would happen if target field is scanned after moving element)
2620 Stop[newlx][newly] = TRUE;
2623 else if (IS_BLOCKED(lx, ly))
2628 boolean cut_mode = NO_CUTTING;
2629 int element_old, content_old;
2631 Blocked2Moving(lx, ly, &oldx, &oldy);
2634 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2635 MovDir[oldx][oldy] == MV_RIGHT);
2637 element_old = Tile[oldx][oldy];
2638 content_old = Store[oldx][oldy];
2640 if (element_old == EL_QUICKSAND_EMPTYING ||
2641 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2642 element_old == EL_MAGIC_WALL_EMPTYING ||
2643 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2644 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2645 element_old == EL_AMOEBA_DROPPING)
2646 cut_mode = CUT_ABOVE;
2648 DrawScreenElement(x, y, EL_EMPTY);
2651 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2653 else if (cut_mode == NO_CUTTING)
2654 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2657 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2660 else if (IS_DRAWABLE(element))
2661 DrawScreenElement(x, y, element);
2663 DrawScreenElement(x, y, EL_EMPTY);
2666 void DrawLevelField(int x, int y)
2668 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2669 DrawScreenField(SCREENX(x), SCREENY(y));
2670 else if (IS_MOVING(x, y))
2674 Moving2Blocked(x, y, &newx, &newy);
2675 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2676 DrawScreenField(SCREENX(newx), SCREENY(newy));
2678 else if (IS_BLOCKED(x, y))
2682 Blocked2Moving(x, y, &oldx, &oldy);
2683 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2684 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2688 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2689 int (*el2img_function)(int), boolean masked,
2690 int element_bits_draw)
2692 int element_base = map_mm_wall_element(element);
2693 int element_bits = (IS_DF_WALL(element) ?
2694 element - EL_DF_WALL_START :
2695 IS_MM_WALL(element) ?
2696 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2697 int graphic = el2img_function(element_base);
2698 int tilesize_draw = tilesize / 2;
2703 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2705 for (i = 0; i < 4; i++)
2707 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2708 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2710 if (!(element_bits_draw & (1 << i)))
2713 if (element_bits & (1 << i))
2716 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2717 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2719 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2720 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2725 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2726 tilesize_draw, tilesize_draw);
2731 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2732 boolean masked, int element_bits_draw)
2734 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2735 element, tilesize, el2edimg, masked, element_bits_draw);
2738 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2739 int (*el2img_function)(int))
2741 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2745 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2748 if (IS_MM_WALL(element))
2750 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2751 element, tilesize, el2edimg, masked, 0x000f);
2755 int graphic = el2edimg(element);
2758 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2760 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2764 void DrawSizedElement(int x, int y, int element, int tilesize)
2766 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2769 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2771 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2774 void DrawMiniElement(int x, int y, int element)
2778 graphic = el2edimg(element);
2779 DrawMiniGraphic(x, y, graphic);
2782 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2785 int x = sx + scroll_x, y = sy + scroll_y;
2787 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2788 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2789 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2790 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2792 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2795 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2797 int x = sx + scroll_x, y = sy + scroll_y;
2799 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2800 DrawMiniElement(sx, sy, EL_EMPTY);
2801 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2802 DrawMiniElement(sx, sy, Tile[x][y]);
2804 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2807 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2808 int x, int y, int xsize, int ysize,
2809 int tile_width, int tile_height)
2813 int dst_x = startx + x * tile_width;
2814 int dst_y = starty + y * tile_height;
2815 int width = graphic_info[graphic].width;
2816 int height = graphic_info[graphic].height;
2817 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2818 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2819 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2820 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2821 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2822 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2823 boolean draw_masked = graphic_info[graphic].draw_masked;
2825 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2827 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2829 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2833 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2834 inner_sx + (x - 1) * tile_width % inner_width);
2835 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2836 inner_sy + (y - 1) * tile_height % inner_height);
2839 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2842 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2846 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2847 int x, int y, int xsize, int ysize,
2850 int font_width = getFontWidth(font_nr);
2851 int font_height = getFontHeight(font_nr);
2853 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2854 font_width, font_height);
2857 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2859 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2860 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2861 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2862 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2863 boolean no_delay = (tape.warp_forward);
2864 unsigned int anim_delay = 0;
2865 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2866 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2867 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2868 int font_width = getFontWidth(font_nr);
2869 int font_height = getFontHeight(font_nr);
2870 int max_xsize = level.envelope[envelope_nr].xsize;
2871 int max_ysize = level.envelope[envelope_nr].ysize;
2872 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2873 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2874 int xend = max_xsize;
2875 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2876 int xstep = (xstart < xend ? 1 : 0);
2877 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2879 int end = MAX(xend - xstart, yend - ystart);
2882 for (i = start; i <= end; i++)
2884 int last_frame = end; // last frame of this "for" loop
2885 int x = xstart + i * xstep;
2886 int y = ystart + i * ystep;
2887 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2888 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2889 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2890 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2893 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2895 BlitScreenToBitmap(backbuffer);
2897 SetDrawtoField(DRAW_TO_BACKBUFFER);
2899 for (yy = 0; yy < ysize; yy++)
2900 for (xx = 0; xx < xsize; xx++)
2901 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2903 DrawTextBuffer(sx + font_width, sy + font_height,
2904 level.envelope[envelope_nr].text, font_nr, max_xsize,
2905 xsize - 2, ysize - 2, 0, mask_mode,
2906 level.envelope[envelope_nr].autowrap,
2907 level.envelope[envelope_nr].centered, FALSE);
2909 redraw_mask |= REDRAW_FIELD;
2912 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2915 ClearAutoRepeatKeyEvents();
2918 void ShowEnvelope(int envelope_nr)
2920 int element = EL_ENVELOPE_1 + envelope_nr;
2921 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2922 int sound_opening = element_info[element].sound[ACTION_OPENING];
2923 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2924 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2925 boolean no_delay = (tape.warp_forward);
2926 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2927 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2928 int anim_mode = graphic_info[graphic].anim_mode;
2929 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2930 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2931 boolean overlay_enabled = GetOverlayEnabled();
2933 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2935 SetOverlayEnabled(FALSE);
2938 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2940 if (anim_mode == ANIM_DEFAULT)
2941 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2943 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2946 Delay_WithScreenUpdates(wait_delay_value);
2948 WaitForEventToContinue();
2951 SetOverlayEnabled(overlay_enabled);
2953 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2955 if (anim_mode != ANIM_NONE)
2956 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2958 if (anim_mode == ANIM_DEFAULT)
2959 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2961 game.envelope_active = FALSE;
2963 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2965 redraw_mask |= REDRAW_FIELD;
2969 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2970 int xsize, int ysize)
2972 if (!global.use_envelope_request ||
2973 request.sort_priority <= 0)
2976 if (request.bitmap == NULL ||
2977 xsize > request.xsize ||
2978 ysize > request.ysize)
2980 if (request.bitmap != NULL)
2981 FreeBitmap(request.bitmap);
2983 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2985 SDL_Surface *surface = request.bitmap->surface;
2987 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2988 Fail("SDLGetNativeSurface() failed");
2991 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2993 SDLFreeBitmapTextures(request.bitmap);
2994 SDLCreateBitmapTextures(request.bitmap);
2996 // set envelope request run-time values
2999 request.xsize = xsize;
3000 request.ysize = ysize;
3003 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3005 if (global.use_envelope_request &&
3006 game.request_active_or_moving &&
3007 request.sort_priority > 0 &&
3008 drawing_target == DRAW_TO_SCREEN &&
3009 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3011 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3012 request.sx, request.sy);
3016 static void setRequestBasePosition(int *x, int *y)
3018 int sx_base, sy_base;
3020 if (request.x != -1)
3021 sx_base = request.x;
3022 else if (request.align == ALIGN_LEFT)
3024 else if (request.align == ALIGN_RIGHT)
3025 sx_base = SX + SXSIZE;
3027 sx_base = SX + SXSIZE / 2;
3029 if (request.y != -1)
3030 sy_base = request.y;
3031 else if (request.valign == VALIGN_TOP)
3033 else if (request.valign == VALIGN_BOTTOM)
3034 sy_base = SY + SYSIZE;
3036 sy_base = SY + SYSIZE / 2;
3042 static void setRequestPositionExt(int *x, int *y, int width, int height,
3043 boolean add_border_size)
3045 int border_size = request.border_size;
3046 int sx_base, sy_base;
3049 setRequestBasePosition(&sx_base, &sy_base);
3051 if (request.align == ALIGN_LEFT)
3053 else if (request.align == ALIGN_RIGHT)
3054 sx = sx_base - width;
3056 sx = sx_base - width / 2;
3058 if (request.valign == VALIGN_TOP)
3060 else if (request.valign == VALIGN_BOTTOM)
3061 sy = sy_base - height;
3063 sy = sy_base - height / 2;
3065 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3066 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3068 if (add_border_size)
3078 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3080 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3083 static void DrawEnvelopeRequest(char *text)
3085 char *text_final = text;
3086 char *text_door_style = NULL;
3087 int graphic = IMG_BACKGROUND_REQUEST;
3088 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3089 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3090 int font_nr = FONT_REQUEST;
3091 int font_width = getFontWidth(font_nr);
3092 int font_height = getFontHeight(font_nr);
3093 int border_size = request.border_size;
3094 int line_spacing = request.line_spacing;
3095 int line_height = font_height + line_spacing;
3096 int max_text_width = request.width - 2 * border_size;
3097 int max_text_height = request.height - 2 * border_size;
3098 int line_length = max_text_width / font_width;
3099 int max_lines = max_text_height / line_height;
3100 int text_width = line_length * font_width;
3101 int width = request.width;
3102 int height = request.height;
3103 int tile_size = MAX(request.step_offset, 1);
3104 int x_steps = width / tile_size;
3105 int y_steps = height / tile_size;
3106 int sx_offset = border_size;
3107 int sy_offset = border_size;
3111 if (request.centered)
3112 sx_offset = (request.width - text_width) / 2;
3114 if (request.wrap_single_words && !request.autowrap)
3116 char *src_text_ptr, *dst_text_ptr;
3118 text_door_style = checked_malloc(2 * strlen(text) + 1);
3120 src_text_ptr = text;
3121 dst_text_ptr = text_door_style;
3123 while (*src_text_ptr)
3125 if (*src_text_ptr == ' ' ||
3126 *src_text_ptr == '?' ||
3127 *src_text_ptr == '!')
3128 *dst_text_ptr++ = '\n';
3130 if (*src_text_ptr != ' ')
3131 *dst_text_ptr++ = *src_text_ptr;
3136 *dst_text_ptr = '\0';
3138 text_final = text_door_style;
3141 setRequestPosition(&sx, &sy, FALSE);
3143 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3145 for (y = 0; y < y_steps; y++)
3146 for (x = 0; x < x_steps; x++)
3147 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3148 x, y, x_steps, y_steps,
3149 tile_size, tile_size);
3151 // force DOOR font inside door area
3152 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3154 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3155 line_length, -1, max_lines, line_spacing, mask_mode,
3156 request.autowrap, request.centered, FALSE);
3160 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3161 RedrawGadget(tool_gadget[i]);
3163 // store readily prepared envelope request for later use when animating
3164 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3166 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3168 if (text_door_style)
3169 free(text_door_style);
3172 static void AnimateEnvelopeRequest(int anim_mode, int action)
3174 int graphic = IMG_BACKGROUND_REQUEST;
3175 boolean draw_masked = graphic_info[graphic].draw_masked;
3176 int delay_value_normal = request.step_delay;
3177 int delay_value_fast = delay_value_normal / 2;
3178 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3179 boolean no_delay = (tape.warp_forward);
3180 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3181 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3182 unsigned int anim_delay = 0;
3184 int tile_size = MAX(request.step_offset, 1);
3185 int max_xsize = request.width / tile_size;
3186 int max_ysize = request.height / tile_size;
3187 int max_xsize_inner = max_xsize - 2;
3188 int max_ysize_inner = max_ysize - 2;
3190 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3191 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3192 int xend = max_xsize_inner;
3193 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3194 int xstep = (xstart < xend ? 1 : 0);
3195 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3197 int end = MAX(xend - xstart, yend - ystart);
3200 if (setup.quick_doors)
3207 for (i = start; i <= end; i++)
3209 int last_frame = end; // last frame of this "for" loop
3210 int x = xstart + i * xstep;
3211 int y = ystart + i * ystep;
3212 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3213 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3214 int xsize_size_left = (xsize - 1) * tile_size;
3215 int ysize_size_top = (ysize - 1) * tile_size;
3216 int max_xsize_pos = (max_xsize - 1) * tile_size;
3217 int max_ysize_pos = (max_ysize - 1) * tile_size;
3218 int width = xsize * tile_size;
3219 int height = ysize * tile_size;
3224 setRequestPosition(&src_x, &src_y, FALSE);
3225 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3227 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3229 for (yy = 0; yy < 2; yy++)
3231 for (xx = 0; xx < 2; xx++)
3233 int src_xx = src_x + xx * max_xsize_pos;
3234 int src_yy = src_y + yy * max_ysize_pos;
3235 int dst_xx = dst_x + xx * xsize_size_left;
3236 int dst_yy = dst_y + yy * ysize_size_top;
3237 int xx_size = (xx ? tile_size : xsize_size_left);
3238 int yy_size = (yy ? tile_size : ysize_size_top);
3241 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3242 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3244 BlitBitmap(bitmap_db_store_2, backbuffer,
3245 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3249 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3251 redraw_mask |= REDRAW_FIELD;
3255 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3258 ClearAutoRepeatKeyEvents();
3261 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3263 int graphic = IMG_BACKGROUND_REQUEST;
3264 int sound_opening = SND_REQUEST_OPENING;
3265 int sound_closing = SND_REQUEST_CLOSING;
3266 int anim_mode_1 = request.anim_mode; // (higher priority)
3267 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3268 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3269 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3270 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3272 if (game_status == GAME_MODE_PLAYING)
3273 BlitScreenToBitmap(backbuffer);
3275 SetDrawtoField(DRAW_TO_BACKBUFFER);
3277 // SetDrawBackgroundMask(REDRAW_NONE);
3279 if (action == ACTION_OPENING)
3281 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3283 if (req_state & REQ_ASK)
3285 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3286 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3287 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3288 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3290 else if (req_state & REQ_CONFIRM)
3292 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3293 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3295 else if (req_state & REQ_PLAYER)
3297 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3298 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3299 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3300 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3303 DrawEnvelopeRequest(text);
3306 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3308 if (action == ACTION_OPENING)
3310 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3312 if (anim_mode == ANIM_DEFAULT)
3313 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3315 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3319 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3321 if (anim_mode != ANIM_NONE)
3322 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3324 if (anim_mode == ANIM_DEFAULT)
3325 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3328 game.envelope_active = FALSE;
3330 if (action == ACTION_CLOSING)
3331 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3333 // SetDrawBackgroundMask(last_draw_background_mask);
3335 redraw_mask |= REDRAW_FIELD;
3339 if (action == ACTION_CLOSING &&
3340 game_status == GAME_MODE_PLAYING &&
3341 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3342 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3345 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3347 if (IS_MM_WALL(element))
3349 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3355 int graphic = el2preimg(element);
3357 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3358 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3363 void DrawLevel(int draw_background_mask)
3367 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3368 SetDrawBackgroundMask(draw_background_mask);
3372 for (x = BX1; x <= BX2; x++)
3373 for (y = BY1; y <= BY2; y++)
3374 DrawScreenField(x, y);
3376 redraw_mask |= REDRAW_FIELD;
3379 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3384 for (x = 0; x < size_x; x++)
3385 for (y = 0; y < size_y; y++)
3386 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3388 redraw_mask |= REDRAW_FIELD;
3391 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3395 for (x = 0; x < size_x; x++)
3396 for (y = 0; y < size_y; y++)
3397 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3399 redraw_mask |= REDRAW_FIELD;
3402 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3404 boolean show_level_border = (BorderElement != EL_EMPTY);
3405 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3406 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3407 int tile_size = preview.tile_size;
3408 int preview_width = preview.xsize * tile_size;
3409 int preview_height = preview.ysize * tile_size;
3410 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3411 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3412 int real_preview_width = real_preview_xsize * tile_size;
3413 int real_preview_height = real_preview_ysize * tile_size;
3414 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3415 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3418 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3421 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3423 dst_x += (preview_width - real_preview_width) / 2;
3424 dst_y += (preview_height - real_preview_height) / 2;
3426 for (x = 0; x < real_preview_xsize; x++)
3428 for (y = 0; y < real_preview_ysize; y++)
3430 int lx = from_x + x + (show_level_border ? -1 : 0);
3431 int ly = from_y + y + (show_level_border ? -1 : 0);
3432 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3433 getBorderElement(lx, ly));
3435 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3436 element, tile_size);
3440 redraw_mask |= REDRAW_FIELD;
3443 #define MICROLABEL_EMPTY 0
3444 #define MICROLABEL_LEVEL_NAME 1
3445 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3446 #define MICROLABEL_LEVEL_AUTHOR 3
3447 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3448 #define MICROLABEL_IMPORTED_FROM 5
3449 #define MICROLABEL_IMPORTED_BY_HEAD 6
3450 #define MICROLABEL_IMPORTED_BY 7
3452 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3454 int max_text_width = SXSIZE;
3455 int font_width = getFontWidth(font_nr);
3457 if (pos->align == ALIGN_CENTER)
3458 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3459 else if (pos->align == ALIGN_RIGHT)
3460 max_text_width = pos->x;
3462 max_text_width = SXSIZE - pos->x;
3464 return max_text_width / font_width;
3467 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3469 char label_text[MAX_OUTPUT_LINESIZE + 1];
3470 int max_len_label_text;
3471 int font_nr = pos->font;
3474 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3477 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3478 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3479 mode == MICROLABEL_IMPORTED_BY_HEAD)
3480 font_nr = pos->font_alt;
3482 max_len_label_text = getMaxTextLength(pos, font_nr);
3484 if (pos->size != -1)
3485 max_len_label_text = pos->size;
3487 for (i = 0; i < max_len_label_text; i++)
3488 label_text[i] = ' ';
3489 label_text[max_len_label_text] = '\0';
3491 if (strlen(label_text) > 0)
3492 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3495 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3496 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3497 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3498 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3499 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3500 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3501 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3502 max_len_label_text);
3503 label_text[max_len_label_text] = '\0';
3505 if (strlen(label_text) > 0)
3506 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3508 redraw_mask |= REDRAW_FIELD;
3511 static void DrawPreviewLevelLabel(int mode)
3513 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3516 static void DrawPreviewLevelInfo(int mode)
3518 if (mode == MICROLABEL_LEVEL_NAME)
3519 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3520 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3521 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3524 static void DrawPreviewLevelExt(boolean restart)
3526 static unsigned int scroll_delay = 0;
3527 static unsigned int label_delay = 0;
3528 static int from_x, from_y, scroll_direction;
3529 static int label_state, label_counter;
3530 unsigned int scroll_delay_value = preview.step_delay;
3531 boolean show_level_border = (BorderElement != EL_EMPTY);
3532 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3533 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3540 if (preview.anim_mode == ANIM_CENTERED)
3542 if (level_xsize > preview.xsize)
3543 from_x = (level_xsize - preview.xsize) / 2;
3544 if (level_ysize > preview.ysize)
3545 from_y = (level_ysize - preview.ysize) / 2;
3548 from_x += preview.xoffset;
3549 from_y += preview.yoffset;
3551 scroll_direction = MV_RIGHT;
3555 DrawPreviewLevelPlayfield(from_x, from_y);
3556 DrawPreviewLevelLabel(label_state);
3558 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3559 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3561 // initialize delay counters
3562 ResetDelayCounter(&scroll_delay);
3563 ResetDelayCounter(&label_delay);
3565 if (leveldir_current->name)
3567 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3568 char label_text[MAX_OUTPUT_LINESIZE + 1];
3569 int font_nr = pos->font;
3570 int max_len_label_text = getMaxTextLength(pos, font_nr);
3572 if (pos->size != -1)
3573 max_len_label_text = pos->size;
3575 strncpy(label_text, leveldir_current->name, max_len_label_text);
3576 label_text[max_len_label_text] = '\0';
3578 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3579 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3585 // scroll preview level, if needed
3586 if (preview.anim_mode != ANIM_NONE &&
3587 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3588 DelayReached(&scroll_delay, scroll_delay_value))
3590 switch (scroll_direction)
3595 from_x -= preview.step_offset;
3596 from_x = (from_x < 0 ? 0 : from_x);
3599 scroll_direction = MV_UP;
3603 if (from_x < level_xsize - preview.xsize)
3605 from_x += preview.step_offset;
3606 from_x = (from_x > level_xsize - preview.xsize ?
3607 level_xsize - preview.xsize : from_x);
3610 scroll_direction = MV_DOWN;
3616 from_y -= preview.step_offset;
3617 from_y = (from_y < 0 ? 0 : from_y);
3620 scroll_direction = MV_RIGHT;
3624 if (from_y < level_ysize - preview.ysize)
3626 from_y += preview.step_offset;
3627 from_y = (from_y > level_ysize - preview.ysize ?
3628 level_ysize - preview.ysize : from_y);
3631 scroll_direction = MV_LEFT;
3638 DrawPreviewLevelPlayfield(from_x, from_y);
3641 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3642 // redraw micro level label, if needed
3643 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3644 !strEqual(level.author, ANONYMOUS_NAME) &&
3645 !strEqual(level.author, leveldir_current->name) &&
3646 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3648 int max_label_counter = 23;
3650 if (leveldir_current->imported_from != NULL &&
3651 strlen(leveldir_current->imported_from) > 0)
3652 max_label_counter += 14;
3653 if (leveldir_current->imported_by != NULL &&
3654 strlen(leveldir_current->imported_by) > 0)
3655 max_label_counter += 14;
3657 label_counter = (label_counter + 1) % max_label_counter;
3658 label_state = (label_counter >= 0 && label_counter <= 7 ?
3659 MICROLABEL_LEVEL_NAME :
3660 label_counter >= 9 && label_counter <= 12 ?
3661 MICROLABEL_LEVEL_AUTHOR_HEAD :
3662 label_counter >= 14 && label_counter <= 21 ?
3663 MICROLABEL_LEVEL_AUTHOR :
3664 label_counter >= 23 && label_counter <= 26 ?
3665 MICROLABEL_IMPORTED_FROM_HEAD :
3666 label_counter >= 28 && label_counter <= 35 ?
3667 MICROLABEL_IMPORTED_FROM :
3668 label_counter >= 37 && label_counter <= 40 ?
3669 MICROLABEL_IMPORTED_BY_HEAD :
3670 label_counter >= 42 && label_counter <= 49 ?
3671 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3673 if (leveldir_current->imported_from == NULL &&
3674 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3675 label_state == MICROLABEL_IMPORTED_FROM))
3676 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3677 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3679 DrawPreviewLevelLabel(label_state);
3683 void DrawPreviewPlayers(void)
3685 if (game_status != GAME_MODE_MAIN)
3688 // do not draw preview players if level preview redefined, but players aren't
3689 if (preview.redefined && !menu.main.preview_players.redefined)
3692 boolean player_found[MAX_PLAYERS];
3693 int num_players = 0;
3696 for (i = 0; i < MAX_PLAYERS; i++)
3697 player_found[i] = FALSE;
3699 // check which players can be found in the level (simple approach)
3700 for (x = 0; x < lev_fieldx; x++)
3702 for (y = 0; y < lev_fieldy; y++)
3704 int element = level.field[x][y];
3706 if (IS_PLAYER_ELEMENT(element))
3708 int player_nr = GET_PLAYER_NR(element);
3710 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3712 if (!player_found[player_nr])
3715 player_found[player_nr] = TRUE;
3720 struct TextPosInfo *pos = &menu.main.preview_players;
3721 int tile_size = pos->tile_size;
3722 int border_size = pos->border_size;
3723 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3724 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3725 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3726 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3727 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3728 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3729 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3730 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3731 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3732 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3733 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3734 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3736 // clear area in which the players will be drawn
3737 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3738 max_players_width, max_players_height);
3740 if (!network.enabled && !setup.team_mode)
3743 // only draw players if level is suited for team mode
3744 if (num_players < 2)
3747 // draw all players that were found in the level
3748 for (i = 0; i < MAX_PLAYERS; i++)
3750 if (player_found[i])
3752 int graphic = el2img(EL_PLAYER_1 + i);
3754 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3756 xpos += player_xoffset;
3757 ypos += player_yoffset;
3762 void DrawPreviewLevelInitial(void)
3764 DrawPreviewLevelExt(TRUE);
3765 DrawPreviewPlayers();
3768 void DrawPreviewLevelAnimation(void)
3770 DrawPreviewLevelExt(FALSE);
3773 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3774 int border_size, int font_nr)
3776 int graphic = el2img(EL_PLAYER_1 + player_nr);
3777 int font_height = getFontHeight(font_nr);
3778 int player_height = MAX(tile_size, font_height);
3779 int xoffset_text = tile_size + border_size;
3780 int yoffset_text = (player_height - font_height) / 2;
3781 int yoffset_graphic = (player_height - tile_size) / 2;
3782 char *player_name = getNetworkPlayerName(player_nr + 1);
3784 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3786 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3789 static void DrawNetworkPlayersExt(boolean force)
3791 if (game_status != GAME_MODE_MAIN)
3794 if (!network.connected && !force)
3797 // do not draw network players if level preview redefined, but players aren't
3798 if (preview.redefined && !menu.main.network_players.redefined)
3801 int num_players = 0;
3804 for (i = 0; i < MAX_PLAYERS; i++)
3805 if (stored_player[i].connected_network)
3808 struct TextPosInfo *pos = &menu.main.network_players;
3809 int tile_size = pos->tile_size;
3810 int border_size = pos->border_size;
3811 int xoffset_text = tile_size + border_size;
3812 int font_nr = pos->font;
3813 int font_width = getFontWidth(font_nr);
3814 int font_height = getFontHeight(font_nr);
3815 int player_height = MAX(tile_size, font_height);
3816 int player_yoffset = player_height + border_size;
3817 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3818 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3819 int all_players_height = num_players * player_yoffset - border_size;
3820 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3821 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3822 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3824 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3825 max_players_width, max_players_height);
3827 // first draw local network player ...
3828 for (i = 0; i < MAX_PLAYERS; i++)
3830 if (stored_player[i].connected_network &&
3831 stored_player[i].connected_locally)
3833 char *player_name = getNetworkPlayerName(i + 1);
3834 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3835 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3837 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3839 ypos += player_yoffset;
3843 // ... then draw all other network players
3844 for (i = 0; i < MAX_PLAYERS; i++)
3846 if (stored_player[i].connected_network &&
3847 !stored_player[i].connected_locally)
3849 char *player_name = getNetworkPlayerName(i + 1);
3850 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3851 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3853 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3855 ypos += player_yoffset;
3860 void DrawNetworkPlayers(void)
3862 DrawNetworkPlayersExt(FALSE);
3865 void ClearNetworkPlayers(void)
3867 DrawNetworkPlayersExt(TRUE);
3870 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3871 int graphic, int lx, int ly,
3874 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3876 if (mask_mode == USE_MASKING)
3877 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3879 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3882 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3883 int graphic, int sync_frame, int mask_mode)
3885 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3887 if (mask_mode == USE_MASKING)
3888 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3890 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3893 static void DrawGraphicAnimation(int x, int y, int graphic)
3895 int lx = LEVELX(x), ly = LEVELY(y);
3896 int mask_mode = NO_MASKING;
3898 if (!IN_SCR_FIELD(x, y))
3901 if (game.use_masked_elements)
3903 if (Tile[lx][ly] != EL_EMPTY)
3905 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3907 mask_mode = USE_MASKING;
3911 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3912 graphic, lx, ly, mask_mode);
3914 MarkTileDirty(x, y);
3917 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3919 int lx = LEVELX(x), ly = LEVELY(y);
3920 int mask_mode = NO_MASKING;
3922 if (!IN_SCR_FIELD(x, y))
3925 if (game.use_masked_elements)
3927 if (Tile[lx][ly] != EL_EMPTY)
3929 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3931 mask_mode = USE_MASKING;
3935 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3936 graphic, lx, ly, mask_mode);
3938 MarkTileDirty(x, y);
3941 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3943 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3946 void DrawLevelElementAnimation(int x, int y, int element)
3948 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3950 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3953 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3955 int sx = SCREENX(x), sy = SCREENY(y);
3957 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3960 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3963 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3966 DrawGraphicAnimation(sx, sy, graphic);
3969 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3970 DrawLevelFieldCrumbled(x, y);
3972 if (GFX_CRUMBLED(Tile[x][y]))
3973 DrawLevelFieldCrumbled(x, y);
3977 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3979 int sx = SCREENX(x), sy = SCREENY(y);
3982 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3985 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3987 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3990 DrawGraphicAnimation(sx, sy, graphic);
3992 if (GFX_CRUMBLED(element))
3993 DrawLevelFieldCrumbled(x, y);
3996 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3998 if (player->use_murphy)
4000 // this works only because currently only one player can be "murphy" ...
4001 static int last_horizontal_dir = MV_LEFT;
4002 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4004 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4005 last_horizontal_dir = move_dir;
4007 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4009 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4011 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4017 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4020 static boolean equalGraphics(int graphic1, int graphic2)
4022 struct GraphicInfo *g1 = &graphic_info[graphic1];
4023 struct GraphicInfo *g2 = &graphic_info[graphic2];
4025 return (g1->bitmap == g2->bitmap &&
4026 g1->src_x == g2->src_x &&
4027 g1->src_y == g2->src_y &&
4028 g1->anim_frames == g2->anim_frames &&
4029 g1->anim_delay == g2->anim_delay &&
4030 g1->anim_mode == g2->anim_mode);
4033 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4037 DRAW_PLAYER_STAGE_INIT = 0,
4038 DRAW_PLAYER_STAGE_LAST_FIELD,
4039 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4040 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4041 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4042 DRAW_PLAYER_STAGE_PLAYER,
4044 DRAW_PLAYER_STAGE_PLAYER,
4045 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4047 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4048 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4050 NUM_DRAW_PLAYER_STAGES
4053 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4055 static int static_last_player_graphic[MAX_PLAYERS];
4056 static int static_last_player_frame[MAX_PLAYERS];
4057 static boolean static_player_is_opaque[MAX_PLAYERS];
4058 static boolean draw_player[MAX_PLAYERS];
4059 int pnr = player->index_nr;
4061 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4063 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4064 static_last_player_frame[pnr] = player->Frame;
4065 static_player_is_opaque[pnr] = FALSE;
4067 draw_player[pnr] = TRUE;
4070 if (!draw_player[pnr])
4074 if (!IN_LEV_FIELD(player->jx, player->jy))
4076 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4077 Debug("draw:DrawPlayerExt", "This should never happen!");
4079 draw_player[pnr] = FALSE;
4085 int last_player_graphic = static_last_player_graphic[pnr];
4086 int last_player_frame = static_last_player_frame[pnr];
4087 boolean player_is_opaque = static_player_is_opaque[pnr];
4089 int jx = player->jx;
4090 int jy = player->jy;
4091 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4092 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4093 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4094 int last_jx = (player->is_moving ? jx - dx : jx);
4095 int last_jy = (player->is_moving ? jy - dy : jy);
4096 int next_jx = jx + dx;
4097 int next_jy = jy + dy;
4098 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4099 int sx = SCREENX(jx);
4100 int sy = SCREENY(jy);
4101 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4102 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4103 int element = Tile[jx][jy];
4104 int last_element = Tile[last_jx][last_jy];
4105 int action = (player->is_pushing ? ACTION_PUSHING :
4106 player->is_digging ? ACTION_DIGGING :
4107 player->is_collecting ? ACTION_COLLECTING :
4108 player->is_moving ? ACTION_MOVING :
4109 player->is_snapping ? ACTION_SNAPPING :
4110 player->is_dropping ? ACTION_DROPPING :
4111 player->is_waiting ? player->action_waiting :
4114 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4116 // ------------------------------------------------------------------------
4117 // initialize drawing the player
4118 // ------------------------------------------------------------------------
4120 draw_player[pnr] = FALSE;
4122 // GfxElement[][] is set to the element the player is digging or collecting;
4123 // remove also for off-screen player if the player is not moving anymore
4124 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4125 GfxElement[jx][jy] = EL_UNDEFINED;
4127 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4130 if (element == EL_EXPLOSION)
4133 InitPlayerGfxAnimation(player, action, move_dir);
4135 draw_player[pnr] = TRUE;
4137 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4139 // ------------------------------------------------------------------------
4140 // draw things in the field the player is leaving, if needed
4141 // ------------------------------------------------------------------------
4143 if (!IN_SCR_FIELD(sx, sy))
4144 draw_player[pnr] = FALSE;
4146 if (!player->is_moving)
4149 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4151 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4153 if (last_element == EL_DYNAMITE_ACTIVE ||
4154 last_element == EL_EM_DYNAMITE_ACTIVE ||
4155 last_element == EL_SP_DISK_RED_ACTIVE)
4156 DrawDynamite(last_jx, last_jy);
4158 DrawLevelFieldThruMask(last_jx, last_jy);
4160 else if (last_element == EL_DYNAMITE_ACTIVE ||
4161 last_element == EL_EM_DYNAMITE_ACTIVE ||
4162 last_element == EL_SP_DISK_RED_ACTIVE)
4163 DrawDynamite(last_jx, last_jy);
4165 DrawLevelField(last_jx, last_jy);
4167 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4168 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4170 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4172 // ------------------------------------------------------------------------
4173 // draw things behind the player, if needed
4174 // ------------------------------------------------------------------------
4178 DrawLevelElement(jx, jy, Back[jx][jy]);
4183 if (IS_ACTIVE_BOMB(element))
4185 DrawLevelElement(jx, jy, EL_EMPTY);
4190 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4192 int old_element = GfxElement[jx][jy];
4193 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4194 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4196 if (GFX_CRUMBLED(old_element))
4197 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4199 DrawScreenGraphic(sx, sy, old_graphic, frame);
4201 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4202 static_player_is_opaque[pnr] = TRUE;
4206 GfxElement[jx][jy] = EL_UNDEFINED;
4208 // make sure that pushed elements are drawn with correct frame rate
4209 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4211 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4212 GfxFrame[jx][jy] = player->StepFrame;
4214 DrawLevelField(jx, jy);
4217 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4219 // ------------------------------------------------------------------------
4220 // draw things the player is pushing, if needed
4221 // ------------------------------------------------------------------------
4223 if (!player->is_pushing || !player->is_moving)
4226 int gfx_frame = GfxFrame[jx][jy];
4228 if (!IS_MOVING(jx, jy)) // push movement already finished
4230 element = Tile[next_jx][next_jy];
4231 gfx_frame = GfxFrame[next_jx][next_jy];
4234 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4235 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4236 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4238 // draw background element under pushed element (like the Sokoban field)
4239 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4241 // this allows transparent pushing animation over non-black background
4244 DrawLevelElement(jx, jy, Back[jx][jy]);
4246 DrawLevelElement(jx, jy, EL_EMPTY);
4248 if (Back[next_jx][next_jy])
4249 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4251 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4253 else if (Back[next_jx][next_jy])
4254 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4256 int px = SCREENX(jx), py = SCREENY(jy);
4257 int pxx = (TILEX - ABS(sxx)) * dx;
4258 int pyy = (TILEY - ABS(syy)) * dy;
4261 // do not draw (EM style) pushing animation when pushing is finished
4262 // (two-tile animations usually do not contain start and end frame)
4263 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4264 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4266 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4268 // masked drawing is needed for EMC style (double) movement graphics
4269 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4270 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4273 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4275 // ------------------------------------------------------------------------
4276 // draw player himself
4277 // ------------------------------------------------------------------------
4279 int graphic = getPlayerGraphic(player, move_dir);
4281 // in the case of changed player action or direction, prevent the current
4282 // animation frame from being restarted for identical animations
4283 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4284 player->Frame = last_player_frame;
4286 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4288 if (player_is_opaque)
4289 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4291 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4293 if (SHIELD_ON(player))
4295 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4296 IMG_SHIELD_NORMAL_ACTIVE);
4297 frame = getGraphicAnimationFrame(graphic, -1);
4299 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4302 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4304 // ------------------------------------------------------------------------
4305 // draw things in front of player (active dynamite or dynabombs)
4306 // ------------------------------------------------------------------------
4308 if (IS_ACTIVE_BOMB(element))
4310 int graphic = el2img(element);
4311 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4313 if (game.emulation == EMU_SUPAPLEX)
4314 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4316 DrawGraphicThruMask(sx, sy, graphic, frame);
4319 if (player_is_moving && last_element == EL_EXPLOSION)
4321 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4322 GfxElement[last_jx][last_jy] : EL_EMPTY);
4323 int graphic = el_act2img(element, ACTION_EXPLODING);
4324 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4325 int phase = ExplodePhase[last_jx][last_jy] - 1;
4326 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4329 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4332 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4334 // ------------------------------------------------------------------------
4335 // draw elements the player is just walking/passing through/under
4336 // ------------------------------------------------------------------------
4338 if (player_is_moving)
4340 // handle the field the player is leaving ...
4341 if (IS_ACCESSIBLE_INSIDE(last_element))
4342 DrawLevelField(last_jx, last_jy);
4343 else if (IS_ACCESSIBLE_UNDER(last_element))
4344 DrawLevelFieldThruMask(last_jx, last_jy);
4347 // do not redraw accessible elements if the player is just pushing them
4348 if (!player_is_moving || !player->is_pushing)
4350 // ... and the field the player is entering
4351 if (IS_ACCESSIBLE_INSIDE(element))
4352 DrawLevelField(jx, jy);
4353 else if (IS_ACCESSIBLE_UNDER(element))
4354 DrawLevelFieldThruMask(jx, jy);
4357 MarkTileDirty(sx, sy);
4361 void DrawPlayer(struct PlayerInfo *player)
4365 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4366 DrawPlayerExt(player, i);
4369 void DrawAllPlayers(void)
4373 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4374 for (j = 0; j < MAX_PLAYERS; j++)
4375 if (stored_player[j].active)
4376 DrawPlayerExt(&stored_player[j], i);
4379 void DrawPlayerField(int x, int y)
4381 if (!IS_PLAYER(x, y))
4384 DrawPlayer(PLAYERINFO(x, y));
4387 // ----------------------------------------------------------------------------
4389 void WaitForEventToContinue(void)
4391 boolean first_wait = TRUE;
4392 boolean still_wait = TRUE;
4394 if (program.headless)
4397 // simulate releasing mouse button over last gadget, if still pressed
4399 HandleGadgets(-1, -1, 0);
4401 button_status = MB_RELEASED;
4404 ClearPlayerAction();
4410 if (NextValidEvent(&event))
4414 case EVENT_BUTTONPRESS:
4415 case EVENT_FINGERPRESS:
4419 case EVENT_BUTTONRELEASE:
4420 case EVENT_FINGERRELEASE:
4421 still_wait = first_wait;
4424 case EVENT_KEYPRESS:
4425 case SDL_CONTROLLERBUTTONDOWN:
4426 case SDL_JOYBUTTONDOWN:
4431 HandleOtherEvents(&event);
4435 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4440 if (!PendingEvent())
4445 #define MAX_REQUEST_LINES 13
4446 #define MAX_REQUEST_LINE_FONT1_LEN 7
4447 #define MAX_REQUEST_LINE_FONT2_LEN 10
4449 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4451 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4453 int draw_buffer_last = GetDrawtoField();
4454 int width = request.width;
4455 int height = request.height;
4459 // when showing request dialog after game ended, deactivate game panel
4460 if (game_just_ended)
4461 game.panel.active = FALSE;
4463 game.request_active = TRUE;
4465 setRequestPosition(&sx, &sy, FALSE);
4467 button_status = MB_RELEASED;
4469 request_gadget_id = -1;
4474 boolean event_handled = FALSE;
4476 if (game_just_ended)
4478 SetDrawtoField(draw_buffer_game);
4480 HandleGameActions();
4482 SetDrawtoField(DRAW_TO_BACKBUFFER);
4484 if (global.use_envelope_request)
4486 // copy current state of request area to middle of playfield area
4487 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4495 while (NextValidEvent(&event))
4497 event_handled = TRUE;
4501 case EVENT_BUTTONPRESS:
4502 case EVENT_BUTTONRELEASE:
4503 case EVENT_MOTIONNOTIFY:
4507 if (event.type == EVENT_MOTIONNOTIFY)
4512 motion_status = TRUE;
4513 mx = ((MotionEvent *) &event)->x;
4514 my = ((MotionEvent *) &event)->y;
4518 motion_status = FALSE;
4519 mx = ((ButtonEvent *) &event)->x;
4520 my = ((ButtonEvent *) &event)->y;
4521 if (event.type == EVENT_BUTTONPRESS)
4522 button_status = ((ButtonEvent *) &event)->button;
4524 button_status = MB_RELEASED;
4527 // this sets 'request_gadget_id'
4528 HandleGadgets(mx, my, button_status);
4530 switch (request_gadget_id)
4532 case TOOL_CTRL_ID_YES:
4533 case TOOL_CTRL_ID_TOUCH_YES:
4536 case TOOL_CTRL_ID_NO:
4537 case TOOL_CTRL_ID_TOUCH_NO:
4540 case TOOL_CTRL_ID_CONFIRM:
4541 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4542 result = TRUE | FALSE;
4545 case TOOL_CTRL_ID_PLAYER_1:
4548 case TOOL_CTRL_ID_PLAYER_2:
4551 case TOOL_CTRL_ID_PLAYER_3:
4554 case TOOL_CTRL_ID_PLAYER_4:
4559 // only check clickable animations if no request gadget clicked
4560 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4567 case SDL_WINDOWEVENT:
4568 HandleWindowEvent((WindowEvent *) &event);
4571 case SDL_APP_WILLENTERBACKGROUND:
4572 case SDL_APP_DIDENTERBACKGROUND:
4573 case SDL_APP_WILLENTERFOREGROUND:
4574 case SDL_APP_DIDENTERFOREGROUND:
4575 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4578 case EVENT_KEYPRESS:
4580 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4585 if (req_state & REQ_CONFIRM)
4594 #if defined(KSYM_Rewind)
4595 case KSYM_Rewind: // for Amazon Fire TV remote
4604 #if defined(KSYM_FastForward)
4605 case KSYM_FastForward: // for Amazon Fire TV remote
4611 HandleKeysDebug(key, KEY_PRESSED);
4615 if (req_state & REQ_PLAYER)
4617 int old_player_nr = setup.network_player_nr;
4620 result = old_player_nr + 1;
4625 result = old_player_nr + 1;
4656 case EVENT_FINGERRELEASE:
4657 case EVENT_KEYRELEASE:
4658 ClearPlayerAction();
4661 case SDL_CONTROLLERBUTTONDOWN:
4662 switch (event.cbutton.button)
4664 case SDL_CONTROLLER_BUTTON_A:
4665 case SDL_CONTROLLER_BUTTON_X:
4666 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4667 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4671 case SDL_CONTROLLER_BUTTON_B:
4672 case SDL_CONTROLLER_BUTTON_Y:
4673 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4674 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4675 case SDL_CONTROLLER_BUTTON_BACK:
4680 if (req_state & REQ_PLAYER)
4682 int old_player_nr = setup.network_player_nr;
4685 result = old_player_nr + 1;
4687 switch (event.cbutton.button)
4689 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4690 case SDL_CONTROLLER_BUTTON_Y:
4694 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4695 case SDL_CONTROLLER_BUTTON_B:
4699 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4700 case SDL_CONTROLLER_BUTTON_A:
4704 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4705 case SDL_CONTROLLER_BUTTON_X:
4716 case SDL_CONTROLLERBUTTONUP:
4717 HandleJoystickEvent(&event);
4718 ClearPlayerAction();
4722 HandleOtherEvents(&event);
4727 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4729 int joy = AnyJoystick();
4731 if (joy & JOY_BUTTON_1)
4733 else if (joy & JOY_BUTTON_2)
4736 else if (AnyJoystick())
4738 int joy = AnyJoystick();
4740 if (req_state & REQ_PLAYER)
4744 else if (joy & JOY_RIGHT)
4746 else if (joy & JOY_DOWN)
4748 else if (joy & JOY_LEFT)
4755 if (game_just_ended)
4757 if (global.use_envelope_request)
4759 // copy back current state of pressed buttons inside request area
4760 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4764 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4770 SetDrawtoField(draw_buffer_last);
4772 game.request_active = FALSE;
4777 static boolean RequestDoor(char *text, unsigned int req_state)
4779 int draw_buffer_last = GetDrawtoField();
4780 unsigned int old_door_state;
4781 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4782 int font_nr = FONT_TEXT_2;
4787 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4789 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4790 font_nr = FONT_TEXT_1;
4793 if (game_status == GAME_MODE_PLAYING)
4794 BlitScreenToBitmap(backbuffer);
4796 // disable deactivated drawing when quick-loading level tape recording
4797 if (tape.playing && tape.deactivate_display)
4798 TapeDeactivateDisplayOff(TRUE);
4800 SetMouseCursor(CURSOR_DEFAULT);
4802 // pause network game while waiting for request to answer
4803 if (network.enabled &&
4804 game_status == GAME_MODE_PLAYING &&
4805 !game.all_players_gone &&
4806 req_state & REQUEST_WAIT_FOR_INPUT)
4807 SendToServer_PausePlaying();
4809 old_door_state = GetDoorState();
4811 // simulate releasing mouse button over last gadget, if still pressed
4813 HandleGadgets(-1, -1, 0);
4817 // draw released gadget before proceeding
4820 if (old_door_state & DOOR_OPEN_1)
4822 CloseDoor(DOOR_CLOSE_1);
4824 // save old door content
4825 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4826 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4829 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4830 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4832 // clear door drawing field
4833 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4835 // force DOOR font inside door area
4836 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4838 // write text for request
4839 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4841 char text_line[max_request_line_len + 1];
4847 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4849 tc = *(text_ptr + tx);
4850 // if (!tc || tc == ' ')
4851 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4855 if ((tc == '?' || tc == '!') && tl == 0)
4865 strncpy(text_line, text_ptr, tl);
4868 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4869 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4870 text_line, font_nr);
4872 text_ptr += tl + (tc == ' ' ? 1 : 0);
4873 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4878 if (req_state & REQ_ASK)
4880 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4881 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4882 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4883 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4885 else if (req_state & REQ_CONFIRM)
4887 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4888 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4890 else if (req_state & REQ_PLAYER)
4892 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4893 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4894 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4895 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4898 // copy request gadgets to door backbuffer
4899 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4901 OpenDoor(DOOR_OPEN_1);
4903 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4905 if (game_status == GAME_MODE_PLAYING)
4907 SetPanelBackground();
4908 SetDrawBackgroundMask(REDRAW_DOOR_1);
4912 SetDrawBackgroundMask(REDRAW_FIELD);
4918 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4920 // ---------- handle request buttons ----------
4921 result = RequestHandleEvents(req_state, draw_buffer_last);
4925 if (!(req_state & REQ_STAY_OPEN))
4927 CloseDoor(DOOR_CLOSE_1);
4929 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4930 (req_state & REQ_REOPEN))
4931 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4936 if (game_status == GAME_MODE_PLAYING)
4938 SetPanelBackground();
4939 SetDrawBackgroundMask(REDRAW_DOOR_1);
4943 SetDrawBackgroundMask(REDRAW_FIELD);
4946 // continue network game after request
4947 if (network.enabled &&
4948 game_status == GAME_MODE_PLAYING &&
4949 !game.all_players_gone &&
4950 req_state & REQUEST_WAIT_FOR_INPUT)
4951 SendToServer_ContinuePlaying();
4953 // restore deactivated drawing when quick-loading level tape recording
4954 if (tape.playing && tape.deactivate_display)
4955 TapeDeactivateDisplayOn();
4960 static boolean RequestEnvelope(char *text, unsigned int req_state)
4962 int draw_buffer_last = GetDrawtoField();
4965 if (game_status == GAME_MODE_PLAYING)
4966 BlitScreenToBitmap(backbuffer);
4968 // disable deactivated drawing when quick-loading level tape recording
4969 if (tape.playing && tape.deactivate_display)
4970 TapeDeactivateDisplayOff(TRUE);
4972 SetMouseCursor(CURSOR_DEFAULT);
4974 // pause network game while waiting for request to answer
4975 if (network.enabled &&
4976 game_status == GAME_MODE_PLAYING &&
4977 !game.all_players_gone &&
4978 req_state & REQUEST_WAIT_FOR_INPUT)
4979 SendToServer_PausePlaying();
4981 // simulate releasing mouse button over last gadget, if still pressed
4983 HandleGadgets(-1, -1, 0);
4987 // (replace with setting corresponding request background)
4988 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4989 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4991 // clear door drawing field
4992 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4994 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4996 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4998 if (game_status == GAME_MODE_PLAYING)
5000 SetPanelBackground();
5001 SetDrawBackgroundMask(REDRAW_DOOR_1);
5005 SetDrawBackgroundMask(REDRAW_FIELD);
5011 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5013 // ---------- handle request buttons ----------
5014 result = RequestHandleEvents(req_state, draw_buffer_last);
5018 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5022 if (game_status == GAME_MODE_PLAYING)
5024 SetPanelBackground();
5025 SetDrawBackgroundMask(REDRAW_DOOR_1);
5029 SetDrawBackgroundMask(REDRAW_FIELD);
5032 // continue network game after request
5033 if (network.enabled &&
5034 game_status == GAME_MODE_PLAYING &&
5035 !game.all_players_gone &&
5036 req_state & REQUEST_WAIT_FOR_INPUT)
5037 SendToServer_ContinuePlaying();
5039 // restore deactivated drawing when quick-loading level tape recording
5040 if (tape.playing && tape.deactivate_display)
5041 TapeDeactivateDisplayOn();
5046 boolean Request(char *text, unsigned int req_state)
5048 boolean overlay_enabled = GetOverlayEnabled();
5051 game.request_active_or_moving = TRUE;
5053 SetOverlayEnabled(FALSE);
5055 if (global.use_envelope_request)
5056 result = RequestEnvelope(text, req_state);
5058 result = RequestDoor(text, req_state);
5060 SetOverlayEnabled(overlay_enabled);
5062 game.request_active_or_moving = FALSE;
5067 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5069 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5070 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5073 if (dpo1->sort_priority != dpo2->sort_priority)
5074 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5076 compare_result = dpo1->nr - dpo2->nr;
5078 return compare_result;
5081 void InitGraphicCompatibilityInfo_Doors(void)
5087 struct DoorInfo *door;
5091 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5092 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5094 { -1, -1, -1, NULL }
5096 struct Rect door_rect_list[] =
5098 { DX, DY, DXSIZE, DYSIZE },
5099 { VX, VY, VXSIZE, VYSIZE }
5103 for (i = 0; doors[i].door_token != -1; i++)
5105 int door_token = doors[i].door_token;
5106 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5107 int part_1 = doors[i].part_1;
5108 int part_8 = doors[i].part_8;
5109 int part_2 = part_1 + 1;
5110 int part_3 = part_1 + 2;
5111 struct DoorInfo *door = doors[i].door;
5112 struct Rect *door_rect = &door_rect_list[door_index];
5113 boolean door_gfx_redefined = FALSE;
5115 // check if any door part graphic definitions have been redefined
5117 for (j = 0; door_part_controls[j].door_token != -1; j++)
5119 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5120 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5122 if (dpc->door_token == door_token && fi->redefined)
5123 door_gfx_redefined = TRUE;
5126 // check for old-style door graphic/animation modifications
5128 if (!door_gfx_redefined)
5130 if (door->anim_mode & ANIM_STATIC_PANEL)
5132 door->panel.step_xoffset = 0;
5133 door->panel.step_yoffset = 0;
5136 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5138 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5139 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5140 int num_door_steps, num_panel_steps;
5142 // remove door part graphics other than the two default wings
5144 for (j = 0; door_part_controls[j].door_token != -1; j++)
5146 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5147 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5149 if (dpc->graphic >= part_3 &&
5150 dpc->graphic <= part_8)
5154 // set graphics and screen positions of the default wings
5156 g_part_1->width = door_rect->width;
5157 g_part_1->height = door_rect->height;
5158 g_part_2->width = door_rect->width;
5159 g_part_2->height = door_rect->height;
5160 g_part_2->src_x = door_rect->width;
5161 g_part_2->src_y = g_part_1->src_y;
5163 door->part_2.x = door->part_1.x;
5164 door->part_2.y = door->part_1.y;
5166 if (door->width != -1)
5168 g_part_1->width = door->width;
5169 g_part_2->width = door->width;
5171 // special treatment for graphics and screen position of right wing
5172 g_part_2->src_x += door_rect->width - door->width;
5173 door->part_2.x += door_rect->width - door->width;
5176 if (door->height != -1)
5178 g_part_1->height = door->height;
5179 g_part_2->height = door->height;
5181 // special treatment for graphics and screen position of bottom wing
5182 g_part_2->src_y += door_rect->height - door->height;
5183 door->part_2.y += door_rect->height - door->height;
5186 // set animation delays for the default wings and panels
5188 door->part_1.step_delay = door->step_delay;
5189 door->part_2.step_delay = door->step_delay;
5190 door->panel.step_delay = door->step_delay;
5192 // set animation draw order for the default wings
5194 door->part_1.sort_priority = 2; // draw left wing over ...
5195 door->part_2.sort_priority = 1; // ... right wing
5197 // set animation draw offset for the default wings
5199 if (door->anim_mode & ANIM_HORIZONTAL)
5201 door->part_1.step_xoffset = door->step_offset;
5202 door->part_1.step_yoffset = 0;
5203 door->part_2.step_xoffset = door->step_offset * -1;
5204 door->part_2.step_yoffset = 0;
5206 num_door_steps = g_part_1->width / door->step_offset;
5208 else // ANIM_VERTICAL
5210 door->part_1.step_xoffset = 0;
5211 door->part_1.step_yoffset = door->step_offset;
5212 door->part_2.step_xoffset = 0;
5213 door->part_2.step_yoffset = door->step_offset * -1;
5215 num_door_steps = g_part_1->height / door->step_offset;
5218 // set animation draw offset for the default panels
5220 if (door->step_offset > 1)
5222 num_panel_steps = 2 * door_rect->height / door->step_offset;
5223 door->panel.start_step = num_panel_steps - num_door_steps;
5224 door->panel.start_step_closing = door->panel.start_step;
5228 num_panel_steps = door_rect->height / door->step_offset;
5229 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5230 door->panel.start_step_closing = door->panel.start_step;
5231 door->panel.step_delay *= 2;
5238 void InitDoors(void)
5242 for (i = 0; door_part_controls[i].door_token != -1; i++)
5244 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5245 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5247 // initialize "start_step_opening" and "start_step_closing", if needed
5248 if (dpc->pos->start_step_opening == 0 &&
5249 dpc->pos->start_step_closing == 0)
5251 // dpc->pos->start_step_opening = dpc->pos->start_step;
5252 dpc->pos->start_step_closing = dpc->pos->start_step;
5255 // fill structure for door part draw order (sorted below)
5257 dpo->sort_priority = dpc->pos->sort_priority;
5260 // sort door part controls according to sort_priority and graphic number
5261 qsort(door_part_order, MAX_DOOR_PARTS,
5262 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5265 unsigned int OpenDoor(unsigned int door_state)
5267 if (door_state & DOOR_COPY_BACK)
5269 if (door_state & DOOR_OPEN_1)
5270 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5271 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5273 if (door_state & DOOR_OPEN_2)
5274 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5275 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5277 door_state &= ~DOOR_COPY_BACK;
5280 return MoveDoor(door_state);
5283 unsigned int CloseDoor(unsigned int door_state)
5285 unsigned int old_door_state = GetDoorState();
5287 if (!(door_state & DOOR_NO_COPY_BACK))
5289 if (old_door_state & DOOR_OPEN_1)
5290 BlitBitmap(backbuffer, bitmap_db_door_1,
5291 DX, DY, DXSIZE, DYSIZE, 0, 0);
5293 if (old_door_state & DOOR_OPEN_2)
5294 BlitBitmap(backbuffer, bitmap_db_door_2,
5295 VX, VY, VXSIZE, VYSIZE, 0, 0);
5297 door_state &= ~DOOR_NO_COPY_BACK;
5300 return MoveDoor(door_state);
5303 unsigned int GetDoorState(void)
5305 return MoveDoor(DOOR_GET_STATE);
5308 unsigned int SetDoorState(unsigned int door_state)
5310 return MoveDoor(door_state | DOOR_SET_STATE);
5313 static int euclid(int a, int b)
5315 return (b ? euclid(b, a % b) : a);
5318 unsigned int MoveDoor(unsigned int door_state)
5320 struct Rect door_rect_list[] =
5322 { DX, DY, DXSIZE, DYSIZE },
5323 { VX, VY, VXSIZE, VYSIZE }
5325 static int door1 = DOOR_CLOSE_1;
5326 static int door2 = DOOR_CLOSE_2;
5327 unsigned int door_delay = 0;
5328 unsigned int door_delay_value;
5331 if (door_state == DOOR_GET_STATE)
5332 return (door1 | door2);
5334 if (door_state & DOOR_SET_STATE)
5336 if (door_state & DOOR_ACTION_1)
5337 door1 = door_state & DOOR_ACTION_1;
5338 if (door_state & DOOR_ACTION_2)
5339 door2 = door_state & DOOR_ACTION_2;
5341 return (door1 | door2);
5344 if (!(door_state & DOOR_FORCE_REDRAW))
5346 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5347 door_state &= ~DOOR_OPEN_1;
5348 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5349 door_state &= ~DOOR_CLOSE_1;
5350 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5351 door_state &= ~DOOR_OPEN_2;
5352 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5353 door_state &= ~DOOR_CLOSE_2;
5356 if (global.autoplay_leveldir)
5358 door_state |= DOOR_NO_DELAY;
5359 door_state &= ~DOOR_CLOSE_ALL;
5362 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5363 door_state |= DOOR_NO_DELAY;
5365 if (door_state & DOOR_ACTION)
5367 boolean door_panel_drawn[NUM_DOORS];
5368 boolean panel_has_doors[NUM_DOORS];
5369 boolean door_part_skip[MAX_DOOR_PARTS];
5370 boolean door_part_done[MAX_DOOR_PARTS];
5371 boolean door_part_done_all;
5372 int num_steps[MAX_DOOR_PARTS];
5373 int max_move_delay = 0; // delay for complete animations of all doors
5374 int max_step_delay = 0; // delay (ms) between two animation frames
5375 int num_move_steps = 0; // number of animation steps for all doors
5376 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5377 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5378 int current_move_delay = 0;
5382 for (i = 0; i < NUM_DOORS; i++)
5383 panel_has_doors[i] = FALSE;
5385 for (i = 0; i < MAX_DOOR_PARTS; i++)
5387 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5388 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5389 int door_token = dpc->door_token;
5391 door_part_done[i] = FALSE;
5392 door_part_skip[i] = (!(door_state & door_token) ||
5396 for (i = 0; i < MAX_DOOR_PARTS; i++)
5398 int nr = door_part_order[i].nr;
5399 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5400 struct DoorPartPosInfo *pos = dpc->pos;
5401 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5402 int door_token = dpc->door_token;
5403 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5404 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5405 int step_xoffset = ABS(pos->step_xoffset);
5406 int step_yoffset = ABS(pos->step_yoffset);
5407 int step_delay = pos->step_delay;
5408 int current_door_state = door_state & door_token;
5409 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5410 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5411 boolean part_opening = (is_panel ? door_closing : door_opening);
5412 int start_step = (part_opening ? pos->start_step_opening :
5413 pos->start_step_closing);
5414 float move_xsize = (step_xoffset ? g->width : 0);
5415 float move_ysize = (step_yoffset ? g->height : 0);
5416 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5417 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5418 int move_steps = (move_xsteps && move_ysteps ?
5419 MIN(move_xsteps, move_ysteps) :
5420 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5421 int move_delay = move_steps * step_delay;
5423 if (door_part_skip[nr])
5426 max_move_delay = MAX(max_move_delay, move_delay);
5427 max_step_delay = (max_step_delay == 0 ? step_delay :
5428 euclid(max_step_delay, step_delay));
5429 num_steps[nr] = move_steps;
5433 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5435 panel_has_doors[door_index] = TRUE;
5439 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5441 num_move_steps = max_move_delay / max_step_delay;
5442 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5444 door_delay_value = max_step_delay;
5446 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5448 start = num_move_steps - 1;
5452 // opening door sound has priority over simultaneously closing door
5453 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5455 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5457 if (door_state & DOOR_OPEN_1)
5458 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5459 if (door_state & DOOR_OPEN_2)
5460 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5462 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5464 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5466 if (door_state & DOOR_CLOSE_1)
5467 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5468 if (door_state & DOOR_CLOSE_2)
5469 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5473 for (k = start; k < num_move_steps; k++)
5475 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5477 door_part_done_all = TRUE;
5479 for (i = 0; i < NUM_DOORS; i++)
5480 door_panel_drawn[i] = FALSE;
5482 for (i = 0; i < MAX_DOOR_PARTS; i++)
5484 int nr = door_part_order[i].nr;
5485 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5486 struct DoorPartPosInfo *pos = dpc->pos;
5487 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5488 int door_token = dpc->door_token;
5489 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5490 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5491 boolean is_panel_and_door_has_closed = FALSE;
5492 struct Rect *door_rect = &door_rect_list[door_index];
5493 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5495 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5496 int current_door_state = door_state & door_token;
5497 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5498 boolean door_closing = !door_opening;
5499 boolean part_opening = (is_panel ? door_closing : door_opening);
5500 boolean part_closing = !part_opening;
5501 int start_step = (part_opening ? pos->start_step_opening :
5502 pos->start_step_closing);
5503 int step_delay = pos->step_delay;
5504 int step_factor = step_delay / max_step_delay;
5505 int k1 = (step_factor ? k / step_factor + 1 : k);
5506 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5507 int kk = MAX(0, k2);
5510 int src_x, src_y, src_xx, src_yy;
5511 int dst_x, dst_y, dst_xx, dst_yy;
5514 if (door_part_skip[nr])
5517 if (!(door_state & door_token))
5525 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5526 int kk_door = MAX(0, k2_door);
5527 int sync_frame = kk_door * door_delay_value;
5528 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5530 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5531 &g_src_x, &g_src_y);
5536 if (!door_panel_drawn[door_index])
5538 ClearRectangle(drawto, door_rect->x, door_rect->y,
5539 door_rect->width, door_rect->height);
5541 door_panel_drawn[door_index] = TRUE;
5544 // draw opening or closing door parts
5546 if (pos->step_xoffset < 0) // door part on right side
5549 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5552 if (dst_xx + width > door_rect->width)
5553 width = door_rect->width - dst_xx;
5555 else // door part on left side
5558 dst_xx = pos->x - kk * pos->step_xoffset;
5562 src_xx = ABS(dst_xx);
5566 width = g->width - src_xx;
5568 if (width > door_rect->width)
5569 width = door_rect->width;
5571 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5574 if (pos->step_yoffset < 0) // door part on bottom side
5577 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5580 if (dst_yy + height > door_rect->height)
5581 height = door_rect->height - dst_yy;
5583 else // door part on top side
5586 dst_yy = pos->y - kk * pos->step_yoffset;
5590 src_yy = ABS(dst_yy);
5594 height = g->height - src_yy;
5597 src_x = g_src_x + src_xx;
5598 src_y = g_src_y + src_yy;
5600 dst_x = door_rect->x + dst_xx;
5601 dst_y = door_rect->y + dst_yy;
5603 is_panel_and_door_has_closed =
5606 panel_has_doors[door_index] &&
5607 k >= num_move_steps_doors_only - 1);
5609 if (width >= 0 && width <= g->width &&
5610 height >= 0 && height <= g->height &&
5611 !is_panel_and_door_has_closed)
5613 if (is_panel || !pos->draw_masked)
5614 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5617 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5621 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5623 if ((part_opening && (width < 0 || height < 0)) ||
5624 (part_closing && (width >= g->width && height >= g->height)))
5625 door_part_done[nr] = TRUE;
5627 // continue door part animations, but not panel after door has closed
5628 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5629 door_part_done_all = FALSE;
5632 if (!(door_state & DOOR_NO_DELAY))
5636 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5638 current_move_delay += max_step_delay;
5640 // prevent OS (Windows) from complaining about program not responding
5644 if (door_part_done_all)
5648 if (!(door_state & DOOR_NO_DELAY))
5650 // wait for specified door action post delay
5651 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5652 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5653 else if (door_state & DOOR_ACTION_1)
5654 door_delay_value = door_1.post_delay;
5655 else if (door_state & DOOR_ACTION_2)
5656 door_delay_value = door_2.post_delay;
5658 while (!DelayReached(&door_delay, door_delay_value))
5663 if (door_state & DOOR_ACTION_1)
5664 door1 = door_state & DOOR_ACTION_1;
5665 if (door_state & DOOR_ACTION_2)
5666 door2 = door_state & DOOR_ACTION_2;
5668 // draw masked border over door area
5669 DrawMaskedBorder(REDRAW_DOOR_1);
5670 DrawMaskedBorder(REDRAW_DOOR_2);
5672 ClearAutoRepeatKeyEvents();
5674 return (door1 | door2);
5677 static boolean useSpecialEditorDoor(void)
5679 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5680 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5682 // do not draw special editor door if editor border defined or redefined
5683 if (graphic_info[graphic].bitmap != NULL || redefined)
5686 // do not draw special editor door if global border defined to be empty
5687 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5690 // do not draw special editor door if viewport definitions do not match
5694 EY + EYSIZE != VY + VYSIZE)
5700 void DrawSpecialEditorDoor(void)
5702 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5703 int top_border_width = gfx1->width;
5704 int top_border_height = gfx1->height;
5705 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5706 int ex = EX - outer_border;
5707 int ey = EY - outer_border;
5708 int vy = VY - outer_border;
5709 int exsize = EXSIZE + 2 * outer_border;
5711 if (!useSpecialEditorDoor())
5714 // draw bigger level editor toolbox window
5715 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5716 top_border_width, top_border_height, ex, ey - top_border_height);
5717 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5718 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5720 redraw_mask |= REDRAW_ALL;
5723 void UndrawSpecialEditorDoor(void)
5725 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5726 int top_border_width = gfx1->width;
5727 int top_border_height = gfx1->height;
5728 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5729 int ex = EX - outer_border;
5730 int ey = EY - outer_border;
5731 int ey_top = ey - top_border_height;
5732 int exsize = EXSIZE + 2 * outer_border;
5733 int eysize = EYSIZE + 2 * outer_border;
5735 if (!useSpecialEditorDoor())
5738 // draw normal tape recorder window
5739 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5741 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5742 ex, ey_top, top_border_width, top_border_height,
5744 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5745 ex, ey, exsize, eysize, ex, ey);
5749 // if screen background is set to "[NONE]", clear editor toolbox window
5750 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5751 ClearRectangle(drawto, ex, ey, exsize, eysize);
5754 redraw_mask |= REDRAW_ALL;
5758 // ---------- new tool button stuff -------------------------------------------
5763 struct TextPosInfo *pos;
5765 boolean is_touch_button;
5767 } toolbutton_info[NUM_TOOL_BUTTONS] =
5770 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5771 TOOL_CTRL_ID_YES, FALSE, "yes"
5774 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5775 TOOL_CTRL_ID_NO, FALSE, "no"
5778 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5779 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5782 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5783 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5786 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5787 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5790 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5791 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5794 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5795 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5798 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5799 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5802 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5803 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5806 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5807 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5811 void CreateToolButtons(void)
5815 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5817 int graphic = toolbutton_info[i].graphic;
5818 struct GraphicInfo *gfx = &graphic_info[graphic];
5819 struct TextPosInfo *pos = toolbutton_info[i].pos;
5820 struct GadgetInfo *gi;
5821 Bitmap *deco_bitmap = None;
5822 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5823 unsigned int event_mask = GD_EVENT_RELEASED;
5824 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5825 int base_x = (is_touch_button ? 0 : DX);
5826 int base_y = (is_touch_button ? 0 : DY);
5827 int gd_x = gfx->src_x;
5828 int gd_y = gfx->src_y;
5829 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5830 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5835 if (global.use_envelope_request && !is_touch_button)
5837 setRequestPosition(&base_x, &base_y, TRUE);
5839 // check if request buttons are outside of envelope and fix, if needed
5840 if (x < 0 || x + gfx->width > request.width ||
5841 y < 0 || y + gfx->height > request.height)
5843 if (id == TOOL_CTRL_ID_YES)
5846 y = request.height - 2 * request.border_size - gfx->height;
5848 else if (id == TOOL_CTRL_ID_NO)
5850 x = request.width - 2 * request.border_size - gfx->width;
5851 y = request.height - 2 * request.border_size - gfx->height;
5853 else if (id == TOOL_CTRL_ID_CONFIRM)
5855 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5856 y = request.height - 2 * request.border_size - gfx->height;
5858 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5860 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5862 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5863 y = request.height - 2 * request.border_size - gfx->height * 2;
5865 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5866 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5871 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5873 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5875 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5876 pos->size, &deco_bitmap, &deco_x, &deco_y);
5877 deco_xpos = (gfx->width - pos->size) / 2;
5878 deco_ypos = (gfx->height - pos->size) / 2;
5881 gi = CreateGadget(GDI_CUSTOM_ID, id,
5882 GDI_IMAGE_ID, graphic,
5883 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5886 GDI_WIDTH, gfx->width,
5887 GDI_HEIGHT, gfx->height,
5888 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5889 GDI_STATE, GD_BUTTON_UNPRESSED,
5890 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5891 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5892 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5893 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5894 GDI_DECORATION_SIZE, pos->size, pos->size,
5895 GDI_DECORATION_SHIFTING, 1, 1,
5896 GDI_DIRECT_DRAW, FALSE,
5897 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5898 GDI_EVENT_MASK, event_mask,
5899 GDI_CALLBACK_ACTION, HandleToolButtons,
5903 Fail("cannot create gadget");
5905 tool_gadget[id] = gi;
5909 void FreeToolButtons(void)
5913 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5914 FreeGadget(tool_gadget[i]);
5917 static void UnmapToolButtons(void)
5921 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5922 UnmapGadget(tool_gadget[i]);
5925 static void HandleToolButtons(struct GadgetInfo *gi)
5927 request_gadget_id = gi->custom_id;
5930 static struct Mapping_EM_to_RND_object
5933 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5934 boolean is_backside; // backside of moving element
5940 em_object_mapping_list[GAME_TILE_MAX + 1] =
5943 Zborder, FALSE, FALSE,
5947 Zplayer, FALSE, FALSE,
5956 Ztank, FALSE, FALSE,
5960 Zeater, FALSE, FALSE,
5964 Zdynamite, FALSE, FALSE,
5968 Zboom, FALSE, FALSE,
5973 Xchain, FALSE, FALSE,
5974 EL_DEFAULT, ACTION_EXPLODING, -1
5977 Xboom_bug, FALSE, FALSE,
5978 EL_BUG, ACTION_EXPLODING, -1
5981 Xboom_tank, FALSE, FALSE,
5982 EL_SPACESHIP, ACTION_EXPLODING, -1
5985 Xboom_android, FALSE, FALSE,
5986 EL_EMC_ANDROID, ACTION_OTHER, -1
5989 Xboom_1, FALSE, FALSE,
5990 EL_DEFAULT, ACTION_EXPLODING, -1
5993 Xboom_2, FALSE, FALSE,
5994 EL_DEFAULT, ACTION_EXPLODING, -1
5998 Xblank, TRUE, FALSE,
6003 Xsplash_e, FALSE, FALSE,
6004 EL_ACID_SPLASH_RIGHT, -1, -1
6007 Xsplash_w, FALSE, FALSE,
6008 EL_ACID_SPLASH_LEFT, -1, -1
6012 Xplant, TRUE, FALSE,
6013 EL_EMC_PLANT, -1, -1
6016 Yplant, FALSE, FALSE,
6017 EL_EMC_PLANT, -1, -1
6021 Xacid_1, TRUE, FALSE,
6025 Xacid_2, FALSE, FALSE,
6029 Xacid_3, FALSE, FALSE,
6033 Xacid_4, FALSE, FALSE,
6037 Xacid_5, FALSE, FALSE,
6041 Xacid_6, FALSE, FALSE,
6045 Xacid_7, FALSE, FALSE,
6049 Xacid_8, FALSE, FALSE,
6054 Xfake_acid_1, TRUE, FALSE,
6055 EL_EMC_FAKE_ACID, -1, -1
6058 Xfake_acid_2, FALSE, FALSE,
6059 EL_EMC_FAKE_ACID, -1, -1
6062 Xfake_acid_3, FALSE, FALSE,
6063 EL_EMC_FAKE_ACID, -1, -1
6066 Xfake_acid_4, FALSE, FALSE,
6067 EL_EMC_FAKE_ACID, -1, -1
6070 Xfake_acid_5, FALSE, FALSE,
6071 EL_EMC_FAKE_ACID, -1, -1
6074 Xfake_acid_6, FALSE, FALSE,
6075 EL_EMC_FAKE_ACID, -1, -1
6078 Xfake_acid_7, FALSE, FALSE,
6079 EL_EMC_FAKE_ACID, -1, -1
6082 Xfake_acid_8, FALSE, FALSE,
6083 EL_EMC_FAKE_ACID, -1, -1
6087 Xfake_acid_1_player, FALSE, FALSE,
6088 EL_EMC_FAKE_ACID, -1, -1
6091 Xfake_acid_2_player, FALSE, FALSE,
6092 EL_EMC_FAKE_ACID, -1, -1
6095 Xfake_acid_3_player, FALSE, FALSE,
6096 EL_EMC_FAKE_ACID, -1, -1
6099 Xfake_acid_4_player, FALSE, FALSE,
6100 EL_EMC_FAKE_ACID, -1, -1
6103 Xfake_acid_5_player, FALSE, FALSE,
6104 EL_EMC_FAKE_ACID, -1, -1
6107 Xfake_acid_6_player, FALSE, FALSE,
6108 EL_EMC_FAKE_ACID, -1, -1
6111 Xfake_acid_7_player, FALSE, FALSE,
6112 EL_EMC_FAKE_ACID, -1, -1
6115 Xfake_acid_8_player, FALSE, FALSE,
6116 EL_EMC_FAKE_ACID, -1, -1
6120 Xgrass, TRUE, FALSE,
6121 EL_EMC_GRASS, -1, -1
6124 Ygrass_nB, FALSE, FALSE,
6125 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6128 Ygrass_eB, FALSE, FALSE,
6129 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6132 Ygrass_sB, FALSE, FALSE,
6133 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6136 Ygrass_wB, FALSE, FALSE,
6137 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6145 Ydirt_nB, FALSE, FALSE,
6146 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6149 Ydirt_eB, FALSE, FALSE,
6150 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6153 Ydirt_sB, FALSE, FALSE,
6154 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6157 Ydirt_wB, FALSE, FALSE,
6158 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6162 Xandroid, TRUE, FALSE,
6163 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6166 Xandroid_1_n, FALSE, FALSE,
6167 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6170 Xandroid_2_n, FALSE, FALSE,
6171 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6174 Xandroid_1_e, FALSE, FALSE,
6175 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6178 Xandroid_2_e, FALSE, FALSE,
6179 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6182 Xandroid_1_w, FALSE, FALSE,
6183 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6186 Xandroid_2_w, FALSE, FALSE,
6187 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6190 Xandroid_1_s, FALSE, FALSE,
6191 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6194 Xandroid_2_s, FALSE, FALSE,
6195 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6198 Yandroid_n, FALSE, FALSE,
6199 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6202 Yandroid_nB, FALSE, TRUE,
6203 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6206 Yandroid_ne, FALSE, FALSE,
6207 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6210 Yandroid_neB, FALSE, TRUE,
6211 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6214 Yandroid_e, FALSE, FALSE,
6215 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6218 Yandroid_eB, FALSE, TRUE,
6219 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6222 Yandroid_se, FALSE, FALSE,
6223 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6226 Yandroid_seB, FALSE, TRUE,
6227 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6230 Yandroid_s, FALSE, FALSE,
6231 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6234 Yandroid_sB, FALSE, TRUE,
6235 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6238 Yandroid_sw, FALSE, FALSE,
6239 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6242 Yandroid_swB, FALSE, TRUE,
6243 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6246 Yandroid_w, FALSE, FALSE,
6247 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6250 Yandroid_wB, FALSE, TRUE,
6251 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6254 Yandroid_nw, FALSE, FALSE,
6255 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6258 Yandroid_nwB, FALSE, TRUE,
6259 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6263 Xeater_n, TRUE, FALSE,
6264 EL_YAMYAM_UP, -1, -1
6267 Xeater_e, TRUE, FALSE,
6268 EL_YAMYAM_RIGHT, -1, -1
6271 Xeater_w, TRUE, FALSE,
6272 EL_YAMYAM_LEFT, -1, -1
6275 Xeater_s, TRUE, FALSE,
6276 EL_YAMYAM_DOWN, -1, -1
6279 Yeater_n, FALSE, FALSE,
6280 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6283 Yeater_nB, FALSE, TRUE,
6284 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6287 Yeater_e, FALSE, FALSE,
6288 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6291 Yeater_eB, FALSE, TRUE,
6292 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6295 Yeater_s, FALSE, FALSE,
6296 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6299 Yeater_sB, FALSE, TRUE,
6300 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6303 Yeater_w, FALSE, FALSE,
6304 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6307 Yeater_wB, FALSE, TRUE,
6308 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6311 Yeater_stone, FALSE, FALSE,
6312 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6315 Yeater_spring, FALSE, FALSE,
6316 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6320 Xalien, TRUE, FALSE,
6324 Xalien_pause, FALSE, FALSE,
6328 Yalien_n, FALSE, FALSE,
6329 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6332 Yalien_nB, FALSE, TRUE,
6333 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6336 Yalien_e, FALSE, FALSE,
6337 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6340 Yalien_eB, FALSE, TRUE,
6341 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6344 Yalien_s, FALSE, FALSE,
6345 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6348 Yalien_sB, FALSE, TRUE,
6349 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6352 Yalien_w, FALSE, FALSE,
6353 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6356 Yalien_wB, FALSE, TRUE,
6357 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6360 Yalien_stone, FALSE, FALSE,
6361 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6364 Yalien_spring, FALSE, FALSE,
6365 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6369 Xbug_1_n, TRUE, FALSE,
6373 Xbug_1_e, TRUE, FALSE,
6374 EL_BUG_RIGHT, -1, -1
6377 Xbug_1_s, TRUE, FALSE,
6381 Xbug_1_w, TRUE, FALSE,
6385 Xbug_2_n, FALSE, FALSE,
6389 Xbug_2_e, FALSE, FALSE,
6390 EL_BUG_RIGHT, -1, -1
6393 Xbug_2_s, FALSE, FALSE,
6397 Xbug_2_w, FALSE, FALSE,
6401 Ybug_n, FALSE, FALSE,
6402 EL_BUG, ACTION_MOVING, MV_BIT_UP
6405 Ybug_nB, FALSE, TRUE,
6406 EL_BUG, ACTION_MOVING, MV_BIT_UP
6409 Ybug_e, FALSE, FALSE,
6410 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6413 Ybug_eB, FALSE, TRUE,
6414 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6417 Ybug_s, FALSE, FALSE,
6418 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6421 Ybug_sB, FALSE, TRUE,
6422 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6425 Ybug_w, FALSE, FALSE,
6426 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6429 Ybug_wB, FALSE, TRUE,
6430 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6433 Ybug_w_n, FALSE, FALSE,
6434 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6437 Ybug_n_e, FALSE, FALSE,
6438 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6441 Ybug_e_s, FALSE, FALSE,
6442 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6445 Ybug_s_w, FALSE, FALSE,
6446 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6449 Ybug_e_n, FALSE, FALSE,
6450 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6453 Ybug_s_e, FALSE, FALSE,
6454 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6457 Ybug_w_s, FALSE, FALSE,
6458 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6461 Ybug_n_w, FALSE, FALSE,
6462 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6465 Ybug_stone, FALSE, FALSE,
6466 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6469 Ybug_spring, FALSE, FALSE,
6470 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6474 Xtank_1_n, TRUE, FALSE,
6475 EL_SPACESHIP_UP, -1, -1
6478 Xtank_1_e, TRUE, FALSE,
6479 EL_SPACESHIP_RIGHT, -1, -1
6482 Xtank_1_s, TRUE, FALSE,
6483 EL_SPACESHIP_DOWN, -1, -1
6486 Xtank_1_w, TRUE, FALSE,
6487 EL_SPACESHIP_LEFT, -1, -1
6490 Xtank_2_n, FALSE, FALSE,
6491 EL_SPACESHIP_UP, -1, -1
6494 Xtank_2_e, FALSE, FALSE,
6495 EL_SPACESHIP_RIGHT, -1, -1
6498 Xtank_2_s, FALSE, FALSE,
6499 EL_SPACESHIP_DOWN, -1, -1
6502 Xtank_2_w, FALSE, FALSE,
6503 EL_SPACESHIP_LEFT, -1, -1
6506 Ytank_n, FALSE, FALSE,
6507 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6510 Ytank_nB, FALSE, TRUE,
6511 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6514 Ytank_e, FALSE, FALSE,
6515 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6518 Ytank_eB, FALSE, TRUE,
6519 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6522 Ytank_s, FALSE, FALSE,
6523 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6526 Ytank_sB, FALSE, TRUE,
6527 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6530 Ytank_w, FALSE, FALSE,
6531 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6534 Ytank_wB, FALSE, TRUE,
6535 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6538 Ytank_w_n, FALSE, FALSE,
6539 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6542 Ytank_n_e, FALSE, FALSE,
6543 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6546 Ytank_e_s, FALSE, FALSE,
6547 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6550 Ytank_s_w, FALSE, FALSE,
6551 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6554 Ytank_e_n, FALSE, FALSE,
6555 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6558 Ytank_s_e, FALSE, FALSE,
6559 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6562 Ytank_w_s, FALSE, FALSE,
6563 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6566 Ytank_n_w, FALSE, FALSE,
6567 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6570 Ytank_stone, FALSE, FALSE,
6571 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6574 Ytank_spring, FALSE, FALSE,
6575 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6579 Xemerald, TRUE, FALSE,
6583 Xemerald_pause, FALSE, FALSE,
6587 Xemerald_fall, FALSE, FALSE,
6591 Xemerald_shine, FALSE, FALSE,
6592 EL_EMERALD, ACTION_TWINKLING, -1
6595 Yemerald_s, FALSE, FALSE,
6596 EL_EMERALD, ACTION_FALLING, -1
6599 Yemerald_sB, FALSE, TRUE,
6600 EL_EMERALD, ACTION_FALLING, -1
6603 Yemerald_e, FALSE, FALSE,
6604 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6607 Yemerald_eB, FALSE, TRUE,
6608 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6611 Yemerald_w, FALSE, FALSE,
6612 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6615 Yemerald_wB, FALSE, TRUE,
6616 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6619 Yemerald_blank, FALSE, FALSE,
6620 EL_EMERALD, ACTION_COLLECTING, -1
6624 Xdiamond, TRUE, FALSE,
6628 Xdiamond_pause, FALSE, FALSE,
6632 Xdiamond_fall, FALSE, FALSE,
6636 Xdiamond_shine, FALSE, FALSE,
6637 EL_DIAMOND, ACTION_TWINKLING, -1
6640 Ydiamond_s, FALSE, FALSE,
6641 EL_DIAMOND, ACTION_FALLING, -1
6644 Ydiamond_sB, FALSE, TRUE,
6645 EL_DIAMOND, ACTION_FALLING, -1
6648 Ydiamond_e, FALSE, FALSE,
6649 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6652 Ydiamond_eB, FALSE, TRUE,
6653 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6656 Ydiamond_w, FALSE, FALSE,
6657 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6660 Ydiamond_wB, FALSE, TRUE,
6661 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6664 Ydiamond_blank, FALSE, FALSE,
6665 EL_DIAMOND, ACTION_COLLECTING, -1
6668 Ydiamond_stone, FALSE, FALSE,
6669 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6673 Xstone, TRUE, FALSE,
6677 Xstone_pause, FALSE, FALSE,
6681 Xstone_fall, FALSE, FALSE,
6685 Ystone_s, FALSE, FALSE,
6686 EL_ROCK, ACTION_FALLING, -1
6689 Ystone_sB, FALSE, TRUE,
6690 EL_ROCK, ACTION_FALLING, -1
6693 Ystone_e, FALSE, FALSE,
6694 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6697 Ystone_eB, FALSE, TRUE,
6698 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6701 Ystone_w, FALSE, FALSE,
6702 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6705 Ystone_wB, FALSE, TRUE,
6706 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6714 Xbomb_pause, FALSE, FALSE,
6718 Xbomb_fall, FALSE, FALSE,
6722 Ybomb_s, FALSE, FALSE,
6723 EL_BOMB, ACTION_FALLING, -1
6726 Ybomb_sB, FALSE, TRUE,
6727 EL_BOMB, ACTION_FALLING, -1
6730 Ybomb_e, FALSE, FALSE,
6731 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6734 Ybomb_eB, FALSE, TRUE,
6735 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6738 Ybomb_w, FALSE, FALSE,
6739 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6742 Ybomb_wB, FALSE, TRUE,
6743 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6746 Ybomb_blank, FALSE, FALSE,
6747 EL_BOMB, ACTION_ACTIVATING, -1
6755 Xnut_pause, FALSE, FALSE,
6759 Xnut_fall, FALSE, FALSE,
6763 Ynut_s, FALSE, FALSE,
6764 EL_NUT, ACTION_FALLING, -1
6767 Ynut_sB, FALSE, TRUE,
6768 EL_NUT, ACTION_FALLING, -1
6771 Ynut_e, FALSE, FALSE,
6772 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6775 Ynut_eB, FALSE, TRUE,
6776 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6779 Ynut_w, FALSE, FALSE,
6780 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6783 Ynut_wB, FALSE, TRUE,
6784 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6787 Ynut_stone, FALSE, FALSE,
6788 EL_NUT, ACTION_BREAKING, -1
6792 Xspring, TRUE, FALSE,
6796 Xspring_pause, FALSE, FALSE,
6800 Xspring_e, TRUE, FALSE,
6801 EL_SPRING_RIGHT, -1, -1
6804 Xspring_w, TRUE, FALSE,
6805 EL_SPRING_LEFT, -1, -1
6808 Xspring_fall, FALSE, FALSE,
6812 Yspring_s, FALSE, FALSE,
6813 EL_SPRING, ACTION_FALLING, -1
6816 Yspring_sB, FALSE, TRUE,
6817 EL_SPRING, ACTION_FALLING, -1
6820 Yspring_e, FALSE, FALSE,
6821 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6824 Yspring_eB, FALSE, TRUE,
6825 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6828 Yspring_w, FALSE, FALSE,
6829 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6832 Yspring_wB, FALSE, TRUE,
6833 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6836 Yspring_alien_e, FALSE, FALSE,
6837 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6840 Yspring_alien_eB, FALSE, TRUE,
6841 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6844 Yspring_alien_w, FALSE, FALSE,
6845 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6848 Yspring_alien_wB, FALSE, TRUE,
6849 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6853 Xpush_emerald_e, FALSE, FALSE,
6854 EL_EMERALD, -1, MV_BIT_RIGHT
6857 Xpush_emerald_w, FALSE, FALSE,
6858 EL_EMERALD, -1, MV_BIT_LEFT
6861 Xpush_diamond_e, FALSE, FALSE,
6862 EL_DIAMOND, -1, MV_BIT_RIGHT
6865 Xpush_diamond_w, FALSE, FALSE,
6866 EL_DIAMOND, -1, MV_BIT_LEFT
6869 Xpush_stone_e, FALSE, FALSE,
6870 EL_ROCK, -1, MV_BIT_RIGHT
6873 Xpush_stone_w, FALSE, FALSE,
6874 EL_ROCK, -1, MV_BIT_LEFT
6877 Xpush_bomb_e, FALSE, FALSE,
6878 EL_BOMB, -1, MV_BIT_RIGHT
6881 Xpush_bomb_w, FALSE, FALSE,
6882 EL_BOMB, -1, MV_BIT_LEFT
6885 Xpush_nut_e, FALSE, FALSE,
6886 EL_NUT, -1, MV_BIT_RIGHT
6889 Xpush_nut_w, FALSE, FALSE,
6890 EL_NUT, -1, MV_BIT_LEFT
6893 Xpush_spring_e, FALSE, FALSE,
6894 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6897 Xpush_spring_w, FALSE, FALSE,
6898 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6902 Xdynamite, TRUE, FALSE,
6903 EL_EM_DYNAMITE, -1, -1
6906 Ydynamite_blank, FALSE, FALSE,
6907 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6910 Xdynamite_1, TRUE, FALSE,
6911 EL_EM_DYNAMITE_ACTIVE, -1, -1
6914 Xdynamite_2, FALSE, FALSE,
6915 EL_EM_DYNAMITE_ACTIVE, -1, -1
6918 Xdynamite_3, FALSE, FALSE,
6919 EL_EM_DYNAMITE_ACTIVE, -1, -1
6922 Xdynamite_4, FALSE, FALSE,
6923 EL_EM_DYNAMITE_ACTIVE, -1, -1
6927 Xkey_1, TRUE, FALSE,
6931 Xkey_2, TRUE, FALSE,
6935 Xkey_3, TRUE, FALSE,
6939 Xkey_4, TRUE, FALSE,
6943 Xkey_5, TRUE, FALSE,
6944 EL_EMC_KEY_5, -1, -1
6947 Xkey_6, TRUE, FALSE,
6948 EL_EMC_KEY_6, -1, -1
6951 Xkey_7, TRUE, FALSE,
6952 EL_EMC_KEY_7, -1, -1
6955 Xkey_8, TRUE, FALSE,
6956 EL_EMC_KEY_8, -1, -1
6960 Xdoor_1, TRUE, FALSE,
6961 EL_EM_GATE_1, -1, -1
6964 Xdoor_2, TRUE, FALSE,
6965 EL_EM_GATE_2, -1, -1
6968 Xdoor_3, TRUE, FALSE,
6969 EL_EM_GATE_3, -1, -1
6972 Xdoor_4, TRUE, FALSE,
6973 EL_EM_GATE_4, -1, -1
6976 Xdoor_5, TRUE, FALSE,
6977 EL_EMC_GATE_5, -1, -1
6980 Xdoor_6, TRUE, FALSE,
6981 EL_EMC_GATE_6, -1, -1
6984 Xdoor_7, TRUE, FALSE,
6985 EL_EMC_GATE_7, -1, -1
6988 Xdoor_8, TRUE, FALSE,
6989 EL_EMC_GATE_8, -1, -1
6993 Xfake_door_1, TRUE, FALSE,
6994 EL_EM_GATE_1_GRAY, -1, -1
6997 Xfake_door_2, TRUE, FALSE,
6998 EL_EM_GATE_2_GRAY, -1, -1
7001 Xfake_door_3, TRUE, FALSE,
7002 EL_EM_GATE_3_GRAY, -1, -1
7005 Xfake_door_4, TRUE, FALSE,
7006 EL_EM_GATE_4_GRAY, -1, -1
7009 Xfake_door_5, TRUE, FALSE,
7010 EL_EMC_GATE_5_GRAY, -1, -1
7013 Xfake_door_6, TRUE, FALSE,
7014 EL_EMC_GATE_6_GRAY, -1, -1
7017 Xfake_door_7, TRUE, FALSE,
7018 EL_EMC_GATE_7_GRAY, -1, -1
7021 Xfake_door_8, TRUE, FALSE,
7022 EL_EMC_GATE_8_GRAY, -1, -1
7026 Xballoon, TRUE, FALSE,
7030 Yballoon_n, FALSE, FALSE,
7031 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7034 Yballoon_nB, FALSE, TRUE,
7035 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7038 Yballoon_e, FALSE, FALSE,
7039 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7042 Yballoon_eB, FALSE, TRUE,
7043 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7046 Yballoon_s, FALSE, FALSE,
7047 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7050 Yballoon_sB, FALSE, TRUE,
7051 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7054 Yballoon_w, FALSE, FALSE,
7055 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7058 Yballoon_wB, FALSE, TRUE,
7059 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7063 Xball_1, TRUE, FALSE,
7064 EL_EMC_MAGIC_BALL, -1, -1
7067 Yball_1, FALSE, FALSE,
7068 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7071 Xball_2, FALSE, FALSE,
7072 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7075 Yball_2, FALSE, FALSE,
7076 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7079 Yball_blank, FALSE, FALSE,
7080 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7084 Xamoeba_1, TRUE, FALSE,
7085 EL_AMOEBA_DRY, ACTION_OTHER, -1
7088 Xamoeba_2, FALSE, FALSE,
7089 EL_AMOEBA_DRY, ACTION_OTHER, -1
7092 Xamoeba_3, FALSE, FALSE,
7093 EL_AMOEBA_DRY, ACTION_OTHER, -1
7096 Xamoeba_4, FALSE, FALSE,
7097 EL_AMOEBA_DRY, ACTION_OTHER, -1
7100 Xamoeba_5, TRUE, FALSE,
7101 EL_AMOEBA_WET, ACTION_OTHER, -1
7104 Xamoeba_6, FALSE, FALSE,
7105 EL_AMOEBA_WET, ACTION_OTHER, -1
7108 Xamoeba_7, FALSE, FALSE,
7109 EL_AMOEBA_WET, ACTION_OTHER, -1
7112 Xamoeba_8, FALSE, FALSE,
7113 EL_AMOEBA_WET, ACTION_OTHER, -1
7118 EL_AMOEBA_DROP, ACTION_GROWING, -1
7121 Xdrip_fall, FALSE, FALSE,
7122 EL_AMOEBA_DROP, -1, -1
7125 Xdrip_stretch, FALSE, FALSE,
7126 EL_AMOEBA_DROP, ACTION_FALLING, -1
7129 Xdrip_stretchB, FALSE, TRUE,
7130 EL_AMOEBA_DROP, ACTION_FALLING, -1
7133 Ydrip_1_s, FALSE, FALSE,
7134 EL_AMOEBA_DROP, ACTION_FALLING, -1
7137 Ydrip_1_sB, FALSE, TRUE,
7138 EL_AMOEBA_DROP, ACTION_FALLING, -1
7141 Ydrip_2_s, FALSE, FALSE,
7142 EL_AMOEBA_DROP, ACTION_FALLING, -1
7145 Ydrip_2_sB, FALSE, TRUE,
7146 EL_AMOEBA_DROP, ACTION_FALLING, -1
7150 Xwonderwall, TRUE, FALSE,
7151 EL_MAGIC_WALL, -1, -1
7154 Ywonderwall, FALSE, FALSE,
7155 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7159 Xwheel, TRUE, FALSE,
7160 EL_ROBOT_WHEEL, -1, -1
7163 Ywheel, FALSE, FALSE,
7164 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7168 Xswitch, TRUE, FALSE,
7169 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7172 Yswitch, FALSE, FALSE,
7173 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7177 Xbumper, TRUE, FALSE,
7178 EL_EMC_SPRING_BUMPER, -1, -1
7181 Ybumper, FALSE, FALSE,
7182 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7186 Xacid_nw, TRUE, FALSE,
7187 EL_ACID_POOL_TOPLEFT, -1, -1
7190 Xacid_ne, TRUE, FALSE,
7191 EL_ACID_POOL_TOPRIGHT, -1, -1
7194 Xacid_sw, TRUE, FALSE,
7195 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7198 Xacid_s, TRUE, FALSE,
7199 EL_ACID_POOL_BOTTOM, -1, -1
7202 Xacid_se, TRUE, FALSE,
7203 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7207 Xfake_blank, TRUE, FALSE,
7208 EL_INVISIBLE_WALL, -1, -1
7211 Yfake_blank, FALSE, FALSE,
7212 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7216 Xfake_grass, TRUE, FALSE,
7217 EL_EMC_FAKE_GRASS, -1, -1
7220 Yfake_grass, FALSE, FALSE,
7221 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7225 Xfake_amoeba, TRUE, FALSE,
7226 EL_EMC_DRIPPER, -1, -1
7229 Yfake_amoeba, FALSE, FALSE,
7230 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7234 Xlenses, TRUE, FALSE,
7235 EL_EMC_LENSES, -1, -1
7239 Xmagnify, TRUE, FALSE,
7240 EL_EMC_MAGNIFIER, -1, -1
7245 EL_QUICKSAND_EMPTY, -1, -1
7248 Xsand_stone, TRUE, FALSE,
7249 EL_QUICKSAND_FULL, -1, -1
7252 Xsand_stonein_1, FALSE, TRUE,
7253 EL_ROCK, ACTION_FILLING, -1
7256 Xsand_stonein_2, FALSE, TRUE,
7257 EL_ROCK, ACTION_FILLING, -1
7260 Xsand_stonein_3, FALSE, TRUE,
7261 EL_ROCK, ACTION_FILLING, -1
7264 Xsand_stonein_4, FALSE, TRUE,
7265 EL_ROCK, ACTION_FILLING, -1
7268 Xsand_sandstone_1, FALSE, FALSE,
7269 EL_QUICKSAND_FILLING, -1, -1
7272 Xsand_sandstone_2, FALSE, FALSE,
7273 EL_QUICKSAND_FILLING, -1, -1
7276 Xsand_sandstone_3, FALSE, FALSE,
7277 EL_QUICKSAND_FILLING, -1, -1
7280 Xsand_sandstone_4, FALSE, FALSE,
7281 EL_QUICKSAND_FILLING, -1, -1
7284 Xsand_stonesand_1, FALSE, FALSE,
7285 EL_QUICKSAND_EMPTYING, -1, -1
7288 Xsand_stonesand_2, FALSE, FALSE,
7289 EL_QUICKSAND_EMPTYING, -1, -1
7292 Xsand_stonesand_3, FALSE, FALSE,
7293 EL_QUICKSAND_EMPTYING, -1, -1
7296 Xsand_stonesand_4, FALSE, FALSE,
7297 EL_QUICKSAND_EMPTYING, -1, -1
7300 Xsand_stoneout_1, FALSE, FALSE,
7301 EL_ROCK, ACTION_EMPTYING, -1
7304 Xsand_stoneout_2, FALSE, FALSE,
7305 EL_ROCK, ACTION_EMPTYING, -1
7308 Xsand_stonesand_quickout_1, FALSE, FALSE,
7309 EL_QUICKSAND_EMPTYING, -1, -1
7312 Xsand_stonesand_quickout_2, FALSE, FALSE,
7313 EL_QUICKSAND_EMPTYING, -1, -1
7317 Xslide_ns, TRUE, FALSE,
7318 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7321 Yslide_ns_blank, FALSE, FALSE,
7322 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7325 Xslide_ew, TRUE, FALSE,
7326 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7329 Yslide_ew_blank, FALSE, FALSE,
7330 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7334 Xwind_n, TRUE, FALSE,
7335 EL_BALLOON_SWITCH_UP, -1, -1
7338 Xwind_e, TRUE, FALSE,
7339 EL_BALLOON_SWITCH_RIGHT, -1, -1
7342 Xwind_s, TRUE, FALSE,
7343 EL_BALLOON_SWITCH_DOWN, -1, -1
7346 Xwind_w, TRUE, FALSE,
7347 EL_BALLOON_SWITCH_LEFT, -1, -1
7350 Xwind_any, TRUE, FALSE,
7351 EL_BALLOON_SWITCH_ANY, -1, -1
7354 Xwind_stop, TRUE, FALSE,
7355 EL_BALLOON_SWITCH_NONE, -1, -1
7360 EL_EM_EXIT_CLOSED, -1, -1
7363 Xexit_1, TRUE, FALSE,
7364 EL_EM_EXIT_OPEN, -1, -1
7367 Xexit_2, FALSE, FALSE,
7368 EL_EM_EXIT_OPEN, -1, -1
7371 Xexit_3, FALSE, FALSE,
7372 EL_EM_EXIT_OPEN, -1, -1
7376 Xpause, FALSE, FALSE,
7381 Xwall_1, TRUE, FALSE,
7385 Xwall_2, TRUE, FALSE,
7386 EL_EMC_WALL_14, -1, -1
7389 Xwall_3, TRUE, FALSE,
7390 EL_EMC_WALL_15, -1, -1
7393 Xwall_4, TRUE, FALSE,
7394 EL_EMC_WALL_16, -1, -1
7398 Xroundwall_1, TRUE, FALSE,
7399 EL_WALL_SLIPPERY, -1, -1
7402 Xroundwall_2, TRUE, FALSE,
7403 EL_EMC_WALL_SLIPPERY_2, -1, -1
7406 Xroundwall_3, TRUE, FALSE,
7407 EL_EMC_WALL_SLIPPERY_3, -1, -1
7410 Xroundwall_4, TRUE, FALSE,
7411 EL_EMC_WALL_SLIPPERY_4, -1, -1
7415 Xsteel_1, TRUE, FALSE,
7416 EL_STEELWALL, -1, -1
7419 Xsteel_2, TRUE, FALSE,
7420 EL_EMC_STEELWALL_2, -1, -1
7423 Xsteel_3, TRUE, FALSE,
7424 EL_EMC_STEELWALL_3, -1, -1
7427 Xsteel_4, TRUE, FALSE,
7428 EL_EMC_STEELWALL_4, -1, -1
7432 Xdecor_1, TRUE, FALSE,
7433 EL_EMC_WALL_8, -1, -1
7436 Xdecor_2, TRUE, FALSE,
7437 EL_EMC_WALL_6, -1, -1
7440 Xdecor_3, TRUE, FALSE,
7441 EL_EMC_WALL_4, -1, -1
7444 Xdecor_4, TRUE, FALSE,
7445 EL_EMC_WALL_7, -1, -1
7448 Xdecor_5, TRUE, FALSE,
7449 EL_EMC_WALL_5, -1, -1
7452 Xdecor_6, TRUE, FALSE,
7453 EL_EMC_WALL_9, -1, -1
7456 Xdecor_7, TRUE, FALSE,
7457 EL_EMC_WALL_10, -1, -1
7460 Xdecor_8, TRUE, FALSE,
7461 EL_EMC_WALL_1, -1, -1
7464 Xdecor_9, TRUE, FALSE,
7465 EL_EMC_WALL_2, -1, -1
7468 Xdecor_10, TRUE, FALSE,
7469 EL_EMC_WALL_3, -1, -1
7472 Xdecor_11, TRUE, FALSE,
7473 EL_EMC_WALL_11, -1, -1
7476 Xdecor_12, TRUE, FALSE,
7477 EL_EMC_WALL_12, -1, -1
7481 Xalpha_0, TRUE, FALSE,
7482 EL_CHAR('0'), -1, -1
7485 Xalpha_1, TRUE, FALSE,
7486 EL_CHAR('1'), -1, -1
7489 Xalpha_2, TRUE, FALSE,
7490 EL_CHAR('2'), -1, -1
7493 Xalpha_3, TRUE, FALSE,
7494 EL_CHAR('3'), -1, -1
7497 Xalpha_4, TRUE, FALSE,
7498 EL_CHAR('4'), -1, -1
7501 Xalpha_5, TRUE, FALSE,
7502 EL_CHAR('5'), -1, -1
7505 Xalpha_6, TRUE, FALSE,
7506 EL_CHAR('6'), -1, -1
7509 Xalpha_7, TRUE, FALSE,
7510 EL_CHAR('7'), -1, -1
7513 Xalpha_8, TRUE, FALSE,
7514 EL_CHAR('8'), -1, -1
7517 Xalpha_9, TRUE, FALSE,
7518 EL_CHAR('9'), -1, -1
7521 Xalpha_excla, TRUE, FALSE,
7522 EL_CHAR('!'), -1, -1
7525 Xalpha_apost, TRUE, FALSE,
7526 EL_CHAR('\''), -1, -1
7529 Xalpha_comma, TRUE, FALSE,
7530 EL_CHAR(','), -1, -1
7533 Xalpha_minus, TRUE, FALSE,
7534 EL_CHAR('-'), -1, -1
7537 Xalpha_perio, TRUE, FALSE,
7538 EL_CHAR('.'), -1, -1
7541 Xalpha_colon, TRUE, FALSE,
7542 EL_CHAR(':'), -1, -1
7545 Xalpha_quest, TRUE, FALSE,
7546 EL_CHAR('?'), -1, -1
7549 Xalpha_a, TRUE, FALSE,
7550 EL_CHAR('A'), -1, -1
7553 Xalpha_b, TRUE, FALSE,
7554 EL_CHAR('B'), -1, -1
7557 Xalpha_c, TRUE, FALSE,
7558 EL_CHAR('C'), -1, -1
7561 Xalpha_d, TRUE, FALSE,
7562 EL_CHAR('D'), -1, -1
7565 Xalpha_e, TRUE, FALSE,
7566 EL_CHAR('E'), -1, -1
7569 Xalpha_f, TRUE, FALSE,
7570 EL_CHAR('F'), -1, -1
7573 Xalpha_g, TRUE, FALSE,
7574 EL_CHAR('G'), -1, -1
7577 Xalpha_h, TRUE, FALSE,
7578 EL_CHAR('H'), -1, -1
7581 Xalpha_i, TRUE, FALSE,
7582 EL_CHAR('I'), -1, -1
7585 Xalpha_j, TRUE, FALSE,
7586 EL_CHAR('J'), -1, -1
7589 Xalpha_k, TRUE, FALSE,
7590 EL_CHAR('K'), -1, -1
7593 Xalpha_l, TRUE, FALSE,
7594 EL_CHAR('L'), -1, -1
7597 Xalpha_m, TRUE, FALSE,
7598 EL_CHAR('M'), -1, -1
7601 Xalpha_n, TRUE, FALSE,
7602 EL_CHAR('N'), -1, -1
7605 Xalpha_o, TRUE, FALSE,
7606 EL_CHAR('O'), -1, -1
7609 Xalpha_p, TRUE, FALSE,
7610 EL_CHAR('P'), -1, -1
7613 Xalpha_q, TRUE, FALSE,
7614 EL_CHAR('Q'), -1, -1
7617 Xalpha_r, TRUE, FALSE,
7618 EL_CHAR('R'), -1, -1
7621 Xalpha_s, TRUE, FALSE,
7622 EL_CHAR('S'), -1, -1
7625 Xalpha_t, TRUE, FALSE,
7626 EL_CHAR('T'), -1, -1
7629 Xalpha_u, TRUE, FALSE,
7630 EL_CHAR('U'), -1, -1
7633 Xalpha_v, TRUE, FALSE,
7634 EL_CHAR('V'), -1, -1
7637 Xalpha_w, TRUE, FALSE,
7638 EL_CHAR('W'), -1, -1
7641 Xalpha_x, TRUE, FALSE,
7642 EL_CHAR('X'), -1, -1
7645 Xalpha_y, TRUE, FALSE,
7646 EL_CHAR('Y'), -1, -1
7649 Xalpha_z, TRUE, FALSE,
7650 EL_CHAR('Z'), -1, -1
7653 Xalpha_arrow_e, TRUE, FALSE,
7654 EL_CHAR('>'), -1, -1
7657 Xalpha_arrow_w, TRUE, FALSE,
7658 EL_CHAR('<'), -1, -1
7661 Xalpha_copyr, TRUE, FALSE,
7662 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7666 Ykey_1_blank, FALSE, FALSE,
7667 EL_EM_KEY_1, ACTION_COLLECTING, -1
7670 Ykey_2_blank, FALSE, FALSE,
7671 EL_EM_KEY_2, ACTION_COLLECTING, -1
7674 Ykey_3_blank, FALSE, FALSE,
7675 EL_EM_KEY_3, ACTION_COLLECTING, -1
7678 Ykey_4_blank, FALSE, FALSE,
7679 EL_EM_KEY_4, ACTION_COLLECTING, -1
7682 Ykey_5_blank, FALSE, FALSE,
7683 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7686 Ykey_6_blank, FALSE, FALSE,
7687 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7690 Ykey_7_blank, FALSE, FALSE,
7691 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7694 Ykey_8_blank, FALSE, FALSE,
7695 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7698 Ylenses_blank, FALSE, FALSE,
7699 EL_EMC_LENSES, ACTION_COLLECTING, -1
7702 Ymagnify_blank, FALSE, FALSE,
7703 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7706 Ygrass_blank, FALSE, FALSE,
7707 EL_EMC_GRASS, ACTION_SNAPPING, -1
7710 Ydirt_blank, FALSE, FALSE,
7711 EL_SAND, ACTION_SNAPPING, -1
7720 static struct Mapping_EM_to_RND_player
7729 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7733 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7737 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7741 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7745 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7749 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7753 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7757 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7761 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7765 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7769 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7773 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7777 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7781 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7785 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7789 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7793 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7797 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7801 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7805 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7809 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7813 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7817 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7821 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7825 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7829 EL_PLAYER_1, ACTION_DEFAULT, -1,
7833 EL_PLAYER_2, ACTION_DEFAULT, -1,
7837 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7841 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7845 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7849 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7853 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7857 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7861 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7865 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7869 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7873 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7877 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7881 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7885 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7889 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7893 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7897 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7901 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7905 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7909 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7913 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7917 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7921 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7925 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7929 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7933 EL_PLAYER_3, ACTION_DEFAULT, -1,
7937 EL_PLAYER_4, ACTION_DEFAULT, -1,
7946 int map_element_RND_to_EM_cave(int element_rnd)
7948 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7949 static boolean mapping_initialized = FALSE;
7951 if (!mapping_initialized)
7955 // return "Xalpha_quest" for all undefined elements in mapping array
7956 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7957 mapping_RND_to_EM[i] = Xalpha_quest;
7959 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7960 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7961 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7962 em_object_mapping_list[i].element_em;
7964 mapping_initialized = TRUE;
7967 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7969 Warn("invalid RND level element %d", element_rnd);
7974 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7977 int map_element_EM_to_RND_cave(int element_em_cave)
7979 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7980 static boolean mapping_initialized = FALSE;
7982 if (!mapping_initialized)
7986 // return "EL_UNKNOWN" for all undefined elements in mapping array
7987 for (i = 0; i < GAME_TILE_MAX; i++)
7988 mapping_EM_to_RND[i] = EL_UNKNOWN;
7990 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7991 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7992 em_object_mapping_list[i].element_rnd;
7994 mapping_initialized = TRUE;
7997 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7999 Warn("invalid EM cave element %d", element_em_cave);
8004 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8007 int map_element_EM_to_RND_game(int element_em_game)
8009 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8010 static boolean mapping_initialized = FALSE;
8012 if (!mapping_initialized)
8016 // return "EL_UNKNOWN" for all undefined elements in mapping array
8017 for (i = 0; i < GAME_TILE_MAX; i++)
8018 mapping_EM_to_RND[i] = EL_UNKNOWN;
8020 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8021 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8022 em_object_mapping_list[i].element_rnd;
8024 mapping_initialized = TRUE;
8027 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8029 Warn("invalid EM game element %d", element_em_game);
8034 return mapping_EM_to_RND[element_em_game];
8037 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8039 struct LevelInfo_EM *level_em = level->native_em_level;
8040 struct CAVE *cav = level_em->cav;
8043 for (i = 0; i < GAME_TILE_MAX; i++)
8044 cav->android_array[i] = Cblank;
8046 for (i = 0; i < level->num_android_clone_elements; i++)
8048 int element_rnd = level->android_clone_element[i];
8049 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8051 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8052 if (em_object_mapping_list[j].element_rnd == element_rnd)
8053 cav->android_array[em_object_mapping_list[j].element_em] =
8058 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8060 struct LevelInfo_EM *level_em = level->native_em_level;
8061 struct CAVE *cav = level_em->cav;
8064 level->num_android_clone_elements = 0;
8066 for (i = 0; i < GAME_TILE_MAX; i++)
8068 int element_em_cave = cav->android_array[i];
8070 boolean element_found = FALSE;
8072 if (element_em_cave == Cblank)
8075 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8077 for (j = 0; j < level->num_android_clone_elements; j++)
8078 if (level->android_clone_element[j] == element_rnd)
8079 element_found = TRUE;
8083 level->android_clone_element[level->num_android_clone_elements++] =
8086 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8091 if (level->num_android_clone_elements == 0)
8093 level->num_android_clone_elements = 1;
8094 level->android_clone_element[0] = EL_EMPTY;
8098 int map_direction_RND_to_EM(int direction)
8100 return (direction == MV_UP ? 0 :
8101 direction == MV_RIGHT ? 1 :
8102 direction == MV_DOWN ? 2 :
8103 direction == MV_LEFT ? 3 :
8107 int map_direction_EM_to_RND(int direction)
8109 return (direction == 0 ? MV_UP :
8110 direction == 1 ? MV_RIGHT :
8111 direction == 2 ? MV_DOWN :
8112 direction == 3 ? MV_LEFT :
8116 int map_element_RND_to_SP(int element_rnd)
8118 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8120 if (element_rnd >= EL_SP_START &&
8121 element_rnd <= EL_SP_END)
8122 element_sp = element_rnd - EL_SP_START;
8123 else if (element_rnd == EL_EMPTY_SPACE)
8125 else if (element_rnd == EL_INVISIBLE_WALL)
8131 int map_element_SP_to_RND(int element_sp)
8133 int element_rnd = EL_UNKNOWN;
8135 if (element_sp >= 0x00 &&
8137 element_rnd = EL_SP_START + element_sp;
8138 else if (element_sp == 0x28)
8139 element_rnd = EL_INVISIBLE_WALL;
8144 int map_action_SP_to_RND(int action_sp)
8148 case actActive: return ACTION_ACTIVE;
8149 case actImpact: return ACTION_IMPACT;
8150 case actExploding: return ACTION_EXPLODING;
8151 case actDigging: return ACTION_DIGGING;
8152 case actSnapping: return ACTION_SNAPPING;
8153 case actCollecting: return ACTION_COLLECTING;
8154 case actPassing: return ACTION_PASSING;
8155 case actPushing: return ACTION_PUSHING;
8156 case actDropping: return ACTION_DROPPING;
8158 default: return ACTION_DEFAULT;
8162 int map_element_RND_to_MM(int element_rnd)
8164 return (element_rnd >= EL_MM_START_1 &&
8165 element_rnd <= EL_MM_END_1 ?
8166 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8168 element_rnd >= EL_MM_START_2 &&
8169 element_rnd <= EL_MM_END_2 ?
8170 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8172 element_rnd >= EL_CHAR_START &&
8173 element_rnd <= EL_CHAR_END ?
8174 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8176 element_rnd >= EL_MM_RUNTIME_START &&
8177 element_rnd <= EL_MM_RUNTIME_END ?
8178 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8180 element_rnd >= EL_MM_DUMMY_START &&
8181 element_rnd <= EL_MM_DUMMY_END ?
8182 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8184 EL_MM_EMPTY_NATIVE);
8187 int map_element_MM_to_RND(int element_mm)
8189 return (element_mm == EL_MM_EMPTY_NATIVE ||
8190 element_mm == EL_DF_EMPTY_NATIVE ?
8193 element_mm >= EL_MM_START_1_NATIVE &&
8194 element_mm <= EL_MM_END_1_NATIVE ?
8195 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8197 element_mm >= EL_MM_START_2_NATIVE &&
8198 element_mm <= EL_MM_END_2_NATIVE ?
8199 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8201 element_mm >= EL_MM_CHAR_START_NATIVE &&
8202 element_mm <= EL_MM_CHAR_END_NATIVE ?
8203 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8205 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8206 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8207 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8209 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8210 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8211 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8216 int map_action_MM_to_RND(int action_mm)
8218 // all MM actions are defined to exactly match their RND counterparts
8222 int map_sound_MM_to_RND(int sound_mm)
8226 case SND_MM_GAME_LEVELTIME_CHARGING:
8227 return SND_GAME_LEVELTIME_CHARGING;
8229 case SND_MM_GAME_HEALTH_CHARGING:
8230 return SND_GAME_HEALTH_CHARGING;
8233 return SND_UNDEFINED;
8237 int map_mm_wall_element(int element)
8239 return (element >= EL_MM_STEEL_WALL_START &&
8240 element <= EL_MM_STEEL_WALL_END ?
8243 element >= EL_MM_WOODEN_WALL_START &&
8244 element <= EL_MM_WOODEN_WALL_END ?
8247 element >= EL_MM_ICE_WALL_START &&
8248 element <= EL_MM_ICE_WALL_END ?
8251 element >= EL_MM_AMOEBA_WALL_START &&
8252 element <= EL_MM_AMOEBA_WALL_END ?
8255 element >= EL_DF_STEEL_WALL_START &&
8256 element <= EL_DF_STEEL_WALL_END ?
8259 element >= EL_DF_WOODEN_WALL_START &&
8260 element <= EL_DF_WOODEN_WALL_END ?
8266 int map_mm_wall_element_editor(int element)
8270 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8271 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8272 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8273 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8274 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8275 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8277 default: return element;
8281 int get_next_element(int element)
8285 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8286 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8287 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8288 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8289 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8290 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8291 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8292 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8293 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8294 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8295 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8297 default: return element;
8301 int el2img_mm(int element_mm)
8303 return el2img(map_element_MM_to_RND(element_mm));
8306 int el_act_dir2img(int element, int action, int direction)
8308 element = GFX_ELEMENT(element);
8309 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8311 // direction_graphic[][] == graphic[] for undefined direction graphics
8312 return element_info[element].direction_graphic[action][direction];
8315 static int el_act_dir2crm(int element, int action, int direction)
8317 element = GFX_ELEMENT(element);
8318 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8320 // direction_graphic[][] == graphic[] for undefined direction graphics
8321 return element_info[element].direction_crumbled[action][direction];
8324 int el_act2img(int element, int action)
8326 element = GFX_ELEMENT(element);
8328 return element_info[element].graphic[action];
8331 int el_act2crm(int element, int action)
8333 element = GFX_ELEMENT(element);
8335 return element_info[element].crumbled[action];
8338 int el_dir2img(int element, int direction)
8340 element = GFX_ELEMENT(element);
8342 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8345 int el2baseimg(int element)
8347 return element_info[element].graphic[ACTION_DEFAULT];
8350 int el2img(int element)
8352 element = GFX_ELEMENT(element);
8354 return element_info[element].graphic[ACTION_DEFAULT];
8357 int el2edimg(int element)
8359 element = GFX_ELEMENT(element);
8361 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8364 int el2preimg(int element)
8366 element = GFX_ELEMENT(element);
8368 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8371 int el2panelimg(int element)
8373 element = GFX_ELEMENT(element);
8375 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8378 int font2baseimg(int font_nr)
8380 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8383 int getBeltNrFromBeltElement(int element)
8385 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8386 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8387 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8390 int getBeltNrFromBeltActiveElement(int element)
8392 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8393 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8394 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8397 int getBeltNrFromBeltSwitchElement(int element)
8399 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8400 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8401 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8404 int getBeltDirNrFromBeltElement(int element)
8406 static int belt_base_element[4] =
8408 EL_CONVEYOR_BELT_1_LEFT,
8409 EL_CONVEYOR_BELT_2_LEFT,
8410 EL_CONVEYOR_BELT_3_LEFT,
8411 EL_CONVEYOR_BELT_4_LEFT
8414 int belt_nr = getBeltNrFromBeltElement(element);
8415 int belt_dir_nr = element - belt_base_element[belt_nr];
8417 return (belt_dir_nr % 3);
8420 int getBeltDirNrFromBeltSwitchElement(int element)
8422 static int belt_base_element[4] =
8424 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8425 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8426 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8427 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8430 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8431 int belt_dir_nr = element - belt_base_element[belt_nr];
8433 return (belt_dir_nr % 3);
8436 int getBeltDirFromBeltElement(int element)
8438 static int belt_move_dir[3] =
8445 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8447 return belt_move_dir[belt_dir_nr];
8450 int getBeltDirFromBeltSwitchElement(int element)
8452 static int belt_move_dir[3] =
8459 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8461 return belt_move_dir[belt_dir_nr];
8464 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8466 static int belt_base_element[4] =
8468 EL_CONVEYOR_BELT_1_LEFT,
8469 EL_CONVEYOR_BELT_2_LEFT,
8470 EL_CONVEYOR_BELT_3_LEFT,
8471 EL_CONVEYOR_BELT_4_LEFT
8474 return belt_base_element[belt_nr] + belt_dir_nr;
8477 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8479 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8481 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8484 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8486 static int belt_base_element[4] =
8488 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8489 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8490 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8491 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8494 return belt_base_element[belt_nr] + belt_dir_nr;
8497 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8499 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8501 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8504 boolean swapTiles_EM(boolean is_pre_emc_cave)
8506 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8509 boolean getTeamMode_EM(void)
8511 return game.team_mode || network_playing;
8514 boolean isActivePlayer_EM(int player_nr)
8516 return stored_player[player_nr].active;
8519 unsigned int InitRND(int seed)
8521 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8522 return InitEngineRandom_EM(seed);
8523 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8524 return InitEngineRandom_SP(seed);
8525 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8526 return InitEngineRandom_MM(seed);
8528 return InitEngineRandom_RND(seed);
8531 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8532 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8534 static int get_effective_element_EM(int tile, int frame_em)
8536 int element = object_mapping[tile].element_rnd;
8537 int action = object_mapping[tile].action;
8538 boolean is_backside = object_mapping[tile].is_backside;
8539 boolean action_removing = (action == ACTION_DIGGING ||
8540 action == ACTION_SNAPPING ||
8541 action == ACTION_COLLECTING);
8549 return (frame_em > 5 ? EL_EMPTY : element);
8555 else // frame_em == 7
8566 case Ydiamond_stone:
8570 case Xdrip_stretchB:
8586 case Ymagnify_blank:
8589 case Xsand_stonein_1:
8590 case Xsand_stonein_2:
8591 case Xsand_stonein_3:
8592 case Xsand_stonein_4:
8596 return (is_backside || action_removing ? EL_EMPTY : element);
8601 static boolean check_linear_animation_EM(int tile)
8605 case Xsand_stonesand_1:
8606 case Xsand_stonesand_quickout_1:
8607 case Xsand_sandstone_1:
8608 case Xsand_stonein_1:
8609 case Xsand_stoneout_1:
8637 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8638 boolean has_crumbled_graphics,
8639 int crumbled, int sync_frame)
8641 // if element can be crumbled, but certain action graphics are just empty
8642 // space (like instantly snapping sand to empty space in 1 frame), do not
8643 // treat these empty space graphics as crumbled graphics in EMC engine
8644 if (crumbled == IMG_EMPTY_SPACE)
8645 has_crumbled_graphics = FALSE;
8647 if (has_crumbled_graphics)
8649 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8650 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8651 g_crumbled->anim_delay,
8652 g_crumbled->anim_mode,
8653 g_crumbled->anim_start_frame,
8656 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8657 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8659 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8660 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8662 g_em->has_crumbled_graphics = TRUE;
8666 g_em->crumbled_bitmap = NULL;
8667 g_em->crumbled_src_x = 0;
8668 g_em->crumbled_src_y = 0;
8669 g_em->crumbled_border_size = 0;
8670 g_em->crumbled_tile_size = 0;
8672 g_em->has_crumbled_graphics = FALSE;
8677 void ResetGfxAnimation_EM(int x, int y, int tile)
8683 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8684 int tile, int frame_em, int x, int y)
8686 int action = object_mapping[tile].action;
8687 int direction = object_mapping[tile].direction;
8688 int effective_element = get_effective_element_EM(tile, frame_em);
8689 int graphic = (direction == MV_NONE ?
8690 el_act2img(effective_element, action) :
8691 el_act_dir2img(effective_element, action, direction));
8692 struct GraphicInfo *g = &graphic_info[graphic];
8694 boolean action_removing = (action == ACTION_DIGGING ||
8695 action == ACTION_SNAPPING ||
8696 action == ACTION_COLLECTING);
8697 boolean action_moving = (action == ACTION_FALLING ||
8698 action == ACTION_MOVING ||
8699 action == ACTION_PUSHING ||
8700 action == ACTION_EATING ||
8701 action == ACTION_FILLING ||
8702 action == ACTION_EMPTYING);
8703 boolean action_falling = (action == ACTION_FALLING ||
8704 action == ACTION_FILLING ||
8705 action == ACTION_EMPTYING);
8707 // special case: graphic uses "2nd movement tile" and has defined
8708 // 7 frames for movement animation (or less) => use default graphic
8709 // for last (8th) frame which ends the movement animation
8710 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8712 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8713 graphic = (direction == MV_NONE ?
8714 el_act2img(effective_element, action) :
8715 el_act_dir2img(effective_element, action, direction));
8717 g = &graphic_info[graphic];
8720 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8724 else if (action_moving)
8726 boolean is_backside = object_mapping[tile].is_backside;
8730 int direction = object_mapping[tile].direction;
8731 int move_dir = (action_falling ? MV_DOWN : direction);
8736 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8737 if (g->double_movement && frame_em == 0)
8741 if (move_dir == MV_LEFT)
8742 GfxFrame[x - 1][y] = GfxFrame[x][y];
8743 else if (move_dir == MV_RIGHT)
8744 GfxFrame[x + 1][y] = GfxFrame[x][y];
8745 else if (move_dir == MV_UP)
8746 GfxFrame[x][y - 1] = GfxFrame[x][y];
8747 else if (move_dir == MV_DOWN)
8748 GfxFrame[x][y + 1] = GfxFrame[x][y];
8755 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8756 if (tile == Xsand_stonesand_quickout_1 ||
8757 tile == Xsand_stonesand_quickout_2)
8761 if (graphic_info[graphic].anim_global_sync)
8762 sync_frame = FrameCounter;
8763 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8764 sync_frame = GfxFrame[x][y];
8766 sync_frame = 0; // playfield border (pseudo steel)
8768 SetRandomAnimationValue(x, y);
8770 int frame = getAnimationFrame(g->anim_frames,
8773 g->anim_start_frame,
8776 g_em->unique_identifier =
8777 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8780 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8781 int tile, int frame_em, int x, int y)
8783 int action = object_mapping[tile].action;
8784 int direction = object_mapping[tile].direction;
8785 boolean is_backside = object_mapping[tile].is_backside;
8786 int effective_element = get_effective_element_EM(tile, frame_em);
8787 int effective_action = action;
8788 int graphic = (direction == MV_NONE ?
8789 el_act2img(effective_element, effective_action) :
8790 el_act_dir2img(effective_element, effective_action,
8792 int crumbled = (direction == MV_NONE ?
8793 el_act2crm(effective_element, effective_action) :
8794 el_act_dir2crm(effective_element, effective_action,
8796 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8797 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8798 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8799 struct GraphicInfo *g = &graphic_info[graphic];
8802 // special case: graphic uses "2nd movement tile" and has defined
8803 // 7 frames for movement animation (or less) => use default graphic
8804 // for last (8th) frame which ends the movement animation
8805 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8807 effective_action = ACTION_DEFAULT;
8808 graphic = (direction == MV_NONE ?
8809 el_act2img(effective_element, effective_action) :
8810 el_act_dir2img(effective_element, effective_action,
8812 crumbled = (direction == MV_NONE ?
8813 el_act2crm(effective_element, effective_action) :
8814 el_act_dir2crm(effective_element, effective_action,
8817 g = &graphic_info[graphic];
8820 if (graphic_info[graphic].anim_global_sync)
8821 sync_frame = FrameCounter;
8822 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8823 sync_frame = GfxFrame[x][y];
8825 sync_frame = 0; // playfield border (pseudo steel)
8827 SetRandomAnimationValue(x, y);
8829 int frame = getAnimationFrame(g->anim_frames,
8832 g->anim_start_frame,
8835 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8836 g->double_movement && is_backside);
8838 // (updating the "crumbled" graphic definitions is probably not really needed,
8839 // as animations for crumbled graphics can't be longer than one EMC cycle)
8840 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8844 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8845 int player_nr, int anim, int frame_em)
8847 int element = player_mapping[player_nr][anim].element_rnd;
8848 int action = player_mapping[player_nr][anim].action;
8849 int direction = player_mapping[player_nr][anim].direction;
8850 int graphic = (direction == MV_NONE ?
8851 el_act2img(element, action) :
8852 el_act_dir2img(element, action, direction));
8853 struct GraphicInfo *g = &graphic_info[graphic];
8856 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8858 stored_player[player_nr].StepFrame = frame_em;
8860 sync_frame = stored_player[player_nr].Frame;
8862 int frame = getAnimationFrame(g->anim_frames,
8865 g->anim_start_frame,
8868 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8869 &g_em->src_x, &g_em->src_y, FALSE);
8872 void InitGraphicInfo_EM(void)
8876 // always start with reliable default values
8877 for (i = 0; i < GAME_TILE_MAX; i++)
8879 object_mapping[i].element_rnd = EL_UNKNOWN;
8880 object_mapping[i].is_backside = FALSE;
8881 object_mapping[i].action = ACTION_DEFAULT;
8882 object_mapping[i].direction = MV_NONE;
8885 // always start with reliable default values
8886 for (p = 0; p < MAX_PLAYERS; p++)
8888 for (i = 0; i < PLY_MAX; i++)
8890 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8891 player_mapping[p][i].action = ACTION_DEFAULT;
8892 player_mapping[p][i].direction = MV_NONE;
8896 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8898 int e = em_object_mapping_list[i].element_em;
8900 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8901 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8903 if (em_object_mapping_list[i].action != -1)
8904 object_mapping[e].action = em_object_mapping_list[i].action;
8906 if (em_object_mapping_list[i].direction != -1)
8907 object_mapping[e].direction =
8908 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8911 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8913 int a = em_player_mapping_list[i].action_em;
8914 int p = em_player_mapping_list[i].player_nr;
8916 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8918 if (em_player_mapping_list[i].action != -1)
8919 player_mapping[p][a].action = em_player_mapping_list[i].action;
8921 if (em_player_mapping_list[i].direction != -1)
8922 player_mapping[p][a].direction =
8923 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8926 for (i = 0; i < GAME_TILE_MAX; i++)
8928 int element = object_mapping[i].element_rnd;
8929 int action = object_mapping[i].action;
8930 int direction = object_mapping[i].direction;
8931 boolean is_backside = object_mapping[i].is_backside;
8932 boolean action_exploding = ((action == ACTION_EXPLODING ||
8933 action == ACTION_SMASHED_BY_ROCK ||
8934 action == ACTION_SMASHED_BY_SPRING) &&
8935 element != EL_DIAMOND);
8936 boolean action_active = (action == ACTION_ACTIVE);
8937 boolean action_other = (action == ACTION_OTHER);
8939 for (j = 0; j < 8; j++)
8941 int effective_element = get_effective_element_EM(i, j);
8942 int effective_action = (j < 7 ? action :
8943 i == Xdrip_stretch ? action :
8944 i == Xdrip_stretchB ? action :
8945 i == Ydrip_1_s ? action :
8946 i == Ydrip_1_sB ? action :
8947 i == Yball_1 ? action :
8948 i == Xball_2 ? action :
8949 i == Yball_2 ? action :
8950 i == Yball_blank ? action :
8951 i == Ykey_1_blank ? action :
8952 i == Ykey_2_blank ? action :
8953 i == Ykey_3_blank ? action :
8954 i == Ykey_4_blank ? action :
8955 i == Ykey_5_blank ? action :
8956 i == Ykey_6_blank ? action :
8957 i == Ykey_7_blank ? action :
8958 i == Ykey_8_blank ? action :
8959 i == Ylenses_blank ? action :
8960 i == Ymagnify_blank ? action :
8961 i == Ygrass_blank ? action :
8962 i == Ydirt_blank ? action :
8963 i == Xsand_stonein_1 ? action :
8964 i == Xsand_stonein_2 ? action :
8965 i == Xsand_stonein_3 ? action :
8966 i == Xsand_stonein_4 ? action :
8967 i == Xsand_stoneout_1 ? action :
8968 i == Xsand_stoneout_2 ? action :
8969 i == Xboom_android ? ACTION_EXPLODING :
8970 action_exploding ? ACTION_EXPLODING :
8971 action_active ? action :
8972 action_other ? action :
8974 int graphic = (el_act_dir2img(effective_element, effective_action,
8976 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8978 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8979 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8980 boolean has_action_graphics = (graphic != base_graphic);
8981 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8982 struct GraphicInfo *g = &graphic_info[graphic];
8983 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8986 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8987 boolean special_animation = (action != ACTION_DEFAULT &&
8988 g->anim_frames == 3 &&
8989 g->anim_delay == 2 &&
8990 g->anim_mode & ANIM_LINEAR);
8991 int sync_frame = (i == Xdrip_stretch ? 7 :
8992 i == Xdrip_stretchB ? 7 :
8993 i == Ydrip_2_s ? j + 8 :
8994 i == Ydrip_2_sB ? j + 8 :
9003 i == Xfake_acid_1 ? 0 :
9004 i == Xfake_acid_2 ? 10 :
9005 i == Xfake_acid_3 ? 20 :
9006 i == Xfake_acid_4 ? 30 :
9007 i == Xfake_acid_5 ? 40 :
9008 i == Xfake_acid_6 ? 50 :
9009 i == Xfake_acid_7 ? 60 :
9010 i == Xfake_acid_8 ? 70 :
9011 i == Xfake_acid_1_player ? 0 :
9012 i == Xfake_acid_2_player ? 10 :
9013 i == Xfake_acid_3_player ? 20 :
9014 i == Xfake_acid_4_player ? 30 :
9015 i == Xfake_acid_5_player ? 40 :
9016 i == Xfake_acid_6_player ? 50 :
9017 i == Xfake_acid_7_player ? 60 :
9018 i == Xfake_acid_8_player ? 70 :
9020 i == Yball_2 ? j + 8 :
9021 i == Yball_blank ? j + 1 :
9022 i == Ykey_1_blank ? j + 1 :
9023 i == Ykey_2_blank ? j + 1 :
9024 i == Ykey_3_blank ? j + 1 :
9025 i == Ykey_4_blank ? j + 1 :
9026 i == Ykey_5_blank ? j + 1 :
9027 i == Ykey_6_blank ? j + 1 :
9028 i == Ykey_7_blank ? j + 1 :
9029 i == Ykey_8_blank ? j + 1 :
9030 i == Ylenses_blank ? j + 1 :
9031 i == Ymagnify_blank ? j + 1 :
9032 i == Ygrass_blank ? j + 1 :
9033 i == Ydirt_blank ? j + 1 :
9034 i == Xamoeba_1 ? 0 :
9035 i == Xamoeba_2 ? 1 :
9036 i == Xamoeba_3 ? 2 :
9037 i == Xamoeba_4 ? 3 :
9038 i == Xamoeba_5 ? 0 :
9039 i == Xamoeba_6 ? 1 :
9040 i == Xamoeba_7 ? 2 :
9041 i == Xamoeba_8 ? 3 :
9042 i == Xexit_2 ? j + 8 :
9043 i == Xexit_3 ? j + 16 :
9044 i == Xdynamite_1 ? 0 :
9045 i == Xdynamite_2 ? 8 :
9046 i == Xdynamite_3 ? 16 :
9047 i == Xdynamite_4 ? 24 :
9048 i == Xsand_stonein_1 ? j + 1 :
9049 i == Xsand_stonein_2 ? j + 9 :
9050 i == Xsand_stonein_3 ? j + 17 :
9051 i == Xsand_stonein_4 ? j + 25 :
9052 i == Xsand_stoneout_1 && j == 0 ? 0 :
9053 i == Xsand_stoneout_1 && j == 1 ? 0 :
9054 i == Xsand_stoneout_1 && j == 2 ? 1 :
9055 i == Xsand_stoneout_1 && j == 3 ? 2 :
9056 i == Xsand_stoneout_1 && j == 4 ? 2 :
9057 i == Xsand_stoneout_1 && j == 5 ? 3 :
9058 i == Xsand_stoneout_1 && j == 6 ? 4 :
9059 i == Xsand_stoneout_1 && j == 7 ? 4 :
9060 i == Xsand_stoneout_2 && j == 0 ? 5 :
9061 i == Xsand_stoneout_2 && j == 1 ? 6 :
9062 i == Xsand_stoneout_2 && j == 2 ? 7 :
9063 i == Xsand_stoneout_2 && j == 3 ? 8 :
9064 i == Xsand_stoneout_2 && j == 4 ? 9 :
9065 i == Xsand_stoneout_2 && j == 5 ? 11 :
9066 i == Xsand_stoneout_2 && j == 6 ? 13 :
9067 i == Xsand_stoneout_2 && j == 7 ? 15 :
9068 i == Xboom_bug && j == 1 ? 2 :
9069 i == Xboom_bug && j == 2 ? 2 :
9070 i == Xboom_bug && j == 3 ? 4 :
9071 i == Xboom_bug && j == 4 ? 4 :
9072 i == Xboom_bug && j == 5 ? 2 :
9073 i == Xboom_bug && j == 6 ? 2 :
9074 i == Xboom_bug && j == 7 ? 0 :
9075 i == Xboom_tank && j == 1 ? 2 :
9076 i == Xboom_tank && j == 2 ? 2 :
9077 i == Xboom_tank && j == 3 ? 4 :
9078 i == Xboom_tank && j == 4 ? 4 :
9079 i == Xboom_tank && j == 5 ? 2 :
9080 i == Xboom_tank && j == 6 ? 2 :
9081 i == Xboom_tank && j == 7 ? 0 :
9082 i == Xboom_android && j == 7 ? 6 :
9083 i == Xboom_1 && j == 1 ? 2 :
9084 i == Xboom_1 && j == 2 ? 2 :
9085 i == Xboom_1 && j == 3 ? 4 :
9086 i == Xboom_1 && j == 4 ? 4 :
9087 i == Xboom_1 && j == 5 ? 6 :
9088 i == Xboom_1 && j == 6 ? 6 :
9089 i == Xboom_1 && j == 7 ? 8 :
9090 i == Xboom_2 && j == 0 ? 8 :
9091 i == Xboom_2 && j == 1 ? 8 :
9092 i == Xboom_2 && j == 2 ? 10 :
9093 i == Xboom_2 && j == 3 ? 10 :
9094 i == Xboom_2 && j == 4 ? 10 :
9095 i == Xboom_2 && j == 5 ? 12 :
9096 i == Xboom_2 && j == 6 ? 12 :
9097 i == Xboom_2 && j == 7 ? 12 :
9098 special_animation && j == 4 ? 3 :
9099 effective_action != action ? 0 :
9101 int frame = getAnimationFrame(g->anim_frames,
9104 g->anim_start_frame,
9107 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9108 g->double_movement && is_backside);
9110 g_em->bitmap = src_bitmap;
9111 g_em->src_x = src_x;
9112 g_em->src_y = src_y;
9113 g_em->src_offset_x = 0;
9114 g_em->src_offset_y = 0;
9115 g_em->dst_offset_x = 0;
9116 g_em->dst_offset_y = 0;
9117 g_em->width = TILEX;
9118 g_em->height = TILEY;
9120 g_em->preserve_background = FALSE;
9122 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9125 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9126 effective_action == ACTION_MOVING ||
9127 effective_action == ACTION_PUSHING ||
9128 effective_action == ACTION_EATING)) ||
9129 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9130 effective_action == ACTION_EMPTYING)))
9133 (effective_action == ACTION_FALLING ||
9134 effective_action == ACTION_FILLING ||
9135 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9136 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9137 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9138 int num_steps = (i == Ydrip_1_s ? 16 :
9139 i == Ydrip_1_sB ? 16 :
9140 i == Ydrip_2_s ? 16 :
9141 i == Ydrip_2_sB ? 16 :
9142 i == Xsand_stonein_1 ? 32 :
9143 i == Xsand_stonein_2 ? 32 :
9144 i == Xsand_stonein_3 ? 32 :
9145 i == Xsand_stonein_4 ? 32 :
9146 i == Xsand_stoneout_1 ? 16 :
9147 i == Xsand_stoneout_2 ? 16 : 8);
9148 int cx = ABS(dx) * (TILEX / num_steps);
9149 int cy = ABS(dy) * (TILEY / num_steps);
9150 int step_frame = (i == Ydrip_2_s ? j + 8 :
9151 i == Ydrip_2_sB ? j + 8 :
9152 i == Xsand_stonein_2 ? j + 8 :
9153 i == Xsand_stonein_3 ? j + 16 :
9154 i == Xsand_stonein_4 ? j + 24 :
9155 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9156 int step = (is_backside ? step_frame : num_steps - step_frame);
9158 if (is_backside) // tile where movement starts
9160 if (dx < 0 || dy < 0)
9162 g_em->src_offset_x = cx * step;
9163 g_em->src_offset_y = cy * step;
9167 g_em->dst_offset_x = cx * step;
9168 g_em->dst_offset_y = cy * step;
9171 else // tile where movement ends
9173 if (dx < 0 || dy < 0)
9175 g_em->dst_offset_x = cx * step;
9176 g_em->dst_offset_y = cy * step;
9180 g_em->src_offset_x = cx * step;
9181 g_em->src_offset_y = cy * step;
9185 g_em->width = TILEX - cx * step;
9186 g_em->height = TILEY - cy * step;
9189 // create unique graphic identifier to decide if tile must be redrawn
9190 /* bit 31 - 16 (16 bit): EM style graphic
9191 bit 15 - 12 ( 4 bit): EM style frame
9192 bit 11 - 6 ( 6 bit): graphic width
9193 bit 5 - 0 ( 6 bit): graphic height */
9194 g_em->unique_identifier =
9195 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9199 for (i = 0; i < GAME_TILE_MAX; i++)
9201 for (j = 0; j < 8; j++)
9203 int element = object_mapping[i].element_rnd;
9204 int action = object_mapping[i].action;
9205 int direction = object_mapping[i].direction;
9206 boolean is_backside = object_mapping[i].is_backside;
9207 int graphic_action = el_act_dir2img(element, action, direction);
9208 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9210 if ((action == ACTION_SMASHED_BY_ROCK ||
9211 action == ACTION_SMASHED_BY_SPRING ||
9212 action == ACTION_EATING) &&
9213 graphic_action == graphic_default)
9215 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9216 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9217 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9218 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9221 // no separate animation for "smashed by rock" -- use rock instead
9222 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9223 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9225 g_em->bitmap = g_xx->bitmap;
9226 g_em->src_x = g_xx->src_x;
9227 g_em->src_y = g_xx->src_y;
9228 g_em->src_offset_x = g_xx->src_offset_x;
9229 g_em->src_offset_y = g_xx->src_offset_y;
9230 g_em->dst_offset_x = g_xx->dst_offset_x;
9231 g_em->dst_offset_y = g_xx->dst_offset_y;
9232 g_em->width = g_xx->width;
9233 g_em->height = g_xx->height;
9234 g_em->unique_identifier = g_xx->unique_identifier;
9237 g_em->preserve_background = TRUE;
9242 for (p = 0; p < MAX_PLAYERS; p++)
9244 for (i = 0; i < PLY_MAX; i++)
9246 int element = player_mapping[p][i].element_rnd;
9247 int action = player_mapping[p][i].action;
9248 int direction = player_mapping[p][i].direction;
9250 for (j = 0; j < 8; j++)
9252 int effective_element = element;
9253 int effective_action = action;
9254 int graphic = (direction == MV_NONE ?
9255 el_act2img(effective_element, effective_action) :
9256 el_act_dir2img(effective_element, effective_action,
9258 struct GraphicInfo *g = &graphic_info[graphic];
9259 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9263 int frame = getAnimationFrame(g->anim_frames,
9266 g->anim_start_frame,
9269 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9271 g_em->bitmap = src_bitmap;
9272 g_em->src_x = src_x;
9273 g_em->src_y = src_y;
9274 g_em->src_offset_x = 0;
9275 g_em->src_offset_y = 0;
9276 g_em->dst_offset_x = 0;
9277 g_em->dst_offset_y = 0;
9278 g_em->width = TILEX;
9279 g_em->height = TILEY;
9285 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9286 boolean any_player_moving,
9287 boolean any_player_snapping,
9288 boolean any_player_dropping)
9290 if (frame == 7 && !any_player_dropping)
9292 if (!local_player->was_waiting)
9294 if (!CheckSaveEngineSnapshotToList())
9297 local_player->was_waiting = TRUE;
9300 else if (any_player_moving || any_player_snapping || any_player_dropping)
9302 local_player->was_waiting = FALSE;
9306 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9307 boolean murphy_is_dropping)
9309 if (murphy_is_waiting)
9311 if (!local_player->was_waiting)
9313 if (!CheckSaveEngineSnapshotToList())
9316 local_player->was_waiting = TRUE;
9321 local_player->was_waiting = FALSE;
9325 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9326 boolean button_released)
9328 if (button_released)
9330 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9331 CheckSaveEngineSnapshotToList();
9333 else if (element_clicked)
9335 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9336 CheckSaveEngineSnapshotToList();
9338 game.snapshot.changed_action = TRUE;
9342 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9343 boolean any_player_moving,
9344 boolean any_player_snapping,
9345 boolean any_player_dropping)
9347 if (tape.single_step && tape.recording && !tape.pausing)
9348 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9349 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9351 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9352 any_player_snapping, any_player_dropping);
9354 return tape.pausing;
9357 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9358 boolean murphy_is_dropping)
9360 boolean murphy_starts_dropping = FALSE;
9363 for (i = 0; i < MAX_PLAYERS; i++)
9364 if (stored_player[i].force_dropping)
9365 murphy_starts_dropping = TRUE;
9367 if (tape.single_step && tape.recording && !tape.pausing)
9368 if (murphy_is_waiting && !murphy_starts_dropping)
9369 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9371 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9374 void CheckSingleStepMode_MM(boolean element_clicked,
9375 boolean button_released)
9377 if (tape.single_step && tape.recording && !tape.pausing)
9378 if (button_released)
9379 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9381 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9384 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9385 int graphic, int sync_frame, int x, int y)
9387 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9389 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9392 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9394 return (IS_NEXT_FRAME(sync_frame, graphic));
9397 int getGraphicInfo_Delay(int graphic)
9399 return graphic_info[graphic].anim_delay;
9402 void PlayMenuSoundExt(int sound)
9404 if (sound == SND_UNDEFINED)
9407 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9408 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9411 if (IS_LOOP_SOUND(sound))
9412 PlaySoundLoop(sound);
9417 void PlayMenuSound(void)
9419 PlayMenuSoundExt(menu.sound[game_status]);
9422 void PlayMenuSoundStereo(int sound, int stereo_position)
9424 if (sound == SND_UNDEFINED)
9427 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9428 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9431 if (IS_LOOP_SOUND(sound))
9432 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9434 PlaySoundStereo(sound, stereo_position);
9437 void PlayMenuSoundIfLoopExt(int sound)
9439 if (sound == SND_UNDEFINED)
9442 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9443 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9446 if (IS_LOOP_SOUND(sound))
9447 PlaySoundLoop(sound);
9450 void PlayMenuSoundIfLoop(void)
9452 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9455 void PlayMenuMusicExt(int music)
9457 if (music == MUS_UNDEFINED)
9460 if (!setup.sound_music)
9463 if (IS_LOOP_MUSIC(music))
9464 PlayMusicLoop(music);
9469 void PlayMenuMusic(void)
9471 char *curr_music = getCurrentlyPlayingMusicFilename();
9472 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9474 if (!strEqual(curr_music, next_music))
9475 PlayMenuMusicExt(menu.music[game_status]);
9478 void PlayMenuSoundsAndMusic(void)
9484 static void FadeMenuSounds(void)
9489 static void FadeMenuMusic(void)
9491 char *curr_music = getCurrentlyPlayingMusicFilename();
9492 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9494 if (!strEqual(curr_music, next_music))
9498 void FadeMenuSoundsAndMusic(void)
9504 void PlaySoundActivating(void)
9507 PlaySound(SND_MENU_ITEM_ACTIVATING);
9511 void PlaySoundSelecting(void)
9514 PlaySound(SND_MENU_ITEM_SELECTING);
9518 void ToggleFullscreenIfNeeded(void)
9520 // if setup and video fullscreen state are already matching, nothing do do
9521 if (setup.fullscreen == video.fullscreen_enabled ||
9522 !video.fullscreen_available)
9525 SDLSetWindowFullscreen(setup.fullscreen);
9527 // set setup value according to successfully changed fullscreen mode
9528 setup.fullscreen = video.fullscreen_enabled;
9531 void ChangeWindowScalingIfNeeded(void)
9533 // if setup and video window scaling are already matching, nothing do do
9534 if (setup.window_scaling_percent == video.window_scaling_percent ||
9535 video.fullscreen_enabled)
9538 SDLSetWindowScaling(setup.window_scaling_percent);
9540 // set setup value according to successfully changed window scaling
9541 setup.window_scaling_percent = video.window_scaling_percent;
9544 void ChangeVsyncModeIfNeeded(void)
9546 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9547 int video_vsync_mode = video.vsync_mode;
9549 // if setup and video vsync mode are already matching, nothing do do
9550 if (setup_vsync_mode == video_vsync_mode)
9553 // if renderer is using OpenGL, vsync mode can directly be changed
9554 SDLSetScreenVsyncMode(setup.vsync_mode);
9556 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9557 if (video.vsync_mode == video_vsync_mode)
9559 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9561 // save backbuffer content which gets lost when re-creating screen
9562 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9564 // force re-creating screen and renderer to set new vsync mode
9565 video.fullscreen_enabled = !setup.fullscreen;
9567 // when creating new renderer, destroy textures linked to old renderer
9568 FreeAllImageTextures(); // needs old renderer to free the textures
9570 // re-create screen and renderer (including change of vsync mode)
9571 ChangeVideoModeIfNeeded(setup.fullscreen);
9573 // set setup value according to successfully changed fullscreen mode
9574 setup.fullscreen = video.fullscreen_enabled;
9576 // restore backbuffer content from temporary backbuffer backup bitmap
9577 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9578 FreeBitmap(tmp_backbuffer);
9580 // update visible window/screen
9581 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9583 // when changing vsync mode, re-create textures for new renderer
9584 InitImageTextures();
9587 // set setup value according to successfully changed vsync mode
9588 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9591 static void JoinRectangles(int *x, int *y, int *width, int *height,
9592 int x2, int y2, int width2, int height2)
9594 // do not join with "off-screen" rectangle
9595 if (x2 == -1 || y2 == -1)
9600 *width = MAX(*width, width2);
9601 *height = MAX(*height, height2);
9604 void SetAnimStatus(int anim_status_new)
9606 if (anim_status_new == GAME_MODE_MAIN)
9607 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9608 else if (anim_status_new == GAME_MODE_NAMES)
9609 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9610 else if (anim_status_new == GAME_MODE_SCORES)
9611 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9613 global.anim_status_next = anim_status_new;
9615 // directly set screen modes that are entered without fading
9616 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9617 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9618 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9619 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9620 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9621 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9622 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9623 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9624 global.anim_status = global.anim_status_next;
9627 void SetGameStatus(int game_status_new)
9629 if (game_status_new != game_status)
9630 game_status_last_screen = game_status;
9632 game_status = game_status_new;
9634 SetAnimStatus(game_status_new);
9637 void SetFontStatus(int game_status_new)
9639 static int last_game_status = -1;
9641 if (game_status_new != -1)
9643 // set game status for font use after storing last game status
9644 last_game_status = game_status;
9645 game_status = game_status_new;
9649 // reset game status after font use from last stored game status
9650 game_status = last_game_status;
9654 void ResetFontStatus(void)
9659 void SetLevelSetInfo(char *identifier, int level_nr)
9661 setString(&levelset.identifier, identifier);
9663 levelset.level_nr = level_nr;
9666 boolean CheckIfAllViewportsHaveChanged(void)
9668 // if game status has not changed, viewports have not changed either
9669 if (game_status == game_status_last)
9672 // check if all viewports have changed with current game status
9674 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9675 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9676 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9677 int new_real_sx = vp_playfield->x;
9678 int new_real_sy = vp_playfield->y;
9679 int new_full_sxsize = vp_playfield->width;
9680 int new_full_sysize = vp_playfield->height;
9681 int new_dx = vp_door_1->x;
9682 int new_dy = vp_door_1->y;
9683 int new_dxsize = vp_door_1->width;
9684 int new_dysize = vp_door_1->height;
9685 int new_vx = vp_door_2->x;
9686 int new_vy = vp_door_2->y;
9687 int new_vxsize = vp_door_2->width;
9688 int new_vysize = vp_door_2->height;
9690 boolean playfield_viewport_has_changed =
9691 (new_real_sx != REAL_SX ||
9692 new_real_sy != REAL_SY ||
9693 new_full_sxsize != FULL_SXSIZE ||
9694 new_full_sysize != FULL_SYSIZE);
9696 boolean door_1_viewport_has_changed =
9699 new_dxsize != DXSIZE ||
9700 new_dysize != DYSIZE);
9702 boolean door_2_viewport_has_changed =
9705 new_vxsize != VXSIZE ||
9706 new_vysize != VYSIZE ||
9707 game_status_last == GAME_MODE_EDITOR);
9709 return (playfield_viewport_has_changed &&
9710 door_1_viewport_has_changed &&
9711 door_2_viewport_has_changed);
9714 boolean CheckFadeAll(void)
9716 return (CheckIfGlobalBorderHasChanged() ||
9717 CheckIfAllViewportsHaveChanged());
9720 void ChangeViewportPropertiesIfNeeded(void)
9722 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9723 FALSE : setup.small_game_graphics);
9724 int gfx_game_mode = game_status;
9725 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9727 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9728 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9729 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9730 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9731 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9732 int new_win_xsize = vp_window->width;
9733 int new_win_ysize = vp_window->height;
9734 int border_left = vp_playfield->border_left;
9735 int border_right = vp_playfield->border_right;
9736 int border_top = vp_playfield->border_top;
9737 int border_bottom = vp_playfield->border_bottom;
9738 int new_sx = vp_playfield->x + border_left;
9739 int new_sy = vp_playfield->y + border_top;
9740 int new_sxsize = vp_playfield->width - border_left - border_right;
9741 int new_sysize = vp_playfield->height - border_top - border_bottom;
9742 int new_real_sx = vp_playfield->x;
9743 int new_real_sy = vp_playfield->y;
9744 int new_full_sxsize = vp_playfield->width;
9745 int new_full_sysize = vp_playfield->height;
9746 int new_dx = vp_door_1->x;
9747 int new_dy = vp_door_1->y;
9748 int new_dxsize = vp_door_1->width;
9749 int new_dysize = vp_door_1->height;
9750 int new_vx = vp_door_2->x;
9751 int new_vy = vp_door_2->y;
9752 int new_vxsize = vp_door_2->width;
9753 int new_vysize = vp_door_2->height;
9754 int new_ex = vp_door_3->x;
9755 int new_ey = vp_door_3->y;
9756 int new_exsize = vp_door_3->width;
9757 int new_eysize = vp_door_3->height;
9758 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9759 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9760 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9761 int new_scr_fieldx = new_sxsize / tilesize;
9762 int new_scr_fieldy = new_sysize / tilesize;
9763 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9764 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9765 boolean init_gfx_buffers = FALSE;
9766 boolean init_video_buffer = FALSE;
9767 boolean init_gadgets_and_anims = FALSE;
9768 boolean init_em_graphics = FALSE;
9770 if (new_win_xsize != WIN_XSIZE ||
9771 new_win_ysize != WIN_YSIZE)
9773 WIN_XSIZE = new_win_xsize;
9774 WIN_YSIZE = new_win_ysize;
9776 init_video_buffer = TRUE;
9777 init_gfx_buffers = TRUE;
9778 init_gadgets_and_anims = TRUE;
9780 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9783 if (new_scr_fieldx != SCR_FIELDX ||
9784 new_scr_fieldy != SCR_FIELDY)
9786 // this always toggles between MAIN and GAME when using small tile size
9788 SCR_FIELDX = new_scr_fieldx;
9789 SCR_FIELDY = new_scr_fieldy;
9791 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9802 new_sxsize != SXSIZE ||
9803 new_sysize != SYSIZE ||
9804 new_dxsize != DXSIZE ||
9805 new_dysize != DYSIZE ||
9806 new_vxsize != VXSIZE ||
9807 new_vysize != VYSIZE ||
9808 new_exsize != EXSIZE ||
9809 new_eysize != EYSIZE ||
9810 new_real_sx != REAL_SX ||
9811 new_real_sy != REAL_SY ||
9812 new_full_sxsize != FULL_SXSIZE ||
9813 new_full_sysize != FULL_SYSIZE ||
9814 new_tilesize_var != TILESIZE_VAR
9817 // ------------------------------------------------------------------------
9818 // determine next fading area for changed viewport definitions
9819 // ------------------------------------------------------------------------
9821 // start with current playfield area (default fading area)
9824 FADE_SXSIZE = FULL_SXSIZE;
9825 FADE_SYSIZE = FULL_SYSIZE;
9827 // add new playfield area if position or size has changed
9828 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9829 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9831 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9832 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9835 // add current and new door 1 area if position or size has changed
9836 if (new_dx != DX || new_dy != DY ||
9837 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9839 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9840 DX, DY, DXSIZE, DYSIZE);
9841 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9842 new_dx, new_dy, new_dxsize, new_dysize);
9845 // add current and new door 2 area if position or size has changed
9846 if (new_vx != VX || new_vy != VY ||
9847 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9849 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9850 VX, VY, VXSIZE, VYSIZE);
9851 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9852 new_vx, new_vy, new_vxsize, new_vysize);
9855 // ------------------------------------------------------------------------
9856 // handle changed tile size
9857 // ------------------------------------------------------------------------
9859 if (new_tilesize_var != TILESIZE_VAR)
9861 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9863 // changing tile size invalidates scroll values of engine snapshots
9864 FreeEngineSnapshotSingle();
9866 // changing tile size requires update of graphic mapping for EM engine
9867 init_em_graphics = TRUE;
9878 SXSIZE = new_sxsize;
9879 SYSIZE = new_sysize;
9880 DXSIZE = new_dxsize;
9881 DYSIZE = new_dysize;
9882 VXSIZE = new_vxsize;
9883 VYSIZE = new_vysize;
9884 EXSIZE = new_exsize;
9885 EYSIZE = new_eysize;
9886 REAL_SX = new_real_sx;
9887 REAL_SY = new_real_sy;
9888 FULL_SXSIZE = new_full_sxsize;
9889 FULL_SYSIZE = new_full_sysize;
9890 TILESIZE_VAR = new_tilesize_var;
9892 init_gfx_buffers = TRUE;
9893 init_gadgets_and_anims = TRUE;
9895 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9896 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9899 if (init_gfx_buffers)
9901 // Debug("tools:viewport", "init_gfx_buffers");
9903 SCR_FIELDX = new_scr_fieldx_buffers;
9904 SCR_FIELDY = new_scr_fieldy_buffers;
9908 SCR_FIELDX = new_scr_fieldx;
9909 SCR_FIELDY = new_scr_fieldy;
9911 SetDrawDeactivationMask(REDRAW_NONE);
9912 SetDrawBackgroundMask(REDRAW_FIELD);
9915 if (init_video_buffer)
9917 // Debug("tools:viewport", "init_video_buffer");
9919 FreeAllImageTextures(); // needs old renderer to free the textures
9921 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9922 InitImageTextures();
9925 if (init_gadgets_and_anims)
9927 // Debug("tools:viewport", "init_gadgets_and_anims");
9930 InitGlobalAnimations();
9933 if (init_em_graphics)
9935 InitGraphicInfo_EM();
9940 // ============================================================================
9942 // ============================================================================
9944 #if defined(PLATFORM_WIN32)
9945 /* FILETIME of Jan 1 1970 00:00:00. */
9946 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9949 * timezone information is stored outside the kernel so tzp isn't used anymore.
9951 * Note: this function is not for Win32 high precision timing purpose. See
9954 int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9957 SYSTEMTIME system_time;
9958 ULARGE_INTEGER ularge;
9960 GetSystemTime(&system_time);
9961 SystemTimeToFileTime(&system_time, &file_time);
9962 ularge.LowPart = file_time.dwLowDateTime;
9963 ularge.HighPart = file_time.dwHighDateTime;
9965 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9966 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9972 static char *test_init_uuid_random_function_simple(void)
9974 static char seed_text[100];
9975 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9977 sprintf(seed_text, "%d", seed);
9982 static char *test_init_uuid_random_function_better(void)
9984 static char seed_text[100];
9985 struct timeval current_time;
9987 gettimeofday(¤t_time, NULL);
9989 prng_seed_bytes(¤t_time, sizeof(current_time));
9991 sprintf(seed_text, "%ld.%ld",
9992 (long)current_time.tv_sec,
9993 (long)current_time.tv_usec);
9998 #if defined(PLATFORM_WIN32)
9999 static char *test_init_uuid_random_function_better_windows(void)
10001 static char seed_text[100];
10002 struct timeval current_time;
10004 gettimeofday_windows(¤t_time, NULL);
10006 prng_seed_bytes(¤t_time, sizeof(current_time));
10008 sprintf(seed_text, "%ld.%ld",
10009 (long)current_time.tv_sec,
10010 (long)current_time.tv_usec);
10016 static unsigned int test_uuid_random_function_simple(int max)
10018 return GetSimpleRandom(max);
10021 static unsigned int test_uuid_random_function_better(int max)
10023 return (max > 0 ? prng_get_uint() % max : 0);
10026 #if defined(PLATFORM_WIN32)
10027 #define NUM_UUID_TESTS 3
10029 #define NUM_UUID_TESTS 2
10032 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10034 struct hashtable *hash_seeds =
10035 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10036 struct hashtable *hash_uuids =
10037 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10038 static char message[100];
10041 char *random_name = (nr == 0 ? "simple" : "better");
10042 char *random_type = (always_seed ? "always" : "only once");
10043 char *(*init_random_function)(void) =
10045 test_init_uuid_random_function_simple :
10046 test_init_uuid_random_function_better);
10047 unsigned int (*random_function)(int) =
10049 test_uuid_random_function_simple :
10050 test_uuid_random_function_better);
10053 #if defined(PLATFORM_WIN32)
10056 random_name = "windows";
10057 init_random_function = test_init_uuid_random_function_better_windows;
10063 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10064 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10066 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10067 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10068 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10070 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10074 // always initialize random number generator at least once
10075 init_random_function();
10077 unsigned int time_start = SDL_GetTicks();
10079 for (i = 0; i < num_uuids; i++)
10083 char *seed = getStringCopy(init_random_function());
10085 hashtable_remove(hash_seeds, seed);
10086 hashtable_insert(hash_seeds, seed, "1");
10089 char *uuid = getStringCopy(getUUIDExt(random_function));
10091 hashtable_remove(hash_uuids, uuid);
10092 hashtable_insert(hash_uuids, uuid, "1");
10095 int num_unique_seeds = hashtable_count(hash_seeds);
10096 int num_unique_uuids = hashtable_count(hash_uuids);
10098 unsigned int time_needed = SDL_GetTicks() - time_start;
10100 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10102 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10105 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10107 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10108 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10110 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10112 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10114 Request(message, REQ_CONFIRM);
10116 hashtable_destroy(hash_seeds, 0);
10117 hashtable_destroy(hash_uuids, 0);
10120 void TestGeneratingUUIDs(void)
10122 int num_uuids = 1000000;
10125 for (i = 0; i < NUM_UUID_TESTS; i++)
10126 for (j = 0; j < 2; j++)
10127 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10129 CloseAllAndExit(0);