1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 // currently not available
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 // never draw masked screen borders on borderless screens
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD(void)
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void DrawTileCursor(int draw_target)
634 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
639 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
644 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
647 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
652 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653 BlitScreenToBitmap_EM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655 BlitScreenToBitmap_SP(target_bitmap);
656 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657 BlitScreenToBitmap_MM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659 BlitScreenToBitmap_RND(target_bitmap);
661 redraw_mask |= REDRAW_FIELD;
664 static void DrawFramesPerSecond(void)
667 int font_nr = FONT_TEXT_2;
668 int font_width = getFontWidth(font_nr);
669 int draw_deactivation_mask = GetDrawDeactivationMask();
670 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
672 // draw FPS with leading space (needed if field buffer deactivated)
673 sprintf(text, " %04.1f fps", global.frames_per_second);
675 // override draw deactivation mask (required for invisible warp mode)
676 SetDrawDeactivationMask(REDRAW_NONE);
678 // draw opaque FPS if field buffer deactivated, else draw masked FPS
679 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
682 // set draw deactivation mask to previous value
683 SetDrawDeactivationMask(draw_deactivation_mask);
685 // force full-screen redraw in this frame
686 redraw_mask = REDRAW_ALL;
690 static void PrintFrameTimeDebugging(void)
692 static unsigned int last_counter = 0;
693 unsigned int counter = Counter();
694 int diff_1 = counter - last_counter;
695 int diff_2 = diff_1 - GAME_FRAME_DELAY;
697 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698 char diff_bar[2 * diff_2_max + 5];
702 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
704 for (i = 0; i < diff_2_max; i++)
705 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706 i >= diff_2_max - diff_2_cut ? '-' : ' ');
708 diff_bar[pos++] = '|';
710 for (i = 0; i < diff_2_max; i++)
711 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
713 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
715 diff_bar[pos++] = '\0';
717 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
720 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
723 last_counter = counter;
727 static int unifiedRedrawMask(int mask)
729 if (mask & REDRAW_ALL)
732 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
740 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
743 void BackToFront(void)
745 static int last_redraw_mask = REDRAW_NONE;
747 // force screen redraw in every frame to continue drawing global animations
748 // (but always use the last redraw mask to prevent unwanted side effects)
749 if (redraw_mask == REDRAW_NONE)
750 redraw_mask = last_redraw_mask;
752 last_redraw_mask = redraw_mask;
755 // masked border now drawn immediately when blitting backbuffer to window
757 // draw masked border to all viewports, if defined
758 DrawMaskedBorder(redraw_mask);
761 // draw frames per second (only if debug mode is enabled)
762 if (redraw_mask & REDRAW_FPS)
763 DrawFramesPerSecond();
765 // remove playfield redraw before potentially merging with doors redraw
766 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767 redraw_mask &= ~REDRAW_FIELD;
769 // redraw complete window if both playfield and (some) doors need redraw
770 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771 redraw_mask = REDRAW_ALL;
773 /* although redrawing the whole window would be fine for normal gameplay,
774 being able to only redraw the playfield is required for deactivating
775 certain drawing areas (mainly playfield) to work, which is needed for
776 warp-forward to be fast enough (by skipping redraw of most frames) */
778 if (redraw_mask & REDRAW_ALL)
780 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
782 else if (redraw_mask & REDRAW_FIELD)
784 BlitBitmap(backbuffer, window,
785 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
787 else if (redraw_mask & REDRAW_DOORS)
789 // merge door areas to prevent calling screen redraw more than once
795 if (redraw_mask & REDRAW_DOOR_1)
799 x2 = MAX(x2, DX + DXSIZE);
800 y2 = MAX(y2, DY + DYSIZE);
803 if (redraw_mask & REDRAW_DOOR_2)
807 x2 = MAX(x2, VX + VXSIZE);
808 y2 = MAX(y2, VY + VYSIZE);
811 if (redraw_mask & REDRAW_DOOR_3)
815 x2 = MAX(x2, EX + EXSIZE);
816 y2 = MAX(y2, EY + EYSIZE);
819 // make sure that at least one pixel is blitted, and inside the screen
820 // (else nothing is blitted, causing the animations not to be updated)
821 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823 x2 = MIN(MAX(1, x2), WIN_XSIZE);
824 y2 = MIN(MAX(1, y2), WIN_YSIZE);
826 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
829 redraw_mask = REDRAW_NONE;
832 PrintFrameTimeDebugging();
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
838 unsigned int frame_delay_value_old = GetVideoFrameDelay();
840 SetVideoFrameDelay(frame_delay_value);
844 SetVideoFrameDelay(frame_delay_value_old);
847 static int fade_type_skip = FADE_TYPE_NONE;
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
851 void (*draw_border_function)(void) = NULL;
852 int x, y, width, height;
853 int fade_delay, post_delay;
855 if (fade_type == FADE_TYPE_FADE_OUT)
857 if (fade_type_skip != FADE_TYPE_NONE)
859 // skip all fade operations until specified fade operation
860 if (fade_type & fade_type_skip)
861 fade_type_skip = FADE_TYPE_NONE;
866 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
870 redraw_mask |= fade_mask;
872 if (fade_type == FADE_TYPE_SKIP)
874 fade_type_skip = fade_mode;
879 fade_delay = fading.fade_delay;
880 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
882 if (fade_type_skip != FADE_TYPE_NONE)
884 // skip all fade operations until specified fade operation
885 if (fade_type & fade_type_skip)
886 fade_type_skip = FADE_TYPE_NONE;
891 if (global.autoplay_leveldir)
896 if (fade_mask == REDRAW_FIELD)
901 height = FADE_SYSIZE;
903 if (border.draw_masked_when_fading)
904 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
906 DrawMaskedBorder_FIELD(); // draw once
916 // when switching screens without fading, set fade delay to zero
917 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
920 // do not display black frame when fading out without fade delay
921 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
924 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925 draw_border_function);
927 redraw_mask &= ~fade_mask;
929 ClearAutoRepeatKeyEvents();
932 static void SetScreenStates_BeforeFadingIn(void)
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
940 // set screen mode for animations back to fading
941 global.anim_status = GAME_MODE_PSEUDO_FADING;
944 static void SetScreenStates_AfterFadingIn(void)
946 // store new source screen (to use correct masked border for fading)
947 gfx.fade_border_source_status = global.border_status;
949 global.anim_status = global.anim_status_next;
952 static void SetScreenStates_BeforeFadingOut(void)
954 // store new target screen (to use correct masked border for fading)
955 gfx.fade_border_target_status = game_status;
957 // set screen mode for animations to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 // store backbuffer with all animations that will be stopped for fading out
961 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
964 static void SetScreenStates_AfterFadingOut(void)
966 global.border_status = game_status;
969 void FadeIn(int fade_mask)
971 SetScreenStates_BeforeFadingIn();
974 DrawMaskedBorder(REDRAW_ALL);
977 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
980 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
984 FADE_SXSIZE = FULL_SXSIZE;
985 FADE_SYSIZE = FULL_SYSIZE;
987 // activate virtual buttons depending on upcoming game status
988 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989 game_status == GAME_MODE_PLAYING && !tape.playing)
990 SetOverlayActive(TRUE);
992 SetScreenStates_AfterFadingIn();
994 // force update of global animation status in case of rapid screen changes
995 redraw_mask = REDRAW_ALL;
999 void FadeOut(int fade_mask)
1001 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1006 SetScreenStates_BeforeFadingOut();
1008 SetTileCursorActive(FALSE);
1009 SetOverlayActive(FALSE);
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1018 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1020 SetScreenStates_AfterFadingOut();
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1025 static struct TitleFadingInfo fading_leave_stored;
1028 fading_leave_stored = fading_leave;
1030 fading = fading_leave_stored;
1033 void FadeSetEnterMenu(void)
1035 fading = menu.enter_menu;
1037 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1040 void FadeSetLeaveMenu(void)
1042 fading = menu.leave_menu;
1044 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetEnterScreen(void)
1049 fading = menu.enter_screen[game_status];
1051 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1054 void FadeSetNextScreen(void)
1056 fading = menu.next_screen[game_status];
1058 // (do not overwrite fade mode set by FadeSetEnterScreen)
1059 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1062 void FadeSetLeaveScreen(void)
1064 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1067 void FadeSetFromType(int type)
1069 if (type & TYPE_ENTER_SCREEN)
1070 FadeSetEnterScreen();
1071 else if (type & TYPE_ENTER)
1073 else if (type & TYPE_LEAVE)
1077 void FadeSetDisabled(void)
1079 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1081 fading = fading_none;
1084 void FadeSkipNextFadeIn(void)
1086 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1089 void FadeSkipNextFadeOut(void)
1091 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1096 if (graphic == IMG_UNDEFINED)
1099 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1101 return (graphic_info[graphic].bitmap != NULL || redefined ?
1102 graphic_info[graphic].bitmap :
1103 graphic_info[default_graphic].bitmap);
1106 static Bitmap *getBackgroundBitmap(int graphic)
1108 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1113 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1119 (status == GAME_MODE_MAIN ||
1120 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1121 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1122 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1123 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1126 return getGlobalBorderBitmap(graphic);
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1131 if (graphic_info[graphic].bitmap)
1132 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1135 void SetMainBackgroundImageIfDefined(int graphic)
1137 if (graphic_info[graphic].bitmap)
1138 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1143 if (graphic_info[graphic].bitmap)
1144 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1147 void SetWindowBackgroundImage(int graphic)
1149 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetMainBackgroundImage(int graphic)
1154 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetDoorBackgroundImage(int graphic)
1159 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetPanelBackground(void)
1164 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1166 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1169 SetDoorBackgroundBitmap(bitmap_db_panel);
1172 void DrawBackground(int x, int y, int width, int height)
1174 // "drawto" might still point to playfield buffer here (hall of fame)
1175 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1177 if (IN_GFX_FIELD_FULL(x, y))
1178 redraw_mask |= REDRAW_FIELD;
1179 else if (IN_GFX_DOOR_1(x, y))
1180 redraw_mask |= REDRAW_DOOR_1;
1181 else if (IN_GFX_DOOR_2(x, y))
1182 redraw_mask |= REDRAW_DOOR_2;
1183 else if (IN_GFX_DOOR_3(x, y))
1184 redraw_mask |= REDRAW_DOOR_3;
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1189 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1191 if (font->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1199 struct GraphicInfo *g = &graphic_info[graphic];
1201 if (g->bitmap == NULL)
1204 DrawBackground(x, y, width, height);
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1219 boolean CheckIfGlobalBorderHasChanged(void)
1221 // if game status has not changed, global border has not changed either
1222 if (game_status == game_status_last)
1225 // determine and store new global border bitmap for current game status
1226 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1228 return (global_border_bitmap_last != global_border_bitmap);
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1236 // if game status has not changed, nothing has to be redrawn
1237 if (game_status == game_status_last)
1240 // redraw if last screen was title screen
1241 if (game_status_last == GAME_MODE_TITLE)
1244 // redraw if global screen border has changed
1245 if (CheckIfGlobalBorderHasChanged())
1248 // redraw if position or size of playfield area has changed
1249 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1253 // redraw if position or size of door area has changed
1254 if (dx_last != DX || dy_last != DY ||
1255 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1258 // redraw if position or size of tape area has changed
1259 if (vx_last != VX || vy_last != VY ||
1260 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1263 // redraw if position or size of editor area has changed
1264 if (ex_last != EX || ey_last != EY ||
1265 exsize_last != EXSIZE || eysize_last != EYSIZE)
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1275 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1277 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1280 void RedrawGlobalBorder(void)
1282 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 RedrawGlobalBorderFromBitmap(bitmap);
1286 redraw_mask = REDRAW_ALL;
1289 static void RedrawGlobalBorderIfNeeded(void)
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 if (game_status == game_status_last)
1296 // copy current draw buffer to later copy back areas that have not changed
1297 if (game_status_last != GAME_MODE_TITLE)
1298 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301 if (CheckIfGlobalBorderRedrawIsNeeded())
1303 // determine and store new global border bitmap for current game status
1304 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1307 // redraw global screen border (or clear, if defined to be empty)
1308 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1310 if (game_status == GAME_MODE_EDITOR)
1311 DrawSpecialEditorDoor();
1313 // copy previous playfield and door areas, if they are defined on both
1314 // previous and current screen and if they still have the same size
1316 if (real_sx_last != -1 && real_sy_last != -1 &&
1317 REAL_SX != -1 && REAL_SY != -1 &&
1318 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1323 if (dx_last != -1 && dy_last != -1 &&
1324 DX != -1 && DY != -1 &&
1325 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326 BlitBitmap(bitmap_db_store_1, backbuffer,
1327 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1329 if (game_status != GAME_MODE_EDITOR)
1331 if (vx_last != -1 && vy_last != -1 &&
1332 VX != -1 && VY != -1 &&
1333 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334 BlitBitmap(bitmap_db_store_1, backbuffer,
1335 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1339 if (ex_last != -1 && ey_last != -1 &&
1340 EX != -1 && EY != -1 &&
1341 exsize_last == EXSIZE && eysize_last == EYSIZE)
1342 BlitBitmap(bitmap_db_store_1, backbuffer,
1343 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1346 redraw_mask = REDRAW_ALL;
1349 game_status_last = game_status;
1351 global_border_bitmap_last = global_border_bitmap;
1353 real_sx_last = REAL_SX;
1354 real_sy_last = REAL_SY;
1355 full_sxsize_last = FULL_SXSIZE;
1356 full_sysize_last = FULL_SYSIZE;
1359 dxsize_last = DXSIZE;
1360 dysize_last = DYSIZE;
1363 vxsize_last = VXSIZE;
1364 vysize_last = VYSIZE;
1367 exsize_last = EXSIZE;
1368 eysize_last = EYSIZE;
1371 void ClearField(void)
1373 RedrawGlobalBorderIfNeeded();
1375 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376 // (when entering hall of fame after playing)
1377 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1379 // !!! maybe this should be done before clearing the background !!!
1380 if (game_status == GAME_MODE_PLAYING)
1382 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1387 SetDrawtoField(DRAW_TO_BACKBUFFER);
1391 void MarkTileDirty(int x, int y)
1393 redraw_mask |= REDRAW_FIELD;
1396 void SetBorderElement(void)
1400 BorderElement = EL_EMPTY;
1402 // only the R'n'D game engine may use an additional steelwall border
1403 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1406 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1408 for (x = 0; x < lev_fieldx; x++)
1410 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411 BorderElement = EL_STEELWALL;
1413 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1419 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1420 int max_array_fieldx, int max_array_fieldy,
1421 short field[max_array_fieldx][max_array_fieldy],
1422 int max_fieldx, int max_fieldy)
1424 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1425 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1426 int old_element = field[start_x][start_y];
1429 // do nothing if start field already has the desired content
1430 if (old_element == fill_element)
1433 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1435 while (stack_pos > 0)
1437 struct XY current = stack_buffer[--stack_pos];
1440 field[current.x][current.y] = fill_element;
1442 for (i = 0; i < 4; i++)
1444 int x = current.x + check[i].x;
1445 int y = current.y + check[i].y;
1447 // check for stack buffer overflow (should not happen)
1448 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1449 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1451 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1452 stack_buffer[stack_pos++] = (struct XY){ x, y };
1457 void FloodFillLevel(int from_x, int from_y, int fill_element,
1458 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1459 int max_fieldx, int max_fieldy)
1461 FloodFillLevelExt(from_x, from_y, fill_element,
1462 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1463 max_fieldx, max_fieldy);
1466 void SetRandomAnimationValue(int x, int y)
1468 gfx.anim_random_frame = GfxRandom[x][y];
1471 int getGraphicAnimationFrame(int graphic, int sync_frame)
1473 // animation synchronized with global frame counter, not move position
1474 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1475 sync_frame = FrameCounter;
1477 return getAnimationFrame(graphic_info[graphic].anim_frames,
1478 graphic_info[graphic].anim_delay,
1479 graphic_info[graphic].anim_mode,
1480 graphic_info[graphic].anim_start_frame,
1484 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1486 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1488 struct GraphicInfo *g = &graphic_info[graphic];
1489 int xsize = MAX(1, g->anim_frames_per_line);
1490 int ysize = MAX(1, g->anim_frames / xsize);
1491 int xoffset = g->anim_start_frame % xsize;
1492 int yoffset = g->anim_start_frame % ysize;
1493 int x = (lx + xoffset + xsize) % xsize;
1494 int y = (ly + yoffset + ysize) % ysize;
1495 int sync_frame = y * xsize + x;
1497 return sync_frame % g->anim_frames;
1499 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1501 struct GraphicInfo *g = &graphic_info[graphic];
1502 int x = (lx + lev_fieldx) % lev_fieldx;
1503 int y = (ly + lev_fieldy) % lev_fieldy;
1504 int sync_frame = GfxRandomStatic[x][y];
1506 return sync_frame % g->anim_frames;
1509 return getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1512 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1514 struct GraphicInfo *g = &graphic_info[graphic];
1515 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1517 if (tilesize == gfx.standard_tile_size)
1518 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1519 else if (tilesize == game.tile_size)
1520 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1522 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1525 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1526 boolean get_backside)
1528 struct GraphicInfo *g = &graphic_info[graphic];
1529 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1530 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1532 if (g->offset_y == 0) // frames are ordered horizontally
1534 int max_width = g->anim_frames_per_line * g->width;
1535 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1537 *x = pos % max_width;
1538 *y = src_y % g->height + pos / max_width * g->height;
1540 else if (g->offset_x == 0) // frames are ordered vertically
1542 int max_height = g->anim_frames_per_line * g->height;
1543 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1545 *x = src_x % g->width + pos / max_height * g->width;
1546 *y = pos % max_height;
1548 else // frames are ordered diagonally
1550 *x = src_x + frame * g->offset_x;
1551 *y = src_y + frame * g->offset_y;
1555 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1556 Bitmap **bitmap, int *x, int *y,
1557 boolean get_backside)
1559 struct GraphicInfo *g = &graphic_info[graphic];
1561 // if no graphics defined at all, use fallback graphics
1562 if (g->bitmaps == NULL)
1563 *g = graphic_info[IMG_CHAR_EXCLAM];
1565 // if no in-game graphics defined, always use standard graphic size
1566 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1567 tilesize = TILESIZE;
1569 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1570 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1572 *x = *x * tilesize / g->tile_size;
1573 *y = *y * tilesize / g->tile_size;
1576 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1577 Bitmap **bitmap, int *x, int *y)
1579 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1582 void getFixedGraphicSource(int graphic, int frame,
1583 Bitmap **bitmap, int *x, int *y)
1585 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1588 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1590 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1593 void getGlobalAnimGraphicSource(int graphic, int frame,
1594 Bitmap **bitmap, int *x, int *y)
1596 struct GraphicInfo *g = &graphic_info[graphic];
1598 // if no graphics defined at all, use fallback graphics
1599 if (g->bitmaps == NULL)
1600 *g = graphic_info[IMG_CHAR_EXCLAM];
1602 // use original size graphics, if existing, else use standard size graphics
1603 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1604 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1606 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1608 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1611 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1612 int *x, int *y, boolean get_backside)
1614 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1618 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1620 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1623 void DrawGraphic(int x, int y, int graphic, int frame)
1626 if (!IN_SCR_FIELD(x, y))
1628 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1629 Debug("draw:DrawGraphic", "This should never happen!");
1635 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1638 MarkTileDirty(x, y);
1641 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1644 if (!IN_SCR_FIELD(x, y))
1646 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1648 Debug("draw:DrawFixedGraphic", "This should never happen!");
1654 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1656 MarkTileDirty(x, y);
1659 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1665 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1667 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1670 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1676 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1680 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1687 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1693 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1696 MarkTileDirty(x, y);
1699 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1702 if (!IN_SCR_FIELD(x, y))
1704 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1706 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1712 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1714 MarkTileDirty(x, y);
1717 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1723 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1725 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1729 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1730 int graphic, int frame)
1735 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1737 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1741 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1743 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1745 MarkTileDirty(x / tilesize, y / tilesize);
1748 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1751 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1752 graphic, frame, tilesize);
1753 MarkTileDirty(x / tilesize, y / tilesize);
1756 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1762 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1763 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1766 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1767 int frame, int tilesize)
1772 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1773 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1776 void DrawMiniGraphic(int x, int y, int graphic)
1778 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1779 MarkTileDirty(x / 2, y / 2);
1782 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1787 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1788 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1791 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1792 int graphic, int frame,
1793 int cut_mode, int mask_mode)
1798 int width = TILEX, height = TILEY;
1801 if (dx || dy) // shifted graphic
1803 if (x < BX1) // object enters playfield from the left
1810 else if (x > BX2) // object enters playfield from the right
1816 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1822 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1824 else if (dx) // general horizontal movement
1825 MarkTileDirty(x + SIGN(dx), y);
1827 if (y < BY1) // object enters playfield from the top
1829 if (cut_mode == CUT_BELOW) // object completely above top border
1837 else if (y > BY2) // object enters playfield from the bottom
1843 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1849 else if (dy > 0 && cut_mode == CUT_ABOVE)
1851 if (y == BY2) // object completely above bottom border
1857 MarkTileDirty(x, y + 1);
1858 } // object leaves playfield to the bottom
1859 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1861 else if (dy) // general vertical movement
1862 MarkTileDirty(x, y + SIGN(dy));
1866 if (!IN_SCR_FIELD(x, y))
1868 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1870 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1876 width = width * TILESIZE_VAR / TILESIZE;
1877 height = height * TILESIZE_VAR / TILESIZE;
1878 cx = cx * TILESIZE_VAR / TILESIZE;
1879 cy = cy * TILESIZE_VAR / TILESIZE;
1880 dx = dx * TILESIZE_VAR / TILESIZE;
1881 dy = dy * TILESIZE_VAR / TILESIZE;
1883 if (width > 0 && height > 0)
1885 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1890 dst_x = FX + x * TILEX_VAR + dx;
1891 dst_y = FY + y * TILEY_VAR + dy;
1893 if (mask_mode == USE_MASKING)
1894 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1897 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1900 MarkTileDirty(x, y);
1904 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1905 int graphic, int frame,
1906 int cut_mode, int mask_mode)
1911 int width = TILEX_VAR, height = TILEY_VAR;
1914 int x2 = x + SIGN(dx);
1915 int y2 = y + SIGN(dy);
1917 // movement with two-tile animations must be sync'ed with movement position,
1918 // not with current GfxFrame (which can be higher when using slow movement)
1919 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1920 int anim_frames = graphic_info[graphic].anim_frames;
1922 // (we also need anim_delay here for movement animations with less frames)
1923 int anim_delay = graphic_info[graphic].anim_delay;
1924 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1926 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1927 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1929 // re-calculate animation frame for two-tile movement animation
1930 frame = getGraphicAnimationFrame(graphic, sync_frame);
1932 // check if movement start graphic inside screen area and should be drawn
1933 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1935 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1937 dst_x = FX + x1 * TILEX_VAR;
1938 dst_y = FY + y1 * TILEY_VAR;
1940 if (mask_mode == USE_MASKING)
1941 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1944 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1947 MarkTileDirty(x1, y1);
1950 // check if movement end graphic inside screen area and should be drawn
1951 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1953 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1955 dst_x = FX + x2 * TILEX_VAR;
1956 dst_y = FY + y2 * TILEY_VAR;
1958 if (mask_mode == USE_MASKING)
1959 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1962 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1965 MarkTileDirty(x2, y2);
1969 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1970 int graphic, int frame,
1971 int cut_mode, int mask_mode)
1975 DrawGraphic(x, y, graphic, frame);
1980 if (graphic_info[graphic].double_movement) // EM style movement images
1981 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1983 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1986 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1987 int graphic, int frame, int cut_mode)
1989 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1992 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1993 int cut_mode, int mask_mode)
1995 int lx = LEVELX(x), ly = LEVELY(y);
1999 if (element == EL_EMPTY)
2000 element = GfxElementEmpty[lx][ly];
2002 if (IN_LEV_FIELD(lx, ly))
2004 SetRandomAnimationValue(lx, ly);
2006 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2007 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2009 // do not use double (EM style) movement graphic when not moving
2010 if (graphic_info[graphic].double_movement && !dx && !dy)
2012 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2013 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2016 if (game.use_masked_elements && (dx || dy))
2017 mask_mode = USE_MASKING;
2019 else // border element
2021 graphic = el2img(element);
2022 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2025 if (element == EL_EXPANDABLE_WALL)
2027 boolean left_stopped = FALSE, right_stopped = FALSE;
2029 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2030 left_stopped = TRUE;
2031 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2032 right_stopped = TRUE;
2034 if (left_stopped && right_stopped)
2036 else if (left_stopped)
2038 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2039 frame = graphic_info[graphic].anim_frames - 1;
2041 else if (right_stopped)
2043 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2044 frame = graphic_info[graphic].anim_frames - 1;
2049 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2050 else if (mask_mode == USE_MASKING)
2051 DrawGraphicThruMask(x, y, graphic, frame);
2053 DrawGraphic(x, y, graphic, frame);
2056 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2057 int cut_mode, int mask_mode)
2059 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2060 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2061 cut_mode, mask_mode);
2064 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2067 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2070 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2073 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2076 void DrawLevelElementThruMask(int x, int y, int element)
2078 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2081 void DrawLevelFieldThruMask(int x, int y)
2083 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2086 // !!! implementation of quicksand is totally broken !!!
2087 #define IS_CRUMBLED_TILE(x, y, e) \
2088 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2089 !IS_MOVING(x, y) || \
2090 (e) == EL_QUICKSAND_EMPTYING || \
2091 (e) == EL_QUICKSAND_FAST_EMPTYING))
2093 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2098 int width, height, cx, cy;
2099 int sx = SCREENX(x), sy = SCREENY(y);
2100 int crumbled_border_size = graphic_info[graphic].border_size;
2101 int crumbled_tile_size = graphic_info[graphic].tile_size;
2102 int crumbled_border_size_var =
2103 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2106 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2108 for (i = 1; i < 4; i++)
2110 int dxx = (i & 1 ? dx : 0);
2111 int dyy = (i & 2 ? dy : 0);
2114 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2117 // check if neighbour field is of same crumble type
2118 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2119 graphic_info[graphic].class ==
2120 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2122 // return if check prevents inner corner
2123 if (same == (dxx == dx && dyy == dy))
2127 // if we reach this point, we have an inner corner
2129 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2131 width = crumbled_border_size_var;
2132 height = crumbled_border_size_var;
2133 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2134 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2136 if (game.use_masked_elements)
2138 int graphic0 = el2img(EL_EMPTY);
2139 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2140 Bitmap *src_bitmap0;
2143 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2145 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2147 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2149 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2151 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2154 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2156 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2159 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2164 int width, height, bx, by, cx, cy;
2165 int sx = SCREENX(x), sy = SCREENY(y);
2166 int crumbled_border_size = graphic_info[graphic].border_size;
2167 int crumbled_tile_size = graphic_info[graphic].tile_size;
2168 int crumbled_border_size_var =
2169 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2170 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2173 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2175 // only needed when using masked elements
2176 int graphic0 = el2img(EL_EMPTY);
2177 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2178 Bitmap *src_bitmap0;
2181 if (game.use_masked_elements)
2182 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2184 // draw simple, sloppy, non-corner-accurate crumbled border
2186 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2187 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2188 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2189 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2191 if (game.use_masked_elements)
2193 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2195 FX + sx * TILEX_VAR + cx,
2196 FY + sy * TILEY_VAR + cy);
2198 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2200 FX + sx * TILEX_VAR + cx,
2201 FY + sy * TILEY_VAR + cy);
2204 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2206 FX + sx * TILEX_VAR + cx,
2207 FY + sy * TILEY_VAR + cy);
2209 // (remaining middle border part must be at least as big as corner part)
2210 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2211 crumbled_border_size_var >= TILESIZE_VAR / 3)
2214 // correct corners of crumbled border, if needed
2216 for (i = -1; i <= 1; i += 2)
2218 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2219 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2220 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2223 // check if neighbour field is of same crumble type
2224 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2225 graphic_info[graphic].class ==
2226 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2228 // no crumbled corner, but continued crumbled border
2230 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2231 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2232 int b1 = (i == 1 ? crumbled_border_size_var :
2233 TILESIZE_VAR - 2 * crumbled_border_size_var);
2235 width = crumbled_border_size_var;
2236 height = crumbled_border_size_var;
2238 if (dir == 1 || dir == 2)
2253 if (game.use_masked_elements)
2255 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2257 FX + sx * TILEX_VAR + cx,
2258 FY + sy * TILEY_VAR + cy);
2260 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2262 FX + sx * TILEX_VAR + cx,
2263 FY + sy * TILEY_VAR + cy);
2266 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2268 FX + sx * TILEX_VAR + cx,
2269 FY + sy * TILEY_VAR + cy);
2274 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2276 int sx = SCREENX(x), sy = SCREENY(y);
2279 static int xy[4][2] =
2287 if (!IN_LEV_FIELD(x, y))
2290 element = TILE_GFX_ELEMENT(x, y);
2292 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2294 if (!IN_SCR_FIELD(sx, sy))
2297 // crumble field borders towards direct neighbour fields
2298 for (i = 0; i < 4; i++)
2300 int xx = x + xy[i][0];
2301 int yy = y + xy[i][1];
2303 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2306 // check if neighbour field is of same crumble type
2307 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2308 graphic_info[graphic].class ==
2309 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2312 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2315 // crumble inner field corners towards corner neighbour fields
2316 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2317 graphic_info[graphic].anim_frames == 2)
2319 for (i = 0; i < 4; i++)
2321 int dx = (i & 1 ? +1 : -1);
2322 int dy = (i & 2 ? +1 : -1);
2324 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2328 MarkTileDirty(sx, sy);
2330 else // center field is not crumbled -- crumble neighbour fields
2332 // crumble field borders of direct neighbour fields
2333 for (i = 0; i < 4; i++)
2335 int xx = x + xy[i][0];
2336 int yy = y + xy[i][1];
2337 int sxx = sx + xy[i][0];
2338 int syy = sy + xy[i][1];
2340 if (!IN_LEV_FIELD(xx, yy) ||
2341 !IN_SCR_FIELD(sxx, syy))
2344 // do not crumble fields that are being digged or snapped
2345 if (Tile[xx][yy] == EL_EMPTY ||
2346 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2349 element = TILE_GFX_ELEMENT(xx, yy);
2351 if (!IS_CRUMBLED_TILE(xx, yy, element))
2354 graphic = el_act2crm(element, ACTION_DEFAULT);
2356 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2358 MarkTileDirty(sxx, syy);
2361 // crumble inner field corners of corner neighbour fields
2362 for (i = 0; i < 4; i++)
2364 int dx = (i & 1 ? +1 : -1);
2365 int dy = (i & 2 ? +1 : -1);
2371 if (!IN_LEV_FIELD(xx, yy) ||
2372 !IN_SCR_FIELD(sxx, syy))
2375 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2378 element = TILE_GFX_ELEMENT(xx, yy);
2380 if (!IS_CRUMBLED_TILE(xx, yy, element))
2383 graphic = el_act2crm(element, ACTION_DEFAULT);
2385 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2386 graphic_info[graphic].anim_frames == 2)
2387 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2389 MarkTileDirty(sxx, syy);
2394 void DrawLevelFieldCrumbled(int x, int y)
2398 if (!IN_LEV_FIELD(x, y))
2401 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2402 GfxElement[x][y] != EL_UNDEFINED &&
2403 GFX_CRUMBLED(GfxElement[x][y]))
2405 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2410 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2412 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2415 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2418 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2419 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2420 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2421 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2422 int sx = SCREENX(x), sy = SCREENY(y);
2424 DrawScreenGraphic(sx, sy, graphic1, frame1);
2425 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2428 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2430 int sx = SCREENX(x), sy = SCREENY(y);
2431 static int xy[4][2] =
2440 // crumble direct neighbour fields (required for field borders)
2441 for (i = 0; i < 4; i++)
2443 int xx = x + xy[i][0];
2444 int yy = y + xy[i][1];
2445 int sxx = sx + xy[i][0];
2446 int syy = sy + xy[i][1];
2448 if (!IN_LEV_FIELD(xx, yy) ||
2449 !IN_SCR_FIELD(sxx, syy) ||
2450 !GFX_CRUMBLED(Tile[xx][yy]) ||
2454 DrawLevelField(xx, yy);
2457 // crumble corner neighbour fields (required for inner field corners)
2458 for (i = 0; i < 4; i++)
2460 int dx = (i & 1 ? +1 : -1);
2461 int dy = (i & 2 ? +1 : -1);
2467 if (!IN_LEV_FIELD(xx, yy) ||
2468 !IN_SCR_FIELD(sxx, syy) ||
2469 !GFX_CRUMBLED(Tile[xx][yy]) ||
2473 int element = TILE_GFX_ELEMENT(xx, yy);
2474 int graphic = el_act2crm(element, ACTION_DEFAULT);
2476 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2477 graphic_info[graphic].anim_frames == 2)
2478 DrawLevelField(xx, yy);
2482 static int getBorderElement(int x, int y)
2486 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2487 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2488 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2489 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2490 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2491 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2492 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2494 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2495 int steel_position = (x == -1 && y == -1 ? 0 :
2496 x == lev_fieldx && y == -1 ? 1 :
2497 x == -1 && y == lev_fieldy ? 2 :
2498 x == lev_fieldx && y == lev_fieldy ? 3 :
2499 x == -1 || x == lev_fieldx ? 4 :
2500 y == -1 || y == lev_fieldy ? 5 : 6);
2502 return border[steel_position][steel_type];
2505 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2507 if (game.use_masked_elements)
2509 if (graphic != el2img(EL_EMPTY))
2510 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2512 DrawGraphicThruMask(x, y, graphic, frame);
2516 DrawGraphic(x, y, graphic, frame);
2520 void DrawScreenElement(int x, int y, int element)
2522 int mask_mode = NO_MASKING;
2524 if (game.use_masked_elements)
2526 int lx = LEVELX(x), ly = LEVELY(y);
2528 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2530 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2532 mask_mode = USE_MASKING;
2536 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2537 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2540 void DrawLevelElement(int x, int y, int element)
2542 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2543 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2546 void DrawScreenField(int x, int y)
2548 int lx = LEVELX(x), ly = LEVELY(y);
2549 int element, content;
2551 if (!IN_LEV_FIELD(lx, ly))
2553 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2556 element = getBorderElement(lx, ly);
2558 DrawScreenElement(x, y, element);
2563 element = Tile[lx][ly];
2564 content = Store[lx][ly];
2566 if (IS_MOVING(lx, ly))
2568 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2569 boolean cut_mode = NO_CUTTING;
2571 if (element == EL_QUICKSAND_EMPTYING ||
2572 element == EL_QUICKSAND_FAST_EMPTYING ||
2573 element == EL_MAGIC_WALL_EMPTYING ||
2574 element == EL_BD_MAGIC_WALL_EMPTYING ||
2575 element == EL_DC_MAGIC_WALL_EMPTYING ||
2576 element == EL_AMOEBA_DROPPING)
2577 cut_mode = CUT_ABOVE;
2578 else if (element == EL_QUICKSAND_FILLING ||
2579 element == EL_QUICKSAND_FAST_FILLING ||
2580 element == EL_MAGIC_WALL_FILLING ||
2581 element == EL_BD_MAGIC_WALL_FILLING ||
2582 element == EL_DC_MAGIC_WALL_FILLING)
2583 cut_mode = CUT_BELOW;
2585 if (cut_mode == CUT_ABOVE)
2586 DrawScreenElement(x, y, element);
2588 DrawScreenElement(x, y, EL_EMPTY);
2590 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2592 int dir = MovDir[lx][ly];
2593 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2594 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2596 if (IN_SCR_FIELD(newx, newy))
2597 DrawScreenElement(newx, newy, EL_EMPTY);
2601 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2602 else if (cut_mode == NO_CUTTING)
2603 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2606 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2608 if (cut_mode == CUT_BELOW &&
2609 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2610 DrawLevelElement(lx, ly + 1, element);
2613 if (content == EL_ACID)
2615 int dir = MovDir[lx][ly];
2616 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2617 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2619 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2621 // prevent target field from being drawn again (but without masking)
2622 // (this would happen if target field is scanned after moving element)
2623 Stop[newlx][newly] = TRUE;
2626 else if (IS_BLOCKED(lx, ly))
2631 boolean cut_mode = NO_CUTTING;
2632 int element_old, content_old;
2634 Blocked2Moving(lx, ly, &oldx, &oldy);
2637 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2638 MovDir[oldx][oldy] == MV_RIGHT);
2640 element_old = Tile[oldx][oldy];
2641 content_old = Store[oldx][oldy];
2643 if (element_old == EL_QUICKSAND_EMPTYING ||
2644 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2645 element_old == EL_MAGIC_WALL_EMPTYING ||
2646 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2647 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2648 element_old == EL_AMOEBA_DROPPING)
2649 cut_mode = CUT_ABOVE;
2651 DrawScreenElement(x, y, EL_EMPTY);
2654 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2656 else if (cut_mode == NO_CUTTING)
2657 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2660 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2663 else if (IS_DRAWABLE(element))
2664 DrawScreenElement(x, y, element);
2666 DrawScreenElement(x, y, EL_EMPTY);
2669 void DrawLevelField(int x, int y)
2671 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2672 DrawScreenField(SCREENX(x), SCREENY(y));
2673 else if (IS_MOVING(x, y))
2677 Moving2Blocked(x, y, &newx, &newy);
2678 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2679 DrawScreenField(SCREENX(newx), SCREENY(newy));
2681 else if (IS_BLOCKED(x, y))
2685 Blocked2Moving(x, y, &oldx, &oldy);
2686 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2687 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2691 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2692 int (*el2img_function)(int), boolean masked,
2693 int element_bits_draw)
2695 int element_base = map_mm_wall_element(element);
2696 int element_bits = (IS_DF_WALL(element) ?
2697 element - EL_DF_WALL_START :
2698 IS_MM_WALL(element) ?
2699 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2700 int graphic = el2img_function(element_base);
2701 int tilesize_draw = tilesize / 2;
2706 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2708 for (i = 0; i < 4; i++)
2710 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2711 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2713 if (!(element_bits_draw & (1 << i)))
2716 if (element_bits & (1 << i))
2719 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2720 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2722 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2723 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2728 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2729 tilesize_draw, tilesize_draw);
2734 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2735 boolean masked, int element_bits_draw)
2737 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2738 element, tilesize, el2edimg, masked, element_bits_draw);
2741 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2742 int (*el2img_function)(int))
2744 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2748 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2751 if (IS_MM_WALL(element))
2753 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2754 element, tilesize, el2edimg, masked, 0x000f);
2758 int graphic = el2edimg(element);
2761 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2763 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2767 void DrawSizedElement(int x, int y, int element, int tilesize)
2769 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2772 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2774 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2777 void DrawMiniElement(int x, int y, int element)
2781 graphic = el2edimg(element);
2782 DrawMiniGraphic(x, y, graphic);
2785 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2788 int x = sx + scroll_x, y = sy + scroll_y;
2790 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2791 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2792 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2793 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2795 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2798 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2800 int x = sx + scroll_x, y = sy + scroll_y;
2802 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2803 DrawMiniElement(sx, sy, EL_EMPTY);
2804 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2805 DrawMiniElement(sx, sy, Tile[x][y]);
2807 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2810 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2811 int x, int y, int xsize, int ysize,
2812 int tile_width, int tile_height)
2816 int dst_x = startx + x * tile_width;
2817 int dst_y = starty + y * tile_height;
2818 int width = graphic_info[graphic].width;
2819 int height = graphic_info[graphic].height;
2820 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2821 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2822 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2823 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2824 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2825 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2826 boolean draw_masked = graphic_info[graphic].draw_masked;
2828 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2830 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2832 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2836 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2837 inner_sx + (x - 1) * tile_width % inner_width);
2838 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2839 inner_sy + (y - 1) * tile_height % inner_height);
2842 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2845 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2849 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2850 int x, int y, int xsize, int ysize,
2853 int font_width = getFontWidth(font_nr);
2854 int font_height = getFontHeight(font_nr);
2856 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2857 font_width, font_height);
2860 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2862 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2863 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2864 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2865 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2866 boolean no_delay = (tape.warp_forward);
2867 unsigned int anim_delay = 0;
2868 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2869 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2870 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2871 int font_width = getFontWidth(font_nr);
2872 int font_height = getFontHeight(font_nr);
2873 int max_xsize = level.envelope[envelope_nr].xsize;
2874 int max_ysize = level.envelope[envelope_nr].ysize;
2875 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2876 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2877 int xend = max_xsize;
2878 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2879 int xstep = (xstart < xend ? 1 : 0);
2880 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2882 int end = MAX(xend - xstart, yend - ystart);
2885 for (i = start; i <= end; i++)
2887 int last_frame = end; // last frame of this "for" loop
2888 int x = xstart + i * xstep;
2889 int y = ystart + i * ystep;
2890 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2891 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2892 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2893 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2896 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2898 BlitScreenToBitmap(backbuffer);
2900 SetDrawtoField(DRAW_TO_BACKBUFFER);
2902 for (yy = 0; yy < ysize; yy++)
2903 for (xx = 0; xx < xsize; xx++)
2904 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2906 DrawTextBuffer(sx + font_width, sy + font_height,
2907 level.envelope[envelope_nr].text, font_nr, max_xsize,
2908 xsize - 2, ysize - 2, 0, mask_mode,
2909 level.envelope[envelope_nr].autowrap,
2910 level.envelope[envelope_nr].centered, FALSE);
2912 redraw_mask |= REDRAW_FIELD;
2915 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2918 ClearAutoRepeatKeyEvents();
2921 void ShowEnvelope(int envelope_nr)
2923 int element = EL_ENVELOPE_1 + envelope_nr;
2924 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2925 int sound_opening = element_info[element].sound[ACTION_OPENING];
2926 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2927 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2928 boolean no_delay = (tape.warp_forward);
2929 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2930 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2931 int anim_mode = graphic_info[graphic].anim_mode;
2932 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2933 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2934 boolean overlay_enabled = GetOverlayEnabled();
2936 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2938 SetOverlayEnabled(FALSE);
2941 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2943 if (anim_mode == ANIM_DEFAULT)
2944 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2946 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2949 Delay_WithScreenUpdates(wait_delay_value);
2951 WaitForEventToContinue();
2954 SetOverlayEnabled(overlay_enabled);
2956 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2958 if (anim_mode != ANIM_NONE)
2959 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2961 if (anim_mode == ANIM_DEFAULT)
2962 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2964 game.envelope_active = FALSE;
2966 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2968 redraw_mask |= REDRAW_FIELD;
2972 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2973 int xsize, int ysize)
2975 if (!global.use_envelope_request ||
2976 request.sort_priority <= 0)
2979 if (request.bitmap == NULL ||
2980 xsize > request.xsize ||
2981 ysize > request.ysize)
2983 if (request.bitmap != NULL)
2984 FreeBitmap(request.bitmap);
2986 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2988 SDL_Surface *surface = request.bitmap->surface;
2990 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2991 Fail("SDLGetNativeSurface() failed");
2994 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2996 SDLFreeBitmapTextures(request.bitmap);
2997 SDLCreateBitmapTextures(request.bitmap);
2999 // set envelope request run-time values
3002 request.xsize = xsize;
3003 request.ysize = ysize;
3006 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3008 if (global.use_envelope_request &&
3009 game.request_active_or_moving &&
3010 request.sort_priority > 0 &&
3011 drawing_target == DRAW_TO_SCREEN &&
3012 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3014 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3015 request.sx, request.sy);
3019 static void setRequestBasePosition(int *x, int *y)
3021 int sx_base, sy_base;
3023 if (request.x != -1)
3024 sx_base = request.x;
3025 else if (request.align == ALIGN_LEFT)
3027 else if (request.align == ALIGN_RIGHT)
3028 sx_base = SX + SXSIZE;
3030 sx_base = SX + SXSIZE / 2;
3032 if (request.y != -1)
3033 sy_base = request.y;
3034 else if (request.valign == VALIGN_TOP)
3036 else if (request.valign == VALIGN_BOTTOM)
3037 sy_base = SY + SYSIZE;
3039 sy_base = SY + SYSIZE / 2;
3045 static void setRequestPositionExt(int *x, int *y, int width, int height,
3046 boolean add_border_size)
3048 int border_size = request.border_size;
3049 int sx_base, sy_base;
3052 setRequestBasePosition(&sx_base, &sy_base);
3054 if (request.align == ALIGN_LEFT)
3056 else if (request.align == ALIGN_RIGHT)
3057 sx = sx_base - width;
3059 sx = sx_base - width / 2;
3061 if (request.valign == VALIGN_TOP)
3063 else if (request.valign == VALIGN_BOTTOM)
3064 sy = sy_base - height;
3066 sy = sy_base - height / 2;
3068 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3069 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3071 if (add_border_size)
3081 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3083 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3086 static void DrawEnvelopeRequest(char *text)
3088 char *text_final = text;
3089 char *text_door_style = NULL;
3090 int graphic = IMG_BACKGROUND_REQUEST;
3091 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3092 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3093 int font_nr = FONT_REQUEST;
3094 int font_width = getFontWidth(font_nr);
3095 int font_height = getFontHeight(font_nr);
3096 int border_size = request.border_size;
3097 int line_spacing = request.line_spacing;
3098 int line_height = font_height + line_spacing;
3099 int max_text_width = request.width - 2 * border_size;
3100 int max_text_height = request.height - 2 * border_size;
3101 int line_length = max_text_width / font_width;
3102 int max_lines = max_text_height / line_height;
3103 int text_width = line_length * font_width;
3104 int width = request.width;
3105 int height = request.height;
3106 int tile_size = MAX(request.step_offset, 1);
3107 int x_steps = width / tile_size;
3108 int y_steps = height / tile_size;
3109 int sx_offset = border_size;
3110 int sy_offset = border_size;
3114 if (request.centered)
3115 sx_offset = (request.width - text_width) / 2;
3117 if (request.wrap_single_words && !request.autowrap)
3119 char *src_text_ptr, *dst_text_ptr;
3121 text_door_style = checked_malloc(2 * strlen(text) + 1);
3123 src_text_ptr = text;
3124 dst_text_ptr = text_door_style;
3126 while (*src_text_ptr)
3128 if (*src_text_ptr == ' ' ||
3129 *src_text_ptr == '?' ||
3130 *src_text_ptr == '!')
3131 *dst_text_ptr++ = '\n';
3133 if (*src_text_ptr != ' ')
3134 *dst_text_ptr++ = *src_text_ptr;
3139 *dst_text_ptr = '\0';
3141 text_final = text_door_style;
3144 setRequestPosition(&sx, &sy, FALSE);
3146 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3148 for (y = 0; y < y_steps; y++)
3149 for (x = 0; x < x_steps; x++)
3150 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3151 x, y, x_steps, y_steps,
3152 tile_size, tile_size);
3154 // force DOOR font inside door area
3155 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3157 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3158 line_length, -1, max_lines, line_spacing, mask_mode,
3159 request.autowrap, request.centered, FALSE);
3163 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3164 RedrawGadget(tool_gadget[i]);
3166 // store readily prepared envelope request for later use when animating
3167 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3169 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3171 if (text_door_style)
3172 free(text_door_style);
3175 static void AnimateEnvelopeRequest(int anim_mode, int action)
3177 int graphic = IMG_BACKGROUND_REQUEST;
3178 boolean draw_masked = graphic_info[graphic].draw_masked;
3179 int delay_value_normal = request.step_delay;
3180 int delay_value_fast = delay_value_normal / 2;
3181 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3182 boolean no_delay = (tape.warp_forward);
3183 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3184 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3185 unsigned int anim_delay = 0;
3187 int tile_size = MAX(request.step_offset, 1);
3188 int max_xsize = request.width / tile_size;
3189 int max_ysize = request.height / tile_size;
3190 int max_xsize_inner = max_xsize - 2;
3191 int max_ysize_inner = max_ysize - 2;
3193 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3194 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3195 int xend = max_xsize_inner;
3196 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3197 int xstep = (xstart < xend ? 1 : 0);
3198 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3200 int end = MAX(xend - xstart, yend - ystart);
3203 if (setup.quick_doors)
3210 for (i = start; i <= end; i++)
3212 int last_frame = end; // last frame of this "for" loop
3213 int x = xstart + i * xstep;
3214 int y = ystart + i * ystep;
3215 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3216 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3217 int xsize_size_left = (xsize - 1) * tile_size;
3218 int ysize_size_top = (ysize - 1) * tile_size;
3219 int max_xsize_pos = (max_xsize - 1) * tile_size;
3220 int max_ysize_pos = (max_ysize - 1) * tile_size;
3221 int width = xsize * tile_size;
3222 int height = ysize * tile_size;
3227 setRequestPosition(&src_x, &src_y, FALSE);
3228 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3230 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3232 for (yy = 0; yy < 2; yy++)
3234 for (xx = 0; xx < 2; xx++)
3236 int src_xx = src_x + xx * max_xsize_pos;
3237 int src_yy = src_y + yy * max_ysize_pos;
3238 int dst_xx = dst_x + xx * xsize_size_left;
3239 int dst_yy = dst_y + yy * ysize_size_top;
3240 int xx_size = (xx ? tile_size : xsize_size_left);
3241 int yy_size = (yy ? tile_size : ysize_size_top);
3244 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3245 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3247 BlitBitmap(bitmap_db_store_2, backbuffer,
3248 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3252 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3254 redraw_mask |= REDRAW_FIELD;
3258 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3261 ClearAutoRepeatKeyEvents();
3264 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3266 int graphic = IMG_BACKGROUND_REQUEST;
3267 int sound_opening = SND_REQUEST_OPENING;
3268 int sound_closing = SND_REQUEST_CLOSING;
3269 int anim_mode_1 = request.anim_mode; // (higher priority)
3270 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3271 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3272 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3273 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3275 if (game_status == GAME_MODE_PLAYING)
3276 BlitScreenToBitmap(backbuffer);
3278 SetDrawtoField(DRAW_TO_BACKBUFFER);
3280 // SetDrawBackgroundMask(REDRAW_NONE);
3282 if (action == ACTION_OPENING)
3284 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3286 if (req_state & REQ_ASK)
3288 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3289 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3290 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3291 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3293 else if (req_state & REQ_CONFIRM)
3295 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3296 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3298 else if (req_state & REQ_PLAYER)
3300 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3301 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3302 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3303 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3306 DrawEnvelopeRequest(text);
3309 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3311 if (action == ACTION_OPENING)
3313 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3315 if (anim_mode == ANIM_DEFAULT)
3316 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3318 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3322 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3324 if (anim_mode != ANIM_NONE)
3325 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3327 if (anim_mode == ANIM_DEFAULT)
3328 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3331 game.envelope_active = FALSE;
3333 if (action == ACTION_CLOSING)
3334 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3336 // SetDrawBackgroundMask(last_draw_background_mask);
3338 redraw_mask |= REDRAW_FIELD;
3342 if (action == ACTION_CLOSING &&
3343 game_status == GAME_MODE_PLAYING &&
3344 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3345 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3348 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3350 if (IS_MM_WALL(element))
3352 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3358 int graphic = el2preimg(element);
3360 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3361 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3366 void DrawLevel(int draw_background_mask)
3370 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3371 SetDrawBackgroundMask(draw_background_mask);
3375 for (x = BX1; x <= BX2; x++)
3376 for (y = BY1; y <= BY2; y++)
3377 DrawScreenField(x, y);
3379 redraw_mask |= REDRAW_FIELD;
3382 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3387 for (x = 0; x < size_x; x++)
3388 for (y = 0; y < size_y; y++)
3389 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3391 redraw_mask |= REDRAW_FIELD;
3394 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3398 for (x = 0; x < size_x; x++)
3399 for (y = 0; y < size_y; y++)
3400 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3402 redraw_mask |= REDRAW_FIELD;
3405 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3407 boolean show_level_border = (BorderElement != EL_EMPTY);
3408 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3409 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3410 int tile_size = preview.tile_size;
3411 int preview_width = preview.xsize * tile_size;
3412 int preview_height = preview.ysize * tile_size;
3413 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3414 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3415 int real_preview_width = real_preview_xsize * tile_size;
3416 int real_preview_height = real_preview_ysize * tile_size;
3417 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3418 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3421 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3424 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3426 dst_x += (preview_width - real_preview_width) / 2;
3427 dst_y += (preview_height - real_preview_height) / 2;
3429 for (x = 0; x < real_preview_xsize; x++)
3431 for (y = 0; y < real_preview_ysize; y++)
3433 int lx = from_x + x + (show_level_border ? -1 : 0);
3434 int ly = from_y + y + (show_level_border ? -1 : 0);
3435 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3436 getBorderElement(lx, ly));
3438 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3439 element, tile_size);
3443 redraw_mask |= REDRAW_FIELD;
3446 #define MICROLABEL_EMPTY 0
3447 #define MICROLABEL_LEVEL_NAME 1
3448 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3449 #define MICROLABEL_LEVEL_AUTHOR 3
3450 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3451 #define MICROLABEL_IMPORTED_FROM 5
3452 #define MICROLABEL_IMPORTED_BY_HEAD 6
3453 #define MICROLABEL_IMPORTED_BY 7
3455 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3457 int max_text_width = SXSIZE;
3458 int font_width = getFontWidth(font_nr);
3460 if (pos->align == ALIGN_CENTER)
3461 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3462 else if (pos->align == ALIGN_RIGHT)
3463 max_text_width = pos->x;
3465 max_text_width = SXSIZE - pos->x;
3467 return max_text_width / font_width;
3470 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3472 char label_text[MAX_OUTPUT_LINESIZE + 1];
3473 int max_len_label_text;
3474 int font_nr = pos->font;
3477 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3480 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3481 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3482 mode == MICROLABEL_IMPORTED_BY_HEAD)
3483 font_nr = pos->font_alt;
3485 max_len_label_text = getMaxTextLength(pos, font_nr);
3487 if (pos->size != -1)
3488 max_len_label_text = pos->size;
3490 for (i = 0; i < max_len_label_text; i++)
3491 label_text[i] = ' ';
3492 label_text[max_len_label_text] = '\0';
3494 if (strlen(label_text) > 0)
3495 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3498 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3499 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3500 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3501 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3502 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3503 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3504 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3505 max_len_label_text);
3506 label_text[max_len_label_text] = '\0';
3508 if (strlen(label_text) > 0)
3509 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3511 redraw_mask |= REDRAW_FIELD;
3514 static void DrawPreviewLevelLabel(int mode)
3516 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3519 static void DrawPreviewLevelInfo(int mode)
3521 if (mode == MICROLABEL_LEVEL_NAME)
3522 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3523 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3524 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3527 static void DrawPreviewLevelExt(boolean restart)
3529 static unsigned int scroll_delay = 0;
3530 static unsigned int label_delay = 0;
3531 static int from_x, from_y, scroll_direction;
3532 static int label_state, label_counter;
3533 unsigned int scroll_delay_value = preview.step_delay;
3534 boolean show_level_border = (BorderElement != EL_EMPTY);
3535 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3536 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3543 if (preview.anim_mode == ANIM_CENTERED)
3545 if (level_xsize > preview.xsize)
3546 from_x = (level_xsize - preview.xsize) / 2;
3547 if (level_ysize > preview.ysize)
3548 from_y = (level_ysize - preview.ysize) / 2;
3551 from_x += preview.xoffset;
3552 from_y += preview.yoffset;
3554 scroll_direction = MV_RIGHT;
3558 DrawPreviewLevelPlayfield(from_x, from_y);
3559 DrawPreviewLevelLabel(label_state);
3561 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3562 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3564 // initialize delay counters
3565 ResetDelayCounter(&scroll_delay);
3566 ResetDelayCounter(&label_delay);
3568 if (leveldir_current->name)
3570 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3571 char label_text[MAX_OUTPUT_LINESIZE + 1];
3572 int font_nr = pos->font;
3573 int max_len_label_text = getMaxTextLength(pos, font_nr);
3575 if (pos->size != -1)
3576 max_len_label_text = pos->size;
3578 strncpy(label_text, leveldir_current->name, max_len_label_text);
3579 label_text[max_len_label_text] = '\0';
3581 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3582 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3588 // scroll preview level, if needed
3589 if (preview.anim_mode != ANIM_NONE &&
3590 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3591 DelayReached(&scroll_delay, scroll_delay_value))
3593 switch (scroll_direction)
3598 from_x -= preview.step_offset;
3599 from_x = (from_x < 0 ? 0 : from_x);
3602 scroll_direction = MV_UP;
3606 if (from_x < level_xsize - preview.xsize)
3608 from_x += preview.step_offset;
3609 from_x = (from_x > level_xsize - preview.xsize ?
3610 level_xsize - preview.xsize : from_x);
3613 scroll_direction = MV_DOWN;
3619 from_y -= preview.step_offset;
3620 from_y = (from_y < 0 ? 0 : from_y);
3623 scroll_direction = MV_RIGHT;
3627 if (from_y < level_ysize - preview.ysize)
3629 from_y += preview.step_offset;
3630 from_y = (from_y > level_ysize - preview.ysize ?
3631 level_ysize - preview.ysize : from_y);
3634 scroll_direction = MV_LEFT;
3641 DrawPreviewLevelPlayfield(from_x, from_y);
3644 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3645 // redraw micro level label, if needed
3646 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3647 !strEqual(level.author, ANONYMOUS_NAME) &&
3648 !strEqual(level.author, leveldir_current->name) &&
3649 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3651 int max_label_counter = 23;
3653 if (leveldir_current->imported_from != NULL &&
3654 strlen(leveldir_current->imported_from) > 0)
3655 max_label_counter += 14;
3656 if (leveldir_current->imported_by != NULL &&
3657 strlen(leveldir_current->imported_by) > 0)
3658 max_label_counter += 14;
3660 label_counter = (label_counter + 1) % max_label_counter;
3661 label_state = (label_counter >= 0 && label_counter <= 7 ?
3662 MICROLABEL_LEVEL_NAME :
3663 label_counter >= 9 && label_counter <= 12 ?
3664 MICROLABEL_LEVEL_AUTHOR_HEAD :
3665 label_counter >= 14 && label_counter <= 21 ?
3666 MICROLABEL_LEVEL_AUTHOR :
3667 label_counter >= 23 && label_counter <= 26 ?
3668 MICROLABEL_IMPORTED_FROM_HEAD :
3669 label_counter >= 28 && label_counter <= 35 ?
3670 MICROLABEL_IMPORTED_FROM :
3671 label_counter >= 37 && label_counter <= 40 ?
3672 MICROLABEL_IMPORTED_BY_HEAD :
3673 label_counter >= 42 && label_counter <= 49 ?
3674 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3676 if (leveldir_current->imported_from == NULL &&
3677 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3678 label_state == MICROLABEL_IMPORTED_FROM))
3679 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3680 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3682 DrawPreviewLevelLabel(label_state);
3686 void DrawPreviewPlayers(void)
3688 if (game_status != GAME_MODE_MAIN)
3691 // do not draw preview players if level preview redefined, but players aren't
3692 if (preview.redefined && !menu.main.preview_players.redefined)
3695 boolean player_found[MAX_PLAYERS];
3696 int num_players = 0;
3699 for (i = 0; i < MAX_PLAYERS; i++)
3700 player_found[i] = FALSE;
3702 // check which players can be found in the level (simple approach)
3703 for (x = 0; x < lev_fieldx; x++)
3705 for (y = 0; y < lev_fieldy; y++)
3707 int element = level.field[x][y];
3709 if (IS_PLAYER_ELEMENT(element))
3711 int player_nr = GET_PLAYER_NR(element);
3713 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3715 if (!player_found[player_nr])
3718 player_found[player_nr] = TRUE;
3723 struct TextPosInfo *pos = &menu.main.preview_players;
3724 int tile_size = pos->tile_size;
3725 int border_size = pos->border_size;
3726 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3727 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3728 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3729 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3730 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3731 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3732 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3733 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3734 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3735 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3736 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3737 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3739 // clear area in which the players will be drawn
3740 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3741 max_players_width, max_players_height);
3743 if (!network.enabled && !setup.team_mode)
3746 // only draw players if level is suited for team mode
3747 if (num_players < 2)
3750 // draw all players that were found in the level
3751 for (i = 0; i < MAX_PLAYERS; i++)
3753 if (player_found[i])
3755 int graphic = el2img(EL_PLAYER_1 + i);
3757 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3759 xpos += player_xoffset;
3760 ypos += player_yoffset;
3765 void DrawPreviewLevelInitial(void)
3767 DrawPreviewLevelExt(TRUE);
3768 DrawPreviewPlayers();
3771 void DrawPreviewLevelAnimation(void)
3773 DrawPreviewLevelExt(FALSE);
3776 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3777 int border_size, int font_nr)
3779 int graphic = el2img(EL_PLAYER_1 + player_nr);
3780 int font_height = getFontHeight(font_nr);
3781 int player_height = MAX(tile_size, font_height);
3782 int xoffset_text = tile_size + border_size;
3783 int yoffset_text = (player_height - font_height) / 2;
3784 int yoffset_graphic = (player_height - tile_size) / 2;
3785 char *player_name = getNetworkPlayerName(player_nr + 1);
3787 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3789 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3792 static void DrawNetworkPlayersExt(boolean force)
3794 if (game_status != GAME_MODE_MAIN)
3797 if (!network.connected && !force)
3800 // do not draw network players if level preview redefined, but players aren't
3801 if (preview.redefined && !menu.main.network_players.redefined)
3804 int num_players = 0;
3807 for (i = 0; i < MAX_PLAYERS; i++)
3808 if (stored_player[i].connected_network)
3811 struct TextPosInfo *pos = &menu.main.network_players;
3812 int tile_size = pos->tile_size;
3813 int border_size = pos->border_size;
3814 int xoffset_text = tile_size + border_size;
3815 int font_nr = pos->font;
3816 int font_width = getFontWidth(font_nr);
3817 int font_height = getFontHeight(font_nr);
3818 int player_height = MAX(tile_size, font_height);
3819 int player_yoffset = player_height + border_size;
3820 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3821 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3822 int all_players_height = num_players * player_yoffset - border_size;
3823 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3824 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3825 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3827 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3828 max_players_width, max_players_height);
3830 // first draw local network player ...
3831 for (i = 0; i < MAX_PLAYERS; i++)
3833 if (stored_player[i].connected_network &&
3834 stored_player[i].connected_locally)
3836 char *player_name = getNetworkPlayerName(i + 1);
3837 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3838 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3840 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3842 ypos += player_yoffset;
3846 // ... then draw all other network players
3847 for (i = 0; i < MAX_PLAYERS; i++)
3849 if (stored_player[i].connected_network &&
3850 !stored_player[i].connected_locally)
3852 char *player_name = getNetworkPlayerName(i + 1);
3853 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3854 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3856 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3858 ypos += player_yoffset;
3863 void DrawNetworkPlayers(void)
3865 DrawNetworkPlayersExt(FALSE);
3868 void ClearNetworkPlayers(void)
3870 DrawNetworkPlayersExt(TRUE);
3873 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3874 int graphic, int lx, int ly,
3877 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3879 if (mask_mode == USE_MASKING)
3880 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3882 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3885 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3886 int graphic, int sync_frame, int mask_mode)
3888 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3890 if (mask_mode == USE_MASKING)
3891 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3893 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3896 static void DrawGraphicAnimation(int x, int y, int graphic)
3898 int lx = LEVELX(x), ly = LEVELY(y);
3899 int mask_mode = NO_MASKING;
3901 if (!IN_SCR_FIELD(x, y))
3904 if (game.use_masked_elements)
3906 if (Tile[lx][ly] != EL_EMPTY)
3908 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3910 mask_mode = USE_MASKING;
3914 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3915 graphic, lx, ly, mask_mode);
3917 MarkTileDirty(x, y);
3920 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3922 int lx = LEVELX(x), ly = LEVELY(y);
3923 int mask_mode = NO_MASKING;
3925 if (!IN_SCR_FIELD(x, y))
3928 if (game.use_masked_elements)
3930 if (Tile[lx][ly] != EL_EMPTY)
3932 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3934 mask_mode = USE_MASKING;
3938 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3939 graphic, lx, ly, mask_mode);
3941 MarkTileDirty(x, y);
3944 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3946 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3949 void DrawLevelElementAnimation(int x, int y, int element)
3951 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3953 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3956 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3958 int sx = SCREENX(x), sy = SCREENY(y);
3960 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3963 if (Tile[x][y] == EL_EMPTY)
3964 graphic = el2img(GfxElementEmpty[x][y]);
3966 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3969 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3972 DrawGraphicAnimation(sx, sy, graphic);
3975 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3976 DrawLevelFieldCrumbled(x, y);
3978 if (GFX_CRUMBLED(Tile[x][y]))
3979 DrawLevelFieldCrumbled(x, y);
3983 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3985 int sx = SCREENX(x), sy = SCREENY(y);
3988 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3991 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3993 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3996 DrawGraphicAnimation(sx, sy, graphic);
3998 if (GFX_CRUMBLED(element))
3999 DrawLevelFieldCrumbled(x, y);
4002 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4004 if (player->use_murphy)
4006 // this works only because currently only one player can be "murphy" ...
4007 static int last_horizontal_dir = MV_LEFT;
4008 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4010 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4011 last_horizontal_dir = move_dir;
4013 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4015 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4017 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4023 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4026 static boolean equalGraphics(int graphic1, int graphic2)
4028 struct GraphicInfo *g1 = &graphic_info[graphic1];
4029 struct GraphicInfo *g2 = &graphic_info[graphic2];
4031 return (g1->bitmap == g2->bitmap &&
4032 g1->src_x == g2->src_x &&
4033 g1->src_y == g2->src_y &&
4034 g1->anim_frames == g2->anim_frames &&
4035 g1->anim_delay == g2->anim_delay &&
4036 g1->anim_mode == g2->anim_mode);
4039 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4043 DRAW_PLAYER_STAGE_INIT = 0,
4044 DRAW_PLAYER_STAGE_LAST_FIELD,
4045 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4046 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4047 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4048 DRAW_PLAYER_STAGE_PLAYER,
4050 DRAW_PLAYER_STAGE_PLAYER,
4051 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4053 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4054 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4056 NUM_DRAW_PLAYER_STAGES
4059 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4061 static int static_last_player_graphic[MAX_PLAYERS];
4062 static int static_last_player_frame[MAX_PLAYERS];
4063 static boolean static_player_is_opaque[MAX_PLAYERS];
4064 static boolean draw_player[MAX_PLAYERS];
4065 int pnr = player->index_nr;
4067 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4069 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4070 static_last_player_frame[pnr] = player->Frame;
4071 static_player_is_opaque[pnr] = FALSE;
4073 draw_player[pnr] = TRUE;
4076 if (!draw_player[pnr])
4080 if (!IN_LEV_FIELD(player->jx, player->jy))
4082 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4083 Debug("draw:DrawPlayerExt", "This should never happen!");
4085 draw_player[pnr] = FALSE;
4091 int last_player_graphic = static_last_player_graphic[pnr];
4092 int last_player_frame = static_last_player_frame[pnr];
4093 boolean player_is_opaque = static_player_is_opaque[pnr];
4095 int jx = player->jx;
4096 int jy = player->jy;
4097 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4098 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4099 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4100 int last_jx = (player->is_moving ? jx - dx : jx);
4101 int last_jy = (player->is_moving ? jy - dy : jy);
4102 int next_jx = jx + dx;
4103 int next_jy = jy + dy;
4104 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4105 int sx = SCREENX(jx);
4106 int sy = SCREENY(jy);
4107 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4108 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4109 int element = Tile[jx][jy];
4110 int last_element = Tile[last_jx][last_jy];
4111 int action = (player->is_pushing ? ACTION_PUSHING :
4112 player->is_digging ? ACTION_DIGGING :
4113 player->is_collecting ? ACTION_COLLECTING :
4114 player->is_moving ? ACTION_MOVING :
4115 player->is_snapping ? ACTION_SNAPPING :
4116 player->is_dropping ? ACTION_DROPPING :
4117 player->is_waiting ? player->action_waiting :
4120 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4122 // ------------------------------------------------------------------------
4123 // initialize drawing the player
4124 // ------------------------------------------------------------------------
4126 draw_player[pnr] = FALSE;
4128 // GfxElement[][] is set to the element the player is digging or collecting;
4129 // remove also for off-screen player if the player is not moving anymore
4130 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4131 GfxElement[jx][jy] = EL_UNDEFINED;
4133 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4136 if (element == EL_EXPLOSION)
4139 InitPlayerGfxAnimation(player, action, move_dir);
4141 draw_player[pnr] = TRUE;
4143 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4145 // ------------------------------------------------------------------------
4146 // draw things in the field the player is leaving, if needed
4147 // ------------------------------------------------------------------------
4149 if (!IN_SCR_FIELD(sx, sy))
4150 draw_player[pnr] = FALSE;
4152 if (!player->is_moving)
4155 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4157 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4159 if (last_element == EL_DYNAMITE_ACTIVE ||
4160 last_element == EL_EM_DYNAMITE_ACTIVE ||
4161 last_element == EL_SP_DISK_RED_ACTIVE)
4162 DrawDynamite(last_jx, last_jy);
4164 DrawLevelFieldThruMask(last_jx, last_jy);
4166 else if (last_element == EL_DYNAMITE_ACTIVE ||
4167 last_element == EL_EM_DYNAMITE_ACTIVE ||
4168 last_element == EL_SP_DISK_RED_ACTIVE)
4169 DrawDynamite(last_jx, last_jy);
4171 DrawLevelField(last_jx, last_jy);
4173 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4174 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4176 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4178 // ------------------------------------------------------------------------
4179 // draw things behind the player, if needed
4180 // ------------------------------------------------------------------------
4184 DrawLevelElement(jx, jy, Back[jx][jy]);
4189 if (IS_ACTIVE_BOMB(element))
4191 DrawLevelElement(jx, jy, EL_EMPTY);
4196 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4198 int old_element = GfxElement[jx][jy];
4199 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4200 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4202 if (GFX_CRUMBLED(old_element))
4203 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4205 DrawScreenGraphic(sx, sy, old_graphic, frame);
4207 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4208 static_player_is_opaque[pnr] = TRUE;
4212 GfxElement[jx][jy] = EL_UNDEFINED;
4214 // make sure that pushed elements are drawn with correct frame rate
4215 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4217 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4218 GfxFrame[jx][jy] = player->StepFrame;
4220 DrawLevelField(jx, jy);
4223 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4225 // ------------------------------------------------------------------------
4226 // draw things the player is pushing, if needed
4227 // ------------------------------------------------------------------------
4229 if (!player->is_pushing || !player->is_moving)
4232 int gfx_frame = GfxFrame[jx][jy];
4234 if (!IS_MOVING(jx, jy)) // push movement already finished
4236 element = Tile[next_jx][next_jy];
4237 gfx_frame = GfxFrame[next_jx][next_jy];
4240 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4241 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4242 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4244 // draw background element under pushed element (like the Sokoban field)
4245 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4247 // this allows transparent pushing animation over non-black background
4250 DrawLevelElement(jx, jy, Back[jx][jy]);
4252 DrawLevelElement(jx, jy, EL_EMPTY);
4254 if (Back[next_jx][next_jy])
4255 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4257 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4259 else if (Back[next_jx][next_jy])
4260 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4262 int px = SCREENX(jx), py = SCREENY(jy);
4263 int pxx = (TILEX - ABS(sxx)) * dx;
4264 int pyy = (TILEY - ABS(syy)) * dy;
4267 // do not draw (EM style) pushing animation when pushing is finished
4268 // (two-tile animations usually do not contain start and end frame)
4269 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4270 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4272 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4274 // masked drawing is needed for EMC style (double) movement graphics
4275 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4276 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4279 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4281 // ------------------------------------------------------------------------
4282 // draw player himself
4283 // ------------------------------------------------------------------------
4285 int graphic = getPlayerGraphic(player, move_dir);
4287 // in the case of changed player action or direction, prevent the current
4288 // animation frame from being restarted for identical animations
4289 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4290 player->Frame = last_player_frame;
4292 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4294 if (player_is_opaque)
4295 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4297 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4299 if (SHIELD_ON(player))
4301 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4302 IMG_SHIELD_NORMAL_ACTIVE);
4303 frame = getGraphicAnimationFrame(graphic, -1);
4305 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4308 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4310 // ------------------------------------------------------------------------
4311 // draw things in front of player (active dynamite or dynabombs)
4312 // ------------------------------------------------------------------------
4314 if (IS_ACTIVE_BOMB(element))
4316 int graphic = el2img(element);
4317 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4319 if (game.emulation == EMU_SUPAPLEX)
4320 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4322 DrawGraphicThruMask(sx, sy, graphic, frame);
4325 if (player_is_moving && last_element == EL_EXPLOSION)
4327 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4328 GfxElement[last_jx][last_jy] : EL_EMPTY);
4329 int graphic = el_act2img(element, ACTION_EXPLODING);
4330 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4331 int phase = ExplodePhase[last_jx][last_jy] - 1;
4332 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4335 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4338 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4340 // ------------------------------------------------------------------------
4341 // draw elements the player is just walking/passing through/under
4342 // ------------------------------------------------------------------------
4344 if (player_is_moving)
4346 // handle the field the player is leaving ...
4347 if (IS_ACCESSIBLE_INSIDE(last_element))
4348 DrawLevelField(last_jx, last_jy);
4349 else if (IS_ACCESSIBLE_UNDER(last_element))
4350 DrawLevelFieldThruMask(last_jx, last_jy);
4353 // do not redraw accessible elements if the player is just pushing them
4354 if (!player_is_moving || !player->is_pushing)
4356 // ... and the field the player is entering
4357 if (IS_ACCESSIBLE_INSIDE(element))
4358 DrawLevelField(jx, jy);
4359 else if (IS_ACCESSIBLE_UNDER(element))
4360 DrawLevelFieldThruMask(jx, jy);
4363 MarkTileDirty(sx, sy);
4367 void DrawPlayer(struct PlayerInfo *player)
4371 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4372 DrawPlayerExt(player, i);
4375 void DrawAllPlayers(void)
4379 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4380 for (j = 0; j < MAX_PLAYERS; j++)
4381 if (stored_player[j].active)
4382 DrawPlayerExt(&stored_player[j], i);
4385 void DrawPlayerField(int x, int y)
4387 if (!IS_PLAYER(x, y))
4390 DrawPlayer(PLAYERINFO(x, y));
4393 // ----------------------------------------------------------------------------
4395 void WaitForEventToContinue(void)
4397 boolean first_wait = TRUE;
4398 boolean still_wait = TRUE;
4400 if (program.headless)
4403 // simulate releasing mouse button over last gadget, if still pressed
4405 HandleGadgets(-1, -1, 0);
4407 button_status = MB_RELEASED;
4410 ClearPlayerAction();
4416 if (NextValidEvent(&event))
4420 case EVENT_BUTTONPRESS:
4421 case EVENT_FINGERPRESS:
4425 case EVENT_BUTTONRELEASE:
4426 case EVENT_FINGERRELEASE:
4427 still_wait = first_wait;
4430 case EVENT_KEYPRESS:
4431 case SDL_CONTROLLERBUTTONDOWN:
4432 case SDL_JOYBUTTONDOWN:
4437 HandleOtherEvents(&event);
4441 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4446 if (!PendingEvent())
4451 #define MAX_REQUEST_LINES 13
4452 #define MAX_REQUEST_LINE_FONT1_LEN 7
4453 #define MAX_REQUEST_LINE_FONT2_LEN 10
4455 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4457 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4459 int draw_buffer_last = GetDrawtoField();
4460 int width = request.width;
4461 int height = request.height;
4465 // when showing request dialog after game ended, deactivate game panel
4466 if (game_just_ended)
4467 game.panel.active = FALSE;
4469 game.request_active = TRUE;
4471 setRequestPosition(&sx, &sy, FALSE);
4473 button_status = MB_RELEASED;
4475 request_gadget_id = -1;
4480 boolean event_handled = FALSE;
4482 if (game_just_ended)
4484 SetDrawtoField(draw_buffer_game);
4486 HandleGameActions();
4488 SetDrawtoField(DRAW_TO_BACKBUFFER);
4490 if (global.use_envelope_request)
4492 // copy current state of request area to middle of playfield area
4493 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4501 while (NextValidEvent(&event))
4503 event_handled = TRUE;
4507 case EVENT_BUTTONPRESS:
4508 case EVENT_BUTTONRELEASE:
4509 case EVENT_MOTIONNOTIFY:
4513 if (event.type == EVENT_MOTIONNOTIFY)
4518 motion_status = TRUE;
4519 mx = ((MotionEvent *) &event)->x;
4520 my = ((MotionEvent *) &event)->y;
4524 motion_status = FALSE;
4525 mx = ((ButtonEvent *) &event)->x;
4526 my = ((ButtonEvent *) &event)->y;
4527 if (event.type == EVENT_BUTTONPRESS)
4528 button_status = ((ButtonEvent *) &event)->button;
4530 button_status = MB_RELEASED;
4533 // this sets 'request_gadget_id'
4534 HandleGadgets(mx, my, button_status);
4536 switch (request_gadget_id)
4538 case TOOL_CTRL_ID_YES:
4539 case TOOL_CTRL_ID_TOUCH_YES:
4542 case TOOL_CTRL_ID_NO:
4543 case TOOL_CTRL_ID_TOUCH_NO:
4546 case TOOL_CTRL_ID_CONFIRM:
4547 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4548 result = TRUE | FALSE;
4551 case TOOL_CTRL_ID_PLAYER_1:
4554 case TOOL_CTRL_ID_PLAYER_2:
4557 case TOOL_CTRL_ID_PLAYER_3:
4560 case TOOL_CTRL_ID_PLAYER_4:
4565 // only check clickable animations if no request gadget clicked
4566 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4573 case SDL_WINDOWEVENT:
4574 HandleWindowEvent((WindowEvent *) &event);
4577 case SDL_APP_WILLENTERBACKGROUND:
4578 case SDL_APP_DIDENTERBACKGROUND:
4579 case SDL_APP_WILLENTERFOREGROUND:
4580 case SDL_APP_DIDENTERFOREGROUND:
4581 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4584 case EVENT_KEYPRESS:
4586 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4591 if (req_state & REQ_CONFIRM)
4600 #if defined(KSYM_Rewind)
4601 case KSYM_Rewind: // for Amazon Fire TV remote
4610 #if defined(KSYM_FastForward)
4611 case KSYM_FastForward: // for Amazon Fire TV remote
4617 HandleKeysDebug(key, KEY_PRESSED);
4621 if (req_state & REQ_PLAYER)
4623 int old_player_nr = setup.network_player_nr;
4626 result = old_player_nr + 1;
4631 result = old_player_nr + 1;
4662 case EVENT_FINGERRELEASE:
4663 case EVENT_KEYRELEASE:
4664 ClearPlayerAction();
4667 case SDL_CONTROLLERBUTTONDOWN:
4668 switch (event.cbutton.button)
4670 case SDL_CONTROLLER_BUTTON_A:
4671 case SDL_CONTROLLER_BUTTON_X:
4672 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4673 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4677 case SDL_CONTROLLER_BUTTON_B:
4678 case SDL_CONTROLLER_BUTTON_Y:
4679 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4680 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4681 case SDL_CONTROLLER_BUTTON_BACK:
4686 if (req_state & REQ_PLAYER)
4688 int old_player_nr = setup.network_player_nr;
4691 result = old_player_nr + 1;
4693 switch (event.cbutton.button)
4695 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4696 case SDL_CONTROLLER_BUTTON_Y:
4700 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4701 case SDL_CONTROLLER_BUTTON_B:
4705 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4706 case SDL_CONTROLLER_BUTTON_A:
4710 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4711 case SDL_CONTROLLER_BUTTON_X:
4722 case SDL_CONTROLLERBUTTONUP:
4723 HandleJoystickEvent(&event);
4724 ClearPlayerAction();
4728 HandleOtherEvents(&event);
4733 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4735 int joy = AnyJoystick();
4737 if (joy & JOY_BUTTON_1)
4739 else if (joy & JOY_BUTTON_2)
4742 else if (AnyJoystick())
4744 int joy = AnyJoystick();
4746 if (req_state & REQ_PLAYER)
4750 else if (joy & JOY_RIGHT)
4752 else if (joy & JOY_DOWN)
4754 else if (joy & JOY_LEFT)
4761 if (game_just_ended)
4763 if (global.use_envelope_request)
4765 // copy back current state of pressed buttons inside request area
4766 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4770 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4776 SetDrawtoField(draw_buffer_last);
4778 game.request_active = FALSE;
4783 static boolean RequestDoor(char *text, unsigned int req_state)
4785 int draw_buffer_last = GetDrawtoField();
4786 unsigned int old_door_state;
4787 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4788 int font_nr = FONT_TEXT_2;
4793 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4795 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4796 font_nr = FONT_TEXT_1;
4799 if (game_status == GAME_MODE_PLAYING)
4800 BlitScreenToBitmap(backbuffer);
4802 // disable deactivated drawing when quick-loading level tape recording
4803 if (tape.playing && tape.deactivate_display)
4804 TapeDeactivateDisplayOff(TRUE);
4806 SetMouseCursor(CURSOR_DEFAULT);
4808 // pause network game while waiting for request to answer
4809 if (network.enabled &&
4810 game_status == GAME_MODE_PLAYING &&
4811 !game.all_players_gone &&
4812 req_state & REQUEST_WAIT_FOR_INPUT)
4813 SendToServer_PausePlaying();
4815 old_door_state = GetDoorState();
4817 // simulate releasing mouse button over last gadget, if still pressed
4819 HandleGadgets(-1, -1, 0);
4823 // draw released gadget before proceeding
4826 if (old_door_state & DOOR_OPEN_1)
4828 CloseDoor(DOOR_CLOSE_1);
4830 // save old door content
4831 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4832 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4835 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4836 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4838 // clear door drawing field
4839 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4841 // force DOOR font inside door area
4842 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4844 // write text for request
4845 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4847 char text_line[max_request_line_len + 1];
4853 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4855 tc = *(text_ptr + tx);
4856 // if (!tc || tc == ' ')
4857 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4861 if ((tc == '?' || tc == '!') && tl == 0)
4871 strncpy(text_line, text_ptr, tl);
4874 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4875 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4876 text_line, font_nr);
4878 text_ptr += tl + (tc == ' ' ? 1 : 0);
4879 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4884 if (req_state & REQ_ASK)
4886 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4887 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4888 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4889 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4891 else if (req_state & REQ_CONFIRM)
4893 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4894 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4896 else if (req_state & REQ_PLAYER)
4898 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4899 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4900 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4901 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4904 // copy request gadgets to door backbuffer
4905 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4907 OpenDoor(DOOR_OPEN_1);
4909 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4911 if (game_status == GAME_MODE_PLAYING)
4913 SetPanelBackground();
4914 SetDrawBackgroundMask(REDRAW_DOOR_1);
4918 SetDrawBackgroundMask(REDRAW_FIELD);
4924 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4926 // ---------- handle request buttons ----------
4927 result = RequestHandleEvents(req_state, draw_buffer_last);
4931 if (!(req_state & REQ_STAY_OPEN))
4933 CloseDoor(DOOR_CLOSE_1);
4935 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4936 (req_state & REQ_REOPEN))
4937 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4942 if (game_status == GAME_MODE_PLAYING)
4944 SetPanelBackground();
4945 SetDrawBackgroundMask(REDRAW_DOOR_1);
4949 SetDrawBackgroundMask(REDRAW_FIELD);
4952 // continue network game after request
4953 if (network.enabled &&
4954 game_status == GAME_MODE_PLAYING &&
4955 !game.all_players_gone &&
4956 req_state & REQUEST_WAIT_FOR_INPUT)
4957 SendToServer_ContinuePlaying();
4959 // restore deactivated drawing when quick-loading level tape recording
4960 if (tape.playing && tape.deactivate_display)
4961 TapeDeactivateDisplayOn();
4966 static boolean RequestEnvelope(char *text, unsigned int req_state)
4968 int draw_buffer_last = GetDrawtoField();
4971 if (game_status == GAME_MODE_PLAYING)
4972 BlitScreenToBitmap(backbuffer);
4974 // disable deactivated drawing when quick-loading level tape recording
4975 if (tape.playing && tape.deactivate_display)
4976 TapeDeactivateDisplayOff(TRUE);
4978 SetMouseCursor(CURSOR_DEFAULT);
4980 // pause network game while waiting for request to answer
4981 if (network.enabled &&
4982 game_status == GAME_MODE_PLAYING &&
4983 !game.all_players_gone &&
4984 req_state & REQUEST_WAIT_FOR_INPUT)
4985 SendToServer_PausePlaying();
4987 // simulate releasing mouse button over last gadget, if still pressed
4989 HandleGadgets(-1, -1, 0);
4993 // (replace with setting corresponding request background)
4994 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4995 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4997 // clear door drawing field
4998 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5000 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5002 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5004 if (game_status == GAME_MODE_PLAYING)
5006 SetPanelBackground();
5007 SetDrawBackgroundMask(REDRAW_DOOR_1);
5011 SetDrawBackgroundMask(REDRAW_FIELD);
5017 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5019 // ---------- handle request buttons ----------
5020 result = RequestHandleEvents(req_state, draw_buffer_last);
5024 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5028 if (game_status == GAME_MODE_PLAYING)
5030 SetPanelBackground();
5031 SetDrawBackgroundMask(REDRAW_DOOR_1);
5035 SetDrawBackgroundMask(REDRAW_FIELD);
5038 // continue network game after request
5039 if (network.enabled &&
5040 game_status == GAME_MODE_PLAYING &&
5041 !game.all_players_gone &&
5042 req_state & REQUEST_WAIT_FOR_INPUT)
5043 SendToServer_ContinuePlaying();
5045 // restore deactivated drawing when quick-loading level tape recording
5046 if (tape.playing && tape.deactivate_display)
5047 TapeDeactivateDisplayOn();
5052 boolean Request(char *text, unsigned int req_state)
5054 boolean overlay_enabled = GetOverlayEnabled();
5057 game.request_active_or_moving = TRUE;
5059 SetOverlayEnabled(FALSE);
5061 if (global.use_envelope_request)
5062 result = RequestEnvelope(text, req_state);
5064 result = RequestDoor(text, req_state);
5066 SetOverlayEnabled(overlay_enabled);
5068 game.request_active_or_moving = FALSE;
5073 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5075 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5076 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5079 if (dpo1->sort_priority != dpo2->sort_priority)
5080 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5082 compare_result = dpo1->nr - dpo2->nr;
5084 return compare_result;
5087 void InitGraphicCompatibilityInfo_Doors(void)
5093 struct DoorInfo *door;
5097 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5098 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5100 { -1, -1, -1, NULL }
5102 struct Rect door_rect_list[] =
5104 { DX, DY, DXSIZE, DYSIZE },
5105 { VX, VY, VXSIZE, VYSIZE }
5109 for (i = 0; doors[i].door_token != -1; i++)
5111 int door_token = doors[i].door_token;
5112 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5113 int part_1 = doors[i].part_1;
5114 int part_8 = doors[i].part_8;
5115 int part_2 = part_1 + 1;
5116 int part_3 = part_1 + 2;
5117 struct DoorInfo *door = doors[i].door;
5118 struct Rect *door_rect = &door_rect_list[door_index];
5119 boolean door_gfx_redefined = FALSE;
5121 // check if any door part graphic definitions have been redefined
5123 for (j = 0; door_part_controls[j].door_token != -1; j++)
5125 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5126 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5128 if (dpc->door_token == door_token && fi->redefined)
5129 door_gfx_redefined = TRUE;
5132 // check for old-style door graphic/animation modifications
5134 if (!door_gfx_redefined)
5136 if (door->anim_mode & ANIM_STATIC_PANEL)
5138 door->panel.step_xoffset = 0;
5139 door->panel.step_yoffset = 0;
5142 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5144 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5145 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5146 int num_door_steps, num_panel_steps;
5148 // remove door part graphics other than the two default wings
5150 for (j = 0; door_part_controls[j].door_token != -1; j++)
5152 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5153 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5155 if (dpc->graphic >= part_3 &&
5156 dpc->graphic <= part_8)
5160 // set graphics and screen positions of the default wings
5162 g_part_1->width = door_rect->width;
5163 g_part_1->height = door_rect->height;
5164 g_part_2->width = door_rect->width;
5165 g_part_2->height = door_rect->height;
5166 g_part_2->src_x = door_rect->width;
5167 g_part_2->src_y = g_part_1->src_y;
5169 door->part_2.x = door->part_1.x;
5170 door->part_2.y = door->part_1.y;
5172 if (door->width != -1)
5174 g_part_1->width = door->width;
5175 g_part_2->width = door->width;
5177 // special treatment for graphics and screen position of right wing
5178 g_part_2->src_x += door_rect->width - door->width;
5179 door->part_2.x += door_rect->width - door->width;
5182 if (door->height != -1)
5184 g_part_1->height = door->height;
5185 g_part_2->height = door->height;
5187 // special treatment for graphics and screen position of bottom wing
5188 g_part_2->src_y += door_rect->height - door->height;
5189 door->part_2.y += door_rect->height - door->height;
5192 // set animation delays for the default wings and panels
5194 door->part_1.step_delay = door->step_delay;
5195 door->part_2.step_delay = door->step_delay;
5196 door->panel.step_delay = door->step_delay;
5198 // set animation draw order for the default wings
5200 door->part_1.sort_priority = 2; // draw left wing over ...
5201 door->part_2.sort_priority = 1; // ... right wing
5203 // set animation draw offset for the default wings
5205 if (door->anim_mode & ANIM_HORIZONTAL)
5207 door->part_1.step_xoffset = door->step_offset;
5208 door->part_1.step_yoffset = 0;
5209 door->part_2.step_xoffset = door->step_offset * -1;
5210 door->part_2.step_yoffset = 0;
5212 num_door_steps = g_part_1->width / door->step_offset;
5214 else // ANIM_VERTICAL
5216 door->part_1.step_xoffset = 0;
5217 door->part_1.step_yoffset = door->step_offset;
5218 door->part_2.step_xoffset = 0;
5219 door->part_2.step_yoffset = door->step_offset * -1;
5221 num_door_steps = g_part_1->height / door->step_offset;
5224 // set animation draw offset for the default panels
5226 if (door->step_offset > 1)
5228 num_panel_steps = 2 * door_rect->height / door->step_offset;
5229 door->panel.start_step = num_panel_steps - num_door_steps;
5230 door->panel.start_step_closing = door->panel.start_step;
5234 num_panel_steps = door_rect->height / door->step_offset;
5235 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5236 door->panel.start_step_closing = door->panel.start_step;
5237 door->panel.step_delay *= 2;
5244 void InitDoors(void)
5248 for (i = 0; door_part_controls[i].door_token != -1; i++)
5250 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5251 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5253 // initialize "start_step_opening" and "start_step_closing", if needed
5254 if (dpc->pos->start_step_opening == 0 &&
5255 dpc->pos->start_step_closing == 0)
5257 // dpc->pos->start_step_opening = dpc->pos->start_step;
5258 dpc->pos->start_step_closing = dpc->pos->start_step;
5261 // fill structure for door part draw order (sorted below)
5263 dpo->sort_priority = dpc->pos->sort_priority;
5266 // sort door part controls according to sort_priority and graphic number
5267 qsort(door_part_order, MAX_DOOR_PARTS,
5268 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5271 unsigned int OpenDoor(unsigned int door_state)
5273 if (door_state & DOOR_COPY_BACK)
5275 if (door_state & DOOR_OPEN_1)
5276 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5277 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5279 if (door_state & DOOR_OPEN_2)
5280 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5281 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5283 door_state &= ~DOOR_COPY_BACK;
5286 return MoveDoor(door_state);
5289 unsigned int CloseDoor(unsigned int door_state)
5291 unsigned int old_door_state = GetDoorState();
5293 if (!(door_state & DOOR_NO_COPY_BACK))
5295 if (old_door_state & DOOR_OPEN_1)
5296 BlitBitmap(backbuffer, bitmap_db_door_1,
5297 DX, DY, DXSIZE, DYSIZE, 0, 0);
5299 if (old_door_state & DOOR_OPEN_2)
5300 BlitBitmap(backbuffer, bitmap_db_door_2,
5301 VX, VY, VXSIZE, VYSIZE, 0, 0);
5303 door_state &= ~DOOR_NO_COPY_BACK;
5306 return MoveDoor(door_state);
5309 unsigned int GetDoorState(void)
5311 return MoveDoor(DOOR_GET_STATE);
5314 unsigned int SetDoorState(unsigned int door_state)
5316 return MoveDoor(door_state | DOOR_SET_STATE);
5319 static int euclid(int a, int b)
5321 return (b ? euclid(b, a % b) : a);
5324 unsigned int MoveDoor(unsigned int door_state)
5326 struct Rect door_rect_list[] =
5328 { DX, DY, DXSIZE, DYSIZE },
5329 { VX, VY, VXSIZE, VYSIZE }
5331 static int door1 = DOOR_CLOSE_1;
5332 static int door2 = DOOR_CLOSE_2;
5333 unsigned int door_delay = 0;
5334 unsigned int door_delay_value;
5337 if (door_state == DOOR_GET_STATE)
5338 return (door1 | door2);
5340 if (door_state & DOOR_SET_STATE)
5342 if (door_state & DOOR_ACTION_1)
5343 door1 = door_state & DOOR_ACTION_1;
5344 if (door_state & DOOR_ACTION_2)
5345 door2 = door_state & DOOR_ACTION_2;
5347 return (door1 | door2);
5350 if (!(door_state & DOOR_FORCE_REDRAW))
5352 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5353 door_state &= ~DOOR_OPEN_1;
5354 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5355 door_state &= ~DOOR_CLOSE_1;
5356 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5357 door_state &= ~DOOR_OPEN_2;
5358 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5359 door_state &= ~DOOR_CLOSE_2;
5362 if (global.autoplay_leveldir)
5364 door_state |= DOOR_NO_DELAY;
5365 door_state &= ~DOOR_CLOSE_ALL;
5368 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5369 door_state |= DOOR_NO_DELAY;
5371 if (door_state & DOOR_ACTION)
5373 boolean door_panel_drawn[NUM_DOORS];
5374 boolean panel_has_doors[NUM_DOORS];
5375 boolean door_part_skip[MAX_DOOR_PARTS];
5376 boolean door_part_done[MAX_DOOR_PARTS];
5377 boolean door_part_done_all;
5378 int num_steps[MAX_DOOR_PARTS];
5379 int max_move_delay = 0; // delay for complete animations of all doors
5380 int max_step_delay = 0; // delay (ms) between two animation frames
5381 int num_move_steps = 0; // number of animation steps for all doors
5382 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5383 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5384 int current_move_delay = 0;
5388 for (i = 0; i < NUM_DOORS; i++)
5389 panel_has_doors[i] = FALSE;
5391 for (i = 0; i < MAX_DOOR_PARTS; i++)
5393 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5394 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5395 int door_token = dpc->door_token;
5397 door_part_done[i] = FALSE;
5398 door_part_skip[i] = (!(door_state & door_token) ||
5402 for (i = 0; i < MAX_DOOR_PARTS; i++)
5404 int nr = door_part_order[i].nr;
5405 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5406 struct DoorPartPosInfo *pos = dpc->pos;
5407 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5408 int door_token = dpc->door_token;
5409 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5410 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5411 int step_xoffset = ABS(pos->step_xoffset);
5412 int step_yoffset = ABS(pos->step_yoffset);
5413 int step_delay = pos->step_delay;
5414 int current_door_state = door_state & door_token;
5415 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5416 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5417 boolean part_opening = (is_panel ? door_closing : door_opening);
5418 int start_step = (part_opening ? pos->start_step_opening :
5419 pos->start_step_closing);
5420 float move_xsize = (step_xoffset ? g->width : 0);
5421 float move_ysize = (step_yoffset ? g->height : 0);
5422 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5423 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5424 int move_steps = (move_xsteps && move_ysteps ?
5425 MIN(move_xsteps, move_ysteps) :
5426 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5427 int move_delay = move_steps * step_delay;
5429 if (door_part_skip[nr])
5432 max_move_delay = MAX(max_move_delay, move_delay);
5433 max_step_delay = (max_step_delay == 0 ? step_delay :
5434 euclid(max_step_delay, step_delay));
5435 num_steps[nr] = move_steps;
5439 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5441 panel_has_doors[door_index] = TRUE;
5445 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5447 num_move_steps = max_move_delay / max_step_delay;
5448 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5450 door_delay_value = max_step_delay;
5452 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5454 start = num_move_steps - 1;
5458 // opening door sound has priority over simultaneously closing door
5459 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5461 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5463 if (door_state & DOOR_OPEN_1)
5464 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5465 if (door_state & DOOR_OPEN_2)
5466 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5468 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5470 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5472 if (door_state & DOOR_CLOSE_1)
5473 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5474 if (door_state & DOOR_CLOSE_2)
5475 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5479 for (k = start; k < num_move_steps; k++)
5481 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5483 door_part_done_all = TRUE;
5485 for (i = 0; i < NUM_DOORS; i++)
5486 door_panel_drawn[i] = FALSE;
5488 for (i = 0; i < MAX_DOOR_PARTS; i++)
5490 int nr = door_part_order[i].nr;
5491 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5492 struct DoorPartPosInfo *pos = dpc->pos;
5493 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5494 int door_token = dpc->door_token;
5495 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5496 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5497 boolean is_panel_and_door_has_closed = FALSE;
5498 struct Rect *door_rect = &door_rect_list[door_index];
5499 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5501 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5502 int current_door_state = door_state & door_token;
5503 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5504 boolean door_closing = !door_opening;
5505 boolean part_opening = (is_panel ? door_closing : door_opening);
5506 boolean part_closing = !part_opening;
5507 int start_step = (part_opening ? pos->start_step_opening :
5508 pos->start_step_closing);
5509 int step_delay = pos->step_delay;
5510 int step_factor = step_delay / max_step_delay;
5511 int k1 = (step_factor ? k / step_factor + 1 : k);
5512 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5513 int kk = MAX(0, k2);
5516 int src_x, src_y, src_xx, src_yy;
5517 int dst_x, dst_y, dst_xx, dst_yy;
5520 if (door_part_skip[nr])
5523 if (!(door_state & door_token))
5531 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5532 int kk_door = MAX(0, k2_door);
5533 int sync_frame = kk_door * door_delay_value;
5534 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5536 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5537 &g_src_x, &g_src_y);
5542 if (!door_panel_drawn[door_index])
5544 ClearRectangle(drawto, door_rect->x, door_rect->y,
5545 door_rect->width, door_rect->height);
5547 door_panel_drawn[door_index] = TRUE;
5550 // draw opening or closing door parts
5552 if (pos->step_xoffset < 0) // door part on right side
5555 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5558 if (dst_xx + width > door_rect->width)
5559 width = door_rect->width - dst_xx;
5561 else // door part on left side
5564 dst_xx = pos->x - kk * pos->step_xoffset;
5568 src_xx = ABS(dst_xx);
5572 width = g->width - src_xx;
5574 if (width > door_rect->width)
5575 width = door_rect->width;
5577 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5580 if (pos->step_yoffset < 0) // door part on bottom side
5583 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5586 if (dst_yy + height > door_rect->height)
5587 height = door_rect->height - dst_yy;
5589 else // door part on top side
5592 dst_yy = pos->y - kk * pos->step_yoffset;
5596 src_yy = ABS(dst_yy);
5600 height = g->height - src_yy;
5603 src_x = g_src_x + src_xx;
5604 src_y = g_src_y + src_yy;
5606 dst_x = door_rect->x + dst_xx;
5607 dst_y = door_rect->y + dst_yy;
5609 is_panel_and_door_has_closed =
5612 panel_has_doors[door_index] &&
5613 k >= num_move_steps_doors_only - 1);
5615 if (width >= 0 && width <= g->width &&
5616 height >= 0 && height <= g->height &&
5617 !is_panel_and_door_has_closed)
5619 if (is_panel || !pos->draw_masked)
5620 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5623 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5627 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5629 if ((part_opening && (width < 0 || height < 0)) ||
5630 (part_closing && (width >= g->width && height >= g->height)))
5631 door_part_done[nr] = TRUE;
5633 // continue door part animations, but not panel after door has closed
5634 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5635 door_part_done_all = FALSE;
5638 if (!(door_state & DOOR_NO_DELAY))
5642 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5644 current_move_delay += max_step_delay;
5646 // prevent OS (Windows) from complaining about program not responding
5650 if (door_part_done_all)
5654 if (!(door_state & DOOR_NO_DELAY))
5656 // wait for specified door action post delay
5657 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5658 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5659 else if (door_state & DOOR_ACTION_1)
5660 door_delay_value = door_1.post_delay;
5661 else if (door_state & DOOR_ACTION_2)
5662 door_delay_value = door_2.post_delay;
5664 while (!DelayReached(&door_delay, door_delay_value))
5669 if (door_state & DOOR_ACTION_1)
5670 door1 = door_state & DOOR_ACTION_1;
5671 if (door_state & DOOR_ACTION_2)
5672 door2 = door_state & DOOR_ACTION_2;
5674 // draw masked border over door area
5675 DrawMaskedBorder(REDRAW_DOOR_1);
5676 DrawMaskedBorder(REDRAW_DOOR_2);
5678 ClearAutoRepeatKeyEvents();
5680 return (door1 | door2);
5683 static boolean useSpecialEditorDoor(void)
5685 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5686 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5688 // do not draw special editor door if editor border defined or redefined
5689 if (graphic_info[graphic].bitmap != NULL || redefined)
5692 // do not draw special editor door if global border defined to be empty
5693 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5696 // do not draw special editor door if viewport definitions do not match
5700 EY + EYSIZE != VY + VYSIZE)
5706 void DrawSpecialEditorDoor(void)
5708 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5709 int top_border_width = gfx1->width;
5710 int top_border_height = gfx1->height;
5711 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5712 int ex = EX - outer_border;
5713 int ey = EY - outer_border;
5714 int vy = VY - outer_border;
5715 int exsize = EXSIZE + 2 * outer_border;
5717 if (!useSpecialEditorDoor())
5720 // draw bigger level editor toolbox window
5721 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5722 top_border_width, top_border_height, ex, ey - top_border_height);
5723 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5724 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5726 redraw_mask |= REDRAW_ALL;
5729 void UndrawSpecialEditorDoor(void)
5731 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5732 int top_border_width = gfx1->width;
5733 int top_border_height = gfx1->height;
5734 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5735 int ex = EX - outer_border;
5736 int ey = EY - outer_border;
5737 int ey_top = ey - top_border_height;
5738 int exsize = EXSIZE + 2 * outer_border;
5739 int eysize = EYSIZE + 2 * outer_border;
5741 if (!useSpecialEditorDoor())
5744 // draw normal tape recorder window
5745 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5747 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5748 ex, ey_top, top_border_width, top_border_height,
5750 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5751 ex, ey, exsize, eysize, ex, ey);
5755 // if screen background is set to "[NONE]", clear editor toolbox window
5756 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5757 ClearRectangle(drawto, ex, ey, exsize, eysize);
5760 redraw_mask |= REDRAW_ALL;
5764 // ---------- new tool button stuff -------------------------------------------
5769 struct TextPosInfo *pos;
5771 boolean is_touch_button;
5773 } toolbutton_info[NUM_TOOL_BUTTONS] =
5776 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5777 TOOL_CTRL_ID_YES, FALSE, "yes"
5780 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5781 TOOL_CTRL_ID_NO, FALSE, "no"
5784 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5785 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5788 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5789 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5792 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5793 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5796 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5797 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5800 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5801 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5804 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5805 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5808 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5809 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5812 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5813 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5817 void CreateToolButtons(void)
5821 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5823 int graphic = toolbutton_info[i].graphic;
5824 struct GraphicInfo *gfx = &graphic_info[graphic];
5825 struct TextPosInfo *pos = toolbutton_info[i].pos;
5826 struct GadgetInfo *gi;
5827 Bitmap *deco_bitmap = None;
5828 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5829 unsigned int event_mask = GD_EVENT_RELEASED;
5830 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5831 int base_x = (is_touch_button ? 0 : DX);
5832 int base_y = (is_touch_button ? 0 : DY);
5833 int gd_x = gfx->src_x;
5834 int gd_y = gfx->src_y;
5835 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5836 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5841 if (global.use_envelope_request && !is_touch_button)
5843 setRequestPosition(&base_x, &base_y, TRUE);
5845 // check if request buttons are outside of envelope and fix, if needed
5846 if (x < 0 || x + gfx->width > request.width ||
5847 y < 0 || y + gfx->height > request.height)
5849 if (id == TOOL_CTRL_ID_YES)
5852 y = request.height - 2 * request.border_size - gfx->height;
5854 else if (id == TOOL_CTRL_ID_NO)
5856 x = request.width - 2 * request.border_size - gfx->width;
5857 y = request.height - 2 * request.border_size - gfx->height;
5859 else if (id == TOOL_CTRL_ID_CONFIRM)
5861 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5862 y = request.height - 2 * request.border_size - gfx->height;
5864 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5866 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5868 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5869 y = request.height - 2 * request.border_size - gfx->height * 2;
5871 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5872 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5877 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5879 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5881 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5882 pos->size, &deco_bitmap, &deco_x, &deco_y);
5883 deco_xpos = (gfx->width - pos->size) / 2;
5884 deco_ypos = (gfx->height - pos->size) / 2;
5887 gi = CreateGadget(GDI_CUSTOM_ID, id,
5888 GDI_IMAGE_ID, graphic,
5889 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5892 GDI_WIDTH, gfx->width,
5893 GDI_HEIGHT, gfx->height,
5894 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5895 GDI_STATE, GD_BUTTON_UNPRESSED,
5896 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5897 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5898 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5899 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5900 GDI_DECORATION_SIZE, pos->size, pos->size,
5901 GDI_DECORATION_SHIFTING, 1, 1,
5902 GDI_DIRECT_DRAW, FALSE,
5903 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5904 GDI_EVENT_MASK, event_mask,
5905 GDI_CALLBACK_ACTION, HandleToolButtons,
5909 Fail("cannot create gadget");
5911 tool_gadget[id] = gi;
5915 void FreeToolButtons(void)
5919 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5920 FreeGadget(tool_gadget[i]);
5923 static void UnmapToolButtons(void)
5927 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5928 UnmapGadget(tool_gadget[i]);
5931 static void HandleToolButtons(struct GadgetInfo *gi)
5933 request_gadget_id = gi->custom_id;
5936 static struct Mapping_EM_to_RND_object
5939 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5940 boolean is_backside; // backside of moving element
5946 em_object_mapping_list[GAME_TILE_MAX + 1] =
5949 Zborder, FALSE, FALSE,
5953 Zplayer, FALSE, FALSE,
5962 Ztank, FALSE, FALSE,
5966 Zeater, FALSE, FALSE,
5970 Zdynamite, FALSE, FALSE,
5974 Zboom, FALSE, FALSE,
5979 Xchain, FALSE, FALSE,
5980 EL_DEFAULT, ACTION_EXPLODING, -1
5983 Xboom_bug, FALSE, FALSE,
5984 EL_BUG, ACTION_EXPLODING, -1
5987 Xboom_tank, FALSE, FALSE,
5988 EL_SPACESHIP, ACTION_EXPLODING, -1
5991 Xboom_android, FALSE, FALSE,
5992 EL_EMC_ANDROID, ACTION_OTHER, -1
5995 Xboom_1, FALSE, FALSE,
5996 EL_DEFAULT, ACTION_EXPLODING, -1
5999 Xboom_2, FALSE, FALSE,
6000 EL_DEFAULT, ACTION_EXPLODING, -1
6004 Xblank, TRUE, FALSE,
6009 Xsplash_e, FALSE, FALSE,
6010 EL_ACID_SPLASH_RIGHT, -1, -1
6013 Xsplash_w, FALSE, FALSE,
6014 EL_ACID_SPLASH_LEFT, -1, -1
6018 Xplant, TRUE, FALSE,
6019 EL_EMC_PLANT, -1, -1
6022 Yplant, FALSE, FALSE,
6023 EL_EMC_PLANT, -1, -1
6027 Xacid_1, TRUE, FALSE,
6031 Xacid_2, FALSE, FALSE,
6035 Xacid_3, FALSE, FALSE,
6039 Xacid_4, FALSE, FALSE,
6043 Xacid_5, FALSE, FALSE,
6047 Xacid_6, FALSE, FALSE,
6051 Xacid_7, FALSE, FALSE,
6055 Xacid_8, FALSE, FALSE,
6060 Xfake_acid_1, TRUE, FALSE,
6061 EL_EMC_FAKE_ACID, -1, -1
6064 Xfake_acid_2, FALSE, FALSE,
6065 EL_EMC_FAKE_ACID, -1, -1
6068 Xfake_acid_3, FALSE, FALSE,
6069 EL_EMC_FAKE_ACID, -1, -1
6072 Xfake_acid_4, FALSE, FALSE,
6073 EL_EMC_FAKE_ACID, -1, -1
6076 Xfake_acid_5, FALSE, FALSE,
6077 EL_EMC_FAKE_ACID, -1, -1
6080 Xfake_acid_6, FALSE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_7, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_8, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6093 Xfake_acid_1_player, FALSE, FALSE,
6094 EL_EMC_FAKE_ACID, -1, -1
6097 Xfake_acid_2_player, FALSE, FALSE,
6098 EL_EMC_FAKE_ACID, -1, -1
6101 Xfake_acid_3_player, FALSE, FALSE,
6102 EL_EMC_FAKE_ACID, -1, -1
6105 Xfake_acid_4_player, FALSE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_5_player, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_6_player, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_7_player, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_8_player, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6126 Xgrass, TRUE, FALSE,
6127 EL_EMC_GRASS, -1, -1
6130 Ygrass_nB, FALSE, FALSE,
6131 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6134 Ygrass_eB, FALSE, FALSE,
6135 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6138 Ygrass_sB, FALSE, FALSE,
6139 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6142 Ygrass_wB, FALSE, FALSE,
6143 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6151 Ydirt_nB, FALSE, FALSE,
6152 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6155 Ydirt_eB, FALSE, FALSE,
6156 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6159 Ydirt_sB, FALSE, FALSE,
6160 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6163 Ydirt_wB, FALSE, FALSE,
6164 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6168 Xandroid, TRUE, FALSE,
6169 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6172 Xandroid_1_n, FALSE, FALSE,
6173 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6176 Xandroid_2_n, FALSE, FALSE,
6177 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6180 Xandroid_1_e, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6184 Xandroid_2_e, FALSE, FALSE,
6185 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6188 Xandroid_1_w, FALSE, FALSE,
6189 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6192 Xandroid_2_w, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6196 Xandroid_1_s, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6200 Xandroid_2_s, FALSE, FALSE,
6201 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6204 Yandroid_n, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6208 Yandroid_nB, FALSE, TRUE,
6209 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6212 Yandroid_ne, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6216 Yandroid_neB, FALSE, TRUE,
6217 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6220 Yandroid_e, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6224 Yandroid_eB, FALSE, TRUE,
6225 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6228 Yandroid_se, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6232 Yandroid_seB, FALSE, TRUE,
6233 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6236 Yandroid_s, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6240 Yandroid_sB, FALSE, TRUE,
6241 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6244 Yandroid_sw, FALSE, FALSE,
6245 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6248 Yandroid_swB, FALSE, TRUE,
6249 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6252 Yandroid_w, FALSE, FALSE,
6253 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6256 Yandroid_wB, FALSE, TRUE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6260 Yandroid_nw, FALSE, FALSE,
6261 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6264 Yandroid_nwB, FALSE, TRUE,
6265 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6269 Xeater_n, TRUE, FALSE,
6270 EL_YAMYAM_UP, -1, -1
6273 Xeater_e, TRUE, FALSE,
6274 EL_YAMYAM_RIGHT, -1, -1
6277 Xeater_w, TRUE, FALSE,
6278 EL_YAMYAM_LEFT, -1, -1
6281 Xeater_s, TRUE, FALSE,
6282 EL_YAMYAM_DOWN, -1, -1
6285 Yeater_n, FALSE, FALSE,
6286 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6289 Yeater_nB, FALSE, TRUE,
6290 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6293 Yeater_e, FALSE, FALSE,
6294 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6297 Yeater_eB, FALSE, TRUE,
6298 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6301 Yeater_s, FALSE, FALSE,
6302 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6305 Yeater_sB, FALSE, TRUE,
6306 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6309 Yeater_w, FALSE, FALSE,
6310 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6313 Yeater_wB, FALSE, TRUE,
6314 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6317 Yeater_stone, FALSE, FALSE,
6318 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6321 Yeater_spring, FALSE, FALSE,
6322 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6326 Xalien, TRUE, FALSE,
6330 Xalien_pause, FALSE, FALSE,
6334 Yalien_n, FALSE, FALSE,
6335 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6338 Yalien_nB, FALSE, TRUE,
6339 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6342 Yalien_e, FALSE, FALSE,
6343 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6346 Yalien_eB, FALSE, TRUE,
6347 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6350 Yalien_s, FALSE, FALSE,
6351 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6354 Yalien_sB, FALSE, TRUE,
6355 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6358 Yalien_w, FALSE, FALSE,
6359 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6362 Yalien_wB, FALSE, TRUE,
6363 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6366 Yalien_stone, FALSE, FALSE,
6367 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6370 Yalien_spring, FALSE, FALSE,
6371 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6375 Xbug_1_n, TRUE, FALSE,
6379 Xbug_1_e, TRUE, FALSE,
6380 EL_BUG_RIGHT, -1, -1
6383 Xbug_1_s, TRUE, FALSE,
6387 Xbug_1_w, TRUE, FALSE,
6391 Xbug_2_n, FALSE, FALSE,
6395 Xbug_2_e, FALSE, FALSE,
6396 EL_BUG_RIGHT, -1, -1
6399 Xbug_2_s, FALSE, FALSE,
6403 Xbug_2_w, FALSE, FALSE,
6407 Ybug_n, FALSE, FALSE,
6408 EL_BUG, ACTION_MOVING, MV_BIT_UP
6411 Ybug_nB, FALSE, TRUE,
6412 EL_BUG, ACTION_MOVING, MV_BIT_UP
6415 Ybug_e, FALSE, FALSE,
6416 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6419 Ybug_eB, FALSE, TRUE,
6420 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6423 Ybug_s, FALSE, FALSE,
6424 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6427 Ybug_sB, FALSE, TRUE,
6428 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6431 Ybug_w, FALSE, FALSE,
6432 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6435 Ybug_wB, FALSE, TRUE,
6436 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6439 Ybug_w_n, FALSE, FALSE,
6440 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6443 Ybug_n_e, FALSE, FALSE,
6444 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6447 Ybug_e_s, FALSE, FALSE,
6448 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6451 Ybug_s_w, FALSE, FALSE,
6452 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6455 Ybug_e_n, FALSE, FALSE,
6456 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6459 Ybug_s_e, FALSE, FALSE,
6460 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6463 Ybug_w_s, FALSE, FALSE,
6464 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6467 Ybug_n_w, FALSE, FALSE,
6468 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6471 Ybug_stone, FALSE, FALSE,
6472 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6475 Ybug_spring, FALSE, FALSE,
6476 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6480 Xtank_1_n, TRUE, FALSE,
6481 EL_SPACESHIP_UP, -1, -1
6484 Xtank_1_e, TRUE, FALSE,
6485 EL_SPACESHIP_RIGHT, -1, -1
6488 Xtank_1_s, TRUE, FALSE,
6489 EL_SPACESHIP_DOWN, -1, -1
6492 Xtank_1_w, TRUE, FALSE,
6493 EL_SPACESHIP_LEFT, -1, -1
6496 Xtank_2_n, FALSE, FALSE,
6497 EL_SPACESHIP_UP, -1, -1
6500 Xtank_2_e, FALSE, FALSE,
6501 EL_SPACESHIP_RIGHT, -1, -1
6504 Xtank_2_s, FALSE, FALSE,
6505 EL_SPACESHIP_DOWN, -1, -1
6508 Xtank_2_w, FALSE, FALSE,
6509 EL_SPACESHIP_LEFT, -1, -1
6512 Ytank_n, FALSE, FALSE,
6513 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6516 Ytank_nB, FALSE, TRUE,
6517 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6520 Ytank_e, FALSE, FALSE,
6521 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6524 Ytank_eB, FALSE, TRUE,
6525 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6528 Ytank_s, FALSE, FALSE,
6529 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6532 Ytank_sB, FALSE, TRUE,
6533 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6536 Ytank_w, FALSE, FALSE,
6537 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6540 Ytank_wB, FALSE, TRUE,
6541 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6544 Ytank_w_n, FALSE, FALSE,
6545 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6548 Ytank_n_e, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6552 Ytank_e_s, FALSE, FALSE,
6553 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6556 Ytank_s_w, FALSE, FALSE,
6557 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6560 Ytank_e_n, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6564 Ytank_s_e, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6568 Ytank_w_s, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6572 Ytank_n_w, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6576 Ytank_stone, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6580 Ytank_spring, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6585 Xemerald, TRUE, FALSE,
6589 Xemerald_pause, FALSE, FALSE,
6593 Xemerald_fall, FALSE, FALSE,
6597 Xemerald_shine, FALSE, FALSE,
6598 EL_EMERALD, ACTION_TWINKLING, -1
6601 Yemerald_s, FALSE, FALSE,
6602 EL_EMERALD, ACTION_FALLING, -1
6605 Yemerald_sB, FALSE, TRUE,
6606 EL_EMERALD, ACTION_FALLING, -1
6609 Yemerald_e, FALSE, FALSE,
6610 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6613 Yemerald_eB, FALSE, TRUE,
6614 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6617 Yemerald_w, FALSE, FALSE,
6618 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6621 Yemerald_wB, FALSE, TRUE,
6622 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6625 Yemerald_blank, FALSE, FALSE,
6626 EL_EMERALD, ACTION_COLLECTING, -1
6630 Xdiamond, TRUE, FALSE,
6634 Xdiamond_pause, FALSE, FALSE,
6638 Xdiamond_fall, FALSE, FALSE,
6642 Xdiamond_shine, FALSE, FALSE,
6643 EL_DIAMOND, ACTION_TWINKLING, -1
6646 Ydiamond_s, FALSE, FALSE,
6647 EL_DIAMOND, ACTION_FALLING, -1
6650 Ydiamond_sB, FALSE, TRUE,
6651 EL_DIAMOND, ACTION_FALLING, -1
6654 Ydiamond_e, FALSE, FALSE,
6655 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6658 Ydiamond_eB, FALSE, TRUE,
6659 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6662 Ydiamond_w, FALSE, FALSE,
6663 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6666 Ydiamond_wB, FALSE, TRUE,
6667 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6670 Ydiamond_blank, FALSE, FALSE,
6671 EL_DIAMOND, ACTION_COLLECTING, -1
6674 Ydiamond_stone, FALSE, FALSE,
6675 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6679 Xstone, TRUE, FALSE,
6683 Xstone_pause, FALSE, FALSE,
6687 Xstone_fall, FALSE, FALSE,
6691 Ystone_s, FALSE, FALSE,
6692 EL_ROCK, ACTION_FALLING, -1
6695 Ystone_sB, FALSE, TRUE,
6696 EL_ROCK, ACTION_FALLING, -1
6699 Ystone_e, FALSE, FALSE,
6700 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6703 Ystone_eB, FALSE, TRUE,
6704 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6707 Ystone_w, FALSE, FALSE,
6708 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6711 Ystone_wB, FALSE, TRUE,
6712 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6720 Xbomb_pause, FALSE, FALSE,
6724 Xbomb_fall, FALSE, FALSE,
6728 Ybomb_s, FALSE, FALSE,
6729 EL_BOMB, ACTION_FALLING, -1
6732 Ybomb_sB, FALSE, TRUE,
6733 EL_BOMB, ACTION_FALLING, -1
6736 Ybomb_e, FALSE, FALSE,
6737 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6740 Ybomb_eB, FALSE, TRUE,
6741 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6744 Ybomb_w, FALSE, FALSE,
6745 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6748 Ybomb_wB, FALSE, TRUE,
6749 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6752 Ybomb_blank, FALSE, FALSE,
6753 EL_BOMB, ACTION_ACTIVATING, -1
6761 Xnut_pause, FALSE, FALSE,
6765 Xnut_fall, FALSE, FALSE,
6769 Ynut_s, FALSE, FALSE,
6770 EL_NUT, ACTION_FALLING, -1
6773 Ynut_sB, FALSE, TRUE,
6774 EL_NUT, ACTION_FALLING, -1
6777 Ynut_e, FALSE, FALSE,
6778 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6781 Ynut_eB, FALSE, TRUE,
6782 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6785 Ynut_w, FALSE, FALSE,
6786 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6789 Ynut_wB, FALSE, TRUE,
6790 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6793 Ynut_stone, FALSE, FALSE,
6794 EL_NUT, ACTION_BREAKING, -1
6798 Xspring, TRUE, FALSE,
6802 Xspring_pause, FALSE, FALSE,
6806 Xspring_e, TRUE, FALSE,
6807 EL_SPRING_RIGHT, -1, -1
6810 Xspring_w, TRUE, FALSE,
6811 EL_SPRING_LEFT, -1, -1
6814 Xspring_fall, FALSE, FALSE,
6818 Yspring_s, FALSE, FALSE,
6819 EL_SPRING, ACTION_FALLING, -1
6822 Yspring_sB, FALSE, TRUE,
6823 EL_SPRING, ACTION_FALLING, -1
6826 Yspring_e, FALSE, FALSE,
6827 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6830 Yspring_eB, FALSE, TRUE,
6831 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6834 Yspring_w, FALSE, FALSE,
6835 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6838 Yspring_wB, FALSE, TRUE,
6839 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6842 Yspring_alien_e, FALSE, FALSE,
6843 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6846 Yspring_alien_eB, FALSE, TRUE,
6847 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6850 Yspring_alien_w, FALSE, FALSE,
6851 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6854 Yspring_alien_wB, FALSE, TRUE,
6855 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6859 Xpush_emerald_e, FALSE, FALSE,
6860 EL_EMERALD, -1, MV_BIT_RIGHT
6863 Xpush_emerald_w, FALSE, FALSE,
6864 EL_EMERALD, -1, MV_BIT_LEFT
6867 Xpush_diamond_e, FALSE, FALSE,
6868 EL_DIAMOND, -1, MV_BIT_RIGHT
6871 Xpush_diamond_w, FALSE, FALSE,
6872 EL_DIAMOND, -1, MV_BIT_LEFT
6875 Xpush_stone_e, FALSE, FALSE,
6876 EL_ROCK, -1, MV_BIT_RIGHT
6879 Xpush_stone_w, FALSE, FALSE,
6880 EL_ROCK, -1, MV_BIT_LEFT
6883 Xpush_bomb_e, FALSE, FALSE,
6884 EL_BOMB, -1, MV_BIT_RIGHT
6887 Xpush_bomb_w, FALSE, FALSE,
6888 EL_BOMB, -1, MV_BIT_LEFT
6891 Xpush_nut_e, FALSE, FALSE,
6892 EL_NUT, -1, MV_BIT_RIGHT
6895 Xpush_nut_w, FALSE, FALSE,
6896 EL_NUT, -1, MV_BIT_LEFT
6899 Xpush_spring_e, FALSE, FALSE,
6900 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6903 Xpush_spring_w, FALSE, FALSE,
6904 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6908 Xdynamite, TRUE, FALSE,
6909 EL_EM_DYNAMITE, -1, -1
6912 Ydynamite_blank, FALSE, FALSE,
6913 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6916 Xdynamite_1, TRUE, FALSE,
6917 EL_EM_DYNAMITE_ACTIVE, -1, -1
6920 Xdynamite_2, FALSE, FALSE,
6921 EL_EM_DYNAMITE_ACTIVE, -1, -1
6924 Xdynamite_3, FALSE, FALSE,
6925 EL_EM_DYNAMITE_ACTIVE, -1, -1
6928 Xdynamite_4, FALSE, FALSE,
6929 EL_EM_DYNAMITE_ACTIVE, -1, -1
6933 Xkey_1, TRUE, FALSE,
6937 Xkey_2, TRUE, FALSE,
6941 Xkey_3, TRUE, FALSE,
6945 Xkey_4, TRUE, FALSE,
6949 Xkey_5, TRUE, FALSE,
6950 EL_EMC_KEY_5, -1, -1
6953 Xkey_6, TRUE, FALSE,
6954 EL_EMC_KEY_6, -1, -1
6957 Xkey_7, TRUE, FALSE,
6958 EL_EMC_KEY_7, -1, -1
6961 Xkey_8, TRUE, FALSE,
6962 EL_EMC_KEY_8, -1, -1
6966 Xdoor_1, TRUE, FALSE,
6967 EL_EM_GATE_1, -1, -1
6970 Xdoor_2, TRUE, FALSE,
6971 EL_EM_GATE_2, -1, -1
6974 Xdoor_3, TRUE, FALSE,
6975 EL_EM_GATE_3, -1, -1
6978 Xdoor_4, TRUE, FALSE,
6979 EL_EM_GATE_4, -1, -1
6982 Xdoor_5, TRUE, FALSE,
6983 EL_EMC_GATE_5, -1, -1
6986 Xdoor_6, TRUE, FALSE,
6987 EL_EMC_GATE_6, -1, -1
6990 Xdoor_7, TRUE, FALSE,
6991 EL_EMC_GATE_7, -1, -1
6994 Xdoor_8, TRUE, FALSE,
6995 EL_EMC_GATE_8, -1, -1
6999 Xfake_door_1, TRUE, FALSE,
7000 EL_EM_GATE_1_GRAY, -1, -1
7003 Xfake_door_2, TRUE, FALSE,
7004 EL_EM_GATE_2_GRAY, -1, -1
7007 Xfake_door_3, TRUE, FALSE,
7008 EL_EM_GATE_3_GRAY, -1, -1
7011 Xfake_door_4, TRUE, FALSE,
7012 EL_EM_GATE_4_GRAY, -1, -1
7015 Xfake_door_5, TRUE, FALSE,
7016 EL_EMC_GATE_5_GRAY, -1, -1
7019 Xfake_door_6, TRUE, FALSE,
7020 EL_EMC_GATE_6_GRAY, -1, -1
7023 Xfake_door_7, TRUE, FALSE,
7024 EL_EMC_GATE_7_GRAY, -1, -1
7027 Xfake_door_8, TRUE, FALSE,
7028 EL_EMC_GATE_8_GRAY, -1, -1
7032 Xballoon, TRUE, FALSE,
7036 Yballoon_n, FALSE, FALSE,
7037 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7040 Yballoon_nB, FALSE, TRUE,
7041 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7044 Yballoon_e, FALSE, FALSE,
7045 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7048 Yballoon_eB, FALSE, TRUE,
7049 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7052 Yballoon_s, FALSE, FALSE,
7053 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7056 Yballoon_sB, FALSE, TRUE,
7057 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7060 Yballoon_w, FALSE, FALSE,
7061 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7064 Yballoon_wB, FALSE, TRUE,
7065 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7069 Xball_1, TRUE, FALSE,
7070 EL_EMC_MAGIC_BALL, -1, -1
7073 Yball_1, FALSE, FALSE,
7074 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7077 Xball_2, FALSE, FALSE,
7078 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7081 Yball_2, FALSE, FALSE,
7082 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7085 Yball_blank, FALSE, FALSE,
7086 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7090 Xamoeba_1, TRUE, FALSE,
7091 EL_AMOEBA_DRY, ACTION_OTHER, -1
7094 Xamoeba_2, FALSE, FALSE,
7095 EL_AMOEBA_DRY, ACTION_OTHER, -1
7098 Xamoeba_3, FALSE, FALSE,
7099 EL_AMOEBA_DRY, ACTION_OTHER, -1
7102 Xamoeba_4, FALSE, FALSE,
7103 EL_AMOEBA_DRY, ACTION_OTHER, -1
7106 Xamoeba_5, TRUE, FALSE,
7107 EL_AMOEBA_WET, ACTION_OTHER, -1
7110 Xamoeba_6, FALSE, FALSE,
7111 EL_AMOEBA_WET, ACTION_OTHER, -1
7114 Xamoeba_7, FALSE, FALSE,
7115 EL_AMOEBA_WET, ACTION_OTHER, -1
7118 Xamoeba_8, FALSE, FALSE,
7119 EL_AMOEBA_WET, ACTION_OTHER, -1
7124 EL_AMOEBA_DROP, ACTION_GROWING, -1
7127 Xdrip_fall, FALSE, FALSE,
7128 EL_AMOEBA_DROP, -1, -1
7131 Xdrip_stretch, FALSE, FALSE,
7132 EL_AMOEBA_DROP, ACTION_FALLING, -1
7135 Xdrip_stretchB, FALSE, TRUE,
7136 EL_AMOEBA_DROP, ACTION_FALLING, -1
7139 Ydrip_1_s, FALSE, FALSE,
7140 EL_AMOEBA_DROP, ACTION_FALLING, -1
7143 Ydrip_1_sB, FALSE, TRUE,
7144 EL_AMOEBA_DROP, ACTION_FALLING, -1
7147 Ydrip_2_s, FALSE, FALSE,
7148 EL_AMOEBA_DROP, ACTION_FALLING, -1
7151 Ydrip_2_sB, FALSE, TRUE,
7152 EL_AMOEBA_DROP, ACTION_FALLING, -1
7156 Xwonderwall, TRUE, FALSE,
7157 EL_MAGIC_WALL, -1, -1
7160 Ywonderwall, FALSE, FALSE,
7161 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7165 Xwheel, TRUE, FALSE,
7166 EL_ROBOT_WHEEL, -1, -1
7169 Ywheel, FALSE, FALSE,
7170 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7174 Xswitch, TRUE, FALSE,
7175 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7178 Yswitch, FALSE, FALSE,
7179 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7183 Xbumper, TRUE, FALSE,
7184 EL_EMC_SPRING_BUMPER, -1, -1
7187 Ybumper, FALSE, FALSE,
7188 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7192 Xacid_nw, TRUE, FALSE,
7193 EL_ACID_POOL_TOPLEFT, -1, -1
7196 Xacid_ne, TRUE, FALSE,
7197 EL_ACID_POOL_TOPRIGHT, -1, -1
7200 Xacid_sw, TRUE, FALSE,
7201 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7204 Xacid_s, TRUE, FALSE,
7205 EL_ACID_POOL_BOTTOM, -1, -1
7208 Xacid_se, TRUE, FALSE,
7209 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7213 Xfake_blank, TRUE, FALSE,
7214 EL_INVISIBLE_WALL, -1, -1
7217 Yfake_blank, FALSE, FALSE,
7218 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7222 Xfake_grass, TRUE, FALSE,
7223 EL_EMC_FAKE_GRASS, -1, -1
7226 Yfake_grass, FALSE, FALSE,
7227 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7231 Xfake_amoeba, TRUE, FALSE,
7232 EL_EMC_DRIPPER, -1, -1
7235 Yfake_amoeba, FALSE, FALSE,
7236 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7240 Xlenses, TRUE, FALSE,
7241 EL_EMC_LENSES, -1, -1
7245 Xmagnify, TRUE, FALSE,
7246 EL_EMC_MAGNIFIER, -1, -1
7251 EL_QUICKSAND_EMPTY, -1, -1
7254 Xsand_stone, TRUE, FALSE,
7255 EL_QUICKSAND_FULL, -1, -1
7258 Xsand_stonein_1, FALSE, TRUE,
7259 EL_ROCK, ACTION_FILLING, -1
7262 Xsand_stonein_2, FALSE, TRUE,
7263 EL_ROCK, ACTION_FILLING, -1
7266 Xsand_stonein_3, FALSE, TRUE,
7267 EL_ROCK, ACTION_FILLING, -1
7270 Xsand_stonein_4, FALSE, TRUE,
7271 EL_ROCK, ACTION_FILLING, -1
7274 Xsand_sandstone_1, FALSE, FALSE,
7275 EL_QUICKSAND_FILLING, -1, -1
7278 Xsand_sandstone_2, FALSE, FALSE,
7279 EL_QUICKSAND_FILLING, -1, -1
7282 Xsand_sandstone_3, FALSE, FALSE,
7283 EL_QUICKSAND_FILLING, -1, -1
7286 Xsand_sandstone_4, FALSE, FALSE,
7287 EL_QUICKSAND_FILLING, -1, -1
7290 Xsand_stonesand_1, FALSE, FALSE,
7291 EL_QUICKSAND_EMPTYING, -1, -1
7294 Xsand_stonesand_2, FALSE, FALSE,
7295 EL_QUICKSAND_EMPTYING, -1, -1
7298 Xsand_stonesand_3, FALSE, FALSE,
7299 EL_QUICKSAND_EMPTYING, -1, -1
7302 Xsand_stonesand_4, FALSE, FALSE,
7303 EL_QUICKSAND_EMPTYING, -1, -1
7306 Xsand_stoneout_1, FALSE, FALSE,
7307 EL_ROCK, ACTION_EMPTYING, -1
7310 Xsand_stoneout_2, FALSE, FALSE,
7311 EL_ROCK, ACTION_EMPTYING, -1
7314 Xsand_stonesand_quickout_1, FALSE, FALSE,
7315 EL_QUICKSAND_EMPTYING, -1, -1
7318 Xsand_stonesand_quickout_2, FALSE, FALSE,
7319 EL_QUICKSAND_EMPTYING, -1, -1
7323 Xslide_ns, TRUE, FALSE,
7324 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7327 Yslide_ns_blank, FALSE, FALSE,
7328 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7331 Xslide_ew, TRUE, FALSE,
7332 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7335 Yslide_ew_blank, FALSE, FALSE,
7336 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7340 Xwind_n, TRUE, FALSE,
7341 EL_BALLOON_SWITCH_UP, -1, -1
7344 Xwind_e, TRUE, FALSE,
7345 EL_BALLOON_SWITCH_RIGHT, -1, -1
7348 Xwind_s, TRUE, FALSE,
7349 EL_BALLOON_SWITCH_DOWN, -1, -1
7352 Xwind_w, TRUE, FALSE,
7353 EL_BALLOON_SWITCH_LEFT, -1, -1
7356 Xwind_any, TRUE, FALSE,
7357 EL_BALLOON_SWITCH_ANY, -1, -1
7360 Xwind_stop, TRUE, FALSE,
7361 EL_BALLOON_SWITCH_NONE, -1, -1
7366 EL_EM_EXIT_CLOSED, -1, -1
7369 Xexit_1, TRUE, FALSE,
7370 EL_EM_EXIT_OPEN, -1, -1
7373 Xexit_2, FALSE, FALSE,
7374 EL_EM_EXIT_OPEN, -1, -1
7377 Xexit_3, FALSE, FALSE,
7378 EL_EM_EXIT_OPEN, -1, -1
7382 Xpause, FALSE, FALSE,
7387 Xwall_1, TRUE, FALSE,
7391 Xwall_2, TRUE, FALSE,
7392 EL_EMC_WALL_14, -1, -1
7395 Xwall_3, TRUE, FALSE,
7396 EL_EMC_WALL_15, -1, -1
7399 Xwall_4, TRUE, FALSE,
7400 EL_EMC_WALL_16, -1, -1
7404 Xroundwall_1, TRUE, FALSE,
7405 EL_WALL_SLIPPERY, -1, -1
7408 Xroundwall_2, TRUE, FALSE,
7409 EL_EMC_WALL_SLIPPERY_2, -1, -1
7412 Xroundwall_3, TRUE, FALSE,
7413 EL_EMC_WALL_SLIPPERY_3, -1, -1
7416 Xroundwall_4, TRUE, FALSE,
7417 EL_EMC_WALL_SLIPPERY_4, -1, -1
7421 Xsteel_1, TRUE, FALSE,
7422 EL_STEELWALL, -1, -1
7425 Xsteel_2, TRUE, FALSE,
7426 EL_EMC_STEELWALL_2, -1, -1
7429 Xsteel_3, TRUE, FALSE,
7430 EL_EMC_STEELWALL_3, -1, -1
7433 Xsteel_4, TRUE, FALSE,
7434 EL_EMC_STEELWALL_4, -1, -1
7438 Xdecor_1, TRUE, FALSE,
7439 EL_EMC_WALL_8, -1, -1
7442 Xdecor_2, TRUE, FALSE,
7443 EL_EMC_WALL_6, -1, -1
7446 Xdecor_3, TRUE, FALSE,
7447 EL_EMC_WALL_4, -1, -1
7450 Xdecor_4, TRUE, FALSE,
7451 EL_EMC_WALL_7, -1, -1
7454 Xdecor_5, TRUE, FALSE,
7455 EL_EMC_WALL_5, -1, -1
7458 Xdecor_6, TRUE, FALSE,
7459 EL_EMC_WALL_9, -1, -1
7462 Xdecor_7, TRUE, FALSE,
7463 EL_EMC_WALL_10, -1, -1
7466 Xdecor_8, TRUE, FALSE,
7467 EL_EMC_WALL_1, -1, -1
7470 Xdecor_9, TRUE, FALSE,
7471 EL_EMC_WALL_2, -1, -1
7474 Xdecor_10, TRUE, FALSE,
7475 EL_EMC_WALL_3, -1, -1
7478 Xdecor_11, TRUE, FALSE,
7479 EL_EMC_WALL_11, -1, -1
7482 Xdecor_12, TRUE, FALSE,
7483 EL_EMC_WALL_12, -1, -1
7487 Xalpha_0, TRUE, FALSE,
7488 EL_CHAR('0'), -1, -1
7491 Xalpha_1, TRUE, FALSE,
7492 EL_CHAR('1'), -1, -1
7495 Xalpha_2, TRUE, FALSE,
7496 EL_CHAR('2'), -1, -1
7499 Xalpha_3, TRUE, FALSE,
7500 EL_CHAR('3'), -1, -1
7503 Xalpha_4, TRUE, FALSE,
7504 EL_CHAR('4'), -1, -1
7507 Xalpha_5, TRUE, FALSE,
7508 EL_CHAR('5'), -1, -1
7511 Xalpha_6, TRUE, FALSE,
7512 EL_CHAR('6'), -1, -1
7515 Xalpha_7, TRUE, FALSE,
7516 EL_CHAR('7'), -1, -1
7519 Xalpha_8, TRUE, FALSE,
7520 EL_CHAR('8'), -1, -1
7523 Xalpha_9, TRUE, FALSE,
7524 EL_CHAR('9'), -1, -1
7527 Xalpha_excla, TRUE, FALSE,
7528 EL_CHAR('!'), -1, -1
7531 Xalpha_apost, TRUE, FALSE,
7532 EL_CHAR('\''), -1, -1
7535 Xalpha_comma, TRUE, FALSE,
7536 EL_CHAR(','), -1, -1
7539 Xalpha_minus, TRUE, FALSE,
7540 EL_CHAR('-'), -1, -1
7543 Xalpha_perio, TRUE, FALSE,
7544 EL_CHAR('.'), -1, -1
7547 Xalpha_colon, TRUE, FALSE,
7548 EL_CHAR(':'), -1, -1
7551 Xalpha_quest, TRUE, FALSE,
7552 EL_CHAR('?'), -1, -1
7555 Xalpha_a, TRUE, FALSE,
7556 EL_CHAR('A'), -1, -1
7559 Xalpha_b, TRUE, FALSE,
7560 EL_CHAR('B'), -1, -1
7563 Xalpha_c, TRUE, FALSE,
7564 EL_CHAR('C'), -1, -1
7567 Xalpha_d, TRUE, FALSE,
7568 EL_CHAR('D'), -1, -1
7571 Xalpha_e, TRUE, FALSE,
7572 EL_CHAR('E'), -1, -1
7575 Xalpha_f, TRUE, FALSE,
7576 EL_CHAR('F'), -1, -1
7579 Xalpha_g, TRUE, FALSE,
7580 EL_CHAR('G'), -1, -1
7583 Xalpha_h, TRUE, FALSE,
7584 EL_CHAR('H'), -1, -1
7587 Xalpha_i, TRUE, FALSE,
7588 EL_CHAR('I'), -1, -1
7591 Xalpha_j, TRUE, FALSE,
7592 EL_CHAR('J'), -1, -1
7595 Xalpha_k, TRUE, FALSE,
7596 EL_CHAR('K'), -1, -1
7599 Xalpha_l, TRUE, FALSE,
7600 EL_CHAR('L'), -1, -1
7603 Xalpha_m, TRUE, FALSE,
7604 EL_CHAR('M'), -1, -1
7607 Xalpha_n, TRUE, FALSE,
7608 EL_CHAR('N'), -1, -1
7611 Xalpha_o, TRUE, FALSE,
7612 EL_CHAR('O'), -1, -1
7615 Xalpha_p, TRUE, FALSE,
7616 EL_CHAR('P'), -1, -1
7619 Xalpha_q, TRUE, FALSE,
7620 EL_CHAR('Q'), -1, -1
7623 Xalpha_r, TRUE, FALSE,
7624 EL_CHAR('R'), -1, -1
7627 Xalpha_s, TRUE, FALSE,
7628 EL_CHAR('S'), -1, -1
7631 Xalpha_t, TRUE, FALSE,
7632 EL_CHAR('T'), -1, -1
7635 Xalpha_u, TRUE, FALSE,
7636 EL_CHAR('U'), -1, -1
7639 Xalpha_v, TRUE, FALSE,
7640 EL_CHAR('V'), -1, -1
7643 Xalpha_w, TRUE, FALSE,
7644 EL_CHAR('W'), -1, -1
7647 Xalpha_x, TRUE, FALSE,
7648 EL_CHAR('X'), -1, -1
7651 Xalpha_y, TRUE, FALSE,
7652 EL_CHAR('Y'), -1, -1
7655 Xalpha_z, TRUE, FALSE,
7656 EL_CHAR('Z'), -1, -1
7659 Xalpha_arrow_e, TRUE, FALSE,
7660 EL_CHAR('>'), -1, -1
7663 Xalpha_arrow_w, TRUE, FALSE,
7664 EL_CHAR('<'), -1, -1
7667 Xalpha_copyr, TRUE, FALSE,
7668 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7672 Ykey_1_blank, FALSE, FALSE,
7673 EL_EM_KEY_1, ACTION_COLLECTING, -1
7676 Ykey_2_blank, FALSE, FALSE,
7677 EL_EM_KEY_2, ACTION_COLLECTING, -1
7680 Ykey_3_blank, FALSE, FALSE,
7681 EL_EM_KEY_3, ACTION_COLLECTING, -1
7684 Ykey_4_blank, FALSE, FALSE,
7685 EL_EM_KEY_4, ACTION_COLLECTING, -1
7688 Ykey_5_blank, FALSE, FALSE,
7689 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7692 Ykey_6_blank, FALSE, FALSE,
7693 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7696 Ykey_7_blank, FALSE, FALSE,
7697 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7700 Ykey_8_blank, FALSE, FALSE,
7701 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7704 Ylenses_blank, FALSE, FALSE,
7705 EL_EMC_LENSES, ACTION_COLLECTING, -1
7708 Ymagnify_blank, FALSE, FALSE,
7709 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7712 Ygrass_blank, FALSE, FALSE,
7713 EL_EMC_GRASS, ACTION_SNAPPING, -1
7716 Ydirt_blank, FALSE, FALSE,
7717 EL_SAND, ACTION_SNAPPING, -1
7726 static struct Mapping_EM_to_RND_player
7735 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7739 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7743 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7747 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7751 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7755 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7759 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7763 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7767 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7771 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7775 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7779 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7783 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7787 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7791 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7795 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7799 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7803 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7807 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7811 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7815 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7819 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7823 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7827 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7831 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7835 EL_PLAYER_1, ACTION_DEFAULT, -1,
7839 EL_PLAYER_2, ACTION_DEFAULT, -1,
7843 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7847 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7851 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7855 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7859 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7863 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7867 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7871 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7875 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7879 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7883 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7887 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7891 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7895 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7899 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7903 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7907 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7911 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7915 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7919 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7923 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7927 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7931 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7935 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7939 EL_PLAYER_3, ACTION_DEFAULT, -1,
7943 EL_PLAYER_4, ACTION_DEFAULT, -1,
7952 int map_element_RND_to_EM_cave(int element_rnd)
7954 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7955 static boolean mapping_initialized = FALSE;
7957 if (!mapping_initialized)
7961 // return "Xalpha_quest" for all undefined elements in mapping array
7962 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7963 mapping_RND_to_EM[i] = Xalpha_quest;
7965 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7966 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7967 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7968 em_object_mapping_list[i].element_em;
7970 mapping_initialized = TRUE;
7973 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7975 Warn("invalid RND level element %d", element_rnd);
7980 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7983 int map_element_EM_to_RND_cave(int element_em_cave)
7985 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7986 static boolean mapping_initialized = FALSE;
7988 if (!mapping_initialized)
7992 // return "EL_UNKNOWN" for all undefined elements in mapping array
7993 for (i = 0; i < GAME_TILE_MAX; i++)
7994 mapping_EM_to_RND[i] = EL_UNKNOWN;
7996 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7997 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7998 em_object_mapping_list[i].element_rnd;
8000 mapping_initialized = TRUE;
8003 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8005 Warn("invalid EM cave element %d", element_em_cave);
8010 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8013 int map_element_EM_to_RND_game(int element_em_game)
8015 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8016 static boolean mapping_initialized = FALSE;
8018 if (!mapping_initialized)
8022 // return "EL_UNKNOWN" for all undefined elements in mapping array
8023 for (i = 0; i < GAME_TILE_MAX; i++)
8024 mapping_EM_to_RND[i] = EL_UNKNOWN;
8026 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8027 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8028 em_object_mapping_list[i].element_rnd;
8030 mapping_initialized = TRUE;
8033 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8035 Warn("invalid EM game element %d", element_em_game);
8040 return mapping_EM_to_RND[element_em_game];
8043 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8045 struct LevelInfo_EM *level_em = level->native_em_level;
8046 struct CAVE *cav = level_em->cav;
8049 for (i = 0; i < GAME_TILE_MAX; i++)
8050 cav->android_array[i] = Cblank;
8052 for (i = 0; i < level->num_android_clone_elements; i++)
8054 int element_rnd = level->android_clone_element[i];
8055 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8057 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8058 if (em_object_mapping_list[j].element_rnd == element_rnd)
8059 cav->android_array[em_object_mapping_list[j].element_em] =
8064 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8066 struct LevelInfo_EM *level_em = level->native_em_level;
8067 struct CAVE *cav = level_em->cav;
8070 level->num_android_clone_elements = 0;
8072 for (i = 0; i < GAME_TILE_MAX; i++)
8074 int element_em_cave = cav->android_array[i];
8076 boolean element_found = FALSE;
8078 if (element_em_cave == Cblank)
8081 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8083 for (j = 0; j < level->num_android_clone_elements; j++)
8084 if (level->android_clone_element[j] == element_rnd)
8085 element_found = TRUE;
8089 level->android_clone_element[level->num_android_clone_elements++] =
8092 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8097 if (level->num_android_clone_elements == 0)
8099 level->num_android_clone_elements = 1;
8100 level->android_clone_element[0] = EL_EMPTY;
8104 int map_direction_RND_to_EM(int direction)
8106 return (direction == MV_UP ? 0 :
8107 direction == MV_RIGHT ? 1 :
8108 direction == MV_DOWN ? 2 :
8109 direction == MV_LEFT ? 3 :
8113 int map_direction_EM_to_RND(int direction)
8115 return (direction == 0 ? MV_UP :
8116 direction == 1 ? MV_RIGHT :
8117 direction == 2 ? MV_DOWN :
8118 direction == 3 ? MV_LEFT :
8122 int map_element_RND_to_SP(int element_rnd)
8124 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8126 if (element_rnd >= EL_SP_START &&
8127 element_rnd <= EL_SP_END)
8128 element_sp = element_rnd - EL_SP_START;
8129 else if (element_rnd == EL_EMPTY_SPACE)
8131 else if (element_rnd == EL_INVISIBLE_WALL)
8137 int map_element_SP_to_RND(int element_sp)
8139 int element_rnd = EL_UNKNOWN;
8141 if (element_sp >= 0x00 &&
8143 element_rnd = EL_SP_START + element_sp;
8144 else if (element_sp == 0x28)
8145 element_rnd = EL_INVISIBLE_WALL;
8150 int map_action_SP_to_RND(int action_sp)
8154 case actActive: return ACTION_ACTIVE;
8155 case actImpact: return ACTION_IMPACT;
8156 case actExploding: return ACTION_EXPLODING;
8157 case actDigging: return ACTION_DIGGING;
8158 case actSnapping: return ACTION_SNAPPING;
8159 case actCollecting: return ACTION_COLLECTING;
8160 case actPassing: return ACTION_PASSING;
8161 case actPushing: return ACTION_PUSHING;
8162 case actDropping: return ACTION_DROPPING;
8164 default: return ACTION_DEFAULT;
8168 int map_element_RND_to_MM(int element_rnd)
8170 return (element_rnd >= EL_MM_START_1 &&
8171 element_rnd <= EL_MM_END_1 ?
8172 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8174 element_rnd >= EL_MM_START_2 &&
8175 element_rnd <= EL_MM_END_2 ?
8176 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8178 element_rnd >= EL_CHAR_START &&
8179 element_rnd <= EL_CHAR_END ?
8180 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8182 element_rnd >= EL_MM_RUNTIME_START &&
8183 element_rnd <= EL_MM_RUNTIME_END ?
8184 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8186 element_rnd >= EL_MM_DUMMY_START &&
8187 element_rnd <= EL_MM_DUMMY_END ?
8188 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8190 EL_MM_EMPTY_NATIVE);
8193 int map_element_MM_to_RND(int element_mm)
8195 return (element_mm == EL_MM_EMPTY_NATIVE ||
8196 element_mm == EL_DF_EMPTY_NATIVE ?
8199 element_mm >= EL_MM_START_1_NATIVE &&
8200 element_mm <= EL_MM_END_1_NATIVE ?
8201 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8203 element_mm >= EL_MM_START_2_NATIVE &&
8204 element_mm <= EL_MM_END_2_NATIVE ?
8205 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8207 element_mm >= EL_MM_CHAR_START_NATIVE &&
8208 element_mm <= EL_MM_CHAR_END_NATIVE ?
8209 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8211 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8212 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8213 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8215 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8216 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8217 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8222 int map_action_MM_to_RND(int action_mm)
8224 // all MM actions are defined to exactly match their RND counterparts
8228 int map_sound_MM_to_RND(int sound_mm)
8232 case SND_MM_GAME_LEVELTIME_CHARGING:
8233 return SND_GAME_LEVELTIME_CHARGING;
8235 case SND_MM_GAME_HEALTH_CHARGING:
8236 return SND_GAME_HEALTH_CHARGING;
8239 return SND_UNDEFINED;
8243 int map_mm_wall_element(int element)
8245 return (element >= EL_MM_STEEL_WALL_START &&
8246 element <= EL_MM_STEEL_WALL_END ?
8249 element >= EL_MM_WOODEN_WALL_START &&
8250 element <= EL_MM_WOODEN_WALL_END ?
8253 element >= EL_MM_ICE_WALL_START &&
8254 element <= EL_MM_ICE_WALL_END ?
8257 element >= EL_MM_AMOEBA_WALL_START &&
8258 element <= EL_MM_AMOEBA_WALL_END ?
8261 element >= EL_DF_STEEL_WALL_START &&
8262 element <= EL_DF_STEEL_WALL_END ?
8265 element >= EL_DF_WOODEN_WALL_START &&
8266 element <= EL_DF_WOODEN_WALL_END ?
8272 int map_mm_wall_element_editor(int element)
8276 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8277 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8278 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8279 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8280 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8281 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8283 default: return element;
8287 int get_next_element(int element)
8291 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8292 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8293 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8294 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8295 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8296 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8297 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8298 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8299 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8300 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8301 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8303 default: return element;
8307 int el2img_mm(int element_mm)
8309 return el2img(map_element_MM_to_RND(element_mm));
8312 int el_act_dir2img(int element, int action, int direction)
8314 element = GFX_ELEMENT(element);
8315 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8317 // direction_graphic[][] == graphic[] for undefined direction graphics
8318 return element_info[element].direction_graphic[action][direction];
8321 static int el_act_dir2crm(int element, int action, int direction)
8323 element = GFX_ELEMENT(element);
8324 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8326 // direction_graphic[][] == graphic[] for undefined direction graphics
8327 return element_info[element].direction_crumbled[action][direction];
8330 int el_act2img(int element, int action)
8332 element = GFX_ELEMENT(element);
8334 return element_info[element].graphic[action];
8337 int el_act2crm(int element, int action)
8339 element = GFX_ELEMENT(element);
8341 return element_info[element].crumbled[action];
8344 int el_dir2img(int element, int direction)
8346 element = GFX_ELEMENT(element);
8348 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8351 int el2baseimg(int element)
8353 return element_info[element].graphic[ACTION_DEFAULT];
8356 int el2img(int element)
8358 element = GFX_ELEMENT(element);
8360 return element_info[element].graphic[ACTION_DEFAULT];
8363 int el2edimg(int element)
8365 element = GFX_ELEMENT(element);
8367 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8370 int el2preimg(int element)
8372 element = GFX_ELEMENT(element);
8374 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8377 int el2panelimg(int element)
8379 element = GFX_ELEMENT(element);
8381 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8384 int font2baseimg(int font_nr)
8386 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8389 int getBeltNrFromBeltElement(int element)
8391 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8392 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8393 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8396 int getBeltNrFromBeltActiveElement(int element)
8398 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8399 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8400 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8403 int getBeltNrFromBeltSwitchElement(int element)
8405 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8406 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8407 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8410 int getBeltDirNrFromBeltElement(int element)
8412 static int belt_base_element[4] =
8414 EL_CONVEYOR_BELT_1_LEFT,
8415 EL_CONVEYOR_BELT_2_LEFT,
8416 EL_CONVEYOR_BELT_3_LEFT,
8417 EL_CONVEYOR_BELT_4_LEFT
8420 int belt_nr = getBeltNrFromBeltElement(element);
8421 int belt_dir_nr = element - belt_base_element[belt_nr];
8423 return (belt_dir_nr % 3);
8426 int getBeltDirNrFromBeltSwitchElement(int element)
8428 static int belt_base_element[4] =
8430 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8431 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8432 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8433 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8436 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8437 int belt_dir_nr = element - belt_base_element[belt_nr];
8439 return (belt_dir_nr % 3);
8442 int getBeltDirFromBeltElement(int element)
8444 static int belt_move_dir[3] =
8451 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8453 return belt_move_dir[belt_dir_nr];
8456 int getBeltDirFromBeltSwitchElement(int element)
8458 static int belt_move_dir[3] =
8465 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8467 return belt_move_dir[belt_dir_nr];
8470 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8472 static int belt_base_element[4] =
8474 EL_CONVEYOR_BELT_1_LEFT,
8475 EL_CONVEYOR_BELT_2_LEFT,
8476 EL_CONVEYOR_BELT_3_LEFT,
8477 EL_CONVEYOR_BELT_4_LEFT
8480 return belt_base_element[belt_nr] + belt_dir_nr;
8483 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8485 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8487 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8490 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8492 static int belt_base_element[4] =
8494 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8495 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8496 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8497 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8500 return belt_base_element[belt_nr] + belt_dir_nr;
8503 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8505 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8507 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8510 boolean swapTiles_EM(boolean is_pre_emc_cave)
8512 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8515 boolean getTeamMode_EM(void)
8517 return game.team_mode || network_playing;
8520 boolean isActivePlayer_EM(int player_nr)
8522 return stored_player[player_nr].active;
8525 unsigned int InitRND(int seed)
8527 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8528 return InitEngineRandom_EM(seed);
8529 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8530 return InitEngineRandom_SP(seed);
8531 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8532 return InitEngineRandom_MM(seed);
8534 return InitEngineRandom_RND(seed);
8537 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8538 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8540 static int get_effective_element_EM(int tile, int frame_em)
8542 int element = object_mapping[tile].element_rnd;
8543 int action = object_mapping[tile].action;
8544 boolean is_backside = object_mapping[tile].is_backside;
8545 boolean action_removing = (action == ACTION_DIGGING ||
8546 action == ACTION_SNAPPING ||
8547 action == ACTION_COLLECTING);
8555 return (frame_em > 5 ? EL_EMPTY : element);
8561 else // frame_em == 7
8572 case Ydiamond_stone:
8576 case Xdrip_stretchB:
8592 case Ymagnify_blank:
8595 case Xsand_stonein_1:
8596 case Xsand_stonein_2:
8597 case Xsand_stonein_3:
8598 case Xsand_stonein_4:
8602 return (is_backside || action_removing ? EL_EMPTY : element);
8607 static boolean check_linear_animation_EM(int tile)
8611 case Xsand_stonesand_1:
8612 case Xsand_stonesand_quickout_1:
8613 case Xsand_sandstone_1:
8614 case Xsand_stonein_1:
8615 case Xsand_stoneout_1:
8643 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8644 boolean has_crumbled_graphics,
8645 int crumbled, int sync_frame)
8647 // if element can be crumbled, but certain action graphics are just empty
8648 // space (like instantly snapping sand to empty space in 1 frame), do not
8649 // treat these empty space graphics as crumbled graphics in EMC engine
8650 if (crumbled == IMG_EMPTY_SPACE)
8651 has_crumbled_graphics = FALSE;
8653 if (has_crumbled_graphics)
8655 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8656 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8657 g_crumbled->anim_delay,
8658 g_crumbled->anim_mode,
8659 g_crumbled->anim_start_frame,
8662 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8663 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8665 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8666 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8668 g_em->has_crumbled_graphics = TRUE;
8672 g_em->crumbled_bitmap = NULL;
8673 g_em->crumbled_src_x = 0;
8674 g_em->crumbled_src_y = 0;
8675 g_em->crumbled_border_size = 0;
8676 g_em->crumbled_tile_size = 0;
8678 g_em->has_crumbled_graphics = FALSE;
8683 void ResetGfxAnimation_EM(int x, int y, int tile)
8689 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8690 int tile, int frame_em, int x, int y)
8692 int action = object_mapping[tile].action;
8693 int direction = object_mapping[tile].direction;
8694 int effective_element = get_effective_element_EM(tile, frame_em);
8695 int graphic = (direction == MV_NONE ?
8696 el_act2img(effective_element, action) :
8697 el_act_dir2img(effective_element, action, direction));
8698 struct GraphicInfo *g = &graphic_info[graphic];
8700 boolean action_removing = (action == ACTION_DIGGING ||
8701 action == ACTION_SNAPPING ||
8702 action == ACTION_COLLECTING);
8703 boolean action_moving = (action == ACTION_FALLING ||
8704 action == ACTION_MOVING ||
8705 action == ACTION_PUSHING ||
8706 action == ACTION_EATING ||
8707 action == ACTION_FILLING ||
8708 action == ACTION_EMPTYING);
8709 boolean action_falling = (action == ACTION_FALLING ||
8710 action == ACTION_FILLING ||
8711 action == ACTION_EMPTYING);
8713 // special case: graphic uses "2nd movement tile" and has defined
8714 // 7 frames for movement animation (or less) => use default graphic
8715 // for last (8th) frame which ends the movement animation
8716 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8718 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8719 graphic = (direction == MV_NONE ?
8720 el_act2img(effective_element, action) :
8721 el_act_dir2img(effective_element, action, direction));
8723 g = &graphic_info[graphic];
8726 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8730 else if (action_moving)
8732 boolean is_backside = object_mapping[tile].is_backside;
8736 int direction = object_mapping[tile].direction;
8737 int move_dir = (action_falling ? MV_DOWN : direction);
8742 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8743 if (g->double_movement && frame_em == 0)
8747 if (move_dir == MV_LEFT)
8748 GfxFrame[x - 1][y] = GfxFrame[x][y];
8749 else if (move_dir == MV_RIGHT)
8750 GfxFrame[x + 1][y] = GfxFrame[x][y];
8751 else if (move_dir == MV_UP)
8752 GfxFrame[x][y - 1] = GfxFrame[x][y];
8753 else if (move_dir == MV_DOWN)
8754 GfxFrame[x][y + 1] = GfxFrame[x][y];
8761 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8762 if (tile == Xsand_stonesand_quickout_1 ||
8763 tile == Xsand_stonesand_quickout_2)
8767 if (graphic_info[graphic].anim_global_sync)
8768 sync_frame = FrameCounter;
8769 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8770 sync_frame = GfxFrame[x][y];
8772 sync_frame = 0; // playfield border (pseudo steel)
8774 SetRandomAnimationValue(x, y);
8776 int frame = getAnimationFrame(g->anim_frames,
8779 g->anim_start_frame,
8782 g_em->unique_identifier =
8783 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8786 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8787 int tile, int frame_em, int x, int y)
8789 int action = object_mapping[tile].action;
8790 int direction = object_mapping[tile].direction;
8791 boolean is_backside = object_mapping[tile].is_backside;
8792 int effective_element = get_effective_element_EM(tile, frame_em);
8793 int effective_action = action;
8794 int graphic = (direction == MV_NONE ?
8795 el_act2img(effective_element, effective_action) :
8796 el_act_dir2img(effective_element, effective_action,
8798 int crumbled = (direction == MV_NONE ?
8799 el_act2crm(effective_element, effective_action) :
8800 el_act_dir2crm(effective_element, effective_action,
8802 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8803 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8804 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8805 struct GraphicInfo *g = &graphic_info[graphic];
8808 // special case: graphic uses "2nd movement tile" and has defined
8809 // 7 frames for movement animation (or less) => use default graphic
8810 // for last (8th) frame which ends the movement animation
8811 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8813 effective_action = ACTION_DEFAULT;
8814 graphic = (direction == MV_NONE ?
8815 el_act2img(effective_element, effective_action) :
8816 el_act_dir2img(effective_element, effective_action,
8818 crumbled = (direction == MV_NONE ?
8819 el_act2crm(effective_element, effective_action) :
8820 el_act_dir2crm(effective_element, effective_action,
8823 g = &graphic_info[graphic];
8826 if (graphic_info[graphic].anim_global_sync)
8827 sync_frame = FrameCounter;
8828 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8829 sync_frame = GfxFrame[x][y];
8831 sync_frame = 0; // playfield border (pseudo steel)
8833 SetRandomAnimationValue(x, y);
8835 int frame = getAnimationFrame(g->anim_frames,
8838 g->anim_start_frame,
8841 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8842 g->double_movement && is_backside);
8844 // (updating the "crumbled" graphic definitions is probably not really needed,
8845 // as animations for crumbled graphics can't be longer than one EMC cycle)
8846 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8850 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8851 int player_nr, int anim, int frame_em)
8853 int element = player_mapping[player_nr][anim].element_rnd;
8854 int action = player_mapping[player_nr][anim].action;
8855 int direction = player_mapping[player_nr][anim].direction;
8856 int graphic = (direction == MV_NONE ?
8857 el_act2img(element, action) :
8858 el_act_dir2img(element, action, direction));
8859 struct GraphicInfo *g = &graphic_info[graphic];
8862 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8864 stored_player[player_nr].StepFrame = frame_em;
8866 sync_frame = stored_player[player_nr].Frame;
8868 int frame = getAnimationFrame(g->anim_frames,
8871 g->anim_start_frame,
8874 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8875 &g_em->src_x, &g_em->src_y, FALSE);
8878 void InitGraphicInfo_EM(void)
8882 // always start with reliable default values
8883 for (i = 0; i < GAME_TILE_MAX; i++)
8885 object_mapping[i].element_rnd = EL_UNKNOWN;
8886 object_mapping[i].is_backside = FALSE;
8887 object_mapping[i].action = ACTION_DEFAULT;
8888 object_mapping[i].direction = MV_NONE;
8891 // always start with reliable default values
8892 for (p = 0; p < MAX_PLAYERS; p++)
8894 for (i = 0; i < PLY_MAX; i++)
8896 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8897 player_mapping[p][i].action = ACTION_DEFAULT;
8898 player_mapping[p][i].direction = MV_NONE;
8902 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8904 int e = em_object_mapping_list[i].element_em;
8906 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8907 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8909 if (em_object_mapping_list[i].action != -1)
8910 object_mapping[e].action = em_object_mapping_list[i].action;
8912 if (em_object_mapping_list[i].direction != -1)
8913 object_mapping[e].direction =
8914 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8917 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8919 int a = em_player_mapping_list[i].action_em;
8920 int p = em_player_mapping_list[i].player_nr;
8922 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8924 if (em_player_mapping_list[i].action != -1)
8925 player_mapping[p][a].action = em_player_mapping_list[i].action;
8927 if (em_player_mapping_list[i].direction != -1)
8928 player_mapping[p][a].direction =
8929 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8932 for (i = 0; i < GAME_TILE_MAX; i++)
8934 int element = object_mapping[i].element_rnd;
8935 int action = object_mapping[i].action;
8936 int direction = object_mapping[i].direction;
8937 boolean is_backside = object_mapping[i].is_backside;
8938 boolean action_exploding = ((action == ACTION_EXPLODING ||
8939 action == ACTION_SMASHED_BY_ROCK ||
8940 action == ACTION_SMASHED_BY_SPRING) &&
8941 element != EL_DIAMOND);
8942 boolean action_active = (action == ACTION_ACTIVE);
8943 boolean action_other = (action == ACTION_OTHER);
8945 for (j = 0; j < 8; j++)
8947 int effective_element = get_effective_element_EM(i, j);
8948 int effective_action = (j < 7 ? action :
8949 i == Xdrip_stretch ? action :
8950 i == Xdrip_stretchB ? action :
8951 i == Ydrip_1_s ? action :
8952 i == Ydrip_1_sB ? action :
8953 i == Yball_1 ? action :
8954 i == Xball_2 ? action :
8955 i == Yball_2 ? action :
8956 i == Yball_blank ? action :
8957 i == Ykey_1_blank ? action :
8958 i == Ykey_2_blank ? action :
8959 i == Ykey_3_blank ? action :
8960 i == Ykey_4_blank ? action :
8961 i == Ykey_5_blank ? action :
8962 i == Ykey_6_blank ? action :
8963 i == Ykey_7_blank ? action :
8964 i == Ykey_8_blank ? action :
8965 i == Ylenses_blank ? action :
8966 i == Ymagnify_blank ? action :
8967 i == Ygrass_blank ? action :
8968 i == Ydirt_blank ? action :
8969 i == Xsand_stonein_1 ? action :
8970 i == Xsand_stonein_2 ? action :
8971 i == Xsand_stonein_3 ? action :
8972 i == Xsand_stonein_4 ? action :
8973 i == Xsand_stoneout_1 ? action :
8974 i == Xsand_stoneout_2 ? action :
8975 i == Xboom_android ? ACTION_EXPLODING :
8976 action_exploding ? ACTION_EXPLODING :
8977 action_active ? action :
8978 action_other ? action :
8980 int graphic = (el_act_dir2img(effective_element, effective_action,
8982 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8984 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8985 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8986 boolean has_action_graphics = (graphic != base_graphic);
8987 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8988 struct GraphicInfo *g = &graphic_info[graphic];
8989 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8992 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8993 boolean special_animation = (action != ACTION_DEFAULT &&
8994 g->anim_frames == 3 &&
8995 g->anim_delay == 2 &&
8996 g->anim_mode & ANIM_LINEAR);
8997 int sync_frame = (i == Xdrip_stretch ? 7 :
8998 i == Xdrip_stretchB ? 7 :
8999 i == Ydrip_2_s ? j + 8 :
9000 i == Ydrip_2_sB ? j + 8 :
9009 i == Xfake_acid_1 ? 0 :
9010 i == Xfake_acid_2 ? 10 :
9011 i == Xfake_acid_3 ? 20 :
9012 i == Xfake_acid_4 ? 30 :
9013 i == Xfake_acid_5 ? 40 :
9014 i == Xfake_acid_6 ? 50 :
9015 i == Xfake_acid_7 ? 60 :
9016 i == Xfake_acid_8 ? 70 :
9017 i == Xfake_acid_1_player ? 0 :
9018 i == Xfake_acid_2_player ? 10 :
9019 i == Xfake_acid_3_player ? 20 :
9020 i == Xfake_acid_4_player ? 30 :
9021 i == Xfake_acid_5_player ? 40 :
9022 i == Xfake_acid_6_player ? 50 :
9023 i == Xfake_acid_7_player ? 60 :
9024 i == Xfake_acid_8_player ? 70 :
9026 i == Yball_2 ? j + 8 :
9027 i == Yball_blank ? j + 1 :
9028 i == Ykey_1_blank ? j + 1 :
9029 i == Ykey_2_blank ? j + 1 :
9030 i == Ykey_3_blank ? j + 1 :
9031 i == Ykey_4_blank ? j + 1 :
9032 i == Ykey_5_blank ? j + 1 :
9033 i == Ykey_6_blank ? j + 1 :
9034 i == Ykey_7_blank ? j + 1 :
9035 i == Ykey_8_blank ? j + 1 :
9036 i == Ylenses_blank ? j + 1 :
9037 i == Ymagnify_blank ? j + 1 :
9038 i == Ygrass_blank ? j + 1 :
9039 i == Ydirt_blank ? j + 1 :
9040 i == Xamoeba_1 ? 0 :
9041 i == Xamoeba_2 ? 1 :
9042 i == Xamoeba_3 ? 2 :
9043 i == Xamoeba_4 ? 3 :
9044 i == Xamoeba_5 ? 0 :
9045 i == Xamoeba_6 ? 1 :
9046 i == Xamoeba_7 ? 2 :
9047 i == Xamoeba_8 ? 3 :
9048 i == Xexit_2 ? j + 8 :
9049 i == Xexit_3 ? j + 16 :
9050 i == Xdynamite_1 ? 0 :
9051 i == Xdynamite_2 ? 8 :
9052 i == Xdynamite_3 ? 16 :
9053 i == Xdynamite_4 ? 24 :
9054 i == Xsand_stonein_1 ? j + 1 :
9055 i == Xsand_stonein_2 ? j + 9 :
9056 i == Xsand_stonein_3 ? j + 17 :
9057 i == Xsand_stonein_4 ? j + 25 :
9058 i == Xsand_stoneout_1 && j == 0 ? 0 :
9059 i == Xsand_stoneout_1 && j == 1 ? 0 :
9060 i == Xsand_stoneout_1 && j == 2 ? 1 :
9061 i == Xsand_stoneout_1 && j == 3 ? 2 :
9062 i == Xsand_stoneout_1 && j == 4 ? 2 :
9063 i == Xsand_stoneout_1 && j == 5 ? 3 :
9064 i == Xsand_stoneout_1 && j == 6 ? 4 :
9065 i == Xsand_stoneout_1 && j == 7 ? 4 :
9066 i == Xsand_stoneout_2 && j == 0 ? 5 :
9067 i == Xsand_stoneout_2 && j == 1 ? 6 :
9068 i == Xsand_stoneout_2 && j == 2 ? 7 :
9069 i == Xsand_stoneout_2 && j == 3 ? 8 :
9070 i == Xsand_stoneout_2 && j == 4 ? 9 :
9071 i == Xsand_stoneout_2 && j == 5 ? 11 :
9072 i == Xsand_stoneout_2 && j == 6 ? 13 :
9073 i == Xsand_stoneout_2 && j == 7 ? 15 :
9074 i == Xboom_bug && j == 1 ? 2 :
9075 i == Xboom_bug && j == 2 ? 2 :
9076 i == Xboom_bug && j == 3 ? 4 :
9077 i == Xboom_bug && j == 4 ? 4 :
9078 i == Xboom_bug && j == 5 ? 2 :
9079 i == Xboom_bug && j == 6 ? 2 :
9080 i == Xboom_bug && j == 7 ? 0 :
9081 i == Xboom_tank && j == 1 ? 2 :
9082 i == Xboom_tank && j == 2 ? 2 :
9083 i == Xboom_tank && j == 3 ? 4 :
9084 i == Xboom_tank && j == 4 ? 4 :
9085 i == Xboom_tank && j == 5 ? 2 :
9086 i == Xboom_tank && j == 6 ? 2 :
9087 i == Xboom_tank && j == 7 ? 0 :
9088 i == Xboom_android && j == 7 ? 6 :
9089 i == Xboom_1 && j == 1 ? 2 :
9090 i == Xboom_1 && j == 2 ? 2 :
9091 i == Xboom_1 && j == 3 ? 4 :
9092 i == Xboom_1 && j == 4 ? 4 :
9093 i == Xboom_1 && j == 5 ? 6 :
9094 i == Xboom_1 && j == 6 ? 6 :
9095 i == Xboom_1 && j == 7 ? 8 :
9096 i == Xboom_2 && j == 0 ? 8 :
9097 i == Xboom_2 && j == 1 ? 8 :
9098 i == Xboom_2 && j == 2 ? 10 :
9099 i == Xboom_2 && j == 3 ? 10 :
9100 i == Xboom_2 && j == 4 ? 10 :
9101 i == Xboom_2 && j == 5 ? 12 :
9102 i == Xboom_2 && j == 6 ? 12 :
9103 i == Xboom_2 && j == 7 ? 12 :
9104 special_animation && j == 4 ? 3 :
9105 effective_action != action ? 0 :
9107 int frame = getAnimationFrame(g->anim_frames,
9110 g->anim_start_frame,
9113 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9114 g->double_movement && is_backside);
9116 g_em->bitmap = src_bitmap;
9117 g_em->src_x = src_x;
9118 g_em->src_y = src_y;
9119 g_em->src_offset_x = 0;
9120 g_em->src_offset_y = 0;
9121 g_em->dst_offset_x = 0;
9122 g_em->dst_offset_y = 0;
9123 g_em->width = TILEX;
9124 g_em->height = TILEY;
9126 g_em->preserve_background = FALSE;
9128 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9131 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9132 effective_action == ACTION_MOVING ||
9133 effective_action == ACTION_PUSHING ||
9134 effective_action == ACTION_EATING)) ||
9135 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9136 effective_action == ACTION_EMPTYING)))
9139 (effective_action == ACTION_FALLING ||
9140 effective_action == ACTION_FILLING ||
9141 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9142 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9143 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9144 int num_steps = (i == Ydrip_1_s ? 16 :
9145 i == Ydrip_1_sB ? 16 :
9146 i == Ydrip_2_s ? 16 :
9147 i == Ydrip_2_sB ? 16 :
9148 i == Xsand_stonein_1 ? 32 :
9149 i == Xsand_stonein_2 ? 32 :
9150 i == Xsand_stonein_3 ? 32 :
9151 i == Xsand_stonein_4 ? 32 :
9152 i == Xsand_stoneout_1 ? 16 :
9153 i == Xsand_stoneout_2 ? 16 : 8);
9154 int cx = ABS(dx) * (TILEX / num_steps);
9155 int cy = ABS(dy) * (TILEY / num_steps);
9156 int step_frame = (i == Ydrip_2_s ? j + 8 :
9157 i == Ydrip_2_sB ? j + 8 :
9158 i == Xsand_stonein_2 ? j + 8 :
9159 i == Xsand_stonein_3 ? j + 16 :
9160 i == Xsand_stonein_4 ? j + 24 :
9161 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9162 int step = (is_backside ? step_frame : num_steps - step_frame);
9164 if (is_backside) // tile where movement starts
9166 if (dx < 0 || dy < 0)
9168 g_em->src_offset_x = cx * step;
9169 g_em->src_offset_y = cy * step;
9173 g_em->dst_offset_x = cx * step;
9174 g_em->dst_offset_y = cy * step;
9177 else // tile where movement ends
9179 if (dx < 0 || dy < 0)
9181 g_em->dst_offset_x = cx * step;
9182 g_em->dst_offset_y = cy * step;
9186 g_em->src_offset_x = cx * step;
9187 g_em->src_offset_y = cy * step;
9191 g_em->width = TILEX - cx * step;
9192 g_em->height = TILEY - cy * step;
9195 // create unique graphic identifier to decide if tile must be redrawn
9196 /* bit 31 - 16 (16 bit): EM style graphic
9197 bit 15 - 12 ( 4 bit): EM style frame
9198 bit 11 - 6 ( 6 bit): graphic width
9199 bit 5 - 0 ( 6 bit): graphic height */
9200 g_em->unique_identifier =
9201 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9205 for (i = 0; i < GAME_TILE_MAX; i++)
9207 for (j = 0; j < 8; j++)
9209 int element = object_mapping[i].element_rnd;
9210 int action = object_mapping[i].action;
9211 int direction = object_mapping[i].direction;
9212 boolean is_backside = object_mapping[i].is_backside;
9213 int graphic_action = el_act_dir2img(element, action, direction);
9214 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9216 if ((action == ACTION_SMASHED_BY_ROCK ||
9217 action == ACTION_SMASHED_BY_SPRING ||
9218 action == ACTION_EATING) &&
9219 graphic_action == graphic_default)
9221 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9222 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9223 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9224 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9227 // no separate animation for "smashed by rock" -- use rock instead
9228 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9229 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9231 g_em->bitmap = g_xx->bitmap;
9232 g_em->src_x = g_xx->src_x;
9233 g_em->src_y = g_xx->src_y;
9234 g_em->src_offset_x = g_xx->src_offset_x;
9235 g_em->src_offset_y = g_xx->src_offset_y;
9236 g_em->dst_offset_x = g_xx->dst_offset_x;
9237 g_em->dst_offset_y = g_xx->dst_offset_y;
9238 g_em->width = g_xx->width;
9239 g_em->height = g_xx->height;
9240 g_em->unique_identifier = g_xx->unique_identifier;
9243 g_em->preserve_background = TRUE;
9248 for (p = 0; p < MAX_PLAYERS; p++)
9250 for (i = 0; i < PLY_MAX; i++)
9252 int element = player_mapping[p][i].element_rnd;
9253 int action = player_mapping[p][i].action;
9254 int direction = player_mapping[p][i].direction;
9256 for (j = 0; j < 8; j++)
9258 int effective_element = element;
9259 int effective_action = action;
9260 int graphic = (direction == MV_NONE ?
9261 el_act2img(effective_element, effective_action) :
9262 el_act_dir2img(effective_element, effective_action,
9264 struct GraphicInfo *g = &graphic_info[graphic];
9265 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9269 int frame = getAnimationFrame(g->anim_frames,
9272 g->anim_start_frame,
9275 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9277 g_em->bitmap = src_bitmap;
9278 g_em->src_x = src_x;
9279 g_em->src_y = src_y;
9280 g_em->src_offset_x = 0;
9281 g_em->src_offset_y = 0;
9282 g_em->dst_offset_x = 0;
9283 g_em->dst_offset_y = 0;
9284 g_em->width = TILEX;
9285 g_em->height = TILEY;
9291 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9292 boolean any_player_moving,
9293 boolean any_player_snapping,
9294 boolean any_player_dropping)
9296 if (frame == 7 && !any_player_dropping)
9298 if (!local_player->was_waiting)
9300 if (!CheckSaveEngineSnapshotToList())
9303 local_player->was_waiting = TRUE;
9306 else if (any_player_moving || any_player_snapping || any_player_dropping)
9308 local_player->was_waiting = FALSE;
9312 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9313 boolean murphy_is_dropping)
9315 if (murphy_is_waiting)
9317 if (!local_player->was_waiting)
9319 if (!CheckSaveEngineSnapshotToList())
9322 local_player->was_waiting = TRUE;
9327 local_player->was_waiting = FALSE;
9331 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9332 boolean button_released)
9334 if (button_released)
9336 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9337 CheckSaveEngineSnapshotToList();
9339 else if (element_clicked)
9341 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9342 CheckSaveEngineSnapshotToList();
9344 game.snapshot.changed_action = TRUE;
9348 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9349 boolean any_player_moving,
9350 boolean any_player_snapping,
9351 boolean any_player_dropping)
9353 if (tape.single_step && tape.recording && !tape.pausing)
9354 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9355 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9357 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9358 any_player_snapping, any_player_dropping);
9360 return tape.pausing;
9363 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9364 boolean murphy_is_dropping)
9366 boolean murphy_starts_dropping = FALSE;
9369 for (i = 0; i < MAX_PLAYERS; i++)
9370 if (stored_player[i].force_dropping)
9371 murphy_starts_dropping = TRUE;
9373 if (tape.single_step && tape.recording && !tape.pausing)
9374 if (murphy_is_waiting && !murphy_starts_dropping)
9375 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9377 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9380 void CheckSingleStepMode_MM(boolean element_clicked,
9381 boolean button_released)
9383 if (tape.single_step && tape.recording && !tape.pausing)
9384 if (button_released)
9385 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9387 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9390 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9391 int graphic, int sync_frame, int x, int y)
9393 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9395 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9398 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9400 return (IS_NEXT_FRAME(sync_frame, graphic));
9403 int getGraphicInfo_Delay(int graphic)
9405 return graphic_info[graphic].anim_delay;
9408 void PlayMenuSoundExt(int sound)
9410 if (sound == SND_UNDEFINED)
9413 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9414 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9417 if (IS_LOOP_SOUND(sound))
9418 PlaySoundLoop(sound);
9423 void PlayMenuSound(void)
9425 PlayMenuSoundExt(menu.sound[game_status]);
9428 void PlayMenuSoundStereo(int sound, int stereo_position)
9430 if (sound == SND_UNDEFINED)
9433 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9434 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9437 if (IS_LOOP_SOUND(sound))
9438 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9440 PlaySoundStereo(sound, stereo_position);
9443 void PlayMenuSoundIfLoopExt(int sound)
9445 if (sound == SND_UNDEFINED)
9448 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9449 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9452 if (IS_LOOP_SOUND(sound))
9453 PlaySoundLoop(sound);
9456 void PlayMenuSoundIfLoop(void)
9458 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9461 void PlayMenuMusicExt(int music)
9463 if (music == MUS_UNDEFINED)
9466 if (!setup.sound_music)
9469 if (IS_LOOP_MUSIC(music))
9470 PlayMusicLoop(music);
9475 void PlayMenuMusic(void)
9477 char *curr_music = getCurrentlyPlayingMusicFilename();
9478 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9480 if (!strEqual(curr_music, next_music))
9481 PlayMenuMusicExt(menu.music[game_status]);
9484 void PlayMenuSoundsAndMusic(void)
9490 static void FadeMenuSounds(void)
9495 static void FadeMenuMusic(void)
9497 char *curr_music = getCurrentlyPlayingMusicFilename();
9498 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9500 if (!strEqual(curr_music, next_music))
9504 void FadeMenuSoundsAndMusic(void)
9510 void PlaySoundActivating(void)
9513 PlaySound(SND_MENU_ITEM_ACTIVATING);
9517 void PlaySoundSelecting(void)
9520 PlaySound(SND_MENU_ITEM_SELECTING);
9524 void ToggleFullscreenIfNeeded(void)
9526 // if setup and video fullscreen state are already matching, nothing do do
9527 if (setup.fullscreen == video.fullscreen_enabled ||
9528 !video.fullscreen_available)
9531 SDLSetWindowFullscreen(setup.fullscreen);
9533 // set setup value according to successfully changed fullscreen mode
9534 setup.fullscreen = video.fullscreen_enabled;
9537 void ChangeWindowScalingIfNeeded(void)
9539 // if setup and video window scaling are already matching, nothing do do
9540 if (setup.window_scaling_percent == video.window_scaling_percent ||
9541 video.fullscreen_enabled)
9544 SDLSetWindowScaling(setup.window_scaling_percent);
9546 // set setup value according to successfully changed window scaling
9547 setup.window_scaling_percent = video.window_scaling_percent;
9550 void ChangeVsyncModeIfNeeded(void)
9552 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9553 int video_vsync_mode = video.vsync_mode;
9555 // if setup and video vsync mode are already matching, nothing do do
9556 if (setup_vsync_mode == video_vsync_mode)
9559 // if renderer is using OpenGL, vsync mode can directly be changed
9560 SDLSetScreenVsyncMode(setup.vsync_mode);
9562 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9563 if (video.vsync_mode == video_vsync_mode)
9565 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9567 // save backbuffer content which gets lost when re-creating screen
9568 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9570 // force re-creating screen and renderer to set new vsync mode
9571 video.fullscreen_enabled = !setup.fullscreen;
9573 // when creating new renderer, destroy textures linked to old renderer
9574 FreeAllImageTextures(); // needs old renderer to free the textures
9576 // re-create screen and renderer (including change of vsync mode)
9577 ChangeVideoModeIfNeeded(setup.fullscreen);
9579 // set setup value according to successfully changed fullscreen mode
9580 setup.fullscreen = video.fullscreen_enabled;
9582 // restore backbuffer content from temporary backbuffer backup bitmap
9583 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9584 FreeBitmap(tmp_backbuffer);
9586 // update visible window/screen
9587 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9589 // when changing vsync mode, re-create textures for new renderer
9590 InitImageTextures();
9593 // set setup value according to successfully changed vsync mode
9594 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9597 static void JoinRectangles(int *x, int *y, int *width, int *height,
9598 int x2, int y2, int width2, int height2)
9600 // do not join with "off-screen" rectangle
9601 if (x2 == -1 || y2 == -1)
9606 *width = MAX(*width, width2);
9607 *height = MAX(*height, height2);
9610 void SetAnimStatus(int anim_status_new)
9612 if (anim_status_new == GAME_MODE_MAIN)
9613 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9614 else if (anim_status_new == GAME_MODE_NAMES)
9615 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9616 else if (anim_status_new == GAME_MODE_SCORES)
9617 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9619 global.anim_status_next = anim_status_new;
9621 // directly set screen modes that are entered without fading
9622 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9623 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9624 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9625 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9626 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9627 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9628 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9629 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9630 global.anim_status = global.anim_status_next;
9633 void SetGameStatus(int game_status_new)
9635 if (game_status_new != game_status)
9636 game_status_last_screen = game_status;
9638 game_status = game_status_new;
9640 SetAnimStatus(game_status_new);
9643 void SetFontStatus(int game_status_new)
9645 static int last_game_status = -1;
9647 if (game_status_new != -1)
9649 // set game status for font use after storing last game status
9650 last_game_status = game_status;
9651 game_status = game_status_new;
9655 // reset game status after font use from last stored game status
9656 game_status = last_game_status;
9660 void ResetFontStatus(void)
9665 void SetLevelSetInfo(char *identifier, int level_nr)
9667 setString(&levelset.identifier, identifier);
9669 levelset.level_nr = level_nr;
9672 boolean CheckIfAllViewportsHaveChanged(void)
9674 // if game status has not changed, viewports have not changed either
9675 if (game_status == game_status_last)
9678 // check if all viewports have changed with current game status
9680 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9681 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9682 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9683 int new_real_sx = vp_playfield->x;
9684 int new_real_sy = vp_playfield->y;
9685 int new_full_sxsize = vp_playfield->width;
9686 int new_full_sysize = vp_playfield->height;
9687 int new_dx = vp_door_1->x;
9688 int new_dy = vp_door_1->y;
9689 int new_dxsize = vp_door_1->width;
9690 int new_dysize = vp_door_1->height;
9691 int new_vx = vp_door_2->x;
9692 int new_vy = vp_door_2->y;
9693 int new_vxsize = vp_door_2->width;
9694 int new_vysize = vp_door_2->height;
9696 boolean playfield_viewport_has_changed =
9697 (new_real_sx != REAL_SX ||
9698 new_real_sy != REAL_SY ||
9699 new_full_sxsize != FULL_SXSIZE ||
9700 new_full_sysize != FULL_SYSIZE);
9702 boolean door_1_viewport_has_changed =
9705 new_dxsize != DXSIZE ||
9706 new_dysize != DYSIZE);
9708 boolean door_2_viewport_has_changed =
9711 new_vxsize != VXSIZE ||
9712 new_vysize != VYSIZE ||
9713 game_status_last == GAME_MODE_EDITOR);
9715 return (playfield_viewport_has_changed &&
9716 door_1_viewport_has_changed &&
9717 door_2_viewport_has_changed);
9720 boolean CheckFadeAll(void)
9722 return (CheckIfGlobalBorderHasChanged() ||
9723 CheckIfAllViewportsHaveChanged());
9726 void ChangeViewportPropertiesIfNeeded(void)
9728 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9729 FALSE : setup.small_game_graphics);
9730 int gfx_game_mode = game_status;
9731 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9733 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9734 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9735 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9736 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9737 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9738 int new_win_xsize = vp_window->width;
9739 int new_win_ysize = vp_window->height;
9740 int border_left = vp_playfield->border_left;
9741 int border_right = vp_playfield->border_right;
9742 int border_top = vp_playfield->border_top;
9743 int border_bottom = vp_playfield->border_bottom;
9744 int new_sx = vp_playfield->x + border_left;
9745 int new_sy = vp_playfield->y + border_top;
9746 int new_sxsize = vp_playfield->width - border_left - border_right;
9747 int new_sysize = vp_playfield->height - border_top - border_bottom;
9748 int new_real_sx = vp_playfield->x;
9749 int new_real_sy = vp_playfield->y;
9750 int new_full_sxsize = vp_playfield->width;
9751 int new_full_sysize = vp_playfield->height;
9752 int new_dx = vp_door_1->x;
9753 int new_dy = vp_door_1->y;
9754 int new_dxsize = vp_door_1->width;
9755 int new_dysize = vp_door_1->height;
9756 int new_vx = vp_door_2->x;
9757 int new_vy = vp_door_2->y;
9758 int new_vxsize = vp_door_2->width;
9759 int new_vysize = vp_door_2->height;
9760 int new_ex = vp_door_3->x;
9761 int new_ey = vp_door_3->y;
9762 int new_exsize = vp_door_3->width;
9763 int new_eysize = vp_door_3->height;
9764 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9765 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9766 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9767 int new_scr_fieldx = new_sxsize / tilesize;
9768 int new_scr_fieldy = new_sysize / tilesize;
9769 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9770 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9771 boolean init_gfx_buffers = FALSE;
9772 boolean init_video_buffer = FALSE;
9773 boolean init_gadgets_and_anims = FALSE;
9774 boolean init_em_graphics = FALSE;
9776 if (new_win_xsize != WIN_XSIZE ||
9777 new_win_ysize != WIN_YSIZE)
9779 WIN_XSIZE = new_win_xsize;
9780 WIN_YSIZE = new_win_ysize;
9782 init_video_buffer = TRUE;
9783 init_gfx_buffers = TRUE;
9784 init_gadgets_and_anims = TRUE;
9786 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9789 if (new_scr_fieldx != SCR_FIELDX ||
9790 new_scr_fieldy != SCR_FIELDY)
9792 // this always toggles between MAIN and GAME when using small tile size
9794 SCR_FIELDX = new_scr_fieldx;
9795 SCR_FIELDY = new_scr_fieldy;
9797 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9808 new_sxsize != SXSIZE ||
9809 new_sysize != SYSIZE ||
9810 new_dxsize != DXSIZE ||
9811 new_dysize != DYSIZE ||
9812 new_vxsize != VXSIZE ||
9813 new_vysize != VYSIZE ||
9814 new_exsize != EXSIZE ||
9815 new_eysize != EYSIZE ||
9816 new_real_sx != REAL_SX ||
9817 new_real_sy != REAL_SY ||
9818 new_full_sxsize != FULL_SXSIZE ||
9819 new_full_sysize != FULL_SYSIZE ||
9820 new_tilesize_var != TILESIZE_VAR
9823 // ------------------------------------------------------------------------
9824 // determine next fading area for changed viewport definitions
9825 // ------------------------------------------------------------------------
9827 // start with current playfield area (default fading area)
9830 FADE_SXSIZE = FULL_SXSIZE;
9831 FADE_SYSIZE = FULL_SYSIZE;
9833 // add new playfield area if position or size has changed
9834 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9835 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9837 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9838 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9841 // add current and new door 1 area if position or size has changed
9842 if (new_dx != DX || new_dy != DY ||
9843 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9845 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9846 DX, DY, DXSIZE, DYSIZE);
9847 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9848 new_dx, new_dy, new_dxsize, new_dysize);
9851 // add current and new door 2 area if position or size has changed
9852 if (new_vx != VX || new_vy != VY ||
9853 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9855 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9856 VX, VY, VXSIZE, VYSIZE);
9857 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9858 new_vx, new_vy, new_vxsize, new_vysize);
9861 // ------------------------------------------------------------------------
9862 // handle changed tile size
9863 // ------------------------------------------------------------------------
9865 if (new_tilesize_var != TILESIZE_VAR)
9867 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9869 // changing tile size invalidates scroll values of engine snapshots
9870 FreeEngineSnapshotSingle();
9872 // changing tile size requires update of graphic mapping for EM engine
9873 init_em_graphics = TRUE;
9884 SXSIZE = new_sxsize;
9885 SYSIZE = new_sysize;
9886 DXSIZE = new_dxsize;
9887 DYSIZE = new_dysize;
9888 VXSIZE = new_vxsize;
9889 VYSIZE = new_vysize;
9890 EXSIZE = new_exsize;
9891 EYSIZE = new_eysize;
9892 REAL_SX = new_real_sx;
9893 REAL_SY = new_real_sy;
9894 FULL_SXSIZE = new_full_sxsize;
9895 FULL_SYSIZE = new_full_sysize;
9896 TILESIZE_VAR = new_tilesize_var;
9898 init_gfx_buffers = TRUE;
9899 init_gadgets_and_anims = TRUE;
9901 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9902 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9905 if (init_gfx_buffers)
9907 // Debug("tools:viewport", "init_gfx_buffers");
9909 SCR_FIELDX = new_scr_fieldx_buffers;
9910 SCR_FIELDY = new_scr_fieldy_buffers;
9914 SCR_FIELDX = new_scr_fieldx;
9915 SCR_FIELDY = new_scr_fieldy;
9917 SetDrawDeactivationMask(REDRAW_NONE);
9918 SetDrawBackgroundMask(REDRAW_FIELD);
9921 if (init_video_buffer)
9923 // Debug("tools:viewport", "init_video_buffer");
9925 FreeAllImageTextures(); // needs old renderer to free the textures
9927 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9928 InitImageTextures();
9931 if (init_gadgets_and_anims)
9933 // Debug("tools:viewport", "init_gadgets_and_anims");
9936 InitGlobalAnimations();
9939 if (init_em_graphics)
9941 InitGraphicInfo_EM();
9946 // ============================================================================
9948 // ============================================================================
9950 #if defined(PLATFORM_WIN32)
9951 /* FILETIME of Jan 1 1970 00:00:00. */
9952 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9955 * timezone information is stored outside the kernel so tzp isn't used anymore.
9957 * Note: this function is not for Win32 high precision timing purpose. See
9960 int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9963 SYSTEMTIME system_time;
9964 ULARGE_INTEGER ularge;
9966 GetSystemTime(&system_time);
9967 SystemTimeToFileTime(&system_time, &file_time);
9968 ularge.LowPart = file_time.dwLowDateTime;
9969 ularge.HighPart = file_time.dwHighDateTime;
9971 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9972 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9978 static char *test_init_uuid_random_function_simple(void)
9980 static char seed_text[100];
9981 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9983 sprintf(seed_text, "%d", seed);
9988 static char *test_init_uuid_random_function_better(void)
9990 static char seed_text[100];
9991 struct timeval current_time;
9993 gettimeofday(¤t_time, NULL);
9995 prng_seed_bytes(¤t_time, sizeof(current_time));
9997 sprintf(seed_text, "%ld.%ld",
9998 (long)current_time.tv_sec,
9999 (long)current_time.tv_usec);
10004 #if defined(PLATFORM_WIN32)
10005 static char *test_init_uuid_random_function_better_windows(void)
10007 static char seed_text[100];
10008 struct timeval current_time;
10010 gettimeofday_windows(¤t_time, NULL);
10012 prng_seed_bytes(¤t_time, sizeof(current_time));
10014 sprintf(seed_text, "%ld.%ld",
10015 (long)current_time.tv_sec,
10016 (long)current_time.tv_usec);
10022 static unsigned int test_uuid_random_function_simple(int max)
10024 return GetSimpleRandom(max);
10027 static unsigned int test_uuid_random_function_better(int max)
10029 return (max > 0 ? prng_get_uint() % max : 0);
10032 #if defined(PLATFORM_WIN32)
10033 #define NUM_UUID_TESTS 3
10035 #define NUM_UUID_TESTS 2
10038 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10040 struct hashtable *hash_seeds =
10041 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10042 struct hashtable *hash_uuids =
10043 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10044 static char message[100];
10047 char *random_name = (nr == 0 ? "simple" : "better");
10048 char *random_type = (always_seed ? "always" : "only once");
10049 char *(*init_random_function)(void) =
10051 test_init_uuid_random_function_simple :
10052 test_init_uuid_random_function_better);
10053 unsigned int (*random_function)(int) =
10055 test_uuid_random_function_simple :
10056 test_uuid_random_function_better);
10059 #if defined(PLATFORM_WIN32)
10062 random_name = "windows";
10063 init_random_function = test_init_uuid_random_function_better_windows;
10069 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10070 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10072 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10073 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10074 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10076 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10080 // always initialize random number generator at least once
10081 init_random_function();
10083 unsigned int time_start = SDL_GetTicks();
10085 for (i = 0; i < num_uuids; i++)
10089 char *seed = getStringCopy(init_random_function());
10091 hashtable_remove(hash_seeds, seed);
10092 hashtable_insert(hash_seeds, seed, "1");
10095 char *uuid = getStringCopy(getUUIDExt(random_function));
10097 hashtable_remove(hash_uuids, uuid);
10098 hashtable_insert(hash_uuids, uuid, "1");
10101 int num_unique_seeds = hashtable_count(hash_seeds);
10102 int num_unique_uuids = hashtable_count(hash_uuids);
10104 unsigned int time_needed = SDL_GetTicks() - time_start;
10106 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10108 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10111 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10113 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10114 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10116 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10118 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10120 Request(message, REQ_CONFIRM);
10122 hashtable_destroy(hash_seeds, 0);
10123 hashtable_destroy(hash_uuids, 0);
10126 void TestGeneratingUUIDs(void)
10128 int num_uuids = 1000000;
10131 for (i = 0; i < NUM_UUID_TESTS; i++)
10132 for (j = 0; j < 2; j++)
10133 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10135 CloseAllAndExit(0);