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 return getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1489 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1491 struct GraphicInfo *g = &graphic_info[graphic];
1492 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1494 if (tilesize == gfx.standard_tile_size)
1495 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1496 else if (tilesize == game.tile_size)
1497 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1499 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1502 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1503 boolean get_backside)
1505 struct GraphicInfo *g = &graphic_info[graphic];
1506 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1507 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1509 if (g->offset_y == 0) // frames are ordered horizontally
1511 int max_width = g->anim_frames_per_line * g->width;
1512 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1514 *x = pos % max_width;
1515 *y = src_y % g->height + pos / max_width * g->height;
1517 else if (g->offset_x == 0) // frames are ordered vertically
1519 int max_height = g->anim_frames_per_line * g->height;
1520 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1522 *x = src_x % g->width + pos / max_height * g->width;
1523 *y = pos % max_height;
1525 else // frames are ordered diagonally
1527 *x = src_x + frame * g->offset_x;
1528 *y = src_y + frame * g->offset_y;
1532 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1533 Bitmap **bitmap, int *x, int *y,
1534 boolean get_backside)
1536 struct GraphicInfo *g = &graphic_info[graphic];
1538 // if no graphics defined at all, use fallback graphics
1539 if (g->bitmaps == NULL)
1540 *g = graphic_info[IMG_CHAR_EXCLAM];
1542 // if no in-game graphics defined, always use standard graphic size
1543 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1544 tilesize = TILESIZE;
1546 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1547 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1549 *x = *x * tilesize / g->tile_size;
1550 *y = *y * tilesize / g->tile_size;
1553 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1554 Bitmap **bitmap, int *x, int *y)
1556 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1559 void getFixedGraphicSource(int graphic, int frame,
1560 Bitmap **bitmap, int *x, int *y)
1562 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1565 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1567 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1570 void getGlobalAnimGraphicSource(int graphic, int frame,
1571 Bitmap **bitmap, int *x, int *y)
1573 struct GraphicInfo *g = &graphic_info[graphic];
1575 // if no graphics defined at all, use fallback graphics
1576 if (g->bitmaps == NULL)
1577 *g = graphic_info[IMG_CHAR_EXCLAM];
1579 // use original size graphics, if existing, else use standard size graphics
1580 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1581 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1583 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1585 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1588 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1589 int *x, int *y, boolean get_backside)
1591 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1595 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1597 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1600 void DrawGraphic(int x, int y, int graphic, int frame)
1603 if (!IN_SCR_FIELD(x, y))
1605 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1606 Debug("draw:DrawGraphic", "This should never happen!");
1612 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1615 MarkTileDirty(x, y);
1618 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1621 if (!IN_SCR_FIELD(x, y))
1623 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1625 Debug("draw:DrawFixedGraphic", "This should never happen!");
1631 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1633 MarkTileDirty(x, y);
1636 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1642 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1644 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1647 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1653 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1654 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1657 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1660 if (!IN_SCR_FIELD(x, y))
1662 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1664 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1670 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1673 MarkTileDirty(x, y);
1676 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1679 if (!IN_SCR_FIELD(x, y))
1681 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1683 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1689 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1691 MarkTileDirty(x, y);
1694 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1700 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1702 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1706 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1707 int graphic, int frame)
1712 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1714 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1718 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1720 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1722 MarkTileDirty(x / tilesize, y / tilesize);
1725 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1728 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1729 graphic, frame, tilesize);
1730 MarkTileDirty(x / tilesize, y / tilesize);
1733 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1739 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1740 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1743 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1744 int frame, int tilesize)
1749 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1750 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1753 void DrawMiniGraphic(int x, int y, int graphic)
1755 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1756 MarkTileDirty(x / 2, y / 2);
1759 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1764 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1765 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1768 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1769 int graphic, int frame,
1770 int cut_mode, int mask_mode)
1775 int width = TILEX, height = TILEY;
1778 if (dx || dy) // shifted graphic
1780 if (x < BX1) // object enters playfield from the left
1787 else if (x > BX2) // object enters playfield from the right
1793 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1799 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1801 else if (dx) // general horizontal movement
1802 MarkTileDirty(x + SIGN(dx), y);
1804 if (y < BY1) // object enters playfield from the top
1806 if (cut_mode == CUT_BELOW) // object completely above top border
1814 else if (y > BY2) // object enters playfield from the bottom
1820 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1826 else if (dy > 0 && cut_mode == CUT_ABOVE)
1828 if (y == BY2) // object completely above bottom border
1834 MarkTileDirty(x, y + 1);
1835 } // object leaves playfield to the bottom
1836 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1838 else if (dy) // general vertical movement
1839 MarkTileDirty(x, y + SIGN(dy));
1843 if (!IN_SCR_FIELD(x, y))
1845 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1847 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1853 width = width * TILESIZE_VAR / TILESIZE;
1854 height = height * TILESIZE_VAR / TILESIZE;
1855 cx = cx * TILESIZE_VAR / TILESIZE;
1856 cy = cy * TILESIZE_VAR / TILESIZE;
1857 dx = dx * TILESIZE_VAR / TILESIZE;
1858 dy = dy * TILESIZE_VAR / TILESIZE;
1860 if (width > 0 && height > 0)
1862 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1867 dst_x = FX + x * TILEX_VAR + dx;
1868 dst_y = FY + y * TILEY_VAR + dy;
1870 if (mask_mode == USE_MASKING)
1871 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1874 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1877 MarkTileDirty(x, y);
1881 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1882 int graphic, int frame,
1883 int cut_mode, int mask_mode)
1888 int width = TILEX_VAR, height = TILEY_VAR;
1891 int x2 = x + SIGN(dx);
1892 int y2 = y + SIGN(dy);
1894 // movement with two-tile animations must be sync'ed with movement position,
1895 // not with current GfxFrame (which can be higher when using slow movement)
1896 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1897 int anim_frames = graphic_info[graphic].anim_frames;
1899 // (we also need anim_delay here for movement animations with less frames)
1900 int anim_delay = graphic_info[graphic].anim_delay;
1901 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1903 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1904 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1906 // re-calculate animation frame for two-tile movement animation
1907 frame = getGraphicAnimationFrame(graphic, sync_frame);
1909 // check if movement start graphic inside screen area and should be drawn
1910 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1912 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1914 dst_x = FX + x1 * TILEX_VAR;
1915 dst_y = FY + y1 * TILEY_VAR;
1917 if (mask_mode == USE_MASKING)
1918 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1921 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1924 MarkTileDirty(x1, y1);
1927 // check if movement end graphic inside screen area and should be drawn
1928 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1930 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1932 dst_x = FX + x2 * TILEX_VAR;
1933 dst_y = FY + y2 * TILEY_VAR;
1935 if (mask_mode == USE_MASKING)
1936 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1939 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1942 MarkTileDirty(x2, y2);
1946 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1947 int graphic, int frame,
1948 int cut_mode, int mask_mode)
1952 DrawGraphic(x, y, graphic, frame);
1957 if (graphic_info[graphic].double_movement) // EM style movement images
1958 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1960 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1963 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1964 int graphic, int frame, int cut_mode)
1966 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1969 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1970 int cut_mode, int mask_mode)
1972 int lx = LEVELX(x), ly = LEVELY(y);
1976 if (IN_LEV_FIELD(lx, ly))
1978 SetRandomAnimationValue(lx, ly);
1980 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1981 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
1983 // do not use double (EM style) movement graphic when not moving
1984 if (graphic_info[graphic].double_movement && !dx && !dy)
1986 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1987 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
1990 if (game.use_masked_elements && (dx || dy))
1991 mask_mode = USE_MASKING;
1993 else // border element
1995 graphic = el2img(element);
1996 frame = getGraphicAnimationFrame(graphic, -1);
1999 if (element == EL_EXPANDABLE_WALL)
2001 boolean left_stopped = FALSE, right_stopped = FALSE;
2003 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2004 left_stopped = TRUE;
2005 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2006 right_stopped = TRUE;
2008 if (left_stopped && right_stopped)
2010 else if (left_stopped)
2012 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2013 frame = graphic_info[graphic].anim_frames - 1;
2015 else if (right_stopped)
2017 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2018 frame = graphic_info[graphic].anim_frames - 1;
2023 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2024 else if (mask_mode == USE_MASKING)
2025 DrawGraphicThruMask(x, y, graphic, frame);
2027 DrawGraphic(x, y, graphic, frame);
2030 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2031 int cut_mode, int mask_mode)
2033 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2034 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2035 cut_mode, mask_mode);
2038 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2041 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2044 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2047 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2050 void DrawLevelElementThruMask(int x, int y, int element)
2052 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2055 void DrawLevelFieldThruMask(int x, int y)
2057 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2060 // !!! implementation of quicksand is totally broken !!!
2061 #define IS_CRUMBLED_TILE(x, y, e) \
2062 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2063 !IS_MOVING(x, y) || \
2064 (e) == EL_QUICKSAND_EMPTYING || \
2065 (e) == EL_QUICKSAND_FAST_EMPTYING))
2067 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2072 int width, height, cx, cy;
2073 int sx = SCREENX(x), sy = SCREENY(y);
2074 int crumbled_border_size = graphic_info[graphic].border_size;
2075 int crumbled_tile_size = graphic_info[graphic].tile_size;
2076 int crumbled_border_size_var =
2077 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2080 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2082 for (i = 1; i < 4; i++)
2084 int dxx = (i & 1 ? dx : 0);
2085 int dyy = (i & 2 ? dy : 0);
2088 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2091 // check if neighbour field is of same crumble type
2092 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2093 graphic_info[graphic].class ==
2094 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2096 // return if check prevents inner corner
2097 if (same == (dxx == dx && dyy == dy))
2101 // if we reach this point, we have an inner corner
2103 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2105 width = crumbled_border_size_var;
2106 height = crumbled_border_size_var;
2107 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2108 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2110 if (game.use_masked_elements)
2112 int graphic0 = el2img(EL_EMPTY);
2113 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2114 Bitmap *src_bitmap0;
2117 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2119 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2121 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2123 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2125 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2128 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2130 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2133 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2138 int width, height, bx, by, cx, cy;
2139 int sx = SCREENX(x), sy = SCREENY(y);
2140 int crumbled_border_size = graphic_info[graphic].border_size;
2141 int crumbled_tile_size = graphic_info[graphic].tile_size;
2142 int crumbled_border_size_var =
2143 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2144 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2147 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2149 // only needed when using masked elements
2150 int graphic0 = el2img(EL_EMPTY);
2151 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2152 Bitmap *src_bitmap0;
2155 if (game.use_masked_elements)
2156 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2158 // draw simple, sloppy, non-corner-accurate crumbled border
2160 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2161 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2162 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2163 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2165 if (game.use_masked_elements)
2167 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2169 FX + sx * TILEX_VAR + cx,
2170 FY + sy * TILEY_VAR + cy);
2172 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2174 FX + sx * TILEX_VAR + cx,
2175 FY + sy * TILEY_VAR + cy);
2178 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2180 FX + sx * TILEX_VAR + cx,
2181 FY + sy * TILEY_VAR + cy);
2183 // (remaining middle border part must be at least as big as corner part)
2184 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2185 crumbled_border_size_var >= TILESIZE_VAR / 3)
2188 // correct corners of crumbled border, if needed
2190 for (i = -1; i <= 1; i += 2)
2192 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2193 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2194 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2197 // check if neighbour field is of same crumble type
2198 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2199 graphic_info[graphic].class ==
2200 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2202 // no crumbled corner, but continued crumbled border
2204 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2205 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2206 int b1 = (i == 1 ? crumbled_border_size_var :
2207 TILESIZE_VAR - 2 * crumbled_border_size_var);
2209 width = crumbled_border_size_var;
2210 height = crumbled_border_size_var;
2212 if (dir == 1 || dir == 2)
2227 if (game.use_masked_elements)
2229 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2231 FX + sx * TILEX_VAR + cx,
2232 FY + sy * TILEY_VAR + cy);
2234 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2236 FX + sx * TILEX_VAR + cx,
2237 FY + sy * TILEY_VAR + cy);
2240 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2242 FX + sx * TILEX_VAR + cx,
2243 FY + sy * TILEY_VAR + cy);
2248 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2250 int sx = SCREENX(x), sy = SCREENY(y);
2253 static int xy[4][2] =
2261 if (!IN_LEV_FIELD(x, y))
2264 element = TILE_GFX_ELEMENT(x, y);
2266 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2268 if (!IN_SCR_FIELD(sx, sy))
2271 // crumble field borders towards direct neighbour fields
2272 for (i = 0; i < 4; i++)
2274 int xx = x + xy[i][0];
2275 int yy = y + xy[i][1];
2277 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2280 // check if neighbour field is of same crumble type
2281 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2282 graphic_info[graphic].class ==
2283 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2286 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2289 // crumble inner field corners towards corner neighbour fields
2290 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2291 graphic_info[graphic].anim_frames == 2)
2293 for (i = 0; i < 4; i++)
2295 int dx = (i & 1 ? +1 : -1);
2296 int dy = (i & 2 ? +1 : -1);
2298 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2302 MarkTileDirty(sx, sy);
2304 else // center field is not crumbled -- crumble neighbour fields
2306 // crumble field borders of direct neighbour fields
2307 for (i = 0; i < 4; i++)
2309 int xx = x + xy[i][0];
2310 int yy = y + xy[i][1];
2311 int sxx = sx + xy[i][0];
2312 int syy = sy + xy[i][1];
2314 if (!IN_LEV_FIELD(xx, yy) ||
2315 !IN_SCR_FIELD(sxx, syy))
2318 // do not crumble fields that are being digged or snapped
2319 if (Tile[xx][yy] == EL_EMPTY ||
2320 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2323 element = TILE_GFX_ELEMENT(xx, yy);
2325 if (!IS_CRUMBLED_TILE(xx, yy, element))
2328 graphic = el_act2crm(element, ACTION_DEFAULT);
2330 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2332 MarkTileDirty(sxx, syy);
2335 // crumble inner field corners of corner neighbour fields
2336 for (i = 0; i < 4; i++)
2338 int dx = (i & 1 ? +1 : -1);
2339 int dy = (i & 2 ? +1 : -1);
2345 if (!IN_LEV_FIELD(xx, yy) ||
2346 !IN_SCR_FIELD(sxx, syy))
2349 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2352 element = TILE_GFX_ELEMENT(xx, yy);
2354 if (!IS_CRUMBLED_TILE(xx, yy, element))
2357 graphic = el_act2crm(element, ACTION_DEFAULT);
2359 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2360 graphic_info[graphic].anim_frames == 2)
2361 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2363 MarkTileDirty(sxx, syy);
2368 void DrawLevelFieldCrumbled(int x, int y)
2372 if (!IN_LEV_FIELD(x, y))
2375 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2376 GfxElement[x][y] != EL_UNDEFINED &&
2377 GFX_CRUMBLED(GfxElement[x][y]))
2379 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2384 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2386 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2389 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2392 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2393 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2394 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2395 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2396 int sx = SCREENX(x), sy = SCREENY(y);
2398 DrawScreenGraphic(sx, sy, graphic1, frame1);
2399 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2402 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2404 int sx = SCREENX(x), sy = SCREENY(y);
2405 static int xy[4][2] =
2414 // crumble direct neighbour fields (required for field borders)
2415 for (i = 0; i < 4; i++)
2417 int xx = x + xy[i][0];
2418 int yy = y + xy[i][1];
2419 int sxx = sx + xy[i][0];
2420 int syy = sy + xy[i][1];
2422 if (!IN_LEV_FIELD(xx, yy) ||
2423 !IN_SCR_FIELD(sxx, syy) ||
2424 !GFX_CRUMBLED(Tile[xx][yy]) ||
2428 DrawLevelField(xx, yy);
2431 // crumble corner neighbour fields (required for inner field corners)
2432 for (i = 0; i < 4; i++)
2434 int dx = (i & 1 ? +1 : -1);
2435 int dy = (i & 2 ? +1 : -1);
2441 if (!IN_LEV_FIELD(xx, yy) ||
2442 !IN_SCR_FIELD(sxx, syy) ||
2443 !GFX_CRUMBLED(Tile[xx][yy]) ||
2447 int element = TILE_GFX_ELEMENT(xx, yy);
2448 int graphic = el_act2crm(element, ACTION_DEFAULT);
2450 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2451 graphic_info[graphic].anim_frames == 2)
2452 DrawLevelField(xx, yy);
2456 static int getBorderElement(int x, int y)
2460 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2461 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2462 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2463 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2464 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2465 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2466 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2468 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2469 int steel_position = (x == -1 && y == -1 ? 0 :
2470 x == lev_fieldx && y == -1 ? 1 :
2471 x == -1 && y == lev_fieldy ? 2 :
2472 x == lev_fieldx && y == lev_fieldy ? 3 :
2473 x == -1 || x == lev_fieldx ? 4 :
2474 y == -1 || y == lev_fieldy ? 5 : 6);
2476 return border[steel_position][steel_type];
2479 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2481 if (game.use_masked_elements)
2483 if (graphic != el2img(EL_EMPTY))
2484 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2486 DrawGraphicThruMask(x, y, graphic, frame);
2490 DrawGraphic(x, y, graphic, frame);
2494 void DrawScreenElement(int x, int y, int element)
2496 int mask_mode = NO_MASKING;
2498 if (game.use_masked_elements)
2500 int lx = LEVELX(x), ly = LEVELY(y);
2502 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2504 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2506 mask_mode = USE_MASKING;
2510 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2511 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2514 void DrawLevelElement(int x, int y, int element)
2516 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2517 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2520 void DrawScreenField(int x, int y)
2522 int lx = LEVELX(x), ly = LEVELY(y);
2523 int element, content;
2525 if (!IN_LEV_FIELD(lx, ly))
2527 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2530 element = getBorderElement(lx, ly);
2532 DrawScreenElement(x, y, element);
2537 element = Tile[lx][ly];
2538 content = Store[lx][ly];
2540 if (IS_MOVING(lx, ly))
2542 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2543 boolean cut_mode = NO_CUTTING;
2545 if (element == EL_QUICKSAND_EMPTYING ||
2546 element == EL_QUICKSAND_FAST_EMPTYING ||
2547 element == EL_MAGIC_WALL_EMPTYING ||
2548 element == EL_BD_MAGIC_WALL_EMPTYING ||
2549 element == EL_DC_MAGIC_WALL_EMPTYING ||
2550 element == EL_AMOEBA_DROPPING)
2551 cut_mode = CUT_ABOVE;
2552 else if (element == EL_QUICKSAND_FILLING ||
2553 element == EL_QUICKSAND_FAST_FILLING ||
2554 element == EL_MAGIC_WALL_FILLING ||
2555 element == EL_BD_MAGIC_WALL_FILLING ||
2556 element == EL_DC_MAGIC_WALL_FILLING)
2557 cut_mode = CUT_BELOW;
2559 if (cut_mode == CUT_ABOVE)
2560 DrawScreenElement(x, y, element);
2562 DrawScreenElement(x, y, EL_EMPTY);
2564 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2566 int dir = MovDir[lx][ly];
2567 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2568 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2570 if (IN_SCR_FIELD(newx, newy))
2571 DrawScreenElement(newx, newy, EL_EMPTY);
2575 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2576 else if (cut_mode == NO_CUTTING)
2577 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2580 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2582 if (cut_mode == CUT_BELOW &&
2583 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2584 DrawLevelElement(lx, ly + 1, element);
2587 if (content == EL_ACID)
2589 int dir = MovDir[lx][ly];
2590 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2591 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2593 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2595 // prevent target field from being drawn again (but without masking)
2596 // (this would happen if target field is scanned after moving element)
2597 Stop[newlx][newly] = TRUE;
2600 else if (IS_BLOCKED(lx, ly))
2605 boolean cut_mode = NO_CUTTING;
2606 int element_old, content_old;
2608 Blocked2Moving(lx, ly, &oldx, &oldy);
2611 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2612 MovDir[oldx][oldy] == MV_RIGHT);
2614 element_old = Tile[oldx][oldy];
2615 content_old = Store[oldx][oldy];
2617 if (element_old == EL_QUICKSAND_EMPTYING ||
2618 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2619 element_old == EL_MAGIC_WALL_EMPTYING ||
2620 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2621 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2622 element_old == EL_AMOEBA_DROPPING)
2623 cut_mode = CUT_ABOVE;
2625 DrawScreenElement(x, y, EL_EMPTY);
2628 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2630 else if (cut_mode == NO_CUTTING)
2631 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2634 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2637 else if (IS_DRAWABLE(element))
2638 DrawScreenElement(x, y, element);
2640 DrawScreenElement(x, y, EL_EMPTY);
2643 void DrawLevelField(int x, int y)
2645 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2646 DrawScreenField(SCREENX(x), SCREENY(y));
2647 else if (IS_MOVING(x, y))
2651 Moving2Blocked(x, y, &newx, &newy);
2652 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2653 DrawScreenField(SCREENX(newx), SCREENY(newy));
2655 else if (IS_BLOCKED(x, y))
2659 Blocked2Moving(x, y, &oldx, &oldy);
2660 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2661 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2665 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2666 int (*el2img_function)(int), boolean masked,
2667 int element_bits_draw)
2669 int element_base = map_mm_wall_element(element);
2670 int element_bits = (IS_DF_WALL(element) ?
2671 element - EL_DF_WALL_START :
2672 IS_MM_WALL(element) ?
2673 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2674 int graphic = el2img_function(element_base);
2675 int tilesize_draw = tilesize / 2;
2680 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2682 for (i = 0; i < 4; i++)
2684 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2685 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2687 if (!(element_bits_draw & (1 << i)))
2690 if (element_bits & (1 << i))
2693 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2694 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2696 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2697 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2702 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2703 tilesize_draw, tilesize_draw);
2708 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2709 boolean masked, int element_bits_draw)
2711 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2712 element, tilesize, el2edimg, masked, element_bits_draw);
2715 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2716 int (*el2img_function)(int))
2718 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2722 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2725 if (IS_MM_WALL(element))
2727 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2728 element, tilesize, el2edimg, masked, 0x000f);
2732 int graphic = el2edimg(element);
2735 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2737 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2741 void DrawSizedElement(int x, int y, int element, int tilesize)
2743 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2746 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2748 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2751 void DrawMiniElement(int x, int y, int element)
2755 graphic = el2edimg(element);
2756 DrawMiniGraphic(x, y, graphic);
2759 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2762 int x = sx + scroll_x, y = sy + scroll_y;
2764 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2765 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2766 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2767 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2769 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2772 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2774 int x = sx + scroll_x, y = sy + scroll_y;
2776 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2777 DrawMiniElement(sx, sy, EL_EMPTY);
2778 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2779 DrawMiniElement(sx, sy, Tile[x][y]);
2781 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2784 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2785 int x, int y, int xsize, int ysize,
2786 int tile_width, int tile_height)
2790 int dst_x = startx + x * tile_width;
2791 int dst_y = starty + y * tile_height;
2792 int width = graphic_info[graphic].width;
2793 int height = graphic_info[graphic].height;
2794 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2795 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2796 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2797 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2798 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2799 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2800 boolean draw_masked = graphic_info[graphic].draw_masked;
2802 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2804 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2806 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2810 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2811 inner_sx + (x - 1) * tile_width % inner_width);
2812 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2813 inner_sy + (y - 1) * tile_height % inner_height);
2816 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2819 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2823 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2824 int x, int y, int xsize, int ysize,
2827 int font_width = getFontWidth(font_nr);
2828 int font_height = getFontHeight(font_nr);
2830 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2831 font_width, font_height);
2834 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2836 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2837 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2838 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2839 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2840 boolean no_delay = (tape.warp_forward);
2841 unsigned int anim_delay = 0;
2842 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2843 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2844 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2845 int font_width = getFontWidth(font_nr);
2846 int font_height = getFontHeight(font_nr);
2847 int max_xsize = level.envelope[envelope_nr].xsize;
2848 int max_ysize = level.envelope[envelope_nr].ysize;
2849 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2850 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2851 int xend = max_xsize;
2852 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2853 int xstep = (xstart < xend ? 1 : 0);
2854 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2856 int end = MAX(xend - xstart, yend - ystart);
2859 for (i = start; i <= end; i++)
2861 int last_frame = end; // last frame of this "for" loop
2862 int x = xstart + i * xstep;
2863 int y = ystart + i * ystep;
2864 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2865 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2866 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2867 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2870 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2872 BlitScreenToBitmap(backbuffer);
2874 SetDrawtoField(DRAW_TO_BACKBUFFER);
2876 for (yy = 0; yy < ysize; yy++)
2877 for (xx = 0; xx < xsize; xx++)
2878 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2880 DrawTextBuffer(sx + font_width, sy + font_height,
2881 level.envelope[envelope_nr].text, font_nr, max_xsize,
2882 xsize - 2, ysize - 2, 0, mask_mode,
2883 level.envelope[envelope_nr].autowrap,
2884 level.envelope[envelope_nr].centered, FALSE);
2886 redraw_mask |= REDRAW_FIELD;
2889 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2892 ClearAutoRepeatKeyEvents();
2895 void ShowEnvelope(int envelope_nr)
2897 int element = EL_ENVELOPE_1 + envelope_nr;
2898 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2899 int sound_opening = element_info[element].sound[ACTION_OPENING];
2900 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2901 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2902 boolean no_delay = (tape.warp_forward);
2903 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2904 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2905 int anim_mode = graphic_info[graphic].anim_mode;
2906 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2907 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2908 boolean overlay_enabled = GetOverlayEnabled();
2910 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2912 SetOverlayEnabled(FALSE);
2915 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2917 if (anim_mode == ANIM_DEFAULT)
2918 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2920 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2923 Delay_WithScreenUpdates(wait_delay_value);
2925 WaitForEventToContinue();
2928 SetOverlayEnabled(overlay_enabled);
2930 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2932 if (anim_mode != ANIM_NONE)
2933 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2935 if (anim_mode == ANIM_DEFAULT)
2936 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2938 game.envelope_active = FALSE;
2940 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2942 redraw_mask |= REDRAW_FIELD;
2946 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2947 int xsize, int ysize)
2949 if (!global.use_envelope_request ||
2950 request.sort_priority <= 0)
2953 if (request.bitmap == NULL ||
2954 xsize > request.xsize ||
2955 ysize > request.ysize)
2957 if (request.bitmap != NULL)
2958 FreeBitmap(request.bitmap);
2960 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2962 SDL_Surface *surface = request.bitmap->surface;
2964 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2965 Fail("SDLGetNativeSurface() failed");
2968 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2970 SDLFreeBitmapTextures(request.bitmap);
2971 SDLCreateBitmapTextures(request.bitmap);
2973 // set envelope request run-time values
2976 request.xsize = xsize;
2977 request.ysize = ysize;
2980 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2982 if (global.use_envelope_request &&
2983 game.request_active_or_moving &&
2984 request.sort_priority > 0 &&
2985 drawing_target == DRAW_TO_SCREEN &&
2986 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2988 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2989 request.sx, request.sy);
2993 static void setRequestBasePosition(int *x, int *y)
2995 int sx_base, sy_base;
2997 if (request.x != -1)
2998 sx_base = request.x;
2999 else if (request.align == ALIGN_LEFT)
3001 else if (request.align == ALIGN_RIGHT)
3002 sx_base = SX + SXSIZE;
3004 sx_base = SX + SXSIZE / 2;
3006 if (request.y != -1)
3007 sy_base = request.y;
3008 else if (request.valign == VALIGN_TOP)
3010 else if (request.valign == VALIGN_BOTTOM)
3011 sy_base = SY + SYSIZE;
3013 sy_base = SY + SYSIZE / 2;
3019 static void setRequestPositionExt(int *x, int *y, int width, int height,
3020 boolean add_border_size)
3022 int border_size = request.border_size;
3023 int sx_base, sy_base;
3026 setRequestBasePosition(&sx_base, &sy_base);
3028 if (request.align == ALIGN_LEFT)
3030 else if (request.align == ALIGN_RIGHT)
3031 sx = sx_base - width;
3033 sx = sx_base - width / 2;
3035 if (request.valign == VALIGN_TOP)
3037 else if (request.valign == VALIGN_BOTTOM)
3038 sy = sy_base - height;
3040 sy = sy_base - height / 2;
3042 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3043 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3045 if (add_border_size)
3055 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3057 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3060 static void DrawEnvelopeRequest(char *text)
3062 char *text_final = text;
3063 char *text_door_style = NULL;
3064 int graphic = IMG_BACKGROUND_REQUEST;
3065 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3066 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3067 int font_nr = FONT_REQUEST;
3068 int font_width = getFontWidth(font_nr);
3069 int font_height = getFontHeight(font_nr);
3070 int border_size = request.border_size;
3071 int line_spacing = request.line_spacing;
3072 int line_height = font_height + line_spacing;
3073 int max_text_width = request.width - 2 * border_size;
3074 int max_text_height = request.height - 2 * border_size;
3075 int line_length = max_text_width / font_width;
3076 int max_lines = max_text_height / line_height;
3077 int text_width = line_length * font_width;
3078 int width = request.width;
3079 int height = request.height;
3080 int tile_size = MAX(request.step_offset, 1);
3081 int x_steps = width / tile_size;
3082 int y_steps = height / tile_size;
3083 int sx_offset = border_size;
3084 int sy_offset = border_size;
3088 if (request.centered)
3089 sx_offset = (request.width - text_width) / 2;
3091 if (request.wrap_single_words && !request.autowrap)
3093 char *src_text_ptr, *dst_text_ptr;
3095 text_door_style = checked_malloc(2 * strlen(text) + 1);
3097 src_text_ptr = text;
3098 dst_text_ptr = text_door_style;
3100 while (*src_text_ptr)
3102 if (*src_text_ptr == ' ' ||
3103 *src_text_ptr == '?' ||
3104 *src_text_ptr == '!')
3105 *dst_text_ptr++ = '\n';
3107 if (*src_text_ptr != ' ')
3108 *dst_text_ptr++ = *src_text_ptr;
3113 *dst_text_ptr = '\0';
3115 text_final = text_door_style;
3118 setRequestPosition(&sx, &sy, FALSE);
3120 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3122 for (y = 0; y < y_steps; y++)
3123 for (x = 0; x < x_steps; x++)
3124 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3125 x, y, x_steps, y_steps,
3126 tile_size, tile_size);
3128 // force DOOR font inside door area
3129 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3131 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3132 line_length, -1, max_lines, line_spacing, mask_mode,
3133 request.autowrap, request.centered, FALSE);
3137 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3138 RedrawGadget(tool_gadget[i]);
3140 // store readily prepared envelope request for later use when animating
3141 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3143 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3145 if (text_door_style)
3146 free(text_door_style);
3149 static void AnimateEnvelopeRequest(int anim_mode, int action)
3151 int graphic = IMG_BACKGROUND_REQUEST;
3152 boolean draw_masked = graphic_info[graphic].draw_masked;
3153 int delay_value_normal = request.step_delay;
3154 int delay_value_fast = delay_value_normal / 2;
3155 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3156 boolean no_delay = (tape.warp_forward);
3157 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3158 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3159 unsigned int anim_delay = 0;
3161 int tile_size = MAX(request.step_offset, 1);
3162 int max_xsize = request.width / tile_size;
3163 int max_ysize = request.height / tile_size;
3164 int max_xsize_inner = max_xsize - 2;
3165 int max_ysize_inner = max_ysize - 2;
3167 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3168 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3169 int xend = max_xsize_inner;
3170 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3171 int xstep = (xstart < xend ? 1 : 0);
3172 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3174 int end = MAX(xend - xstart, yend - ystart);
3177 if (setup.quick_doors)
3184 for (i = start; i <= end; i++)
3186 int last_frame = end; // last frame of this "for" loop
3187 int x = xstart + i * xstep;
3188 int y = ystart + i * ystep;
3189 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3190 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3191 int xsize_size_left = (xsize - 1) * tile_size;
3192 int ysize_size_top = (ysize - 1) * tile_size;
3193 int max_xsize_pos = (max_xsize - 1) * tile_size;
3194 int max_ysize_pos = (max_ysize - 1) * tile_size;
3195 int width = xsize * tile_size;
3196 int height = ysize * tile_size;
3201 setRequestPosition(&src_x, &src_y, FALSE);
3202 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3204 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3206 for (yy = 0; yy < 2; yy++)
3208 for (xx = 0; xx < 2; xx++)
3210 int src_xx = src_x + xx * max_xsize_pos;
3211 int src_yy = src_y + yy * max_ysize_pos;
3212 int dst_xx = dst_x + xx * xsize_size_left;
3213 int dst_yy = dst_y + yy * ysize_size_top;
3214 int xx_size = (xx ? tile_size : xsize_size_left);
3215 int yy_size = (yy ? tile_size : ysize_size_top);
3218 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3219 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3221 BlitBitmap(bitmap_db_store_2, backbuffer,
3222 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3226 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3228 redraw_mask |= REDRAW_FIELD;
3232 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3235 ClearAutoRepeatKeyEvents();
3238 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3240 int graphic = IMG_BACKGROUND_REQUEST;
3241 int sound_opening = SND_REQUEST_OPENING;
3242 int sound_closing = SND_REQUEST_CLOSING;
3243 int anim_mode_1 = request.anim_mode; // (higher priority)
3244 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3245 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3246 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3247 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3249 if (game_status == GAME_MODE_PLAYING)
3250 BlitScreenToBitmap(backbuffer);
3252 SetDrawtoField(DRAW_TO_BACKBUFFER);
3254 // SetDrawBackgroundMask(REDRAW_NONE);
3256 if (action == ACTION_OPENING)
3258 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3260 if (req_state & REQ_ASK)
3262 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3263 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3264 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3265 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3267 else if (req_state & REQ_CONFIRM)
3269 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3270 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3272 else if (req_state & REQ_PLAYER)
3274 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3275 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3276 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3277 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3280 DrawEnvelopeRequest(text);
3283 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3285 if (action == ACTION_OPENING)
3287 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3289 if (anim_mode == ANIM_DEFAULT)
3290 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3292 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3296 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3298 if (anim_mode != ANIM_NONE)
3299 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3301 if (anim_mode == ANIM_DEFAULT)
3302 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3305 game.envelope_active = FALSE;
3307 if (action == ACTION_CLOSING)
3308 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3310 // SetDrawBackgroundMask(last_draw_background_mask);
3312 redraw_mask |= REDRAW_FIELD;
3316 if (action == ACTION_CLOSING &&
3317 game_status == GAME_MODE_PLAYING &&
3318 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3319 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3322 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3324 if (IS_MM_WALL(element))
3326 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3332 int graphic = el2preimg(element);
3334 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3335 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3340 void DrawLevel(int draw_background_mask)
3344 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3345 SetDrawBackgroundMask(draw_background_mask);
3349 for (x = BX1; x <= BX2; x++)
3350 for (y = BY1; y <= BY2; y++)
3351 DrawScreenField(x, y);
3353 redraw_mask |= REDRAW_FIELD;
3356 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3361 for (x = 0; x < size_x; x++)
3362 for (y = 0; y < size_y; y++)
3363 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3365 redraw_mask |= REDRAW_FIELD;
3368 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3372 for (x = 0; x < size_x; x++)
3373 for (y = 0; y < size_y; y++)
3374 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3376 redraw_mask |= REDRAW_FIELD;
3379 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3381 boolean show_level_border = (BorderElement != EL_EMPTY);
3382 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3383 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3384 int tile_size = preview.tile_size;
3385 int preview_width = preview.xsize * tile_size;
3386 int preview_height = preview.ysize * tile_size;
3387 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3388 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3389 int real_preview_width = real_preview_xsize * tile_size;
3390 int real_preview_height = real_preview_ysize * tile_size;
3391 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3392 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3395 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3398 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3400 dst_x += (preview_width - real_preview_width) / 2;
3401 dst_y += (preview_height - real_preview_height) / 2;
3403 for (x = 0; x < real_preview_xsize; x++)
3405 for (y = 0; y < real_preview_ysize; y++)
3407 int lx = from_x + x + (show_level_border ? -1 : 0);
3408 int ly = from_y + y + (show_level_border ? -1 : 0);
3409 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3410 getBorderElement(lx, ly));
3412 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3413 element, tile_size);
3417 redraw_mask |= REDRAW_FIELD;
3420 #define MICROLABEL_EMPTY 0
3421 #define MICROLABEL_LEVEL_NAME 1
3422 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3423 #define MICROLABEL_LEVEL_AUTHOR 3
3424 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3425 #define MICROLABEL_IMPORTED_FROM 5
3426 #define MICROLABEL_IMPORTED_BY_HEAD 6
3427 #define MICROLABEL_IMPORTED_BY 7
3429 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3431 int max_text_width = SXSIZE;
3432 int font_width = getFontWidth(font_nr);
3434 if (pos->align == ALIGN_CENTER)
3435 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3436 else if (pos->align == ALIGN_RIGHT)
3437 max_text_width = pos->x;
3439 max_text_width = SXSIZE - pos->x;
3441 return max_text_width / font_width;
3444 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3446 char label_text[MAX_OUTPUT_LINESIZE + 1];
3447 int max_len_label_text;
3448 int font_nr = pos->font;
3451 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3454 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3455 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3456 mode == MICROLABEL_IMPORTED_BY_HEAD)
3457 font_nr = pos->font_alt;
3459 max_len_label_text = getMaxTextLength(pos, font_nr);
3461 if (pos->size != -1)
3462 max_len_label_text = pos->size;
3464 for (i = 0; i < max_len_label_text; i++)
3465 label_text[i] = ' ';
3466 label_text[max_len_label_text] = '\0';
3468 if (strlen(label_text) > 0)
3469 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3472 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3473 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3474 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3475 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3476 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3477 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3478 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3479 max_len_label_text);
3480 label_text[max_len_label_text] = '\0';
3482 if (strlen(label_text) > 0)
3483 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3485 redraw_mask |= REDRAW_FIELD;
3488 static void DrawPreviewLevelLabel(int mode)
3490 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3493 static void DrawPreviewLevelInfo(int mode)
3495 if (mode == MICROLABEL_LEVEL_NAME)
3496 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3497 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3498 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3501 static void DrawPreviewLevelExt(boolean restart)
3503 static unsigned int scroll_delay = 0;
3504 static unsigned int label_delay = 0;
3505 static int from_x, from_y, scroll_direction;
3506 static int label_state, label_counter;
3507 unsigned int scroll_delay_value = preview.step_delay;
3508 boolean show_level_border = (BorderElement != EL_EMPTY);
3509 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3510 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3517 if (preview.anim_mode == ANIM_CENTERED)
3519 if (level_xsize > preview.xsize)
3520 from_x = (level_xsize - preview.xsize) / 2;
3521 if (level_ysize > preview.ysize)
3522 from_y = (level_ysize - preview.ysize) / 2;
3525 from_x += preview.xoffset;
3526 from_y += preview.yoffset;
3528 scroll_direction = MV_RIGHT;
3532 DrawPreviewLevelPlayfield(from_x, from_y);
3533 DrawPreviewLevelLabel(label_state);
3535 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3536 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3538 // initialize delay counters
3539 ResetDelayCounter(&scroll_delay);
3540 ResetDelayCounter(&label_delay);
3542 if (leveldir_current->name)
3544 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3545 char label_text[MAX_OUTPUT_LINESIZE + 1];
3546 int font_nr = pos->font;
3547 int max_len_label_text = getMaxTextLength(pos, font_nr);
3549 if (pos->size != -1)
3550 max_len_label_text = pos->size;
3552 strncpy(label_text, leveldir_current->name, max_len_label_text);
3553 label_text[max_len_label_text] = '\0';
3555 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3556 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3562 // scroll preview level, if needed
3563 if (preview.anim_mode != ANIM_NONE &&
3564 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3565 DelayReached(&scroll_delay, scroll_delay_value))
3567 switch (scroll_direction)
3572 from_x -= preview.step_offset;
3573 from_x = (from_x < 0 ? 0 : from_x);
3576 scroll_direction = MV_UP;
3580 if (from_x < level_xsize - preview.xsize)
3582 from_x += preview.step_offset;
3583 from_x = (from_x > level_xsize - preview.xsize ?
3584 level_xsize - preview.xsize : from_x);
3587 scroll_direction = MV_DOWN;
3593 from_y -= preview.step_offset;
3594 from_y = (from_y < 0 ? 0 : from_y);
3597 scroll_direction = MV_RIGHT;
3601 if (from_y < level_ysize - preview.ysize)
3603 from_y += preview.step_offset;
3604 from_y = (from_y > level_ysize - preview.ysize ?
3605 level_ysize - preview.ysize : from_y);
3608 scroll_direction = MV_LEFT;
3615 DrawPreviewLevelPlayfield(from_x, from_y);
3618 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3619 // redraw micro level label, if needed
3620 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3621 !strEqual(level.author, ANONYMOUS_NAME) &&
3622 !strEqual(level.author, leveldir_current->name) &&
3623 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3625 int max_label_counter = 23;
3627 if (leveldir_current->imported_from != NULL &&
3628 strlen(leveldir_current->imported_from) > 0)
3629 max_label_counter += 14;
3630 if (leveldir_current->imported_by != NULL &&
3631 strlen(leveldir_current->imported_by) > 0)
3632 max_label_counter += 14;
3634 label_counter = (label_counter + 1) % max_label_counter;
3635 label_state = (label_counter >= 0 && label_counter <= 7 ?
3636 MICROLABEL_LEVEL_NAME :
3637 label_counter >= 9 && label_counter <= 12 ?
3638 MICROLABEL_LEVEL_AUTHOR_HEAD :
3639 label_counter >= 14 && label_counter <= 21 ?
3640 MICROLABEL_LEVEL_AUTHOR :
3641 label_counter >= 23 && label_counter <= 26 ?
3642 MICROLABEL_IMPORTED_FROM_HEAD :
3643 label_counter >= 28 && label_counter <= 35 ?
3644 MICROLABEL_IMPORTED_FROM :
3645 label_counter >= 37 && label_counter <= 40 ?
3646 MICROLABEL_IMPORTED_BY_HEAD :
3647 label_counter >= 42 && label_counter <= 49 ?
3648 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3650 if (leveldir_current->imported_from == NULL &&
3651 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3652 label_state == MICROLABEL_IMPORTED_FROM))
3653 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3654 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3656 DrawPreviewLevelLabel(label_state);
3660 void DrawPreviewPlayers(void)
3662 if (game_status != GAME_MODE_MAIN)
3665 // do not draw preview players if level preview redefined, but players aren't
3666 if (preview.redefined && !menu.main.preview_players.redefined)
3669 boolean player_found[MAX_PLAYERS];
3670 int num_players = 0;
3673 for (i = 0; i < MAX_PLAYERS; i++)
3674 player_found[i] = FALSE;
3676 // check which players can be found in the level (simple approach)
3677 for (x = 0; x < lev_fieldx; x++)
3679 for (y = 0; y < lev_fieldy; y++)
3681 int element = level.field[x][y];
3683 if (IS_PLAYER_ELEMENT(element))
3685 int player_nr = GET_PLAYER_NR(element);
3687 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3689 if (!player_found[player_nr])
3692 player_found[player_nr] = TRUE;
3697 struct TextPosInfo *pos = &menu.main.preview_players;
3698 int tile_size = pos->tile_size;
3699 int border_size = pos->border_size;
3700 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3701 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3702 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3703 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3704 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3705 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3706 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3707 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3708 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3709 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3710 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3711 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3713 // clear area in which the players will be drawn
3714 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3715 max_players_width, max_players_height);
3717 if (!network.enabled && !setup.team_mode)
3720 // only draw players if level is suited for team mode
3721 if (num_players < 2)
3724 // draw all players that were found in the level
3725 for (i = 0; i < MAX_PLAYERS; i++)
3727 if (player_found[i])
3729 int graphic = el2img(EL_PLAYER_1 + i);
3731 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3733 xpos += player_xoffset;
3734 ypos += player_yoffset;
3739 void DrawPreviewLevelInitial(void)
3741 DrawPreviewLevelExt(TRUE);
3742 DrawPreviewPlayers();
3745 void DrawPreviewLevelAnimation(void)
3747 DrawPreviewLevelExt(FALSE);
3750 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3751 int border_size, int font_nr)
3753 int graphic = el2img(EL_PLAYER_1 + player_nr);
3754 int font_height = getFontHeight(font_nr);
3755 int player_height = MAX(tile_size, font_height);
3756 int xoffset_text = tile_size + border_size;
3757 int yoffset_text = (player_height - font_height) / 2;
3758 int yoffset_graphic = (player_height - tile_size) / 2;
3759 char *player_name = getNetworkPlayerName(player_nr + 1);
3761 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3763 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3766 static void DrawNetworkPlayersExt(boolean force)
3768 if (game_status != GAME_MODE_MAIN)
3771 if (!network.connected && !force)
3774 // do not draw network players if level preview redefined, but players aren't
3775 if (preview.redefined && !menu.main.network_players.redefined)
3778 int num_players = 0;
3781 for (i = 0; i < MAX_PLAYERS; i++)
3782 if (stored_player[i].connected_network)
3785 struct TextPosInfo *pos = &menu.main.network_players;
3786 int tile_size = pos->tile_size;
3787 int border_size = pos->border_size;
3788 int xoffset_text = tile_size + border_size;
3789 int font_nr = pos->font;
3790 int font_width = getFontWidth(font_nr);
3791 int font_height = getFontHeight(font_nr);
3792 int player_height = MAX(tile_size, font_height);
3793 int player_yoffset = player_height + border_size;
3794 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3795 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3796 int all_players_height = num_players * player_yoffset - border_size;
3797 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3798 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3799 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3801 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3802 max_players_width, max_players_height);
3804 // first draw local network player ...
3805 for (i = 0; i < MAX_PLAYERS; i++)
3807 if (stored_player[i].connected_network &&
3808 stored_player[i].connected_locally)
3810 char *player_name = getNetworkPlayerName(i + 1);
3811 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3812 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3814 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3816 ypos += player_yoffset;
3820 // ... then draw all other network players
3821 for (i = 0; i < MAX_PLAYERS; i++)
3823 if (stored_player[i].connected_network &&
3824 !stored_player[i].connected_locally)
3826 char *player_name = getNetworkPlayerName(i + 1);
3827 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3828 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3830 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3832 ypos += player_yoffset;
3837 void DrawNetworkPlayers(void)
3839 DrawNetworkPlayersExt(FALSE);
3842 void ClearNetworkPlayers(void)
3844 DrawNetworkPlayersExt(TRUE);
3847 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3848 int graphic, int lx, int ly,
3851 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3853 if (mask_mode == USE_MASKING)
3854 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3856 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3859 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3860 int graphic, int sync_frame, int mask_mode)
3862 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3864 if (mask_mode == USE_MASKING)
3865 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3867 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3870 static void DrawGraphicAnimation(int x, int y, int graphic)
3872 int lx = LEVELX(x), ly = LEVELY(y);
3873 int mask_mode = NO_MASKING;
3875 if (!IN_SCR_FIELD(x, y))
3878 if (game.use_masked_elements)
3880 if (Tile[lx][ly] != EL_EMPTY)
3882 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3884 mask_mode = USE_MASKING;
3888 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3889 graphic, lx, ly, mask_mode);
3891 MarkTileDirty(x, y);
3894 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3896 int lx = LEVELX(x), ly = LEVELY(y);
3897 int mask_mode = NO_MASKING;
3899 if (!IN_SCR_FIELD(x, y))
3902 if (game.use_masked_elements)
3904 if (Tile[lx][ly] != EL_EMPTY)
3906 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3908 mask_mode = USE_MASKING;
3912 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3913 graphic, lx, ly, mask_mode);
3915 MarkTileDirty(x, y);
3918 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3920 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3923 void DrawLevelElementAnimation(int x, int y, int element)
3925 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3927 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3930 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3932 int sx = SCREENX(x), sy = SCREENY(y);
3934 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3937 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3940 DrawGraphicAnimation(sx, sy, graphic);
3943 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3944 DrawLevelFieldCrumbled(x, y);
3946 if (GFX_CRUMBLED(Tile[x][y]))
3947 DrawLevelFieldCrumbled(x, y);
3951 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3953 int sx = SCREENX(x), sy = SCREENY(y);
3956 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3959 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3961 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3964 DrawGraphicAnimation(sx, sy, graphic);
3966 if (GFX_CRUMBLED(element))
3967 DrawLevelFieldCrumbled(x, y);
3970 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3972 if (player->use_murphy)
3974 // this works only because currently only one player can be "murphy" ...
3975 static int last_horizontal_dir = MV_LEFT;
3976 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3978 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3979 last_horizontal_dir = move_dir;
3981 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3983 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3985 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3991 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3994 static boolean equalGraphics(int graphic1, int graphic2)
3996 struct GraphicInfo *g1 = &graphic_info[graphic1];
3997 struct GraphicInfo *g2 = &graphic_info[graphic2];
3999 return (g1->bitmap == g2->bitmap &&
4000 g1->src_x == g2->src_x &&
4001 g1->src_y == g2->src_y &&
4002 g1->anim_frames == g2->anim_frames &&
4003 g1->anim_delay == g2->anim_delay &&
4004 g1->anim_mode == g2->anim_mode);
4007 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4011 DRAW_PLAYER_STAGE_INIT = 0,
4012 DRAW_PLAYER_STAGE_LAST_FIELD,
4013 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4014 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4015 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4016 DRAW_PLAYER_STAGE_PLAYER,
4018 DRAW_PLAYER_STAGE_PLAYER,
4019 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4021 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4022 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4024 NUM_DRAW_PLAYER_STAGES
4027 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4029 static int static_last_player_graphic[MAX_PLAYERS];
4030 static int static_last_player_frame[MAX_PLAYERS];
4031 static boolean static_player_is_opaque[MAX_PLAYERS];
4032 static boolean draw_player[MAX_PLAYERS];
4033 int pnr = player->index_nr;
4035 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4037 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4038 static_last_player_frame[pnr] = player->Frame;
4039 static_player_is_opaque[pnr] = FALSE;
4041 draw_player[pnr] = TRUE;
4044 if (!draw_player[pnr])
4048 if (!IN_LEV_FIELD(player->jx, player->jy))
4050 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4051 Debug("draw:DrawPlayerExt", "This should never happen!");
4053 draw_player[pnr] = FALSE;
4059 int last_player_graphic = static_last_player_graphic[pnr];
4060 int last_player_frame = static_last_player_frame[pnr];
4061 boolean player_is_opaque = static_player_is_opaque[pnr];
4063 int jx = player->jx;
4064 int jy = player->jy;
4065 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4066 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4067 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4068 int last_jx = (player->is_moving ? jx - dx : jx);
4069 int last_jy = (player->is_moving ? jy - dy : jy);
4070 int next_jx = jx + dx;
4071 int next_jy = jy + dy;
4072 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4073 int sx = SCREENX(jx);
4074 int sy = SCREENY(jy);
4075 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4076 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4077 int element = Tile[jx][jy];
4078 int last_element = Tile[last_jx][last_jy];
4079 int action = (player->is_pushing ? ACTION_PUSHING :
4080 player->is_digging ? ACTION_DIGGING :
4081 player->is_collecting ? ACTION_COLLECTING :
4082 player->is_moving ? ACTION_MOVING :
4083 player->is_snapping ? ACTION_SNAPPING :
4084 player->is_dropping ? ACTION_DROPPING :
4085 player->is_waiting ? player->action_waiting :
4088 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4090 // ------------------------------------------------------------------------
4091 // initialize drawing the player
4092 // ------------------------------------------------------------------------
4094 draw_player[pnr] = FALSE;
4096 // GfxElement[][] is set to the element the player is digging or collecting;
4097 // remove also for off-screen player if the player is not moving anymore
4098 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4099 GfxElement[jx][jy] = EL_UNDEFINED;
4101 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4104 if (element == EL_EXPLOSION)
4107 InitPlayerGfxAnimation(player, action, move_dir);
4109 draw_player[pnr] = TRUE;
4111 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4113 // ------------------------------------------------------------------------
4114 // draw things in the field the player is leaving, if needed
4115 // ------------------------------------------------------------------------
4117 if (!IN_SCR_FIELD(sx, sy))
4118 draw_player[pnr] = FALSE;
4120 if (!player->is_moving)
4123 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4125 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4127 if (last_element == EL_DYNAMITE_ACTIVE ||
4128 last_element == EL_EM_DYNAMITE_ACTIVE ||
4129 last_element == EL_SP_DISK_RED_ACTIVE)
4130 DrawDynamite(last_jx, last_jy);
4132 DrawLevelFieldThruMask(last_jx, last_jy);
4134 else if (last_element == EL_DYNAMITE_ACTIVE ||
4135 last_element == EL_EM_DYNAMITE_ACTIVE ||
4136 last_element == EL_SP_DISK_RED_ACTIVE)
4137 DrawDynamite(last_jx, last_jy);
4139 DrawLevelField(last_jx, last_jy);
4141 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4142 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4144 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4146 // ------------------------------------------------------------------------
4147 // draw things behind the player, if needed
4148 // ------------------------------------------------------------------------
4152 DrawLevelElement(jx, jy, Back[jx][jy]);
4157 if (IS_ACTIVE_BOMB(element))
4159 DrawLevelElement(jx, jy, EL_EMPTY);
4164 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4166 int old_element = GfxElement[jx][jy];
4167 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4168 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4170 if (GFX_CRUMBLED(old_element))
4171 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4173 DrawScreenGraphic(sx, sy, old_graphic, frame);
4175 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4176 static_player_is_opaque[pnr] = TRUE;
4180 GfxElement[jx][jy] = EL_UNDEFINED;
4182 // make sure that pushed elements are drawn with correct frame rate
4183 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4185 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4186 GfxFrame[jx][jy] = player->StepFrame;
4188 DrawLevelField(jx, jy);
4191 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4193 // ------------------------------------------------------------------------
4194 // draw things the player is pushing, if needed
4195 // ------------------------------------------------------------------------
4197 if (!player->is_pushing || !player->is_moving)
4200 int gfx_frame = GfxFrame[jx][jy];
4202 if (!IS_MOVING(jx, jy)) // push movement already finished
4204 element = Tile[next_jx][next_jy];
4205 gfx_frame = GfxFrame[next_jx][next_jy];
4208 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4209 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4210 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4212 // draw background element under pushed element (like the Sokoban field)
4213 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4215 // this allows transparent pushing animation over non-black background
4218 DrawLevelElement(jx, jy, Back[jx][jy]);
4220 DrawLevelElement(jx, jy, EL_EMPTY);
4222 if (Back[next_jx][next_jy])
4223 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4225 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4227 else if (Back[next_jx][next_jy])
4228 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4230 int px = SCREENX(jx), py = SCREENY(jy);
4231 int pxx = (TILEX - ABS(sxx)) * dx;
4232 int pyy = (TILEY - ABS(syy)) * dy;
4235 // do not draw (EM style) pushing animation when pushing is finished
4236 // (two-tile animations usually do not contain start and end frame)
4237 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4238 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4240 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4242 // masked drawing is needed for EMC style (double) movement graphics
4243 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4244 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4247 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4249 // ------------------------------------------------------------------------
4250 // draw player himself
4251 // ------------------------------------------------------------------------
4253 int graphic = getPlayerGraphic(player, move_dir);
4255 // in the case of changed player action or direction, prevent the current
4256 // animation frame from being restarted for identical animations
4257 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4258 player->Frame = last_player_frame;
4260 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4262 if (player_is_opaque)
4263 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4265 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4267 if (SHIELD_ON(player))
4269 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4270 IMG_SHIELD_NORMAL_ACTIVE);
4271 frame = getGraphicAnimationFrame(graphic, -1);
4273 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4276 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4278 // ------------------------------------------------------------------------
4279 // draw things in front of player (active dynamite or dynabombs)
4280 // ------------------------------------------------------------------------
4282 if (IS_ACTIVE_BOMB(element))
4284 int graphic = el2img(element);
4285 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4287 if (game.emulation == EMU_SUPAPLEX)
4288 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4290 DrawGraphicThruMask(sx, sy, graphic, frame);
4293 if (player_is_moving && last_element == EL_EXPLOSION)
4295 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4296 GfxElement[last_jx][last_jy] : EL_EMPTY);
4297 int graphic = el_act2img(element, ACTION_EXPLODING);
4298 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4299 int phase = ExplodePhase[last_jx][last_jy] - 1;
4300 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4303 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4306 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4308 // ------------------------------------------------------------------------
4309 // draw elements the player is just walking/passing through/under
4310 // ------------------------------------------------------------------------
4312 if (player_is_moving)
4314 // handle the field the player is leaving ...
4315 if (IS_ACCESSIBLE_INSIDE(last_element))
4316 DrawLevelField(last_jx, last_jy);
4317 else if (IS_ACCESSIBLE_UNDER(last_element))
4318 DrawLevelFieldThruMask(last_jx, last_jy);
4321 // do not redraw accessible elements if the player is just pushing them
4322 if (!player_is_moving || !player->is_pushing)
4324 // ... and the field the player is entering
4325 if (IS_ACCESSIBLE_INSIDE(element))
4326 DrawLevelField(jx, jy);
4327 else if (IS_ACCESSIBLE_UNDER(element))
4328 DrawLevelFieldThruMask(jx, jy);
4331 MarkTileDirty(sx, sy);
4335 void DrawPlayer(struct PlayerInfo *player)
4339 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4340 DrawPlayerExt(player, i);
4343 void DrawAllPlayers(void)
4347 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4348 for (j = 0; j < MAX_PLAYERS; j++)
4349 if (stored_player[j].active)
4350 DrawPlayerExt(&stored_player[j], i);
4353 void DrawPlayerField(int x, int y)
4355 if (!IS_PLAYER(x, y))
4358 DrawPlayer(PLAYERINFO(x, y));
4361 // ----------------------------------------------------------------------------
4363 void WaitForEventToContinue(void)
4365 boolean first_wait = TRUE;
4366 boolean still_wait = TRUE;
4368 if (program.headless)
4371 // simulate releasing mouse button over last gadget, if still pressed
4373 HandleGadgets(-1, -1, 0);
4375 button_status = MB_RELEASED;
4378 ClearPlayerAction();
4384 if (NextValidEvent(&event))
4388 case EVENT_BUTTONPRESS:
4389 case EVENT_FINGERPRESS:
4393 case EVENT_BUTTONRELEASE:
4394 case EVENT_FINGERRELEASE:
4395 still_wait = first_wait;
4398 case EVENT_KEYPRESS:
4399 case SDL_CONTROLLERBUTTONDOWN:
4400 case SDL_JOYBUTTONDOWN:
4405 HandleOtherEvents(&event);
4409 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4414 if (!PendingEvent())
4419 #define MAX_REQUEST_LINES 13
4420 #define MAX_REQUEST_LINE_FONT1_LEN 7
4421 #define MAX_REQUEST_LINE_FONT2_LEN 10
4423 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4425 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4427 int draw_buffer_last = GetDrawtoField();
4428 int width = request.width;
4429 int height = request.height;
4433 // when showing request dialog after game ended, deactivate game panel
4434 if (game_just_ended)
4435 game.panel.active = FALSE;
4437 game.request_active = TRUE;
4439 setRequestPosition(&sx, &sy, FALSE);
4441 button_status = MB_RELEASED;
4443 request_gadget_id = -1;
4448 boolean event_handled = FALSE;
4450 if (game_just_ended)
4452 SetDrawtoField(draw_buffer_game);
4454 HandleGameActions();
4456 SetDrawtoField(DRAW_TO_BACKBUFFER);
4458 if (global.use_envelope_request)
4460 // copy current state of request area to middle of playfield area
4461 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4469 while (NextValidEvent(&event))
4471 event_handled = TRUE;
4475 case EVENT_BUTTONPRESS:
4476 case EVENT_BUTTONRELEASE:
4477 case EVENT_MOTIONNOTIFY:
4481 if (event.type == EVENT_MOTIONNOTIFY)
4486 motion_status = TRUE;
4487 mx = ((MotionEvent *) &event)->x;
4488 my = ((MotionEvent *) &event)->y;
4492 motion_status = FALSE;
4493 mx = ((ButtonEvent *) &event)->x;
4494 my = ((ButtonEvent *) &event)->y;
4495 if (event.type == EVENT_BUTTONPRESS)
4496 button_status = ((ButtonEvent *) &event)->button;
4498 button_status = MB_RELEASED;
4501 // this sets 'request_gadget_id'
4502 HandleGadgets(mx, my, button_status);
4504 switch (request_gadget_id)
4506 case TOOL_CTRL_ID_YES:
4507 case TOOL_CTRL_ID_TOUCH_YES:
4510 case TOOL_CTRL_ID_NO:
4511 case TOOL_CTRL_ID_TOUCH_NO:
4514 case TOOL_CTRL_ID_CONFIRM:
4515 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4516 result = TRUE | FALSE;
4519 case TOOL_CTRL_ID_PLAYER_1:
4522 case TOOL_CTRL_ID_PLAYER_2:
4525 case TOOL_CTRL_ID_PLAYER_3:
4528 case TOOL_CTRL_ID_PLAYER_4:
4533 // only check clickable animations if no request gadget clicked
4534 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4541 case SDL_WINDOWEVENT:
4542 HandleWindowEvent((WindowEvent *) &event);
4545 case SDL_APP_WILLENTERBACKGROUND:
4546 case SDL_APP_DIDENTERBACKGROUND:
4547 case SDL_APP_WILLENTERFOREGROUND:
4548 case SDL_APP_DIDENTERFOREGROUND:
4549 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4552 case EVENT_KEYPRESS:
4554 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4559 if (req_state & REQ_CONFIRM)
4568 #if defined(KSYM_Rewind)
4569 case KSYM_Rewind: // for Amazon Fire TV remote
4578 #if defined(KSYM_FastForward)
4579 case KSYM_FastForward: // for Amazon Fire TV remote
4585 HandleKeysDebug(key, KEY_PRESSED);
4589 if (req_state & REQ_PLAYER)
4591 int old_player_nr = setup.network_player_nr;
4594 result = old_player_nr + 1;
4599 result = old_player_nr + 1;
4630 case EVENT_FINGERRELEASE:
4631 case EVENT_KEYRELEASE:
4632 ClearPlayerAction();
4635 case SDL_CONTROLLERBUTTONDOWN:
4636 switch (event.cbutton.button)
4638 case SDL_CONTROLLER_BUTTON_A:
4639 case SDL_CONTROLLER_BUTTON_X:
4640 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4641 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4645 case SDL_CONTROLLER_BUTTON_B:
4646 case SDL_CONTROLLER_BUTTON_Y:
4647 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4648 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4649 case SDL_CONTROLLER_BUTTON_BACK:
4654 if (req_state & REQ_PLAYER)
4656 int old_player_nr = setup.network_player_nr;
4659 result = old_player_nr + 1;
4661 switch (event.cbutton.button)
4663 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4664 case SDL_CONTROLLER_BUTTON_Y:
4668 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4669 case SDL_CONTROLLER_BUTTON_B:
4673 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4674 case SDL_CONTROLLER_BUTTON_A:
4678 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4679 case SDL_CONTROLLER_BUTTON_X:
4690 case SDL_CONTROLLERBUTTONUP:
4691 HandleJoystickEvent(&event);
4692 ClearPlayerAction();
4696 HandleOtherEvents(&event);
4701 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4703 int joy = AnyJoystick();
4705 if (joy & JOY_BUTTON_1)
4707 else if (joy & JOY_BUTTON_2)
4710 else if (AnyJoystick())
4712 int joy = AnyJoystick();
4714 if (req_state & REQ_PLAYER)
4718 else if (joy & JOY_RIGHT)
4720 else if (joy & JOY_DOWN)
4722 else if (joy & JOY_LEFT)
4729 if (game_just_ended)
4731 if (global.use_envelope_request)
4733 // copy back current state of pressed buttons inside request area
4734 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4738 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4744 SetDrawtoField(draw_buffer_last);
4746 game.request_active = FALSE;
4751 static boolean RequestDoor(char *text, unsigned int req_state)
4753 int draw_buffer_last = GetDrawtoField();
4754 unsigned int old_door_state;
4755 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4756 int font_nr = FONT_TEXT_2;
4761 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4763 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4764 font_nr = FONT_TEXT_1;
4767 if (game_status == GAME_MODE_PLAYING)
4768 BlitScreenToBitmap(backbuffer);
4770 // disable deactivated drawing when quick-loading level tape recording
4771 if (tape.playing && tape.deactivate_display)
4772 TapeDeactivateDisplayOff(TRUE);
4774 SetMouseCursor(CURSOR_DEFAULT);
4776 // pause network game while waiting for request to answer
4777 if (network.enabled &&
4778 game_status == GAME_MODE_PLAYING &&
4779 !game.all_players_gone &&
4780 req_state & REQUEST_WAIT_FOR_INPUT)
4781 SendToServer_PausePlaying();
4783 old_door_state = GetDoorState();
4785 // simulate releasing mouse button over last gadget, if still pressed
4787 HandleGadgets(-1, -1, 0);
4791 // draw released gadget before proceeding
4794 if (old_door_state & DOOR_OPEN_1)
4796 CloseDoor(DOOR_CLOSE_1);
4798 // save old door content
4799 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4800 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4803 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4804 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4806 // clear door drawing field
4807 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4809 // force DOOR font inside door area
4810 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4812 // write text for request
4813 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4815 char text_line[max_request_line_len + 1];
4821 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4823 tc = *(text_ptr + tx);
4824 // if (!tc || tc == ' ')
4825 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4829 if ((tc == '?' || tc == '!') && tl == 0)
4839 strncpy(text_line, text_ptr, tl);
4842 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4843 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4844 text_line, font_nr);
4846 text_ptr += tl + (tc == ' ' ? 1 : 0);
4847 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4852 if (req_state & REQ_ASK)
4854 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4855 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4856 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4857 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4859 else if (req_state & REQ_CONFIRM)
4861 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4862 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4864 else if (req_state & REQ_PLAYER)
4866 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4867 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4868 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4869 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4872 // copy request gadgets to door backbuffer
4873 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4875 OpenDoor(DOOR_OPEN_1);
4877 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4879 if (game_status == GAME_MODE_PLAYING)
4881 SetPanelBackground();
4882 SetDrawBackgroundMask(REDRAW_DOOR_1);
4886 SetDrawBackgroundMask(REDRAW_FIELD);
4892 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4894 // ---------- handle request buttons ----------
4895 result = RequestHandleEvents(req_state, draw_buffer_last);
4899 if (!(req_state & REQ_STAY_OPEN))
4901 CloseDoor(DOOR_CLOSE_1);
4903 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4904 (req_state & REQ_REOPEN))
4905 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4910 if (game_status == GAME_MODE_PLAYING)
4912 SetPanelBackground();
4913 SetDrawBackgroundMask(REDRAW_DOOR_1);
4917 SetDrawBackgroundMask(REDRAW_FIELD);
4920 // continue network game after request
4921 if (network.enabled &&
4922 game_status == GAME_MODE_PLAYING &&
4923 !game.all_players_gone &&
4924 req_state & REQUEST_WAIT_FOR_INPUT)
4925 SendToServer_ContinuePlaying();
4927 // restore deactivated drawing when quick-loading level tape recording
4928 if (tape.playing && tape.deactivate_display)
4929 TapeDeactivateDisplayOn();
4934 static boolean RequestEnvelope(char *text, unsigned int req_state)
4936 int draw_buffer_last = GetDrawtoField();
4939 if (game_status == GAME_MODE_PLAYING)
4940 BlitScreenToBitmap(backbuffer);
4942 // disable deactivated drawing when quick-loading level tape recording
4943 if (tape.playing && tape.deactivate_display)
4944 TapeDeactivateDisplayOff(TRUE);
4946 SetMouseCursor(CURSOR_DEFAULT);
4948 // pause network game while waiting for request to answer
4949 if (network.enabled &&
4950 game_status == GAME_MODE_PLAYING &&
4951 !game.all_players_gone &&
4952 req_state & REQUEST_WAIT_FOR_INPUT)
4953 SendToServer_PausePlaying();
4955 // simulate releasing mouse button over last gadget, if still pressed
4957 HandleGadgets(-1, -1, 0);
4961 // (replace with setting corresponding request background)
4962 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4963 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4965 // clear door drawing field
4966 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4968 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4970 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4972 if (game_status == GAME_MODE_PLAYING)
4974 SetPanelBackground();
4975 SetDrawBackgroundMask(REDRAW_DOOR_1);
4979 SetDrawBackgroundMask(REDRAW_FIELD);
4985 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4987 // ---------- handle request buttons ----------
4988 result = RequestHandleEvents(req_state, draw_buffer_last);
4992 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4996 if (game_status == GAME_MODE_PLAYING)
4998 SetPanelBackground();
4999 SetDrawBackgroundMask(REDRAW_DOOR_1);
5003 SetDrawBackgroundMask(REDRAW_FIELD);
5006 // continue network game after request
5007 if (network.enabled &&
5008 game_status == GAME_MODE_PLAYING &&
5009 !game.all_players_gone &&
5010 req_state & REQUEST_WAIT_FOR_INPUT)
5011 SendToServer_ContinuePlaying();
5013 // restore deactivated drawing when quick-loading level tape recording
5014 if (tape.playing && tape.deactivate_display)
5015 TapeDeactivateDisplayOn();
5020 boolean Request(char *text, unsigned int req_state)
5022 boolean overlay_enabled = GetOverlayEnabled();
5025 game.request_active_or_moving = TRUE;
5027 SetOverlayEnabled(FALSE);
5029 if (global.use_envelope_request)
5030 result = RequestEnvelope(text, req_state);
5032 result = RequestDoor(text, req_state);
5034 SetOverlayEnabled(overlay_enabled);
5036 game.request_active_or_moving = FALSE;
5041 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5043 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5044 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5047 if (dpo1->sort_priority != dpo2->sort_priority)
5048 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5050 compare_result = dpo1->nr - dpo2->nr;
5052 return compare_result;
5055 void InitGraphicCompatibilityInfo_Doors(void)
5061 struct DoorInfo *door;
5065 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5066 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5068 { -1, -1, -1, NULL }
5070 struct Rect door_rect_list[] =
5072 { DX, DY, DXSIZE, DYSIZE },
5073 { VX, VY, VXSIZE, VYSIZE }
5077 for (i = 0; doors[i].door_token != -1; i++)
5079 int door_token = doors[i].door_token;
5080 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5081 int part_1 = doors[i].part_1;
5082 int part_8 = doors[i].part_8;
5083 int part_2 = part_1 + 1;
5084 int part_3 = part_1 + 2;
5085 struct DoorInfo *door = doors[i].door;
5086 struct Rect *door_rect = &door_rect_list[door_index];
5087 boolean door_gfx_redefined = FALSE;
5089 // check if any door part graphic definitions have been redefined
5091 for (j = 0; door_part_controls[j].door_token != -1; j++)
5093 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5094 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5096 if (dpc->door_token == door_token && fi->redefined)
5097 door_gfx_redefined = TRUE;
5100 // check for old-style door graphic/animation modifications
5102 if (!door_gfx_redefined)
5104 if (door->anim_mode & ANIM_STATIC_PANEL)
5106 door->panel.step_xoffset = 0;
5107 door->panel.step_yoffset = 0;
5110 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5112 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5113 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5114 int num_door_steps, num_panel_steps;
5116 // remove door part graphics other than the two default wings
5118 for (j = 0; door_part_controls[j].door_token != -1; j++)
5120 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5121 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5123 if (dpc->graphic >= part_3 &&
5124 dpc->graphic <= part_8)
5128 // set graphics and screen positions of the default wings
5130 g_part_1->width = door_rect->width;
5131 g_part_1->height = door_rect->height;
5132 g_part_2->width = door_rect->width;
5133 g_part_2->height = door_rect->height;
5134 g_part_2->src_x = door_rect->width;
5135 g_part_2->src_y = g_part_1->src_y;
5137 door->part_2.x = door->part_1.x;
5138 door->part_2.y = door->part_1.y;
5140 if (door->width != -1)
5142 g_part_1->width = door->width;
5143 g_part_2->width = door->width;
5145 // special treatment for graphics and screen position of right wing
5146 g_part_2->src_x += door_rect->width - door->width;
5147 door->part_2.x += door_rect->width - door->width;
5150 if (door->height != -1)
5152 g_part_1->height = door->height;
5153 g_part_2->height = door->height;
5155 // special treatment for graphics and screen position of bottom wing
5156 g_part_2->src_y += door_rect->height - door->height;
5157 door->part_2.y += door_rect->height - door->height;
5160 // set animation delays for the default wings and panels
5162 door->part_1.step_delay = door->step_delay;
5163 door->part_2.step_delay = door->step_delay;
5164 door->panel.step_delay = door->step_delay;
5166 // set animation draw order for the default wings
5168 door->part_1.sort_priority = 2; // draw left wing over ...
5169 door->part_2.sort_priority = 1; // ... right wing
5171 // set animation draw offset for the default wings
5173 if (door->anim_mode & ANIM_HORIZONTAL)
5175 door->part_1.step_xoffset = door->step_offset;
5176 door->part_1.step_yoffset = 0;
5177 door->part_2.step_xoffset = door->step_offset * -1;
5178 door->part_2.step_yoffset = 0;
5180 num_door_steps = g_part_1->width / door->step_offset;
5182 else // ANIM_VERTICAL
5184 door->part_1.step_xoffset = 0;
5185 door->part_1.step_yoffset = door->step_offset;
5186 door->part_2.step_xoffset = 0;
5187 door->part_2.step_yoffset = door->step_offset * -1;
5189 num_door_steps = g_part_1->height / door->step_offset;
5192 // set animation draw offset for the default panels
5194 if (door->step_offset > 1)
5196 num_panel_steps = 2 * door_rect->height / door->step_offset;
5197 door->panel.start_step = num_panel_steps - num_door_steps;
5198 door->panel.start_step_closing = door->panel.start_step;
5202 num_panel_steps = door_rect->height / door->step_offset;
5203 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5204 door->panel.start_step_closing = door->panel.start_step;
5205 door->panel.step_delay *= 2;
5212 void InitDoors(void)
5216 for (i = 0; door_part_controls[i].door_token != -1; i++)
5218 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5219 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5221 // initialize "start_step_opening" and "start_step_closing", if needed
5222 if (dpc->pos->start_step_opening == 0 &&
5223 dpc->pos->start_step_closing == 0)
5225 // dpc->pos->start_step_opening = dpc->pos->start_step;
5226 dpc->pos->start_step_closing = dpc->pos->start_step;
5229 // fill structure for door part draw order (sorted below)
5231 dpo->sort_priority = dpc->pos->sort_priority;
5234 // sort door part controls according to sort_priority and graphic number
5235 qsort(door_part_order, MAX_DOOR_PARTS,
5236 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5239 unsigned int OpenDoor(unsigned int door_state)
5241 if (door_state & DOOR_COPY_BACK)
5243 if (door_state & DOOR_OPEN_1)
5244 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5245 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5247 if (door_state & DOOR_OPEN_2)
5248 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5249 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5251 door_state &= ~DOOR_COPY_BACK;
5254 return MoveDoor(door_state);
5257 unsigned int CloseDoor(unsigned int door_state)
5259 unsigned int old_door_state = GetDoorState();
5261 if (!(door_state & DOOR_NO_COPY_BACK))
5263 if (old_door_state & DOOR_OPEN_1)
5264 BlitBitmap(backbuffer, bitmap_db_door_1,
5265 DX, DY, DXSIZE, DYSIZE, 0, 0);
5267 if (old_door_state & DOOR_OPEN_2)
5268 BlitBitmap(backbuffer, bitmap_db_door_2,
5269 VX, VY, VXSIZE, VYSIZE, 0, 0);
5271 door_state &= ~DOOR_NO_COPY_BACK;
5274 return MoveDoor(door_state);
5277 unsigned int GetDoorState(void)
5279 return MoveDoor(DOOR_GET_STATE);
5282 unsigned int SetDoorState(unsigned int door_state)
5284 return MoveDoor(door_state | DOOR_SET_STATE);
5287 static int euclid(int a, int b)
5289 return (b ? euclid(b, a % b) : a);
5292 unsigned int MoveDoor(unsigned int door_state)
5294 struct Rect door_rect_list[] =
5296 { DX, DY, DXSIZE, DYSIZE },
5297 { VX, VY, VXSIZE, VYSIZE }
5299 static int door1 = DOOR_CLOSE_1;
5300 static int door2 = DOOR_CLOSE_2;
5301 unsigned int door_delay = 0;
5302 unsigned int door_delay_value;
5305 if (door_state == DOOR_GET_STATE)
5306 return (door1 | door2);
5308 if (door_state & DOOR_SET_STATE)
5310 if (door_state & DOOR_ACTION_1)
5311 door1 = door_state & DOOR_ACTION_1;
5312 if (door_state & DOOR_ACTION_2)
5313 door2 = door_state & DOOR_ACTION_2;
5315 return (door1 | door2);
5318 if (!(door_state & DOOR_FORCE_REDRAW))
5320 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5321 door_state &= ~DOOR_OPEN_1;
5322 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5323 door_state &= ~DOOR_CLOSE_1;
5324 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5325 door_state &= ~DOOR_OPEN_2;
5326 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5327 door_state &= ~DOOR_CLOSE_2;
5330 if (global.autoplay_leveldir)
5332 door_state |= DOOR_NO_DELAY;
5333 door_state &= ~DOOR_CLOSE_ALL;
5336 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5337 door_state |= DOOR_NO_DELAY;
5339 if (door_state & DOOR_ACTION)
5341 boolean door_panel_drawn[NUM_DOORS];
5342 boolean panel_has_doors[NUM_DOORS];
5343 boolean door_part_skip[MAX_DOOR_PARTS];
5344 boolean door_part_done[MAX_DOOR_PARTS];
5345 boolean door_part_done_all;
5346 int num_steps[MAX_DOOR_PARTS];
5347 int max_move_delay = 0; // delay for complete animations of all doors
5348 int max_step_delay = 0; // delay (ms) between two animation frames
5349 int num_move_steps = 0; // number of animation steps for all doors
5350 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5351 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5352 int current_move_delay = 0;
5356 for (i = 0; i < NUM_DOORS; i++)
5357 panel_has_doors[i] = FALSE;
5359 for (i = 0; i < MAX_DOOR_PARTS; i++)
5361 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5362 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5363 int door_token = dpc->door_token;
5365 door_part_done[i] = FALSE;
5366 door_part_skip[i] = (!(door_state & door_token) ||
5370 for (i = 0; i < MAX_DOOR_PARTS; i++)
5372 int nr = door_part_order[i].nr;
5373 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5374 struct DoorPartPosInfo *pos = dpc->pos;
5375 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5376 int door_token = dpc->door_token;
5377 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5378 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5379 int step_xoffset = ABS(pos->step_xoffset);
5380 int step_yoffset = ABS(pos->step_yoffset);
5381 int step_delay = pos->step_delay;
5382 int current_door_state = door_state & door_token;
5383 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5384 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5385 boolean part_opening = (is_panel ? door_closing : door_opening);
5386 int start_step = (part_opening ? pos->start_step_opening :
5387 pos->start_step_closing);
5388 float move_xsize = (step_xoffset ? g->width : 0);
5389 float move_ysize = (step_yoffset ? g->height : 0);
5390 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5391 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5392 int move_steps = (move_xsteps && move_ysteps ?
5393 MIN(move_xsteps, move_ysteps) :
5394 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5395 int move_delay = move_steps * step_delay;
5397 if (door_part_skip[nr])
5400 max_move_delay = MAX(max_move_delay, move_delay);
5401 max_step_delay = (max_step_delay == 0 ? step_delay :
5402 euclid(max_step_delay, step_delay));
5403 num_steps[nr] = move_steps;
5407 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5409 panel_has_doors[door_index] = TRUE;
5413 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5415 num_move_steps = max_move_delay / max_step_delay;
5416 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5418 door_delay_value = max_step_delay;
5420 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5422 start = num_move_steps - 1;
5426 // opening door sound has priority over simultaneously closing door
5427 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5429 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5431 if (door_state & DOOR_OPEN_1)
5432 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5433 if (door_state & DOOR_OPEN_2)
5434 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5436 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5438 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5440 if (door_state & DOOR_CLOSE_1)
5441 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5442 if (door_state & DOOR_CLOSE_2)
5443 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5447 for (k = start; k < num_move_steps; k++)
5449 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5451 door_part_done_all = TRUE;
5453 for (i = 0; i < NUM_DOORS; i++)
5454 door_panel_drawn[i] = FALSE;
5456 for (i = 0; i < MAX_DOOR_PARTS; i++)
5458 int nr = door_part_order[i].nr;
5459 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5460 struct DoorPartPosInfo *pos = dpc->pos;
5461 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5462 int door_token = dpc->door_token;
5463 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5464 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5465 boolean is_panel_and_door_has_closed = FALSE;
5466 struct Rect *door_rect = &door_rect_list[door_index];
5467 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5469 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5470 int current_door_state = door_state & door_token;
5471 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5472 boolean door_closing = !door_opening;
5473 boolean part_opening = (is_panel ? door_closing : door_opening);
5474 boolean part_closing = !part_opening;
5475 int start_step = (part_opening ? pos->start_step_opening :
5476 pos->start_step_closing);
5477 int step_delay = pos->step_delay;
5478 int step_factor = step_delay / max_step_delay;
5479 int k1 = (step_factor ? k / step_factor + 1 : k);
5480 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5481 int kk = MAX(0, k2);
5484 int src_x, src_y, src_xx, src_yy;
5485 int dst_x, dst_y, dst_xx, dst_yy;
5488 if (door_part_skip[nr])
5491 if (!(door_state & door_token))
5499 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5500 int kk_door = MAX(0, k2_door);
5501 int sync_frame = kk_door * door_delay_value;
5502 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5504 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5505 &g_src_x, &g_src_y);
5510 if (!door_panel_drawn[door_index])
5512 ClearRectangle(drawto, door_rect->x, door_rect->y,
5513 door_rect->width, door_rect->height);
5515 door_panel_drawn[door_index] = TRUE;
5518 // draw opening or closing door parts
5520 if (pos->step_xoffset < 0) // door part on right side
5523 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5526 if (dst_xx + width > door_rect->width)
5527 width = door_rect->width - dst_xx;
5529 else // door part on left side
5532 dst_xx = pos->x - kk * pos->step_xoffset;
5536 src_xx = ABS(dst_xx);
5540 width = g->width - src_xx;
5542 if (width > door_rect->width)
5543 width = door_rect->width;
5545 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5548 if (pos->step_yoffset < 0) // door part on bottom side
5551 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5554 if (dst_yy + height > door_rect->height)
5555 height = door_rect->height - dst_yy;
5557 else // door part on top side
5560 dst_yy = pos->y - kk * pos->step_yoffset;
5564 src_yy = ABS(dst_yy);
5568 height = g->height - src_yy;
5571 src_x = g_src_x + src_xx;
5572 src_y = g_src_y + src_yy;
5574 dst_x = door_rect->x + dst_xx;
5575 dst_y = door_rect->y + dst_yy;
5577 is_panel_and_door_has_closed =
5580 panel_has_doors[door_index] &&
5581 k >= num_move_steps_doors_only - 1);
5583 if (width >= 0 && width <= g->width &&
5584 height >= 0 && height <= g->height &&
5585 !is_panel_and_door_has_closed)
5587 if (is_panel || !pos->draw_masked)
5588 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5591 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5595 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5597 if ((part_opening && (width < 0 || height < 0)) ||
5598 (part_closing && (width >= g->width && height >= g->height)))
5599 door_part_done[nr] = TRUE;
5601 // continue door part animations, but not panel after door has closed
5602 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5603 door_part_done_all = FALSE;
5606 if (!(door_state & DOOR_NO_DELAY))
5610 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5612 current_move_delay += max_step_delay;
5614 // prevent OS (Windows) from complaining about program not responding
5618 if (door_part_done_all)
5622 if (!(door_state & DOOR_NO_DELAY))
5624 // wait for specified door action post delay
5625 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5626 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5627 else if (door_state & DOOR_ACTION_1)
5628 door_delay_value = door_1.post_delay;
5629 else if (door_state & DOOR_ACTION_2)
5630 door_delay_value = door_2.post_delay;
5632 while (!DelayReached(&door_delay, door_delay_value))
5637 if (door_state & DOOR_ACTION_1)
5638 door1 = door_state & DOOR_ACTION_1;
5639 if (door_state & DOOR_ACTION_2)
5640 door2 = door_state & DOOR_ACTION_2;
5642 // draw masked border over door area
5643 DrawMaskedBorder(REDRAW_DOOR_1);
5644 DrawMaskedBorder(REDRAW_DOOR_2);
5646 ClearAutoRepeatKeyEvents();
5648 return (door1 | door2);
5651 static boolean useSpecialEditorDoor(void)
5653 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5654 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5656 // do not draw special editor door if editor border defined or redefined
5657 if (graphic_info[graphic].bitmap != NULL || redefined)
5660 // do not draw special editor door if global border defined to be empty
5661 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5664 // do not draw special editor door if viewport definitions do not match
5668 EY + EYSIZE != VY + VYSIZE)
5674 void DrawSpecialEditorDoor(void)
5676 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5677 int top_border_width = gfx1->width;
5678 int top_border_height = gfx1->height;
5679 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5680 int ex = EX - outer_border;
5681 int ey = EY - outer_border;
5682 int vy = VY - outer_border;
5683 int exsize = EXSIZE + 2 * outer_border;
5685 if (!useSpecialEditorDoor())
5688 // draw bigger level editor toolbox window
5689 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5690 top_border_width, top_border_height, ex, ey - top_border_height);
5691 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5692 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5694 redraw_mask |= REDRAW_ALL;
5697 void UndrawSpecialEditorDoor(void)
5699 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5700 int top_border_width = gfx1->width;
5701 int top_border_height = gfx1->height;
5702 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5703 int ex = EX - outer_border;
5704 int ey = EY - outer_border;
5705 int ey_top = ey - top_border_height;
5706 int exsize = EXSIZE + 2 * outer_border;
5707 int eysize = EYSIZE + 2 * outer_border;
5709 if (!useSpecialEditorDoor())
5712 // draw normal tape recorder window
5713 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5715 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5716 ex, ey_top, top_border_width, top_border_height,
5718 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5719 ex, ey, exsize, eysize, ex, ey);
5723 // if screen background is set to "[NONE]", clear editor toolbox window
5724 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5725 ClearRectangle(drawto, ex, ey, exsize, eysize);
5728 redraw_mask |= REDRAW_ALL;
5732 // ---------- new tool button stuff -------------------------------------------
5737 struct TextPosInfo *pos;
5739 boolean is_touch_button;
5741 } toolbutton_info[NUM_TOOL_BUTTONS] =
5744 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5745 TOOL_CTRL_ID_YES, FALSE, "yes"
5748 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5749 TOOL_CTRL_ID_NO, FALSE, "no"
5752 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5753 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5756 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5757 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5760 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5761 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5764 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5765 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5768 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5769 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5772 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5773 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5776 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5777 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5780 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5781 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5785 void CreateToolButtons(void)
5789 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5791 int graphic = toolbutton_info[i].graphic;
5792 struct GraphicInfo *gfx = &graphic_info[graphic];
5793 struct TextPosInfo *pos = toolbutton_info[i].pos;
5794 struct GadgetInfo *gi;
5795 Bitmap *deco_bitmap = None;
5796 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5797 unsigned int event_mask = GD_EVENT_RELEASED;
5798 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5799 int base_x = (is_touch_button ? 0 : DX);
5800 int base_y = (is_touch_button ? 0 : DY);
5801 int gd_x = gfx->src_x;
5802 int gd_y = gfx->src_y;
5803 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5804 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5809 if (global.use_envelope_request && !is_touch_button)
5811 setRequestPosition(&base_x, &base_y, TRUE);
5813 // check if request buttons are outside of envelope and fix, if needed
5814 if (x < 0 || x + gfx->width > request.width ||
5815 y < 0 || y + gfx->height > request.height)
5817 if (id == TOOL_CTRL_ID_YES)
5820 y = request.height - 2 * request.border_size - gfx->height;
5822 else if (id == TOOL_CTRL_ID_NO)
5824 x = request.width - 2 * request.border_size - gfx->width;
5825 y = request.height - 2 * request.border_size - gfx->height;
5827 else if (id == TOOL_CTRL_ID_CONFIRM)
5829 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5830 y = request.height - 2 * request.border_size - gfx->height;
5832 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5834 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5836 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5837 y = request.height - 2 * request.border_size - gfx->height * 2;
5839 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5840 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5845 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5847 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5849 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5850 pos->size, &deco_bitmap, &deco_x, &deco_y);
5851 deco_xpos = (gfx->width - pos->size) / 2;
5852 deco_ypos = (gfx->height - pos->size) / 2;
5855 gi = CreateGadget(GDI_CUSTOM_ID, id,
5856 GDI_IMAGE_ID, graphic,
5857 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5860 GDI_WIDTH, gfx->width,
5861 GDI_HEIGHT, gfx->height,
5862 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5863 GDI_STATE, GD_BUTTON_UNPRESSED,
5864 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5865 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5866 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5867 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5868 GDI_DECORATION_SIZE, pos->size, pos->size,
5869 GDI_DECORATION_SHIFTING, 1, 1,
5870 GDI_DIRECT_DRAW, FALSE,
5871 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5872 GDI_EVENT_MASK, event_mask,
5873 GDI_CALLBACK_ACTION, HandleToolButtons,
5877 Fail("cannot create gadget");
5879 tool_gadget[id] = gi;
5883 void FreeToolButtons(void)
5887 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5888 FreeGadget(tool_gadget[i]);
5891 static void UnmapToolButtons(void)
5895 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5896 UnmapGadget(tool_gadget[i]);
5899 static void HandleToolButtons(struct GadgetInfo *gi)
5901 request_gadget_id = gi->custom_id;
5904 static struct Mapping_EM_to_RND_object
5907 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5908 boolean is_backside; // backside of moving element
5914 em_object_mapping_list[GAME_TILE_MAX + 1] =
5917 Zborder, FALSE, FALSE,
5921 Zplayer, FALSE, FALSE,
5930 Ztank, FALSE, FALSE,
5934 Zeater, FALSE, FALSE,
5938 Zdynamite, FALSE, FALSE,
5942 Zboom, FALSE, FALSE,
5947 Xchain, FALSE, FALSE,
5948 EL_DEFAULT, ACTION_EXPLODING, -1
5951 Xboom_bug, FALSE, FALSE,
5952 EL_BUG, ACTION_EXPLODING, -1
5955 Xboom_tank, FALSE, FALSE,
5956 EL_SPACESHIP, ACTION_EXPLODING, -1
5959 Xboom_android, FALSE, FALSE,
5960 EL_EMC_ANDROID, ACTION_OTHER, -1
5963 Xboom_1, FALSE, FALSE,
5964 EL_DEFAULT, ACTION_EXPLODING, -1
5967 Xboom_2, FALSE, FALSE,
5968 EL_DEFAULT, ACTION_EXPLODING, -1
5972 Xblank, TRUE, FALSE,
5977 Xsplash_e, FALSE, FALSE,
5978 EL_ACID_SPLASH_RIGHT, -1, -1
5981 Xsplash_w, FALSE, FALSE,
5982 EL_ACID_SPLASH_LEFT, -1, -1
5986 Xplant, TRUE, FALSE,
5987 EL_EMC_PLANT, -1, -1
5990 Yplant, FALSE, FALSE,
5991 EL_EMC_PLANT, -1, -1
5995 Xacid_1, TRUE, FALSE,
5999 Xacid_2, FALSE, FALSE,
6003 Xacid_3, FALSE, FALSE,
6007 Xacid_4, FALSE, FALSE,
6011 Xacid_5, FALSE, FALSE,
6015 Xacid_6, FALSE, FALSE,
6019 Xacid_7, FALSE, FALSE,
6023 Xacid_8, FALSE, FALSE,
6028 Xfake_acid_1, TRUE, FALSE,
6029 EL_EMC_FAKE_ACID, -1, -1
6032 Xfake_acid_2, FALSE, FALSE,
6033 EL_EMC_FAKE_ACID, -1, -1
6036 Xfake_acid_3, FALSE, FALSE,
6037 EL_EMC_FAKE_ACID, -1, -1
6040 Xfake_acid_4, FALSE, FALSE,
6041 EL_EMC_FAKE_ACID, -1, -1
6044 Xfake_acid_5, FALSE, FALSE,
6045 EL_EMC_FAKE_ACID, -1, -1
6048 Xfake_acid_6, FALSE, FALSE,
6049 EL_EMC_FAKE_ACID, -1, -1
6052 Xfake_acid_7, FALSE, FALSE,
6053 EL_EMC_FAKE_ACID, -1, -1
6056 Xfake_acid_8, FALSE, FALSE,
6057 EL_EMC_FAKE_ACID, -1, -1
6061 Xfake_acid_1_player, FALSE, FALSE,
6062 EL_EMC_FAKE_ACID, -1, -1
6065 Xfake_acid_2_player, FALSE, FALSE,
6066 EL_EMC_FAKE_ACID, -1, -1
6069 Xfake_acid_3_player, FALSE, FALSE,
6070 EL_EMC_FAKE_ACID, -1, -1
6073 Xfake_acid_4_player, FALSE, FALSE,
6074 EL_EMC_FAKE_ACID, -1, -1
6077 Xfake_acid_5_player, FALSE, FALSE,
6078 EL_EMC_FAKE_ACID, -1, -1
6081 Xfake_acid_6_player, FALSE, FALSE,
6082 EL_EMC_FAKE_ACID, -1, -1
6085 Xfake_acid_7_player, FALSE, FALSE,
6086 EL_EMC_FAKE_ACID, -1, -1
6089 Xfake_acid_8_player, FALSE, FALSE,
6090 EL_EMC_FAKE_ACID, -1, -1
6094 Xgrass, TRUE, FALSE,
6095 EL_EMC_GRASS, -1, -1
6098 Ygrass_nB, FALSE, FALSE,
6099 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6102 Ygrass_eB, FALSE, FALSE,
6103 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6106 Ygrass_sB, FALSE, FALSE,
6107 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6110 Ygrass_wB, FALSE, FALSE,
6111 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6119 Ydirt_nB, FALSE, FALSE,
6120 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6123 Ydirt_eB, FALSE, FALSE,
6124 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6127 Ydirt_sB, FALSE, FALSE,
6128 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6131 Ydirt_wB, FALSE, FALSE,
6132 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6136 Xandroid, TRUE, FALSE,
6137 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6140 Xandroid_1_n, FALSE, FALSE,
6141 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6144 Xandroid_2_n, FALSE, FALSE,
6145 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6148 Xandroid_1_e, FALSE, FALSE,
6149 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6152 Xandroid_2_e, FALSE, FALSE,
6153 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6156 Xandroid_1_w, FALSE, FALSE,
6157 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6160 Xandroid_2_w, FALSE, FALSE,
6161 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6164 Xandroid_1_s, FALSE, FALSE,
6165 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6168 Xandroid_2_s, FALSE, FALSE,
6169 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6172 Yandroid_n, FALSE, FALSE,
6173 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6176 Yandroid_nB, FALSE, TRUE,
6177 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6180 Yandroid_ne, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6184 Yandroid_neB, FALSE, TRUE,
6185 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6188 Yandroid_e, FALSE, FALSE,
6189 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6192 Yandroid_eB, FALSE, TRUE,
6193 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6196 Yandroid_se, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6200 Yandroid_seB, FALSE, TRUE,
6201 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6204 Yandroid_s, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6208 Yandroid_sB, FALSE, TRUE,
6209 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6212 Yandroid_sw, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6216 Yandroid_swB, FALSE, TRUE,
6217 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6220 Yandroid_w, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6224 Yandroid_wB, FALSE, TRUE,
6225 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6228 Yandroid_nw, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6232 Yandroid_nwB, FALSE, TRUE,
6233 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6237 Xeater_n, TRUE, FALSE,
6238 EL_YAMYAM_UP, -1, -1
6241 Xeater_e, TRUE, FALSE,
6242 EL_YAMYAM_RIGHT, -1, -1
6245 Xeater_w, TRUE, FALSE,
6246 EL_YAMYAM_LEFT, -1, -1
6249 Xeater_s, TRUE, FALSE,
6250 EL_YAMYAM_DOWN, -1, -1
6253 Yeater_n, FALSE, FALSE,
6254 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6257 Yeater_nB, FALSE, TRUE,
6258 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6261 Yeater_e, FALSE, FALSE,
6262 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6265 Yeater_eB, FALSE, TRUE,
6266 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6269 Yeater_s, FALSE, FALSE,
6270 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6273 Yeater_sB, FALSE, TRUE,
6274 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6277 Yeater_w, FALSE, FALSE,
6278 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6281 Yeater_wB, FALSE, TRUE,
6282 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6285 Yeater_stone, FALSE, FALSE,
6286 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6289 Yeater_spring, FALSE, FALSE,
6290 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6294 Xalien, TRUE, FALSE,
6298 Xalien_pause, FALSE, FALSE,
6302 Yalien_n, FALSE, FALSE,
6303 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6306 Yalien_nB, FALSE, TRUE,
6307 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6310 Yalien_e, FALSE, FALSE,
6311 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6314 Yalien_eB, FALSE, TRUE,
6315 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6318 Yalien_s, FALSE, FALSE,
6319 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6322 Yalien_sB, FALSE, TRUE,
6323 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6326 Yalien_w, FALSE, FALSE,
6327 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6330 Yalien_wB, FALSE, TRUE,
6331 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6334 Yalien_stone, FALSE, FALSE,
6335 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6338 Yalien_spring, FALSE, FALSE,
6339 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6343 Xbug_1_n, TRUE, FALSE,
6347 Xbug_1_e, TRUE, FALSE,
6348 EL_BUG_RIGHT, -1, -1
6351 Xbug_1_s, TRUE, FALSE,
6355 Xbug_1_w, TRUE, FALSE,
6359 Xbug_2_n, FALSE, FALSE,
6363 Xbug_2_e, FALSE, FALSE,
6364 EL_BUG_RIGHT, -1, -1
6367 Xbug_2_s, FALSE, FALSE,
6371 Xbug_2_w, FALSE, FALSE,
6375 Ybug_n, FALSE, FALSE,
6376 EL_BUG, ACTION_MOVING, MV_BIT_UP
6379 Ybug_nB, FALSE, TRUE,
6380 EL_BUG, ACTION_MOVING, MV_BIT_UP
6383 Ybug_e, FALSE, FALSE,
6384 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6387 Ybug_eB, FALSE, TRUE,
6388 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6391 Ybug_s, FALSE, FALSE,
6392 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6395 Ybug_sB, FALSE, TRUE,
6396 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6399 Ybug_w, FALSE, FALSE,
6400 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6403 Ybug_wB, FALSE, TRUE,
6404 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6407 Ybug_w_n, FALSE, FALSE,
6408 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6411 Ybug_n_e, FALSE, FALSE,
6412 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6415 Ybug_e_s, FALSE, FALSE,
6416 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6419 Ybug_s_w, FALSE, FALSE,
6420 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6423 Ybug_e_n, FALSE, FALSE,
6424 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6427 Ybug_s_e, FALSE, FALSE,
6428 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6431 Ybug_w_s, FALSE, FALSE,
6432 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6435 Ybug_n_w, FALSE, FALSE,
6436 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6439 Ybug_stone, FALSE, FALSE,
6440 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6443 Ybug_spring, FALSE, FALSE,
6444 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6448 Xtank_1_n, TRUE, FALSE,
6449 EL_SPACESHIP_UP, -1, -1
6452 Xtank_1_e, TRUE, FALSE,
6453 EL_SPACESHIP_RIGHT, -1, -1
6456 Xtank_1_s, TRUE, FALSE,
6457 EL_SPACESHIP_DOWN, -1, -1
6460 Xtank_1_w, TRUE, FALSE,
6461 EL_SPACESHIP_LEFT, -1, -1
6464 Xtank_2_n, FALSE, FALSE,
6465 EL_SPACESHIP_UP, -1, -1
6468 Xtank_2_e, FALSE, FALSE,
6469 EL_SPACESHIP_RIGHT, -1, -1
6472 Xtank_2_s, FALSE, FALSE,
6473 EL_SPACESHIP_DOWN, -1, -1
6476 Xtank_2_w, FALSE, FALSE,
6477 EL_SPACESHIP_LEFT, -1, -1
6480 Ytank_n, FALSE, FALSE,
6481 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6484 Ytank_nB, FALSE, TRUE,
6485 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6488 Ytank_e, FALSE, FALSE,
6489 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6492 Ytank_eB, FALSE, TRUE,
6493 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6496 Ytank_s, FALSE, FALSE,
6497 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6500 Ytank_sB, FALSE, TRUE,
6501 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6504 Ytank_w, FALSE, FALSE,
6505 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6508 Ytank_wB, FALSE, TRUE,
6509 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6512 Ytank_w_n, FALSE, FALSE,
6513 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6516 Ytank_n_e, FALSE, FALSE,
6517 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6520 Ytank_e_s, FALSE, FALSE,
6521 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6524 Ytank_s_w, FALSE, FALSE,
6525 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6528 Ytank_e_n, FALSE, FALSE,
6529 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6532 Ytank_s_e, FALSE, FALSE,
6533 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6536 Ytank_w_s, FALSE, FALSE,
6537 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6540 Ytank_n_w, FALSE, FALSE,
6541 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6544 Ytank_stone, FALSE, FALSE,
6545 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6548 Ytank_spring, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6553 Xemerald, TRUE, FALSE,
6557 Xemerald_pause, FALSE, FALSE,
6561 Xemerald_fall, FALSE, FALSE,
6565 Xemerald_shine, FALSE, FALSE,
6566 EL_EMERALD, ACTION_TWINKLING, -1
6569 Yemerald_s, FALSE, FALSE,
6570 EL_EMERALD, ACTION_FALLING, -1
6573 Yemerald_sB, FALSE, TRUE,
6574 EL_EMERALD, ACTION_FALLING, -1
6577 Yemerald_e, FALSE, FALSE,
6578 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6581 Yemerald_eB, FALSE, TRUE,
6582 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6585 Yemerald_w, FALSE, FALSE,
6586 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6589 Yemerald_wB, FALSE, TRUE,
6590 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6593 Yemerald_blank, FALSE, FALSE,
6594 EL_EMERALD, ACTION_COLLECTING, -1
6598 Xdiamond, TRUE, FALSE,
6602 Xdiamond_pause, FALSE, FALSE,
6606 Xdiamond_fall, FALSE, FALSE,
6610 Xdiamond_shine, FALSE, FALSE,
6611 EL_DIAMOND, ACTION_TWINKLING, -1
6614 Ydiamond_s, FALSE, FALSE,
6615 EL_DIAMOND, ACTION_FALLING, -1
6618 Ydiamond_sB, FALSE, TRUE,
6619 EL_DIAMOND, ACTION_FALLING, -1
6622 Ydiamond_e, FALSE, FALSE,
6623 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6626 Ydiamond_eB, FALSE, TRUE,
6627 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6630 Ydiamond_w, FALSE, FALSE,
6631 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6634 Ydiamond_wB, FALSE, TRUE,
6635 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6638 Ydiamond_blank, FALSE, FALSE,
6639 EL_DIAMOND, ACTION_COLLECTING, -1
6642 Ydiamond_stone, FALSE, FALSE,
6643 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6647 Xstone, TRUE, FALSE,
6651 Xstone_pause, FALSE, FALSE,
6655 Xstone_fall, FALSE, FALSE,
6659 Ystone_s, FALSE, FALSE,
6660 EL_ROCK, ACTION_FALLING, -1
6663 Ystone_sB, FALSE, TRUE,
6664 EL_ROCK, ACTION_FALLING, -1
6667 Ystone_e, FALSE, FALSE,
6668 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6671 Ystone_eB, FALSE, TRUE,
6672 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6675 Ystone_w, FALSE, FALSE,
6676 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6679 Ystone_wB, FALSE, TRUE,
6680 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6688 Xbomb_pause, FALSE, FALSE,
6692 Xbomb_fall, FALSE, FALSE,
6696 Ybomb_s, FALSE, FALSE,
6697 EL_BOMB, ACTION_FALLING, -1
6700 Ybomb_sB, FALSE, TRUE,
6701 EL_BOMB, ACTION_FALLING, -1
6704 Ybomb_e, FALSE, FALSE,
6705 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6708 Ybomb_eB, FALSE, TRUE,
6709 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6712 Ybomb_w, FALSE, FALSE,
6713 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6716 Ybomb_wB, FALSE, TRUE,
6717 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6720 Ybomb_blank, FALSE, FALSE,
6721 EL_BOMB, ACTION_ACTIVATING, -1
6729 Xnut_pause, FALSE, FALSE,
6733 Xnut_fall, FALSE, FALSE,
6737 Ynut_s, FALSE, FALSE,
6738 EL_NUT, ACTION_FALLING, -1
6741 Ynut_sB, FALSE, TRUE,
6742 EL_NUT, ACTION_FALLING, -1
6745 Ynut_e, FALSE, FALSE,
6746 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6749 Ynut_eB, FALSE, TRUE,
6750 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6753 Ynut_w, FALSE, FALSE,
6754 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6757 Ynut_wB, FALSE, TRUE,
6758 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6761 Ynut_stone, FALSE, FALSE,
6762 EL_NUT, ACTION_BREAKING, -1
6766 Xspring, TRUE, FALSE,
6770 Xspring_pause, FALSE, FALSE,
6774 Xspring_e, TRUE, FALSE,
6775 EL_SPRING_RIGHT, -1, -1
6778 Xspring_w, TRUE, FALSE,
6779 EL_SPRING_LEFT, -1, -1
6782 Xspring_fall, FALSE, FALSE,
6786 Yspring_s, FALSE, FALSE,
6787 EL_SPRING, ACTION_FALLING, -1
6790 Yspring_sB, FALSE, TRUE,
6791 EL_SPRING, ACTION_FALLING, -1
6794 Yspring_e, FALSE, FALSE,
6795 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6798 Yspring_eB, FALSE, TRUE,
6799 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6802 Yspring_w, FALSE, FALSE,
6803 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6806 Yspring_wB, FALSE, TRUE,
6807 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6810 Yspring_alien_e, FALSE, FALSE,
6811 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6814 Yspring_alien_eB, FALSE, TRUE,
6815 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6818 Yspring_alien_w, FALSE, FALSE,
6819 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6822 Yspring_alien_wB, FALSE, TRUE,
6823 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6827 Xpush_emerald_e, FALSE, FALSE,
6828 EL_EMERALD, -1, MV_BIT_RIGHT
6831 Xpush_emerald_w, FALSE, FALSE,
6832 EL_EMERALD, -1, MV_BIT_LEFT
6835 Xpush_diamond_e, FALSE, FALSE,
6836 EL_DIAMOND, -1, MV_BIT_RIGHT
6839 Xpush_diamond_w, FALSE, FALSE,
6840 EL_DIAMOND, -1, MV_BIT_LEFT
6843 Xpush_stone_e, FALSE, FALSE,
6844 EL_ROCK, -1, MV_BIT_RIGHT
6847 Xpush_stone_w, FALSE, FALSE,
6848 EL_ROCK, -1, MV_BIT_LEFT
6851 Xpush_bomb_e, FALSE, FALSE,
6852 EL_BOMB, -1, MV_BIT_RIGHT
6855 Xpush_bomb_w, FALSE, FALSE,
6856 EL_BOMB, -1, MV_BIT_LEFT
6859 Xpush_nut_e, FALSE, FALSE,
6860 EL_NUT, -1, MV_BIT_RIGHT
6863 Xpush_nut_w, FALSE, FALSE,
6864 EL_NUT, -1, MV_BIT_LEFT
6867 Xpush_spring_e, FALSE, FALSE,
6868 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6871 Xpush_spring_w, FALSE, FALSE,
6872 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6876 Xdynamite, TRUE, FALSE,
6877 EL_EM_DYNAMITE, -1, -1
6880 Ydynamite_blank, FALSE, FALSE,
6881 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6884 Xdynamite_1, TRUE, FALSE,
6885 EL_EM_DYNAMITE_ACTIVE, -1, -1
6888 Xdynamite_2, FALSE, FALSE,
6889 EL_EM_DYNAMITE_ACTIVE, -1, -1
6892 Xdynamite_3, FALSE, FALSE,
6893 EL_EM_DYNAMITE_ACTIVE, -1, -1
6896 Xdynamite_4, FALSE, FALSE,
6897 EL_EM_DYNAMITE_ACTIVE, -1, -1
6901 Xkey_1, TRUE, FALSE,
6905 Xkey_2, TRUE, FALSE,
6909 Xkey_3, TRUE, FALSE,
6913 Xkey_4, TRUE, FALSE,
6917 Xkey_5, TRUE, FALSE,
6918 EL_EMC_KEY_5, -1, -1
6921 Xkey_6, TRUE, FALSE,
6922 EL_EMC_KEY_6, -1, -1
6925 Xkey_7, TRUE, FALSE,
6926 EL_EMC_KEY_7, -1, -1
6929 Xkey_8, TRUE, FALSE,
6930 EL_EMC_KEY_8, -1, -1
6934 Xdoor_1, TRUE, FALSE,
6935 EL_EM_GATE_1, -1, -1
6938 Xdoor_2, TRUE, FALSE,
6939 EL_EM_GATE_2, -1, -1
6942 Xdoor_3, TRUE, FALSE,
6943 EL_EM_GATE_3, -1, -1
6946 Xdoor_4, TRUE, FALSE,
6947 EL_EM_GATE_4, -1, -1
6950 Xdoor_5, TRUE, FALSE,
6951 EL_EMC_GATE_5, -1, -1
6954 Xdoor_6, TRUE, FALSE,
6955 EL_EMC_GATE_6, -1, -1
6958 Xdoor_7, TRUE, FALSE,
6959 EL_EMC_GATE_7, -1, -1
6962 Xdoor_8, TRUE, FALSE,
6963 EL_EMC_GATE_8, -1, -1
6967 Xfake_door_1, TRUE, FALSE,
6968 EL_EM_GATE_1_GRAY, -1, -1
6971 Xfake_door_2, TRUE, FALSE,
6972 EL_EM_GATE_2_GRAY, -1, -1
6975 Xfake_door_3, TRUE, FALSE,
6976 EL_EM_GATE_3_GRAY, -1, -1
6979 Xfake_door_4, TRUE, FALSE,
6980 EL_EM_GATE_4_GRAY, -1, -1
6983 Xfake_door_5, TRUE, FALSE,
6984 EL_EMC_GATE_5_GRAY, -1, -1
6987 Xfake_door_6, TRUE, FALSE,
6988 EL_EMC_GATE_6_GRAY, -1, -1
6991 Xfake_door_7, TRUE, FALSE,
6992 EL_EMC_GATE_7_GRAY, -1, -1
6995 Xfake_door_8, TRUE, FALSE,
6996 EL_EMC_GATE_8_GRAY, -1, -1
7000 Xballoon, TRUE, FALSE,
7004 Yballoon_n, FALSE, FALSE,
7005 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7008 Yballoon_nB, FALSE, TRUE,
7009 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7012 Yballoon_e, FALSE, FALSE,
7013 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7016 Yballoon_eB, FALSE, TRUE,
7017 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7020 Yballoon_s, FALSE, FALSE,
7021 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7024 Yballoon_sB, FALSE, TRUE,
7025 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7028 Yballoon_w, FALSE, FALSE,
7029 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7032 Yballoon_wB, FALSE, TRUE,
7033 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7037 Xball_1, TRUE, FALSE,
7038 EL_EMC_MAGIC_BALL, -1, -1
7041 Yball_1, FALSE, FALSE,
7042 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7045 Xball_2, FALSE, FALSE,
7046 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7049 Yball_2, FALSE, FALSE,
7050 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7053 Yball_blank, FALSE, FALSE,
7054 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7058 Xamoeba_1, TRUE, FALSE,
7059 EL_AMOEBA_DRY, ACTION_OTHER, -1
7062 Xamoeba_2, FALSE, FALSE,
7063 EL_AMOEBA_DRY, ACTION_OTHER, -1
7066 Xamoeba_3, FALSE, FALSE,
7067 EL_AMOEBA_DRY, ACTION_OTHER, -1
7070 Xamoeba_4, FALSE, FALSE,
7071 EL_AMOEBA_DRY, ACTION_OTHER, -1
7074 Xamoeba_5, TRUE, FALSE,
7075 EL_AMOEBA_WET, ACTION_OTHER, -1
7078 Xamoeba_6, FALSE, FALSE,
7079 EL_AMOEBA_WET, ACTION_OTHER, -1
7082 Xamoeba_7, FALSE, FALSE,
7083 EL_AMOEBA_WET, ACTION_OTHER, -1
7086 Xamoeba_8, FALSE, FALSE,
7087 EL_AMOEBA_WET, ACTION_OTHER, -1
7092 EL_AMOEBA_DROP, ACTION_GROWING, -1
7095 Xdrip_fall, FALSE, FALSE,
7096 EL_AMOEBA_DROP, -1, -1
7099 Xdrip_stretch, FALSE, FALSE,
7100 EL_AMOEBA_DROP, ACTION_FALLING, -1
7103 Xdrip_stretchB, FALSE, TRUE,
7104 EL_AMOEBA_DROP, ACTION_FALLING, -1
7107 Ydrip_1_s, FALSE, FALSE,
7108 EL_AMOEBA_DROP, ACTION_FALLING, -1
7111 Ydrip_1_sB, FALSE, TRUE,
7112 EL_AMOEBA_DROP, ACTION_FALLING, -1
7115 Ydrip_2_s, FALSE, FALSE,
7116 EL_AMOEBA_DROP, ACTION_FALLING, -1
7119 Ydrip_2_sB, FALSE, TRUE,
7120 EL_AMOEBA_DROP, ACTION_FALLING, -1
7124 Xwonderwall, TRUE, FALSE,
7125 EL_MAGIC_WALL, -1, -1
7128 Ywonderwall, FALSE, FALSE,
7129 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7133 Xwheel, TRUE, FALSE,
7134 EL_ROBOT_WHEEL, -1, -1
7137 Ywheel, FALSE, FALSE,
7138 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7142 Xswitch, TRUE, FALSE,
7143 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7146 Yswitch, FALSE, FALSE,
7147 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7151 Xbumper, TRUE, FALSE,
7152 EL_EMC_SPRING_BUMPER, -1, -1
7155 Ybumper, FALSE, FALSE,
7156 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7160 Xacid_nw, TRUE, FALSE,
7161 EL_ACID_POOL_TOPLEFT, -1, -1
7164 Xacid_ne, TRUE, FALSE,
7165 EL_ACID_POOL_TOPRIGHT, -1, -1
7168 Xacid_sw, TRUE, FALSE,
7169 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7172 Xacid_s, TRUE, FALSE,
7173 EL_ACID_POOL_BOTTOM, -1, -1
7176 Xacid_se, TRUE, FALSE,
7177 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7181 Xfake_blank, TRUE, FALSE,
7182 EL_INVISIBLE_WALL, -1, -1
7185 Yfake_blank, FALSE, FALSE,
7186 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7190 Xfake_grass, TRUE, FALSE,
7191 EL_EMC_FAKE_GRASS, -1, -1
7194 Yfake_grass, FALSE, FALSE,
7195 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7199 Xfake_amoeba, TRUE, FALSE,
7200 EL_EMC_DRIPPER, -1, -1
7203 Yfake_amoeba, FALSE, FALSE,
7204 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7208 Xlenses, TRUE, FALSE,
7209 EL_EMC_LENSES, -1, -1
7213 Xmagnify, TRUE, FALSE,
7214 EL_EMC_MAGNIFIER, -1, -1
7219 EL_QUICKSAND_EMPTY, -1, -1
7222 Xsand_stone, TRUE, FALSE,
7223 EL_QUICKSAND_FULL, -1, -1
7226 Xsand_stonein_1, FALSE, TRUE,
7227 EL_ROCK, ACTION_FILLING, -1
7230 Xsand_stonein_2, FALSE, TRUE,
7231 EL_ROCK, ACTION_FILLING, -1
7234 Xsand_stonein_3, FALSE, TRUE,
7235 EL_ROCK, ACTION_FILLING, -1
7238 Xsand_stonein_4, FALSE, TRUE,
7239 EL_ROCK, ACTION_FILLING, -1
7242 Xsand_sandstone_1, FALSE, FALSE,
7243 EL_QUICKSAND_FILLING, -1, -1
7246 Xsand_sandstone_2, FALSE, FALSE,
7247 EL_QUICKSAND_FILLING, -1, -1
7250 Xsand_sandstone_3, FALSE, FALSE,
7251 EL_QUICKSAND_FILLING, -1, -1
7254 Xsand_sandstone_4, FALSE, FALSE,
7255 EL_QUICKSAND_FILLING, -1, -1
7258 Xsand_stonesand_1, FALSE, FALSE,
7259 EL_QUICKSAND_EMPTYING, -1, -1
7262 Xsand_stonesand_2, FALSE, FALSE,
7263 EL_QUICKSAND_EMPTYING, -1, -1
7266 Xsand_stonesand_3, FALSE, FALSE,
7267 EL_QUICKSAND_EMPTYING, -1, -1
7270 Xsand_stonesand_4, FALSE, FALSE,
7271 EL_QUICKSAND_EMPTYING, -1, -1
7274 Xsand_stoneout_1, FALSE, FALSE,
7275 EL_ROCK, ACTION_EMPTYING, -1
7278 Xsand_stoneout_2, FALSE, FALSE,
7279 EL_ROCK, ACTION_EMPTYING, -1
7282 Xsand_stonesand_quickout_1, FALSE, FALSE,
7283 EL_QUICKSAND_EMPTYING, -1, -1
7286 Xsand_stonesand_quickout_2, FALSE, FALSE,
7287 EL_QUICKSAND_EMPTYING, -1, -1
7291 Xslide_ns, TRUE, FALSE,
7292 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7295 Yslide_ns_blank, FALSE, FALSE,
7296 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7299 Xslide_ew, TRUE, FALSE,
7300 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7303 Yslide_ew_blank, FALSE, FALSE,
7304 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7308 Xwind_n, TRUE, FALSE,
7309 EL_BALLOON_SWITCH_UP, -1, -1
7312 Xwind_e, TRUE, FALSE,
7313 EL_BALLOON_SWITCH_RIGHT, -1, -1
7316 Xwind_s, TRUE, FALSE,
7317 EL_BALLOON_SWITCH_DOWN, -1, -1
7320 Xwind_w, TRUE, FALSE,
7321 EL_BALLOON_SWITCH_LEFT, -1, -1
7324 Xwind_any, TRUE, FALSE,
7325 EL_BALLOON_SWITCH_ANY, -1, -1
7328 Xwind_stop, TRUE, FALSE,
7329 EL_BALLOON_SWITCH_NONE, -1, -1
7334 EL_EM_EXIT_CLOSED, -1, -1
7337 Xexit_1, TRUE, FALSE,
7338 EL_EM_EXIT_OPEN, -1, -1
7341 Xexit_2, FALSE, FALSE,
7342 EL_EM_EXIT_OPEN, -1, -1
7345 Xexit_3, FALSE, FALSE,
7346 EL_EM_EXIT_OPEN, -1, -1
7350 Xpause, FALSE, FALSE,
7355 Xwall_1, TRUE, FALSE,
7359 Xwall_2, TRUE, FALSE,
7360 EL_EMC_WALL_14, -1, -1
7363 Xwall_3, TRUE, FALSE,
7364 EL_EMC_WALL_15, -1, -1
7367 Xwall_4, TRUE, FALSE,
7368 EL_EMC_WALL_16, -1, -1
7372 Xroundwall_1, TRUE, FALSE,
7373 EL_WALL_SLIPPERY, -1, -1
7376 Xroundwall_2, TRUE, FALSE,
7377 EL_EMC_WALL_SLIPPERY_2, -1, -1
7380 Xroundwall_3, TRUE, FALSE,
7381 EL_EMC_WALL_SLIPPERY_3, -1, -1
7384 Xroundwall_4, TRUE, FALSE,
7385 EL_EMC_WALL_SLIPPERY_4, -1, -1
7389 Xsteel_1, TRUE, FALSE,
7390 EL_STEELWALL, -1, -1
7393 Xsteel_2, TRUE, FALSE,
7394 EL_EMC_STEELWALL_2, -1, -1
7397 Xsteel_3, TRUE, FALSE,
7398 EL_EMC_STEELWALL_3, -1, -1
7401 Xsteel_4, TRUE, FALSE,
7402 EL_EMC_STEELWALL_4, -1, -1
7406 Xdecor_1, TRUE, FALSE,
7407 EL_EMC_WALL_8, -1, -1
7410 Xdecor_2, TRUE, FALSE,
7411 EL_EMC_WALL_6, -1, -1
7414 Xdecor_3, TRUE, FALSE,
7415 EL_EMC_WALL_4, -1, -1
7418 Xdecor_4, TRUE, FALSE,
7419 EL_EMC_WALL_7, -1, -1
7422 Xdecor_5, TRUE, FALSE,
7423 EL_EMC_WALL_5, -1, -1
7426 Xdecor_6, TRUE, FALSE,
7427 EL_EMC_WALL_9, -1, -1
7430 Xdecor_7, TRUE, FALSE,
7431 EL_EMC_WALL_10, -1, -1
7434 Xdecor_8, TRUE, FALSE,
7435 EL_EMC_WALL_1, -1, -1
7438 Xdecor_9, TRUE, FALSE,
7439 EL_EMC_WALL_2, -1, -1
7442 Xdecor_10, TRUE, FALSE,
7443 EL_EMC_WALL_3, -1, -1
7446 Xdecor_11, TRUE, FALSE,
7447 EL_EMC_WALL_11, -1, -1
7450 Xdecor_12, TRUE, FALSE,
7451 EL_EMC_WALL_12, -1, -1
7455 Xalpha_0, TRUE, FALSE,
7456 EL_CHAR('0'), -1, -1
7459 Xalpha_1, TRUE, FALSE,
7460 EL_CHAR('1'), -1, -1
7463 Xalpha_2, TRUE, FALSE,
7464 EL_CHAR('2'), -1, -1
7467 Xalpha_3, TRUE, FALSE,
7468 EL_CHAR('3'), -1, -1
7471 Xalpha_4, TRUE, FALSE,
7472 EL_CHAR('4'), -1, -1
7475 Xalpha_5, TRUE, FALSE,
7476 EL_CHAR('5'), -1, -1
7479 Xalpha_6, TRUE, FALSE,
7480 EL_CHAR('6'), -1, -1
7483 Xalpha_7, TRUE, FALSE,
7484 EL_CHAR('7'), -1, -1
7487 Xalpha_8, TRUE, FALSE,
7488 EL_CHAR('8'), -1, -1
7491 Xalpha_9, TRUE, FALSE,
7492 EL_CHAR('9'), -1, -1
7495 Xalpha_excla, TRUE, FALSE,
7496 EL_CHAR('!'), -1, -1
7499 Xalpha_apost, TRUE, FALSE,
7500 EL_CHAR('\''), -1, -1
7503 Xalpha_comma, TRUE, FALSE,
7504 EL_CHAR(','), -1, -1
7507 Xalpha_minus, TRUE, FALSE,
7508 EL_CHAR('-'), -1, -1
7511 Xalpha_perio, TRUE, FALSE,
7512 EL_CHAR('.'), -1, -1
7515 Xalpha_colon, TRUE, FALSE,
7516 EL_CHAR(':'), -1, -1
7519 Xalpha_quest, TRUE, FALSE,
7520 EL_CHAR('?'), -1, -1
7523 Xalpha_a, TRUE, FALSE,
7524 EL_CHAR('A'), -1, -1
7527 Xalpha_b, TRUE, FALSE,
7528 EL_CHAR('B'), -1, -1
7531 Xalpha_c, TRUE, FALSE,
7532 EL_CHAR('C'), -1, -1
7535 Xalpha_d, TRUE, FALSE,
7536 EL_CHAR('D'), -1, -1
7539 Xalpha_e, TRUE, FALSE,
7540 EL_CHAR('E'), -1, -1
7543 Xalpha_f, TRUE, FALSE,
7544 EL_CHAR('F'), -1, -1
7547 Xalpha_g, TRUE, FALSE,
7548 EL_CHAR('G'), -1, -1
7551 Xalpha_h, TRUE, FALSE,
7552 EL_CHAR('H'), -1, -1
7555 Xalpha_i, TRUE, FALSE,
7556 EL_CHAR('I'), -1, -1
7559 Xalpha_j, TRUE, FALSE,
7560 EL_CHAR('J'), -1, -1
7563 Xalpha_k, TRUE, FALSE,
7564 EL_CHAR('K'), -1, -1
7567 Xalpha_l, TRUE, FALSE,
7568 EL_CHAR('L'), -1, -1
7571 Xalpha_m, TRUE, FALSE,
7572 EL_CHAR('M'), -1, -1
7575 Xalpha_n, TRUE, FALSE,
7576 EL_CHAR('N'), -1, -1
7579 Xalpha_o, TRUE, FALSE,
7580 EL_CHAR('O'), -1, -1
7583 Xalpha_p, TRUE, FALSE,
7584 EL_CHAR('P'), -1, -1
7587 Xalpha_q, TRUE, FALSE,
7588 EL_CHAR('Q'), -1, -1
7591 Xalpha_r, TRUE, FALSE,
7592 EL_CHAR('R'), -1, -1
7595 Xalpha_s, TRUE, FALSE,
7596 EL_CHAR('S'), -1, -1
7599 Xalpha_t, TRUE, FALSE,
7600 EL_CHAR('T'), -1, -1
7603 Xalpha_u, TRUE, FALSE,
7604 EL_CHAR('U'), -1, -1
7607 Xalpha_v, TRUE, FALSE,
7608 EL_CHAR('V'), -1, -1
7611 Xalpha_w, TRUE, FALSE,
7612 EL_CHAR('W'), -1, -1
7615 Xalpha_x, TRUE, FALSE,
7616 EL_CHAR('X'), -1, -1
7619 Xalpha_y, TRUE, FALSE,
7620 EL_CHAR('Y'), -1, -1
7623 Xalpha_z, TRUE, FALSE,
7624 EL_CHAR('Z'), -1, -1
7627 Xalpha_arrow_e, TRUE, FALSE,
7628 EL_CHAR('>'), -1, -1
7631 Xalpha_arrow_w, TRUE, FALSE,
7632 EL_CHAR('<'), -1, -1
7635 Xalpha_copyr, TRUE, FALSE,
7636 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7640 Ykey_1_blank, FALSE, FALSE,
7641 EL_EM_KEY_1, ACTION_COLLECTING, -1
7644 Ykey_2_blank, FALSE, FALSE,
7645 EL_EM_KEY_2, ACTION_COLLECTING, -1
7648 Ykey_3_blank, FALSE, FALSE,
7649 EL_EM_KEY_3, ACTION_COLLECTING, -1
7652 Ykey_4_blank, FALSE, FALSE,
7653 EL_EM_KEY_4, ACTION_COLLECTING, -1
7656 Ykey_5_blank, FALSE, FALSE,
7657 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7660 Ykey_6_blank, FALSE, FALSE,
7661 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7664 Ykey_7_blank, FALSE, FALSE,
7665 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7668 Ykey_8_blank, FALSE, FALSE,
7669 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7672 Ylenses_blank, FALSE, FALSE,
7673 EL_EMC_LENSES, ACTION_COLLECTING, -1
7676 Ymagnify_blank, FALSE, FALSE,
7677 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7680 Ygrass_blank, FALSE, FALSE,
7681 EL_EMC_GRASS, ACTION_SNAPPING, -1
7684 Ydirt_blank, FALSE, FALSE,
7685 EL_SAND, ACTION_SNAPPING, -1
7694 static struct Mapping_EM_to_RND_player
7703 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7707 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7711 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7715 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7719 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7723 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7727 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7731 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7735 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7739 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7743 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7747 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7751 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7755 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7759 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7763 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7767 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7771 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7775 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7779 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7783 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7787 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7791 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7795 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7799 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7803 EL_PLAYER_1, ACTION_DEFAULT, -1,
7807 EL_PLAYER_2, ACTION_DEFAULT, -1,
7811 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7815 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7819 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7823 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7827 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7831 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7835 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7839 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7843 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7847 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7851 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7855 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7859 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7863 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7867 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7871 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7875 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7879 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7883 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7887 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7891 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7895 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7899 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7903 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7907 EL_PLAYER_3, ACTION_DEFAULT, -1,
7911 EL_PLAYER_4, ACTION_DEFAULT, -1,
7920 int map_element_RND_to_EM_cave(int element_rnd)
7922 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7923 static boolean mapping_initialized = FALSE;
7925 if (!mapping_initialized)
7929 // return "Xalpha_quest" for all undefined elements in mapping array
7930 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7931 mapping_RND_to_EM[i] = Xalpha_quest;
7933 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7934 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7935 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7936 em_object_mapping_list[i].element_em;
7938 mapping_initialized = TRUE;
7941 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7943 Warn("invalid RND level element %d", element_rnd);
7948 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7951 int map_element_EM_to_RND_cave(int element_em_cave)
7953 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7954 static boolean mapping_initialized = FALSE;
7956 if (!mapping_initialized)
7960 // return "EL_UNKNOWN" for all undefined elements in mapping array
7961 for (i = 0; i < GAME_TILE_MAX; i++)
7962 mapping_EM_to_RND[i] = EL_UNKNOWN;
7964 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7965 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7966 em_object_mapping_list[i].element_rnd;
7968 mapping_initialized = TRUE;
7971 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7973 Warn("invalid EM cave element %d", element_em_cave);
7978 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7981 int map_element_EM_to_RND_game(int element_em_game)
7983 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7984 static boolean mapping_initialized = FALSE;
7986 if (!mapping_initialized)
7990 // return "EL_UNKNOWN" for all undefined elements in mapping array
7991 for (i = 0; i < GAME_TILE_MAX; i++)
7992 mapping_EM_to_RND[i] = EL_UNKNOWN;
7994 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7995 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7996 em_object_mapping_list[i].element_rnd;
7998 mapping_initialized = TRUE;
8001 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8003 Warn("invalid EM game element %d", element_em_game);
8008 return mapping_EM_to_RND[element_em_game];
8011 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8013 struct LevelInfo_EM *level_em = level->native_em_level;
8014 struct CAVE *cav = level_em->cav;
8017 for (i = 0; i < GAME_TILE_MAX; i++)
8018 cav->android_array[i] = Cblank;
8020 for (i = 0; i < level->num_android_clone_elements; i++)
8022 int element_rnd = level->android_clone_element[i];
8023 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8025 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8026 if (em_object_mapping_list[j].element_rnd == element_rnd)
8027 cav->android_array[em_object_mapping_list[j].element_em] =
8032 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8034 struct LevelInfo_EM *level_em = level->native_em_level;
8035 struct CAVE *cav = level_em->cav;
8038 level->num_android_clone_elements = 0;
8040 for (i = 0; i < GAME_TILE_MAX; i++)
8042 int element_em_cave = cav->android_array[i];
8044 boolean element_found = FALSE;
8046 if (element_em_cave == Cblank)
8049 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8051 for (j = 0; j < level->num_android_clone_elements; j++)
8052 if (level->android_clone_element[j] == element_rnd)
8053 element_found = TRUE;
8057 level->android_clone_element[level->num_android_clone_elements++] =
8060 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8065 if (level->num_android_clone_elements == 0)
8067 level->num_android_clone_elements = 1;
8068 level->android_clone_element[0] = EL_EMPTY;
8072 int map_direction_RND_to_EM(int direction)
8074 return (direction == MV_UP ? 0 :
8075 direction == MV_RIGHT ? 1 :
8076 direction == MV_DOWN ? 2 :
8077 direction == MV_LEFT ? 3 :
8081 int map_direction_EM_to_RND(int direction)
8083 return (direction == 0 ? MV_UP :
8084 direction == 1 ? MV_RIGHT :
8085 direction == 2 ? MV_DOWN :
8086 direction == 3 ? MV_LEFT :
8090 int map_element_RND_to_SP(int element_rnd)
8092 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8094 if (element_rnd >= EL_SP_START &&
8095 element_rnd <= EL_SP_END)
8096 element_sp = element_rnd - EL_SP_START;
8097 else if (element_rnd == EL_EMPTY_SPACE)
8099 else if (element_rnd == EL_INVISIBLE_WALL)
8105 int map_element_SP_to_RND(int element_sp)
8107 int element_rnd = EL_UNKNOWN;
8109 if (element_sp >= 0x00 &&
8111 element_rnd = EL_SP_START + element_sp;
8112 else if (element_sp == 0x28)
8113 element_rnd = EL_INVISIBLE_WALL;
8118 int map_action_SP_to_RND(int action_sp)
8122 case actActive: return ACTION_ACTIVE;
8123 case actImpact: return ACTION_IMPACT;
8124 case actExploding: return ACTION_EXPLODING;
8125 case actDigging: return ACTION_DIGGING;
8126 case actSnapping: return ACTION_SNAPPING;
8127 case actCollecting: return ACTION_COLLECTING;
8128 case actPassing: return ACTION_PASSING;
8129 case actPushing: return ACTION_PUSHING;
8130 case actDropping: return ACTION_DROPPING;
8132 default: return ACTION_DEFAULT;
8136 int map_element_RND_to_MM(int element_rnd)
8138 return (element_rnd >= EL_MM_START_1 &&
8139 element_rnd <= EL_MM_END_1 ?
8140 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8142 element_rnd >= EL_MM_START_2 &&
8143 element_rnd <= EL_MM_END_2 ?
8144 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8146 element_rnd >= EL_CHAR_START &&
8147 element_rnd <= EL_CHAR_END ?
8148 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8150 element_rnd >= EL_MM_RUNTIME_START &&
8151 element_rnd <= EL_MM_RUNTIME_END ?
8152 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8154 element_rnd >= EL_MM_DUMMY_START &&
8155 element_rnd <= EL_MM_DUMMY_END ?
8156 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8158 EL_MM_EMPTY_NATIVE);
8161 int map_element_MM_to_RND(int element_mm)
8163 return (element_mm == EL_MM_EMPTY_NATIVE ||
8164 element_mm == EL_DF_EMPTY_NATIVE ?
8167 element_mm >= EL_MM_START_1_NATIVE &&
8168 element_mm <= EL_MM_END_1_NATIVE ?
8169 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8171 element_mm >= EL_MM_START_2_NATIVE &&
8172 element_mm <= EL_MM_END_2_NATIVE ?
8173 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8175 element_mm >= EL_MM_CHAR_START_NATIVE &&
8176 element_mm <= EL_MM_CHAR_END_NATIVE ?
8177 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8179 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8180 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8181 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8183 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8184 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8185 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8190 int map_action_MM_to_RND(int action_mm)
8192 // all MM actions are defined to exactly match their RND counterparts
8196 int map_sound_MM_to_RND(int sound_mm)
8200 case SND_MM_GAME_LEVELTIME_CHARGING:
8201 return SND_GAME_LEVELTIME_CHARGING;
8203 case SND_MM_GAME_HEALTH_CHARGING:
8204 return SND_GAME_HEALTH_CHARGING;
8207 return SND_UNDEFINED;
8211 int map_mm_wall_element(int element)
8213 return (element >= EL_MM_STEEL_WALL_START &&
8214 element <= EL_MM_STEEL_WALL_END ?
8217 element >= EL_MM_WOODEN_WALL_START &&
8218 element <= EL_MM_WOODEN_WALL_END ?
8221 element >= EL_MM_ICE_WALL_START &&
8222 element <= EL_MM_ICE_WALL_END ?
8225 element >= EL_MM_AMOEBA_WALL_START &&
8226 element <= EL_MM_AMOEBA_WALL_END ?
8229 element >= EL_DF_STEEL_WALL_START &&
8230 element <= EL_DF_STEEL_WALL_END ?
8233 element >= EL_DF_WOODEN_WALL_START &&
8234 element <= EL_DF_WOODEN_WALL_END ?
8240 int map_mm_wall_element_editor(int element)
8244 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8245 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8246 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8247 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8248 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8249 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8251 default: return element;
8255 int get_next_element(int element)
8259 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8260 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8261 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8262 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8263 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8264 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8265 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8266 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8267 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8268 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8269 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8271 default: return element;
8275 int el2img_mm(int element_mm)
8277 return el2img(map_element_MM_to_RND(element_mm));
8280 int el_act_dir2img(int element, int action, int direction)
8282 element = GFX_ELEMENT(element);
8283 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8285 // direction_graphic[][] == graphic[] for undefined direction graphics
8286 return element_info[element].direction_graphic[action][direction];
8289 static int el_act_dir2crm(int element, int action, int direction)
8291 element = GFX_ELEMENT(element);
8292 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8294 // direction_graphic[][] == graphic[] for undefined direction graphics
8295 return element_info[element].direction_crumbled[action][direction];
8298 int el_act2img(int element, int action)
8300 element = GFX_ELEMENT(element);
8302 return element_info[element].graphic[action];
8305 int el_act2crm(int element, int action)
8307 element = GFX_ELEMENT(element);
8309 return element_info[element].crumbled[action];
8312 int el_dir2img(int element, int direction)
8314 element = GFX_ELEMENT(element);
8316 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8319 int el2baseimg(int element)
8321 return element_info[element].graphic[ACTION_DEFAULT];
8324 int el2img(int element)
8326 element = GFX_ELEMENT(element);
8328 return element_info[element].graphic[ACTION_DEFAULT];
8331 int el2edimg(int element)
8333 element = GFX_ELEMENT(element);
8335 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8338 int el2preimg(int element)
8340 element = GFX_ELEMENT(element);
8342 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8345 int el2panelimg(int element)
8347 element = GFX_ELEMENT(element);
8349 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8352 int font2baseimg(int font_nr)
8354 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8357 int getBeltNrFromBeltElement(int element)
8359 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8360 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8361 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8364 int getBeltNrFromBeltActiveElement(int element)
8366 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8367 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8368 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8371 int getBeltNrFromBeltSwitchElement(int element)
8373 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8374 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8375 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8378 int getBeltDirNrFromBeltElement(int element)
8380 static int belt_base_element[4] =
8382 EL_CONVEYOR_BELT_1_LEFT,
8383 EL_CONVEYOR_BELT_2_LEFT,
8384 EL_CONVEYOR_BELT_3_LEFT,
8385 EL_CONVEYOR_BELT_4_LEFT
8388 int belt_nr = getBeltNrFromBeltElement(element);
8389 int belt_dir_nr = element - belt_base_element[belt_nr];
8391 return (belt_dir_nr % 3);
8394 int getBeltDirNrFromBeltSwitchElement(int element)
8396 static int belt_base_element[4] =
8398 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8399 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8400 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8401 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8404 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8405 int belt_dir_nr = element - belt_base_element[belt_nr];
8407 return (belt_dir_nr % 3);
8410 int getBeltDirFromBeltElement(int element)
8412 static int belt_move_dir[3] =
8419 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8421 return belt_move_dir[belt_dir_nr];
8424 int getBeltDirFromBeltSwitchElement(int element)
8426 static int belt_move_dir[3] =
8433 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8435 return belt_move_dir[belt_dir_nr];
8438 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8440 static int belt_base_element[4] =
8442 EL_CONVEYOR_BELT_1_LEFT,
8443 EL_CONVEYOR_BELT_2_LEFT,
8444 EL_CONVEYOR_BELT_3_LEFT,
8445 EL_CONVEYOR_BELT_4_LEFT
8448 return belt_base_element[belt_nr] + belt_dir_nr;
8451 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8453 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8455 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8458 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8460 static int belt_base_element[4] =
8462 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8463 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8464 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8465 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8468 return belt_base_element[belt_nr] + belt_dir_nr;
8471 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8473 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8475 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8478 boolean swapTiles_EM(boolean is_pre_emc_cave)
8480 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8483 boolean getTeamMode_EM(void)
8485 return game.team_mode || network_playing;
8488 boolean isActivePlayer_EM(int player_nr)
8490 return stored_player[player_nr].active;
8493 unsigned int InitRND(int seed)
8495 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8496 return InitEngineRandom_EM(seed);
8497 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8498 return InitEngineRandom_SP(seed);
8499 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8500 return InitEngineRandom_MM(seed);
8502 return InitEngineRandom_RND(seed);
8505 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8506 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8508 static int get_effective_element_EM(int tile, int frame_em)
8510 int element = object_mapping[tile].element_rnd;
8511 int action = object_mapping[tile].action;
8512 boolean is_backside = object_mapping[tile].is_backside;
8513 boolean action_removing = (action == ACTION_DIGGING ||
8514 action == ACTION_SNAPPING ||
8515 action == ACTION_COLLECTING);
8523 return (frame_em > 5 ? EL_EMPTY : element);
8529 else // frame_em == 7
8540 case Ydiamond_stone:
8544 case Xdrip_stretchB:
8560 case Ymagnify_blank:
8563 case Xsand_stonein_1:
8564 case Xsand_stonein_2:
8565 case Xsand_stonein_3:
8566 case Xsand_stonein_4:
8570 return (is_backside || action_removing ? EL_EMPTY : element);
8575 static boolean check_linear_animation_EM(int tile)
8579 case Xsand_stonesand_1:
8580 case Xsand_stonesand_quickout_1:
8581 case Xsand_sandstone_1:
8582 case Xsand_stonein_1:
8583 case Xsand_stoneout_1:
8611 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8612 boolean has_crumbled_graphics,
8613 int crumbled, int sync_frame)
8615 // if element can be crumbled, but certain action graphics are just empty
8616 // space (like instantly snapping sand to empty space in 1 frame), do not
8617 // treat these empty space graphics as crumbled graphics in EMC engine
8618 if (crumbled == IMG_EMPTY_SPACE)
8619 has_crumbled_graphics = FALSE;
8621 if (has_crumbled_graphics)
8623 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8624 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8625 g_crumbled->anim_delay,
8626 g_crumbled->anim_mode,
8627 g_crumbled->anim_start_frame,
8630 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8631 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8633 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8634 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8636 g_em->has_crumbled_graphics = TRUE;
8640 g_em->crumbled_bitmap = NULL;
8641 g_em->crumbled_src_x = 0;
8642 g_em->crumbled_src_y = 0;
8643 g_em->crumbled_border_size = 0;
8644 g_em->crumbled_tile_size = 0;
8646 g_em->has_crumbled_graphics = FALSE;
8651 void ResetGfxAnimation_EM(int x, int y, int tile)
8657 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8658 int tile, int frame_em, int x, int y)
8660 int action = object_mapping[tile].action;
8661 int direction = object_mapping[tile].direction;
8662 int effective_element = get_effective_element_EM(tile, frame_em);
8663 int graphic = (direction == MV_NONE ?
8664 el_act2img(effective_element, action) :
8665 el_act_dir2img(effective_element, action, direction));
8666 struct GraphicInfo *g = &graphic_info[graphic];
8668 boolean action_removing = (action == ACTION_DIGGING ||
8669 action == ACTION_SNAPPING ||
8670 action == ACTION_COLLECTING);
8671 boolean action_moving = (action == ACTION_FALLING ||
8672 action == ACTION_MOVING ||
8673 action == ACTION_PUSHING ||
8674 action == ACTION_EATING ||
8675 action == ACTION_FILLING ||
8676 action == ACTION_EMPTYING);
8677 boolean action_falling = (action == ACTION_FALLING ||
8678 action == ACTION_FILLING ||
8679 action == ACTION_EMPTYING);
8681 // special case: graphic uses "2nd movement tile" and has defined
8682 // 7 frames for movement animation (or less) => use default graphic
8683 // for last (8th) frame which ends the movement animation
8684 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8686 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8687 graphic = (direction == MV_NONE ?
8688 el_act2img(effective_element, action) :
8689 el_act_dir2img(effective_element, action, direction));
8691 g = &graphic_info[graphic];
8694 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8698 else if (action_moving)
8700 boolean is_backside = object_mapping[tile].is_backside;
8704 int direction = object_mapping[tile].direction;
8705 int move_dir = (action_falling ? MV_DOWN : direction);
8710 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8711 if (g->double_movement && frame_em == 0)
8715 if (move_dir == MV_LEFT)
8716 GfxFrame[x - 1][y] = GfxFrame[x][y];
8717 else if (move_dir == MV_RIGHT)
8718 GfxFrame[x + 1][y] = GfxFrame[x][y];
8719 else if (move_dir == MV_UP)
8720 GfxFrame[x][y - 1] = GfxFrame[x][y];
8721 else if (move_dir == MV_DOWN)
8722 GfxFrame[x][y + 1] = GfxFrame[x][y];
8729 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8730 if (tile == Xsand_stonesand_quickout_1 ||
8731 tile == Xsand_stonesand_quickout_2)
8735 if (graphic_info[graphic].anim_global_sync)
8736 sync_frame = FrameCounter;
8737 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8738 sync_frame = GfxFrame[x][y];
8740 sync_frame = 0; // playfield border (pseudo steel)
8742 SetRandomAnimationValue(x, y);
8744 int frame = getAnimationFrame(g->anim_frames,
8747 g->anim_start_frame,
8750 g_em->unique_identifier =
8751 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8754 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8755 int tile, int frame_em, int x, int y)
8757 int action = object_mapping[tile].action;
8758 int direction = object_mapping[tile].direction;
8759 boolean is_backside = object_mapping[tile].is_backside;
8760 int effective_element = get_effective_element_EM(tile, frame_em);
8761 int effective_action = action;
8762 int graphic = (direction == MV_NONE ?
8763 el_act2img(effective_element, effective_action) :
8764 el_act_dir2img(effective_element, effective_action,
8766 int crumbled = (direction == MV_NONE ?
8767 el_act2crm(effective_element, effective_action) :
8768 el_act_dir2crm(effective_element, effective_action,
8770 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8771 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8772 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8773 struct GraphicInfo *g = &graphic_info[graphic];
8776 // special case: graphic uses "2nd movement tile" and has defined
8777 // 7 frames for movement animation (or less) => use default graphic
8778 // for last (8th) frame which ends the movement animation
8779 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8781 effective_action = ACTION_DEFAULT;
8782 graphic = (direction == MV_NONE ?
8783 el_act2img(effective_element, effective_action) :
8784 el_act_dir2img(effective_element, effective_action,
8786 crumbled = (direction == MV_NONE ?
8787 el_act2crm(effective_element, effective_action) :
8788 el_act_dir2crm(effective_element, effective_action,
8791 g = &graphic_info[graphic];
8794 if (graphic_info[graphic].anim_global_sync)
8795 sync_frame = FrameCounter;
8796 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8797 sync_frame = GfxFrame[x][y];
8799 sync_frame = 0; // playfield border (pseudo steel)
8801 SetRandomAnimationValue(x, y);
8803 int frame = getAnimationFrame(g->anim_frames,
8806 g->anim_start_frame,
8809 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8810 g->double_movement && is_backside);
8812 // (updating the "crumbled" graphic definitions is probably not really needed,
8813 // as animations for crumbled graphics can't be longer than one EMC cycle)
8814 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8818 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8819 int player_nr, int anim, int frame_em)
8821 int element = player_mapping[player_nr][anim].element_rnd;
8822 int action = player_mapping[player_nr][anim].action;
8823 int direction = player_mapping[player_nr][anim].direction;
8824 int graphic = (direction == MV_NONE ?
8825 el_act2img(element, action) :
8826 el_act_dir2img(element, action, direction));
8827 struct GraphicInfo *g = &graphic_info[graphic];
8830 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8832 stored_player[player_nr].StepFrame = frame_em;
8834 sync_frame = stored_player[player_nr].Frame;
8836 int frame = getAnimationFrame(g->anim_frames,
8839 g->anim_start_frame,
8842 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8843 &g_em->src_x, &g_em->src_y, FALSE);
8846 void InitGraphicInfo_EM(void)
8850 // always start with reliable default values
8851 for (i = 0; i < GAME_TILE_MAX; i++)
8853 object_mapping[i].element_rnd = EL_UNKNOWN;
8854 object_mapping[i].is_backside = FALSE;
8855 object_mapping[i].action = ACTION_DEFAULT;
8856 object_mapping[i].direction = MV_NONE;
8859 // always start with reliable default values
8860 for (p = 0; p < MAX_PLAYERS; p++)
8862 for (i = 0; i < PLY_MAX; i++)
8864 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8865 player_mapping[p][i].action = ACTION_DEFAULT;
8866 player_mapping[p][i].direction = MV_NONE;
8870 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8872 int e = em_object_mapping_list[i].element_em;
8874 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8875 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8877 if (em_object_mapping_list[i].action != -1)
8878 object_mapping[e].action = em_object_mapping_list[i].action;
8880 if (em_object_mapping_list[i].direction != -1)
8881 object_mapping[e].direction =
8882 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8885 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8887 int a = em_player_mapping_list[i].action_em;
8888 int p = em_player_mapping_list[i].player_nr;
8890 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8892 if (em_player_mapping_list[i].action != -1)
8893 player_mapping[p][a].action = em_player_mapping_list[i].action;
8895 if (em_player_mapping_list[i].direction != -1)
8896 player_mapping[p][a].direction =
8897 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8900 for (i = 0; i < GAME_TILE_MAX; i++)
8902 int element = object_mapping[i].element_rnd;
8903 int action = object_mapping[i].action;
8904 int direction = object_mapping[i].direction;
8905 boolean is_backside = object_mapping[i].is_backside;
8906 boolean action_exploding = ((action == ACTION_EXPLODING ||
8907 action == ACTION_SMASHED_BY_ROCK ||
8908 action == ACTION_SMASHED_BY_SPRING) &&
8909 element != EL_DIAMOND);
8910 boolean action_active = (action == ACTION_ACTIVE);
8911 boolean action_other = (action == ACTION_OTHER);
8913 for (j = 0; j < 8; j++)
8915 int effective_element = get_effective_element_EM(i, j);
8916 int effective_action = (j < 7 ? action :
8917 i == Xdrip_stretch ? action :
8918 i == Xdrip_stretchB ? action :
8919 i == Ydrip_1_s ? action :
8920 i == Ydrip_1_sB ? action :
8921 i == Yball_1 ? action :
8922 i == Xball_2 ? action :
8923 i == Yball_2 ? action :
8924 i == Yball_blank ? action :
8925 i == Ykey_1_blank ? action :
8926 i == Ykey_2_blank ? action :
8927 i == Ykey_3_blank ? action :
8928 i == Ykey_4_blank ? action :
8929 i == Ykey_5_blank ? action :
8930 i == Ykey_6_blank ? action :
8931 i == Ykey_7_blank ? action :
8932 i == Ykey_8_blank ? action :
8933 i == Ylenses_blank ? action :
8934 i == Ymagnify_blank ? action :
8935 i == Ygrass_blank ? action :
8936 i == Ydirt_blank ? action :
8937 i == Xsand_stonein_1 ? action :
8938 i == Xsand_stonein_2 ? action :
8939 i == Xsand_stonein_3 ? action :
8940 i == Xsand_stonein_4 ? action :
8941 i == Xsand_stoneout_1 ? action :
8942 i == Xsand_stoneout_2 ? action :
8943 i == Xboom_android ? ACTION_EXPLODING :
8944 action_exploding ? ACTION_EXPLODING :
8945 action_active ? action :
8946 action_other ? action :
8948 int graphic = (el_act_dir2img(effective_element, effective_action,
8950 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8952 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8953 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8954 boolean has_action_graphics = (graphic != base_graphic);
8955 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8956 struct GraphicInfo *g = &graphic_info[graphic];
8957 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8960 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8961 boolean special_animation = (action != ACTION_DEFAULT &&
8962 g->anim_frames == 3 &&
8963 g->anim_delay == 2 &&
8964 g->anim_mode & ANIM_LINEAR);
8965 int sync_frame = (i == Xdrip_stretch ? 7 :
8966 i == Xdrip_stretchB ? 7 :
8967 i == Ydrip_2_s ? j + 8 :
8968 i == Ydrip_2_sB ? j + 8 :
8977 i == Xfake_acid_1 ? 0 :
8978 i == Xfake_acid_2 ? 10 :
8979 i == Xfake_acid_3 ? 20 :
8980 i == Xfake_acid_4 ? 30 :
8981 i == Xfake_acid_5 ? 40 :
8982 i == Xfake_acid_6 ? 50 :
8983 i == Xfake_acid_7 ? 60 :
8984 i == Xfake_acid_8 ? 70 :
8985 i == Xfake_acid_1_player ? 0 :
8986 i == Xfake_acid_2_player ? 10 :
8987 i == Xfake_acid_3_player ? 20 :
8988 i == Xfake_acid_4_player ? 30 :
8989 i == Xfake_acid_5_player ? 40 :
8990 i == Xfake_acid_6_player ? 50 :
8991 i == Xfake_acid_7_player ? 60 :
8992 i == Xfake_acid_8_player ? 70 :
8994 i == Yball_2 ? j + 8 :
8995 i == Yball_blank ? j + 1 :
8996 i == Ykey_1_blank ? j + 1 :
8997 i == Ykey_2_blank ? j + 1 :
8998 i == Ykey_3_blank ? j + 1 :
8999 i == Ykey_4_blank ? j + 1 :
9000 i == Ykey_5_blank ? j + 1 :
9001 i == Ykey_6_blank ? j + 1 :
9002 i == Ykey_7_blank ? j + 1 :
9003 i == Ykey_8_blank ? j + 1 :
9004 i == Ylenses_blank ? j + 1 :
9005 i == Ymagnify_blank ? j + 1 :
9006 i == Ygrass_blank ? j + 1 :
9007 i == Ydirt_blank ? j + 1 :
9008 i == Xamoeba_1 ? 0 :
9009 i == Xamoeba_2 ? 1 :
9010 i == Xamoeba_3 ? 2 :
9011 i == Xamoeba_4 ? 3 :
9012 i == Xamoeba_5 ? 0 :
9013 i == Xamoeba_6 ? 1 :
9014 i == Xamoeba_7 ? 2 :
9015 i == Xamoeba_8 ? 3 :
9016 i == Xexit_2 ? j + 8 :
9017 i == Xexit_3 ? j + 16 :
9018 i == Xdynamite_1 ? 0 :
9019 i == Xdynamite_2 ? 8 :
9020 i == Xdynamite_3 ? 16 :
9021 i == Xdynamite_4 ? 24 :
9022 i == Xsand_stonein_1 ? j + 1 :
9023 i == Xsand_stonein_2 ? j + 9 :
9024 i == Xsand_stonein_3 ? j + 17 :
9025 i == Xsand_stonein_4 ? j + 25 :
9026 i == Xsand_stoneout_1 && j == 0 ? 0 :
9027 i == Xsand_stoneout_1 && j == 1 ? 0 :
9028 i == Xsand_stoneout_1 && j == 2 ? 1 :
9029 i == Xsand_stoneout_1 && j == 3 ? 2 :
9030 i == Xsand_stoneout_1 && j == 4 ? 2 :
9031 i == Xsand_stoneout_1 && j == 5 ? 3 :
9032 i == Xsand_stoneout_1 && j == 6 ? 4 :
9033 i == Xsand_stoneout_1 && j == 7 ? 4 :
9034 i == Xsand_stoneout_2 && j == 0 ? 5 :
9035 i == Xsand_stoneout_2 && j == 1 ? 6 :
9036 i == Xsand_stoneout_2 && j == 2 ? 7 :
9037 i == Xsand_stoneout_2 && j == 3 ? 8 :
9038 i == Xsand_stoneout_2 && j == 4 ? 9 :
9039 i == Xsand_stoneout_2 && j == 5 ? 11 :
9040 i == Xsand_stoneout_2 && j == 6 ? 13 :
9041 i == Xsand_stoneout_2 && j == 7 ? 15 :
9042 i == Xboom_bug && j == 1 ? 2 :
9043 i == Xboom_bug && j == 2 ? 2 :
9044 i == Xboom_bug && j == 3 ? 4 :
9045 i == Xboom_bug && j == 4 ? 4 :
9046 i == Xboom_bug && j == 5 ? 2 :
9047 i == Xboom_bug && j == 6 ? 2 :
9048 i == Xboom_bug && j == 7 ? 0 :
9049 i == Xboom_tank && j == 1 ? 2 :
9050 i == Xboom_tank && j == 2 ? 2 :
9051 i == Xboom_tank && j == 3 ? 4 :
9052 i == Xboom_tank && j == 4 ? 4 :
9053 i == Xboom_tank && j == 5 ? 2 :
9054 i == Xboom_tank && j == 6 ? 2 :
9055 i == Xboom_tank && j == 7 ? 0 :
9056 i == Xboom_android && j == 7 ? 6 :
9057 i == Xboom_1 && j == 1 ? 2 :
9058 i == Xboom_1 && j == 2 ? 2 :
9059 i == Xboom_1 && j == 3 ? 4 :
9060 i == Xboom_1 && j == 4 ? 4 :
9061 i == Xboom_1 && j == 5 ? 6 :
9062 i == Xboom_1 && j == 6 ? 6 :
9063 i == Xboom_1 && j == 7 ? 8 :
9064 i == Xboom_2 && j == 0 ? 8 :
9065 i == Xboom_2 && j == 1 ? 8 :
9066 i == Xboom_2 && j == 2 ? 10 :
9067 i == Xboom_2 && j == 3 ? 10 :
9068 i == Xboom_2 && j == 4 ? 10 :
9069 i == Xboom_2 && j == 5 ? 12 :
9070 i == Xboom_2 && j == 6 ? 12 :
9071 i == Xboom_2 && j == 7 ? 12 :
9072 special_animation && j == 4 ? 3 :
9073 effective_action != action ? 0 :
9075 int frame = getAnimationFrame(g->anim_frames,
9078 g->anim_start_frame,
9081 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9082 g->double_movement && is_backside);
9084 g_em->bitmap = src_bitmap;
9085 g_em->src_x = src_x;
9086 g_em->src_y = src_y;
9087 g_em->src_offset_x = 0;
9088 g_em->src_offset_y = 0;
9089 g_em->dst_offset_x = 0;
9090 g_em->dst_offset_y = 0;
9091 g_em->width = TILEX;
9092 g_em->height = TILEY;
9094 g_em->preserve_background = FALSE;
9096 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9099 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9100 effective_action == ACTION_MOVING ||
9101 effective_action == ACTION_PUSHING ||
9102 effective_action == ACTION_EATING)) ||
9103 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9104 effective_action == ACTION_EMPTYING)))
9107 (effective_action == ACTION_FALLING ||
9108 effective_action == ACTION_FILLING ||
9109 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9110 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9111 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9112 int num_steps = (i == Ydrip_1_s ? 16 :
9113 i == Ydrip_1_sB ? 16 :
9114 i == Ydrip_2_s ? 16 :
9115 i == Ydrip_2_sB ? 16 :
9116 i == Xsand_stonein_1 ? 32 :
9117 i == Xsand_stonein_2 ? 32 :
9118 i == Xsand_stonein_3 ? 32 :
9119 i == Xsand_stonein_4 ? 32 :
9120 i == Xsand_stoneout_1 ? 16 :
9121 i == Xsand_stoneout_2 ? 16 : 8);
9122 int cx = ABS(dx) * (TILEX / num_steps);
9123 int cy = ABS(dy) * (TILEY / num_steps);
9124 int step_frame = (i == Ydrip_2_s ? j + 8 :
9125 i == Ydrip_2_sB ? j + 8 :
9126 i == Xsand_stonein_2 ? j + 8 :
9127 i == Xsand_stonein_3 ? j + 16 :
9128 i == Xsand_stonein_4 ? j + 24 :
9129 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9130 int step = (is_backside ? step_frame : num_steps - step_frame);
9132 if (is_backside) // tile where movement starts
9134 if (dx < 0 || dy < 0)
9136 g_em->src_offset_x = cx * step;
9137 g_em->src_offset_y = cy * step;
9141 g_em->dst_offset_x = cx * step;
9142 g_em->dst_offset_y = cy * step;
9145 else // tile where movement ends
9147 if (dx < 0 || dy < 0)
9149 g_em->dst_offset_x = cx * step;
9150 g_em->dst_offset_y = cy * step;
9154 g_em->src_offset_x = cx * step;
9155 g_em->src_offset_y = cy * step;
9159 g_em->width = TILEX - cx * step;
9160 g_em->height = TILEY - cy * step;
9163 // create unique graphic identifier to decide if tile must be redrawn
9164 /* bit 31 - 16 (16 bit): EM style graphic
9165 bit 15 - 12 ( 4 bit): EM style frame
9166 bit 11 - 6 ( 6 bit): graphic width
9167 bit 5 - 0 ( 6 bit): graphic height */
9168 g_em->unique_identifier =
9169 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9173 for (i = 0; i < GAME_TILE_MAX; i++)
9175 for (j = 0; j < 8; j++)
9177 int element = object_mapping[i].element_rnd;
9178 int action = object_mapping[i].action;
9179 int direction = object_mapping[i].direction;
9180 boolean is_backside = object_mapping[i].is_backside;
9181 int graphic_action = el_act_dir2img(element, action, direction);
9182 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9184 if ((action == ACTION_SMASHED_BY_ROCK ||
9185 action == ACTION_SMASHED_BY_SPRING ||
9186 action == ACTION_EATING) &&
9187 graphic_action == graphic_default)
9189 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9190 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9191 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9192 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9195 // no separate animation for "smashed by rock" -- use rock instead
9196 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9197 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9199 g_em->bitmap = g_xx->bitmap;
9200 g_em->src_x = g_xx->src_x;
9201 g_em->src_y = g_xx->src_y;
9202 g_em->src_offset_x = g_xx->src_offset_x;
9203 g_em->src_offset_y = g_xx->src_offset_y;
9204 g_em->dst_offset_x = g_xx->dst_offset_x;
9205 g_em->dst_offset_y = g_xx->dst_offset_y;
9206 g_em->width = g_xx->width;
9207 g_em->height = g_xx->height;
9208 g_em->unique_identifier = g_xx->unique_identifier;
9211 g_em->preserve_background = TRUE;
9216 for (p = 0; p < MAX_PLAYERS; p++)
9218 for (i = 0; i < PLY_MAX; i++)
9220 int element = player_mapping[p][i].element_rnd;
9221 int action = player_mapping[p][i].action;
9222 int direction = player_mapping[p][i].direction;
9224 for (j = 0; j < 8; j++)
9226 int effective_element = element;
9227 int effective_action = action;
9228 int graphic = (direction == MV_NONE ?
9229 el_act2img(effective_element, effective_action) :
9230 el_act_dir2img(effective_element, effective_action,
9232 struct GraphicInfo *g = &graphic_info[graphic];
9233 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9237 int frame = getAnimationFrame(g->anim_frames,
9240 g->anim_start_frame,
9243 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9245 g_em->bitmap = src_bitmap;
9246 g_em->src_x = src_x;
9247 g_em->src_y = src_y;
9248 g_em->src_offset_x = 0;
9249 g_em->src_offset_y = 0;
9250 g_em->dst_offset_x = 0;
9251 g_em->dst_offset_y = 0;
9252 g_em->width = TILEX;
9253 g_em->height = TILEY;
9259 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9260 boolean any_player_moving,
9261 boolean any_player_snapping,
9262 boolean any_player_dropping)
9264 if (frame == 7 && !any_player_dropping)
9266 if (!local_player->was_waiting)
9268 if (!CheckSaveEngineSnapshotToList())
9271 local_player->was_waiting = TRUE;
9274 else if (any_player_moving || any_player_snapping || any_player_dropping)
9276 local_player->was_waiting = FALSE;
9280 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9281 boolean murphy_is_dropping)
9283 if (murphy_is_waiting)
9285 if (!local_player->was_waiting)
9287 if (!CheckSaveEngineSnapshotToList())
9290 local_player->was_waiting = TRUE;
9295 local_player->was_waiting = FALSE;
9299 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9300 boolean button_released)
9302 if (button_released)
9304 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9305 CheckSaveEngineSnapshotToList();
9307 else if (element_clicked)
9309 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9310 CheckSaveEngineSnapshotToList();
9312 game.snapshot.changed_action = TRUE;
9316 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9317 boolean any_player_moving,
9318 boolean any_player_snapping,
9319 boolean any_player_dropping)
9321 if (tape.single_step && tape.recording && !tape.pausing)
9322 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9323 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9325 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9326 any_player_snapping, any_player_dropping);
9328 return tape.pausing;
9331 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9332 boolean murphy_is_dropping)
9334 boolean murphy_starts_dropping = FALSE;
9337 for (i = 0; i < MAX_PLAYERS; i++)
9338 if (stored_player[i].force_dropping)
9339 murphy_starts_dropping = TRUE;
9341 if (tape.single_step && tape.recording && !tape.pausing)
9342 if (murphy_is_waiting && !murphy_starts_dropping)
9343 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9345 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9348 void CheckSingleStepMode_MM(boolean element_clicked,
9349 boolean button_released)
9351 if (tape.single_step && tape.recording && !tape.pausing)
9352 if (button_released)
9353 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9355 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9358 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9359 int graphic, int sync_frame, int x, int y)
9361 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9363 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9366 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9368 return (IS_NEXT_FRAME(sync_frame, graphic));
9371 int getGraphicInfo_Delay(int graphic)
9373 return graphic_info[graphic].anim_delay;
9376 void PlayMenuSoundExt(int sound)
9378 if (sound == SND_UNDEFINED)
9381 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9382 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9385 if (IS_LOOP_SOUND(sound))
9386 PlaySoundLoop(sound);
9391 void PlayMenuSound(void)
9393 PlayMenuSoundExt(menu.sound[game_status]);
9396 void PlayMenuSoundStereo(int sound, int stereo_position)
9398 if (sound == SND_UNDEFINED)
9401 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9402 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9405 if (IS_LOOP_SOUND(sound))
9406 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9408 PlaySoundStereo(sound, stereo_position);
9411 void PlayMenuSoundIfLoopExt(int sound)
9413 if (sound == SND_UNDEFINED)
9416 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9417 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9420 if (IS_LOOP_SOUND(sound))
9421 PlaySoundLoop(sound);
9424 void PlayMenuSoundIfLoop(void)
9426 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9429 void PlayMenuMusicExt(int music)
9431 if (music == MUS_UNDEFINED)
9434 if (!setup.sound_music)
9437 if (IS_LOOP_MUSIC(music))
9438 PlayMusicLoop(music);
9443 void PlayMenuMusic(void)
9445 char *curr_music = getCurrentlyPlayingMusicFilename();
9446 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9448 if (!strEqual(curr_music, next_music))
9449 PlayMenuMusicExt(menu.music[game_status]);
9452 void PlayMenuSoundsAndMusic(void)
9458 static void FadeMenuSounds(void)
9463 static void FadeMenuMusic(void)
9465 char *curr_music = getCurrentlyPlayingMusicFilename();
9466 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9468 if (!strEqual(curr_music, next_music))
9472 void FadeMenuSoundsAndMusic(void)
9478 void PlaySoundActivating(void)
9481 PlaySound(SND_MENU_ITEM_ACTIVATING);
9485 void PlaySoundSelecting(void)
9488 PlaySound(SND_MENU_ITEM_SELECTING);
9492 void ToggleFullscreenIfNeeded(void)
9494 // if setup and video fullscreen state are already matching, nothing do do
9495 if (setup.fullscreen == video.fullscreen_enabled ||
9496 !video.fullscreen_available)
9499 SDLSetWindowFullscreen(setup.fullscreen);
9501 // set setup value according to successfully changed fullscreen mode
9502 setup.fullscreen = video.fullscreen_enabled;
9505 void ChangeWindowScalingIfNeeded(void)
9507 // if setup and video window scaling are already matching, nothing do do
9508 if (setup.window_scaling_percent == video.window_scaling_percent ||
9509 video.fullscreen_enabled)
9512 SDLSetWindowScaling(setup.window_scaling_percent);
9514 // set setup value according to successfully changed window scaling
9515 setup.window_scaling_percent = video.window_scaling_percent;
9518 void ChangeVsyncModeIfNeeded(void)
9520 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9521 int video_vsync_mode = video.vsync_mode;
9523 // if setup and video vsync mode are already matching, nothing do do
9524 if (setup_vsync_mode == video_vsync_mode)
9527 // if renderer is using OpenGL, vsync mode can directly be changed
9528 SDLSetScreenVsyncMode(setup.vsync_mode);
9530 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9531 if (video.vsync_mode == video_vsync_mode)
9533 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9535 // save backbuffer content which gets lost when re-creating screen
9536 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9538 // force re-creating screen and renderer to set new vsync mode
9539 video.fullscreen_enabled = !setup.fullscreen;
9541 // when creating new renderer, destroy textures linked to old renderer
9542 FreeAllImageTextures(); // needs old renderer to free the textures
9544 // re-create screen and renderer (including change of vsync mode)
9545 ChangeVideoModeIfNeeded(setup.fullscreen);
9547 // set setup value according to successfully changed fullscreen mode
9548 setup.fullscreen = video.fullscreen_enabled;
9550 // restore backbuffer content from temporary backbuffer backup bitmap
9551 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9552 FreeBitmap(tmp_backbuffer);
9554 // update visible window/screen
9555 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9557 // when changing vsync mode, re-create textures for new renderer
9558 InitImageTextures();
9561 // set setup value according to successfully changed vsync mode
9562 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9565 static void JoinRectangles(int *x, int *y, int *width, int *height,
9566 int x2, int y2, int width2, int height2)
9568 // do not join with "off-screen" rectangle
9569 if (x2 == -1 || y2 == -1)
9574 *width = MAX(*width, width2);
9575 *height = MAX(*height, height2);
9578 void SetAnimStatus(int anim_status_new)
9580 if (anim_status_new == GAME_MODE_MAIN)
9581 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9582 else if (anim_status_new == GAME_MODE_NAMES)
9583 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9584 else if (anim_status_new == GAME_MODE_SCORES)
9585 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9587 global.anim_status_next = anim_status_new;
9589 // directly set screen modes that are entered without fading
9590 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9591 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9592 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9593 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9594 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9595 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9596 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9597 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9598 global.anim_status = global.anim_status_next;
9601 void SetGameStatus(int game_status_new)
9603 if (game_status_new != game_status)
9604 game_status_last_screen = game_status;
9606 game_status = game_status_new;
9608 SetAnimStatus(game_status_new);
9611 void SetFontStatus(int game_status_new)
9613 static int last_game_status = -1;
9615 if (game_status_new != -1)
9617 // set game status for font use after storing last game status
9618 last_game_status = game_status;
9619 game_status = game_status_new;
9623 // reset game status after font use from last stored game status
9624 game_status = last_game_status;
9628 void ResetFontStatus(void)
9633 void SetLevelSetInfo(char *identifier, int level_nr)
9635 setString(&levelset.identifier, identifier);
9637 levelset.level_nr = level_nr;
9640 boolean CheckIfAllViewportsHaveChanged(void)
9642 // if game status has not changed, viewports have not changed either
9643 if (game_status == game_status_last)
9646 // check if all viewports have changed with current game status
9648 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9649 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9650 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9651 int new_real_sx = vp_playfield->x;
9652 int new_real_sy = vp_playfield->y;
9653 int new_full_sxsize = vp_playfield->width;
9654 int new_full_sysize = vp_playfield->height;
9655 int new_dx = vp_door_1->x;
9656 int new_dy = vp_door_1->y;
9657 int new_dxsize = vp_door_1->width;
9658 int new_dysize = vp_door_1->height;
9659 int new_vx = vp_door_2->x;
9660 int new_vy = vp_door_2->y;
9661 int new_vxsize = vp_door_2->width;
9662 int new_vysize = vp_door_2->height;
9664 boolean playfield_viewport_has_changed =
9665 (new_real_sx != REAL_SX ||
9666 new_real_sy != REAL_SY ||
9667 new_full_sxsize != FULL_SXSIZE ||
9668 new_full_sysize != FULL_SYSIZE);
9670 boolean door_1_viewport_has_changed =
9673 new_dxsize != DXSIZE ||
9674 new_dysize != DYSIZE);
9676 boolean door_2_viewport_has_changed =
9679 new_vxsize != VXSIZE ||
9680 new_vysize != VYSIZE ||
9681 game_status_last == GAME_MODE_EDITOR);
9683 return (playfield_viewport_has_changed &&
9684 door_1_viewport_has_changed &&
9685 door_2_viewport_has_changed);
9688 boolean CheckFadeAll(void)
9690 return (CheckIfGlobalBorderHasChanged() ||
9691 CheckIfAllViewportsHaveChanged());
9694 void ChangeViewportPropertiesIfNeeded(void)
9696 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9697 FALSE : setup.small_game_graphics);
9698 int gfx_game_mode = game_status;
9699 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9701 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9702 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9703 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9704 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9705 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9706 int new_win_xsize = vp_window->width;
9707 int new_win_ysize = vp_window->height;
9708 int border_left = vp_playfield->border_left;
9709 int border_right = vp_playfield->border_right;
9710 int border_top = vp_playfield->border_top;
9711 int border_bottom = vp_playfield->border_bottom;
9712 int new_sx = vp_playfield->x + border_left;
9713 int new_sy = vp_playfield->y + border_top;
9714 int new_sxsize = vp_playfield->width - border_left - border_right;
9715 int new_sysize = vp_playfield->height - border_top - border_bottom;
9716 int new_real_sx = vp_playfield->x;
9717 int new_real_sy = vp_playfield->y;
9718 int new_full_sxsize = vp_playfield->width;
9719 int new_full_sysize = vp_playfield->height;
9720 int new_dx = vp_door_1->x;
9721 int new_dy = vp_door_1->y;
9722 int new_dxsize = vp_door_1->width;
9723 int new_dysize = vp_door_1->height;
9724 int new_vx = vp_door_2->x;
9725 int new_vy = vp_door_2->y;
9726 int new_vxsize = vp_door_2->width;
9727 int new_vysize = vp_door_2->height;
9728 int new_ex = vp_door_3->x;
9729 int new_ey = vp_door_3->y;
9730 int new_exsize = vp_door_3->width;
9731 int new_eysize = vp_door_3->height;
9732 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9733 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9734 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9735 int new_scr_fieldx = new_sxsize / tilesize;
9736 int new_scr_fieldy = new_sysize / tilesize;
9737 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9738 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9739 boolean init_gfx_buffers = FALSE;
9740 boolean init_video_buffer = FALSE;
9741 boolean init_gadgets_and_anims = FALSE;
9742 boolean init_em_graphics = FALSE;
9744 if (new_win_xsize != WIN_XSIZE ||
9745 new_win_ysize != WIN_YSIZE)
9747 WIN_XSIZE = new_win_xsize;
9748 WIN_YSIZE = new_win_ysize;
9750 init_video_buffer = TRUE;
9751 init_gfx_buffers = TRUE;
9752 init_gadgets_and_anims = TRUE;
9754 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9757 if (new_scr_fieldx != SCR_FIELDX ||
9758 new_scr_fieldy != SCR_FIELDY)
9760 // this always toggles between MAIN and GAME when using small tile size
9762 SCR_FIELDX = new_scr_fieldx;
9763 SCR_FIELDY = new_scr_fieldy;
9765 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9776 new_sxsize != SXSIZE ||
9777 new_sysize != SYSIZE ||
9778 new_dxsize != DXSIZE ||
9779 new_dysize != DYSIZE ||
9780 new_vxsize != VXSIZE ||
9781 new_vysize != VYSIZE ||
9782 new_exsize != EXSIZE ||
9783 new_eysize != EYSIZE ||
9784 new_real_sx != REAL_SX ||
9785 new_real_sy != REAL_SY ||
9786 new_full_sxsize != FULL_SXSIZE ||
9787 new_full_sysize != FULL_SYSIZE ||
9788 new_tilesize_var != TILESIZE_VAR
9791 // ------------------------------------------------------------------------
9792 // determine next fading area for changed viewport definitions
9793 // ------------------------------------------------------------------------
9795 // start with current playfield area (default fading area)
9798 FADE_SXSIZE = FULL_SXSIZE;
9799 FADE_SYSIZE = FULL_SYSIZE;
9801 // add new playfield area if position or size has changed
9802 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9803 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9805 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9806 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9809 // add current and new door 1 area if position or size has changed
9810 if (new_dx != DX || new_dy != DY ||
9811 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9813 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9814 DX, DY, DXSIZE, DYSIZE);
9815 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9816 new_dx, new_dy, new_dxsize, new_dysize);
9819 // add current and new door 2 area if position or size has changed
9820 if (new_vx != VX || new_vy != VY ||
9821 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9823 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9824 VX, VY, VXSIZE, VYSIZE);
9825 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9826 new_vx, new_vy, new_vxsize, new_vysize);
9829 // ------------------------------------------------------------------------
9830 // handle changed tile size
9831 // ------------------------------------------------------------------------
9833 if (new_tilesize_var != TILESIZE_VAR)
9835 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9837 // changing tile size invalidates scroll values of engine snapshots
9838 FreeEngineSnapshotSingle();
9840 // changing tile size requires update of graphic mapping for EM engine
9841 init_em_graphics = TRUE;
9852 SXSIZE = new_sxsize;
9853 SYSIZE = new_sysize;
9854 DXSIZE = new_dxsize;
9855 DYSIZE = new_dysize;
9856 VXSIZE = new_vxsize;
9857 VYSIZE = new_vysize;
9858 EXSIZE = new_exsize;
9859 EYSIZE = new_eysize;
9860 REAL_SX = new_real_sx;
9861 REAL_SY = new_real_sy;
9862 FULL_SXSIZE = new_full_sxsize;
9863 FULL_SYSIZE = new_full_sysize;
9864 TILESIZE_VAR = new_tilesize_var;
9866 init_gfx_buffers = TRUE;
9867 init_gadgets_and_anims = TRUE;
9869 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9870 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9873 if (init_gfx_buffers)
9875 // Debug("tools:viewport", "init_gfx_buffers");
9877 SCR_FIELDX = new_scr_fieldx_buffers;
9878 SCR_FIELDY = new_scr_fieldy_buffers;
9882 SCR_FIELDX = new_scr_fieldx;
9883 SCR_FIELDY = new_scr_fieldy;
9885 SetDrawDeactivationMask(REDRAW_NONE);
9886 SetDrawBackgroundMask(REDRAW_FIELD);
9889 if (init_video_buffer)
9891 // Debug("tools:viewport", "init_video_buffer");
9893 FreeAllImageTextures(); // needs old renderer to free the textures
9895 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9896 InitImageTextures();
9899 if (init_gadgets_and_anims)
9901 // Debug("tools:viewport", "init_gadgets_and_anims");
9904 InitGlobalAnimations();
9907 if (init_em_graphics)
9909 InitGraphicInfo_EM();
9914 // ============================================================================
9916 // ============================================================================
9918 #if defined(PLATFORM_WIN32)
9919 /* FILETIME of Jan 1 1970 00:00:00. */
9920 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9923 * timezone information is stored outside the kernel so tzp isn't used anymore.
9925 * Note: this function is not for Win32 high precision timing purpose. See
9928 int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9931 SYSTEMTIME system_time;
9932 ULARGE_INTEGER ularge;
9934 GetSystemTime(&system_time);
9935 SystemTimeToFileTime(&system_time, &file_time);
9936 ularge.LowPart = file_time.dwLowDateTime;
9937 ularge.HighPart = file_time.dwHighDateTime;
9939 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9940 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9946 static char *test_init_uuid_random_function_simple(void)
9948 static char seed_text[100];
9949 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9951 sprintf(seed_text, "%d", seed);
9956 static char *test_init_uuid_random_function_better(void)
9958 static char seed_text[100];
9959 struct timeval current_time;
9961 gettimeofday(¤t_time, NULL);
9963 prng_seed_bytes(¤t_time, sizeof(current_time));
9965 sprintf(seed_text, "%ld.%ld",
9966 (long)current_time.tv_sec,
9967 (long)current_time.tv_usec);
9972 #if defined(PLATFORM_WIN32)
9973 static char *test_init_uuid_random_function_better_windows(void)
9975 static char seed_text[100];
9976 struct timeval current_time;
9978 gettimeofday_windows(¤t_time, NULL);
9980 prng_seed_bytes(¤t_time, sizeof(current_time));
9982 sprintf(seed_text, "%ld.%ld",
9983 (long)current_time.tv_sec,
9984 (long)current_time.tv_usec);
9990 static unsigned int test_uuid_random_function_simple(int max)
9992 return GetSimpleRandom(max);
9995 static unsigned int test_uuid_random_function_better(int max)
9997 return (max > 0 ? prng_get_uint() % max : 0);
10000 #if defined(PLATFORM_WIN32)
10001 #define NUM_UUID_TESTS 3
10003 #define NUM_UUID_TESTS 2
10006 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10008 struct hashtable *hash_seeds =
10009 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10010 struct hashtable *hash_uuids =
10011 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10012 static char message[100];
10015 char *random_name = (nr == 0 ? "simple" : "better");
10016 char *random_type = (always_seed ? "always" : "only once");
10017 char *(*init_random_function)(void) =
10019 test_init_uuid_random_function_simple :
10020 test_init_uuid_random_function_better);
10021 unsigned int (*random_function)(int) =
10023 test_uuid_random_function_simple :
10024 test_uuid_random_function_better);
10027 #if defined(PLATFORM_WIN32)
10030 random_name = "windows";
10031 init_random_function = test_init_uuid_random_function_better_windows;
10037 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10038 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10040 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10041 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10042 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10044 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10048 // always initialize random number generator at least once
10049 init_random_function();
10051 unsigned int time_start = SDL_GetTicks();
10053 for (i = 0; i < num_uuids; i++)
10057 char *seed = getStringCopy(init_random_function());
10059 hashtable_remove(hash_seeds, seed);
10060 hashtable_insert(hash_seeds, seed, "1");
10063 char *uuid = getStringCopy(getUUIDExt(random_function));
10065 hashtable_remove(hash_uuids, uuid);
10066 hashtable_insert(hash_uuids, uuid, "1");
10069 int num_unique_seeds = hashtable_count(hash_seeds);
10070 int num_unique_uuids = hashtable_count(hash_uuids);
10072 unsigned int time_needed = SDL_GetTicks() - time_start;
10074 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10076 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10079 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10081 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10082 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10084 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10086 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10088 Request(message, REQ_CONFIRM);
10090 hashtable_destroy(hash_seeds, 0);
10091 hashtable_destroy(hash_uuids, 0);
10094 void TestGeneratingUUIDs(void)
10096 int num_uuids = 1000000;
10099 for (i = 0; i < NUM_UUID_TESTS; i++)
10100 for (j = 0; j < 2; j++)
10101 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10103 CloseAllAndExit(0);