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 // may be needed if screen field is significantly larger than playfield
1494 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1495 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1496 int sync_frame = y * xsize + x;
1498 return sync_frame % g->anim_frames;
1500 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1502 struct GraphicInfo *g = &graphic_info[graphic];
1503 // may be needed if screen field is significantly larger than playfield
1504 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1505 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1506 int sync_frame = GfxRandomStatic[x][y];
1508 return sync_frame % g->anim_frames;
1511 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1513 return getGraphicAnimationFrame(graphic, sync_frame);
1516 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1518 struct GraphicInfo *g = &graphic_info[graphic];
1519 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1521 if (tilesize == gfx.standard_tile_size)
1522 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1523 else if (tilesize == game.tile_size)
1524 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1526 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1529 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1530 boolean get_backside)
1532 struct GraphicInfo *g = &graphic_info[graphic];
1533 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1534 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1536 if (g->offset_y == 0) // frames are ordered horizontally
1538 int max_width = g->anim_frames_per_line * g->width;
1539 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1541 *x = pos % max_width;
1542 *y = src_y % g->height + pos / max_width * g->height;
1544 else if (g->offset_x == 0) // frames are ordered vertically
1546 int max_height = g->anim_frames_per_line * g->height;
1547 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1549 *x = src_x % g->width + pos / max_height * g->width;
1550 *y = pos % max_height;
1552 else // frames are ordered diagonally
1554 *x = src_x + frame * g->offset_x;
1555 *y = src_y + frame * g->offset_y;
1559 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1560 Bitmap **bitmap, int *x, int *y,
1561 boolean get_backside)
1563 struct GraphicInfo *g = &graphic_info[graphic];
1565 // if no graphics defined at all, use fallback graphics
1566 if (g->bitmaps == NULL)
1567 *g = graphic_info[IMG_CHAR_EXCLAM];
1569 // if no in-game graphics defined, always use standard graphic size
1570 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1571 tilesize = TILESIZE;
1573 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1574 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1576 *x = *x * tilesize / g->tile_size;
1577 *y = *y * tilesize / g->tile_size;
1580 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1581 Bitmap **bitmap, int *x, int *y)
1583 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1586 void getFixedGraphicSource(int graphic, int frame,
1587 Bitmap **bitmap, int *x, int *y)
1589 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1592 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1594 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1597 void getGlobalAnimGraphicSource(int graphic, int frame,
1598 Bitmap **bitmap, int *x, int *y)
1600 struct GraphicInfo *g = &graphic_info[graphic];
1602 // if no graphics defined at all, use fallback graphics
1603 if (g->bitmaps == NULL)
1604 *g = graphic_info[IMG_CHAR_EXCLAM];
1606 // use original size graphics, if existing, else use standard size graphics
1607 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1608 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1610 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1612 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1615 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1616 int *x, int *y, boolean get_backside)
1618 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1622 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1624 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1627 void DrawGraphic(int x, int y, int graphic, int frame)
1630 if (!IN_SCR_FIELD(x, y))
1632 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1633 Debug("draw:DrawGraphic", "This should never happen!");
1639 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1642 MarkTileDirty(x, y);
1645 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1648 if (!IN_SCR_FIELD(x, y))
1650 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1652 Debug("draw:DrawFixedGraphic", "This should never happen!");
1658 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1660 MarkTileDirty(x, y);
1663 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1669 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1671 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1674 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1680 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1681 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1684 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1687 if (!IN_SCR_FIELD(x, y))
1689 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1691 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1697 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1700 MarkTileDirty(x, y);
1703 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1706 if (!IN_SCR_FIELD(x, y))
1708 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1710 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1716 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1718 MarkTileDirty(x, y);
1721 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1727 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1729 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1733 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1734 int graphic, int frame)
1739 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1741 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1745 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1747 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1749 MarkTileDirty(x / tilesize, y / tilesize);
1752 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1755 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1756 graphic, frame, tilesize);
1757 MarkTileDirty(x / tilesize, y / tilesize);
1760 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1766 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1767 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1770 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1771 int frame, int tilesize)
1776 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1777 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1780 void DrawMiniGraphic(int x, int y, int graphic)
1782 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1783 MarkTileDirty(x / 2, y / 2);
1786 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1791 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1792 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1795 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1796 int graphic, int frame,
1797 int cut_mode, int mask_mode)
1802 int width = TILEX, height = TILEY;
1805 if (dx || dy) // shifted graphic
1807 if (x < BX1) // object enters playfield from the left
1814 else if (x > BX2) // object enters playfield from the right
1820 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1826 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1828 else if (dx) // general horizontal movement
1829 MarkTileDirty(x + SIGN(dx), y);
1831 if (y < BY1) // object enters playfield from the top
1833 if (cut_mode == CUT_BELOW) // object completely above top border
1841 else if (y > BY2) // object enters playfield from the bottom
1847 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1853 else if (dy > 0 && cut_mode == CUT_ABOVE)
1855 if (y == BY2) // object completely above bottom border
1861 MarkTileDirty(x, y + 1);
1862 } // object leaves playfield to the bottom
1863 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1865 else if (dy) // general vertical movement
1866 MarkTileDirty(x, y + SIGN(dy));
1870 if (!IN_SCR_FIELD(x, y))
1872 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1874 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1880 width = width * TILESIZE_VAR / TILESIZE;
1881 height = height * TILESIZE_VAR / TILESIZE;
1882 cx = cx * TILESIZE_VAR / TILESIZE;
1883 cy = cy * TILESIZE_VAR / TILESIZE;
1884 dx = dx * TILESIZE_VAR / TILESIZE;
1885 dy = dy * TILESIZE_VAR / TILESIZE;
1887 if (width > 0 && height > 0)
1889 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1894 dst_x = FX + x * TILEX_VAR + dx;
1895 dst_y = FY + y * TILEY_VAR + dy;
1897 if (mask_mode == USE_MASKING)
1898 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1901 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1904 MarkTileDirty(x, y);
1908 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1909 int graphic, int frame,
1910 int cut_mode, int mask_mode)
1915 int width = TILEX_VAR, height = TILEY_VAR;
1918 int x2 = x + SIGN(dx);
1919 int y2 = y + SIGN(dy);
1921 // movement with two-tile animations must be sync'ed with movement position,
1922 // not with current GfxFrame (which can be higher when using slow movement)
1923 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1924 int anim_frames = graphic_info[graphic].anim_frames;
1926 // (we also need anim_delay here for movement animations with less frames)
1927 int anim_delay = graphic_info[graphic].anim_delay;
1928 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1930 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1931 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1933 // re-calculate animation frame for two-tile movement animation
1934 frame = getGraphicAnimationFrame(graphic, sync_frame);
1936 // check if movement start graphic inside screen area and should be drawn
1937 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1939 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1941 dst_x = FX + x1 * TILEX_VAR;
1942 dst_y = FY + y1 * TILEY_VAR;
1944 if (mask_mode == USE_MASKING)
1945 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1948 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 MarkTileDirty(x1, y1);
1954 // check if movement end graphic inside screen area and should be drawn
1955 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1957 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1959 dst_x = FX + x2 * TILEX_VAR;
1960 dst_y = FY + y2 * TILEY_VAR;
1962 if (mask_mode == USE_MASKING)
1963 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1966 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1969 MarkTileDirty(x2, y2);
1973 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1974 int graphic, int frame,
1975 int cut_mode, int mask_mode)
1979 DrawGraphic(x, y, graphic, frame);
1984 if (graphic_info[graphic].double_movement) // EM style movement images
1985 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1987 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1990 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1991 int graphic, int frame, int cut_mode)
1993 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1996 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1997 int cut_mode, int mask_mode)
1999 int lx = LEVELX(x), ly = LEVELY(y);
2003 if (IN_LEV_FIELD(lx, ly))
2005 if (element == EL_EMPTY)
2006 element = GfxElementEmpty[lx][ly];
2008 SetRandomAnimationValue(lx, ly);
2010 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2011 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2013 // do not use double (EM style) movement graphic when not moving
2014 if (graphic_info[graphic].double_movement && !dx && !dy)
2016 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2017 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2020 if (game.use_masked_elements && (dx || dy))
2021 mask_mode = USE_MASKING;
2023 else // border element
2025 graphic = el2img(element);
2026 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2029 if (element == EL_EXPANDABLE_WALL)
2031 boolean left_stopped = FALSE, right_stopped = FALSE;
2033 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2034 left_stopped = TRUE;
2035 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2036 right_stopped = TRUE;
2038 if (left_stopped && right_stopped)
2040 else if (left_stopped)
2042 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2043 frame = graphic_info[graphic].anim_frames - 1;
2045 else if (right_stopped)
2047 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2048 frame = graphic_info[graphic].anim_frames - 1;
2053 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2054 else if (mask_mode == USE_MASKING)
2055 DrawGraphicThruMask(x, y, graphic, frame);
2057 DrawGraphic(x, y, graphic, frame);
2060 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2061 int cut_mode, int mask_mode)
2063 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2064 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2065 cut_mode, mask_mode);
2068 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2071 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2074 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2077 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2080 void DrawLevelElementThruMask(int x, int y, int element)
2082 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2085 void DrawLevelFieldThruMask(int x, int y)
2087 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2090 // !!! implementation of quicksand is totally broken !!!
2091 #define IS_CRUMBLED_TILE(x, y, e) \
2092 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2093 !IS_MOVING(x, y) || \
2094 (e) == EL_QUICKSAND_EMPTYING || \
2095 (e) == EL_QUICKSAND_FAST_EMPTYING))
2097 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2102 int width, height, cx, cy;
2103 int sx = SCREENX(x), sy = SCREENY(y);
2104 int crumbled_border_size = graphic_info[graphic].border_size;
2105 int crumbled_tile_size = graphic_info[graphic].tile_size;
2106 int crumbled_border_size_var =
2107 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2110 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2112 for (i = 1; i < 4; i++)
2114 int dxx = (i & 1 ? dx : 0);
2115 int dyy = (i & 2 ? dy : 0);
2118 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2121 // check if neighbour field is of same crumble type
2122 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2123 graphic_info[graphic].class ==
2124 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2126 // return if check prevents inner corner
2127 if (same == (dxx == dx && dyy == dy))
2131 // if we reach this point, we have an inner corner
2133 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2135 width = crumbled_border_size_var;
2136 height = crumbled_border_size_var;
2137 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2138 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2140 if (game.use_masked_elements)
2142 int graphic0 = el2img(EL_EMPTY);
2143 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2144 Bitmap *src_bitmap0;
2147 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2149 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2151 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2153 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2155 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2158 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2160 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2163 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2168 int width, height, bx, by, cx, cy;
2169 int sx = SCREENX(x), sy = SCREENY(y);
2170 int crumbled_border_size = graphic_info[graphic].border_size;
2171 int crumbled_tile_size = graphic_info[graphic].tile_size;
2172 int crumbled_border_size_var =
2173 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2174 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2177 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2179 // only needed when using masked elements
2180 int graphic0 = el2img(EL_EMPTY);
2181 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2182 Bitmap *src_bitmap0;
2185 if (game.use_masked_elements)
2186 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2188 // draw simple, sloppy, non-corner-accurate crumbled border
2190 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2191 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2192 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2193 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2195 if (game.use_masked_elements)
2197 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2199 FX + sx * TILEX_VAR + cx,
2200 FY + sy * TILEY_VAR + cy);
2202 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2204 FX + sx * TILEX_VAR + cx,
2205 FY + sy * TILEY_VAR + cy);
2208 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2210 FX + sx * TILEX_VAR + cx,
2211 FY + sy * TILEY_VAR + cy);
2213 // (remaining middle border part must be at least as big as corner part)
2214 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2215 crumbled_border_size_var >= TILESIZE_VAR / 3)
2218 // correct corners of crumbled border, if needed
2220 for (i = -1; i <= 1; i += 2)
2222 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2223 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2224 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2227 // check if neighbour field is of same crumble type
2228 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2229 graphic_info[graphic].class ==
2230 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2232 // no crumbled corner, but continued crumbled border
2234 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2235 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2236 int b1 = (i == 1 ? crumbled_border_size_var :
2237 TILESIZE_VAR - 2 * crumbled_border_size_var);
2239 width = crumbled_border_size_var;
2240 height = crumbled_border_size_var;
2242 if (dir == 1 || dir == 2)
2257 if (game.use_masked_elements)
2259 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2261 FX + sx * TILEX_VAR + cx,
2262 FY + sy * TILEY_VAR + cy);
2264 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2266 FX + sx * TILEX_VAR + cx,
2267 FY + sy * TILEY_VAR + cy);
2270 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2272 FX + sx * TILEX_VAR + cx,
2273 FY + sy * TILEY_VAR + cy);
2278 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2280 int sx = SCREENX(x), sy = SCREENY(y);
2283 static int xy[4][2] =
2291 if (!IN_LEV_FIELD(x, y))
2294 element = TILE_GFX_ELEMENT(x, y);
2296 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2298 if (!IN_SCR_FIELD(sx, sy))
2301 // crumble field borders towards direct neighbour fields
2302 for (i = 0; i < 4; i++)
2304 int xx = x + xy[i][0];
2305 int yy = y + xy[i][1];
2307 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2310 // check if neighbour field is of same crumble type
2311 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2312 graphic_info[graphic].class ==
2313 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2316 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2319 // crumble inner field corners towards corner neighbour fields
2320 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2321 graphic_info[graphic].anim_frames == 2)
2323 for (i = 0; i < 4; i++)
2325 int dx = (i & 1 ? +1 : -1);
2326 int dy = (i & 2 ? +1 : -1);
2328 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2332 MarkTileDirty(sx, sy);
2334 else // center field is not crumbled -- crumble neighbour fields
2336 // crumble field borders of direct neighbour fields
2337 for (i = 0; i < 4; i++)
2339 int xx = x + xy[i][0];
2340 int yy = y + xy[i][1];
2341 int sxx = sx + xy[i][0];
2342 int syy = sy + xy[i][1];
2344 if (!IN_LEV_FIELD(xx, yy) ||
2345 !IN_SCR_FIELD(sxx, syy))
2348 // do not crumble fields that are being digged or snapped
2349 if (Tile[xx][yy] == EL_EMPTY ||
2350 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2353 element = TILE_GFX_ELEMENT(xx, yy);
2355 if (!IS_CRUMBLED_TILE(xx, yy, element))
2358 graphic = el_act2crm(element, ACTION_DEFAULT);
2360 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2362 MarkTileDirty(sxx, syy);
2365 // crumble inner field corners of corner neighbour fields
2366 for (i = 0; i < 4; i++)
2368 int dx = (i & 1 ? +1 : -1);
2369 int dy = (i & 2 ? +1 : -1);
2375 if (!IN_LEV_FIELD(xx, yy) ||
2376 !IN_SCR_FIELD(sxx, syy))
2379 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2382 element = TILE_GFX_ELEMENT(xx, yy);
2384 if (!IS_CRUMBLED_TILE(xx, yy, element))
2387 graphic = el_act2crm(element, ACTION_DEFAULT);
2389 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2390 graphic_info[graphic].anim_frames == 2)
2391 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2393 MarkTileDirty(sxx, syy);
2398 void DrawLevelFieldCrumbled(int x, int y)
2402 if (!IN_LEV_FIELD(x, y))
2405 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2406 GfxElement[x][y] != EL_UNDEFINED &&
2407 GFX_CRUMBLED(GfxElement[x][y]))
2409 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2414 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2416 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2419 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2422 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2423 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2424 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2425 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2426 int sx = SCREENX(x), sy = SCREENY(y);
2428 DrawScreenGraphic(sx, sy, graphic1, frame1);
2429 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2432 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2434 int sx = SCREENX(x), sy = SCREENY(y);
2435 static int xy[4][2] =
2444 // crumble direct neighbour fields (required for field borders)
2445 for (i = 0; i < 4; i++)
2447 int xx = x + xy[i][0];
2448 int yy = y + xy[i][1];
2449 int sxx = sx + xy[i][0];
2450 int syy = sy + xy[i][1];
2452 if (!IN_LEV_FIELD(xx, yy) ||
2453 !IN_SCR_FIELD(sxx, syy) ||
2454 !GFX_CRUMBLED(Tile[xx][yy]) ||
2458 DrawLevelField(xx, yy);
2461 // crumble corner neighbour fields (required for inner field corners)
2462 for (i = 0; i < 4; i++)
2464 int dx = (i & 1 ? +1 : -1);
2465 int dy = (i & 2 ? +1 : -1);
2471 if (!IN_LEV_FIELD(xx, yy) ||
2472 !IN_SCR_FIELD(sxx, syy) ||
2473 !GFX_CRUMBLED(Tile[xx][yy]) ||
2477 int element = TILE_GFX_ELEMENT(xx, yy);
2478 int graphic = el_act2crm(element, ACTION_DEFAULT);
2480 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2481 graphic_info[graphic].anim_frames == 2)
2482 DrawLevelField(xx, yy);
2486 static int getBorderElement(int x, int y)
2490 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2491 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2492 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2493 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2494 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2495 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2496 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2498 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2499 int steel_position = (x == -1 && y == -1 ? 0 :
2500 x == lev_fieldx && y == -1 ? 1 :
2501 x == -1 && y == lev_fieldy ? 2 :
2502 x == lev_fieldx && y == lev_fieldy ? 3 :
2503 x == -1 || x == lev_fieldx ? 4 :
2504 y == -1 || y == lev_fieldy ? 5 : 6);
2506 return border[steel_position][steel_type];
2509 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2511 if (game.use_masked_elements)
2513 if (graphic != el2img(EL_EMPTY))
2514 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2516 DrawGraphicThruMask(x, y, graphic, frame);
2520 DrawGraphic(x, y, graphic, frame);
2524 void DrawScreenElement(int x, int y, int element)
2526 int mask_mode = NO_MASKING;
2528 if (game.use_masked_elements)
2530 int lx = LEVELX(x), ly = LEVELY(y);
2532 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2534 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2536 mask_mode = USE_MASKING;
2540 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2541 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2544 void DrawLevelElement(int x, int y, int element)
2546 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2547 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2550 void DrawScreenField(int x, int y)
2552 int lx = LEVELX(x), ly = LEVELY(y);
2553 int element, content;
2555 if (!IN_LEV_FIELD(lx, ly))
2557 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2560 element = getBorderElement(lx, ly);
2562 DrawScreenElement(x, y, element);
2567 element = Tile[lx][ly];
2568 content = Store[lx][ly];
2570 if (IS_MOVING(lx, ly))
2572 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2573 boolean cut_mode = NO_CUTTING;
2575 if (element == EL_QUICKSAND_EMPTYING ||
2576 element == EL_QUICKSAND_FAST_EMPTYING ||
2577 element == EL_MAGIC_WALL_EMPTYING ||
2578 element == EL_BD_MAGIC_WALL_EMPTYING ||
2579 element == EL_DC_MAGIC_WALL_EMPTYING ||
2580 element == EL_AMOEBA_DROPPING)
2581 cut_mode = CUT_ABOVE;
2582 else if (element == EL_QUICKSAND_FILLING ||
2583 element == EL_QUICKSAND_FAST_FILLING ||
2584 element == EL_MAGIC_WALL_FILLING ||
2585 element == EL_BD_MAGIC_WALL_FILLING ||
2586 element == EL_DC_MAGIC_WALL_FILLING)
2587 cut_mode = CUT_BELOW;
2589 if (cut_mode == CUT_ABOVE)
2590 DrawScreenElement(x, y, element);
2592 DrawScreenElement(x, y, EL_EMPTY);
2594 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2596 int dir = MovDir[lx][ly];
2597 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2598 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2600 if (IN_SCR_FIELD(newx, newy))
2601 DrawScreenElement(newx, newy, EL_EMPTY);
2605 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2606 else if (cut_mode == NO_CUTTING)
2607 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2610 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2612 if (cut_mode == CUT_BELOW &&
2613 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2614 DrawLevelElement(lx, ly + 1, element);
2617 if (content == EL_ACID)
2619 int dir = MovDir[lx][ly];
2620 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2621 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2623 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2625 // prevent target field from being drawn again (but without masking)
2626 // (this would happen if target field is scanned after moving element)
2627 Stop[newlx][newly] = TRUE;
2630 else if (IS_BLOCKED(lx, ly))
2635 boolean cut_mode = NO_CUTTING;
2636 int element_old, content_old;
2638 Blocked2Moving(lx, ly, &oldx, &oldy);
2641 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2642 MovDir[oldx][oldy] == MV_RIGHT);
2644 element_old = Tile[oldx][oldy];
2645 content_old = Store[oldx][oldy];
2647 if (element_old == EL_QUICKSAND_EMPTYING ||
2648 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2649 element_old == EL_MAGIC_WALL_EMPTYING ||
2650 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2651 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2652 element_old == EL_AMOEBA_DROPPING)
2653 cut_mode = CUT_ABOVE;
2655 DrawScreenElement(x, y, EL_EMPTY);
2658 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2660 else if (cut_mode == NO_CUTTING)
2661 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2664 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2667 else if (IS_DRAWABLE(element))
2668 DrawScreenElement(x, y, element);
2670 DrawScreenElement(x, y, EL_EMPTY);
2673 void DrawLevelField(int x, int y)
2675 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2676 DrawScreenField(SCREENX(x), SCREENY(y));
2677 else if (IS_MOVING(x, y))
2681 Moving2Blocked(x, y, &newx, &newy);
2682 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2683 DrawScreenField(SCREENX(newx), SCREENY(newy));
2685 else if (IS_BLOCKED(x, y))
2689 Blocked2Moving(x, y, &oldx, &oldy);
2690 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2691 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2695 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2696 int (*el2img_function)(int), boolean masked,
2697 int element_bits_draw)
2699 int element_base = map_mm_wall_element(element);
2700 int element_bits = (IS_DF_WALL(element) ?
2701 element - EL_DF_WALL_START :
2702 IS_MM_WALL(element) ?
2703 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2704 int graphic = el2img_function(element_base);
2705 int tilesize_draw = tilesize / 2;
2710 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2712 for (i = 0; i < 4; i++)
2714 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2715 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2717 if (!(element_bits_draw & (1 << i)))
2720 if (element_bits & (1 << i))
2723 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2724 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2726 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2727 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2732 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2733 tilesize_draw, tilesize_draw);
2738 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2739 boolean masked, int element_bits_draw)
2741 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2742 element, tilesize, el2edimg, masked, element_bits_draw);
2745 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2746 int (*el2img_function)(int))
2748 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2752 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2755 if (IS_MM_WALL(element))
2757 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2758 element, tilesize, el2edimg, masked, 0x000f);
2762 int graphic = el2edimg(element);
2765 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2767 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2771 void DrawSizedElement(int x, int y, int element, int tilesize)
2773 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2776 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2778 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2781 void DrawMiniElement(int x, int y, int element)
2785 graphic = el2edimg(element);
2786 DrawMiniGraphic(x, y, graphic);
2789 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2792 int x = sx + scroll_x, y = sy + scroll_y;
2794 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2795 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2796 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2797 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2799 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2802 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2804 int x = sx + scroll_x, y = sy + scroll_y;
2806 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2807 DrawMiniElement(sx, sy, EL_EMPTY);
2808 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2809 DrawMiniElement(sx, sy, Tile[x][y]);
2811 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2814 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2815 int x, int y, int xsize, int ysize,
2816 int tile_width, int tile_height)
2820 int dst_x = startx + x * tile_width;
2821 int dst_y = starty + y * tile_height;
2822 int width = graphic_info[graphic].width;
2823 int height = graphic_info[graphic].height;
2824 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2825 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2826 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2827 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2828 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2829 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2830 boolean draw_masked = graphic_info[graphic].draw_masked;
2832 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2834 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2836 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2840 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2841 inner_sx + (x - 1) * tile_width % inner_width);
2842 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2843 inner_sy + (y - 1) * tile_height % inner_height);
2846 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2849 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2853 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2854 int x, int y, int xsize, int ysize,
2857 int font_width = getFontWidth(font_nr);
2858 int font_height = getFontHeight(font_nr);
2860 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2861 font_width, font_height);
2864 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2866 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2867 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2868 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2869 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2870 boolean no_delay = (tape.warp_forward);
2871 unsigned int anim_delay = 0;
2872 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2873 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2874 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2875 int font_width = getFontWidth(font_nr);
2876 int font_height = getFontHeight(font_nr);
2877 int max_xsize = level.envelope[envelope_nr].xsize;
2878 int max_ysize = level.envelope[envelope_nr].ysize;
2879 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2880 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2881 int xend = max_xsize;
2882 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2883 int xstep = (xstart < xend ? 1 : 0);
2884 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2886 int end = MAX(xend - xstart, yend - ystart);
2889 for (i = start; i <= end; i++)
2891 int last_frame = end; // last frame of this "for" loop
2892 int x = xstart + i * xstep;
2893 int y = ystart + i * ystep;
2894 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2895 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2896 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2897 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2900 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2902 BlitScreenToBitmap(backbuffer);
2904 SetDrawtoField(DRAW_TO_BACKBUFFER);
2906 for (yy = 0; yy < ysize; yy++)
2907 for (xx = 0; xx < xsize; xx++)
2908 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2910 DrawTextBuffer(sx + font_width, sy + font_height,
2911 level.envelope[envelope_nr].text, font_nr, max_xsize,
2912 xsize - 2, ysize - 2, 0, mask_mode,
2913 level.envelope[envelope_nr].autowrap,
2914 level.envelope[envelope_nr].centered, FALSE);
2916 redraw_mask |= REDRAW_FIELD;
2919 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2922 ClearAutoRepeatKeyEvents();
2925 void ShowEnvelope(int envelope_nr)
2927 int element = EL_ENVELOPE_1 + envelope_nr;
2928 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2929 int sound_opening = element_info[element].sound[ACTION_OPENING];
2930 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2931 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2932 boolean no_delay = (tape.warp_forward);
2933 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2934 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2935 int anim_mode = graphic_info[graphic].anim_mode;
2936 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2937 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2938 boolean overlay_enabled = GetOverlayEnabled();
2940 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2942 SetOverlayEnabled(FALSE);
2945 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2947 if (anim_mode == ANIM_DEFAULT)
2948 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2950 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2953 Delay_WithScreenUpdates(wait_delay_value);
2955 WaitForEventToContinue();
2958 SetOverlayEnabled(overlay_enabled);
2960 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2962 if (anim_mode != ANIM_NONE)
2963 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2965 if (anim_mode == ANIM_DEFAULT)
2966 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2968 game.envelope_active = FALSE;
2970 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2972 redraw_mask |= REDRAW_FIELD;
2976 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2977 int xsize, int ysize)
2979 if (!global.use_envelope_request ||
2980 request.sort_priority <= 0)
2983 if (request.bitmap == NULL ||
2984 xsize > request.xsize ||
2985 ysize > request.ysize)
2987 if (request.bitmap != NULL)
2988 FreeBitmap(request.bitmap);
2990 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2992 SDL_Surface *surface = request.bitmap->surface;
2994 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2995 Fail("SDLGetNativeSurface() failed");
2998 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3000 SDLFreeBitmapTextures(request.bitmap);
3001 SDLCreateBitmapTextures(request.bitmap);
3003 // set envelope request run-time values
3006 request.xsize = xsize;
3007 request.ysize = ysize;
3010 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3012 if (global.use_envelope_request &&
3013 game.request_active_or_moving &&
3014 request.sort_priority > 0 &&
3015 drawing_target == DRAW_TO_SCREEN &&
3016 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3018 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3019 request.sx, request.sy);
3023 static void setRequestBasePosition(int *x, int *y)
3025 int sx_base, sy_base;
3027 if (request.x != -1)
3028 sx_base = request.x;
3029 else if (request.align == ALIGN_LEFT)
3031 else if (request.align == ALIGN_RIGHT)
3032 sx_base = SX + SXSIZE;
3034 sx_base = SX + SXSIZE / 2;
3036 if (request.y != -1)
3037 sy_base = request.y;
3038 else if (request.valign == VALIGN_TOP)
3040 else if (request.valign == VALIGN_BOTTOM)
3041 sy_base = SY + SYSIZE;
3043 sy_base = SY + SYSIZE / 2;
3049 static void setRequestPositionExt(int *x, int *y, int width, int height,
3050 boolean add_border_size)
3052 int border_size = request.border_size;
3053 int sx_base, sy_base;
3056 setRequestBasePosition(&sx_base, &sy_base);
3058 if (request.align == ALIGN_LEFT)
3060 else if (request.align == ALIGN_RIGHT)
3061 sx = sx_base - width;
3063 sx = sx_base - width / 2;
3065 if (request.valign == VALIGN_TOP)
3067 else if (request.valign == VALIGN_BOTTOM)
3068 sy = sy_base - height;
3070 sy = sy_base - height / 2;
3072 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3073 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3075 if (add_border_size)
3085 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3087 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3090 static void DrawEnvelopeRequest(char *text)
3092 char *text_final = text;
3093 char *text_door_style = NULL;
3094 int graphic = IMG_BACKGROUND_REQUEST;
3095 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3096 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3097 int font_nr = FONT_REQUEST;
3098 int font_width = getFontWidth(font_nr);
3099 int font_height = getFontHeight(font_nr);
3100 int border_size = request.border_size;
3101 int line_spacing = request.line_spacing;
3102 int line_height = font_height + line_spacing;
3103 int max_text_width = request.width - 2 * border_size;
3104 int max_text_height = request.height - 2 * border_size;
3105 int line_length = max_text_width / font_width;
3106 int max_lines = max_text_height / line_height;
3107 int text_width = line_length * font_width;
3108 int width = request.width;
3109 int height = request.height;
3110 int tile_size = MAX(request.step_offset, 1);
3111 int x_steps = width / tile_size;
3112 int y_steps = height / tile_size;
3113 int sx_offset = border_size;
3114 int sy_offset = border_size;
3118 if (request.centered)
3119 sx_offset = (request.width - text_width) / 2;
3121 if (request.wrap_single_words && !request.autowrap)
3123 char *src_text_ptr, *dst_text_ptr;
3125 text_door_style = checked_malloc(2 * strlen(text) + 1);
3127 src_text_ptr = text;
3128 dst_text_ptr = text_door_style;
3130 while (*src_text_ptr)
3132 if (*src_text_ptr == ' ' ||
3133 *src_text_ptr == '?' ||
3134 *src_text_ptr == '!')
3135 *dst_text_ptr++ = '\n';
3137 if (*src_text_ptr != ' ')
3138 *dst_text_ptr++ = *src_text_ptr;
3143 *dst_text_ptr = '\0';
3145 text_final = text_door_style;
3148 setRequestPosition(&sx, &sy, FALSE);
3150 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3152 for (y = 0; y < y_steps; y++)
3153 for (x = 0; x < x_steps; x++)
3154 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3155 x, y, x_steps, y_steps,
3156 tile_size, tile_size);
3158 // force DOOR font inside door area
3159 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3161 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3162 line_length, -1, max_lines, line_spacing, mask_mode,
3163 request.autowrap, request.centered, FALSE);
3167 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3168 RedrawGadget(tool_gadget[i]);
3170 // store readily prepared envelope request for later use when animating
3171 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3173 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3175 if (text_door_style)
3176 free(text_door_style);
3179 static void AnimateEnvelopeRequest(int anim_mode, int action)
3181 int graphic = IMG_BACKGROUND_REQUEST;
3182 boolean draw_masked = graphic_info[graphic].draw_masked;
3183 int delay_value_normal = request.step_delay;
3184 int delay_value_fast = delay_value_normal / 2;
3185 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3186 boolean no_delay = (tape.warp_forward);
3187 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3188 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3189 unsigned int anim_delay = 0;
3191 int tile_size = MAX(request.step_offset, 1);
3192 int max_xsize = request.width / tile_size;
3193 int max_ysize = request.height / tile_size;
3194 int max_xsize_inner = max_xsize - 2;
3195 int max_ysize_inner = max_ysize - 2;
3197 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3198 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3199 int xend = max_xsize_inner;
3200 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3201 int xstep = (xstart < xend ? 1 : 0);
3202 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3204 int end = MAX(xend - xstart, yend - ystart);
3207 if (setup.quick_doors)
3214 for (i = start; i <= end; i++)
3216 int last_frame = end; // last frame of this "for" loop
3217 int x = xstart + i * xstep;
3218 int y = ystart + i * ystep;
3219 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3220 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3221 int xsize_size_left = (xsize - 1) * tile_size;
3222 int ysize_size_top = (ysize - 1) * tile_size;
3223 int max_xsize_pos = (max_xsize - 1) * tile_size;
3224 int max_ysize_pos = (max_ysize - 1) * tile_size;
3225 int width = xsize * tile_size;
3226 int height = ysize * tile_size;
3231 setRequestPosition(&src_x, &src_y, FALSE);
3232 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3234 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3236 for (yy = 0; yy < 2; yy++)
3238 for (xx = 0; xx < 2; xx++)
3240 int src_xx = src_x + xx * max_xsize_pos;
3241 int src_yy = src_y + yy * max_ysize_pos;
3242 int dst_xx = dst_x + xx * xsize_size_left;
3243 int dst_yy = dst_y + yy * ysize_size_top;
3244 int xx_size = (xx ? tile_size : xsize_size_left);
3245 int yy_size = (yy ? tile_size : ysize_size_top);
3248 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3249 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3251 BlitBitmap(bitmap_db_store_2, backbuffer,
3252 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3256 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3258 redraw_mask |= REDRAW_FIELD;
3262 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3265 ClearAutoRepeatKeyEvents();
3268 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3270 int graphic = IMG_BACKGROUND_REQUEST;
3271 int sound_opening = SND_REQUEST_OPENING;
3272 int sound_closing = SND_REQUEST_CLOSING;
3273 int anim_mode_1 = request.anim_mode; // (higher priority)
3274 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3275 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3276 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3277 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3279 if (game_status == GAME_MODE_PLAYING)
3280 BlitScreenToBitmap(backbuffer);
3282 SetDrawtoField(DRAW_TO_BACKBUFFER);
3284 // SetDrawBackgroundMask(REDRAW_NONE);
3286 if (action == ACTION_OPENING)
3288 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3290 if (req_state & REQ_ASK)
3292 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3293 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3294 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3295 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3297 else if (req_state & REQ_CONFIRM)
3299 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3300 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3302 else if (req_state & REQ_PLAYER)
3304 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3305 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3306 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3307 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3310 DrawEnvelopeRequest(text);
3313 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3315 if (action == ACTION_OPENING)
3317 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3319 if (anim_mode == ANIM_DEFAULT)
3320 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3322 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3326 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3328 if (anim_mode != ANIM_NONE)
3329 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3331 if (anim_mode == ANIM_DEFAULT)
3332 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3335 game.envelope_active = FALSE;
3337 if (action == ACTION_CLOSING)
3338 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3340 // SetDrawBackgroundMask(last_draw_background_mask);
3342 redraw_mask |= REDRAW_FIELD;
3346 if (action == ACTION_CLOSING &&
3347 game_status == GAME_MODE_PLAYING &&
3348 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3349 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3352 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3354 if (IS_MM_WALL(element))
3356 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3362 int graphic = el2preimg(element);
3364 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3365 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3370 void DrawLevel(int draw_background_mask)
3374 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3375 SetDrawBackgroundMask(draw_background_mask);
3379 for (x = BX1; x <= BX2; x++)
3380 for (y = BY1; y <= BY2; y++)
3381 DrawScreenField(x, y);
3383 redraw_mask |= REDRAW_FIELD;
3386 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3391 for (x = 0; x < size_x; x++)
3392 for (y = 0; y < size_y; y++)
3393 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3395 redraw_mask |= REDRAW_FIELD;
3398 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3402 for (x = 0; x < size_x; x++)
3403 for (y = 0; y < size_y; y++)
3404 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3406 redraw_mask |= REDRAW_FIELD;
3409 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3411 boolean show_level_border = (BorderElement != EL_EMPTY);
3412 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3413 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3414 int tile_size = preview.tile_size;
3415 int preview_width = preview.xsize * tile_size;
3416 int preview_height = preview.ysize * tile_size;
3417 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3418 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3419 int real_preview_width = real_preview_xsize * tile_size;
3420 int real_preview_height = real_preview_ysize * tile_size;
3421 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3422 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3425 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3428 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3430 dst_x += (preview_width - real_preview_width) / 2;
3431 dst_y += (preview_height - real_preview_height) / 2;
3433 for (x = 0; x < real_preview_xsize; x++)
3435 for (y = 0; y < real_preview_ysize; y++)
3437 int lx = from_x + x + (show_level_border ? -1 : 0);
3438 int ly = from_y + y + (show_level_border ? -1 : 0);
3439 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3440 getBorderElement(lx, ly));
3442 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3443 element, tile_size);
3447 redraw_mask |= REDRAW_FIELD;
3450 #define MICROLABEL_EMPTY 0
3451 #define MICROLABEL_LEVEL_NAME 1
3452 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3453 #define MICROLABEL_LEVEL_AUTHOR 3
3454 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3455 #define MICROLABEL_IMPORTED_FROM 5
3456 #define MICROLABEL_IMPORTED_BY_HEAD 6
3457 #define MICROLABEL_IMPORTED_BY 7
3459 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3461 int max_text_width = SXSIZE;
3462 int font_width = getFontWidth(font_nr);
3464 if (pos->align == ALIGN_CENTER)
3465 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3466 else if (pos->align == ALIGN_RIGHT)
3467 max_text_width = pos->x;
3469 max_text_width = SXSIZE - pos->x;
3471 return max_text_width / font_width;
3474 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3476 char label_text[MAX_OUTPUT_LINESIZE + 1];
3477 int max_len_label_text;
3478 int font_nr = pos->font;
3481 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3484 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3485 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3486 mode == MICROLABEL_IMPORTED_BY_HEAD)
3487 font_nr = pos->font_alt;
3489 max_len_label_text = getMaxTextLength(pos, font_nr);
3491 if (pos->size != -1)
3492 max_len_label_text = pos->size;
3494 for (i = 0; i < max_len_label_text; i++)
3495 label_text[i] = ' ';
3496 label_text[max_len_label_text] = '\0';
3498 if (strlen(label_text) > 0)
3499 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3502 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3503 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3504 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3505 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3506 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3507 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3508 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3509 max_len_label_text);
3510 label_text[max_len_label_text] = '\0';
3512 if (strlen(label_text) > 0)
3513 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3515 redraw_mask |= REDRAW_FIELD;
3518 static void DrawPreviewLevelLabel(int mode)
3520 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3523 static void DrawPreviewLevelInfo(int mode)
3525 if (mode == MICROLABEL_LEVEL_NAME)
3526 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3527 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3528 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3531 static void DrawPreviewLevelExt(boolean restart)
3533 static unsigned int scroll_delay = 0;
3534 static unsigned int label_delay = 0;
3535 static int from_x, from_y, scroll_direction;
3536 static int label_state, label_counter;
3537 unsigned int scroll_delay_value = preview.step_delay;
3538 boolean show_level_border = (BorderElement != EL_EMPTY);
3539 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3540 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3547 if (preview.anim_mode == ANIM_CENTERED)
3549 if (level_xsize > preview.xsize)
3550 from_x = (level_xsize - preview.xsize) / 2;
3551 if (level_ysize > preview.ysize)
3552 from_y = (level_ysize - preview.ysize) / 2;
3555 from_x += preview.xoffset;
3556 from_y += preview.yoffset;
3558 scroll_direction = MV_RIGHT;
3562 DrawPreviewLevelPlayfield(from_x, from_y);
3563 DrawPreviewLevelLabel(label_state);
3565 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3566 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3568 // initialize delay counters
3569 ResetDelayCounter(&scroll_delay);
3570 ResetDelayCounter(&label_delay);
3572 if (leveldir_current->name)
3574 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3575 char label_text[MAX_OUTPUT_LINESIZE + 1];
3576 int font_nr = pos->font;
3577 int max_len_label_text = getMaxTextLength(pos, font_nr);
3579 if (pos->size != -1)
3580 max_len_label_text = pos->size;
3582 strncpy(label_text, leveldir_current->name, max_len_label_text);
3583 label_text[max_len_label_text] = '\0';
3585 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3586 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3592 // scroll preview level, if needed
3593 if (preview.anim_mode != ANIM_NONE &&
3594 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3595 DelayReached(&scroll_delay, scroll_delay_value))
3597 switch (scroll_direction)
3602 from_x -= preview.step_offset;
3603 from_x = (from_x < 0 ? 0 : from_x);
3606 scroll_direction = MV_UP;
3610 if (from_x < level_xsize - preview.xsize)
3612 from_x += preview.step_offset;
3613 from_x = (from_x > level_xsize - preview.xsize ?
3614 level_xsize - preview.xsize : from_x);
3617 scroll_direction = MV_DOWN;
3623 from_y -= preview.step_offset;
3624 from_y = (from_y < 0 ? 0 : from_y);
3627 scroll_direction = MV_RIGHT;
3631 if (from_y < level_ysize - preview.ysize)
3633 from_y += preview.step_offset;
3634 from_y = (from_y > level_ysize - preview.ysize ?
3635 level_ysize - preview.ysize : from_y);
3638 scroll_direction = MV_LEFT;
3645 DrawPreviewLevelPlayfield(from_x, from_y);
3648 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3649 // redraw micro level label, if needed
3650 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3651 !strEqual(level.author, ANONYMOUS_NAME) &&
3652 !strEqual(level.author, leveldir_current->name) &&
3653 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3655 int max_label_counter = 23;
3657 if (leveldir_current->imported_from != NULL &&
3658 strlen(leveldir_current->imported_from) > 0)
3659 max_label_counter += 14;
3660 if (leveldir_current->imported_by != NULL &&
3661 strlen(leveldir_current->imported_by) > 0)
3662 max_label_counter += 14;
3664 label_counter = (label_counter + 1) % max_label_counter;
3665 label_state = (label_counter >= 0 && label_counter <= 7 ?
3666 MICROLABEL_LEVEL_NAME :
3667 label_counter >= 9 && label_counter <= 12 ?
3668 MICROLABEL_LEVEL_AUTHOR_HEAD :
3669 label_counter >= 14 && label_counter <= 21 ?
3670 MICROLABEL_LEVEL_AUTHOR :
3671 label_counter >= 23 && label_counter <= 26 ?
3672 MICROLABEL_IMPORTED_FROM_HEAD :
3673 label_counter >= 28 && label_counter <= 35 ?
3674 MICROLABEL_IMPORTED_FROM :
3675 label_counter >= 37 && label_counter <= 40 ?
3676 MICROLABEL_IMPORTED_BY_HEAD :
3677 label_counter >= 42 && label_counter <= 49 ?
3678 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3680 if (leveldir_current->imported_from == NULL &&
3681 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3682 label_state == MICROLABEL_IMPORTED_FROM))
3683 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3684 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3686 DrawPreviewLevelLabel(label_state);
3690 void DrawPreviewPlayers(void)
3692 if (game_status != GAME_MODE_MAIN)
3695 // do not draw preview players if level preview redefined, but players aren't
3696 if (preview.redefined && !menu.main.preview_players.redefined)
3699 boolean player_found[MAX_PLAYERS];
3700 int num_players = 0;
3703 for (i = 0; i < MAX_PLAYERS; i++)
3704 player_found[i] = FALSE;
3706 // check which players can be found in the level (simple approach)
3707 for (x = 0; x < lev_fieldx; x++)
3709 for (y = 0; y < lev_fieldy; y++)
3711 int element = level.field[x][y];
3713 if (IS_PLAYER_ELEMENT(element))
3715 int player_nr = GET_PLAYER_NR(element);
3717 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3719 if (!player_found[player_nr])
3722 player_found[player_nr] = TRUE;
3727 struct TextPosInfo *pos = &menu.main.preview_players;
3728 int tile_size = pos->tile_size;
3729 int border_size = pos->border_size;
3730 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3731 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3732 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3733 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3734 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3735 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3736 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3737 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3738 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3739 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3740 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3741 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3743 // clear area in which the players will be drawn
3744 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3745 max_players_width, max_players_height);
3747 if (!network.enabled && !setup.team_mode)
3750 // only draw players if level is suited for team mode
3751 if (num_players < 2)
3754 // draw all players that were found in the level
3755 for (i = 0; i < MAX_PLAYERS; i++)
3757 if (player_found[i])
3759 int graphic = el2img(EL_PLAYER_1 + i);
3761 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3763 xpos += player_xoffset;
3764 ypos += player_yoffset;
3769 void DrawPreviewLevelInitial(void)
3771 DrawPreviewLevelExt(TRUE);
3772 DrawPreviewPlayers();
3775 void DrawPreviewLevelAnimation(void)
3777 DrawPreviewLevelExt(FALSE);
3780 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3781 int border_size, int font_nr)
3783 int graphic = el2img(EL_PLAYER_1 + player_nr);
3784 int font_height = getFontHeight(font_nr);
3785 int player_height = MAX(tile_size, font_height);
3786 int xoffset_text = tile_size + border_size;
3787 int yoffset_text = (player_height - font_height) / 2;
3788 int yoffset_graphic = (player_height - tile_size) / 2;
3789 char *player_name = getNetworkPlayerName(player_nr + 1);
3791 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3793 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3796 static void DrawNetworkPlayersExt(boolean force)
3798 if (game_status != GAME_MODE_MAIN)
3801 if (!network.connected && !force)
3804 // do not draw network players if level preview redefined, but players aren't
3805 if (preview.redefined && !menu.main.network_players.redefined)
3808 int num_players = 0;
3811 for (i = 0; i < MAX_PLAYERS; i++)
3812 if (stored_player[i].connected_network)
3815 struct TextPosInfo *pos = &menu.main.network_players;
3816 int tile_size = pos->tile_size;
3817 int border_size = pos->border_size;
3818 int xoffset_text = tile_size + border_size;
3819 int font_nr = pos->font;
3820 int font_width = getFontWidth(font_nr);
3821 int font_height = getFontHeight(font_nr);
3822 int player_height = MAX(tile_size, font_height);
3823 int player_yoffset = player_height + border_size;
3824 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3825 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3826 int all_players_height = num_players * player_yoffset - border_size;
3827 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3828 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3829 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3831 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3832 max_players_width, max_players_height);
3834 // first draw local network player ...
3835 for (i = 0; i < MAX_PLAYERS; i++)
3837 if (stored_player[i].connected_network &&
3838 stored_player[i].connected_locally)
3840 char *player_name = getNetworkPlayerName(i + 1);
3841 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3842 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3844 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3846 ypos += player_yoffset;
3850 // ... then draw all other network players
3851 for (i = 0; i < MAX_PLAYERS; i++)
3853 if (stored_player[i].connected_network &&
3854 !stored_player[i].connected_locally)
3856 char *player_name = getNetworkPlayerName(i + 1);
3857 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3858 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3860 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3862 ypos += player_yoffset;
3867 void DrawNetworkPlayers(void)
3869 DrawNetworkPlayersExt(FALSE);
3872 void ClearNetworkPlayers(void)
3874 DrawNetworkPlayersExt(TRUE);
3877 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3878 int graphic, int lx, int ly,
3881 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3883 if (mask_mode == USE_MASKING)
3884 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3886 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3889 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3890 int graphic, int sync_frame, int mask_mode)
3892 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3894 if (mask_mode == USE_MASKING)
3895 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3897 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3900 static void DrawGraphicAnimation(int x, int y, int graphic)
3902 int lx = LEVELX(x), ly = LEVELY(y);
3903 int mask_mode = NO_MASKING;
3905 if (!IN_SCR_FIELD(x, y))
3908 if (game.use_masked_elements)
3910 if (Tile[lx][ly] != EL_EMPTY)
3912 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3914 mask_mode = USE_MASKING;
3918 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3919 graphic, lx, ly, mask_mode);
3921 MarkTileDirty(x, y);
3924 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3926 int lx = LEVELX(x), ly = LEVELY(y);
3927 int mask_mode = NO_MASKING;
3929 if (!IN_SCR_FIELD(x, y))
3932 if (game.use_masked_elements)
3934 if (Tile[lx][ly] != EL_EMPTY)
3936 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3938 mask_mode = USE_MASKING;
3942 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3943 graphic, lx, ly, mask_mode);
3945 MarkTileDirty(x, y);
3948 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3950 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3953 void DrawLevelElementAnimation(int x, int y, int element)
3955 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3957 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3960 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3962 int sx = SCREENX(x), sy = SCREENY(y);
3964 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3967 if (Tile[x][y] == EL_EMPTY)
3968 graphic = el2img(GfxElementEmpty[x][y]);
3970 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3973 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3976 DrawGraphicAnimation(sx, sy, graphic);
3979 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3980 DrawLevelFieldCrumbled(x, y);
3982 if (GFX_CRUMBLED(Tile[x][y]))
3983 DrawLevelFieldCrumbled(x, y);
3987 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3989 int sx = SCREENX(x), sy = SCREENY(y);
3992 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3995 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3997 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4000 DrawGraphicAnimation(sx, sy, graphic);
4002 if (GFX_CRUMBLED(element))
4003 DrawLevelFieldCrumbled(x, y);
4006 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4008 if (player->use_murphy)
4010 // this works only because currently only one player can be "murphy" ...
4011 static int last_horizontal_dir = MV_LEFT;
4012 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4014 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4015 last_horizontal_dir = move_dir;
4017 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4019 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4021 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4027 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4030 static boolean equalGraphics(int graphic1, int graphic2)
4032 struct GraphicInfo *g1 = &graphic_info[graphic1];
4033 struct GraphicInfo *g2 = &graphic_info[graphic2];
4035 return (g1->bitmap == g2->bitmap &&
4036 g1->src_x == g2->src_x &&
4037 g1->src_y == g2->src_y &&
4038 g1->anim_frames == g2->anim_frames &&
4039 g1->anim_delay == g2->anim_delay &&
4040 g1->anim_mode == g2->anim_mode);
4043 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4047 DRAW_PLAYER_STAGE_INIT = 0,
4048 DRAW_PLAYER_STAGE_LAST_FIELD,
4049 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4050 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4051 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4052 DRAW_PLAYER_STAGE_PLAYER,
4054 DRAW_PLAYER_STAGE_PLAYER,
4055 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4057 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4058 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4060 NUM_DRAW_PLAYER_STAGES
4063 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4065 static int static_last_player_graphic[MAX_PLAYERS];
4066 static int static_last_player_frame[MAX_PLAYERS];
4067 static boolean static_player_is_opaque[MAX_PLAYERS];
4068 static boolean draw_player[MAX_PLAYERS];
4069 int pnr = player->index_nr;
4071 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4073 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4074 static_last_player_frame[pnr] = player->Frame;
4075 static_player_is_opaque[pnr] = FALSE;
4077 draw_player[pnr] = TRUE;
4080 if (!draw_player[pnr])
4084 if (!IN_LEV_FIELD(player->jx, player->jy))
4086 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4087 Debug("draw:DrawPlayerExt", "This should never happen!");
4089 draw_player[pnr] = FALSE;
4095 int last_player_graphic = static_last_player_graphic[pnr];
4096 int last_player_frame = static_last_player_frame[pnr];
4097 boolean player_is_opaque = static_player_is_opaque[pnr];
4099 int jx = player->jx;
4100 int jy = player->jy;
4101 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4102 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4103 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4104 int last_jx = (player->is_moving ? jx - dx : jx);
4105 int last_jy = (player->is_moving ? jy - dy : jy);
4106 int next_jx = jx + dx;
4107 int next_jy = jy + dy;
4108 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4109 int sx = SCREENX(jx);
4110 int sy = SCREENY(jy);
4111 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4112 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4113 int element = Tile[jx][jy];
4114 int last_element = Tile[last_jx][last_jy];
4115 int action = (player->is_pushing ? ACTION_PUSHING :
4116 player->is_digging ? ACTION_DIGGING :
4117 player->is_collecting ? ACTION_COLLECTING :
4118 player->is_moving ? ACTION_MOVING :
4119 player->is_snapping ? ACTION_SNAPPING :
4120 player->is_dropping ? ACTION_DROPPING :
4121 player->is_waiting ? player->action_waiting :
4124 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4126 // ------------------------------------------------------------------------
4127 // initialize drawing the player
4128 // ------------------------------------------------------------------------
4130 draw_player[pnr] = FALSE;
4132 // GfxElement[][] is set to the element the player is digging or collecting;
4133 // remove also for off-screen player if the player is not moving anymore
4134 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4135 GfxElement[jx][jy] = EL_UNDEFINED;
4137 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4140 if (element == EL_EXPLOSION)
4143 InitPlayerGfxAnimation(player, action, move_dir);
4145 draw_player[pnr] = TRUE;
4147 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4149 // ------------------------------------------------------------------------
4150 // draw things in the field the player is leaving, if needed
4151 // ------------------------------------------------------------------------
4153 if (!IN_SCR_FIELD(sx, sy))
4154 draw_player[pnr] = FALSE;
4156 if (!player->is_moving)
4159 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4161 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4163 if (last_element == EL_DYNAMITE_ACTIVE ||
4164 last_element == EL_EM_DYNAMITE_ACTIVE ||
4165 last_element == EL_SP_DISK_RED_ACTIVE)
4166 DrawDynamite(last_jx, last_jy);
4168 DrawLevelFieldThruMask(last_jx, last_jy);
4170 else if (last_element == EL_DYNAMITE_ACTIVE ||
4171 last_element == EL_EM_DYNAMITE_ACTIVE ||
4172 last_element == EL_SP_DISK_RED_ACTIVE)
4173 DrawDynamite(last_jx, last_jy);
4175 DrawLevelField(last_jx, last_jy);
4177 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4178 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4180 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4182 // ------------------------------------------------------------------------
4183 // draw things behind the player, if needed
4184 // ------------------------------------------------------------------------
4188 DrawLevelElement(jx, jy, Back[jx][jy]);
4193 if (IS_ACTIVE_BOMB(element))
4195 DrawLevelElement(jx, jy, EL_EMPTY);
4200 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4202 int old_element = GfxElement[jx][jy];
4203 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4204 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4206 if (GFX_CRUMBLED(old_element))
4207 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4209 DrawScreenGraphic(sx, sy, old_graphic, frame);
4211 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4212 static_player_is_opaque[pnr] = TRUE;
4216 GfxElement[jx][jy] = EL_UNDEFINED;
4218 // make sure that pushed elements are drawn with correct frame rate
4219 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4221 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4222 GfxFrame[jx][jy] = player->StepFrame;
4224 DrawLevelField(jx, jy);
4227 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4229 // ------------------------------------------------------------------------
4230 // draw things the player is pushing, if needed
4231 // ------------------------------------------------------------------------
4233 if (!player->is_pushing || !player->is_moving)
4236 int gfx_frame = GfxFrame[jx][jy];
4238 if (!IS_MOVING(jx, jy)) // push movement already finished
4240 element = Tile[next_jx][next_jy];
4241 gfx_frame = GfxFrame[next_jx][next_jy];
4244 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4245 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4246 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4248 // draw background element under pushed element (like the Sokoban field)
4249 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4251 // this allows transparent pushing animation over non-black background
4254 DrawLevelElement(jx, jy, Back[jx][jy]);
4256 DrawLevelElement(jx, jy, EL_EMPTY);
4258 if (Back[next_jx][next_jy])
4259 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4261 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4263 else if (Back[next_jx][next_jy])
4264 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4266 int px = SCREENX(jx), py = SCREENY(jy);
4267 int pxx = (TILEX - ABS(sxx)) * dx;
4268 int pyy = (TILEY - ABS(syy)) * dy;
4271 // do not draw (EM style) pushing animation when pushing is finished
4272 // (two-tile animations usually do not contain start and end frame)
4273 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4274 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4276 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4278 // masked drawing is needed for EMC style (double) movement graphics
4279 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4280 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4283 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4285 // ------------------------------------------------------------------------
4286 // draw player himself
4287 // ------------------------------------------------------------------------
4289 int graphic = getPlayerGraphic(player, move_dir);
4291 // in the case of changed player action or direction, prevent the current
4292 // animation frame from being restarted for identical animations
4293 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4294 player->Frame = last_player_frame;
4296 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4298 if (player_is_opaque)
4299 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4301 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4303 if (SHIELD_ON(player))
4305 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4306 IMG_SHIELD_NORMAL_ACTIVE);
4307 frame = getGraphicAnimationFrame(graphic, -1);
4309 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4312 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4314 // ------------------------------------------------------------------------
4315 // draw things in front of player (active dynamite or dynabombs)
4316 // ------------------------------------------------------------------------
4318 if (IS_ACTIVE_BOMB(element))
4320 int graphic = el2img(element);
4321 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4323 if (game.emulation == EMU_SUPAPLEX)
4324 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4326 DrawGraphicThruMask(sx, sy, graphic, frame);
4329 if (player_is_moving && last_element == EL_EXPLOSION)
4331 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4332 GfxElement[last_jx][last_jy] : EL_EMPTY);
4333 int graphic = el_act2img(element, ACTION_EXPLODING);
4334 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4335 int phase = ExplodePhase[last_jx][last_jy] - 1;
4336 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4339 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4342 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4344 // ------------------------------------------------------------------------
4345 // draw elements the player is just walking/passing through/under
4346 // ------------------------------------------------------------------------
4348 if (player_is_moving)
4350 // handle the field the player is leaving ...
4351 if (IS_ACCESSIBLE_INSIDE(last_element))
4352 DrawLevelField(last_jx, last_jy);
4353 else if (IS_ACCESSIBLE_UNDER(last_element))
4354 DrawLevelFieldThruMask(last_jx, last_jy);
4357 // do not redraw accessible elements if the player is just pushing them
4358 if (!player_is_moving || !player->is_pushing)
4360 // ... and the field the player is entering
4361 if (IS_ACCESSIBLE_INSIDE(element))
4362 DrawLevelField(jx, jy);
4363 else if (IS_ACCESSIBLE_UNDER(element))
4364 DrawLevelFieldThruMask(jx, jy);
4367 MarkTileDirty(sx, sy);
4371 void DrawPlayer(struct PlayerInfo *player)
4375 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4376 DrawPlayerExt(player, i);
4379 void DrawAllPlayers(void)
4383 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4384 for (j = 0; j < MAX_PLAYERS; j++)
4385 if (stored_player[j].active)
4386 DrawPlayerExt(&stored_player[j], i);
4389 void DrawPlayerField(int x, int y)
4391 if (!IS_PLAYER(x, y))
4394 DrawPlayer(PLAYERINFO(x, y));
4397 // ----------------------------------------------------------------------------
4399 void WaitForEventToContinue(void)
4401 boolean first_wait = TRUE;
4402 boolean still_wait = TRUE;
4404 if (program.headless)
4407 // simulate releasing mouse button over last gadget, if still pressed
4409 HandleGadgets(-1, -1, 0);
4411 button_status = MB_RELEASED;
4414 ClearPlayerAction();
4420 if (NextValidEvent(&event))
4424 case EVENT_BUTTONPRESS:
4425 case EVENT_FINGERPRESS:
4429 case EVENT_BUTTONRELEASE:
4430 case EVENT_FINGERRELEASE:
4431 still_wait = first_wait;
4434 case EVENT_KEYPRESS:
4435 case SDL_CONTROLLERBUTTONDOWN:
4436 case SDL_JOYBUTTONDOWN:
4441 HandleOtherEvents(&event);
4445 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4450 if (!PendingEvent())
4455 #define MAX_REQUEST_LINES 13
4456 #define MAX_REQUEST_LINE_FONT1_LEN 7
4457 #define MAX_REQUEST_LINE_FONT2_LEN 10
4459 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4461 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4463 int draw_buffer_last = GetDrawtoField();
4464 int width = request.width;
4465 int height = request.height;
4469 // when showing request dialog after game ended, deactivate game panel
4470 if (game_just_ended)
4471 game.panel.active = FALSE;
4473 game.request_active = TRUE;
4475 setRequestPosition(&sx, &sy, FALSE);
4477 button_status = MB_RELEASED;
4479 request_gadget_id = -1;
4484 boolean event_handled = FALSE;
4486 if (game_just_ended)
4488 SetDrawtoField(draw_buffer_game);
4490 HandleGameActions();
4492 SetDrawtoField(DRAW_TO_BACKBUFFER);
4494 if (global.use_envelope_request)
4496 // copy current state of request area to middle of playfield area
4497 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4505 while (NextValidEvent(&event))
4507 event_handled = TRUE;
4511 case EVENT_BUTTONPRESS:
4512 case EVENT_BUTTONRELEASE:
4513 case EVENT_MOTIONNOTIFY:
4517 if (event.type == EVENT_MOTIONNOTIFY)
4522 motion_status = TRUE;
4523 mx = ((MotionEvent *) &event)->x;
4524 my = ((MotionEvent *) &event)->y;
4528 motion_status = FALSE;
4529 mx = ((ButtonEvent *) &event)->x;
4530 my = ((ButtonEvent *) &event)->y;
4531 if (event.type == EVENT_BUTTONPRESS)
4532 button_status = ((ButtonEvent *) &event)->button;
4534 button_status = MB_RELEASED;
4537 // this sets 'request_gadget_id'
4538 HandleGadgets(mx, my, button_status);
4540 switch (request_gadget_id)
4542 case TOOL_CTRL_ID_YES:
4543 case TOOL_CTRL_ID_TOUCH_YES:
4546 case TOOL_CTRL_ID_NO:
4547 case TOOL_CTRL_ID_TOUCH_NO:
4550 case TOOL_CTRL_ID_CONFIRM:
4551 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4552 result = TRUE | FALSE;
4555 case TOOL_CTRL_ID_PLAYER_1:
4558 case TOOL_CTRL_ID_PLAYER_2:
4561 case TOOL_CTRL_ID_PLAYER_3:
4564 case TOOL_CTRL_ID_PLAYER_4:
4569 // only check clickable animations if no request gadget clicked
4570 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4577 case SDL_WINDOWEVENT:
4578 HandleWindowEvent((WindowEvent *) &event);
4581 case SDL_APP_WILLENTERBACKGROUND:
4582 case SDL_APP_DIDENTERBACKGROUND:
4583 case SDL_APP_WILLENTERFOREGROUND:
4584 case SDL_APP_DIDENTERFOREGROUND:
4585 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4588 case EVENT_KEYPRESS:
4590 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4595 if (req_state & REQ_CONFIRM)
4604 #if defined(KSYM_Rewind)
4605 case KSYM_Rewind: // for Amazon Fire TV remote
4614 #if defined(KSYM_FastForward)
4615 case KSYM_FastForward: // for Amazon Fire TV remote
4621 HandleKeysDebug(key, KEY_PRESSED);
4625 if (req_state & REQ_PLAYER)
4627 int old_player_nr = setup.network_player_nr;
4630 result = old_player_nr + 1;
4635 result = old_player_nr + 1;
4666 case EVENT_FINGERRELEASE:
4667 case EVENT_KEYRELEASE:
4668 ClearPlayerAction();
4671 case SDL_CONTROLLERBUTTONDOWN:
4672 switch (event.cbutton.button)
4674 case SDL_CONTROLLER_BUTTON_A:
4675 case SDL_CONTROLLER_BUTTON_X:
4676 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4677 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4681 case SDL_CONTROLLER_BUTTON_B:
4682 case SDL_CONTROLLER_BUTTON_Y:
4683 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4684 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4685 case SDL_CONTROLLER_BUTTON_BACK:
4690 if (req_state & REQ_PLAYER)
4692 int old_player_nr = setup.network_player_nr;
4695 result = old_player_nr + 1;
4697 switch (event.cbutton.button)
4699 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4700 case SDL_CONTROLLER_BUTTON_Y:
4704 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4705 case SDL_CONTROLLER_BUTTON_B:
4709 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4710 case SDL_CONTROLLER_BUTTON_A:
4714 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4715 case SDL_CONTROLLER_BUTTON_X:
4726 case SDL_CONTROLLERBUTTONUP:
4727 HandleJoystickEvent(&event);
4728 ClearPlayerAction();
4732 HandleOtherEvents(&event);
4737 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4739 int joy = AnyJoystick();
4741 if (joy & JOY_BUTTON_1)
4743 else if (joy & JOY_BUTTON_2)
4746 else if (AnyJoystick())
4748 int joy = AnyJoystick();
4750 if (req_state & REQ_PLAYER)
4754 else if (joy & JOY_RIGHT)
4756 else if (joy & JOY_DOWN)
4758 else if (joy & JOY_LEFT)
4765 if (game_just_ended)
4767 if (global.use_envelope_request)
4769 // copy back current state of pressed buttons inside request area
4770 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4774 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4780 SetDrawtoField(draw_buffer_last);
4782 game.request_active = FALSE;
4787 static boolean RequestDoor(char *text, unsigned int req_state)
4789 int draw_buffer_last = GetDrawtoField();
4790 unsigned int old_door_state;
4791 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4792 int font_nr = FONT_TEXT_2;
4797 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4799 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4800 font_nr = FONT_TEXT_1;
4803 if (game_status == GAME_MODE_PLAYING)
4804 BlitScreenToBitmap(backbuffer);
4806 // disable deactivated drawing when quick-loading level tape recording
4807 if (tape.playing && tape.deactivate_display)
4808 TapeDeactivateDisplayOff(TRUE);
4810 SetMouseCursor(CURSOR_DEFAULT);
4812 // pause network game while waiting for request to answer
4813 if (network.enabled &&
4814 game_status == GAME_MODE_PLAYING &&
4815 !game.all_players_gone &&
4816 req_state & REQUEST_WAIT_FOR_INPUT)
4817 SendToServer_PausePlaying();
4819 old_door_state = GetDoorState();
4821 // simulate releasing mouse button over last gadget, if still pressed
4823 HandleGadgets(-1, -1, 0);
4827 // draw released gadget before proceeding
4830 if (old_door_state & DOOR_OPEN_1)
4832 CloseDoor(DOOR_CLOSE_1);
4834 // save old door content
4835 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4836 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4839 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4840 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4842 // clear door drawing field
4843 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4845 // force DOOR font inside door area
4846 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4848 // write text for request
4849 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4851 char text_line[max_request_line_len + 1];
4857 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4859 tc = *(text_ptr + tx);
4860 // if (!tc || tc == ' ')
4861 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4865 if ((tc == '?' || tc == '!') && tl == 0)
4875 strncpy(text_line, text_ptr, tl);
4878 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4879 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4880 text_line, font_nr);
4882 text_ptr += tl + (tc == ' ' ? 1 : 0);
4883 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4888 if (req_state & REQ_ASK)
4890 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4891 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4892 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4893 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4895 else if (req_state & REQ_CONFIRM)
4897 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4898 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4900 else if (req_state & REQ_PLAYER)
4902 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4903 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4904 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4905 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4908 // copy request gadgets to door backbuffer
4909 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4911 OpenDoor(DOOR_OPEN_1);
4913 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4915 if (game_status == GAME_MODE_PLAYING)
4917 SetPanelBackground();
4918 SetDrawBackgroundMask(REDRAW_DOOR_1);
4922 SetDrawBackgroundMask(REDRAW_FIELD);
4928 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4930 // ---------- handle request buttons ----------
4931 result = RequestHandleEvents(req_state, draw_buffer_last);
4935 if (!(req_state & REQ_STAY_OPEN))
4937 CloseDoor(DOOR_CLOSE_1);
4939 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4940 (req_state & REQ_REOPEN))
4941 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4946 if (game_status == GAME_MODE_PLAYING)
4948 SetPanelBackground();
4949 SetDrawBackgroundMask(REDRAW_DOOR_1);
4953 SetDrawBackgroundMask(REDRAW_FIELD);
4956 // continue network game after request
4957 if (network.enabled &&
4958 game_status == GAME_MODE_PLAYING &&
4959 !game.all_players_gone &&
4960 req_state & REQUEST_WAIT_FOR_INPUT)
4961 SendToServer_ContinuePlaying();
4963 // restore deactivated drawing when quick-loading level tape recording
4964 if (tape.playing && tape.deactivate_display)
4965 TapeDeactivateDisplayOn();
4970 static boolean RequestEnvelope(char *text, unsigned int req_state)
4972 int draw_buffer_last = GetDrawtoField();
4975 if (game_status == GAME_MODE_PLAYING)
4976 BlitScreenToBitmap(backbuffer);
4978 // disable deactivated drawing when quick-loading level tape recording
4979 if (tape.playing && tape.deactivate_display)
4980 TapeDeactivateDisplayOff(TRUE);
4982 SetMouseCursor(CURSOR_DEFAULT);
4984 // pause network game while waiting for request to answer
4985 if (network.enabled &&
4986 game_status == GAME_MODE_PLAYING &&
4987 !game.all_players_gone &&
4988 req_state & REQUEST_WAIT_FOR_INPUT)
4989 SendToServer_PausePlaying();
4991 // simulate releasing mouse button over last gadget, if still pressed
4993 HandleGadgets(-1, -1, 0);
4997 // (replace with setting corresponding request background)
4998 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4999 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5001 // clear door drawing field
5002 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5004 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5006 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5008 if (game_status == GAME_MODE_PLAYING)
5010 SetPanelBackground();
5011 SetDrawBackgroundMask(REDRAW_DOOR_1);
5015 SetDrawBackgroundMask(REDRAW_FIELD);
5021 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5023 // ---------- handle request buttons ----------
5024 result = RequestHandleEvents(req_state, draw_buffer_last);
5028 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5032 if (game_status == GAME_MODE_PLAYING)
5034 SetPanelBackground();
5035 SetDrawBackgroundMask(REDRAW_DOOR_1);
5039 SetDrawBackgroundMask(REDRAW_FIELD);
5042 // continue network game after request
5043 if (network.enabled &&
5044 game_status == GAME_MODE_PLAYING &&
5045 !game.all_players_gone &&
5046 req_state & REQUEST_WAIT_FOR_INPUT)
5047 SendToServer_ContinuePlaying();
5049 // restore deactivated drawing when quick-loading level tape recording
5050 if (tape.playing && tape.deactivate_display)
5051 TapeDeactivateDisplayOn();
5056 boolean Request(char *text, unsigned int req_state)
5058 boolean overlay_enabled = GetOverlayEnabled();
5061 game.request_active_or_moving = TRUE;
5063 SetOverlayEnabled(FALSE);
5065 if (global.use_envelope_request)
5066 result = RequestEnvelope(text, req_state);
5068 result = RequestDoor(text, req_state);
5070 SetOverlayEnabled(overlay_enabled);
5072 game.request_active_or_moving = FALSE;
5077 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5079 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5080 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5083 if (dpo1->sort_priority != dpo2->sort_priority)
5084 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5086 compare_result = dpo1->nr - dpo2->nr;
5088 return compare_result;
5091 void InitGraphicCompatibilityInfo_Doors(void)
5097 struct DoorInfo *door;
5101 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5102 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5104 { -1, -1, -1, NULL }
5106 struct Rect door_rect_list[] =
5108 { DX, DY, DXSIZE, DYSIZE },
5109 { VX, VY, VXSIZE, VYSIZE }
5113 for (i = 0; doors[i].door_token != -1; i++)
5115 int door_token = doors[i].door_token;
5116 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5117 int part_1 = doors[i].part_1;
5118 int part_8 = doors[i].part_8;
5119 int part_2 = part_1 + 1;
5120 int part_3 = part_1 + 2;
5121 struct DoorInfo *door = doors[i].door;
5122 struct Rect *door_rect = &door_rect_list[door_index];
5123 boolean door_gfx_redefined = FALSE;
5125 // check if any door part graphic definitions have been redefined
5127 for (j = 0; door_part_controls[j].door_token != -1; j++)
5129 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5130 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5132 if (dpc->door_token == door_token && fi->redefined)
5133 door_gfx_redefined = TRUE;
5136 // check for old-style door graphic/animation modifications
5138 if (!door_gfx_redefined)
5140 if (door->anim_mode & ANIM_STATIC_PANEL)
5142 door->panel.step_xoffset = 0;
5143 door->panel.step_yoffset = 0;
5146 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5148 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5149 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5150 int num_door_steps, num_panel_steps;
5152 // remove door part graphics other than the two default wings
5154 for (j = 0; door_part_controls[j].door_token != -1; j++)
5156 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5157 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5159 if (dpc->graphic >= part_3 &&
5160 dpc->graphic <= part_8)
5164 // set graphics and screen positions of the default wings
5166 g_part_1->width = door_rect->width;
5167 g_part_1->height = door_rect->height;
5168 g_part_2->width = door_rect->width;
5169 g_part_2->height = door_rect->height;
5170 g_part_2->src_x = door_rect->width;
5171 g_part_2->src_y = g_part_1->src_y;
5173 door->part_2.x = door->part_1.x;
5174 door->part_2.y = door->part_1.y;
5176 if (door->width != -1)
5178 g_part_1->width = door->width;
5179 g_part_2->width = door->width;
5181 // special treatment for graphics and screen position of right wing
5182 g_part_2->src_x += door_rect->width - door->width;
5183 door->part_2.x += door_rect->width - door->width;
5186 if (door->height != -1)
5188 g_part_1->height = door->height;
5189 g_part_2->height = door->height;
5191 // special treatment for graphics and screen position of bottom wing
5192 g_part_2->src_y += door_rect->height - door->height;
5193 door->part_2.y += door_rect->height - door->height;
5196 // set animation delays for the default wings and panels
5198 door->part_1.step_delay = door->step_delay;
5199 door->part_2.step_delay = door->step_delay;
5200 door->panel.step_delay = door->step_delay;
5202 // set animation draw order for the default wings
5204 door->part_1.sort_priority = 2; // draw left wing over ...
5205 door->part_2.sort_priority = 1; // ... right wing
5207 // set animation draw offset for the default wings
5209 if (door->anim_mode & ANIM_HORIZONTAL)
5211 door->part_1.step_xoffset = door->step_offset;
5212 door->part_1.step_yoffset = 0;
5213 door->part_2.step_xoffset = door->step_offset * -1;
5214 door->part_2.step_yoffset = 0;
5216 num_door_steps = g_part_1->width / door->step_offset;
5218 else // ANIM_VERTICAL
5220 door->part_1.step_xoffset = 0;
5221 door->part_1.step_yoffset = door->step_offset;
5222 door->part_2.step_xoffset = 0;
5223 door->part_2.step_yoffset = door->step_offset * -1;
5225 num_door_steps = g_part_1->height / door->step_offset;
5228 // set animation draw offset for the default panels
5230 if (door->step_offset > 1)
5232 num_panel_steps = 2 * door_rect->height / door->step_offset;
5233 door->panel.start_step = num_panel_steps - num_door_steps;
5234 door->panel.start_step_closing = door->panel.start_step;
5238 num_panel_steps = door_rect->height / door->step_offset;
5239 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5240 door->panel.start_step_closing = door->panel.start_step;
5241 door->panel.step_delay *= 2;
5248 void InitDoors(void)
5252 for (i = 0; door_part_controls[i].door_token != -1; i++)
5254 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5255 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5257 // initialize "start_step_opening" and "start_step_closing", if needed
5258 if (dpc->pos->start_step_opening == 0 &&
5259 dpc->pos->start_step_closing == 0)
5261 // dpc->pos->start_step_opening = dpc->pos->start_step;
5262 dpc->pos->start_step_closing = dpc->pos->start_step;
5265 // fill structure for door part draw order (sorted below)
5267 dpo->sort_priority = dpc->pos->sort_priority;
5270 // sort door part controls according to sort_priority and graphic number
5271 qsort(door_part_order, MAX_DOOR_PARTS,
5272 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5275 unsigned int OpenDoor(unsigned int door_state)
5277 if (door_state & DOOR_COPY_BACK)
5279 if (door_state & DOOR_OPEN_1)
5280 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5281 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5283 if (door_state & DOOR_OPEN_2)
5284 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5285 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5287 door_state &= ~DOOR_COPY_BACK;
5290 return MoveDoor(door_state);
5293 unsigned int CloseDoor(unsigned int door_state)
5295 unsigned int old_door_state = GetDoorState();
5297 if (!(door_state & DOOR_NO_COPY_BACK))
5299 if (old_door_state & DOOR_OPEN_1)
5300 BlitBitmap(backbuffer, bitmap_db_door_1,
5301 DX, DY, DXSIZE, DYSIZE, 0, 0);
5303 if (old_door_state & DOOR_OPEN_2)
5304 BlitBitmap(backbuffer, bitmap_db_door_2,
5305 VX, VY, VXSIZE, VYSIZE, 0, 0);
5307 door_state &= ~DOOR_NO_COPY_BACK;
5310 return MoveDoor(door_state);
5313 unsigned int GetDoorState(void)
5315 return MoveDoor(DOOR_GET_STATE);
5318 unsigned int SetDoorState(unsigned int door_state)
5320 return MoveDoor(door_state | DOOR_SET_STATE);
5323 static int euclid(int a, int b)
5325 return (b ? euclid(b, a % b) : a);
5328 unsigned int MoveDoor(unsigned int door_state)
5330 struct Rect door_rect_list[] =
5332 { DX, DY, DXSIZE, DYSIZE },
5333 { VX, VY, VXSIZE, VYSIZE }
5335 static int door1 = DOOR_CLOSE_1;
5336 static int door2 = DOOR_CLOSE_2;
5337 unsigned int door_delay = 0;
5338 unsigned int door_delay_value;
5341 if (door_state == DOOR_GET_STATE)
5342 return (door1 | door2);
5344 if (door_state & DOOR_SET_STATE)
5346 if (door_state & DOOR_ACTION_1)
5347 door1 = door_state & DOOR_ACTION_1;
5348 if (door_state & DOOR_ACTION_2)
5349 door2 = door_state & DOOR_ACTION_2;
5351 return (door1 | door2);
5354 if (!(door_state & DOOR_FORCE_REDRAW))
5356 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5357 door_state &= ~DOOR_OPEN_1;
5358 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5359 door_state &= ~DOOR_CLOSE_1;
5360 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5361 door_state &= ~DOOR_OPEN_2;
5362 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5363 door_state &= ~DOOR_CLOSE_2;
5366 if (global.autoplay_leveldir)
5368 door_state |= DOOR_NO_DELAY;
5369 door_state &= ~DOOR_CLOSE_ALL;
5372 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5373 door_state |= DOOR_NO_DELAY;
5375 if (door_state & DOOR_ACTION)
5377 boolean door_panel_drawn[NUM_DOORS];
5378 boolean panel_has_doors[NUM_DOORS];
5379 boolean door_part_skip[MAX_DOOR_PARTS];
5380 boolean door_part_done[MAX_DOOR_PARTS];
5381 boolean door_part_done_all;
5382 int num_steps[MAX_DOOR_PARTS];
5383 int max_move_delay = 0; // delay for complete animations of all doors
5384 int max_step_delay = 0; // delay (ms) between two animation frames
5385 int num_move_steps = 0; // number of animation steps for all doors
5386 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5387 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5391 for (i = 0; i < NUM_DOORS; i++)
5392 panel_has_doors[i] = FALSE;
5394 for (i = 0; i < MAX_DOOR_PARTS; i++)
5396 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5397 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5398 int door_token = dpc->door_token;
5400 door_part_done[i] = FALSE;
5401 door_part_skip[i] = (!(door_state & door_token) ||
5405 for (i = 0; i < MAX_DOOR_PARTS; i++)
5407 int nr = door_part_order[i].nr;
5408 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5409 struct DoorPartPosInfo *pos = dpc->pos;
5410 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5411 int door_token = dpc->door_token;
5412 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5413 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5414 int step_xoffset = ABS(pos->step_xoffset);
5415 int step_yoffset = ABS(pos->step_yoffset);
5416 int step_delay = pos->step_delay;
5417 int current_door_state = door_state & door_token;
5418 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5419 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5420 boolean part_opening = (is_panel ? door_closing : door_opening);
5421 int start_step = (part_opening ? pos->start_step_opening :
5422 pos->start_step_closing);
5423 float move_xsize = (step_xoffset ? g->width : 0);
5424 float move_ysize = (step_yoffset ? g->height : 0);
5425 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5426 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5427 int move_steps = (move_xsteps && move_ysteps ?
5428 MIN(move_xsteps, move_ysteps) :
5429 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5430 int move_delay = move_steps * step_delay;
5432 if (door_part_skip[nr])
5435 max_move_delay = MAX(max_move_delay, move_delay);
5436 max_step_delay = (max_step_delay == 0 ? step_delay :
5437 euclid(max_step_delay, step_delay));
5438 num_steps[nr] = move_steps;
5442 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5444 panel_has_doors[door_index] = TRUE;
5448 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5450 num_move_steps = max_move_delay / max_step_delay;
5451 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5453 door_delay_value = max_step_delay;
5455 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5457 start = num_move_steps - 1;
5461 // opening door sound has priority over simultaneously closing door
5462 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5464 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5466 if (door_state & DOOR_OPEN_1)
5467 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5468 if (door_state & DOOR_OPEN_2)
5469 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5471 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5473 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5475 if (door_state & DOOR_CLOSE_1)
5476 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5477 if (door_state & DOOR_CLOSE_2)
5478 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5482 for (k = start; k < num_move_steps; k++)
5484 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5486 door_part_done_all = TRUE;
5488 for (i = 0; i < NUM_DOORS; i++)
5489 door_panel_drawn[i] = FALSE;
5491 for (i = 0; i < MAX_DOOR_PARTS; i++)
5493 int nr = door_part_order[i].nr;
5494 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5495 struct DoorPartPosInfo *pos = dpc->pos;
5496 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5497 int door_token = dpc->door_token;
5498 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5499 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5500 boolean is_panel_and_door_has_closed = FALSE;
5501 struct Rect *door_rect = &door_rect_list[door_index];
5502 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5504 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5505 int current_door_state = door_state & door_token;
5506 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5507 boolean door_closing = !door_opening;
5508 boolean part_opening = (is_panel ? door_closing : door_opening);
5509 boolean part_closing = !part_opening;
5510 int start_step = (part_opening ? pos->start_step_opening :
5511 pos->start_step_closing);
5512 int step_delay = pos->step_delay;
5513 int step_factor = step_delay / max_step_delay;
5514 int k1 = (step_factor ? k / step_factor + 1 : k);
5515 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5516 int kk = MAX(0, k2);
5519 int src_x, src_y, src_xx, src_yy;
5520 int dst_x, dst_y, dst_xx, dst_yy;
5523 if (door_part_skip[nr])
5526 if (!(door_state & door_token))
5534 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5535 int kk_door = MAX(0, k2_door);
5536 int sync_frame = kk_door * door_delay_value;
5537 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5539 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5540 &g_src_x, &g_src_y);
5545 if (!door_panel_drawn[door_index])
5547 ClearRectangle(drawto, door_rect->x, door_rect->y,
5548 door_rect->width, door_rect->height);
5550 door_panel_drawn[door_index] = TRUE;
5553 // draw opening or closing door parts
5555 if (pos->step_xoffset < 0) // door part on right side
5558 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5561 if (dst_xx + width > door_rect->width)
5562 width = door_rect->width - dst_xx;
5564 else // door part on left side
5567 dst_xx = pos->x - kk * pos->step_xoffset;
5571 src_xx = ABS(dst_xx);
5575 width = g->width - src_xx;
5577 if (width > door_rect->width)
5578 width = door_rect->width;
5580 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5583 if (pos->step_yoffset < 0) // door part on bottom side
5586 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5589 if (dst_yy + height > door_rect->height)
5590 height = door_rect->height - dst_yy;
5592 else // door part on top side
5595 dst_yy = pos->y - kk * pos->step_yoffset;
5599 src_yy = ABS(dst_yy);
5603 height = g->height - src_yy;
5606 src_x = g_src_x + src_xx;
5607 src_y = g_src_y + src_yy;
5609 dst_x = door_rect->x + dst_xx;
5610 dst_y = door_rect->y + dst_yy;
5612 is_panel_and_door_has_closed =
5615 panel_has_doors[door_index] &&
5616 k >= num_move_steps_doors_only - 1);
5618 if (width >= 0 && width <= g->width &&
5619 height >= 0 && height <= g->height &&
5620 !is_panel_and_door_has_closed)
5622 if (is_panel || !pos->draw_masked)
5623 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5626 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5630 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5632 if ((part_opening && (width < 0 || height < 0)) ||
5633 (part_closing && (width >= g->width && height >= g->height)))
5634 door_part_done[nr] = TRUE;
5636 // continue door part animations, but not panel after door has closed
5637 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5638 door_part_done_all = FALSE;
5641 if (!(door_state & DOOR_NO_DELAY))
5645 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5647 // prevent OS (Windows) from complaining about program not responding
5651 if (door_part_done_all)
5655 if (!(door_state & DOOR_NO_DELAY))
5657 // wait for specified door action post delay
5658 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5659 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5660 else if (door_state & DOOR_ACTION_1)
5661 door_delay_value = door_1.post_delay;
5662 else if (door_state & DOOR_ACTION_2)
5663 door_delay_value = door_2.post_delay;
5665 while (!DelayReached(&door_delay, door_delay_value))
5670 if (door_state & DOOR_ACTION_1)
5671 door1 = door_state & DOOR_ACTION_1;
5672 if (door_state & DOOR_ACTION_2)
5673 door2 = door_state & DOOR_ACTION_2;
5675 // draw masked border over door area
5676 DrawMaskedBorder(REDRAW_DOOR_1);
5677 DrawMaskedBorder(REDRAW_DOOR_2);
5679 ClearAutoRepeatKeyEvents();
5681 return (door1 | door2);
5684 static boolean useSpecialEditorDoor(void)
5686 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5687 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5689 // do not draw special editor door if editor border defined or redefined
5690 if (graphic_info[graphic].bitmap != NULL || redefined)
5693 // do not draw special editor door if global border defined to be empty
5694 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5697 // do not draw special editor door if viewport definitions do not match
5701 EY + EYSIZE != VY + VYSIZE)
5707 void DrawSpecialEditorDoor(void)
5709 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5710 int top_border_width = gfx1->width;
5711 int top_border_height = gfx1->height;
5712 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5713 int ex = EX - outer_border;
5714 int ey = EY - outer_border;
5715 int vy = VY - outer_border;
5716 int exsize = EXSIZE + 2 * outer_border;
5718 if (!useSpecialEditorDoor())
5721 // draw bigger level editor toolbox window
5722 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5723 top_border_width, top_border_height, ex, ey - top_border_height);
5724 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5725 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5727 redraw_mask |= REDRAW_ALL;
5730 void UndrawSpecialEditorDoor(void)
5732 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5733 int top_border_width = gfx1->width;
5734 int top_border_height = gfx1->height;
5735 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5736 int ex = EX - outer_border;
5737 int ey = EY - outer_border;
5738 int ey_top = ey - top_border_height;
5739 int exsize = EXSIZE + 2 * outer_border;
5740 int eysize = EYSIZE + 2 * outer_border;
5742 if (!useSpecialEditorDoor())
5745 // draw normal tape recorder window
5746 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5748 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5749 ex, ey_top, top_border_width, top_border_height,
5751 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5752 ex, ey, exsize, eysize, ex, ey);
5756 // if screen background is set to "[NONE]", clear editor toolbox window
5757 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5758 ClearRectangle(drawto, ex, ey, exsize, eysize);
5761 redraw_mask |= REDRAW_ALL;
5765 // ---------- new tool button stuff -------------------------------------------
5770 struct TextPosInfo *pos;
5772 boolean is_touch_button;
5774 } toolbutton_info[NUM_TOOL_BUTTONS] =
5777 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5778 TOOL_CTRL_ID_YES, FALSE, "yes"
5781 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5782 TOOL_CTRL_ID_NO, FALSE, "no"
5785 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5786 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5789 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5790 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5793 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5794 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5797 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5798 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5801 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5802 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5805 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5806 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5809 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5810 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5813 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5814 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5818 void CreateToolButtons(void)
5822 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5824 int graphic = toolbutton_info[i].graphic;
5825 struct GraphicInfo *gfx = &graphic_info[graphic];
5826 struct TextPosInfo *pos = toolbutton_info[i].pos;
5827 struct GadgetInfo *gi;
5828 Bitmap *deco_bitmap = None;
5829 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5830 unsigned int event_mask = GD_EVENT_RELEASED;
5831 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5832 int base_x = (is_touch_button ? 0 : DX);
5833 int base_y = (is_touch_button ? 0 : DY);
5834 int gd_x = gfx->src_x;
5835 int gd_y = gfx->src_y;
5836 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5837 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5842 if (global.use_envelope_request && !is_touch_button)
5844 setRequestPosition(&base_x, &base_y, TRUE);
5846 // check if request buttons are outside of envelope and fix, if needed
5847 if (x < 0 || x + gfx->width > request.width ||
5848 y < 0 || y + gfx->height > request.height)
5850 if (id == TOOL_CTRL_ID_YES)
5853 y = request.height - 2 * request.border_size - gfx->height;
5855 else if (id == TOOL_CTRL_ID_NO)
5857 x = request.width - 2 * request.border_size - gfx->width;
5858 y = request.height - 2 * request.border_size - gfx->height;
5860 else if (id == TOOL_CTRL_ID_CONFIRM)
5862 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5863 y = request.height - 2 * request.border_size - gfx->height;
5865 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5867 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5869 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5870 y = request.height - 2 * request.border_size - gfx->height * 2;
5872 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5873 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5878 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5880 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5882 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5883 pos->size, &deco_bitmap, &deco_x, &deco_y);
5884 deco_xpos = (gfx->width - pos->size) / 2;
5885 deco_ypos = (gfx->height - pos->size) / 2;
5888 gi = CreateGadget(GDI_CUSTOM_ID, id,
5889 GDI_IMAGE_ID, graphic,
5890 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5893 GDI_WIDTH, gfx->width,
5894 GDI_HEIGHT, gfx->height,
5895 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5896 GDI_STATE, GD_BUTTON_UNPRESSED,
5897 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5898 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5899 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5900 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5901 GDI_DECORATION_SIZE, pos->size, pos->size,
5902 GDI_DECORATION_SHIFTING, 1, 1,
5903 GDI_DIRECT_DRAW, FALSE,
5904 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5905 GDI_EVENT_MASK, event_mask,
5906 GDI_CALLBACK_ACTION, HandleToolButtons,
5910 Fail("cannot create gadget");
5912 tool_gadget[id] = gi;
5916 void FreeToolButtons(void)
5920 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5921 FreeGadget(tool_gadget[i]);
5924 static void UnmapToolButtons(void)
5928 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5929 UnmapGadget(tool_gadget[i]);
5932 static void HandleToolButtons(struct GadgetInfo *gi)
5934 request_gadget_id = gi->custom_id;
5937 static struct Mapping_EM_to_RND_object
5940 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5941 boolean is_backside; // backside of moving element
5947 em_object_mapping_list[GAME_TILE_MAX + 1] =
5950 Zborder, FALSE, FALSE,
5954 Zplayer, FALSE, FALSE,
5963 Ztank, FALSE, FALSE,
5967 Zeater, FALSE, FALSE,
5971 Zdynamite, FALSE, FALSE,
5975 Zboom, FALSE, FALSE,
5980 Xchain, FALSE, FALSE,
5981 EL_DEFAULT, ACTION_EXPLODING, -1
5984 Xboom_bug, FALSE, FALSE,
5985 EL_BUG, ACTION_EXPLODING, -1
5988 Xboom_tank, FALSE, FALSE,
5989 EL_SPACESHIP, ACTION_EXPLODING, -1
5992 Xboom_android, FALSE, FALSE,
5993 EL_EMC_ANDROID, ACTION_OTHER, -1
5996 Xboom_1, FALSE, FALSE,
5997 EL_DEFAULT, ACTION_EXPLODING, -1
6000 Xboom_2, FALSE, FALSE,
6001 EL_DEFAULT, ACTION_EXPLODING, -1
6005 Xblank, TRUE, FALSE,
6010 Xsplash_e, FALSE, FALSE,
6011 EL_ACID_SPLASH_RIGHT, -1, -1
6014 Xsplash_w, FALSE, FALSE,
6015 EL_ACID_SPLASH_LEFT, -1, -1
6019 Xplant, TRUE, FALSE,
6020 EL_EMC_PLANT, -1, -1
6023 Yplant, FALSE, FALSE,
6024 EL_EMC_PLANT, -1, -1
6028 Xacid_1, TRUE, FALSE,
6032 Xacid_2, FALSE, FALSE,
6036 Xacid_3, FALSE, FALSE,
6040 Xacid_4, FALSE, FALSE,
6044 Xacid_5, FALSE, FALSE,
6048 Xacid_6, FALSE, FALSE,
6052 Xacid_7, FALSE, FALSE,
6056 Xacid_8, FALSE, FALSE,
6061 Xfake_acid_1, TRUE, FALSE,
6062 EL_EMC_FAKE_ACID, -1, -1
6065 Xfake_acid_2, FALSE, FALSE,
6066 EL_EMC_FAKE_ACID, -1, -1
6069 Xfake_acid_3, FALSE, FALSE,
6070 EL_EMC_FAKE_ACID, -1, -1
6073 Xfake_acid_4, FALSE, FALSE,
6074 EL_EMC_FAKE_ACID, -1, -1
6077 Xfake_acid_5, FALSE, FALSE,
6078 EL_EMC_FAKE_ACID, -1, -1
6081 Xfake_acid_6, FALSE, FALSE,
6082 EL_EMC_FAKE_ACID, -1, -1
6085 Xfake_acid_7, FALSE, FALSE,
6086 EL_EMC_FAKE_ACID, -1, -1
6089 Xfake_acid_8, FALSE, FALSE,
6090 EL_EMC_FAKE_ACID, -1, -1
6094 Xfake_acid_1_player, FALSE, FALSE,
6095 EL_EMC_FAKE_ACID, -1, -1
6098 Xfake_acid_2_player, FALSE, FALSE,
6099 EL_EMC_FAKE_ACID, -1, -1
6102 Xfake_acid_3_player, FALSE, FALSE,
6103 EL_EMC_FAKE_ACID, -1, -1
6106 Xfake_acid_4_player, FALSE, FALSE,
6107 EL_EMC_FAKE_ACID, -1, -1
6110 Xfake_acid_5_player, FALSE, FALSE,
6111 EL_EMC_FAKE_ACID, -1, -1
6114 Xfake_acid_6_player, FALSE, FALSE,
6115 EL_EMC_FAKE_ACID, -1, -1
6118 Xfake_acid_7_player, FALSE, FALSE,
6119 EL_EMC_FAKE_ACID, -1, -1
6122 Xfake_acid_8_player, FALSE, FALSE,
6123 EL_EMC_FAKE_ACID, -1, -1
6127 Xgrass, TRUE, FALSE,
6128 EL_EMC_GRASS, -1, -1
6131 Ygrass_nB, FALSE, FALSE,
6132 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6135 Ygrass_eB, FALSE, FALSE,
6136 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6139 Ygrass_sB, FALSE, FALSE,
6140 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6143 Ygrass_wB, FALSE, FALSE,
6144 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6152 Ydirt_nB, FALSE, FALSE,
6153 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6156 Ydirt_eB, FALSE, FALSE,
6157 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6160 Ydirt_sB, FALSE, FALSE,
6161 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6164 Ydirt_wB, FALSE, FALSE,
6165 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6169 Xandroid, TRUE, FALSE,
6170 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6173 Xandroid_1_n, FALSE, FALSE,
6174 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6177 Xandroid_2_n, FALSE, FALSE,
6178 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6181 Xandroid_1_e, FALSE, FALSE,
6182 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6185 Xandroid_2_e, FALSE, FALSE,
6186 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6189 Xandroid_1_w, FALSE, FALSE,
6190 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6193 Xandroid_2_w, FALSE, FALSE,
6194 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6197 Xandroid_1_s, FALSE, FALSE,
6198 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6201 Xandroid_2_s, FALSE, FALSE,
6202 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6205 Yandroid_n, FALSE, FALSE,
6206 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6209 Yandroid_nB, FALSE, TRUE,
6210 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6213 Yandroid_ne, FALSE, FALSE,
6214 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6217 Yandroid_neB, FALSE, TRUE,
6218 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6221 Yandroid_e, FALSE, FALSE,
6222 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6225 Yandroid_eB, FALSE, TRUE,
6226 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6229 Yandroid_se, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6233 Yandroid_seB, FALSE, TRUE,
6234 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6237 Yandroid_s, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6241 Yandroid_sB, FALSE, TRUE,
6242 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6245 Yandroid_sw, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6249 Yandroid_swB, FALSE, TRUE,
6250 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6253 Yandroid_w, FALSE, FALSE,
6254 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6257 Yandroid_wB, FALSE, TRUE,
6258 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6261 Yandroid_nw, FALSE, FALSE,
6262 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6265 Yandroid_nwB, FALSE, TRUE,
6266 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6270 Xeater_n, TRUE, FALSE,
6271 EL_YAMYAM_UP, -1, -1
6274 Xeater_e, TRUE, FALSE,
6275 EL_YAMYAM_RIGHT, -1, -1
6278 Xeater_w, TRUE, FALSE,
6279 EL_YAMYAM_LEFT, -1, -1
6282 Xeater_s, TRUE, FALSE,
6283 EL_YAMYAM_DOWN, -1, -1
6286 Yeater_n, FALSE, FALSE,
6287 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6290 Yeater_nB, FALSE, TRUE,
6291 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6294 Yeater_e, FALSE, FALSE,
6295 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6298 Yeater_eB, FALSE, TRUE,
6299 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6302 Yeater_s, FALSE, FALSE,
6303 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6306 Yeater_sB, FALSE, TRUE,
6307 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6310 Yeater_w, FALSE, FALSE,
6311 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6314 Yeater_wB, FALSE, TRUE,
6315 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6318 Yeater_stone, FALSE, FALSE,
6319 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6322 Yeater_spring, FALSE, FALSE,
6323 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6327 Xalien, TRUE, FALSE,
6331 Xalien_pause, FALSE, FALSE,
6335 Yalien_n, FALSE, FALSE,
6336 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6339 Yalien_nB, FALSE, TRUE,
6340 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6343 Yalien_e, FALSE, FALSE,
6344 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6347 Yalien_eB, FALSE, TRUE,
6348 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6351 Yalien_s, FALSE, FALSE,
6352 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6355 Yalien_sB, FALSE, TRUE,
6356 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6359 Yalien_w, FALSE, FALSE,
6360 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6363 Yalien_wB, FALSE, TRUE,
6364 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6367 Yalien_stone, FALSE, FALSE,
6368 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6371 Yalien_spring, FALSE, FALSE,
6372 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6376 Xbug_1_n, TRUE, FALSE,
6380 Xbug_1_e, TRUE, FALSE,
6381 EL_BUG_RIGHT, -1, -1
6384 Xbug_1_s, TRUE, FALSE,
6388 Xbug_1_w, TRUE, FALSE,
6392 Xbug_2_n, FALSE, FALSE,
6396 Xbug_2_e, FALSE, FALSE,
6397 EL_BUG_RIGHT, -1, -1
6400 Xbug_2_s, FALSE, FALSE,
6404 Xbug_2_w, FALSE, FALSE,
6408 Ybug_n, FALSE, FALSE,
6409 EL_BUG, ACTION_MOVING, MV_BIT_UP
6412 Ybug_nB, FALSE, TRUE,
6413 EL_BUG, ACTION_MOVING, MV_BIT_UP
6416 Ybug_e, FALSE, FALSE,
6417 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6420 Ybug_eB, FALSE, TRUE,
6421 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6424 Ybug_s, FALSE, FALSE,
6425 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6428 Ybug_sB, FALSE, TRUE,
6429 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6432 Ybug_w, FALSE, FALSE,
6433 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6436 Ybug_wB, FALSE, TRUE,
6437 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6440 Ybug_w_n, FALSE, FALSE,
6441 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6444 Ybug_n_e, FALSE, FALSE,
6445 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6448 Ybug_e_s, FALSE, FALSE,
6449 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6452 Ybug_s_w, FALSE, FALSE,
6453 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6456 Ybug_e_n, FALSE, FALSE,
6457 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6460 Ybug_s_e, FALSE, FALSE,
6461 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6464 Ybug_w_s, FALSE, FALSE,
6465 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6468 Ybug_n_w, FALSE, FALSE,
6469 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6472 Ybug_stone, FALSE, FALSE,
6473 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6476 Ybug_spring, FALSE, FALSE,
6477 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6481 Xtank_1_n, TRUE, FALSE,
6482 EL_SPACESHIP_UP, -1, -1
6485 Xtank_1_e, TRUE, FALSE,
6486 EL_SPACESHIP_RIGHT, -1, -1
6489 Xtank_1_s, TRUE, FALSE,
6490 EL_SPACESHIP_DOWN, -1, -1
6493 Xtank_1_w, TRUE, FALSE,
6494 EL_SPACESHIP_LEFT, -1, -1
6497 Xtank_2_n, FALSE, FALSE,
6498 EL_SPACESHIP_UP, -1, -1
6501 Xtank_2_e, FALSE, FALSE,
6502 EL_SPACESHIP_RIGHT, -1, -1
6505 Xtank_2_s, FALSE, FALSE,
6506 EL_SPACESHIP_DOWN, -1, -1
6509 Xtank_2_w, FALSE, FALSE,
6510 EL_SPACESHIP_LEFT, -1, -1
6513 Ytank_n, FALSE, FALSE,
6514 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6517 Ytank_nB, FALSE, TRUE,
6518 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6521 Ytank_e, FALSE, FALSE,
6522 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6525 Ytank_eB, FALSE, TRUE,
6526 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6529 Ytank_s, FALSE, FALSE,
6530 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6533 Ytank_sB, FALSE, TRUE,
6534 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6537 Ytank_w, FALSE, FALSE,
6538 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6541 Ytank_wB, FALSE, TRUE,
6542 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6545 Ytank_w_n, FALSE, FALSE,
6546 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6549 Ytank_n_e, FALSE, FALSE,
6550 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6553 Ytank_e_s, FALSE, FALSE,
6554 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6557 Ytank_s_w, FALSE, FALSE,
6558 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6561 Ytank_e_n, FALSE, FALSE,
6562 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6565 Ytank_s_e, FALSE, FALSE,
6566 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6569 Ytank_w_s, FALSE, FALSE,
6570 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6573 Ytank_n_w, FALSE, FALSE,
6574 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6577 Ytank_stone, FALSE, FALSE,
6578 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6581 Ytank_spring, FALSE, FALSE,
6582 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6586 Xemerald, TRUE, FALSE,
6590 Xemerald_pause, FALSE, FALSE,
6594 Xemerald_fall, FALSE, FALSE,
6598 Xemerald_shine, FALSE, FALSE,
6599 EL_EMERALD, ACTION_TWINKLING, -1
6602 Yemerald_s, FALSE, FALSE,
6603 EL_EMERALD, ACTION_FALLING, -1
6606 Yemerald_sB, FALSE, TRUE,
6607 EL_EMERALD, ACTION_FALLING, -1
6610 Yemerald_e, FALSE, FALSE,
6611 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6614 Yemerald_eB, FALSE, TRUE,
6615 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6618 Yemerald_w, FALSE, FALSE,
6619 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6622 Yemerald_wB, FALSE, TRUE,
6623 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6626 Yemerald_blank, FALSE, FALSE,
6627 EL_EMERALD, ACTION_COLLECTING, -1
6631 Xdiamond, TRUE, FALSE,
6635 Xdiamond_pause, FALSE, FALSE,
6639 Xdiamond_fall, FALSE, FALSE,
6643 Xdiamond_shine, FALSE, FALSE,
6644 EL_DIAMOND, ACTION_TWINKLING, -1
6647 Ydiamond_s, FALSE, FALSE,
6648 EL_DIAMOND, ACTION_FALLING, -1
6651 Ydiamond_sB, FALSE, TRUE,
6652 EL_DIAMOND, ACTION_FALLING, -1
6655 Ydiamond_e, FALSE, FALSE,
6656 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6659 Ydiamond_eB, FALSE, TRUE,
6660 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6663 Ydiamond_w, FALSE, FALSE,
6664 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6667 Ydiamond_wB, FALSE, TRUE,
6668 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6671 Ydiamond_blank, FALSE, FALSE,
6672 EL_DIAMOND, ACTION_COLLECTING, -1
6675 Ydiamond_stone, FALSE, FALSE,
6676 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6680 Xstone, TRUE, FALSE,
6684 Xstone_pause, FALSE, FALSE,
6688 Xstone_fall, FALSE, FALSE,
6692 Ystone_s, FALSE, FALSE,
6693 EL_ROCK, ACTION_FALLING, -1
6696 Ystone_sB, FALSE, TRUE,
6697 EL_ROCK, ACTION_FALLING, -1
6700 Ystone_e, FALSE, FALSE,
6701 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6704 Ystone_eB, FALSE, TRUE,
6705 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6708 Ystone_w, FALSE, FALSE,
6709 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6712 Ystone_wB, FALSE, TRUE,
6713 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6721 Xbomb_pause, FALSE, FALSE,
6725 Xbomb_fall, FALSE, FALSE,
6729 Ybomb_s, FALSE, FALSE,
6730 EL_BOMB, ACTION_FALLING, -1
6733 Ybomb_sB, FALSE, TRUE,
6734 EL_BOMB, ACTION_FALLING, -1
6737 Ybomb_e, FALSE, FALSE,
6738 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6741 Ybomb_eB, FALSE, TRUE,
6742 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6745 Ybomb_w, FALSE, FALSE,
6746 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6749 Ybomb_wB, FALSE, TRUE,
6750 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6753 Ybomb_blank, FALSE, FALSE,
6754 EL_BOMB, ACTION_ACTIVATING, -1
6762 Xnut_pause, FALSE, FALSE,
6766 Xnut_fall, FALSE, FALSE,
6770 Ynut_s, FALSE, FALSE,
6771 EL_NUT, ACTION_FALLING, -1
6774 Ynut_sB, FALSE, TRUE,
6775 EL_NUT, ACTION_FALLING, -1
6778 Ynut_e, FALSE, FALSE,
6779 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6782 Ynut_eB, FALSE, TRUE,
6783 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6786 Ynut_w, FALSE, FALSE,
6787 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6790 Ynut_wB, FALSE, TRUE,
6791 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6794 Ynut_stone, FALSE, FALSE,
6795 EL_NUT, ACTION_BREAKING, -1
6799 Xspring, TRUE, FALSE,
6803 Xspring_pause, FALSE, FALSE,
6807 Xspring_e, TRUE, FALSE,
6808 EL_SPRING_RIGHT, -1, -1
6811 Xspring_w, TRUE, FALSE,
6812 EL_SPRING_LEFT, -1, -1
6815 Xspring_fall, FALSE, FALSE,
6819 Yspring_s, FALSE, FALSE,
6820 EL_SPRING, ACTION_FALLING, -1
6823 Yspring_sB, FALSE, TRUE,
6824 EL_SPRING, ACTION_FALLING, -1
6827 Yspring_e, FALSE, FALSE,
6828 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6831 Yspring_eB, FALSE, TRUE,
6832 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6835 Yspring_w, FALSE, FALSE,
6836 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6839 Yspring_wB, FALSE, TRUE,
6840 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6843 Yspring_alien_e, FALSE, FALSE,
6844 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6847 Yspring_alien_eB, FALSE, TRUE,
6848 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6851 Yspring_alien_w, FALSE, FALSE,
6852 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6855 Yspring_alien_wB, FALSE, TRUE,
6856 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6860 Xpush_emerald_e, FALSE, FALSE,
6861 EL_EMERALD, -1, MV_BIT_RIGHT
6864 Xpush_emerald_w, FALSE, FALSE,
6865 EL_EMERALD, -1, MV_BIT_LEFT
6868 Xpush_diamond_e, FALSE, FALSE,
6869 EL_DIAMOND, -1, MV_BIT_RIGHT
6872 Xpush_diamond_w, FALSE, FALSE,
6873 EL_DIAMOND, -1, MV_BIT_LEFT
6876 Xpush_stone_e, FALSE, FALSE,
6877 EL_ROCK, -1, MV_BIT_RIGHT
6880 Xpush_stone_w, FALSE, FALSE,
6881 EL_ROCK, -1, MV_BIT_LEFT
6884 Xpush_bomb_e, FALSE, FALSE,
6885 EL_BOMB, -1, MV_BIT_RIGHT
6888 Xpush_bomb_w, FALSE, FALSE,
6889 EL_BOMB, -1, MV_BIT_LEFT
6892 Xpush_nut_e, FALSE, FALSE,
6893 EL_NUT, -1, MV_BIT_RIGHT
6896 Xpush_nut_w, FALSE, FALSE,
6897 EL_NUT, -1, MV_BIT_LEFT
6900 Xpush_spring_e, FALSE, FALSE,
6901 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6904 Xpush_spring_w, FALSE, FALSE,
6905 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6909 Xdynamite, TRUE, FALSE,
6910 EL_EM_DYNAMITE, -1, -1
6913 Ydynamite_blank, FALSE, FALSE,
6914 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6917 Xdynamite_1, TRUE, FALSE,
6918 EL_EM_DYNAMITE_ACTIVE, -1, -1
6921 Xdynamite_2, FALSE, FALSE,
6922 EL_EM_DYNAMITE_ACTIVE, -1, -1
6925 Xdynamite_3, FALSE, FALSE,
6926 EL_EM_DYNAMITE_ACTIVE, -1, -1
6929 Xdynamite_4, FALSE, FALSE,
6930 EL_EM_DYNAMITE_ACTIVE, -1, -1
6934 Xkey_1, TRUE, FALSE,
6938 Xkey_2, TRUE, FALSE,
6942 Xkey_3, TRUE, FALSE,
6946 Xkey_4, TRUE, FALSE,
6950 Xkey_5, TRUE, FALSE,
6951 EL_EMC_KEY_5, -1, -1
6954 Xkey_6, TRUE, FALSE,
6955 EL_EMC_KEY_6, -1, -1
6958 Xkey_7, TRUE, FALSE,
6959 EL_EMC_KEY_7, -1, -1
6962 Xkey_8, TRUE, FALSE,
6963 EL_EMC_KEY_8, -1, -1
6967 Xdoor_1, TRUE, FALSE,
6968 EL_EM_GATE_1, -1, -1
6971 Xdoor_2, TRUE, FALSE,
6972 EL_EM_GATE_2, -1, -1
6975 Xdoor_3, TRUE, FALSE,
6976 EL_EM_GATE_3, -1, -1
6979 Xdoor_4, TRUE, FALSE,
6980 EL_EM_GATE_4, -1, -1
6983 Xdoor_5, TRUE, FALSE,
6984 EL_EMC_GATE_5, -1, -1
6987 Xdoor_6, TRUE, FALSE,
6988 EL_EMC_GATE_6, -1, -1
6991 Xdoor_7, TRUE, FALSE,
6992 EL_EMC_GATE_7, -1, -1
6995 Xdoor_8, TRUE, FALSE,
6996 EL_EMC_GATE_8, -1, -1
7000 Xfake_door_1, TRUE, FALSE,
7001 EL_EM_GATE_1_GRAY, -1, -1
7004 Xfake_door_2, TRUE, FALSE,
7005 EL_EM_GATE_2_GRAY, -1, -1
7008 Xfake_door_3, TRUE, FALSE,
7009 EL_EM_GATE_3_GRAY, -1, -1
7012 Xfake_door_4, TRUE, FALSE,
7013 EL_EM_GATE_4_GRAY, -1, -1
7016 Xfake_door_5, TRUE, FALSE,
7017 EL_EMC_GATE_5_GRAY, -1, -1
7020 Xfake_door_6, TRUE, FALSE,
7021 EL_EMC_GATE_6_GRAY, -1, -1
7024 Xfake_door_7, TRUE, FALSE,
7025 EL_EMC_GATE_7_GRAY, -1, -1
7028 Xfake_door_8, TRUE, FALSE,
7029 EL_EMC_GATE_8_GRAY, -1, -1
7033 Xballoon, TRUE, FALSE,
7037 Yballoon_n, FALSE, FALSE,
7038 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7041 Yballoon_nB, FALSE, TRUE,
7042 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7045 Yballoon_e, FALSE, FALSE,
7046 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7049 Yballoon_eB, FALSE, TRUE,
7050 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7053 Yballoon_s, FALSE, FALSE,
7054 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7057 Yballoon_sB, FALSE, TRUE,
7058 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7061 Yballoon_w, FALSE, FALSE,
7062 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7065 Yballoon_wB, FALSE, TRUE,
7066 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7070 Xball_1, TRUE, FALSE,
7071 EL_EMC_MAGIC_BALL, -1, -1
7074 Yball_1, FALSE, FALSE,
7075 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7078 Xball_2, FALSE, FALSE,
7079 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7082 Yball_2, FALSE, FALSE,
7083 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7086 Yball_blank, FALSE, FALSE,
7087 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7091 Xamoeba_1, TRUE, FALSE,
7092 EL_AMOEBA_DRY, ACTION_OTHER, -1
7095 Xamoeba_2, FALSE, FALSE,
7096 EL_AMOEBA_DRY, ACTION_OTHER, -1
7099 Xamoeba_3, FALSE, FALSE,
7100 EL_AMOEBA_DRY, ACTION_OTHER, -1
7103 Xamoeba_4, FALSE, FALSE,
7104 EL_AMOEBA_DRY, ACTION_OTHER, -1
7107 Xamoeba_5, TRUE, FALSE,
7108 EL_AMOEBA_WET, ACTION_OTHER, -1
7111 Xamoeba_6, FALSE, FALSE,
7112 EL_AMOEBA_WET, ACTION_OTHER, -1
7115 Xamoeba_7, FALSE, FALSE,
7116 EL_AMOEBA_WET, ACTION_OTHER, -1
7119 Xamoeba_8, FALSE, FALSE,
7120 EL_AMOEBA_WET, ACTION_OTHER, -1
7125 EL_AMOEBA_DROP, ACTION_GROWING, -1
7128 Xdrip_fall, FALSE, FALSE,
7129 EL_AMOEBA_DROP, -1, -1
7132 Xdrip_stretch, FALSE, FALSE,
7133 EL_AMOEBA_DROP, ACTION_FALLING, -1
7136 Xdrip_stretchB, FALSE, TRUE,
7137 EL_AMOEBA_DROP, ACTION_FALLING, -1
7140 Ydrip_1_s, FALSE, FALSE,
7141 EL_AMOEBA_DROP, ACTION_FALLING, -1
7144 Ydrip_1_sB, FALSE, TRUE,
7145 EL_AMOEBA_DROP, ACTION_FALLING, -1
7148 Ydrip_2_s, FALSE, FALSE,
7149 EL_AMOEBA_DROP, ACTION_FALLING, -1
7152 Ydrip_2_sB, FALSE, TRUE,
7153 EL_AMOEBA_DROP, ACTION_FALLING, -1
7157 Xwonderwall, TRUE, FALSE,
7158 EL_MAGIC_WALL, -1, -1
7161 Ywonderwall, FALSE, FALSE,
7162 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7166 Xwheel, TRUE, FALSE,
7167 EL_ROBOT_WHEEL, -1, -1
7170 Ywheel, FALSE, FALSE,
7171 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7175 Xswitch, TRUE, FALSE,
7176 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7179 Yswitch, FALSE, FALSE,
7180 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7184 Xbumper, TRUE, FALSE,
7185 EL_EMC_SPRING_BUMPER, -1, -1
7188 Ybumper, FALSE, FALSE,
7189 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7193 Xacid_nw, TRUE, FALSE,
7194 EL_ACID_POOL_TOPLEFT, -1, -1
7197 Xacid_ne, TRUE, FALSE,
7198 EL_ACID_POOL_TOPRIGHT, -1, -1
7201 Xacid_sw, TRUE, FALSE,
7202 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7205 Xacid_s, TRUE, FALSE,
7206 EL_ACID_POOL_BOTTOM, -1, -1
7209 Xacid_se, TRUE, FALSE,
7210 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7214 Xfake_blank, TRUE, FALSE,
7215 EL_INVISIBLE_WALL, -1, -1
7218 Yfake_blank, FALSE, FALSE,
7219 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7223 Xfake_grass, TRUE, FALSE,
7224 EL_EMC_FAKE_GRASS, -1, -1
7227 Yfake_grass, FALSE, FALSE,
7228 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7232 Xfake_amoeba, TRUE, FALSE,
7233 EL_EMC_DRIPPER, -1, -1
7236 Yfake_amoeba, FALSE, FALSE,
7237 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7241 Xlenses, TRUE, FALSE,
7242 EL_EMC_LENSES, -1, -1
7246 Xmagnify, TRUE, FALSE,
7247 EL_EMC_MAGNIFIER, -1, -1
7252 EL_QUICKSAND_EMPTY, -1, -1
7255 Xsand_stone, TRUE, FALSE,
7256 EL_QUICKSAND_FULL, -1, -1
7259 Xsand_stonein_1, FALSE, TRUE,
7260 EL_ROCK, ACTION_FILLING, -1
7263 Xsand_stonein_2, FALSE, TRUE,
7264 EL_ROCK, ACTION_FILLING, -1
7267 Xsand_stonein_3, FALSE, TRUE,
7268 EL_ROCK, ACTION_FILLING, -1
7271 Xsand_stonein_4, FALSE, TRUE,
7272 EL_ROCK, ACTION_FILLING, -1
7275 Xsand_sandstone_1, FALSE, FALSE,
7276 EL_QUICKSAND_FILLING, -1, -1
7279 Xsand_sandstone_2, FALSE, FALSE,
7280 EL_QUICKSAND_FILLING, -1, -1
7283 Xsand_sandstone_3, FALSE, FALSE,
7284 EL_QUICKSAND_FILLING, -1, -1
7287 Xsand_sandstone_4, FALSE, FALSE,
7288 EL_QUICKSAND_FILLING, -1, -1
7291 Xsand_stonesand_1, FALSE, FALSE,
7292 EL_QUICKSAND_EMPTYING, -1, -1
7295 Xsand_stonesand_2, FALSE, FALSE,
7296 EL_QUICKSAND_EMPTYING, -1, -1
7299 Xsand_stonesand_3, FALSE, FALSE,
7300 EL_QUICKSAND_EMPTYING, -1, -1
7303 Xsand_stonesand_4, FALSE, FALSE,
7304 EL_QUICKSAND_EMPTYING, -1, -1
7307 Xsand_stoneout_1, FALSE, FALSE,
7308 EL_ROCK, ACTION_EMPTYING, -1
7311 Xsand_stoneout_2, FALSE, FALSE,
7312 EL_ROCK, ACTION_EMPTYING, -1
7315 Xsand_stonesand_quickout_1, FALSE, FALSE,
7316 EL_QUICKSAND_EMPTYING, -1, -1
7319 Xsand_stonesand_quickout_2, FALSE, FALSE,
7320 EL_QUICKSAND_EMPTYING, -1, -1
7324 Xslide_ns, TRUE, FALSE,
7325 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7328 Yslide_ns_blank, FALSE, FALSE,
7329 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7332 Xslide_ew, TRUE, FALSE,
7333 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7336 Yslide_ew_blank, FALSE, FALSE,
7337 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7341 Xwind_n, TRUE, FALSE,
7342 EL_BALLOON_SWITCH_UP, -1, -1
7345 Xwind_e, TRUE, FALSE,
7346 EL_BALLOON_SWITCH_RIGHT, -1, -1
7349 Xwind_s, TRUE, FALSE,
7350 EL_BALLOON_SWITCH_DOWN, -1, -1
7353 Xwind_w, TRUE, FALSE,
7354 EL_BALLOON_SWITCH_LEFT, -1, -1
7357 Xwind_any, TRUE, FALSE,
7358 EL_BALLOON_SWITCH_ANY, -1, -1
7361 Xwind_stop, TRUE, FALSE,
7362 EL_BALLOON_SWITCH_NONE, -1, -1
7367 EL_EM_EXIT_CLOSED, -1, -1
7370 Xexit_1, TRUE, FALSE,
7371 EL_EM_EXIT_OPEN, -1, -1
7374 Xexit_2, FALSE, FALSE,
7375 EL_EM_EXIT_OPEN, -1, -1
7378 Xexit_3, FALSE, FALSE,
7379 EL_EM_EXIT_OPEN, -1, -1
7383 Xpause, FALSE, FALSE,
7388 Xwall_1, TRUE, FALSE,
7392 Xwall_2, TRUE, FALSE,
7393 EL_EMC_WALL_14, -1, -1
7396 Xwall_3, TRUE, FALSE,
7397 EL_EMC_WALL_15, -1, -1
7400 Xwall_4, TRUE, FALSE,
7401 EL_EMC_WALL_16, -1, -1
7405 Xroundwall_1, TRUE, FALSE,
7406 EL_WALL_SLIPPERY, -1, -1
7409 Xroundwall_2, TRUE, FALSE,
7410 EL_EMC_WALL_SLIPPERY_2, -1, -1
7413 Xroundwall_3, TRUE, FALSE,
7414 EL_EMC_WALL_SLIPPERY_3, -1, -1
7417 Xroundwall_4, TRUE, FALSE,
7418 EL_EMC_WALL_SLIPPERY_4, -1, -1
7422 Xsteel_1, TRUE, FALSE,
7423 EL_STEELWALL, -1, -1
7426 Xsteel_2, TRUE, FALSE,
7427 EL_EMC_STEELWALL_2, -1, -1
7430 Xsteel_3, TRUE, FALSE,
7431 EL_EMC_STEELWALL_3, -1, -1
7434 Xsteel_4, TRUE, FALSE,
7435 EL_EMC_STEELWALL_4, -1, -1
7439 Xdecor_1, TRUE, FALSE,
7440 EL_EMC_WALL_8, -1, -1
7443 Xdecor_2, TRUE, FALSE,
7444 EL_EMC_WALL_6, -1, -1
7447 Xdecor_3, TRUE, FALSE,
7448 EL_EMC_WALL_4, -1, -1
7451 Xdecor_4, TRUE, FALSE,
7452 EL_EMC_WALL_7, -1, -1
7455 Xdecor_5, TRUE, FALSE,
7456 EL_EMC_WALL_5, -1, -1
7459 Xdecor_6, TRUE, FALSE,
7460 EL_EMC_WALL_9, -1, -1
7463 Xdecor_7, TRUE, FALSE,
7464 EL_EMC_WALL_10, -1, -1
7467 Xdecor_8, TRUE, FALSE,
7468 EL_EMC_WALL_1, -1, -1
7471 Xdecor_9, TRUE, FALSE,
7472 EL_EMC_WALL_2, -1, -1
7475 Xdecor_10, TRUE, FALSE,
7476 EL_EMC_WALL_3, -1, -1
7479 Xdecor_11, TRUE, FALSE,
7480 EL_EMC_WALL_11, -1, -1
7483 Xdecor_12, TRUE, FALSE,
7484 EL_EMC_WALL_12, -1, -1
7488 Xalpha_0, TRUE, FALSE,
7489 EL_CHAR('0'), -1, -1
7492 Xalpha_1, TRUE, FALSE,
7493 EL_CHAR('1'), -1, -1
7496 Xalpha_2, TRUE, FALSE,
7497 EL_CHAR('2'), -1, -1
7500 Xalpha_3, TRUE, FALSE,
7501 EL_CHAR('3'), -1, -1
7504 Xalpha_4, TRUE, FALSE,
7505 EL_CHAR('4'), -1, -1
7508 Xalpha_5, TRUE, FALSE,
7509 EL_CHAR('5'), -1, -1
7512 Xalpha_6, TRUE, FALSE,
7513 EL_CHAR('6'), -1, -1
7516 Xalpha_7, TRUE, FALSE,
7517 EL_CHAR('7'), -1, -1
7520 Xalpha_8, TRUE, FALSE,
7521 EL_CHAR('8'), -1, -1
7524 Xalpha_9, TRUE, FALSE,
7525 EL_CHAR('9'), -1, -1
7528 Xalpha_excla, TRUE, FALSE,
7529 EL_CHAR('!'), -1, -1
7532 Xalpha_apost, TRUE, FALSE,
7533 EL_CHAR('\''), -1, -1
7536 Xalpha_comma, TRUE, FALSE,
7537 EL_CHAR(','), -1, -1
7540 Xalpha_minus, TRUE, FALSE,
7541 EL_CHAR('-'), -1, -1
7544 Xalpha_perio, TRUE, FALSE,
7545 EL_CHAR('.'), -1, -1
7548 Xalpha_colon, TRUE, FALSE,
7549 EL_CHAR(':'), -1, -1
7552 Xalpha_quest, TRUE, FALSE,
7553 EL_CHAR('?'), -1, -1
7556 Xalpha_a, TRUE, FALSE,
7557 EL_CHAR('A'), -1, -1
7560 Xalpha_b, TRUE, FALSE,
7561 EL_CHAR('B'), -1, -1
7564 Xalpha_c, TRUE, FALSE,
7565 EL_CHAR('C'), -1, -1
7568 Xalpha_d, TRUE, FALSE,
7569 EL_CHAR('D'), -1, -1
7572 Xalpha_e, TRUE, FALSE,
7573 EL_CHAR('E'), -1, -1
7576 Xalpha_f, TRUE, FALSE,
7577 EL_CHAR('F'), -1, -1
7580 Xalpha_g, TRUE, FALSE,
7581 EL_CHAR('G'), -1, -1
7584 Xalpha_h, TRUE, FALSE,
7585 EL_CHAR('H'), -1, -1
7588 Xalpha_i, TRUE, FALSE,
7589 EL_CHAR('I'), -1, -1
7592 Xalpha_j, TRUE, FALSE,
7593 EL_CHAR('J'), -1, -1
7596 Xalpha_k, TRUE, FALSE,
7597 EL_CHAR('K'), -1, -1
7600 Xalpha_l, TRUE, FALSE,
7601 EL_CHAR('L'), -1, -1
7604 Xalpha_m, TRUE, FALSE,
7605 EL_CHAR('M'), -1, -1
7608 Xalpha_n, TRUE, FALSE,
7609 EL_CHAR('N'), -1, -1
7612 Xalpha_o, TRUE, FALSE,
7613 EL_CHAR('O'), -1, -1
7616 Xalpha_p, TRUE, FALSE,
7617 EL_CHAR('P'), -1, -1
7620 Xalpha_q, TRUE, FALSE,
7621 EL_CHAR('Q'), -1, -1
7624 Xalpha_r, TRUE, FALSE,
7625 EL_CHAR('R'), -1, -1
7628 Xalpha_s, TRUE, FALSE,
7629 EL_CHAR('S'), -1, -1
7632 Xalpha_t, TRUE, FALSE,
7633 EL_CHAR('T'), -1, -1
7636 Xalpha_u, TRUE, FALSE,
7637 EL_CHAR('U'), -1, -1
7640 Xalpha_v, TRUE, FALSE,
7641 EL_CHAR('V'), -1, -1
7644 Xalpha_w, TRUE, FALSE,
7645 EL_CHAR('W'), -1, -1
7648 Xalpha_x, TRUE, FALSE,
7649 EL_CHAR('X'), -1, -1
7652 Xalpha_y, TRUE, FALSE,
7653 EL_CHAR('Y'), -1, -1
7656 Xalpha_z, TRUE, FALSE,
7657 EL_CHAR('Z'), -1, -1
7660 Xalpha_arrow_e, TRUE, FALSE,
7661 EL_CHAR('>'), -1, -1
7664 Xalpha_arrow_w, TRUE, FALSE,
7665 EL_CHAR('<'), -1, -1
7668 Xalpha_copyr, TRUE, FALSE,
7669 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7673 Ykey_1_blank, FALSE, FALSE,
7674 EL_EM_KEY_1, ACTION_COLLECTING, -1
7677 Ykey_2_blank, FALSE, FALSE,
7678 EL_EM_KEY_2, ACTION_COLLECTING, -1
7681 Ykey_3_blank, FALSE, FALSE,
7682 EL_EM_KEY_3, ACTION_COLLECTING, -1
7685 Ykey_4_blank, FALSE, FALSE,
7686 EL_EM_KEY_4, ACTION_COLLECTING, -1
7689 Ykey_5_blank, FALSE, FALSE,
7690 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7693 Ykey_6_blank, FALSE, FALSE,
7694 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7697 Ykey_7_blank, FALSE, FALSE,
7698 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7701 Ykey_8_blank, FALSE, FALSE,
7702 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7705 Ylenses_blank, FALSE, FALSE,
7706 EL_EMC_LENSES, ACTION_COLLECTING, -1
7709 Ymagnify_blank, FALSE, FALSE,
7710 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7713 Ygrass_blank, FALSE, FALSE,
7714 EL_EMC_GRASS, ACTION_SNAPPING, -1
7717 Ydirt_blank, FALSE, FALSE,
7718 EL_SAND, ACTION_SNAPPING, -1
7727 static struct Mapping_EM_to_RND_player
7736 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7740 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7744 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7748 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7752 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7756 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7760 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7764 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7768 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7772 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7776 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7780 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7784 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7788 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7792 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7796 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7800 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7804 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7808 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7812 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7816 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7820 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7824 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7828 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7832 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7836 EL_PLAYER_1, ACTION_DEFAULT, -1,
7840 EL_PLAYER_2, ACTION_DEFAULT, -1,
7844 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7848 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7852 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7856 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7860 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7864 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7868 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7872 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7876 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7880 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7884 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7888 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7892 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7896 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7900 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7904 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7908 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7912 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7916 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7920 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7924 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7928 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7932 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7936 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7940 EL_PLAYER_3, ACTION_DEFAULT, -1,
7944 EL_PLAYER_4, ACTION_DEFAULT, -1,
7953 int map_element_RND_to_EM_cave(int element_rnd)
7955 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7956 static boolean mapping_initialized = FALSE;
7958 if (!mapping_initialized)
7962 // return "Xalpha_quest" for all undefined elements in mapping array
7963 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7964 mapping_RND_to_EM[i] = Xalpha_quest;
7966 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7967 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7968 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7969 em_object_mapping_list[i].element_em;
7971 mapping_initialized = TRUE;
7974 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7976 Warn("invalid RND level element %d", element_rnd);
7981 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7984 int map_element_EM_to_RND_cave(int element_em_cave)
7986 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7987 static boolean mapping_initialized = FALSE;
7989 if (!mapping_initialized)
7993 // return "EL_UNKNOWN" for all undefined elements in mapping array
7994 for (i = 0; i < GAME_TILE_MAX; i++)
7995 mapping_EM_to_RND[i] = EL_UNKNOWN;
7997 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7998 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7999 em_object_mapping_list[i].element_rnd;
8001 mapping_initialized = TRUE;
8004 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8006 Warn("invalid EM cave element %d", element_em_cave);
8011 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8014 int map_element_EM_to_RND_game(int element_em_game)
8016 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8017 static boolean mapping_initialized = FALSE;
8019 if (!mapping_initialized)
8023 // return "EL_UNKNOWN" for all undefined elements in mapping array
8024 for (i = 0; i < GAME_TILE_MAX; i++)
8025 mapping_EM_to_RND[i] = EL_UNKNOWN;
8027 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8028 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8029 em_object_mapping_list[i].element_rnd;
8031 mapping_initialized = TRUE;
8034 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8036 Warn("invalid EM game element %d", element_em_game);
8041 return mapping_EM_to_RND[element_em_game];
8044 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8046 struct LevelInfo_EM *level_em = level->native_em_level;
8047 struct CAVE *cav = level_em->cav;
8050 for (i = 0; i < GAME_TILE_MAX; i++)
8051 cav->android_array[i] = Cblank;
8053 for (i = 0; i < level->num_android_clone_elements; i++)
8055 int element_rnd = level->android_clone_element[i];
8056 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8058 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8059 if (em_object_mapping_list[j].element_rnd == element_rnd)
8060 cav->android_array[em_object_mapping_list[j].element_em] =
8065 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8067 struct LevelInfo_EM *level_em = level->native_em_level;
8068 struct CAVE *cav = level_em->cav;
8071 level->num_android_clone_elements = 0;
8073 for (i = 0; i < GAME_TILE_MAX; i++)
8075 int element_em_cave = cav->android_array[i];
8077 boolean element_found = FALSE;
8079 if (element_em_cave == Cblank)
8082 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8084 for (j = 0; j < level->num_android_clone_elements; j++)
8085 if (level->android_clone_element[j] == element_rnd)
8086 element_found = TRUE;
8090 level->android_clone_element[level->num_android_clone_elements++] =
8093 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8098 if (level->num_android_clone_elements == 0)
8100 level->num_android_clone_elements = 1;
8101 level->android_clone_element[0] = EL_EMPTY;
8105 int map_direction_RND_to_EM(int direction)
8107 return (direction == MV_UP ? 0 :
8108 direction == MV_RIGHT ? 1 :
8109 direction == MV_DOWN ? 2 :
8110 direction == MV_LEFT ? 3 :
8114 int map_direction_EM_to_RND(int direction)
8116 return (direction == 0 ? MV_UP :
8117 direction == 1 ? MV_RIGHT :
8118 direction == 2 ? MV_DOWN :
8119 direction == 3 ? MV_LEFT :
8123 int map_element_RND_to_SP(int element_rnd)
8125 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8127 if (element_rnd >= EL_SP_START &&
8128 element_rnd <= EL_SP_END)
8129 element_sp = element_rnd - EL_SP_START;
8130 else if (element_rnd == EL_EMPTY_SPACE)
8132 else if (element_rnd == EL_INVISIBLE_WALL)
8138 int map_element_SP_to_RND(int element_sp)
8140 int element_rnd = EL_UNKNOWN;
8142 if (element_sp >= 0x00 &&
8144 element_rnd = EL_SP_START + element_sp;
8145 else if (element_sp == 0x28)
8146 element_rnd = EL_INVISIBLE_WALL;
8151 int map_action_SP_to_RND(int action_sp)
8155 case actActive: return ACTION_ACTIVE;
8156 case actImpact: return ACTION_IMPACT;
8157 case actExploding: return ACTION_EXPLODING;
8158 case actDigging: return ACTION_DIGGING;
8159 case actSnapping: return ACTION_SNAPPING;
8160 case actCollecting: return ACTION_COLLECTING;
8161 case actPassing: return ACTION_PASSING;
8162 case actPushing: return ACTION_PUSHING;
8163 case actDropping: return ACTION_DROPPING;
8165 default: return ACTION_DEFAULT;
8169 int map_element_RND_to_MM(int element_rnd)
8171 return (element_rnd >= EL_MM_START_1 &&
8172 element_rnd <= EL_MM_END_1 ?
8173 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8175 element_rnd >= EL_MM_START_2 &&
8176 element_rnd <= EL_MM_END_2 ?
8177 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8179 element_rnd >= EL_CHAR_START &&
8180 element_rnd <= EL_CHAR_END ?
8181 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8183 element_rnd >= EL_MM_RUNTIME_START &&
8184 element_rnd <= EL_MM_RUNTIME_END ?
8185 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8187 element_rnd >= EL_MM_DUMMY_START &&
8188 element_rnd <= EL_MM_DUMMY_END ?
8189 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8191 EL_MM_EMPTY_NATIVE);
8194 int map_element_MM_to_RND(int element_mm)
8196 return (element_mm == EL_MM_EMPTY_NATIVE ||
8197 element_mm == EL_DF_EMPTY_NATIVE ?
8200 element_mm >= EL_MM_START_1_NATIVE &&
8201 element_mm <= EL_MM_END_1_NATIVE ?
8202 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8204 element_mm >= EL_MM_START_2_NATIVE &&
8205 element_mm <= EL_MM_END_2_NATIVE ?
8206 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8208 element_mm >= EL_MM_CHAR_START_NATIVE &&
8209 element_mm <= EL_MM_CHAR_END_NATIVE ?
8210 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8212 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8213 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8214 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8216 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8217 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8218 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8223 int map_action_MM_to_RND(int action_mm)
8225 // all MM actions are defined to exactly match their RND counterparts
8229 int map_sound_MM_to_RND(int sound_mm)
8233 case SND_MM_GAME_LEVELTIME_CHARGING:
8234 return SND_GAME_LEVELTIME_CHARGING;
8236 case SND_MM_GAME_HEALTH_CHARGING:
8237 return SND_GAME_HEALTH_CHARGING;
8240 return SND_UNDEFINED;
8244 int map_mm_wall_element(int element)
8246 return (element >= EL_MM_STEEL_WALL_START &&
8247 element <= EL_MM_STEEL_WALL_END ?
8250 element >= EL_MM_WOODEN_WALL_START &&
8251 element <= EL_MM_WOODEN_WALL_END ?
8254 element >= EL_MM_ICE_WALL_START &&
8255 element <= EL_MM_ICE_WALL_END ?
8258 element >= EL_MM_AMOEBA_WALL_START &&
8259 element <= EL_MM_AMOEBA_WALL_END ?
8262 element >= EL_DF_STEEL_WALL_START &&
8263 element <= EL_DF_STEEL_WALL_END ?
8266 element >= EL_DF_WOODEN_WALL_START &&
8267 element <= EL_DF_WOODEN_WALL_END ?
8273 int map_mm_wall_element_editor(int element)
8277 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8278 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8279 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8280 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8281 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8282 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8284 default: return element;
8288 int get_next_element(int element)
8292 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8293 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8294 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8295 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8296 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8297 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8298 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8299 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8300 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8301 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8302 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8304 default: return element;
8308 int el2img_mm(int element_mm)
8310 return el2img(map_element_MM_to_RND(element_mm));
8313 int el_act_dir2img(int element, int action, int direction)
8315 element = GFX_ELEMENT(element);
8316 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8318 // direction_graphic[][] == graphic[] for undefined direction graphics
8319 return element_info[element].direction_graphic[action][direction];
8322 static int el_act_dir2crm(int element, int action, int direction)
8324 element = GFX_ELEMENT(element);
8325 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8327 // direction_graphic[][] == graphic[] for undefined direction graphics
8328 return element_info[element].direction_crumbled[action][direction];
8331 int el_act2img(int element, int action)
8333 element = GFX_ELEMENT(element);
8335 return element_info[element].graphic[action];
8338 int el_act2crm(int element, int action)
8340 element = GFX_ELEMENT(element);
8342 return element_info[element].crumbled[action];
8345 int el_dir2img(int element, int direction)
8347 element = GFX_ELEMENT(element);
8349 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8352 int el2baseimg(int element)
8354 return element_info[element].graphic[ACTION_DEFAULT];
8357 int el2img(int element)
8359 element = GFX_ELEMENT(element);
8361 return element_info[element].graphic[ACTION_DEFAULT];
8364 int el2edimg(int element)
8366 element = GFX_ELEMENT(element);
8368 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8371 int el2preimg(int element)
8373 element = GFX_ELEMENT(element);
8375 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8378 int el2panelimg(int element)
8380 element = GFX_ELEMENT(element);
8382 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8385 int font2baseimg(int font_nr)
8387 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8390 int getBeltNrFromBeltElement(int element)
8392 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8393 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8394 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8397 int getBeltNrFromBeltActiveElement(int element)
8399 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8400 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8401 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8404 int getBeltNrFromBeltSwitchElement(int element)
8406 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8407 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8408 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8411 int getBeltDirNrFromBeltElement(int element)
8413 static int belt_base_element[4] =
8415 EL_CONVEYOR_BELT_1_LEFT,
8416 EL_CONVEYOR_BELT_2_LEFT,
8417 EL_CONVEYOR_BELT_3_LEFT,
8418 EL_CONVEYOR_BELT_4_LEFT
8421 int belt_nr = getBeltNrFromBeltElement(element);
8422 int belt_dir_nr = element - belt_base_element[belt_nr];
8424 return (belt_dir_nr % 3);
8427 int getBeltDirNrFromBeltSwitchElement(int element)
8429 static int belt_base_element[4] =
8431 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8432 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8433 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8434 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8437 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8438 int belt_dir_nr = element - belt_base_element[belt_nr];
8440 return (belt_dir_nr % 3);
8443 int getBeltDirFromBeltElement(int element)
8445 static int belt_move_dir[3] =
8452 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8454 return belt_move_dir[belt_dir_nr];
8457 int getBeltDirFromBeltSwitchElement(int element)
8459 static int belt_move_dir[3] =
8466 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8468 return belt_move_dir[belt_dir_nr];
8471 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8473 static int belt_base_element[4] =
8475 EL_CONVEYOR_BELT_1_LEFT,
8476 EL_CONVEYOR_BELT_2_LEFT,
8477 EL_CONVEYOR_BELT_3_LEFT,
8478 EL_CONVEYOR_BELT_4_LEFT
8481 return belt_base_element[belt_nr] + belt_dir_nr;
8484 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8486 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8488 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8491 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8493 static int belt_base_element[4] =
8495 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8496 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8497 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8498 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8501 return belt_base_element[belt_nr] + belt_dir_nr;
8504 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8506 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8508 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8511 boolean swapTiles_EM(boolean is_pre_emc_cave)
8513 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8516 boolean getTeamMode_EM(void)
8518 return game.team_mode || network_playing;
8521 boolean isActivePlayer_EM(int player_nr)
8523 return stored_player[player_nr].active;
8526 unsigned int InitRND(int seed)
8528 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8529 return InitEngineRandom_EM(seed);
8530 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8531 return InitEngineRandom_SP(seed);
8532 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8533 return InitEngineRandom_MM(seed);
8535 return InitEngineRandom_RND(seed);
8538 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8539 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8541 static int get_effective_element_EM(int tile, int frame_em)
8543 int element = object_mapping[tile].element_rnd;
8544 int action = object_mapping[tile].action;
8545 boolean is_backside = object_mapping[tile].is_backside;
8546 boolean action_removing = (action == ACTION_DIGGING ||
8547 action == ACTION_SNAPPING ||
8548 action == ACTION_COLLECTING);
8556 return (frame_em > 5 ? EL_EMPTY : element);
8562 else // frame_em == 7
8573 case Ydiamond_stone:
8577 case Xdrip_stretchB:
8593 case Ymagnify_blank:
8596 case Xsand_stonein_1:
8597 case Xsand_stonein_2:
8598 case Xsand_stonein_3:
8599 case Xsand_stonein_4:
8603 return (is_backside || action_removing ? EL_EMPTY : element);
8608 static boolean check_linear_animation_EM(int tile)
8612 case Xsand_stonesand_1:
8613 case Xsand_stonesand_quickout_1:
8614 case Xsand_sandstone_1:
8615 case Xsand_stonein_1:
8616 case Xsand_stoneout_1:
8644 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8645 boolean has_crumbled_graphics,
8646 int crumbled, int sync_frame)
8648 // if element can be crumbled, but certain action graphics are just empty
8649 // space (like instantly snapping sand to empty space in 1 frame), do not
8650 // treat these empty space graphics as crumbled graphics in EMC engine
8651 if (crumbled == IMG_EMPTY_SPACE)
8652 has_crumbled_graphics = FALSE;
8654 if (has_crumbled_graphics)
8656 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8657 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8658 g_crumbled->anim_delay,
8659 g_crumbled->anim_mode,
8660 g_crumbled->anim_start_frame,
8663 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8664 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8666 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8667 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8669 g_em->has_crumbled_graphics = TRUE;
8673 g_em->crumbled_bitmap = NULL;
8674 g_em->crumbled_src_x = 0;
8675 g_em->crumbled_src_y = 0;
8676 g_em->crumbled_border_size = 0;
8677 g_em->crumbled_tile_size = 0;
8679 g_em->has_crumbled_graphics = FALSE;
8684 void ResetGfxAnimation_EM(int x, int y, int tile)
8690 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8691 int tile, int frame_em, int x, int y)
8693 int action = object_mapping[tile].action;
8694 int direction = object_mapping[tile].direction;
8695 int effective_element = get_effective_element_EM(tile, frame_em);
8696 int graphic = (direction == MV_NONE ?
8697 el_act2img(effective_element, action) :
8698 el_act_dir2img(effective_element, action, direction));
8699 struct GraphicInfo *g = &graphic_info[graphic];
8701 boolean action_removing = (action == ACTION_DIGGING ||
8702 action == ACTION_SNAPPING ||
8703 action == ACTION_COLLECTING);
8704 boolean action_moving = (action == ACTION_FALLING ||
8705 action == ACTION_MOVING ||
8706 action == ACTION_PUSHING ||
8707 action == ACTION_EATING ||
8708 action == ACTION_FILLING ||
8709 action == ACTION_EMPTYING);
8710 boolean action_falling = (action == ACTION_FALLING ||
8711 action == ACTION_FILLING ||
8712 action == ACTION_EMPTYING);
8714 // special case: graphic uses "2nd movement tile" and has defined
8715 // 7 frames for movement animation (or less) => use default graphic
8716 // for last (8th) frame which ends the movement animation
8717 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8719 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8720 graphic = (direction == MV_NONE ?
8721 el_act2img(effective_element, action) :
8722 el_act_dir2img(effective_element, action, direction));
8724 g = &graphic_info[graphic];
8727 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8731 else if (action_moving)
8733 boolean is_backside = object_mapping[tile].is_backside;
8737 int direction = object_mapping[tile].direction;
8738 int move_dir = (action_falling ? MV_DOWN : direction);
8743 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8744 if (g->double_movement && frame_em == 0)
8748 if (move_dir == MV_LEFT)
8749 GfxFrame[x - 1][y] = GfxFrame[x][y];
8750 else if (move_dir == MV_RIGHT)
8751 GfxFrame[x + 1][y] = GfxFrame[x][y];
8752 else if (move_dir == MV_UP)
8753 GfxFrame[x][y - 1] = GfxFrame[x][y];
8754 else if (move_dir == MV_DOWN)
8755 GfxFrame[x][y + 1] = GfxFrame[x][y];
8762 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8763 if (tile == Xsand_stonesand_quickout_1 ||
8764 tile == Xsand_stonesand_quickout_2)
8768 if (graphic_info[graphic].anim_global_sync)
8769 sync_frame = FrameCounter;
8770 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8771 sync_frame = GfxFrame[x][y];
8773 sync_frame = 0; // playfield border (pseudo steel)
8775 SetRandomAnimationValue(x, y);
8777 int frame = getAnimationFrame(g->anim_frames,
8780 g->anim_start_frame,
8783 g_em->unique_identifier =
8784 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8787 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8788 int tile, int frame_em, int x, int y)
8790 int action = object_mapping[tile].action;
8791 int direction = object_mapping[tile].direction;
8792 boolean is_backside = object_mapping[tile].is_backside;
8793 int effective_element = get_effective_element_EM(tile, frame_em);
8794 int effective_action = action;
8795 int graphic = (direction == MV_NONE ?
8796 el_act2img(effective_element, effective_action) :
8797 el_act_dir2img(effective_element, effective_action,
8799 int crumbled = (direction == MV_NONE ?
8800 el_act2crm(effective_element, effective_action) :
8801 el_act_dir2crm(effective_element, effective_action,
8803 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8804 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8805 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8806 struct GraphicInfo *g = &graphic_info[graphic];
8809 // special case: graphic uses "2nd movement tile" and has defined
8810 // 7 frames for movement animation (or less) => use default graphic
8811 // for last (8th) frame which ends the movement animation
8812 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8814 effective_action = ACTION_DEFAULT;
8815 graphic = (direction == MV_NONE ?
8816 el_act2img(effective_element, effective_action) :
8817 el_act_dir2img(effective_element, effective_action,
8819 crumbled = (direction == MV_NONE ?
8820 el_act2crm(effective_element, effective_action) :
8821 el_act_dir2crm(effective_element, effective_action,
8824 g = &graphic_info[graphic];
8827 if (graphic_info[graphic].anim_global_sync)
8828 sync_frame = FrameCounter;
8829 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8830 sync_frame = GfxFrame[x][y];
8832 sync_frame = 0; // playfield border (pseudo steel)
8834 SetRandomAnimationValue(x, y);
8836 int frame = getAnimationFrame(g->anim_frames,
8839 g->anim_start_frame,
8842 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8843 g->double_movement && is_backside);
8845 // (updating the "crumbled" graphic definitions is probably not really needed,
8846 // as animations for crumbled graphics can't be longer than one EMC cycle)
8847 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8851 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8852 int player_nr, int anim, int frame_em)
8854 int element = player_mapping[player_nr][anim].element_rnd;
8855 int action = player_mapping[player_nr][anim].action;
8856 int direction = player_mapping[player_nr][anim].direction;
8857 int graphic = (direction == MV_NONE ?
8858 el_act2img(element, action) :
8859 el_act_dir2img(element, action, direction));
8860 struct GraphicInfo *g = &graphic_info[graphic];
8863 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8865 stored_player[player_nr].StepFrame = frame_em;
8867 sync_frame = stored_player[player_nr].Frame;
8869 int frame = getAnimationFrame(g->anim_frames,
8872 g->anim_start_frame,
8875 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8876 &g_em->src_x, &g_em->src_y, FALSE);
8879 void InitGraphicInfo_EM(void)
8883 // always start with reliable default values
8884 for (i = 0; i < GAME_TILE_MAX; i++)
8886 object_mapping[i].element_rnd = EL_UNKNOWN;
8887 object_mapping[i].is_backside = FALSE;
8888 object_mapping[i].action = ACTION_DEFAULT;
8889 object_mapping[i].direction = MV_NONE;
8892 // always start with reliable default values
8893 for (p = 0; p < MAX_PLAYERS; p++)
8895 for (i = 0; i < PLY_MAX; i++)
8897 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8898 player_mapping[p][i].action = ACTION_DEFAULT;
8899 player_mapping[p][i].direction = MV_NONE;
8903 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8905 int e = em_object_mapping_list[i].element_em;
8907 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8908 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8910 if (em_object_mapping_list[i].action != -1)
8911 object_mapping[e].action = em_object_mapping_list[i].action;
8913 if (em_object_mapping_list[i].direction != -1)
8914 object_mapping[e].direction =
8915 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8918 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8920 int a = em_player_mapping_list[i].action_em;
8921 int p = em_player_mapping_list[i].player_nr;
8923 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8925 if (em_player_mapping_list[i].action != -1)
8926 player_mapping[p][a].action = em_player_mapping_list[i].action;
8928 if (em_player_mapping_list[i].direction != -1)
8929 player_mapping[p][a].direction =
8930 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8933 for (i = 0; i < GAME_TILE_MAX; i++)
8935 int element = object_mapping[i].element_rnd;
8936 int action = object_mapping[i].action;
8937 int direction = object_mapping[i].direction;
8938 boolean is_backside = object_mapping[i].is_backside;
8939 boolean action_exploding = ((action == ACTION_EXPLODING ||
8940 action == ACTION_SMASHED_BY_ROCK ||
8941 action == ACTION_SMASHED_BY_SPRING) &&
8942 element != EL_DIAMOND);
8943 boolean action_active = (action == ACTION_ACTIVE);
8944 boolean action_other = (action == ACTION_OTHER);
8946 for (j = 0; j < 8; j++)
8948 int effective_element = get_effective_element_EM(i, j);
8949 int effective_action = (j < 7 ? action :
8950 i == Xdrip_stretch ? action :
8951 i == Xdrip_stretchB ? action :
8952 i == Ydrip_1_s ? action :
8953 i == Ydrip_1_sB ? action :
8954 i == Yball_1 ? action :
8955 i == Xball_2 ? action :
8956 i == Yball_2 ? action :
8957 i == Yball_blank ? action :
8958 i == Ykey_1_blank ? action :
8959 i == Ykey_2_blank ? action :
8960 i == Ykey_3_blank ? action :
8961 i == Ykey_4_blank ? action :
8962 i == Ykey_5_blank ? action :
8963 i == Ykey_6_blank ? action :
8964 i == Ykey_7_blank ? action :
8965 i == Ykey_8_blank ? action :
8966 i == Ylenses_blank ? action :
8967 i == Ymagnify_blank ? action :
8968 i == Ygrass_blank ? action :
8969 i == Ydirt_blank ? action :
8970 i == Xsand_stonein_1 ? action :
8971 i == Xsand_stonein_2 ? action :
8972 i == Xsand_stonein_3 ? action :
8973 i == Xsand_stonein_4 ? action :
8974 i == Xsand_stoneout_1 ? action :
8975 i == Xsand_stoneout_2 ? action :
8976 i == Xboom_android ? ACTION_EXPLODING :
8977 action_exploding ? ACTION_EXPLODING :
8978 action_active ? action :
8979 action_other ? action :
8981 int graphic = (el_act_dir2img(effective_element, effective_action,
8983 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8985 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8986 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8987 boolean has_action_graphics = (graphic != base_graphic);
8988 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8989 struct GraphicInfo *g = &graphic_info[graphic];
8990 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8993 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8994 boolean special_animation = (action != ACTION_DEFAULT &&
8995 g->anim_frames == 3 &&
8996 g->anim_delay == 2 &&
8997 g->anim_mode & ANIM_LINEAR);
8998 int sync_frame = (i == Xdrip_stretch ? 7 :
8999 i == Xdrip_stretchB ? 7 :
9000 i == Ydrip_2_s ? j + 8 :
9001 i == Ydrip_2_sB ? j + 8 :
9010 i == Xfake_acid_1 ? 0 :
9011 i == Xfake_acid_2 ? 10 :
9012 i == Xfake_acid_3 ? 20 :
9013 i == Xfake_acid_4 ? 30 :
9014 i == Xfake_acid_5 ? 40 :
9015 i == Xfake_acid_6 ? 50 :
9016 i == Xfake_acid_7 ? 60 :
9017 i == Xfake_acid_8 ? 70 :
9018 i == Xfake_acid_1_player ? 0 :
9019 i == Xfake_acid_2_player ? 10 :
9020 i == Xfake_acid_3_player ? 20 :
9021 i == Xfake_acid_4_player ? 30 :
9022 i == Xfake_acid_5_player ? 40 :
9023 i == Xfake_acid_6_player ? 50 :
9024 i == Xfake_acid_7_player ? 60 :
9025 i == Xfake_acid_8_player ? 70 :
9027 i == Yball_2 ? j + 8 :
9028 i == Yball_blank ? j + 1 :
9029 i == Ykey_1_blank ? j + 1 :
9030 i == Ykey_2_blank ? j + 1 :
9031 i == Ykey_3_blank ? j + 1 :
9032 i == Ykey_4_blank ? j + 1 :
9033 i == Ykey_5_blank ? j + 1 :
9034 i == Ykey_6_blank ? j + 1 :
9035 i == Ykey_7_blank ? j + 1 :
9036 i == Ykey_8_blank ? j + 1 :
9037 i == Ylenses_blank ? j + 1 :
9038 i == Ymagnify_blank ? j + 1 :
9039 i == Ygrass_blank ? j + 1 :
9040 i == Ydirt_blank ? j + 1 :
9041 i == Xamoeba_1 ? 0 :
9042 i == Xamoeba_2 ? 1 :
9043 i == Xamoeba_3 ? 2 :
9044 i == Xamoeba_4 ? 3 :
9045 i == Xamoeba_5 ? 0 :
9046 i == Xamoeba_6 ? 1 :
9047 i == Xamoeba_7 ? 2 :
9048 i == Xamoeba_8 ? 3 :
9049 i == Xexit_2 ? j + 8 :
9050 i == Xexit_3 ? j + 16 :
9051 i == Xdynamite_1 ? 0 :
9052 i == Xdynamite_2 ? 8 :
9053 i == Xdynamite_3 ? 16 :
9054 i == Xdynamite_4 ? 24 :
9055 i == Xsand_stonein_1 ? j + 1 :
9056 i == Xsand_stonein_2 ? j + 9 :
9057 i == Xsand_stonein_3 ? j + 17 :
9058 i == Xsand_stonein_4 ? j + 25 :
9059 i == Xsand_stoneout_1 && j == 0 ? 0 :
9060 i == Xsand_stoneout_1 && j == 1 ? 0 :
9061 i == Xsand_stoneout_1 && j == 2 ? 1 :
9062 i == Xsand_stoneout_1 && j == 3 ? 2 :
9063 i == Xsand_stoneout_1 && j == 4 ? 2 :
9064 i == Xsand_stoneout_1 && j == 5 ? 3 :
9065 i == Xsand_stoneout_1 && j == 6 ? 4 :
9066 i == Xsand_stoneout_1 && j == 7 ? 4 :
9067 i == Xsand_stoneout_2 && j == 0 ? 5 :
9068 i == Xsand_stoneout_2 && j == 1 ? 6 :
9069 i == Xsand_stoneout_2 && j == 2 ? 7 :
9070 i == Xsand_stoneout_2 && j == 3 ? 8 :
9071 i == Xsand_stoneout_2 && j == 4 ? 9 :
9072 i == Xsand_stoneout_2 && j == 5 ? 11 :
9073 i == Xsand_stoneout_2 && j == 6 ? 13 :
9074 i == Xsand_stoneout_2 && j == 7 ? 15 :
9075 i == Xboom_bug && j == 1 ? 2 :
9076 i == Xboom_bug && j == 2 ? 2 :
9077 i == Xboom_bug && j == 3 ? 4 :
9078 i == Xboom_bug && j == 4 ? 4 :
9079 i == Xboom_bug && j == 5 ? 2 :
9080 i == Xboom_bug && j == 6 ? 2 :
9081 i == Xboom_bug && j == 7 ? 0 :
9082 i == Xboom_tank && j == 1 ? 2 :
9083 i == Xboom_tank && j == 2 ? 2 :
9084 i == Xboom_tank && j == 3 ? 4 :
9085 i == Xboom_tank && j == 4 ? 4 :
9086 i == Xboom_tank && j == 5 ? 2 :
9087 i == Xboom_tank && j == 6 ? 2 :
9088 i == Xboom_tank && j == 7 ? 0 :
9089 i == Xboom_android && j == 7 ? 6 :
9090 i == Xboom_1 && j == 1 ? 2 :
9091 i == Xboom_1 && j == 2 ? 2 :
9092 i == Xboom_1 && j == 3 ? 4 :
9093 i == Xboom_1 && j == 4 ? 4 :
9094 i == Xboom_1 && j == 5 ? 6 :
9095 i == Xboom_1 && j == 6 ? 6 :
9096 i == Xboom_1 && j == 7 ? 8 :
9097 i == Xboom_2 && j == 0 ? 8 :
9098 i == Xboom_2 && j == 1 ? 8 :
9099 i == Xboom_2 && j == 2 ? 10 :
9100 i == Xboom_2 && j == 3 ? 10 :
9101 i == Xboom_2 && j == 4 ? 10 :
9102 i == Xboom_2 && j == 5 ? 12 :
9103 i == Xboom_2 && j == 6 ? 12 :
9104 i == Xboom_2 && j == 7 ? 12 :
9105 special_animation && j == 4 ? 3 :
9106 effective_action != action ? 0 :
9108 int frame = getAnimationFrame(g->anim_frames,
9111 g->anim_start_frame,
9114 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9115 g->double_movement && is_backside);
9117 g_em->bitmap = src_bitmap;
9118 g_em->src_x = src_x;
9119 g_em->src_y = src_y;
9120 g_em->src_offset_x = 0;
9121 g_em->src_offset_y = 0;
9122 g_em->dst_offset_x = 0;
9123 g_em->dst_offset_y = 0;
9124 g_em->width = TILEX;
9125 g_em->height = TILEY;
9127 g_em->preserve_background = FALSE;
9129 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9132 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9133 effective_action == ACTION_MOVING ||
9134 effective_action == ACTION_PUSHING ||
9135 effective_action == ACTION_EATING)) ||
9136 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9137 effective_action == ACTION_EMPTYING)))
9140 (effective_action == ACTION_FALLING ||
9141 effective_action == ACTION_FILLING ||
9142 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9143 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9144 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9145 int num_steps = (i == Ydrip_1_s ? 16 :
9146 i == Ydrip_1_sB ? 16 :
9147 i == Ydrip_2_s ? 16 :
9148 i == Ydrip_2_sB ? 16 :
9149 i == Xsand_stonein_1 ? 32 :
9150 i == Xsand_stonein_2 ? 32 :
9151 i == Xsand_stonein_3 ? 32 :
9152 i == Xsand_stonein_4 ? 32 :
9153 i == Xsand_stoneout_1 ? 16 :
9154 i == Xsand_stoneout_2 ? 16 : 8);
9155 int cx = ABS(dx) * (TILEX / num_steps);
9156 int cy = ABS(dy) * (TILEY / num_steps);
9157 int step_frame = (i == Ydrip_2_s ? j + 8 :
9158 i == Ydrip_2_sB ? j + 8 :
9159 i == Xsand_stonein_2 ? j + 8 :
9160 i == Xsand_stonein_3 ? j + 16 :
9161 i == Xsand_stonein_4 ? j + 24 :
9162 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9163 int step = (is_backside ? step_frame : num_steps - step_frame);
9165 if (is_backside) // tile where movement starts
9167 if (dx < 0 || dy < 0)
9169 g_em->src_offset_x = cx * step;
9170 g_em->src_offset_y = cy * step;
9174 g_em->dst_offset_x = cx * step;
9175 g_em->dst_offset_y = cy * step;
9178 else // tile where movement ends
9180 if (dx < 0 || dy < 0)
9182 g_em->dst_offset_x = cx * step;
9183 g_em->dst_offset_y = cy * step;
9187 g_em->src_offset_x = cx * step;
9188 g_em->src_offset_y = cy * step;
9192 g_em->width = TILEX - cx * step;
9193 g_em->height = TILEY - cy * step;
9196 // create unique graphic identifier to decide if tile must be redrawn
9197 /* bit 31 - 16 (16 bit): EM style graphic
9198 bit 15 - 12 ( 4 bit): EM style frame
9199 bit 11 - 6 ( 6 bit): graphic width
9200 bit 5 - 0 ( 6 bit): graphic height */
9201 g_em->unique_identifier =
9202 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9206 for (i = 0; i < GAME_TILE_MAX; i++)
9208 for (j = 0; j < 8; j++)
9210 int element = object_mapping[i].element_rnd;
9211 int action = object_mapping[i].action;
9212 int direction = object_mapping[i].direction;
9213 boolean is_backside = object_mapping[i].is_backside;
9214 int graphic_action = el_act_dir2img(element, action, direction);
9215 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9217 if ((action == ACTION_SMASHED_BY_ROCK ||
9218 action == ACTION_SMASHED_BY_SPRING ||
9219 action == ACTION_EATING) &&
9220 graphic_action == graphic_default)
9222 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9223 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9224 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9225 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9228 // no separate animation for "smashed by rock" -- use rock instead
9229 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9230 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9232 g_em->bitmap = g_xx->bitmap;
9233 g_em->src_x = g_xx->src_x;
9234 g_em->src_y = g_xx->src_y;
9235 g_em->src_offset_x = g_xx->src_offset_x;
9236 g_em->src_offset_y = g_xx->src_offset_y;
9237 g_em->dst_offset_x = g_xx->dst_offset_x;
9238 g_em->dst_offset_y = g_xx->dst_offset_y;
9239 g_em->width = g_xx->width;
9240 g_em->height = g_xx->height;
9241 g_em->unique_identifier = g_xx->unique_identifier;
9244 g_em->preserve_background = TRUE;
9249 for (p = 0; p < MAX_PLAYERS; p++)
9251 for (i = 0; i < PLY_MAX; i++)
9253 int element = player_mapping[p][i].element_rnd;
9254 int action = player_mapping[p][i].action;
9255 int direction = player_mapping[p][i].direction;
9257 for (j = 0; j < 8; j++)
9259 int effective_element = element;
9260 int effective_action = action;
9261 int graphic = (direction == MV_NONE ?
9262 el_act2img(effective_element, effective_action) :
9263 el_act_dir2img(effective_element, effective_action,
9265 struct GraphicInfo *g = &graphic_info[graphic];
9266 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9270 int frame = getAnimationFrame(g->anim_frames,
9273 g->anim_start_frame,
9276 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9278 g_em->bitmap = src_bitmap;
9279 g_em->src_x = src_x;
9280 g_em->src_y = src_y;
9281 g_em->src_offset_x = 0;
9282 g_em->src_offset_y = 0;
9283 g_em->dst_offset_x = 0;
9284 g_em->dst_offset_y = 0;
9285 g_em->width = TILEX;
9286 g_em->height = TILEY;
9292 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9293 boolean any_player_moving,
9294 boolean any_player_snapping,
9295 boolean any_player_dropping)
9297 if (frame == 7 && !any_player_dropping)
9299 if (!local_player->was_waiting)
9301 if (!CheckSaveEngineSnapshotToList())
9304 local_player->was_waiting = TRUE;
9307 else if (any_player_moving || any_player_snapping || any_player_dropping)
9309 local_player->was_waiting = FALSE;
9313 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9314 boolean murphy_is_dropping)
9316 if (murphy_is_waiting)
9318 if (!local_player->was_waiting)
9320 if (!CheckSaveEngineSnapshotToList())
9323 local_player->was_waiting = TRUE;
9328 local_player->was_waiting = FALSE;
9332 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9333 boolean button_released)
9335 if (button_released)
9337 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9338 CheckSaveEngineSnapshotToList();
9340 else if (element_clicked)
9342 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9343 CheckSaveEngineSnapshotToList();
9345 game.snapshot.changed_action = TRUE;
9349 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9350 boolean any_player_moving,
9351 boolean any_player_snapping,
9352 boolean any_player_dropping)
9354 if (tape.single_step && tape.recording && !tape.pausing)
9355 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9356 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9358 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9359 any_player_snapping, any_player_dropping);
9361 return tape.pausing;
9364 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9365 boolean murphy_is_dropping)
9367 boolean murphy_starts_dropping = FALSE;
9370 for (i = 0; i < MAX_PLAYERS; i++)
9371 if (stored_player[i].force_dropping)
9372 murphy_starts_dropping = TRUE;
9374 if (tape.single_step && tape.recording && !tape.pausing)
9375 if (murphy_is_waiting && !murphy_starts_dropping)
9376 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9378 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9381 void CheckSingleStepMode_MM(boolean element_clicked,
9382 boolean button_released)
9384 if (tape.single_step && tape.recording && !tape.pausing)
9385 if (button_released)
9386 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9388 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9391 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9392 int graphic, int sync_frame, int x, int y)
9394 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9396 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9399 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9401 return (IS_NEXT_FRAME(sync_frame, graphic));
9404 int getGraphicInfo_Delay(int graphic)
9406 return graphic_info[graphic].anim_delay;
9409 void PlayMenuSoundExt(int sound)
9411 if (sound == SND_UNDEFINED)
9414 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9415 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9418 if (IS_LOOP_SOUND(sound))
9419 PlaySoundLoop(sound);
9424 void PlayMenuSound(void)
9426 PlayMenuSoundExt(menu.sound[game_status]);
9429 void PlayMenuSoundStereo(int sound, int stereo_position)
9431 if (sound == SND_UNDEFINED)
9434 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9435 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9438 if (IS_LOOP_SOUND(sound))
9439 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9441 PlaySoundStereo(sound, stereo_position);
9444 void PlayMenuSoundIfLoopExt(int sound)
9446 if (sound == SND_UNDEFINED)
9449 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9450 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9453 if (IS_LOOP_SOUND(sound))
9454 PlaySoundLoop(sound);
9457 void PlayMenuSoundIfLoop(void)
9459 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9462 void PlayMenuMusicExt(int music)
9464 if (music == MUS_UNDEFINED)
9467 if (!setup.sound_music)
9470 if (IS_LOOP_MUSIC(music))
9471 PlayMusicLoop(music);
9476 void PlayMenuMusic(void)
9478 char *curr_music = getCurrentlyPlayingMusicFilename();
9479 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9481 if (!strEqual(curr_music, next_music))
9482 PlayMenuMusicExt(menu.music[game_status]);
9485 void PlayMenuSoundsAndMusic(void)
9491 static void FadeMenuSounds(void)
9496 static void FadeMenuMusic(void)
9498 char *curr_music = getCurrentlyPlayingMusicFilename();
9499 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9501 if (!strEqual(curr_music, next_music))
9505 void FadeMenuSoundsAndMusic(void)
9511 void PlaySoundActivating(void)
9514 PlaySound(SND_MENU_ITEM_ACTIVATING);
9518 void PlaySoundSelecting(void)
9521 PlaySound(SND_MENU_ITEM_SELECTING);
9525 void ToggleFullscreenIfNeeded(void)
9527 // if setup and video fullscreen state are already matching, nothing do do
9528 if (setup.fullscreen == video.fullscreen_enabled ||
9529 !video.fullscreen_available)
9532 SDLSetWindowFullscreen(setup.fullscreen);
9534 // set setup value according to successfully changed fullscreen mode
9535 setup.fullscreen = video.fullscreen_enabled;
9538 void ChangeWindowScalingIfNeeded(void)
9540 // if setup and video window scaling are already matching, nothing do do
9541 if (setup.window_scaling_percent == video.window_scaling_percent ||
9542 video.fullscreen_enabled)
9545 SDLSetWindowScaling(setup.window_scaling_percent);
9547 // set setup value according to successfully changed window scaling
9548 setup.window_scaling_percent = video.window_scaling_percent;
9551 void ChangeVsyncModeIfNeeded(void)
9553 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9554 int video_vsync_mode = video.vsync_mode;
9556 // if setup and video vsync mode are already matching, nothing do do
9557 if (setup_vsync_mode == video_vsync_mode)
9560 // if renderer is using OpenGL, vsync mode can directly be changed
9561 SDLSetScreenVsyncMode(setup.vsync_mode);
9563 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9564 if (video.vsync_mode == video_vsync_mode)
9566 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9568 // save backbuffer content which gets lost when re-creating screen
9569 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9571 // force re-creating screen and renderer to set new vsync mode
9572 video.fullscreen_enabled = !setup.fullscreen;
9574 // when creating new renderer, destroy textures linked to old renderer
9575 FreeAllImageTextures(); // needs old renderer to free the textures
9577 // re-create screen and renderer (including change of vsync mode)
9578 ChangeVideoModeIfNeeded(setup.fullscreen);
9580 // set setup value according to successfully changed fullscreen mode
9581 setup.fullscreen = video.fullscreen_enabled;
9583 // restore backbuffer content from temporary backbuffer backup bitmap
9584 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9585 FreeBitmap(tmp_backbuffer);
9587 // update visible window/screen
9588 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9590 // when changing vsync mode, re-create textures for new renderer
9591 InitImageTextures();
9594 // set setup value according to successfully changed vsync mode
9595 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9598 static void JoinRectangles(int *x, int *y, int *width, int *height,
9599 int x2, int y2, int width2, int height2)
9601 // do not join with "off-screen" rectangle
9602 if (x2 == -1 || y2 == -1)
9607 *width = MAX(*width, width2);
9608 *height = MAX(*height, height2);
9611 void SetAnimStatus(int anim_status_new)
9613 if (anim_status_new == GAME_MODE_MAIN)
9614 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9615 else if (anim_status_new == GAME_MODE_NAMES)
9616 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9617 else if (anim_status_new == GAME_MODE_SCORES)
9618 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9620 global.anim_status_next = anim_status_new;
9622 // directly set screen modes that are entered without fading
9623 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9624 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9625 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9626 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9627 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9628 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9629 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9630 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9631 global.anim_status = global.anim_status_next;
9634 void SetGameStatus(int game_status_new)
9636 if (game_status_new != game_status)
9637 game_status_last_screen = game_status;
9639 game_status = game_status_new;
9641 SetAnimStatus(game_status_new);
9644 void SetFontStatus(int game_status_new)
9646 static int last_game_status = -1;
9648 if (game_status_new != -1)
9650 // set game status for font use after storing last game status
9651 last_game_status = game_status;
9652 game_status = game_status_new;
9656 // reset game status after font use from last stored game status
9657 game_status = last_game_status;
9661 void ResetFontStatus(void)
9666 void SetLevelSetInfo(char *identifier, int level_nr)
9668 setString(&levelset.identifier, identifier);
9670 levelset.level_nr = level_nr;
9673 boolean CheckIfAllViewportsHaveChanged(void)
9675 // if game status has not changed, viewports have not changed either
9676 if (game_status == game_status_last)
9679 // check if all viewports have changed with current game status
9681 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9682 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9683 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9684 int new_real_sx = vp_playfield->x;
9685 int new_real_sy = vp_playfield->y;
9686 int new_full_sxsize = vp_playfield->width;
9687 int new_full_sysize = vp_playfield->height;
9688 int new_dx = vp_door_1->x;
9689 int new_dy = vp_door_1->y;
9690 int new_dxsize = vp_door_1->width;
9691 int new_dysize = vp_door_1->height;
9692 int new_vx = vp_door_2->x;
9693 int new_vy = vp_door_2->y;
9694 int new_vxsize = vp_door_2->width;
9695 int new_vysize = vp_door_2->height;
9697 boolean playfield_viewport_has_changed =
9698 (new_real_sx != REAL_SX ||
9699 new_real_sy != REAL_SY ||
9700 new_full_sxsize != FULL_SXSIZE ||
9701 new_full_sysize != FULL_SYSIZE);
9703 boolean door_1_viewport_has_changed =
9706 new_dxsize != DXSIZE ||
9707 new_dysize != DYSIZE);
9709 boolean door_2_viewport_has_changed =
9712 new_vxsize != VXSIZE ||
9713 new_vysize != VYSIZE ||
9714 game_status_last == GAME_MODE_EDITOR);
9716 return (playfield_viewport_has_changed &&
9717 door_1_viewport_has_changed &&
9718 door_2_viewport_has_changed);
9721 boolean CheckFadeAll(void)
9723 return (CheckIfGlobalBorderHasChanged() ||
9724 CheckIfAllViewportsHaveChanged());
9727 void ChangeViewportPropertiesIfNeeded(void)
9729 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9730 FALSE : setup.small_game_graphics);
9731 int gfx_game_mode = game_status;
9732 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9734 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9735 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9736 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9737 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9738 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9739 int new_win_xsize = vp_window->width;
9740 int new_win_ysize = vp_window->height;
9741 int border_left = vp_playfield->border_left;
9742 int border_right = vp_playfield->border_right;
9743 int border_top = vp_playfield->border_top;
9744 int border_bottom = vp_playfield->border_bottom;
9745 int new_sx = vp_playfield->x + border_left;
9746 int new_sy = vp_playfield->y + border_top;
9747 int new_sxsize = vp_playfield->width - border_left - border_right;
9748 int new_sysize = vp_playfield->height - border_top - border_bottom;
9749 int new_real_sx = vp_playfield->x;
9750 int new_real_sy = vp_playfield->y;
9751 int new_full_sxsize = vp_playfield->width;
9752 int new_full_sysize = vp_playfield->height;
9753 int new_dx = vp_door_1->x;
9754 int new_dy = vp_door_1->y;
9755 int new_dxsize = vp_door_1->width;
9756 int new_dysize = vp_door_1->height;
9757 int new_vx = vp_door_2->x;
9758 int new_vy = vp_door_2->y;
9759 int new_vxsize = vp_door_2->width;
9760 int new_vysize = vp_door_2->height;
9761 int new_ex = vp_door_3->x;
9762 int new_ey = vp_door_3->y;
9763 int new_exsize = vp_door_3->width;
9764 int new_eysize = vp_door_3->height;
9765 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9766 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9767 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9768 int new_scr_fieldx = new_sxsize / tilesize;
9769 int new_scr_fieldy = new_sysize / tilesize;
9770 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9771 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9772 boolean init_gfx_buffers = FALSE;
9773 boolean init_video_buffer = FALSE;
9774 boolean init_gadgets_and_anims = FALSE;
9775 boolean init_em_graphics = FALSE;
9777 if (new_win_xsize != WIN_XSIZE ||
9778 new_win_ysize != WIN_YSIZE)
9780 WIN_XSIZE = new_win_xsize;
9781 WIN_YSIZE = new_win_ysize;
9783 init_video_buffer = TRUE;
9784 init_gfx_buffers = TRUE;
9785 init_gadgets_and_anims = TRUE;
9787 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9790 if (new_scr_fieldx != SCR_FIELDX ||
9791 new_scr_fieldy != SCR_FIELDY)
9793 // this always toggles between MAIN and GAME when using small tile size
9795 SCR_FIELDX = new_scr_fieldx;
9796 SCR_FIELDY = new_scr_fieldy;
9798 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9809 new_sxsize != SXSIZE ||
9810 new_sysize != SYSIZE ||
9811 new_dxsize != DXSIZE ||
9812 new_dysize != DYSIZE ||
9813 new_vxsize != VXSIZE ||
9814 new_vysize != VYSIZE ||
9815 new_exsize != EXSIZE ||
9816 new_eysize != EYSIZE ||
9817 new_real_sx != REAL_SX ||
9818 new_real_sy != REAL_SY ||
9819 new_full_sxsize != FULL_SXSIZE ||
9820 new_full_sysize != FULL_SYSIZE ||
9821 new_tilesize_var != TILESIZE_VAR
9824 // ------------------------------------------------------------------------
9825 // determine next fading area for changed viewport definitions
9826 // ------------------------------------------------------------------------
9828 // start with current playfield area (default fading area)
9831 FADE_SXSIZE = FULL_SXSIZE;
9832 FADE_SYSIZE = FULL_SYSIZE;
9834 // add new playfield area if position or size has changed
9835 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9836 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9838 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9839 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9842 // add current and new door 1 area if position or size has changed
9843 if (new_dx != DX || new_dy != DY ||
9844 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9846 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9847 DX, DY, DXSIZE, DYSIZE);
9848 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9849 new_dx, new_dy, new_dxsize, new_dysize);
9852 // add current and new door 2 area if position or size has changed
9853 if (new_vx != VX || new_vy != VY ||
9854 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9856 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9857 VX, VY, VXSIZE, VYSIZE);
9858 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9859 new_vx, new_vy, new_vxsize, new_vysize);
9862 // ------------------------------------------------------------------------
9863 // handle changed tile size
9864 // ------------------------------------------------------------------------
9866 if (new_tilesize_var != TILESIZE_VAR)
9868 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9870 // changing tile size invalidates scroll values of engine snapshots
9871 FreeEngineSnapshotSingle();
9873 // changing tile size requires update of graphic mapping for EM engine
9874 init_em_graphics = TRUE;
9885 SXSIZE = new_sxsize;
9886 SYSIZE = new_sysize;
9887 DXSIZE = new_dxsize;
9888 DYSIZE = new_dysize;
9889 VXSIZE = new_vxsize;
9890 VYSIZE = new_vysize;
9891 EXSIZE = new_exsize;
9892 EYSIZE = new_eysize;
9893 REAL_SX = new_real_sx;
9894 REAL_SY = new_real_sy;
9895 FULL_SXSIZE = new_full_sxsize;
9896 FULL_SYSIZE = new_full_sysize;
9897 TILESIZE_VAR = new_tilesize_var;
9899 init_gfx_buffers = TRUE;
9900 init_gadgets_and_anims = TRUE;
9902 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9903 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9906 if (init_gfx_buffers)
9908 // Debug("tools:viewport", "init_gfx_buffers");
9910 SCR_FIELDX = new_scr_fieldx_buffers;
9911 SCR_FIELDY = new_scr_fieldy_buffers;
9915 SCR_FIELDX = new_scr_fieldx;
9916 SCR_FIELDY = new_scr_fieldy;
9918 SetDrawDeactivationMask(REDRAW_NONE);
9919 SetDrawBackgroundMask(REDRAW_FIELD);
9922 if (init_video_buffer)
9924 // Debug("tools:viewport", "init_video_buffer");
9926 FreeAllImageTextures(); // needs old renderer to free the textures
9928 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9929 InitImageTextures();
9932 if (init_gadgets_and_anims)
9934 // Debug("tools:viewport", "init_gadgets_and_anims");
9937 InitGlobalAnimations();
9940 if (init_em_graphics)
9942 InitGraphicInfo_EM();
9947 // ============================================================================
9949 // ============================================================================
9951 #if defined(PLATFORM_WIN32)
9952 /* FILETIME of Jan 1 1970 00:00:00. */
9953 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9956 * timezone information is stored outside the kernel so tzp isn't used anymore.
9958 * Note: this function is not for Win32 high precision timing purpose. See
9961 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9964 SYSTEMTIME system_time;
9965 ULARGE_INTEGER ularge;
9967 GetSystemTime(&system_time);
9968 SystemTimeToFileTime(&system_time, &file_time);
9969 ularge.LowPart = file_time.dwLowDateTime;
9970 ularge.HighPart = file_time.dwHighDateTime;
9972 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9973 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9979 static char *test_init_uuid_random_function_simple(void)
9981 static char seed_text[100];
9982 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9984 sprintf(seed_text, "%d", seed);
9989 static char *test_init_uuid_random_function_better(void)
9991 static char seed_text[100];
9992 struct timeval current_time;
9994 gettimeofday(¤t_time, NULL);
9996 prng_seed_bytes(¤t_time, sizeof(current_time));
9998 sprintf(seed_text, "%ld.%ld",
9999 (long)current_time.tv_sec,
10000 (long)current_time.tv_usec);
10005 #if defined(PLATFORM_WIN32)
10006 static char *test_init_uuid_random_function_better_windows(void)
10008 static char seed_text[100];
10009 struct timeval current_time;
10011 gettimeofday_windows(¤t_time, NULL);
10013 prng_seed_bytes(¤t_time, sizeof(current_time));
10015 sprintf(seed_text, "%ld.%ld",
10016 (long)current_time.tv_sec,
10017 (long)current_time.tv_usec);
10023 static unsigned int test_uuid_random_function_simple(int max)
10025 return GetSimpleRandom(max);
10028 static unsigned int test_uuid_random_function_better(int max)
10030 return (max > 0 ? prng_get_uint() % max : 0);
10033 #if defined(PLATFORM_WIN32)
10034 #define NUM_UUID_TESTS 3
10036 #define NUM_UUID_TESTS 2
10039 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10041 struct hashtable *hash_seeds =
10042 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10043 struct hashtable *hash_uuids =
10044 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10045 static char message[100];
10048 char *random_name = (nr == 0 ? "simple" : "better");
10049 char *random_type = (always_seed ? "always" : "only once");
10050 char *(*init_random_function)(void) =
10052 test_init_uuid_random_function_simple :
10053 test_init_uuid_random_function_better);
10054 unsigned int (*random_function)(int) =
10056 test_uuid_random_function_simple :
10057 test_uuid_random_function_better);
10060 #if defined(PLATFORM_WIN32)
10063 random_name = "windows";
10064 init_random_function = test_init_uuid_random_function_better_windows;
10070 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10071 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10073 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10074 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10075 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10077 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10081 // always initialize random number generator at least once
10082 init_random_function();
10084 unsigned int time_start = SDL_GetTicks();
10086 for (i = 0; i < num_uuids; i++)
10090 char *seed = getStringCopy(init_random_function());
10092 hashtable_remove(hash_seeds, seed);
10093 hashtable_insert(hash_seeds, seed, "1");
10096 char *uuid = getStringCopy(getUUIDExt(random_function));
10098 hashtable_remove(hash_uuids, uuid);
10099 hashtable_insert(hash_uuids, uuid, "1");
10102 int num_unique_seeds = hashtable_count(hash_seeds);
10103 int num_unique_uuids = hashtable_count(hash_uuids);
10105 unsigned int time_needed = SDL_GetTicks() - time_start;
10107 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10109 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10112 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10114 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10115 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10117 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10119 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10121 Request(message, REQ_CONFIRM);
10123 hashtable_destroy(hash_seeds, 0);
10124 hashtable_destroy(hash_uuids, 0);
10127 void TestGeneratingUUIDs(void)
10129 int num_uuids = 1000000;
10132 for (i = 0; i < NUM_UUID_TESTS; i++)
10133 for (j = 0; j < 2; j++)
10134 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10136 CloseAllAndExit(0);