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;
1512 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1514 return getGraphicAnimationFrame(graphic, sync_frame);
1518 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1520 struct GraphicInfo *g = &graphic_info[graphic];
1521 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1523 if (tilesize == gfx.standard_tile_size)
1524 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1525 else if (tilesize == game.tile_size)
1526 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1528 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1531 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1532 boolean get_backside)
1534 struct GraphicInfo *g = &graphic_info[graphic];
1535 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1536 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1538 if (g->offset_y == 0) // frames are ordered horizontally
1540 int max_width = g->anim_frames_per_line * g->width;
1541 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1543 *x = pos % max_width;
1544 *y = src_y % g->height + pos / max_width * g->height;
1546 else if (g->offset_x == 0) // frames are ordered vertically
1548 int max_height = g->anim_frames_per_line * g->height;
1549 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1551 *x = src_x % g->width + pos / max_height * g->width;
1552 *y = pos % max_height;
1554 else // frames are ordered diagonally
1556 *x = src_x + frame * g->offset_x;
1557 *y = src_y + frame * g->offset_y;
1561 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1562 Bitmap **bitmap, int *x, int *y,
1563 boolean get_backside)
1565 struct GraphicInfo *g = &graphic_info[graphic];
1567 // if no graphics defined at all, use fallback graphics
1568 if (g->bitmaps == NULL)
1569 *g = graphic_info[IMG_CHAR_EXCLAM];
1571 // if no in-game graphics defined, always use standard graphic size
1572 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1573 tilesize = TILESIZE;
1575 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1576 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1578 *x = *x * tilesize / g->tile_size;
1579 *y = *y * tilesize / g->tile_size;
1582 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1583 Bitmap **bitmap, int *x, int *y)
1585 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1588 void getFixedGraphicSource(int graphic, int frame,
1589 Bitmap **bitmap, int *x, int *y)
1591 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1594 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1596 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1599 void getGlobalAnimGraphicSource(int graphic, int frame,
1600 Bitmap **bitmap, int *x, int *y)
1602 struct GraphicInfo *g = &graphic_info[graphic];
1604 // if no graphics defined at all, use fallback graphics
1605 if (g->bitmaps == NULL)
1606 *g = graphic_info[IMG_CHAR_EXCLAM];
1608 // use original size graphics, if existing, else use standard size graphics
1609 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1610 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1612 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1614 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1617 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1618 int *x, int *y, boolean get_backside)
1620 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1624 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1626 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1629 void DrawGraphic(int x, int y, int graphic, int frame)
1632 if (!IN_SCR_FIELD(x, y))
1634 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1635 Debug("draw:DrawGraphic", "This should never happen!");
1641 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1644 MarkTileDirty(x, y);
1647 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1650 if (!IN_SCR_FIELD(x, y))
1652 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1654 Debug("draw:DrawFixedGraphic", "This should never happen!");
1660 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1662 MarkTileDirty(x, y);
1665 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1671 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1673 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1676 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1682 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1683 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1686 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1689 if (!IN_SCR_FIELD(x, y))
1691 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1693 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1699 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1702 MarkTileDirty(x, y);
1705 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1708 if (!IN_SCR_FIELD(x, y))
1710 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1712 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1718 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1720 MarkTileDirty(x, y);
1723 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1729 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1731 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1735 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1736 int graphic, int frame)
1741 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1743 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1747 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1749 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1751 MarkTileDirty(x / tilesize, y / tilesize);
1754 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1757 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1758 graphic, frame, tilesize);
1759 MarkTileDirty(x / tilesize, y / tilesize);
1762 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1768 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1769 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1772 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1773 int frame, int tilesize)
1778 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1779 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1782 void DrawMiniGraphic(int x, int y, int graphic)
1784 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1785 MarkTileDirty(x / 2, y / 2);
1788 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1793 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1794 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1797 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1798 int graphic, int frame,
1799 int cut_mode, int mask_mode)
1804 int width = TILEX, height = TILEY;
1807 if (dx || dy) // shifted graphic
1809 if (x < BX1) // object enters playfield from the left
1816 else if (x > BX2) // object enters playfield from the right
1822 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1828 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1830 else if (dx) // general horizontal movement
1831 MarkTileDirty(x + SIGN(dx), y);
1833 if (y < BY1) // object enters playfield from the top
1835 if (cut_mode == CUT_BELOW) // object completely above top border
1843 else if (y > BY2) // object enters playfield from the bottom
1849 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1855 else if (dy > 0 && cut_mode == CUT_ABOVE)
1857 if (y == BY2) // object completely above bottom border
1863 MarkTileDirty(x, y + 1);
1864 } // object leaves playfield to the bottom
1865 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1867 else if (dy) // general vertical movement
1868 MarkTileDirty(x, y + SIGN(dy));
1872 if (!IN_SCR_FIELD(x, y))
1874 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1876 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1882 width = width * TILESIZE_VAR / TILESIZE;
1883 height = height * TILESIZE_VAR / TILESIZE;
1884 cx = cx * TILESIZE_VAR / TILESIZE;
1885 cy = cy * TILESIZE_VAR / TILESIZE;
1886 dx = dx * TILESIZE_VAR / TILESIZE;
1887 dy = dy * TILESIZE_VAR / TILESIZE;
1889 if (width > 0 && height > 0)
1891 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1896 dst_x = FX + x * TILEX_VAR + dx;
1897 dst_y = FY + y * TILEY_VAR + dy;
1899 if (mask_mode == USE_MASKING)
1900 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1903 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1906 MarkTileDirty(x, y);
1910 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1911 int graphic, int frame,
1912 int cut_mode, int mask_mode)
1917 int width = TILEX_VAR, height = TILEY_VAR;
1920 int x2 = x + SIGN(dx);
1921 int y2 = y + SIGN(dy);
1923 // movement with two-tile animations must be sync'ed with movement position,
1924 // not with current GfxFrame (which can be higher when using slow movement)
1925 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1926 int anim_frames = graphic_info[graphic].anim_frames;
1928 // (we also need anim_delay here for movement animations with less frames)
1929 int anim_delay = graphic_info[graphic].anim_delay;
1930 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1932 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1933 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1935 // re-calculate animation frame for two-tile movement animation
1936 frame = getGraphicAnimationFrame(graphic, sync_frame);
1938 // check if movement start graphic inside screen area and should be drawn
1939 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1941 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1943 dst_x = FX + x1 * TILEX_VAR;
1944 dst_y = FY + y1 * TILEY_VAR;
1946 if (mask_mode == USE_MASKING)
1947 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1950 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1953 MarkTileDirty(x1, y1);
1956 // check if movement end graphic inside screen area and should be drawn
1957 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1959 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1961 dst_x = FX + x2 * TILEX_VAR;
1962 dst_y = FY + y2 * TILEY_VAR;
1964 if (mask_mode == USE_MASKING)
1965 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1968 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1971 MarkTileDirty(x2, y2);
1975 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1976 int graphic, int frame,
1977 int cut_mode, int mask_mode)
1981 DrawGraphic(x, y, graphic, frame);
1986 if (graphic_info[graphic].double_movement) // EM style movement images
1987 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1989 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1992 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1993 int graphic, int frame, int cut_mode)
1995 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1998 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1999 int cut_mode, int mask_mode)
2001 int lx = LEVELX(x), ly = LEVELY(y);
2005 if (IN_LEV_FIELD(lx, ly))
2007 if (element == EL_EMPTY)
2008 element = GfxElementEmpty[lx][ly];
2010 SetRandomAnimationValue(lx, ly);
2012 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2013 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2015 // do not use double (EM style) movement graphic when not moving
2016 if (graphic_info[graphic].double_movement && !dx && !dy)
2018 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2019 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2022 if (game.use_masked_elements && (dx || dy))
2023 mask_mode = USE_MASKING;
2025 else // border element
2027 graphic = el2img(element);
2028 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2031 if (element == EL_EXPANDABLE_WALL)
2033 boolean left_stopped = FALSE, right_stopped = FALSE;
2035 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2036 left_stopped = TRUE;
2037 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2038 right_stopped = TRUE;
2040 if (left_stopped && right_stopped)
2042 else if (left_stopped)
2044 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2045 frame = graphic_info[graphic].anim_frames - 1;
2047 else if (right_stopped)
2049 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2050 frame = graphic_info[graphic].anim_frames - 1;
2055 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2056 else if (mask_mode == USE_MASKING)
2057 DrawGraphicThruMask(x, y, graphic, frame);
2059 DrawGraphic(x, y, graphic, frame);
2062 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2063 int cut_mode, int mask_mode)
2065 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2066 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2067 cut_mode, mask_mode);
2070 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2073 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2076 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2079 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2082 void DrawLevelElementThruMask(int x, int y, int element)
2084 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2087 void DrawLevelFieldThruMask(int x, int y)
2089 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2092 // !!! implementation of quicksand is totally broken !!!
2093 #define IS_CRUMBLED_TILE(x, y, e) \
2094 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2095 !IS_MOVING(x, y) || \
2096 (e) == EL_QUICKSAND_EMPTYING || \
2097 (e) == EL_QUICKSAND_FAST_EMPTYING))
2099 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2104 int width, height, cx, cy;
2105 int sx = SCREENX(x), sy = SCREENY(y);
2106 int crumbled_border_size = graphic_info[graphic].border_size;
2107 int crumbled_tile_size = graphic_info[graphic].tile_size;
2108 int crumbled_border_size_var =
2109 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2112 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2114 for (i = 1; i < 4; i++)
2116 int dxx = (i & 1 ? dx : 0);
2117 int dyy = (i & 2 ? dy : 0);
2120 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2123 // check if neighbour field is of same crumble type
2124 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2125 graphic_info[graphic].class ==
2126 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2128 // return if check prevents inner corner
2129 if (same == (dxx == dx && dyy == dy))
2133 // if we reach this point, we have an inner corner
2135 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2137 width = crumbled_border_size_var;
2138 height = crumbled_border_size_var;
2139 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2140 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2142 if (game.use_masked_elements)
2144 int graphic0 = el2img(EL_EMPTY);
2145 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2146 Bitmap *src_bitmap0;
2149 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2151 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2153 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2155 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2157 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2160 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2162 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2165 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2170 int width, height, bx, by, cx, cy;
2171 int sx = SCREENX(x), sy = SCREENY(y);
2172 int crumbled_border_size = graphic_info[graphic].border_size;
2173 int crumbled_tile_size = graphic_info[graphic].tile_size;
2174 int crumbled_border_size_var =
2175 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2176 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2179 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2181 // only needed when using masked elements
2182 int graphic0 = el2img(EL_EMPTY);
2183 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2184 Bitmap *src_bitmap0;
2187 if (game.use_masked_elements)
2188 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2190 // draw simple, sloppy, non-corner-accurate crumbled border
2192 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2193 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2194 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2195 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2197 if (game.use_masked_elements)
2199 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2201 FX + sx * TILEX_VAR + cx,
2202 FY + sy * TILEY_VAR + cy);
2204 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2206 FX + sx * TILEX_VAR + cx,
2207 FY + sy * TILEY_VAR + cy);
2210 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2212 FX + sx * TILEX_VAR + cx,
2213 FY + sy * TILEY_VAR + cy);
2215 // (remaining middle border part must be at least as big as corner part)
2216 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2217 crumbled_border_size_var >= TILESIZE_VAR / 3)
2220 // correct corners of crumbled border, if needed
2222 for (i = -1; i <= 1; i += 2)
2224 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2225 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2226 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2229 // check if neighbour field is of same crumble type
2230 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2231 graphic_info[graphic].class ==
2232 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2234 // no crumbled corner, but continued crumbled border
2236 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2237 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2238 int b1 = (i == 1 ? crumbled_border_size_var :
2239 TILESIZE_VAR - 2 * crumbled_border_size_var);
2241 width = crumbled_border_size_var;
2242 height = crumbled_border_size_var;
2244 if (dir == 1 || dir == 2)
2259 if (game.use_masked_elements)
2261 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2263 FX + sx * TILEX_VAR + cx,
2264 FY + sy * TILEY_VAR + cy);
2266 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2268 FX + sx * TILEX_VAR + cx,
2269 FY + sy * TILEY_VAR + cy);
2272 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2274 FX + sx * TILEX_VAR + cx,
2275 FY + sy * TILEY_VAR + cy);
2280 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2282 int sx = SCREENX(x), sy = SCREENY(y);
2285 static int xy[4][2] =
2293 if (!IN_LEV_FIELD(x, y))
2296 element = TILE_GFX_ELEMENT(x, y);
2298 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2300 if (!IN_SCR_FIELD(sx, sy))
2303 // crumble field borders towards direct neighbour fields
2304 for (i = 0; i < 4; i++)
2306 int xx = x + xy[i][0];
2307 int yy = y + xy[i][1];
2309 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2312 // check if neighbour field is of same crumble type
2313 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2314 graphic_info[graphic].class ==
2315 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2318 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2321 // crumble inner field corners towards corner neighbour fields
2322 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2323 graphic_info[graphic].anim_frames == 2)
2325 for (i = 0; i < 4; i++)
2327 int dx = (i & 1 ? +1 : -1);
2328 int dy = (i & 2 ? +1 : -1);
2330 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2334 MarkTileDirty(sx, sy);
2336 else // center field is not crumbled -- crumble neighbour fields
2338 // crumble field borders of direct neighbour fields
2339 for (i = 0; i < 4; i++)
2341 int xx = x + xy[i][0];
2342 int yy = y + xy[i][1];
2343 int sxx = sx + xy[i][0];
2344 int syy = sy + xy[i][1];
2346 if (!IN_LEV_FIELD(xx, yy) ||
2347 !IN_SCR_FIELD(sxx, syy))
2350 // do not crumble fields that are being digged or snapped
2351 if (Tile[xx][yy] == EL_EMPTY ||
2352 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2355 element = TILE_GFX_ELEMENT(xx, yy);
2357 if (!IS_CRUMBLED_TILE(xx, yy, element))
2360 graphic = el_act2crm(element, ACTION_DEFAULT);
2362 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2364 MarkTileDirty(sxx, syy);
2367 // crumble inner field corners of corner neighbour fields
2368 for (i = 0; i < 4; i++)
2370 int dx = (i & 1 ? +1 : -1);
2371 int dy = (i & 2 ? +1 : -1);
2377 if (!IN_LEV_FIELD(xx, yy) ||
2378 !IN_SCR_FIELD(sxx, syy))
2381 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2384 element = TILE_GFX_ELEMENT(xx, yy);
2386 if (!IS_CRUMBLED_TILE(xx, yy, element))
2389 graphic = el_act2crm(element, ACTION_DEFAULT);
2391 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2392 graphic_info[graphic].anim_frames == 2)
2393 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2395 MarkTileDirty(sxx, syy);
2400 void DrawLevelFieldCrumbled(int x, int y)
2404 if (!IN_LEV_FIELD(x, y))
2407 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2408 GfxElement[x][y] != EL_UNDEFINED &&
2409 GFX_CRUMBLED(GfxElement[x][y]))
2411 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2416 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2418 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2421 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2424 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2425 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2426 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2427 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2428 int sx = SCREENX(x), sy = SCREENY(y);
2430 DrawScreenGraphic(sx, sy, graphic1, frame1);
2431 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2434 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2436 int sx = SCREENX(x), sy = SCREENY(y);
2437 static int xy[4][2] =
2446 // crumble direct neighbour fields (required for field borders)
2447 for (i = 0; i < 4; i++)
2449 int xx = x + xy[i][0];
2450 int yy = y + xy[i][1];
2451 int sxx = sx + xy[i][0];
2452 int syy = sy + xy[i][1];
2454 if (!IN_LEV_FIELD(xx, yy) ||
2455 !IN_SCR_FIELD(sxx, syy) ||
2456 !GFX_CRUMBLED(Tile[xx][yy]) ||
2460 DrawLevelField(xx, yy);
2463 // crumble corner neighbour fields (required for inner field corners)
2464 for (i = 0; i < 4; i++)
2466 int dx = (i & 1 ? +1 : -1);
2467 int dy = (i & 2 ? +1 : -1);
2473 if (!IN_LEV_FIELD(xx, yy) ||
2474 !IN_SCR_FIELD(sxx, syy) ||
2475 !GFX_CRUMBLED(Tile[xx][yy]) ||
2479 int element = TILE_GFX_ELEMENT(xx, yy);
2480 int graphic = el_act2crm(element, ACTION_DEFAULT);
2482 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2483 graphic_info[graphic].anim_frames == 2)
2484 DrawLevelField(xx, yy);
2488 static int getBorderElement(int x, int y)
2492 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2493 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2494 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2495 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2496 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2497 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2498 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2500 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2501 int steel_position = (x == -1 && y == -1 ? 0 :
2502 x == lev_fieldx && y == -1 ? 1 :
2503 x == -1 && y == lev_fieldy ? 2 :
2504 x == lev_fieldx && y == lev_fieldy ? 3 :
2505 x == -1 || x == lev_fieldx ? 4 :
2506 y == -1 || y == lev_fieldy ? 5 : 6);
2508 return border[steel_position][steel_type];
2511 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2513 if (game.use_masked_elements)
2515 if (graphic != el2img(EL_EMPTY))
2516 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2518 DrawGraphicThruMask(x, y, graphic, frame);
2522 DrawGraphic(x, y, graphic, frame);
2526 void DrawScreenElement(int x, int y, int element)
2528 int mask_mode = NO_MASKING;
2530 if (game.use_masked_elements)
2532 int lx = LEVELX(x), ly = LEVELY(y);
2534 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2536 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2538 mask_mode = USE_MASKING;
2542 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2543 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2546 void DrawLevelElement(int x, int y, int element)
2548 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2549 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2552 void DrawScreenField(int x, int y)
2554 int lx = LEVELX(x), ly = LEVELY(y);
2555 int element, content;
2557 if (!IN_LEV_FIELD(lx, ly))
2559 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2562 element = getBorderElement(lx, ly);
2564 DrawScreenElement(x, y, element);
2569 element = Tile[lx][ly];
2570 content = Store[lx][ly];
2572 if (IS_MOVING(lx, ly))
2574 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2575 boolean cut_mode = NO_CUTTING;
2577 if (element == EL_QUICKSAND_EMPTYING ||
2578 element == EL_QUICKSAND_FAST_EMPTYING ||
2579 element == EL_MAGIC_WALL_EMPTYING ||
2580 element == EL_BD_MAGIC_WALL_EMPTYING ||
2581 element == EL_DC_MAGIC_WALL_EMPTYING ||
2582 element == EL_AMOEBA_DROPPING)
2583 cut_mode = CUT_ABOVE;
2584 else if (element == EL_QUICKSAND_FILLING ||
2585 element == EL_QUICKSAND_FAST_FILLING ||
2586 element == EL_MAGIC_WALL_FILLING ||
2587 element == EL_BD_MAGIC_WALL_FILLING ||
2588 element == EL_DC_MAGIC_WALL_FILLING)
2589 cut_mode = CUT_BELOW;
2591 if (cut_mode == CUT_ABOVE)
2592 DrawScreenElement(x, y, element);
2594 DrawScreenElement(x, y, EL_EMPTY);
2596 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2598 int dir = MovDir[lx][ly];
2599 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2600 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2602 if (IN_SCR_FIELD(newx, newy))
2603 DrawScreenElement(newx, newy, EL_EMPTY);
2607 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2608 else if (cut_mode == NO_CUTTING)
2609 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2612 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2614 if (cut_mode == CUT_BELOW &&
2615 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2616 DrawLevelElement(lx, ly + 1, element);
2619 if (content == EL_ACID)
2621 int dir = MovDir[lx][ly];
2622 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2623 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2625 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2627 // prevent target field from being drawn again (but without masking)
2628 // (this would happen if target field is scanned after moving element)
2629 Stop[newlx][newly] = TRUE;
2632 else if (IS_BLOCKED(lx, ly))
2637 boolean cut_mode = NO_CUTTING;
2638 int element_old, content_old;
2640 Blocked2Moving(lx, ly, &oldx, &oldy);
2643 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2644 MovDir[oldx][oldy] == MV_RIGHT);
2646 element_old = Tile[oldx][oldy];
2647 content_old = Store[oldx][oldy];
2649 if (element_old == EL_QUICKSAND_EMPTYING ||
2650 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2651 element_old == EL_MAGIC_WALL_EMPTYING ||
2652 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2653 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2654 element_old == EL_AMOEBA_DROPPING)
2655 cut_mode = CUT_ABOVE;
2657 DrawScreenElement(x, y, EL_EMPTY);
2660 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2662 else if (cut_mode == NO_CUTTING)
2663 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2666 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2669 else if (IS_DRAWABLE(element))
2670 DrawScreenElement(x, y, element);
2672 DrawScreenElement(x, y, EL_EMPTY);
2675 void DrawLevelField(int x, int y)
2677 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2678 DrawScreenField(SCREENX(x), SCREENY(y));
2679 else if (IS_MOVING(x, y))
2683 Moving2Blocked(x, y, &newx, &newy);
2684 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2685 DrawScreenField(SCREENX(newx), SCREENY(newy));
2687 else if (IS_BLOCKED(x, y))
2691 Blocked2Moving(x, y, &oldx, &oldy);
2692 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2693 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2697 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2698 int (*el2img_function)(int), boolean masked,
2699 int element_bits_draw)
2701 int element_base = map_mm_wall_element(element);
2702 int element_bits = (IS_DF_WALL(element) ?
2703 element - EL_DF_WALL_START :
2704 IS_MM_WALL(element) ?
2705 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2706 int graphic = el2img_function(element_base);
2707 int tilesize_draw = tilesize / 2;
2712 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2714 for (i = 0; i < 4; i++)
2716 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2717 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2719 if (!(element_bits_draw & (1 << i)))
2722 if (element_bits & (1 << i))
2725 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2726 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2728 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2729 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2734 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2735 tilesize_draw, tilesize_draw);
2740 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2741 boolean masked, int element_bits_draw)
2743 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2744 element, tilesize, el2edimg, masked, element_bits_draw);
2747 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2748 int (*el2img_function)(int))
2750 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2754 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2757 if (IS_MM_WALL(element))
2759 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2760 element, tilesize, el2edimg, masked, 0x000f);
2764 int graphic = el2edimg(element);
2767 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2769 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2773 void DrawSizedElement(int x, int y, int element, int tilesize)
2775 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2778 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2780 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2783 void DrawMiniElement(int x, int y, int element)
2787 graphic = el2edimg(element);
2788 DrawMiniGraphic(x, y, graphic);
2791 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2794 int x = sx + scroll_x, y = sy + scroll_y;
2796 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2797 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2798 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2799 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2801 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2804 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2806 int x = sx + scroll_x, y = sy + scroll_y;
2808 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2809 DrawMiniElement(sx, sy, EL_EMPTY);
2810 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2811 DrawMiniElement(sx, sy, Tile[x][y]);
2813 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2816 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2817 int x, int y, int xsize, int ysize,
2818 int tile_width, int tile_height)
2822 int dst_x = startx + x * tile_width;
2823 int dst_y = starty + y * tile_height;
2824 int width = graphic_info[graphic].width;
2825 int height = graphic_info[graphic].height;
2826 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2827 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2828 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2829 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2830 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2831 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2832 boolean draw_masked = graphic_info[graphic].draw_masked;
2834 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2836 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2838 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2842 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2843 inner_sx + (x - 1) * tile_width % inner_width);
2844 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2845 inner_sy + (y - 1) * tile_height % inner_height);
2848 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2851 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2855 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2856 int x, int y, int xsize, int ysize,
2859 int font_width = getFontWidth(font_nr);
2860 int font_height = getFontHeight(font_nr);
2862 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2863 font_width, font_height);
2866 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2868 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2869 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2870 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2871 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2872 boolean no_delay = (tape.warp_forward);
2873 unsigned int anim_delay = 0;
2874 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2875 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2876 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2877 int font_width = getFontWidth(font_nr);
2878 int font_height = getFontHeight(font_nr);
2879 int max_xsize = level.envelope[envelope_nr].xsize;
2880 int max_ysize = level.envelope[envelope_nr].ysize;
2881 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2882 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2883 int xend = max_xsize;
2884 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2885 int xstep = (xstart < xend ? 1 : 0);
2886 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2888 int end = MAX(xend - xstart, yend - ystart);
2891 for (i = start; i <= end; i++)
2893 int last_frame = end; // last frame of this "for" loop
2894 int x = xstart + i * xstep;
2895 int y = ystart + i * ystep;
2896 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2897 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2898 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2899 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2902 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2904 BlitScreenToBitmap(backbuffer);
2906 SetDrawtoField(DRAW_TO_BACKBUFFER);
2908 for (yy = 0; yy < ysize; yy++)
2909 for (xx = 0; xx < xsize; xx++)
2910 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2912 DrawTextBuffer(sx + font_width, sy + font_height,
2913 level.envelope[envelope_nr].text, font_nr, max_xsize,
2914 xsize - 2, ysize - 2, 0, mask_mode,
2915 level.envelope[envelope_nr].autowrap,
2916 level.envelope[envelope_nr].centered, FALSE);
2918 redraw_mask |= REDRAW_FIELD;
2921 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2924 ClearAutoRepeatKeyEvents();
2927 void ShowEnvelope(int envelope_nr)
2929 int element = EL_ENVELOPE_1 + envelope_nr;
2930 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2931 int sound_opening = element_info[element].sound[ACTION_OPENING];
2932 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2933 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2934 boolean no_delay = (tape.warp_forward);
2935 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2936 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2937 int anim_mode = graphic_info[graphic].anim_mode;
2938 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2939 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2940 boolean overlay_enabled = GetOverlayEnabled();
2942 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2944 SetOverlayEnabled(FALSE);
2947 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2949 if (anim_mode == ANIM_DEFAULT)
2950 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2952 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2955 Delay_WithScreenUpdates(wait_delay_value);
2957 WaitForEventToContinue();
2960 SetOverlayEnabled(overlay_enabled);
2962 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2964 if (anim_mode != ANIM_NONE)
2965 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2967 if (anim_mode == ANIM_DEFAULT)
2968 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2970 game.envelope_active = FALSE;
2972 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2974 redraw_mask |= REDRAW_FIELD;
2978 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2979 int xsize, int ysize)
2981 if (!global.use_envelope_request ||
2982 request.sort_priority <= 0)
2985 if (request.bitmap == NULL ||
2986 xsize > request.xsize ||
2987 ysize > request.ysize)
2989 if (request.bitmap != NULL)
2990 FreeBitmap(request.bitmap);
2992 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2994 SDL_Surface *surface = request.bitmap->surface;
2996 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2997 Fail("SDLGetNativeSurface() failed");
3000 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3002 SDLFreeBitmapTextures(request.bitmap);
3003 SDLCreateBitmapTextures(request.bitmap);
3005 // set envelope request run-time values
3008 request.xsize = xsize;
3009 request.ysize = ysize;
3012 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3014 if (global.use_envelope_request &&
3015 game.request_active_or_moving &&
3016 request.sort_priority > 0 &&
3017 drawing_target == DRAW_TO_SCREEN &&
3018 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3020 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3021 request.sx, request.sy);
3025 static void setRequestBasePosition(int *x, int *y)
3027 int sx_base, sy_base;
3029 if (request.x != -1)
3030 sx_base = request.x;
3031 else if (request.align == ALIGN_LEFT)
3033 else if (request.align == ALIGN_RIGHT)
3034 sx_base = SX + SXSIZE;
3036 sx_base = SX + SXSIZE / 2;
3038 if (request.y != -1)
3039 sy_base = request.y;
3040 else if (request.valign == VALIGN_TOP)
3042 else if (request.valign == VALIGN_BOTTOM)
3043 sy_base = SY + SYSIZE;
3045 sy_base = SY + SYSIZE / 2;
3051 static void setRequestPositionExt(int *x, int *y, int width, int height,
3052 boolean add_border_size)
3054 int border_size = request.border_size;
3055 int sx_base, sy_base;
3058 setRequestBasePosition(&sx_base, &sy_base);
3060 if (request.align == ALIGN_LEFT)
3062 else if (request.align == ALIGN_RIGHT)
3063 sx = sx_base - width;
3065 sx = sx_base - width / 2;
3067 if (request.valign == VALIGN_TOP)
3069 else if (request.valign == VALIGN_BOTTOM)
3070 sy = sy_base - height;
3072 sy = sy_base - height / 2;
3074 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3075 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3077 if (add_border_size)
3087 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3089 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3092 static void DrawEnvelopeRequest(char *text)
3094 char *text_final = text;
3095 char *text_door_style = NULL;
3096 int graphic = IMG_BACKGROUND_REQUEST;
3097 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3098 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3099 int font_nr = FONT_REQUEST;
3100 int font_width = getFontWidth(font_nr);
3101 int font_height = getFontHeight(font_nr);
3102 int border_size = request.border_size;
3103 int line_spacing = request.line_spacing;
3104 int line_height = font_height + line_spacing;
3105 int max_text_width = request.width - 2 * border_size;
3106 int max_text_height = request.height - 2 * border_size;
3107 int line_length = max_text_width / font_width;
3108 int max_lines = max_text_height / line_height;
3109 int text_width = line_length * font_width;
3110 int width = request.width;
3111 int height = request.height;
3112 int tile_size = MAX(request.step_offset, 1);
3113 int x_steps = width / tile_size;
3114 int y_steps = height / tile_size;
3115 int sx_offset = border_size;
3116 int sy_offset = border_size;
3120 if (request.centered)
3121 sx_offset = (request.width - text_width) / 2;
3123 if (request.wrap_single_words && !request.autowrap)
3125 char *src_text_ptr, *dst_text_ptr;
3127 text_door_style = checked_malloc(2 * strlen(text) + 1);
3129 src_text_ptr = text;
3130 dst_text_ptr = text_door_style;
3132 while (*src_text_ptr)
3134 if (*src_text_ptr == ' ' ||
3135 *src_text_ptr == '?' ||
3136 *src_text_ptr == '!')
3137 *dst_text_ptr++ = '\n';
3139 if (*src_text_ptr != ' ')
3140 *dst_text_ptr++ = *src_text_ptr;
3145 *dst_text_ptr = '\0';
3147 text_final = text_door_style;
3150 setRequestPosition(&sx, &sy, FALSE);
3152 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3154 for (y = 0; y < y_steps; y++)
3155 for (x = 0; x < x_steps; x++)
3156 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3157 x, y, x_steps, y_steps,
3158 tile_size, tile_size);
3160 // force DOOR font inside door area
3161 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3163 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3164 line_length, -1, max_lines, line_spacing, mask_mode,
3165 request.autowrap, request.centered, FALSE);
3169 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3170 RedrawGadget(tool_gadget[i]);
3172 // store readily prepared envelope request for later use when animating
3173 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3175 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3177 if (text_door_style)
3178 free(text_door_style);
3181 static void AnimateEnvelopeRequest(int anim_mode, int action)
3183 int graphic = IMG_BACKGROUND_REQUEST;
3184 boolean draw_masked = graphic_info[graphic].draw_masked;
3185 int delay_value_normal = request.step_delay;
3186 int delay_value_fast = delay_value_normal / 2;
3187 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3188 boolean no_delay = (tape.warp_forward);
3189 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3190 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3191 unsigned int anim_delay = 0;
3193 int tile_size = MAX(request.step_offset, 1);
3194 int max_xsize = request.width / tile_size;
3195 int max_ysize = request.height / tile_size;
3196 int max_xsize_inner = max_xsize - 2;
3197 int max_ysize_inner = max_ysize - 2;
3199 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3200 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3201 int xend = max_xsize_inner;
3202 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3203 int xstep = (xstart < xend ? 1 : 0);
3204 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3206 int end = MAX(xend - xstart, yend - ystart);
3209 if (setup.quick_doors)
3216 for (i = start; i <= end; i++)
3218 int last_frame = end; // last frame of this "for" loop
3219 int x = xstart + i * xstep;
3220 int y = ystart + i * ystep;
3221 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3222 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3223 int xsize_size_left = (xsize - 1) * tile_size;
3224 int ysize_size_top = (ysize - 1) * tile_size;
3225 int max_xsize_pos = (max_xsize - 1) * tile_size;
3226 int max_ysize_pos = (max_ysize - 1) * tile_size;
3227 int width = xsize * tile_size;
3228 int height = ysize * tile_size;
3233 setRequestPosition(&src_x, &src_y, FALSE);
3234 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3236 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3238 for (yy = 0; yy < 2; yy++)
3240 for (xx = 0; xx < 2; xx++)
3242 int src_xx = src_x + xx * max_xsize_pos;
3243 int src_yy = src_y + yy * max_ysize_pos;
3244 int dst_xx = dst_x + xx * xsize_size_left;
3245 int dst_yy = dst_y + yy * ysize_size_top;
3246 int xx_size = (xx ? tile_size : xsize_size_left);
3247 int yy_size = (yy ? tile_size : ysize_size_top);
3250 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3251 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3253 BlitBitmap(bitmap_db_store_2, backbuffer,
3254 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3258 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3260 redraw_mask |= REDRAW_FIELD;
3264 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3267 ClearAutoRepeatKeyEvents();
3270 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3272 int graphic = IMG_BACKGROUND_REQUEST;
3273 int sound_opening = SND_REQUEST_OPENING;
3274 int sound_closing = SND_REQUEST_CLOSING;
3275 int anim_mode_1 = request.anim_mode; // (higher priority)
3276 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3277 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3278 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3279 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3281 if (game_status == GAME_MODE_PLAYING)
3282 BlitScreenToBitmap(backbuffer);
3284 SetDrawtoField(DRAW_TO_BACKBUFFER);
3286 // SetDrawBackgroundMask(REDRAW_NONE);
3288 if (action == ACTION_OPENING)
3290 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3292 if (req_state & REQ_ASK)
3294 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3295 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3296 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3297 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3299 else if (req_state & REQ_CONFIRM)
3301 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3302 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3304 else if (req_state & REQ_PLAYER)
3306 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3307 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3308 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3309 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3312 DrawEnvelopeRequest(text);
3315 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3317 if (action == ACTION_OPENING)
3319 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3321 if (anim_mode == ANIM_DEFAULT)
3322 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3324 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3328 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3330 if (anim_mode != ANIM_NONE)
3331 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3333 if (anim_mode == ANIM_DEFAULT)
3334 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3337 game.envelope_active = FALSE;
3339 if (action == ACTION_CLOSING)
3340 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3342 // SetDrawBackgroundMask(last_draw_background_mask);
3344 redraw_mask |= REDRAW_FIELD;
3348 if (action == ACTION_CLOSING &&
3349 game_status == GAME_MODE_PLAYING &&
3350 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3351 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3354 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3356 if (IS_MM_WALL(element))
3358 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3364 int graphic = el2preimg(element);
3366 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3367 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3372 void DrawLevel(int draw_background_mask)
3376 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3377 SetDrawBackgroundMask(draw_background_mask);
3381 for (x = BX1; x <= BX2; x++)
3382 for (y = BY1; y <= BY2; y++)
3383 DrawScreenField(x, y);
3385 redraw_mask |= REDRAW_FIELD;
3388 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3393 for (x = 0; x < size_x; x++)
3394 for (y = 0; y < size_y; y++)
3395 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3397 redraw_mask |= REDRAW_FIELD;
3400 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3404 for (x = 0; x < size_x; x++)
3405 for (y = 0; y < size_y; y++)
3406 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3408 redraw_mask |= REDRAW_FIELD;
3411 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3413 boolean show_level_border = (BorderElement != EL_EMPTY);
3414 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3415 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3416 int tile_size = preview.tile_size;
3417 int preview_width = preview.xsize * tile_size;
3418 int preview_height = preview.ysize * tile_size;
3419 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3420 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3421 int real_preview_width = real_preview_xsize * tile_size;
3422 int real_preview_height = real_preview_ysize * tile_size;
3423 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3424 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3427 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3430 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3432 dst_x += (preview_width - real_preview_width) / 2;
3433 dst_y += (preview_height - real_preview_height) / 2;
3435 for (x = 0; x < real_preview_xsize; x++)
3437 for (y = 0; y < real_preview_ysize; y++)
3439 int lx = from_x + x + (show_level_border ? -1 : 0);
3440 int ly = from_y + y + (show_level_border ? -1 : 0);
3441 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3442 getBorderElement(lx, ly));
3444 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3445 element, tile_size);
3449 redraw_mask |= REDRAW_FIELD;
3452 #define MICROLABEL_EMPTY 0
3453 #define MICROLABEL_LEVEL_NAME 1
3454 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3455 #define MICROLABEL_LEVEL_AUTHOR 3
3456 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3457 #define MICROLABEL_IMPORTED_FROM 5
3458 #define MICROLABEL_IMPORTED_BY_HEAD 6
3459 #define MICROLABEL_IMPORTED_BY 7
3461 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3463 int max_text_width = SXSIZE;
3464 int font_width = getFontWidth(font_nr);
3466 if (pos->align == ALIGN_CENTER)
3467 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3468 else if (pos->align == ALIGN_RIGHT)
3469 max_text_width = pos->x;
3471 max_text_width = SXSIZE - pos->x;
3473 return max_text_width / font_width;
3476 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3478 char label_text[MAX_OUTPUT_LINESIZE + 1];
3479 int max_len_label_text;
3480 int font_nr = pos->font;
3483 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3486 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3487 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3488 mode == MICROLABEL_IMPORTED_BY_HEAD)
3489 font_nr = pos->font_alt;
3491 max_len_label_text = getMaxTextLength(pos, font_nr);
3493 if (pos->size != -1)
3494 max_len_label_text = pos->size;
3496 for (i = 0; i < max_len_label_text; i++)
3497 label_text[i] = ' ';
3498 label_text[max_len_label_text] = '\0';
3500 if (strlen(label_text) > 0)
3501 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3504 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3505 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3506 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3507 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3508 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3509 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3510 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3511 max_len_label_text);
3512 label_text[max_len_label_text] = '\0';
3514 if (strlen(label_text) > 0)
3515 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3517 redraw_mask |= REDRAW_FIELD;
3520 static void DrawPreviewLevelLabel(int mode)
3522 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3525 static void DrawPreviewLevelInfo(int mode)
3527 if (mode == MICROLABEL_LEVEL_NAME)
3528 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3529 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3530 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3533 static void DrawPreviewLevelExt(boolean restart)
3535 static unsigned int scroll_delay = 0;
3536 static unsigned int label_delay = 0;
3537 static int from_x, from_y, scroll_direction;
3538 static int label_state, label_counter;
3539 unsigned int scroll_delay_value = preview.step_delay;
3540 boolean show_level_border = (BorderElement != EL_EMPTY);
3541 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3542 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3549 if (preview.anim_mode == ANIM_CENTERED)
3551 if (level_xsize > preview.xsize)
3552 from_x = (level_xsize - preview.xsize) / 2;
3553 if (level_ysize > preview.ysize)
3554 from_y = (level_ysize - preview.ysize) / 2;
3557 from_x += preview.xoffset;
3558 from_y += preview.yoffset;
3560 scroll_direction = MV_RIGHT;
3564 DrawPreviewLevelPlayfield(from_x, from_y);
3565 DrawPreviewLevelLabel(label_state);
3567 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3568 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3570 // initialize delay counters
3571 ResetDelayCounter(&scroll_delay);
3572 ResetDelayCounter(&label_delay);
3574 if (leveldir_current->name)
3576 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3577 char label_text[MAX_OUTPUT_LINESIZE + 1];
3578 int font_nr = pos->font;
3579 int max_len_label_text = getMaxTextLength(pos, font_nr);
3581 if (pos->size != -1)
3582 max_len_label_text = pos->size;
3584 strncpy(label_text, leveldir_current->name, max_len_label_text);
3585 label_text[max_len_label_text] = '\0';
3587 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3588 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3594 // scroll preview level, if needed
3595 if (preview.anim_mode != ANIM_NONE &&
3596 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3597 DelayReached(&scroll_delay, scroll_delay_value))
3599 switch (scroll_direction)
3604 from_x -= preview.step_offset;
3605 from_x = (from_x < 0 ? 0 : from_x);
3608 scroll_direction = MV_UP;
3612 if (from_x < level_xsize - preview.xsize)
3614 from_x += preview.step_offset;
3615 from_x = (from_x > level_xsize - preview.xsize ?
3616 level_xsize - preview.xsize : from_x);
3619 scroll_direction = MV_DOWN;
3625 from_y -= preview.step_offset;
3626 from_y = (from_y < 0 ? 0 : from_y);
3629 scroll_direction = MV_RIGHT;
3633 if (from_y < level_ysize - preview.ysize)
3635 from_y += preview.step_offset;
3636 from_y = (from_y > level_ysize - preview.ysize ?
3637 level_ysize - preview.ysize : from_y);
3640 scroll_direction = MV_LEFT;
3647 DrawPreviewLevelPlayfield(from_x, from_y);
3650 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3651 // redraw micro level label, if needed
3652 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3653 !strEqual(level.author, ANONYMOUS_NAME) &&
3654 !strEqual(level.author, leveldir_current->name) &&
3655 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3657 int max_label_counter = 23;
3659 if (leveldir_current->imported_from != NULL &&
3660 strlen(leveldir_current->imported_from) > 0)
3661 max_label_counter += 14;
3662 if (leveldir_current->imported_by != NULL &&
3663 strlen(leveldir_current->imported_by) > 0)
3664 max_label_counter += 14;
3666 label_counter = (label_counter + 1) % max_label_counter;
3667 label_state = (label_counter >= 0 && label_counter <= 7 ?
3668 MICROLABEL_LEVEL_NAME :
3669 label_counter >= 9 && label_counter <= 12 ?
3670 MICROLABEL_LEVEL_AUTHOR_HEAD :
3671 label_counter >= 14 && label_counter <= 21 ?
3672 MICROLABEL_LEVEL_AUTHOR :
3673 label_counter >= 23 && label_counter <= 26 ?
3674 MICROLABEL_IMPORTED_FROM_HEAD :
3675 label_counter >= 28 && label_counter <= 35 ?
3676 MICROLABEL_IMPORTED_FROM :
3677 label_counter >= 37 && label_counter <= 40 ?
3678 MICROLABEL_IMPORTED_BY_HEAD :
3679 label_counter >= 42 && label_counter <= 49 ?
3680 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3682 if (leveldir_current->imported_from == NULL &&
3683 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3684 label_state == MICROLABEL_IMPORTED_FROM))
3685 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3686 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3688 DrawPreviewLevelLabel(label_state);
3692 void DrawPreviewPlayers(void)
3694 if (game_status != GAME_MODE_MAIN)
3697 // do not draw preview players if level preview redefined, but players aren't
3698 if (preview.redefined && !menu.main.preview_players.redefined)
3701 boolean player_found[MAX_PLAYERS];
3702 int num_players = 0;
3705 for (i = 0; i < MAX_PLAYERS; i++)
3706 player_found[i] = FALSE;
3708 // check which players can be found in the level (simple approach)
3709 for (x = 0; x < lev_fieldx; x++)
3711 for (y = 0; y < lev_fieldy; y++)
3713 int element = level.field[x][y];
3715 if (IS_PLAYER_ELEMENT(element))
3717 int player_nr = GET_PLAYER_NR(element);
3719 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3721 if (!player_found[player_nr])
3724 player_found[player_nr] = TRUE;
3729 struct TextPosInfo *pos = &menu.main.preview_players;
3730 int tile_size = pos->tile_size;
3731 int border_size = pos->border_size;
3732 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3733 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3734 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3735 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3736 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3737 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3738 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3739 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3740 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3741 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3742 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3743 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3745 // clear area in which the players will be drawn
3746 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3747 max_players_width, max_players_height);
3749 if (!network.enabled && !setup.team_mode)
3752 // only draw players if level is suited for team mode
3753 if (num_players < 2)
3756 // draw all players that were found in the level
3757 for (i = 0; i < MAX_PLAYERS; i++)
3759 if (player_found[i])
3761 int graphic = el2img(EL_PLAYER_1 + i);
3763 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3765 xpos += player_xoffset;
3766 ypos += player_yoffset;
3771 void DrawPreviewLevelInitial(void)
3773 DrawPreviewLevelExt(TRUE);
3774 DrawPreviewPlayers();
3777 void DrawPreviewLevelAnimation(void)
3779 DrawPreviewLevelExt(FALSE);
3782 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3783 int border_size, int font_nr)
3785 int graphic = el2img(EL_PLAYER_1 + player_nr);
3786 int font_height = getFontHeight(font_nr);
3787 int player_height = MAX(tile_size, font_height);
3788 int xoffset_text = tile_size + border_size;
3789 int yoffset_text = (player_height - font_height) / 2;
3790 int yoffset_graphic = (player_height - tile_size) / 2;
3791 char *player_name = getNetworkPlayerName(player_nr + 1);
3793 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3795 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3798 static void DrawNetworkPlayersExt(boolean force)
3800 if (game_status != GAME_MODE_MAIN)
3803 if (!network.connected && !force)
3806 // do not draw network players if level preview redefined, but players aren't
3807 if (preview.redefined && !menu.main.network_players.redefined)
3810 int num_players = 0;
3813 for (i = 0; i < MAX_PLAYERS; i++)
3814 if (stored_player[i].connected_network)
3817 struct TextPosInfo *pos = &menu.main.network_players;
3818 int tile_size = pos->tile_size;
3819 int border_size = pos->border_size;
3820 int xoffset_text = tile_size + border_size;
3821 int font_nr = pos->font;
3822 int font_width = getFontWidth(font_nr);
3823 int font_height = getFontHeight(font_nr);
3824 int player_height = MAX(tile_size, font_height);
3825 int player_yoffset = player_height + border_size;
3826 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3827 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3828 int all_players_height = num_players * player_yoffset - border_size;
3829 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3830 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3831 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3833 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3834 max_players_width, max_players_height);
3836 // first draw local network player ...
3837 for (i = 0; i < MAX_PLAYERS; i++)
3839 if (stored_player[i].connected_network &&
3840 stored_player[i].connected_locally)
3842 char *player_name = getNetworkPlayerName(i + 1);
3843 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3844 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3846 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3848 ypos += player_yoffset;
3852 // ... then draw all other network players
3853 for (i = 0; i < MAX_PLAYERS; i++)
3855 if (stored_player[i].connected_network &&
3856 !stored_player[i].connected_locally)
3858 char *player_name = getNetworkPlayerName(i + 1);
3859 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3860 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3862 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3864 ypos += player_yoffset;
3869 void DrawNetworkPlayers(void)
3871 DrawNetworkPlayersExt(FALSE);
3874 void ClearNetworkPlayers(void)
3876 DrawNetworkPlayersExt(TRUE);
3879 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3880 int graphic, int lx, int ly,
3883 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3885 if (mask_mode == USE_MASKING)
3886 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3888 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3891 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3892 int graphic, int sync_frame, int mask_mode)
3894 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3896 if (mask_mode == USE_MASKING)
3897 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3899 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3902 static void DrawGraphicAnimation(int x, int y, int graphic)
3904 int lx = LEVELX(x), ly = LEVELY(y);
3905 int mask_mode = NO_MASKING;
3907 if (!IN_SCR_FIELD(x, y))
3910 if (game.use_masked_elements)
3912 if (Tile[lx][ly] != EL_EMPTY)
3914 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3916 mask_mode = USE_MASKING;
3920 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3921 graphic, lx, ly, mask_mode);
3923 MarkTileDirty(x, y);
3926 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3928 int lx = LEVELX(x), ly = LEVELY(y);
3929 int mask_mode = NO_MASKING;
3931 if (!IN_SCR_FIELD(x, y))
3934 if (game.use_masked_elements)
3936 if (Tile[lx][ly] != EL_EMPTY)
3938 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3940 mask_mode = USE_MASKING;
3944 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3945 graphic, lx, ly, mask_mode);
3947 MarkTileDirty(x, y);
3950 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3952 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3955 void DrawLevelElementAnimation(int x, int y, int element)
3957 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3959 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3962 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3964 int sx = SCREENX(x), sy = SCREENY(y);
3966 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3969 if (Tile[x][y] == EL_EMPTY)
3970 graphic = el2img(GfxElementEmpty[x][y]);
3972 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3975 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3978 DrawGraphicAnimation(sx, sy, graphic);
3981 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3982 DrawLevelFieldCrumbled(x, y);
3984 if (GFX_CRUMBLED(Tile[x][y]))
3985 DrawLevelFieldCrumbled(x, y);
3989 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3991 int sx = SCREENX(x), sy = SCREENY(y);
3994 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3997 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3999 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4002 DrawGraphicAnimation(sx, sy, graphic);
4004 if (GFX_CRUMBLED(element))
4005 DrawLevelFieldCrumbled(x, y);
4008 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4010 if (player->use_murphy)
4012 // this works only because currently only one player can be "murphy" ...
4013 static int last_horizontal_dir = MV_LEFT;
4014 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4016 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4017 last_horizontal_dir = move_dir;
4019 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4021 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4023 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4029 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4032 static boolean equalGraphics(int graphic1, int graphic2)
4034 struct GraphicInfo *g1 = &graphic_info[graphic1];
4035 struct GraphicInfo *g2 = &graphic_info[graphic2];
4037 return (g1->bitmap == g2->bitmap &&
4038 g1->src_x == g2->src_x &&
4039 g1->src_y == g2->src_y &&
4040 g1->anim_frames == g2->anim_frames &&
4041 g1->anim_delay == g2->anim_delay &&
4042 g1->anim_mode == g2->anim_mode);
4045 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4049 DRAW_PLAYER_STAGE_INIT = 0,
4050 DRAW_PLAYER_STAGE_LAST_FIELD,
4051 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4052 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4053 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4054 DRAW_PLAYER_STAGE_PLAYER,
4056 DRAW_PLAYER_STAGE_PLAYER,
4057 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4059 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4060 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4062 NUM_DRAW_PLAYER_STAGES
4065 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4067 static int static_last_player_graphic[MAX_PLAYERS];
4068 static int static_last_player_frame[MAX_PLAYERS];
4069 static boolean static_player_is_opaque[MAX_PLAYERS];
4070 static boolean draw_player[MAX_PLAYERS];
4071 int pnr = player->index_nr;
4073 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4075 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4076 static_last_player_frame[pnr] = player->Frame;
4077 static_player_is_opaque[pnr] = FALSE;
4079 draw_player[pnr] = TRUE;
4082 if (!draw_player[pnr])
4086 if (!IN_LEV_FIELD(player->jx, player->jy))
4088 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4089 Debug("draw:DrawPlayerExt", "This should never happen!");
4091 draw_player[pnr] = FALSE;
4097 int last_player_graphic = static_last_player_graphic[pnr];
4098 int last_player_frame = static_last_player_frame[pnr];
4099 boolean player_is_opaque = static_player_is_opaque[pnr];
4101 int jx = player->jx;
4102 int jy = player->jy;
4103 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4104 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4105 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4106 int last_jx = (player->is_moving ? jx - dx : jx);
4107 int last_jy = (player->is_moving ? jy - dy : jy);
4108 int next_jx = jx + dx;
4109 int next_jy = jy + dy;
4110 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4111 int sx = SCREENX(jx);
4112 int sy = SCREENY(jy);
4113 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4114 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4115 int element = Tile[jx][jy];
4116 int last_element = Tile[last_jx][last_jy];
4117 int action = (player->is_pushing ? ACTION_PUSHING :
4118 player->is_digging ? ACTION_DIGGING :
4119 player->is_collecting ? ACTION_COLLECTING :
4120 player->is_moving ? ACTION_MOVING :
4121 player->is_snapping ? ACTION_SNAPPING :
4122 player->is_dropping ? ACTION_DROPPING :
4123 player->is_waiting ? player->action_waiting :
4126 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4128 // ------------------------------------------------------------------------
4129 // initialize drawing the player
4130 // ------------------------------------------------------------------------
4132 draw_player[pnr] = FALSE;
4134 // GfxElement[][] is set to the element the player is digging or collecting;
4135 // remove also for off-screen player if the player is not moving anymore
4136 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4137 GfxElement[jx][jy] = EL_UNDEFINED;
4139 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4142 if (element == EL_EXPLOSION)
4145 InitPlayerGfxAnimation(player, action, move_dir);
4147 draw_player[pnr] = TRUE;
4149 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4151 // ------------------------------------------------------------------------
4152 // draw things in the field the player is leaving, if needed
4153 // ------------------------------------------------------------------------
4155 if (!IN_SCR_FIELD(sx, sy))
4156 draw_player[pnr] = FALSE;
4158 if (!player->is_moving)
4161 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4163 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4165 if (last_element == EL_DYNAMITE_ACTIVE ||
4166 last_element == EL_EM_DYNAMITE_ACTIVE ||
4167 last_element == EL_SP_DISK_RED_ACTIVE)
4168 DrawDynamite(last_jx, last_jy);
4170 DrawLevelFieldThruMask(last_jx, last_jy);
4172 else if (last_element == EL_DYNAMITE_ACTIVE ||
4173 last_element == EL_EM_DYNAMITE_ACTIVE ||
4174 last_element == EL_SP_DISK_RED_ACTIVE)
4175 DrawDynamite(last_jx, last_jy);
4177 DrawLevelField(last_jx, last_jy);
4179 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4180 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4182 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4184 // ------------------------------------------------------------------------
4185 // draw things behind the player, if needed
4186 // ------------------------------------------------------------------------
4190 DrawLevelElement(jx, jy, Back[jx][jy]);
4195 if (IS_ACTIVE_BOMB(element))
4197 DrawLevelElement(jx, jy, EL_EMPTY);
4202 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4204 int old_element = GfxElement[jx][jy];
4205 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4206 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4208 if (GFX_CRUMBLED(old_element))
4209 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4211 DrawScreenGraphic(sx, sy, old_graphic, frame);
4213 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4214 static_player_is_opaque[pnr] = TRUE;
4218 GfxElement[jx][jy] = EL_UNDEFINED;
4220 // make sure that pushed elements are drawn with correct frame rate
4221 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4223 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4224 GfxFrame[jx][jy] = player->StepFrame;
4226 DrawLevelField(jx, jy);
4229 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4231 // ------------------------------------------------------------------------
4232 // draw things the player is pushing, if needed
4233 // ------------------------------------------------------------------------
4235 if (!player->is_pushing || !player->is_moving)
4238 int gfx_frame = GfxFrame[jx][jy];
4240 if (!IS_MOVING(jx, jy)) // push movement already finished
4242 element = Tile[next_jx][next_jy];
4243 gfx_frame = GfxFrame[next_jx][next_jy];
4246 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4247 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4248 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4250 // draw background element under pushed element (like the Sokoban field)
4251 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4253 // this allows transparent pushing animation over non-black background
4256 DrawLevelElement(jx, jy, Back[jx][jy]);
4258 DrawLevelElement(jx, jy, EL_EMPTY);
4260 if (Back[next_jx][next_jy])
4261 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4263 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4265 else if (Back[next_jx][next_jy])
4266 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4268 int px = SCREENX(jx), py = SCREENY(jy);
4269 int pxx = (TILEX - ABS(sxx)) * dx;
4270 int pyy = (TILEY - ABS(syy)) * dy;
4273 // do not draw (EM style) pushing animation when pushing is finished
4274 // (two-tile animations usually do not contain start and end frame)
4275 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4276 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4278 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4280 // masked drawing is needed for EMC style (double) movement graphics
4281 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4282 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4285 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4287 // ------------------------------------------------------------------------
4288 // draw player himself
4289 // ------------------------------------------------------------------------
4291 int graphic = getPlayerGraphic(player, move_dir);
4293 // in the case of changed player action or direction, prevent the current
4294 // animation frame from being restarted for identical animations
4295 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4296 player->Frame = last_player_frame;
4298 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4300 if (player_is_opaque)
4301 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4303 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4305 if (SHIELD_ON(player))
4307 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4308 IMG_SHIELD_NORMAL_ACTIVE);
4309 frame = getGraphicAnimationFrame(graphic, -1);
4311 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4314 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4316 // ------------------------------------------------------------------------
4317 // draw things in front of player (active dynamite or dynabombs)
4318 // ------------------------------------------------------------------------
4320 if (IS_ACTIVE_BOMB(element))
4322 int graphic = el2img(element);
4323 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4325 if (game.emulation == EMU_SUPAPLEX)
4326 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4328 DrawGraphicThruMask(sx, sy, graphic, frame);
4331 if (player_is_moving && last_element == EL_EXPLOSION)
4333 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4334 GfxElement[last_jx][last_jy] : EL_EMPTY);
4335 int graphic = el_act2img(element, ACTION_EXPLODING);
4336 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4337 int phase = ExplodePhase[last_jx][last_jy] - 1;
4338 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4341 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4344 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4346 // ------------------------------------------------------------------------
4347 // draw elements the player is just walking/passing through/under
4348 // ------------------------------------------------------------------------
4350 if (player_is_moving)
4352 // handle the field the player is leaving ...
4353 if (IS_ACCESSIBLE_INSIDE(last_element))
4354 DrawLevelField(last_jx, last_jy);
4355 else if (IS_ACCESSIBLE_UNDER(last_element))
4356 DrawLevelFieldThruMask(last_jx, last_jy);
4359 // do not redraw accessible elements if the player is just pushing them
4360 if (!player_is_moving || !player->is_pushing)
4362 // ... and the field the player is entering
4363 if (IS_ACCESSIBLE_INSIDE(element))
4364 DrawLevelField(jx, jy);
4365 else if (IS_ACCESSIBLE_UNDER(element))
4366 DrawLevelFieldThruMask(jx, jy);
4369 MarkTileDirty(sx, sy);
4373 void DrawPlayer(struct PlayerInfo *player)
4377 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4378 DrawPlayerExt(player, i);
4381 void DrawAllPlayers(void)
4385 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4386 for (j = 0; j < MAX_PLAYERS; j++)
4387 if (stored_player[j].active)
4388 DrawPlayerExt(&stored_player[j], i);
4391 void DrawPlayerField(int x, int y)
4393 if (!IS_PLAYER(x, y))
4396 DrawPlayer(PLAYERINFO(x, y));
4399 // ----------------------------------------------------------------------------
4401 void WaitForEventToContinue(void)
4403 boolean first_wait = TRUE;
4404 boolean still_wait = TRUE;
4406 if (program.headless)
4409 // simulate releasing mouse button over last gadget, if still pressed
4411 HandleGadgets(-1, -1, 0);
4413 button_status = MB_RELEASED;
4416 ClearPlayerAction();
4422 if (NextValidEvent(&event))
4426 case EVENT_BUTTONPRESS:
4427 case EVENT_FINGERPRESS:
4431 case EVENT_BUTTONRELEASE:
4432 case EVENT_FINGERRELEASE:
4433 still_wait = first_wait;
4436 case EVENT_KEYPRESS:
4437 case SDL_CONTROLLERBUTTONDOWN:
4438 case SDL_JOYBUTTONDOWN:
4443 HandleOtherEvents(&event);
4447 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4452 if (!PendingEvent())
4457 #define MAX_REQUEST_LINES 13
4458 #define MAX_REQUEST_LINE_FONT1_LEN 7
4459 #define MAX_REQUEST_LINE_FONT2_LEN 10
4461 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4463 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4465 int draw_buffer_last = GetDrawtoField();
4466 int width = request.width;
4467 int height = request.height;
4471 // when showing request dialog after game ended, deactivate game panel
4472 if (game_just_ended)
4473 game.panel.active = FALSE;
4475 game.request_active = TRUE;
4477 setRequestPosition(&sx, &sy, FALSE);
4479 button_status = MB_RELEASED;
4481 request_gadget_id = -1;
4486 boolean event_handled = FALSE;
4488 if (game_just_ended)
4490 SetDrawtoField(draw_buffer_game);
4492 HandleGameActions();
4494 SetDrawtoField(DRAW_TO_BACKBUFFER);
4496 if (global.use_envelope_request)
4498 // copy current state of request area to middle of playfield area
4499 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4507 while (NextValidEvent(&event))
4509 event_handled = TRUE;
4513 case EVENT_BUTTONPRESS:
4514 case EVENT_BUTTONRELEASE:
4515 case EVENT_MOTIONNOTIFY:
4519 if (event.type == EVENT_MOTIONNOTIFY)
4524 motion_status = TRUE;
4525 mx = ((MotionEvent *) &event)->x;
4526 my = ((MotionEvent *) &event)->y;
4530 motion_status = FALSE;
4531 mx = ((ButtonEvent *) &event)->x;
4532 my = ((ButtonEvent *) &event)->y;
4533 if (event.type == EVENT_BUTTONPRESS)
4534 button_status = ((ButtonEvent *) &event)->button;
4536 button_status = MB_RELEASED;
4539 // this sets 'request_gadget_id'
4540 HandleGadgets(mx, my, button_status);
4542 switch (request_gadget_id)
4544 case TOOL_CTRL_ID_YES:
4545 case TOOL_CTRL_ID_TOUCH_YES:
4548 case TOOL_CTRL_ID_NO:
4549 case TOOL_CTRL_ID_TOUCH_NO:
4552 case TOOL_CTRL_ID_CONFIRM:
4553 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4554 result = TRUE | FALSE;
4557 case TOOL_CTRL_ID_PLAYER_1:
4560 case TOOL_CTRL_ID_PLAYER_2:
4563 case TOOL_CTRL_ID_PLAYER_3:
4566 case TOOL_CTRL_ID_PLAYER_4:
4571 // only check clickable animations if no request gadget clicked
4572 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4579 case SDL_WINDOWEVENT:
4580 HandleWindowEvent((WindowEvent *) &event);
4583 case SDL_APP_WILLENTERBACKGROUND:
4584 case SDL_APP_DIDENTERBACKGROUND:
4585 case SDL_APP_WILLENTERFOREGROUND:
4586 case SDL_APP_DIDENTERFOREGROUND:
4587 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4590 case EVENT_KEYPRESS:
4592 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4597 if (req_state & REQ_CONFIRM)
4606 #if defined(KSYM_Rewind)
4607 case KSYM_Rewind: // for Amazon Fire TV remote
4616 #if defined(KSYM_FastForward)
4617 case KSYM_FastForward: // for Amazon Fire TV remote
4623 HandleKeysDebug(key, KEY_PRESSED);
4627 if (req_state & REQ_PLAYER)
4629 int old_player_nr = setup.network_player_nr;
4632 result = old_player_nr + 1;
4637 result = old_player_nr + 1;
4668 case EVENT_FINGERRELEASE:
4669 case EVENT_KEYRELEASE:
4670 ClearPlayerAction();
4673 case SDL_CONTROLLERBUTTONDOWN:
4674 switch (event.cbutton.button)
4676 case SDL_CONTROLLER_BUTTON_A:
4677 case SDL_CONTROLLER_BUTTON_X:
4678 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4679 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4683 case SDL_CONTROLLER_BUTTON_B:
4684 case SDL_CONTROLLER_BUTTON_Y:
4685 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4686 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4687 case SDL_CONTROLLER_BUTTON_BACK:
4692 if (req_state & REQ_PLAYER)
4694 int old_player_nr = setup.network_player_nr;
4697 result = old_player_nr + 1;
4699 switch (event.cbutton.button)
4701 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4702 case SDL_CONTROLLER_BUTTON_Y:
4706 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4707 case SDL_CONTROLLER_BUTTON_B:
4711 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4712 case SDL_CONTROLLER_BUTTON_A:
4716 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4717 case SDL_CONTROLLER_BUTTON_X:
4728 case SDL_CONTROLLERBUTTONUP:
4729 HandleJoystickEvent(&event);
4730 ClearPlayerAction();
4734 HandleOtherEvents(&event);
4739 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4741 int joy = AnyJoystick();
4743 if (joy & JOY_BUTTON_1)
4745 else if (joy & JOY_BUTTON_2)
4748 else if (AnyJoystick())
4750 int joy = AnyJoystick();
4752 if (req_state & REQ_PLAYER)
4756 else if (joy & JOY_RIGHT)
4758 else if (joy & JOY_DOWN)
4760 else if (joy & JOY_LEFT)
4767 if (game_just_ended)
4769 if (global.use_envelope_request)
4771 // copy back current state of pressed buttons inside request area
4772 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4776 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4782 SetDrawtoField(draw_buffer_last);
4784 game.request_active = FALSE;
4789 static boolean RequestDoor(char *text, unsigned int req_state)
4791 int draw_buffer_last = GetDrawtoField();
4792 unsigned int old_door_state;
4793 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4794 int font_nr = FONT_TEXT_2;
4799 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4801 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4802 font_nr = FONT_TEXT_1;
4805 if (game_status == GAME_MODE_PLAYING)
4806 BlitScreenToBitmap(backbuffer);
4808 // disable deactivated drawing when quick-loading level tape recording
4809 if (tape.playing && tape.deactivate_display)
4810 TapeDeactivateDisplayOff(TRUE);
4812 SetMouseCursor(CURSOR_DEFAULT);
4814 // pause network game while waiting for request to answer
4815 if (network.enabled &&
4816 game_status == GAME_MODE_PLAYING &&
4817 !game.all_players_gone &&
4818 req_state & REQUEST_WAIT_FOR_INPUT)
4819 SendToServer_PausePlaying();
4821 old_door_state = GetDoorState();
4823 // simulate releasing mouse button over last gadget, if still pressed
4825 HandleGadgets(-1, -1, 0);
4829 // draw released gadget before proceeding
4832 if (old_door_state & DOOR_OPEN_1)
4834 CloseDoor(DOOR_CLOSE_1);
4836 // save old door content
4837 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4838 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4841 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4842 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4844 // clear door drawing field
4845 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4847 // force DOOR font inside door area
4848 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4850 // write text for request
4851 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4853 char text_line[max_request_line_len + 1];
4859 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4861 tc = *(text_ptr + tx);
4862 // if (!tc || tc == ' ')
4863 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4867 if ((tc == '?' || tc == '!') && tl == 0)
4877 strncpy(text_line, text_ptr, tl);
4880 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4881 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4882 text_line, font_nr);
4884 text_ptr += tl + (tc == ' ' ? 1 : 0);
4885 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4890 if (req_state & REQ_ASK)
4892 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4893 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4894 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4895 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4897 else if (req_state & REQ_CONFIRM)
4899 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4900 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4902 else if (req_state & REQ_PLAYER)
4904 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4905 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4906 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4907 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4910 // copy request gadgets to door backbuffer
4911 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4913 OpenDoor(DOOR_OPEN_1);
4915 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4917 if (game_status == GAME_MODE_PLAYING)
4919 SetPanelBackground();
4920 SetDrawBackgroundMask(REDRAW_DOOR_1);
4924 SetDrawBackgroundMask(REDRAW_FIELD);
4930 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4932 // ---------- handle request buttons ----------
4933 result = RequestHandleEvents(req_state, draw_buffer_last);
4937 if (!(req_state & REQ_STAY_OPEN))
4939 CloseDoor(DOOR_CLOSE_1);
4941 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4942 (req_state & REQ_REOPEN))
4943 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4948 if (game_status == GAME_MODE_PLAYING)
4950 SetPanelBackground();
4951 SetDrawBackgroundMask(REDRAW_DOOR_1);
4955 SetDrawBackgroundMask(REDRAW_FIELD);
4958 // continue network game after request
4959 if (network.enabled &&
4960 game_status == GAME_MODE_PLAYING &&
4961 !game.all_players_gone &&
4962 req_state & REQUEST_WAIT_FOR_INPUT)
4963 SendToServer_ContinuePlaying();
4965 // restore deactivated drawing when quick-loading level tape recording
4966 if (tape.playing && tape.deactivate_display)
4967 TapeDeactivateDisplayOn();
4972 static boolean RequestEnvelope(char *text, unsigned int req_state)
4974 int draw_buffer_last = GetDrawtoField();
4977 if (game_status == GAME_MODE_PLAYING)
4978 BlitScreenToBitmap(backbuffer);
4980 // disable deactivated drawing when quick-loading level tape recording
4981 if (tape.playing && tape.deactivate_display)
4982 TapeDeactivateDisplayOff(TRUE);
4984 SetMouseCursor(CURSOR_DEFAULT);
4986 // pause network game while waiting for request to answer
4987 if (network.enabled &&
4988 game_status == GAME_MODE_PLAYING &&
4989 !game.all_players_gone &&
4990 req_state & REQUEST_WAIT_FOR_INPUT)
4991 SendToServer_PausePlaying();
4993 // simulate releasing mouse button over last gadget, if still pressed
4995 HandleGadgets(-1, -1, 0);
4999 // (replace with setting corresponding request background)
5000 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5001 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5003 // clear door drawing field
5004 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5006 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5008 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5010 if (game_status == GAME_MODE_PLAYING)
5012 SetPanelBackground();
5013 SetDrawBackgroundMask(REDRAW_DOOR_1);
5017 SetDrawBackgroundMask(REDRAW_FIELD);
5023 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5025 // ---------- handle request buttons ----------
5026 result = RequestHandleEvents(req_state, draw_buffer_last);
5030 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5034 if (game_status == GAME_MODE_PLAYING)
5036 SetPanelBackground();
5037 SetDrawBackgroundMask(REDRAW_DOOR_1);
5041 SetDrawBackgroundMask(REDRAW_FIELD);
5044 // continue network game after request
5045 if (network.enabled &&
5046 game_status == GAME_MODE_PLAYING &&
5047 !game.all_players_gone &&
5048 req_state & REQUEST_WAIT_FOR_INPUT)
5049 SendToServer_ContinuePlaying();
5051 // restore deactivated drawing when quick-loading level tape recording
5052 if (tape.playing && tape.deactivate_display)
5053 TapeDeactivateDisplayOn();
5058 boolean Request(char *text, unsigned int req_state)
5060 boolean overlay_enabled = GetOverlayEnabled();
5063 game.request_active_or_moving = TRUE;
5065 SetOverlayEnabled(FALSE);
5067 if (global.use_envelope_request)
5068 result = RequestEnvelope(text, req_state);
5070 result = RequestDoor(text, req_state);
5072 SetOverlayEnabled(overlay_enabled);
5074 game.request_active_or_moving = FALSE;
5079 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5081 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5082 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5085 if (dpo1->sort_priority != dpo2->sort_priority)
5086 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5088 compare_result = dpo1->nr - dpo2->nr;
5090 return compare_result;
5093 void InitGraphicCompatibilityInfo_Doors(void)
5099 struct DoorInfo *door;
5103 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5104 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5106 { -1, -1, -1, NULL }
5108 struct Rect door_rect_list[] =
5110 { DX, DY, DXSIZE, DYSIZE },
5111 { VX, VY, VXSIZE, VYSIZE }
5115 for (i = 0; doors[i].door_token != -1; i++)
5117 int door_token = doors[i].door_token;
5118 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5119 int part_1 = doors[i].part_1;
5120 int part_8 = doors[i].part_8;
5121 int part_2 = part_1 + 1;
5122 int part_3 = part_1 + 2;
5123 struct DoorInfo *door = doors[i].door;
5124 struct Rect *door_rect = &door_rect_list[door_index];
5125 boolean door_gfx_redefined = FALSE;
5127 // check if any door part graphic definitions have been redefined
5129 for (j = 0; door_part_controls[j].door_token != -1; j++)
5131 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5132 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5134 if (dpc->door_token == door_token && fi->redefined)
5135 door_gfx_redefined = TRUE;
5138 // check for old-style door graphic/animation modifications
5140 if (!door_gfx_redefined)
5142 if (door->anim_mode & ANIM_STATIC_PANEL)
5144 door->panel.step_xoffset = 0;
5145 door->panel.step_yoffset = 0;
5148 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5150 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5151 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5152 int num_door_steps, num_panel_steps;
5154 // remove door part graphics other than the two default wings
5156 for (j = 0; door_part_controls[j].door_token != -1; j++)
5158 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5159 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5161 if (dpc->graphic >= part_3 &&
5162 dpc->graphic <= part_8)
5166 // set graphics and screen positions of the default wings
5168 g_part_1->width = door_rect->width;
5169 g_part_1->height = door_rect->height;
5170 g_part_2->width = door_rect->width;
5171 g_part_2->height = door_rect->height;
5172 g_part_2->src_x = door_rect->width;
5173 g_part_2->src_y = g_part_1->src_y;
5175 door->part_2.x = door->part_1.x;
5176 door->part_2.y = door->part_1.y;
5178 if (door->width != -1)
5180 g_part_1->width = door->width;
5181 g_part_2->width = door->width;
5183 // special treatment for graphics and screen position of right wing
5184 g_part_2->src_x += door_rect->width - door->width;
5185 door->part_2.x += door_rect->width - door->width;
5188 if (door->height != -1)
5190 g_part_1->height = door->height;
5191 g_part_2->height = door->height;
5193 // special treatment for graphics and screen position of bottom wing
5194 g_part_2->src_y += door_rect->height - door->height;
5195 door->part_2.y += door_rect->height - door->height;
5198 // set animation delays for the default wings and panels
5200 door->part_1.step_delay = door->step_delay;
5201 door->part_2.step_delay = door->step_delay;
5202 door->panel.step_delay = door->step_delay;
5204 // set animation draw order for the default wings
5206 door->part_1.sort_priority = 2; // draw left wing over ...
5207 door->part_2.sort_priority = 1; // ... right wing
5209 // set animation draw offset for the default wings
5211 if (door->anim_mode & ANIM_HORIZONTAL)
5213 door->part_1.step_xoffset = door->step_offset;
5214 door->part_1.step_yoffset = 0;
5215 door->part_2.step_xoffset = door->step_offset * -1;
5216 door->part_2.step_yoffset = 0;
5218 num_door_steps = g_part_1->width / door->step_offset;
5220 else // ANIM_VERTICAL
5222 door->part_1.step_xoffset = 0;
5223 door->part_1.step_yoffset = door->step_offset;
5224 door->part_2.step_xoffset = 0;
5225 door->part_2.step_yoffset = door->step_offset * -1;
5227 num_door_steps = g_part_1->height / door->step_offset;
5230 // set animation draw offset for the default panels
5232 if (door->step_offset > 1)
5234 num_panel_steps = 2 * door_rect->height / door->step_offset;
5235 door->panel.start_step = num_panel_steps - num_door_steps;
5236 door->panel.start_step_closing = door->panel.start_step;
5240 num_panel_steps = door_rect->height / door->step_offset;
5241 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5242 door->panel.start_step_closing = door->panel.start_step;
5243 door->panel.step_delay *= 2;
5250 void InitDoors(void)
5254 for (i = 0; door_part_controls[i].door_token != -1; i++)
5256 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5257 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5259 // initialize "start_step_opening" and "start_step_closing", if needed
5260 if (dpc->pos->start_step_opening == 0 &&
5261 dpc->pos->start_step_closing == 0)
5263 // dpc->pos->start_step_opening = dpc->pos->start_step;
5264 dpc->pos->start_step_closing = dpc->pos->start_step;
5267 // fill structure for door part draw order (sorted below)
5269 dpo->sort_priority = dpc->pos->sort_priority;
5272 // sort door part controls according to sort_priority and graphic number
5273 qsort(door_part_order, MAX_DOOR_PARTS,
5274 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5277 unsigned int OpenDoor(unsigned int door_state)
5279 if (door_state & DOOR_COPY_BACK)
5281 if (door_state & DOOR_OPEN_1)
5282 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5283 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5285 if (door_state & DOOR_OPEN_2)
5286 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5287 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5289 door_state &= ~DOOR_COPY_BACK;
5292 return MoveDoor(door_state);
5295 unsigned int CloseDoor(unsigned int door_state)
5297 unsigned int old_door_state = GetDoorState();
5299 if (!(door_state & DOOR_NO_COPY_BACK))
5301 if (old_door_state & DOOR_OPEN_1)
5302 BlitBitmap(backbuffer, bitmap_db_door_1,
5303 DX, DY, DXSIZE, DYSIZE, 0, 0);
5305 if (old_door_state & DOOR_OPEN_2)
5306 BlitBitmap(backbuffer, bitmap_db_door_2,
5307 VX, VY, VXSIZE, VYSIZE, 0, 0);
5309 door_state &= ~DOOR_NO_COPY_BACK;
5312 return MoveDoor(door_state);
5315 unsigned int GetDoorState(void)
5317 return MoveDoor(DOOR_GET_STATE);
5320 unsigned int SetDoorState(unsigned int door_state)
5322 return MoveDoor(door_state | DOOR_SET_STATE);
5325 static int euclid(int a, int b)
5327 return (b ? euclid(b, a % b) : a);
5330 unsigned int MoveDoor(unsigned int door_state)
5332 struct Rect door_rect_list[] =
5334 { DX, DY, DXSIZE, DYSIZE },
5335 { VX, VY, VXSIZE, VYSIZE }
5337 static int door1 = DOOR_CLOSE_1;
5338 static int door2 = DOOR_CLOSE_2;
5339 unsigned int door_delay = 0;
5340 unsigned int door_delay_value;
5343 if (door_state == DOOR_GET_STATE)
5344 return (door1 | door2);
5346 if (door_state & DOOR_SET_STATE)
5348 if (door_state & DOOR_ACTION_1)
5349 door1 = door_state & DOOR_ACTION_1;
5350 if (door_state & DOOR_ACTION_2)
5351 door2 = door_state & DOOR_ACTION_2;
5353 return (door1 | door2);
5356 if (!(door_state & DOOR_FORCE_REDRAW))
5358 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5359 door_state &= ~DOOR_OPEN_1;
5360 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5361 door_state &= ~DOOR_CLOSE_1;
5362 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5363 door_state &= ~DOOR_OPEN_2;
5364 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5365 door_state &= ~DOOR_CLOSE_2;
5368 if (global.autoplay_leveldir)
5370 door_state |= DOOR_NO_DELAY;
5371 door_state &= ~DOOR_CLOSE_ALL;
5374 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5375 door_state |= DOOR_NO_DELAY;
5377 if (door_state & DOOR_ACTION)
5379 boolean door_panel_drawn[NUM_DOORS];
5380 boolean panel_has_doors[NUM_DOORS];
5381 boolean door_part_skip[MAX_DOOR_PARTS];
5382 boolean door_part_done[MAX_DOOR_PARTS];
5383 boolean door_part_done_all;
5384 int num_steps[MAX_DOOR_PARTS];
5385 int max_move_delay = 0; // delay for complete animations of all doors
5386 int max_step_delay = 0; // delay (ms) between two animation frames
5387 int num_move_steps = 0; // number of animation steps for all doors
5388 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5389 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5393 for (i = 0; i < NUM_DOORS; i++)
5394 panel_has_doors[i] = FALSE;
5396 for (i = 0; i < MAX_DOOR_PARTS; i++)
5398 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5399 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5400 int door_token = dpc->door_token;
5402 door_part_done[i] = FALSE;
5403 door_part_skip[i] = (!(door_state & door_token) ||
5407 for (i = 0; i < MAX_DOOR_PARTS; i++)
5409 int nr = door_part_order[i].nr;
5410 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5411 struct DoorPartPosInfo *pos = dpc->pos;
5412 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5413 int door_token = dpc->door_token;
5414 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5415 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5416 int step_xoffset = ABS(pos->step_xoffset);
5417 int step_yoffset = ABS(pos->step_yoffset);
5418 int step_delay = pos->step_delay;
5419 int current_door_state = door_state & door_token;
5420 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5421 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5422 boolean part_opening = (is_panel ? door_closing : door_opening);
5423 int start_step = (part_opening ? pos->start_step_opening :
5424 pos->start_step_closing);
5425 float move_xsize = (step_xoffset ? g->width : 0);
5426 float move_ysize = (step_yoffset ? g->height : 0);
5427 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5428 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5429 int move_steps = (move_xsteps && move_ysteps ?
5430 MIN(move_xsteps, move_ysteps) :
5431 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5432 int move_delay = move_steps * step_delay;
5434 if (door_part_skip[nr])
5437 max_move_delay = MAX(max_move_delay, move_delay);
5438 max_step_delay = (max_step_delay == 0 ? step_delay :
5439 euclid(max_step_delay, step_delay));
5440 num_steps[nr] = move_steps;
5444 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5446 panel_has_doors[door_index] = TRUE;
5450 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5452 num_move_steps = max_move_delay / max_step_delay;
5453 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5455 door_delay_value = max_step_delay;
5457 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5459 start = num_move_steps - 1;
5463 // opening door sound has priority over simultaneously closing door
5464 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5466 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5468 if (door_state & DOOR_OPEN_1)
5469 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5470 if (door_state & DOOR_OPEN_2)
5471 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5473 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5475 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5477 if (door_state & DOOR_CLOSE_1)
5478 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5479 if (door_state & DOOR_CLOSE_2)
5480 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5484 for (k = start; k < num_move_steps; k++)
5486 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5488 door_part_done_all = TRUE;
5490 for (i = 0; i < NUM_DOORS; i++)
5491 door_panel_drawn[i] = FALSE;
5493 for (i = 0; i < MAX_DOOR_PARTS; i++)
5495 int nr = door_part_order[i].nr;
5496 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5497 struct DoorPartPosInfo *pos = dpc->pos;
5498 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5499 int door_token = dpc->door_token;
5500 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5501 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5502 boolean is_panel_and_door_has_closed = FALSE;
5503 struct Rect *door_rect = &door_rect_list[door_index];
5504 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5506 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5507 int current_door_state = door_state & door_token;
5508 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5509 boolean door_closing = !door_opening;
5510 boolean part_opening = (is_panel ? door_closing : door_opening);
5511 boolean part_closing = !part_opening;
5512 int start_step = (part_opening ? pos->start_step_opening :
5513 pos->start_step_closing);
5514 int step_delay = pos->step_delay;
5515 int step_factor = step_delay / max_step_delay;
5516 int k1 = (step_factor ? k / step_factor + 1 : k);
5517 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5518 int kk = MAX(0, k2);
5521 int src_x, src_y, src_xx, src_yy;
5522 int dst_x, dst_y, dst_xx, dst_yy;
5525 if (door_part_skip[nr])
5528 if (!(door_state & door_token))
5536 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5537 int kk_door = MAX(0, k2_door);
5538 int sync_frame = kk_door * door_delay_value;
5539 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5541 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5542 &g_src_x, &g_src_y);
5547 if (!door_panel_drawn[door_index])
5549 ClearRectangle(drawto, door_rect->x, door_rect->y,
5550 door_rect->width, door_rect->height);
5552 door_panel_drawn[door_index] = TRUE;
5555 // draw opening or closing door parts
5557 if (pos->step_xoffset < 0) // door part on right side
5560 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5563 if (dst_xx + width > door_rect->width)
5564 width = door_rect->width - dst_xx;
5566 else // door part on left side
5569 dst_xx = pos->x - kk * pos->step_xoffset;
5573 src_xx = ABS(dst_xx);
5577 width = g->width - src_xx;
5579 if (width > door_rect->width)
5580 width = door_rect->width;
5582 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5585 if (pos->step_yoffset < 0) // door part on bottom side
5588 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5591 if (dst_yy + height > door_rect->height)
5592 height = door_rect->height - dst_yy;
5594 else // door part on top side
5597 dst_yy = pos->y - kk * pos->step_yoffset;
5601 src_yy = ABS(dst_yy);
5605 height = g->height - src_yy;
5608 src_x = g_src_x + src_xx;
5609 src_y = g_src_y + src_yy;
5611 dst_x = door_rect->x + dst_xx;
5612 dst_y = door_rect->y + dst_yy;
5614 is_panel_and_door_has_closed =
5617 panel_has_doors[door_index] &&
5618 k >= num_move_steps_doors_only - 1);
5620 if (width >= 0 && width <= g->width &&
5621 height >= 0 && height <= g->height &&
5622 !is_panel_and_door_has_closed)
5624 if (is_panel || !pos->draw_masked)
5625 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5628 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5632 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5634 if ((part_opening && (width < 0 || height < 0)) ||
5635 (part_closing && (width >= g->width && height >= g->height)))
5636 door_part_done[nr] = TRUE;
5638 // continue door part animations, but not panel after door has closed
5639 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5640 door_part_done_all = FALSE;
5643 if (!(door_state & DOOR_NO_DELAY))
5647 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5649 // prevent OS (Windows) from complaining about program not responding
5653 if (door_part_done_all)
5657 if (!(door_state & DOOR_NO_DELAY))
5659 // wait for specified door action post delay
5660 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5661 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5662 else if (door_state & DOOR_ACTION_1)
5663 door_delay_value = door_1.post_delay;
5664 else if (door_state & DOOR_ACTION_2)
5665 door_delay_value = door_2.post_delay;
5667 while (!DelayReached(&door_delay, door_delay_value))
5672 if (door_state & DOOR_ACTION_1)
5673 door1 = door_state & DOOR_ACTION_1;
5674 if (door_state & DOOR_ACTION_2)
5675 door2 = door_state & DOOR_ACTION_2;
5677 // draw masked border over door area
5678 DrawMaskedBorder(REDRAW_DOOR_1);
5679 DrawMaskedBorder(REDRAW_DOOR_2);
5681 ClearAutoRepeatKeyEvents();
5683 return (door1 | door2);
5686 static boolean useSpecialEditorDoor(void)
5688 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5689 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5691 // do not draw special editor door if editor border defined or redefined
5692 if (graphic_info[graphic].bitmap != NULL || redefined)
5695 // do not draw special editor door if global border defined to be empty
5696 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5699 // do not draw special editor door if viewport definitions do not match
5703 EY + EYSIZE != VY + VYSIZE)
5709 void DrawSpecialEditorDoor(void)
5711 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5712 int top_border_width = gfx1->width;
5713 int top_border_height = gfx1->height;
5714 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5715 int ex = EX - outer_border;
5716 int ey = EY - outer_border;
5717 int vy = VY - outer_border;
5718 int exsize = EXSIZE + 2 * outer_border;
5720 if (!useSpecialEditorDoor())
5723 // draw bigger level editor toolbox window
5724 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5725 top_border_width, top_border_height, ex, ey - top_border_height);
5726 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5727 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5729 redraw_mask |= REDRAW_ALL;
5732 void UndrawSpecialEditorDoor(void)
5734 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5735 int top_border_width = gfx1->width;
5736 int top_border_height = gfx1->height;
5737 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5738 int ex = EX - outer_border;
5739 int ey = EY - outer_border;
5740 int ey_top = ey - top_border_height;
5741 int exsize = EXSIZE + 2 * outer_border;
5742 int eysize = EYSIZE + 2 * outer_border;
5744 if (!useSpecialEditorDoor())
5747 // draw normal tape recorder window
5748 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5750 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5751 ex, ey_top, top_border_width, top_border_height,
5753 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5754 ex, ey, exsize, eysize, ex, ey);
5758 // if screen background is set to "[NONE]", clear editor toolbox window
5759 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5760 ClearRectangle(drawto, ex, ey, exsize, eysize);
5763 redraw_mask |= REDRAW_ALL;
5767 // ---------- new tool button stuff -------------------------------------------
5772 struct TextPosInfo *pos;
5774 boolean is_touch_button;
5776 } toolbutton_info[NUM_TOOL_BUTTONS] =
5779 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5780 TOOL_CTRL_ID_YES, FALSE, "yes"
5783 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5784 TOOL_CTRL_ID_NO, FALSE, "no"
5787 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5788 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5791 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5792 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5795 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5796 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5799 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5800 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5803 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5804 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5807 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5808 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5811 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5812 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5815 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5816 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5820 void CreateToolButtons(void)
5824 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5826 int graphic = toolbutton_info[i].graphic;
5827 struct GraphicInfo *gfx = &graphic_info[graphic];
5828 struct TextPosInfo *pos = toolbutton_info[i].pos;
5829 struct GadgetInfo *gi;
5830 Bitmap *deco_bitmap = None;
5831 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5832 unsigned int event_mask = GD_EVENT_RELEASED;
5833 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5834 int base_x = (is_touch_button ? 0 : DX);
5835 int base_y = (is_touch_button ? 0 : DY);
5836 int gd_x = gfx->src_x;
5837 int gd_y = gfx->src_y;
5838 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5839 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5844 if (global.use_envelope_request && !is_touch_button)
5846 setRequestPosition(&base_x, &base_y, TRUE);
5848 // check if request buttons are outside of envelope and fix, if needed
5849 if (x < 0 || x + gfx->width > request.width ||
5850 y < 0 || y + gfx->height > request.height)
5852 if (id == TOOL_CTRL_ID_YES)
5855 y = request.height - 2 * request.border_size - gfx->height;
5857 else if (id == TOOL_CTRL_ID_NO)
5859 x = request.width - 2 * request.border_size - gfx->width;
5860 y = request.height - 2 * request.border_size - gfx->height;
5862 else if (id == TOOL_CTRL_ID_CONFIRM)
5864 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5865 y = request.height - 2 * request.border_size - gfx->height;
5867 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5869 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5871 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5872 y = request.height - 2 * request.border_size - gfx->height * 2;
5874 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5875 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5880 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5882 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5884 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5885 pos->size, &deco_bitmap, &deco_x, &deco_y);
5886 deco_xpos = (gfx->width - pos->size) / 2;
5887 deco_ypos = (gfx->height - pos->size) / 2;
5890 gi = CreateGadget(GDI_CUSTOM_ID, id,
5891 GDI_IMAGE_ID, graphic,
5892 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5895 GDI_WIDTH, gfx->width,
5896 GDI_HEIGHT, gfx->height,
5897 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5898 GDI_STATE, GD_BUTTON_UNPRESSED,
5899 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5900 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5901 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5902 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5903 GDI_DECORATION_SIZE, pos->size, pos->size,
5904 GDI_DECORATION_SHIFTING, 1, 1,
5905 GDI_DIRECT_DRAW, FALSE,
5906 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5907 GDI_EVENT_MASK, event_mask,
5908 GDI_CALLBACK_ACTION, HandleToolButtons,
5912 Fail("cannot create gadget");
5914 tool_gadget[id] = gi;
5918 void FreeToolButtons(void)
5922 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5923 FreeGadget(tool_gadget[i]);
5926 static void UnmapToolButtons(void)
5930 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5931 UnmapGadget(tool_gadget[i]);
5934 static void HandleToolButtons(struct GadgetInfo *gi)
5936 request_gadget_id = gi->custom_id;
5939 static struct Mapping_EM_to_RND_object
5942 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5943 boolean is_backside; // backside of moving element
5949 em_object_mapping_list[GAME_TILE_MAX + 1] =
5952 Zborder, FALSE, FALSE,
5956 Zplayer, FALSE, FALSE,
5965 Ztank, FALSE, FALSE,
5969 Zeater, FALSE, FALSE,
5973 Zdynamite, FALSE, FALSE,
5977 Zboom, FALSE, FALSE,
5982 Xchain, FALSE, FALSE,
5983 EL_DEFAULT, ACTION_EXPLODING, -1
5986 Xboom_bug, FALSE, FALSE,
5987 EL_BUG, ACTION_EXPLODING, -1
5990 Xboom_tank, FALSE, FALSE,
5991 EL_SPACESHIP, ACTION_EXPLODING, -1
5994 Xboom_android, FALSE, FALSE,
5995 EL_EMC_ANDROID, ACTION_OTHER, -1
5998 Xboom_1, FALSE, FALSE,
5999 EL_DEFAULT, ACTION_EXPLODING, -1
6002 Xboom_2, FALSE, FALSE,
6003 EL_DEFAULT, ACTION_EXPLODING, -1
6007 Xblank, TRUE, FALSE,
6012 Xsplash_e, FALSE, FALSE,
6013 EL_ACID_SPLASH_RIGHT, -1, -1
6016 Xsplash_w, FALSE, FALSE,
6017 EL_ACID_SPLASH_LEFT, -1, -1
6021 Xplant, TRUE, FALSE,
6022 EL_EMC_PLANT, -1, -1
6025 Yplant, FALSE, FALSE,
6026 EL_EMC_PLANT, -1, -1
6030 Xacid_1, TRUE, FALSE,
6034 Xacid_2, FALSE, FALSE,
6038 Xacid_3, FALSE, FALSE,
6042 Xacid_4, FALSE, FALSE,
6046 Xacid_5, FALSE, FALSE,
6050 Xacid_6, FALSE, FALSE,
6054 Xacid_7, FALSE, FALSE,
6058 Xacid_8, FALSE, FALSE,
6063 Xfake_acid_1, TRUE, FALSE,
6064 EL_EMC_FAKE_ACID, -1, -1
6067 Xfake_acid_2, FALSE, FALSE,
6068 EL_EMC_FAKE_ACID, -1, -1
6071 Xfake_acid_3, FALSE, FALSE,
6072 EL_EMC_FAKE_ACID, -1, -1
6075 Xfake_acid_4, FALSE, FALSE,
6076 EL_EMC_FAKE_ACID, -1, -1
6079 Xfake_acid_5, FALSE, FALSE,
6080 EL_EMC_FAKE_ACID, -1, -1
6083 Xfake_acid_6, FALSE, FALSE,
6084 EL_EMC_FAKE_ACID, -1, -1
6087 Xfake_acid_7, FALSE, FALSE,
6088 EL_EMC_FAKE_ACID, -1, -1
6091 Xfake_acid_8, FALSE, FALSE,
6092 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_1_player, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_2_player, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_3_player, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_4_player, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_5_player, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_6_player, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_7_player, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6124 Xfake_acid_8_player, FALSE, FALSE,
6125 EL_EMC_FAKE_ACID, -1, -1
6129 Xgrass, TRUE, FALSE,
6130 EL_EMC_GRASS, -1, -1
6133 Ygrass_nB, FALSE, FALSE,
6134 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6137 Ygrass_eB, FALSE, FALSE,
6138 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6141 Ygrass_sB, FALSE, FALSE,
6142 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6145 Ygrass_wB, FALSE, FALSE,
6146 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6154 Ydirt_nB, FALSE, FALSE,
6155 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6158 Ydirt_eB, FALSE, FALSE,
6159 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6162 Ydirt_sB, FALSE, FALSE,
6163 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6166 Ydirt_wB, FALSE, FALSE,
6167 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6171 Xandroid, TRUE, FALSE,
6172 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6175 Xandroid_1_n, FALSE, FALSE,
6176 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6179 Xandroid_2_n, FALSE, FALSE,
6180 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6183 Xandroid_1_e, FALSE, FALSE,
6184 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6187 Xandroid_2_e, FALSE, FALSE,
6188 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6191 Xandroid_1_w, FALSE, FALSE,
6192 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6195 Xandroid_2_w, FALSE, FALSE,
6196 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6199 Xandroid_1_s, FALSE, FALSE,
6200 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6203 Xandroid_2_s, FALSE, FALSE,
6204 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6207 Yandroid_n, FALSE, FALSE,
6208 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6211 Yandroid_nB, FALSE, TRUE,
6212 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6215 Yandroid_ne, FALSE, FALSE,
6216 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6219 Yandroid_neB, FALSE, TRUE,
6220 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6223 Yandroid_e, FALSE, FALSE,
6224 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6227 Yandroid_eB, FALSE, TRUE,
6228 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6231 Yandroid_se, FALSE, FALSE,
6232 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6235 Yandroid_seB, FALSE, TRUE,
6236 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6239 Yandroid_s, FALSE, FALSE,
6240 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6243 Yandroid_sB, FALSE, TRUE,
6244 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6247 Yandroid_sw, FALSE, FALSE,
6248 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6251 Yandroid_swB, FALSE, TRUE,
6252 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6255 Yandroid_w, FALSE, FALSE,
6256 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6259 Yandroid_wB, FALSE, TRUE,
6260 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6263 Yandroid_nw, FALSE, FALSE,
6264 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6267 Yandroid_nwB, FALSE, TRUE,
6268 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6272 Xeater_n, TRUE, FALSE,
6273 EL_YAMYAM_UP, -1, -1
6276 Xeater_e, TRUE, FALSE,
6277 EL_YAMYAM_RIGHT, -1, -1
6280 Xeater_w, TRUE, FALSE,
6281 EL_YAMYAM_LEFT, -1, -1
6284 Xeater_s, TRUE, FALSE,
6285 EL_YAMYAM_DOWN, -1, -1
6288 Yeater_n, FALSE, FALSE,
6289 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6292 Yeater_nB, FALSE, TRUE,
6293 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6296 Yeater_e, FALSE, FALSE,
6297 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6300 Yeater_eB, FALSE, TRUE,
6301 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6304 Yeater_s, FALSE, FALSE,
6305 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6308 Yeater_sB, FALSE, TRUE,
6309 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6312 Yeater_w, FALSE, FALSE,
6313 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6316 Yeater_wB, FALSE, TRUE,
6317 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6320 Yeater_stone, FALSE, FALSE,
6321 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6324 Yeater_spring, FALSE, FALSE,
6325 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6329 Xalien, TRUE, FALSE,
6333 Xalien_pause, FALSE, FALSE,
6337 Yalien_n, FALSE, FALSE,
6338 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6341 Yalien_nB, FALSE, TRUE,
6342 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6345 Yalien_e, FALSE, FALSE,
6346 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6349 Yalien_eB, FALSE, TRUE,
6350 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6353 Yalien_s, FALSE, FALSE,
6354 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6357 Yalien_sB, FALSE, TRUE,
6358 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6361 Yalien_w, FALSE, FALSE,
6362 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6365 Yalien_wB, FALSE, TRUE,
6366 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6369 Yalien_stone, FALSE, FALSE,
6370 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6373 Yalien_spring, FALSE, FALSE,
6374 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6378 Xbug_1_n, TRUE, FALSE,
6382 Xbug_1_e, TRUE, FALSE,
6383 EL_BUG_RIGHT, -1, -1
6386 Xbug_1_s, TRUE, FALSE,
6390 Xbug_1_w, TRUE, FALSE,
6394 Xbug_2_n, FALSE, FALSE,
6398 Xbug_2_e, FALSE, FALSE,
6399 EL_BUG_RIGHT, -1, -1
6402 Xbug_2_s, FALSE, FALSE,
6406 Xbug_2_w, FALSE, FALSE,
6410 Ybug_n, FALSE, FALSE,
6411 EL_BUG, ACTION_MOVING, MV_BIT_UP
6414 Ybug_nB, FALSE, TRUE,
6415 EL_BUG, ACTION_MOVING, MV_BIT_UP
6418 Ybug_e, FALSE, FALSE,
6419 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6422 Ybug_eB, FALSE, TRUE,
6423 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6426 Ybug_s, FALSE, FALSE,
6427 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6430 Ybug_sB, FALSE, TRUE,
6431 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6434 Ybug_w, FALSE, FALSE,
6435 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6438 Ybug_wB, FALSE, TRUE,
6439 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6442 Ybug_w_n, FALSE, FALSE,
6443 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6446 Ybug_n_e, FALSE, FALSE,
6447 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6450 Ybug_e_s, FALSE, FALSE,
6451 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6454 Ybug_s_w, FALSE, FALSE,
6455 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6458 Ybug_e_n, FALSE, FALSE,
6459 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6462 Ybug_s_e, FALSE, FALSE,
6463 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6466 Ybug_w_s, FALSE, FALSE,
6467 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6470 Ybug_n_w, FALSE, FALSE,
6471 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6474 Ybug_stone, FALSE, FALSE,
6475 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6478 Ybug_spring, FALSE, FALSE,
6479 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6483 Xtank_1_n, TRUE, FALSE,
6484 EL_SPACESHIP_UP, -1, -1
6487 Xtank_1_e, TRUE, FALSE,
6488 EL_SPACESHIP_RIGHT, -1, -1
6491 Xtank_1_s, TRUE, FALSE,
6492 EL_SPACESHIP_DOWN, -1, -1
6495 Xtank_1_w, TRUE, FALSE,
6496 EL_SPACESHIP_LEFT, -1, -1
6499 Xtank_2_n, FALSE, FALSE,
6500 EL_SPACESHIP_UP, -1, -1
6503 Xtank_2_e, FALSE, FALSE,
6504 EL_SPACESHIP_RIGHT, -1, -1
6507 Xtank_2_s, FALSE, FALSE,
6508 EL_SPACESHIP_DOWN, -1, -1
6511 Xtank_2_w, FALSE, FALSE,
6512 EL_SPACESHIP_LEFT, -1, -1
6515 Ytank_n, FALSE, FALSE,
6516 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6519 Ytank_nB, FALSE, TRUE,
6520 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6523 Ytank_e, FALSE, FALSE,
6524 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6527 Ytank_eB, FALSE, TRUE,
6528 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6531 Ytank_s, FALSE, FALSE,
6532 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6535 Ytank_sB, FALSE, TRUE,
6536 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6539 Ytank_w, FALSE, FALSE,
6540 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6543 Ytank_wB, FALSE, TRUE,
6544 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6547 Ytank_w_n, FALSE, FALSE,
6548 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6551 Ytank_n_e, FALSE, FALSE,
6552 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6555 Ytank_e_s, FALSE, FALSE,
6556 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6559 Ytank_s_w, FALSE, FALSE,
6560 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6563 Ytank_e_n, FALSE, FALSE,
6564 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6567 Ytank_s_e, FALSE, FALSE,
6568 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6571 Ytank_w_s, FALSE, FALSE,
6572 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6575 Ytank_n_w, FALSE, FALSE,
6576 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6579 Ytank_stone, FALSE, FALSE,
6580 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6583 Ytank_spring, FALSE, FALSE,
6584 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6588 Xemerald, TRUE, FALSE,
6592 Xemerald_pause, FALSE, FALSE,
6596 Xemerald_fall, FALSE, FALSE,
6600 Xemerald_shine, FALSE, FALSE,
6601 EL_EMERALD, ACTION_TWINKLING, -1
6604 Yemerald_s, FALSE, FALSE,
6605 EL_EMERALD, ACTION_FALLING, -1
6608 Yemerald_sB, FALSE, TRUE,
6609 EL_EMERALD, ACTION_FALLING, -1
6612 Yemerald_e, FALSE, FALSE,
6613 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6616 Yemerald_eB, FALSE, TRUE,
6617 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6620 Yemerald_w, FALSE, FALSE,
6621 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6624 Yemerald_wB, FALSE, TRUE,
6625 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6628 Yemerald_blank, FALSE, FALSE,
6629 EL_EMERALD, ACTION_COLLECTING, -1
6633 Xdiamond, TRUE, FALSE,
6637 Xdiamond_pause, FALSE, FALSE,
6641 Xdiamond_fall, FALSE, FALSE,
6645 Xdiamond_shine, FALSE, FALSE,
6646 EL_DIAMOND, ACTION_TWINKLING, -1
6649 Ydiamond_s, FALSE, FALSE,
6650 EL_DIAMOND, ACTION_FALLING, -1
6653 Ydiamond_sB, FALSE, TRUE,
6654 EL_DIAMOND, ACTION_FALLING, -1
6657 Ydiamond_e, FALSE, FALSE,
6658 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6661 Ydiamond_eB, FALSE, TRUE,
6662 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6665 Ydiamond_w, FALSE, FALSE,
6666 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6669 Ydiamond_wB, FALSE, TRUE,
6670 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6673 Ydiamond_blank, FALSE, FALSE,
6674 EL_DIAMOND, ACTION_COLLECTING, -1
6677 Ydiamond_stone, FALSE, FALSE,
6678 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6682 Xstone, TRUE, FALSE,
6686 Xstone_pause, FALSE, FALSE,
6690 Xstone_fall, FALSE, FALSE,
6694 Ystone_s, FALSE, FALSE,
6695 EL_ROCK, ACTION_FALLING, -1
6698 Ystone_sB, FALSE, TRUE,
6699 EL_ROCK, ACTION_FALLING, -1
6702 Ystone_e, FALSE, FALSE,
6703 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6706 Ystone_eB, FALSE, TRUE,
6707 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6710 Ystone_w, FALSE, FALSE,
6711 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6714 Ystone_wB, FALSE, TRUE,
6715 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6723 Xbomb_pause, FALSE, FALSE,
6727 Xbomb_fall, FALSE, FALSE,
6731 Ybomb_s, FALSE, FALSE,
6732 EL_BOMB, ACTION_FALLING, -1
6735 Ybomb_sB, FALSE, TRUE,
6736 EL_BOMB, ACTION_FALLING, -1
6739 Ybomb_e, FALSE, FALSE,
6740 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6743 Ybomb_eB, FALSE, TRUE,
6744 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6747 Ybomb_w, FALSE, FALSE,
6748 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6751 Ybomb_wB, FALSE, TRUE,
6752 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6755 Ybomb_blank, FALSE, FALSE,
6756 EL_BOMB, ACTION_ACTIVATING, -1
6764 Xnut_pause, FALSE, FALSE,
6768 Xnut_fall, FALSE, FALSE,
6772 Ynut_s, FALSE, FALSE,
6773 EL_NUT, ACTION_FALLING, -1
6776 Ynut_sB, FALSE, TRUE,
6777 EL_NUT, ACTION_FALLING, -1
6780 Ynut_e, FALSE, FALSE,
6781 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6784 Ynut_eB, FALSE, TRUE,
6785 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6788 Ynut_w, FALSE, FALSE,
6789 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6792 Ynut_wB, FALSE, TRUE,
6793 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6796 Ynut_stone, FALSE, FALSE,
6797 EL_NUT, ACTION_BREAKING, -1
6801 Xspring, TRUE, FALSE,
6805 Xspring_pause, FALSE, FALSE,
6809 Xspring_e, TRUE, FALSE,
6810 EL_SPRING_RIGHT, -1, -1
6813 Xspring_w, TRUE, FALSE,
6814 EL_SPRING_LEFT, -1, -1
6817 Xspring_fall, FALSE, FALSE,
6821 Yspring_s, FALSE, FALSE,
6822 EL_SPRING, ACTION_FALLING, -1
6825 Yspring_sB, FALSE, TRUE,
6826 EL_SPRING, ACTION_FALLING, -1
6829 Yspring_e, FALSE, FALSE,
6830 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6833 Yspring_eB, FALSE, TRUE,
6834 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6837 Yspring_w, FALSE, FALSE,
6838 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6841 Yspring_wB, FALSE, TRUE,
6842 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6845 Yspring_alien_e, FALSE, FALSE,
6846 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6849 Yspring_alien_eB, FALSE, TRUE,
6850 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6853 Yspring_alien_w, FALSE, FALSE,
6854 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6857 Yspring_alien_wB, FALSE, TRUE,
6858 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6862 Xpush_emerald_e, FALSE, FALSE,
6863 EL_EMERALD, -1, MV_BIT_RIGHT
6866 Xpush_emerald_w, FALSE, FALSE,
6867 EL_EMERALD, -1, MV_BIT_LEFT
6870 Xpush_diamond_e, FALSE, FALSE,
6871 EL_DIAMOND, -1, MV_BIT_RIGHT
6874 Xpush_diamond_w, FALSE, FALSE,
6875 EL_DIAMOND, -1, MV_BIT_LEFT
6878 Xpush_stone_e, FALSE, FALSE,
6879 EL_ROCK, -1, MV_BIT_RIGHT
6882 Xpush_stone_w, FALSE, FALSE,
6883 EL_ROCK, -1, MV_BIT_LEFT
6886 Xpush_bomb_e, FALSE, FALSE,
6887 EL_BOMB, -1, MV_BIT_RIGHT
6890 Xpush_bomb_w, FALSE, FALSE,
6891 EL_BOMB, -1, MV_BIT_LEFT
6894 Xpush_nut_e, FALSE, FALSE,
6895 EL_NUT, -1, MV_BIT_RIGHT
6898 Xpush_nut_w, FALSE, FALSE,
6899 EL_NUT, -1, MV_BIT_LEFT
6902 Xpush_spring_e, FALSE, FALSE,
6903 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6906 Xpush_spring_w, FALSE, FALSE,
6907 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6911 Xdynamite, TRUE, FALSE,
6912 EL_EM_DYNAMITE, -1, -1
6915 Ydynamite_blank, FALSE, FALSE,
6916 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6919 Xdynamite_1, TRUE, FALSE,
6920 EL_EM_DYNAMITE_ACTIVE, -1, -1
6923 Xdynamite_2, FALSE, FALSE,
6924 EL_EM_DYNAMITE_ACTIVE, -1, -1
6927 Xdynamite_3, FALSE, FALSE,
6928 EL_EM_DYNAMITE_ACTIVE, -1, -1
6931 Xdynamite_4, FALSE, FALSE,
6932 EL_EM_DYNAMITE_ACTIVE, -1, -1
6936 Xkey_1, TRUE, FALSE,
6940 Xkey_2, TRUE, FALSE,
6944 Xkey_3, TRUE, FALSE,
6948 Xkey_4, TRUE, FALSE,
6952 Xkey_5, TRUE, FALSE,
6953 EL_EMC_KEY_5, -1, -1
6956 Xkey_6, TRUE, FALSE,
6957 EL_EMC_KEY_6, -1, -1
6960 Xkey_7, TRUE, FALSE,
6961 EL_EMC_KEY_7, -1, -1
6964 Xkey_8, TRUE, FALSE,
6965 EL_EMC_KEY_8, -1, -1
6969 Xdoor_1, TRUE, FALSE,
6970 EL_EM_GATE_1, -1, -1
6973 Xdoor_2, TRUE, FALSE,
6974 EL_EM_GATE_2, -1, -1
6977 Xdoor_3, TRUE, FALSE,
6978 EL_EM_GATE_3, -1, -1
6981 Xdoor_4, TRUE, FALSE,
6982 EL_EM_GATE_4, -1, -1
6985 Xdoor_5, TRUE, FALSE,
6986 EL_EMC_GATE_5, -1, -1
6989 Xdoor_6, TRUE, FALSE,
6990 EL_EMC_GATE_6, -1, -1
6993 Xdoor_7, TRUE, FALSE,
6994 EL_EMC_GATE_7, -1, -1
6997 Xdoor_8, TRUE, FALSE,
6998 EL_EMC_GATE_8, -1, -1
7002 Xfake_door_1, TRUE, FALSE,
7003 EL_EM_GATE_1_GRAY, -1, -1
7006 Xfake_door_2, TRUE, FALSE,
7007 EL_EM_GATE_2_GRAY, -1, -1
7010 Xfake_door_3, TRUE, FALSE,
7011 EL_EM_GATE_3_GRAY, -1, -1
7014 Xfake_door_4, TRUE, FALSE,
7015 EL_EM_GATE_4_GRAY, -1, -1
7018 Xfake_door_5, TRUE, FALSE,
7019 EL_EMC_GATE_5_GRAY, -1, -1
7022 Xfake_door_6, TRUE, FALSE,
7023 EL_EMC_GATE_6_GRAY, -1, -1
7026 Xfake_door_7, TRUE, FALSE,
7027 EL_EMC_GATE_7_GRAY, -1, -1
7030 Xfake_door_8, TRUE, FALSE,
7031 EL_EMC_GATE_8_GRAY, -1, -1
7035 Xballoon, TRUE, FALSE,
7039 Yballoon_n, FALSE, FALSE,
7040 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7043 Yballoon_nB, FALSE, TRUE,
7044 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7047 Yballoon_e, FALSE, FALSE,
7048 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7051 Yballoon_eB, FALSE, TRUE,
7052 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7055 Yballoon_s, FALSE, FALSE,
7056 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7059 Yballoon_sB, FALSE, TRUE,
7060 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7063 Yballoon_w, FALSE, FALSE,
7064 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7067 Yballoon_wB, FALSE, TRUE,
7068 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7072 Xball_1, TRUE, FALSE,
7073 EL_EMC_MAGIC_BALL, -1, -1
7076 Yball_1, FALSE, FALSE,
7077 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7080 Xball_2, FALSE, FALSE,
7081 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7084 Yball_2, FALSE, FALSE,
7085 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7088 Yball_blank, FALSE, FALSE,
7089 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7093 Xamoeba_1, TRUE, FALSE,
7094 EL_AMOEBA_DRY, ACTION_OTHER, -1
7097 Xamoeba_2, FALSE, FALSE,
7098 EL_AMOEBA_DRY, ACTION_OTHER, -1
7101 Xamoeba_3, FALSE, FALSE,
7102 EL_AMOEBA_DRY, ACTION_OTHER, -1
7105 Xamoeba_4, FALSE, FALSE,
7106 EL_AMOEBA_DRY, ACTION_OTHER, -1
7109 Xamoeba_5, TRUE, FALSE,
7110 EL_AMOEBA_WET, ACTION_OTHER, -1
7113 Xamoeba_6, FALSE, FALSE,
7114 EL_AMOEBA_WET, ACTION_OTHER, -1
7117 Xamoeba_7, FALSE, FALSE,
7118 EL_AMOEBA_WET, ACTION_OTHER, -1
7121 Xamoeba_8, FALSE, FALSE,
7122 EL_AMOEBA_WET, ACTION_OTHER, -1
7127 EL_AMOEBA_DROP, ACTION_GROWING, -1
7130 Xdrip_fall, FALSE, FALSE,
7131 EL_AMOEBA_DROP, -1, -1
7134 Xdrip_stretch, FALSE, FALSE,
7135 EL_AMOEBA_DROP, ACTION_FALLING, -1
7138 Xdrip_stretchB, FALSE, TRUE,
7139 EL_AMOEBA_DROP, ACTION_FALLING, -1
7142 Ydrip_1_s, FALSE, FALSE,
7143 EL_AMOEBA_DROP, ACTION_FALLING, -1
7146 Ydrip_1_sB, FALSE, TRUE,
7147 EL_AMOEBA_DROP, ACTION_FALLING, -1
7150 Ydrip_2_s, FALSE, FALSE,
7151 EL_AMOEBA_DROP, ACTION_FALLING, -1
7154 Ydrip_2_sB, FALSE, TRUE,
7155 EL_AMOEBA_DROP, ACTION_FALLING, -1
7159 Xwonderwall, TRUE, FALSE,
7160 EL_MAGIC_WALL, -1, -1
7163 Ywonderwall, FALSE, FALSE,
7164 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7168 Xwheel, TRUE, FALSE,
7169 EL_ROBOT_WHEEL, -1, -1
7172 Ywheel, FALSE, FALSE,
7173 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7177 Xswitch, TRUE, FALSE,
7178 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7181 Yswitch, FALSE, FALSE,
7182 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7186 Xbumper, TRUE, FALSE,
7187 EL_EMC_SPRING_BUMPER, -1, -1
7190 Ybumper, FALSE, FALSE,
7191 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7195 Xacid_nw, TRUE, FALSE,
7196 EL_ACID_POOL_TOPLEFT, -1, -1
7199 Xacid_ne, TRUE, FALSE,
7200 EL_ACID_POOL_TOPRIGHT, -1, -1
7203 Xacid_sw, TRUE, FALSE,
7204 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7207 Xacid_s, TRUE, FALSE,
7208 EL_ACID_POOL_BOTTOM, -1, -1
7211 Xacid_se, TRUE, FALSE,
7212 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7216 Xfake_blank, TRUE, FALSE,
7217 EL_INVISIBLE_WALL, -1, -1
7220 Yfake_blank, FALSE, FALSE,
7221 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7225 Xfake_grass, TRUE, FALSE,
7226 EL_EMC_FAKE_GRASS, -1, -1
7229 Yfake_grass, FALSE, FALSE,
7230 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7234 Xfake_amoeba, TRUE, FALSE,
7235 EL_EMC_DRIPPER, -1, -1
7238 Yfake_amoeba, FALSE, FALSE,
7239 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7243 Xlenses, TRUE, FALSE,
7244 EL_EMC_LENSES, -1, -1
7248 Xmagnify, TRUE, FALSE,
7249 EL_EMC_MAGNIFIER, -1, -1
7254 EL_QUICKSAND_EMPTY, -1, -1
7257 Xsand_stone, TRUE, FALSE,
7258 EL_QUICKSAND_FULL, -1, -1
7261 Xsand_stonein_1, FALSE, TRUE,
7262 EL_ROCK, ACTION_FILLING, -1
7265 Xsand_stonein_2, FALSE, TRUE,
7266 EL_ROCK, ACTION_FILLING, -1
7269 Xsand_stonein_3, FALSE, TRUE,
7270 EL_ROCK, ACTION_FILLING, -1
7273 Xsand_stonein_4, FALSE, TRUE,
7274 EL_ROCK, ACTION_FILLING, -1
7277 Xsand_sandstone_1, FALSE, FALSE,
7278 EL_QUICKSAND_FILLING, -1, -1
7281 Xsand_sandstone_2, FALSE, FALSE,
7282 EL_QUICKSAND_FILLING, -1, -1
7285 Xsand_sandstone_3, FALSE, FALSE,
7286 EL_QUICKSAND_FILLING, -1, -1
7289 Xsand_sandstone_4, FALSE, FALSE,
7290 EL_QUICKSAND_FILLING, -1, -1
7293 Xsand_stonesand_1, FALSE, FALSE,
7294 EL_QUICKSAND_EMPTYING, -1, -1
7297 Xsand_stonesand_2, FALSE, FALSE,
7298 EL_QUICKSAND_EMPTYING, -1, -1
7301 Xsand_stonesand_3, FALSE, FALSE,
7302 EL_QUICKSAND_EMPTYING, -1, -1
7305 Xsand_stonesand_4, FALSE, FALSE,
7306 EL_QUICKSAND_EMPTYING, -1, -1
7309 Xsand_stoneout_1, FALSE, FALSE,
7310 EL_ROCK, ACTION_EMPTYING, -1
7313 Xsand_stoneout_2, FALSE, FALSE,
7314 EL_ROCK, ACTION_EMPTYING, -1
7317 Xsand_stonesand_quickout_1, FALSE, FALSE,
7318 EL_QUICKSAND_EMPTYING, -1, -1
7321 Xsand_stonesand_quickout_2, FALSE, FALSE,
7322 EL_QUICKSAND_EMPTYING, -1, -1
7326 Xslide_ns, TRUE, FALSE,
7327 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7330 Yslide_ns_blank, FALSE, FALSE,
7331 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7334 Xslide_ew, TRUE, FALSE,
7335 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7338 Yslide_ew_blank, FALSE, FALSE,
7339 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7343 Xwind_n, TRUE, FALSE,
7344 EL_BALLOON_SWITCH_UP, -1, -1
7347 Xwind_e, TRUE, FALSE,
7348 EL_BALLOON_SWITCH_RIGHT, -1, -1
7351 Xwind_s, TRUE, FALSE,
7352 EL_BALLOON_SWITCH_DOWN, -1, -1
7355 Xwind_w, TRUE, FALSE,
7356 EL_BALLOON_SWITCH_LEFT, -1, -1
7359 Xwind_any, TRUE, FALSE,
7360 EL_BALLOON_SWITCH_ANY, -1, -1
7363 Xwind_stop, TRUE, FALSE,
7364 EL_BALLOON_SWITCH_NONE, -1, -1
7369 EL_EM_EXIT_CLOSED, -1, -1
7372 Xexit_1, TRUE, FALSE,
7373 EL_EM_EXIT_OPEN, -1, -1
7376 Xexit_2, FALSE, FALSE,
7377 EL_EM_EXIT_OPEN, -1, -1
7380 Xexit_3, FALSE, FALSE,
7381 EL_EM_EXIT_OPEN, -1, -1
7385 Xpause, FALSE, FALSE,
7390 Xwall_1, TRUE, FALSE,
7394 Xwall_2, TRUE, FALSE,
7395 EL_EMC_WALL_14, -1, -1
7398 Xwall_3, TRUE, FALSE,
7399 EL_EMC_WALL_15, -1, -1
7402 Xwall_4, TRUE, FALSE,
7403 EL_EMC_WALL_16, -1, -1
7407 Xroundwall_1, TRUE, FALSE,
7408 EL_WALL_SLIPPERY, -1, -1
7411 Xroundwall_2, TRUE, FALSE,
7412 EL_EMC_WALL_SLIPPERY_2, -1, -1
7415 Xroundwall_3, TRUE, FALSE,
7416 EL_EMC_WALL_SLIPPERY_3, -1, -1
7419 Xroundwall_4, TRUE, FALSE,
7420 EL_EMC_WALL_SLIPPERY_4, -1, -1
7424 Xsteel_1, TRUE, FALSE,
7425 EL_STEELWALL, -1, -1
7428 Xsteel_2, TRUE, FALSE,
7429 EL_EMC_STEELWALL_2, -1, -1
7432 Xsteel_3, TRUE, FALSE,
7433 EL_EMC_STEELWALL_3, -1, -1
7436 Xsteel_4, TRUE, FALSE,
7437 EL_EMC_STEELWALL_4, -1, -1
7441 Xdecor_1, TRUE, FALSE,
7442 EL_EMC_WALL_8, -1, -1
7445 Xdecor_2, TRUE, FALSE,
7446 EL_EMC_WALL_6, -1, -1
7449 Xdecor_3, TRUE, FALSE,
7450 EL_EMC_WALL_4, -1, -1
7453 Xdecor_4, TRUE, FALSE,
7454 EL_EMC_WALL_7, -1, -1
7457 Xdecor_5, TRUE, FALSE,
7458 EL_EMC_WALL_5, -1, -1
7461 Xdecor_6, TRUE, FALSE,
7462 EL_EMC_WALL_9, -1, -1
7465 Xdecor_7, TRUE, FALSE,
7466 EL_EMC_WALL_10, -1, -1
7469 Xdecor_8, TRUE, FALSE,
7470 EL_EMC_WALL_1, -1, -1
7473 Xdecor_9, TRUE, FALSE,
7474 EL_EMC_WALL_2, -1, -1
7477 Xdecor_10, TRUE, FALSE,
7478 EL_EMC_WALL_3, -1, -1
7481 Xdecor_11, TRUE, FALSE,
7482 EL_EMC_WALL_11, -1, -1
7485 Xdecor_12, TRUE, FALSE,
7486 EL_EMC_WALL_12, -1, -1
7490 Xalpha_0, TRUE, FALSE,
7491 EL_CHAR('0'), -1, -1
7494 Xalpha_1, TRUE, FALSE,
7495 EL_CHAR('1'), -1, -1
7498 Xalpha_2, TRUE, FALSE,
7499 EL_CHAR('2'), -1, -1
7502 Xalpha_3, TRUE, FALSE,
7503 EL_CHAR('3'), -1, -1
7506 Xalpha_4, TRUE, FALSE,
7507 EL_CHAR('4'), -1, -1
7510 Xalpha_5, TRUE, FALSE,
7511 EL_CHAR('5'), -1, -1
7514 Xalpha_6, TRUE, FALSE,
7515 EL_CHAR('6'), -1, -1
7518 Xalpha_7, TRUE, FALSE,
7519 EL_CHAR('7'), -1, -1
7522 Xalpha_8, TRUE, FALSE,
7523 EL_CHAR('8'), -1, -1
7526 Xalpha_9, TRUE, FALSE,
7527 EL_CHAR('9'), -1, -1
7530 Xalpha_excla, TRUE, FALSE,
7531 EL_CHAR('!'), -1, -1
7534 Xalpha_apost, TRUE, FALSE,
7535 EL_CHAR('\''), -1, -1
7538 Xalpha_comma, TRUE, FALSE,
7539 EL_CHAR(','), -1, -1
7542 Xalpha_minus, TRUE, FALSE,
7543 EL_CHAR('-'), -1, -1
7546 Xalpha_perio, TRUE, FALSE,
7547 EL_CHAR('.'), -1, -1
7550 Xalpha_colon, TRUE, FALSE,
7551 EL_CHAR(':'), -1, -1
7554 Xalpha_quest, TRUE, FALSE,
7555 EL_CHAR('?'), -1, -1
7558 Xalpha_a, TRUE, FALSE,
7559 EL_CHAR('A'), -1, -1
7562 Xalpha_b, TRUE, FALSE,
7563 EL_CHAR('B'), -1, -1
7566 Xalpha_c, TRUE, FALSE,
7567 EL_CHAR('C'), -1, -1
7570 Xalpha_d, TRUE, FALSE,
7571 EL_CHAR('D'), -1, -1
7574 Xalpha_e, TRUE, FALSE,
7575 EL_CHAR('E'), -1, -1
7578 Xalpha_f, TRUE, FALSE,
7579 EL_CHAR('F'), -1, -1
7582 Xalpha_g, TRUE, FALSE,
7583 EL_CHAR('G'), -1, -1
7586 Xalpha_h, TRUE, FALSE,
7587 EL_CHAR('H'), -1, -1
7590 Xalpha_i, TRUE, FALSE,
7591 EL_CHAR('I'), -1, -1
7594 Xalpha_j, TRUE, FALSE,
7595 EL_CHAR('J'), -1, -1
7598 Xalpha_k, TRUE, FALSE,
7599 EL_CHAR('K'), -1, -1
7602 Xalpha_l, TRUE, FALSE,
7603 EL_CHAR('L'), -1, -1
7606 Xalpha_m, TRUE, FALSE,
7607 EL_CHAR('M'), -1, -1
7610 Xalpha_n, TRUE, FALSE,
7611 EL_CHAR('N'), -1, -1
7614 Xalpha_o, TRUE, FALSE,
7615 EL_CHAR('O'), -1, -1
7618 Xalpha_p, TRUE, FALSE,
7619 EL_CHAR('P'), -1, -1
7622 Xalpha_q, TRUE, FALSE,
7623 EL_CHAR('Q'), -1, -1
7626 Xalpha_r, TRUE, FALSE,
7627 EL_CHAR('R'), -1, -1
7630 Xalpha_s, TRUE, FALSE,
7631 EL_CHAR('S'), -1, -1
7634 Xalpha_t, TRUE, FALSE,
7635 EL_CHAR('T'), -1, -1
7638 Xalpha_u, TRUE, FALSE,
7639 EL_CHAR('U'), -1, -1
7642 Xalpha_v, TRUE, FALSE,
7643 EL_CHAR('V'), -1, -1
7646 Xalpha_w, TRUE, FALSE,
7647 EL_CHAR('W'), -1, -1
7650 Xalpha_x, TRUE, FALSE,
7651 EL_CHAR('X'), -1, -1
7654 Xalpha_y, TRUE, FALSE,
7655 EL_CHAR('Y'), -1, -1
7658 Xalpha_z, TRUE, FALSE,
7659 EL_CHAR('Z'), -1, -1
7662 Xalpha_arrow_e, TRUE, FALSE,
7663 EL_CHAR('>'), -1, -1
7666 Xalpha_arrow_w, TRUE, FALSE,
7667 EL_CHAR('<'), -1, -1
7670 Xalpha_copyr, TRUE, FALSE,
7671 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7675 Ykey_1_blank, FALSE, FALSE,
7676 EL_EM_KEY_1, ACTION_COLLECTING, -1
7679 Ykey_2_blank, FALSE, FALSE,
7680 EL_EM_KEY_2, ACTION_COLLECTING, -1
7683 Ykey_3_blank, FALSE, FALSE,
7684 EL_EM_KEY_3, ACTION_COLLECTING, -1
7687 Ykey_4_blank, FALSE, FALSE,
7688 EL_EM_KEY_4, ACTION_COLLECTING, -1
7691 Ykey_5_blank, FALSE, FALSE,
7692 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7695 Ykey_6_blank, FALSE, FALSE,
7696 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7699 Ykey_7_blank, FALSE, FALSE,
7700 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7703 Ykey_8_blank, FALSE, FALSE,
7704 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7707 Ylenses_blank, FALSE, FALSE,
7708 EL_EMC_LENSES, ACTION_COLLECTING, -1
7711 Ymagnify_blank, FALSE, FALSE,
7712 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7715 Ygrass_blank, FALSE, FALSE,
7716 EL_EMC_GRASS, ACTION_SNAPPING, -1
7719 Ydirt_blank, FALSE, FALSE,
7720 EL_SAND, ACTION_SNAPPING, -1
7729 static struct Mapping_EM_to_RND_player
7738 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7742 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7746 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7750 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7754 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7758 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7762 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7766 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7770 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7774 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7778 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7782 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7786 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7790 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7794 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7798 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7802 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7806 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7810 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7814 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7818 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7822 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7826 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7830 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7834 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7838 EL_PLAYER_1, ACTION_DEFAULT, -1,
7842 EL_PLAYER_2, ACTION_DEFAULT, -1,
7846 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7850 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7854 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7858 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7862 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7866 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7870 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7874 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7878 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7882 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7886 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7890 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7894 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7898 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7902 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7906 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7910 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7914 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7918 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7922 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7926 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7930 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7934 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7938 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7942 EL_PLAYER_3, ACTION_DEFAULT, -1,
7946 EL_PLAYER_4, ACTION_DEFAULT, -1,
7955 int map_element_RND_to_EM_cave(int element_rnd)
7957 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7958 static boolean mapping_initialized = FALSE;
7960 if (!mapping_initialized)
7964 // return "Xalpha_quest" for all undefined elements in mapping array
7965 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7966 mapping_RND_to_EM[i] = Xalpha_quest;
7968 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7969 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7970 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7971 em_object_mapping_list[i].element_em;
7973 mapping_initialized = TRUE;
7976 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7978 Warn("invalid RND level element %d", element_rnd);
7983 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7986 int map_element_EM_to_RND_cave(int element_em_cave)
7988 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7989 static boolean mapping_initialized = FALSE;
7991 if (!mapping_initialized)
7995 // return "EL_UNKNOWN" for all undefined elements in mapping array
7996 for (i = 0; i < GAME_TILE_MAX; i++)
7997 mapping_EM_to_RND[i] = EL_UNKNOWN;
7999 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8000 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8001 em_object_mapping_list[i].element_rnd;
8003 mapping_initialized = TRUE;
8006 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8008 Warn("invalid EM cave element %d", element_em_cave);
8013 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8016 int map_element_EM_to_RND_game(int element_em_game)
8018 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8019 static boolean mapping_initialized = FALSE;
8021 if (!mapping_initialized)
8025 // return "EL_UNKNOWN" for all undefined elements in mapping array
8026 for (i = 0; i < GAME_TILE_MAX; i++)
8027 mapping_EM_to_RND[i] = EL_UNKNOWN;
8029 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8030 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8031 em_object_mapping_list[i].element_rnd;
8033 mapping_initialized = TRUE;
8036 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8038 Warn("invalid EM game element %d", element_em_game);
8043 return mapping_EM_to_RND[element_em_game];
8046 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8048 struct LevelInfo_EM *level_em = level->native_em_level;
8049 struct CAVE *cav = level_em->cav;
8052 for (i = 0; i < GAME_TILE_MAX; i++)
8053 cav->android_array[i] = Cblank;
8055 for (i = 0; i < level->num_android_clone_elements; i++)
8057 int element_rnd = level->android_clone_element[i];
8058 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8060 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8061 if (em_object_mapping_list[j].element_rnd == element_rnd)
8062 cav->android_array[em_object_mapping_list[j].element_em] =
8067 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8069 struct LevelInfo_EM *level_em = level->native_em_level;
8070 struct CAVE *cav = level_em->cav;
8073 level->num_android_clone_elements = 0;
8075 for (i = 0; i < GAME_TILE_MAX; i++)
8077 int element_em_cave = cav->android_array[i];
8079 boolean element_found = FALSE;
8081 if (element_em_cave == Cblank)
8084 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8086 for (j = 0; j < level->num_android_clone_elements; j++)
8087 if (level->android_clone_element[j] == element_rnd)
8088 element_found = TRUE;
8092 level->android_clone_element[level->num_android_clone_elements++] =
8095 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8100 if (level->num_android_clone_elements == 0)
8102 level->num_android_clone_elements = 1;
8103 level->android_clone_element[0] = EL_EMPTY;
8107 int map_direction_RND_to_EM(int direction)
8109 return (direction == MV_UP ? 0 :
8110 direction == MV_RIGHT ? 1 :
8111 direction == MV_DOWN ? 2 :
8112 direction == MV_LEFT ? 3 :
8116 int map_direction_EM_to_RND(int direction)
8118 return (direction == 0 ? MV_UP :
8119 direction == 1 ? MV_RIGHT :
8120 direction == 2 ? MV_DOWN :
8121 direction == 3 ? MV_LEFT :
8125 int map_element_RND_to_SP(int element_rnd)
8127 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8129 if (element_rnd >= EL_SP_START &&
8130 element_rnd <= EL_SP_END)
8131 element_sp = element_rnd - EL_SP_START;
8132 else if (element_rnd == EL_EMPTY_SPACE)
8134 else if (element_rnd == EL_INVISIBLE_WALL)
8140 int map_element_SP_to_RND(int element_sp)
8142 int element_rnd = EL_UNKNOWN;
8144 if (element_sp >= 0x00 &&
8146 element_rnd = EL_SP_START + element_sp;
8147 else if (element_sp == 0x28)
8148 element_rnd = EL_INVISIBLE_WALL;
8153 int map_action_SP_to_RND(int action_sp)
8157 case actActive: return ACTION_ACTIVE;
8158 case actImpact: return ACTION_IMPACT;
8159 case actExploding: return ACTION_EXPLODING;
8160 case actDigging: return ACTION_DIGGING;
8161 case actSnapping: return ACTION_SNAPPING;
8162 case actCollecting: return ACTION_COLLECTING;
8163 case actPassing: return ACTION_PASSING;
8164 case actPushing: return ACTION_PUSHING;
8165 case actDropping: return ACTION_DROPPING;
8167 default: return ACTION_DEFAULT;
8171 int map_element_RND_to_MM(int element_rnd)
8173 return (element_rnd >= EL_MM_START_1 &&
8174 element_rnd <= EL_MM_END_1 ?
8175 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8177 element_rnd >= EL_MM_START_2 &&
8178 element_rnd <= EL_MM_END_2 ?
8179 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8181 element_rnd >= EL_CHAR_START &&
8182 element_rnd <= EL_CHAR_END ?
8183 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8185 element_rnd >= EL_MM_RUNTIME_START &&
8186 element_rnd <= EL_MM_RUNTIME_END ?
8187 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8189 element_rnd >= EL_MM_DUMMY_START &&
8190 element_rnd <= EL_MM_DUMMY_END ?
8191 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8193 EL_MM_EMPTY_NATIVE);
8196 int map_element_MM_to_RND(int element_mm)
8198 return (element_mm == EL_MM_EMPTY_NATIVE ||
8199 element_mm == EL_DF_EMPTY_NATIVE ?
8202 element_mm >= EL_MM_START_1_NATIVE &&
8203 element_mm <= EL_MM_END_1_NATIVE ?
8204 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8206 element_mm >= EL_MM_START_2_NATIVE &&
8207 element_mm <= EL_MM_END_2_NATIVE ?
8208 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8210 element_mm >= EL_MM_CHAR_START_NATIVE &&
8211 element_mm <= EL_MM_CHAR_END_NATIVE ?
8212 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8214 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8215 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8216 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8218 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8219 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8220 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8225 int map_action_MM_to_RND(int action_mm)
8227 // all MM actions are defined to exactly match their RND counterparts
8231 int map_sound_MM_to_RND(int sound_mm)
8235 case SND_MM_GAME_LEVELTIME_CHARGING:
8236 return SND_GAME_LEVELTIME_CHARGING;
8238 case SND_MM_GAME_HEALTH_CHARGING:
8239 return SND_GAME_HEALTH_CHARGING;
8242 return SND_UNDEFINED;
8246 int map_mm_wall_element(int element)
8248 return (element >= EL_MM_STEEL_WALL_START &&
8249 element <= EL_MM_STEEL_WALL_END ?
8252 element >= EL_MM_WOODEN_WALL_START &&
8253 element <= EL_MM_WOODEN_WALL_END ?
8256 element >= EL_MM_ICE_WALL_START &&
8257 element <= EL_MM_ICE_WALL_END ?
8260 element >= EL_MM_AMOEBA_WALL_START &&
8261 element <= EL_MM_AMOEBA_WALL_END ?
8264 element >= EL_DF_STEEL_WALL_START &&
8265 element <= EL_DF_STEEL_WALL_END ?
8268 element >= EL_DF_WOODEN_WALL_START &&
8269 element <= EL_DF_WOODEN_WALL_END ?
8275 int map_mm_wall_element_editor(int element)
8279 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8280 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8281 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8282 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8283 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8284 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8286 default: return element;
8290 int get_next_element(int element)
8294 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8295 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8296 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8297 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8298 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8299 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8300 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8301 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8302 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8303 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8304 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8306 default: return element;
8310 int el2img_mm(int element_mm)
8312 return el2img(map_element_MM_to_RND(element_mm));
8315 int el_act_dir2img(int element, int action, int direction)
8317 element = GFX_ELEMENT(element);
8318 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8320 // direction_graphic[][] == graphic[] for undefined direction graphics
8321 return element_info[element].direction_graphic[action][direction];
8324 static int el_act_dir2crm(int element, int action, int direction)
8326 element = GFX_ELEMENT(element);
8327 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8329 // direction_graphic[][] == graphic[] for undefined direction graphics
8330 return element_info[element].direction_crumbled[action][direction];
8333 int el_act2img(int element, int action)
8335 element = GFX_ELEMENT(element);
8337 return element_info[element].graphic[action];
8340 int el_act2crm(int element, int action)
8342 element = GFX_ELEMENT(element);
8344 return element_info[element].crumbled[action];
8347 int el_dir2img(int element, int direction)
8349 element = GFX_ELEMENT(element);
8351 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8354 int el2baseimg(int element)
8356 return element_info[element].graphic[ACTION_DEFAULT];
8359 int el2img(int element)
8361 element = GFX_ELEMENT(element);
8363 return element_info[element].graphic[ACTION_DEFAULT];
8366 int el2edimg(int element)
8368 element = GFX_ELEMENT(element);
8370 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8373 int el2preimg(int element)
8375 element = GFX_ELEMENT(element);
8377 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8380 int el2panelimg(int element)
8382 element = GFX_ELEMENT(element);
8384 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8387 int font2baseimg(int font_nr)
8389 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8392 int getBeltNrFromBeltElement(int element)
8394 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8395 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8396 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8399 int getBeltNrFromBeltActiveElement(int element)
8401 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8402 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8403 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8406 int getBeltNrFromBeltSwitchElement(int element)
8408 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8409 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8410 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8413 int getBeltDirNrFromBeltElement(int element)
8415 static int belt_base_element[4] =
8417 EL_CONVEYOR_BELT_1_LEFT,
8418 EL_CONVEYOR_BELT_2_LEFT,
8419 EL_CONVEYOR_BELT_3_LEFT,
8420 EL_CONVEYOR_BELT_4_LEFT
8423 int belt_nr = getBeltNrFromBeltElement(element);
8424 int belt_dir_nr = element - belt_base_element[belt_nr];
8426 return (belt_dir_nr % 3);
8429 int getBeltDirNrFromBeltSwitchElement(int element)
8431 static int belt_base_element[4] =
8433 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8434 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8435 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8436 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8439 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8440 int belt_dir_nr = element - belt_base_element[belt_nr];
8442 return (belt_dir_nr % 3);
8445 int getBeltDirFromBeltElement(int element)
8447 static int belt_move_dir[3] =
8454 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8456 return belt_move_dir[belt_dir_nr];
8459 int getBeltDirFromBeltSwitchElement(int element)
8461 static int belt_move_dir[3] =
8468 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8470 return belt_move_dir[belt_dir_nr];
8473 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8475 static int belt_base_element[4] =
8477 EL_CONVEYOR_BELT_1_LEFT,
8478 EL_CONVEYOR_BELT_2_LEFT,
8479 EL_CONVEYOR_BELT_3_LEFT,
8480 EL_CONVEYOR_BELT_4_LEFT
8483 return belt_base_element[belt_nr] + belt_dir_nr;
8486 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8488 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8490 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8493 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8495 static int belt_base_element[4] =
8497 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8498 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8499 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8500 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8503 return belt_base_element[belt_nr] + belt_dir_nr;
8506 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8508 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8510 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8513 boolean swapTiles_EM(boolean is_pre_emc_cave)
8515 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8518 boolean getTeamMode_EM(void)
8520 return game.team_mode || network_playing;
8523 boolean isActivePlayer_EM(int player_nr)
8525 return stored_player[player_nr].active;
8528 unsigned int InitRND(int seed)
8530 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8531 return InitEngineRandom_EM(seed);
8532 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8533 return InitEngineRandom_SP(seed);
8534 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8535 return InitEngineRandom_MM(seed);
8537 return InitEngineRandom_RND(seed);
8540 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8541 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8543 static int get_effective_element_EM(int tile, int frame_em)
8545 int element = object_mapping[tile].element_rnd;
8546 int action = object_mapping[tile].action;
8547 boolean is_backside = object_mapping[tile].is_backside;
8548 boolean action_removing = (action == ACTION_DIGGING ||
8549 action == ACTION_SNAPPING ||
8550 action == ACTION_COLLECTING);
8558 return (frame_em > 5 ? EL_EMPTY : element);
8564 else // frame_em == 7
8575 case Ydiamond_stone:
8579 case Xdrip_stretchB:
8595 case Ymagnify_blank:
8598 case Xsand_stonein_1:
8599 case Xsand_stonein_2:
8600 case Xsand_stonein_3:
8601 case Xsand_stonein_4:
8605 return (is_backside || action_removing ? EL_EMPTY : element);
8610 static boolean check_linear_animation_EM(int tile)
8614 case Xsand_stonesand_1:
8615 case Xsand_stonesand_quickout_1:
8616 case Xsand_sandstone_1:
8617 case Xsand_stonein_1:
8618 case Xsand_stoneout_1:
8646 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8647 boolean has_crumbled_graphics,
8648 int crumbled, int sync_frame)
8650 // if element can be crumbled, but certain action graphics are just empty
8651 // space (like instantly snapping sand to empty space in 1 frame), do not
8652 // treat these empty space graphics as crumbled graphics in EMC engine
8653 if (crumbled == IMG_EMPTY_SPACE)
8654 has_crumbled_graphics = FALSE;
8656 if (has_crumbled_graphics)
8658 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8659 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8660 g_crumbled->anim_delay,
8661 g_crumbled->anim_mode,
8662 g_crumbled->anim_start_frame,
8665 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8666 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8668 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8669 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8671 g_em->has_crumbled_graphics = TRUE;
8675 g_em->crumbled_bitmap = NULL;
8676 g_em->crumbled_src_x = 0;
8677 g_em->crumbled_src_y = 0;
8678 g_em->crumbled_border_size = 0;
8679 g_em->crumbled_tile_size = 0;
8681 g_em->has_crumbled_graphics = FALSE;
8686 void ResetGfxAnimation_EM(int x, int y, int tile)
8692 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8693 int tile, int frame_em, int x, int y)
8695 int action = object_mapping[tile].action;
8696 int direction = object_mapping[tile].direction;
8697 int effective_element = get_effective_element_EM(tile, frame_em);
8698 int graphic = (direction == MV_NONE ?
8699 el_act2img(effective_element, action) :
8700 el_act_dir2img(effective_element, action, direction));
8701 struct GraphicInfo *g = &graphic_info[graphic];
8703 boolean action_removing = (action == ACTION_DIGGING ||
8704 action == ACTION_SNAPPING ||
8705 action == ACTION_COLLECTING);
8706 boolean action_moving = (action == ACTION_FALLING ||
8707 action == ACTION_MOVING ||
8708 action == ACTION_PUSHING ||
8709 action == ACTION_EATING ||
8710 action == ACTION_FILLING ||
8711 action == ACTION_EMPTYING);
8712 boolean action_falling = (action == ACTION_FALLING ||
8713 action == ACTION_FILLING ||
8714 action == ACTION_EMPTYING);
8716 // special case: graphic uses "2nd movement tile" and has defined
8717 // 7 frames for movement animation (or less) => use default graphic
8718 // for last (8th) frame which ends the movement animation
8719 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8721 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8722 graphic = (direction == MV_NONE ?
8723 el_act2img(effective_element, action) :
8724 el_act_dir2img(effective_element, action, direction));
8726 g = &graphic_info[graphic];
8729 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8733 else if (action_moving)
8735 boolean is_backside = object_mapping[tile].is_backside;
8739 int direction = object_mapping[tile].direction;
8740 int move_dir = (action_falling ? MV_DOWN : direction);
8745 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8746 if (g->double_movement && frame_em == 0)
8750 if (move_dir == MV_LEFT)
8751 GfxFrame[x - 1][y] = GfxFrame[x][y];
8752 else if (move_dir == MV_RIGHT)
8753 GfxFrame[x + 1][y] = GfxFrame[x][y];
8754 else if (move_dir == MV_UP)
8755 GfxFrame[x][y - 1] = GfxFrame[x][y];
8756 else if (move_dir == MV_DOWN)
8757 GfxFrame[x][y + 1] = GfxFrame[x][y];
8764 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8765 if (tile == Xsand_stonesand_quickout_1 ||
8766 tile == Xsand_stonesand_quickout_2)
8770 if (graphic_info[graphic].anim_global_sync)
8771 sync_frame = FrameCounter;
8772 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8773 sync_frame = GfxFrame[x][y];
8775 sync_frame = 0; // playfield border (pseudo steel)
8777 SetRandomAnimationValue(x, y);
8779 int frame = getAnimationFrame(g->anim_frames,
8782 g->anim_start_frame,
8785 g_em->unique_identifier =
8786 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8789 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8790 int tile, int frame_em, int x, int y)
8792 int action = object_mapping[tile].action;
8793 int direction = object_mapping[tile].direction;
8794 boolean is_backside = object_mapping[tile].is_backside;
8795 int effective_element = get_effective_element_EM(tile, frame_em);
8796 int effective_action = action;
8797 int graphic = (direction == MV_NONE ?
8798 el_act2img(effective_element, effective_action) :
8799 el_act_dir2img(effective_element, effective_action,
8801 int crumbled = (direction == MV_NONE ?
8802 el_act2crm(effective_element, effective_action) :
8803 el_act_dir2crm(effective_element, effective_action,
8805 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8806 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8807 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8808 struct GraphicInfo *g = &graphic_info[graphic];
8811 // special case: graphic uses "2nd movement tile" and has defined
8812 // 7 frames for movement animation (or less) => use default graphic
8813 // for last (8th) frame which ends the movement animation
8814 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8816 effective_action = ACTION_DEFAULT;
8817 graphic = (direction == MV_NONE ?
8818 el_act2img(effective_element, effective_action) :
8819 el_act_dir2img(effective_element, effective_action,
8821 crumbled = (direction == MV_NONE ?
8822 el_act2crm(effective_element, effective_action) :
8823 el_act_dir2crm(effective_element, effective_action,
8826 g = &graphic_info[graphic];
8829 if (graphic_info[graphic].anim_global_sync)
8830 sync_frame = FrameCounter;
8831 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8832 sync_frame = GfxFrame[x][y];
8834 sync_frame = 0; // playfield border (pseudo steel)
8836 SetRandomAnimationValue(x, y);
8838 int frame = getAnimationFrame(g->anim_frames,
8841 g->anim_start_frame,
8844 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8845 g->double_movement && is_backside);
8847 // (updating the "crumbled" graphic definitions is probably not really needed,
8848 // as animations for crumbled graphics can't be longer than one EMC cycle)
8849 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8853 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8854 int player_nr, int anim, int frame_em)
8856 int element = player_mapping[player_nr][anim].element_rnd;
8857 int action = player_mapping[player_nr][anim].action;
8858 int direction = player_mapping[player_nr][anim].direction;
8859 int graphic = (direction == MV_NONE ?
8860 el_act2img(element, action) :
8861 el_act_dir2img(element, action, direction));
8862 struct GraphicInfo *g = &graphic_info[graphic];
8865 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8867 stored_player[player_nr].StepFrame = frame_em;
8869 sync_frame = stored_player[player_nr].Frame;
8871 int frame = getAnimationFrame(g->anim_frames,
8874 g->anim_start_frame,
8877 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8878 &g_em->src_x, &g_em->src_y, FALSE);
8881 void InitGraphicInfo_EM(void)
8885 // always start with reliable default values
8886 for (i = 0; i < GAME_TILE_MAX; i++)
8888 object_mapping[i].element_rnd = EL_UNKNOWN;
8889 object_mapping[i].is_backside = FALSE;
8890 object_mapping[i].action = ACTION_DEFAULT;
8891 object_mapping[i].direction = MV_NONE;
8894 // always start with reliable default values
8895 for (p = 0; p < MAX_PLAYERS; p++)
8897 for (i = 0; i < PLY_MAX; i++)
8899 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8900 player_mapping[p][i].action = ACTION_DEFAULT;
8901 player_mapping[p][i].direction = MV_NONE;
8905 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8907 int e = em_object_mapping_list[i].element_em;
8909 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8910 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8912 if (em_object_mapping_list[i].action != -1)
8913 object_mapping[e].action = em_object_mapping_list[i].action;
8915 if (em_object_mapping_list[i].direction != -1)
8916 object_mapping[e].direction =
8917 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8920 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8922 int a = em_player_mapping_list[i].action_em;
8923 int p = em_player_mapping_list[i].player_nr;
8925 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8927 if (em_player_mapping_list[i].action != -1)
8928 player_mapping[p][a].action = em_player_mapping_list[i].action;
8930 if (em_player_mapping_list[i].direction != -1)
8931 player_mapping[p][a].direction =
8932 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8935 for (i = 0; i < GAME_TILE_MAX; i++)
8937 int element = object_mapping[i].element_rnd;
8938 int action = object_mapping[i].action;
8939 int direction = object_mapping[i].direction;
8940 boolean is_backside = object_mapping[i].is_backside;
8941 boolean action_exploding = ((action == ACTION_EXPLODING ||
8942 action == ACTION_SMASHED_BY_ROCK ||
8943 action == ACTION_SMASHED_BY_SPRING) &&
8944 element != EL_DIAMOND);
8945 boolean action_active = (action == ACTION_ACTIVE);
8946 boolean action_other = (action == ACTION_OTHER);
8948 for (j = 0; j < 8; j++)
8950 int effective_element = get_effective_element_EM(i, j);
8951 int effective_action = (j < 7 ? action :
8952 i == Xdrip_stretch ? action :
8953 i == Xdrip_stretchB ? action :
8954 i == Ydrip_1_s ? action :
8955 i == Ydrip_1_sB ? action :
8956 i == Yball_1 ? action :
8957 i == Xball_2 ? action :
8958 i == Yball_2 ? action :
8959 i == Yball_blank ? action :
8960 i == Ykey_1_blank ? action :
8961 i == Ykey_2_blank ? action :
8962 i == Ykey_3_blank ? action :
8963 i == Ykey_4_blank ? action :
8964 i == Ykey_5_blank ? action :
8965 i == Ykey_6_blank ? action :
8966 i == Ykey_7_blank ? action :
8967 i == Ykey_8_blank ? action :
8968 i == Ylenses_blank ? action :
8969 i == Ymagnify_blank ? action :
8970 i == Ygrass_blank ? action :
8971 i == Ydirt_blank ? action :
8972 i == Xsand_stonein_1 ? action :
8973 i == Xsand_stonein_2 ? action :
8974 i == Xsand_stonein_3 ? action :
8975 i == Xsand_stonein_4 ? action :
8976 i == Xsand_stoneout_1 ? action :
8977 i == Xsand_stoneout_2 ? action :
8978 i == Xboom_android ? ACTION_EXPLODING :
8979 action_exploding ? ACTION_EXPLODING :
8980 action_active ? action :
8981 action_other ? action :
8983 int graphic = (el_act_dir2img(effective_element, effective_action,
8985 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8987 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8988 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8989 boolean has_action_graphics = (graphic != base_graphic);
8990 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8991 struct GraphicInfo *g = &graphic_info[graphic];
8992 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8995 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8996 boolean special_animation = (action != ACTION_DEFAULT &&
8997 g->anim_frames == 3 &&
8998 g->anim_delay == 2 &&
8999 g->anim_mode & ANIM_LINEAR);
9000 int sync_frame = (i == Xdrip_stretch ? 7 :
9001 i == Xdrip_stretchB ? 7 :
9002 i == Ydrip_2_s ? j + 8 :
9003 i == Ydrip_2_sB ? j + 8 :
9012 i == Xfake_acid_1 ? 0 :
9013 i == Xfake_acid_2 ? 10 :
9014 i == Xfake_acid_3 ? 20 :
9015 i == Xfake_acid_4 ? 30 :
9016 i == Xfake_acid_5 ? 40 :
9017 i == Xfake_acid_6 ? 50 :
9018 i == Xfake_acid_7 ? 60 :
9019 i == Xfake_acid_8 ? 70 :
9020 i == Xfake_acid_1_player ? 0 :
9021 i == Xfake_acid_2_player ? 10 :
9022 i == Xfake_acid_3_player ? 20 :
9023 i == Xfake_acid_4_player ? 30 :
9024 i == Xfake_acid_5_player ? 40 :
9025 i == Xfake_acid_6_player ? 50 :
9026 i == Xfake_acid_7_player ? 60 :
9027 i == Xfake_acid_8_player ? 70 :
9029 i == Yball_2 ? j + 8 :
9030 i == Yball_blank ? j + 1 :
9031 i == Ykey_1_blank ? j + 1 :
9032 i == Ykey_2_blank ? j + 1 :
9033 i == Ykey_3_blank ? j + 1 :
9034 i == Ykey_4_blank ? j + 1 :
9035 i == Ykey_5_blank ? j + 1 :
9036 i == Ykey_6_blank ? j + 1 :
9037 i == Ykey_7_blank ? j + 1 :
9038 i == Ykey_8_blank ? j + 1 :
9039 i == Ylenses_blank ? j + 1 :
9040 i == Ymagnify_blank ? j + 1 :
9041 i == Ygrass_blank ? j + 1 :
9042 i == Ydirt_blank ? j + 1 :
9043 i == Xamoeba_1 ? 0 :
9044 i == Xamoeba_2 ? 1 :
9045 i == Xamoeba_3 ? 2 :
9046 i == Xamoeba_4 ? 3 :
9047 i == Xamoeba_5 ? 0 :
9048 i == Xamoeba_6 ? 1 :
9049 i == Xamoeba_7 ? 2 :
9050 i == Xamoeba_8 ? 3 :
9051 i == Xexit_2 ? j + 8 :
9052 i == Xexit_3 ? j + 16 :
9053 i == Xdynamite_1 ? 0 :
9054 i == Xdynamite_2 ? 8 :
9055 i == Xdynamite_3 ? 16 :
9056 i == Xdynamite_4 ? 24 :
9057 i == Xsand_stonein_1 ? j + 1 :
9058 i == Xsand_stonein_2 ? j + 9 :
9059 i == Xsand_stonein_3 ? j + 17 :
9060 i == Xsand_stonein_4 ? j + 25 :
9061 i == Xsand_stoneout_1 && j == 0 ? 0 :
9062 i == Xsand_stoneout_1 && j == 1 ? 0 :
9063 i == Xsand_stoneout_1 && j == 2 ? 1 :
9064 i == Xsand_stoneout_1 && j == 3 ? 2 :
9065 i == Xsand_stoneout_1 && j == 4 ? 2 :
9066 i == Xsand_stoneout_1 && j == 5 ? 3 :
9067 i == Xsand_stoneout_1 && j == 6 ? 4 :
9068 i == Xsand_stoneout_1 && j == 7 ? 4 :
9069 i == Xsand_stoneout_2 && j == 0 ? 5 :
9070 i == Xsand_stoneout_2 && j == 1 ? 6 :
9071 i == Xsand_stoneout_2 && j == 2 ? 7 :
9072 i == Xsand_stoneout_2 && j == 3 ? 8 :
9073 i == Xsand_stoneout_2 && j == 4 ? 9 :
9074 i == Xsand_stoneout_2 && j == 5 ? 11 :
9075 i == Xsand_stoneout_2 && j == 6 ? 13 :
9076 i == Xsand_stoneout_2 && j == 7 ? 15 :
9077 i == Xboom_bug && j == 1 ? 2 :
9078 i == Xboom_bug && j == 2 ? 2 :
9079 i == Xboom_bug && j == 3 ? 4 :
9080 i == Xboom_bug && j == 4 ? 4 :
9081 i == Xboom_bug && j == 5 ? 2 :
9082 i == Xboom_bug && j == 6 ? 2 :
9083 i == Xboom_bug && j == 7 ? 0 :
9084 i == Xboom_tank && j == 1 ? 2 :
9085 i == Xboom_tank && j == 2 ? 2 :
9086 i == Xboom_tank && j == 3 ? 4 :
9087 i == Xboom_tank && j == 4 ? 4 :
9088 i == Xboom_tank && j == 5 ? 2 :
9089 i == Xboom_tank && j == 6 ? 2 :
9090 i == Xboom_tank && j == 7 ? 0 :
9091 i == Xboom_android && j == 7 ? 6 :
9092 i == Xboom_1 && j == 1 ? 2 :
9093 i == Xboom_1 && j == 2 ? 2 :
9094 i == Xboom_1 && j == 3 ? 4 :
9095 i == Xboom_1 && j == 4 ? 4 :
9096 i == Xboom_1 && j == 5 ? 6 :
9097 i == Xboom_1 && j == 6 ? 6 :
9098 i == Xboom_1 && j == 7 ? 8 :
9099 i == Xboom_2 && j == 0 ? 8 :
9100 i == Xboom_2 && j == 1 ? 8 :
9101 i == Xboom_2 && j == 2 ? 10 :
9102 i == Xboom_2 && j == 3 ? 10 :
9103 i == Xboom_2 && j == 4 ? 10 :
9104 i == Xboom_2 && j == 5 ? 12 :
9105 i == Xboom_2 && j == 6 ? 12 :
9106 i == Xboom_2 && j == 7 ? 12 :
9107 special_animation && j == 4 ? 3 :
9108 effective_action != action ? 0 :
9110 int frame = getAnimationFrame(g->anim_frames,
9113 g->anim_start_frame,
9116 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9117 g->double_movement && is_backside);
9119 g_em->bitmap = src_bitmap;
9120 g_em->src_x = src_x;
9121 g_em->src_y = src_y;
9122 g_em->src_offset_x = 0;
9123 g_em->src_offset_y = 0;
9124 g_em->dst_offset_x = 0;
9125 g_em->dst_offset_y = 0;
9126 g_em->width = TILEX;
9127 g_em->height = TILEY;
9129 g_em->preserve_background = FALSE;
9131 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9134 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9135 effective_action == ACTION_MOVING ||
9136 effective_action == ACTION_PUSHING ||
9137 effective_action == ACTION_EATING)) ||
9138 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9139 effective_action == ACTION_EMPTYING)))
9142 (effective_action == ACTION_FALLING ||
9143 effective_action == ACTION_FILLING ||
9144 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9145 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9146 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9147 int num_steps = (i == Ydrip_1_s ? 16 :
9148 i == Ydrip_1_sB ? 16 :
9149 i == Ydrip_2_s ? 16 :
9150 i == Ydrip_2_sB ? 16 :
9151 i == Xsand_stonein_1 ? 32 :
9152 i == Xsand_stonein_2 ? 32 :
9153 i == Xsand_stonein_3 ? 32 :
9154 i == Xsand_stonein_4 ? 32 :
9155 i == Xsand_stoneout_1 ? 16 :
9156 i == Xsand_stoneout_2 ? 16 : 8);
9157 int cx = ABS(dx) * (TILEX / num_steps);
9158 int cy = ABS(dy) * (TILEY / num_steps);
9159 int step_frame = (i == Ydrip_2_s ? j + 8 :
9160 i == Ydrip_2_sB ? j + 8 :
9161 i == Xsand_stonein_2 ? j + 8 :
9162 i == Xsand_stonein_3 ? j + 16 :
9163 i == Xsand_stonein_4 ? j + 24 :
9164 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9165 int step = (is_backside ? step_frame : num_steps - step_frame);
9167 if (is_backside) // tile where movement starts
9169 if (dx < 0 || dy < 0)
9171 g_em->src_offset_x = cx * step;
9172 g_em->src_offset_y = cy * step;
9176 g_em->dst_offset_x = cx * step;
9177 g_em->dst_offset_y = cy * step;
9180 else // tile where movement ends
9182 if (dx < 0 || dy < 0)
9184 g_em->dst_offset_x = cx * step;
9185 g_em->dst_offset_y = cy * step;
9189 g_em->src_offset_x = cx * step;
9190 g_em->src_offset_y = cy * step;
9194 g_em->width = TILEX - cx * step;
9195 g_em->height = TILEY - cy * step;
9198 // create unique graphic identifier to decide if tile must be redrawn
9199 /* bit 31 - 16 (16 bit): EM style graphic
9200 bit 15 - 12 ( 4 bit): EM style frame
9201 bit 11 - 6 ( 6 bit): graphic width
9202 bit 5 - 0 ( 6 bit): graphic height */
9203 g_em->unique_identifier =
9204 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9208 for (i = 0; i < GAME_TILE_MAX; i++)
9210 for (j = 0; j < 8; j++)
9212 int element = object_mapping[i].element_rnd;
9213 int action = object_mapping[i].action;
9214 int direction = object_mapping[i].direction;
9215 boolean is_backside = object_mapping[i].is_backside;
9216 int graphic_action = el_act_dir2img(element, action, direction);
9217 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9219 if ((action == ACTION_SMASHED_BY_ROCK ||
9220 action == ACTION_SMASHED_BY_SPRING ||
9221 action == ACTION_EATING) &&
9222 graphic_action == graphic_default)
9224 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9225 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9226 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9227 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9230 // no separate animation for "smashed by rock" -- use rock instead
9231 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9232 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9234 g_em->bitmap = g_xx->bitmap;
9235 g_em->src_x = g_xx->src_x;
9236 g_em->src_y = g_xx->src_y;
9237 g_em->src_offset_x = g_xx->src_offset_x;
9238 g_em->src_offset_y = g_xx->src_offset_y;
9239 g_em->dst_offset_x = g_xx->dst_offset_x;
9240 g_em->dst_offset_y = g_xx->dst_offset_y;
9241 g_em->width = g_xx->width;
9242 g_em->height = g_xx->height;
9243 g_em->unique_identifier = g_xx->unique_identifier;
9246 g_em->preserve_background = TRUE;
9251 for (p = 0; p < MAX_PLAYERS; p++)
9253 for (i = 0; i < PLY_MAX; i++)
9255 int element = player_mapping[p][i].element_rnd;
9256 int action = player_mapping[p][i].action;
9257 int direction = player_mapping[p][i].direction;
9259 for (j = 0; j < 8; j++)
9261 int effective_element = element;
9262 int effective_action = action;
9263 int graphic = (direction == MV_NONE ?
9264 el_act2img(effective_element, effective_action) :
9265 el_act_dir2img(effective_element, effective_action,
9267 struct GraphicInfo *g = &graphic_info[graphic];
9268 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9272 int frame = getAnimationFrame(g->anim_frames,
9275 g->anim_start_frame,
9278 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9280 g_em->bitmap = src_bitmap;
9281 g_em->src_x = src_x;
9282 g_em->src_y = src_y;
9283 g_em->src_offset_x = 0;
9284 g_em->src_offset_y = 0;
9285 g_em->dst_offset_x = 0;
9286 g_em->dst_offset_y = 0;
9287 g_em->width = TILEX;
9288 g_em->height = TILEY;
9294 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9295 boolean any_player_moving,
9296 boolean any_player_snapping,
9297 boolean any_player_dropping)
9299 if (frame == 7 && !any_player_dropping)
9301 if (!local_player->was_waiting)
9303 if (!CheckSaveEngineSnapshotToList())
9306 local_player->was_waiting = TRUE;
9309 else if (any_player_moving || any_player_snapping || any_player_dropping)
9311 local_player->was_waiting = FALSE;
9315 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9316 boolean murphy_is_dropping)
9318 if (murphy_is_waiting)
9320 if (!local_player->was_waiting)
9322 if (!CheckSaveEngineSnapshotToList())
9325 local_player->was_waiting = TRUE;
9330 local_player->was_waiting = FALSE;
9334 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9335 boolean button_released)
9337 if (button_released)
9339 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9340 CheckSaveEngineSnapshotToList();
9342 else if (element_clicked)
9344 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9345 CheckSaveEngineSnapshotToList();
9347 game.snapshot.changed_action = TRUE;
9351 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9352 boolean any_player_moving,
9353 boolean any_player_snapping,
9354 boolean any_player_dropping)
9356 if (tape.single_step && tape.recording && !tape.pausing)
9357 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9358 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9360 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9361 any_player_snapping, any_player_dropping);
9363 return tape.pausing;
9366 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9367 boolean murphy_is_dropping)
9369 boolean murphy_starts_dropping = FALSE;
9372 for (i = 0; i < MAX_PLAYERS; i++)
9373 if (stored_player[i].force_dropping)
9374 murphy_starts_dropping = TRUE;
9376 if (tape.single_step && tape.recording && !tape.pausing)
9377 if (murphy_is_waiting && !murphy_starts_dropping)
9378 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9380 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9383 void CheckSingleStepMode_MM(boolean element_clicked,
9384 boolean button_released)
9386 if (tape.single_step && tape.recording && !tape.pausing)
9387 if (button_released)
9388 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9390 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9393 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9394 int graphic, int sync_frame, int x, int y)
9396 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9398 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9401 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9403 return (IS_NEXT_FRAME(sync_frame, graphic));
9406 int getGraphicInfo_Delay(int graphic)
9408 return graphic_info[graphic].anim_delay;
9411 void PlayMenuSoundExt(int sound)
9413 if (sound == SND_UNDEFINED)
9416 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9417 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9420 if (IS_LOOP_SOUND(sound))
9421 PlaySoundLoop(sound);
9426 void PlayMenuSound(void)
9428 PlayMenuSoundExt(menu.sound[game_status]);
9431 void PlayMenuSoundStereo(int sound, int stereo_position)
9433 if (sound == SND_UNDEFINED)
9436 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9437 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9440 if (IS_LOOP_SOUND(sound))
9441 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9443 PlaySoundStereo(sound, stereo_position);
9446 void PlayMenuSoundIfLoopExt(int sound)
9448 if (sound == SND_UNDEFINED)
9451 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9452 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9455 if (IS_LOOP_SOUND(sound))
9456 PlaySoundLoop(sound);
9459 void PlayMenuSoundIfLoop(void)
9461 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9464 void PlayMenuMusicExt(int music)
9466 if (music == MUS_UNDEFINED)
9469 if (!setup.sound_music)
9472 if (IS_LOOP_MUSIC(music))
9473 PlayMusicLoop(music);
9478 void PlayMenuMusic(void)
9480 char *curr_music = getCurrentlyPlayingMusicFilename();
9481 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9483 if (!strEqual(curr_music, next_music))
9484 PlayMenuMusicExt(menu.music[game_status]);
9487 void PlayMenuSoundsAndMusic(void)
9493 static void FadeMenuSounds(void)
9498 static void FadeMenuMusic(void)
9500 char *curr_music = getCurrentlyPlayingMusicFilename();
9501 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9503 if (!strEqual(curr_music, next_music))
9507 void FadeMenuSoundsAndMusic(void)
9513 void PlaySoundActivating(void)
9516 PlaySound(SND_MENU_ITEM_ACTIVATING);
9520 void PlaySoundSelecting(void)
9523 PlaySound(SND_MENU_ITEM_SELECTING);
9527 void ToggleFullscreenIfNeeded(void)
9529 // if setup and video fullscreen state are already matching, nothing do do
9530 if (setup.fullscreen == video.fullscreen_enabled ||
9531 !video.fullscreen_available)
9534 SDLSetWindowFullscreen(setup.fullscreen);
9536 // set setup value according to successfully changed fullscreen mode
9537 setup.fullscreen = video.fullscreen_enabled;
9540 void ChangeWindowScalingIfNeeded(void)
9542 // if setup and video window scaling are already matching, nothing do do
9543 if (setup.window_scaling_percent == video.window_scaling_percent ||
9544 video.fullscreen_enabled)
9547 SDLSetWindowScaling(setup.window_scaling_percent);
9549 // set setup value according to successfully changed window scaling
9550 setup.window_scaling_percent = video.window_scaling_percent;
9553 void ChangeVsyncModeIfNeeded(void)
9555 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9556 int video_vsync_mode = video.vsync_mode;
9558 // if setup and video vsync mode are already matching, nothing do do
9559 if (setup_vsync_mode == video_vsync_mode)
9562 // if renderer is using OpenGL, vsync mode can directly be changed
9563 SDLSetScreenVsyncMode(setup.vsync_mode);
9565 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9566 if (video.vsync_mode == video_vsync_mode)
9568 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9570 // save backbuffer content which gets lost when re-creating screen
9571 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9573 // force re-creating screen and renderer to set new vsync mode
9574 video.fullscreen_enabled = !setup.fullscreen;
9576 // when creating new renderer, destroy textures linked to old renderer
9577 FreeAllImageTextures(); // needs old renderer to free the textures
9579 // re-create screen and renderer (including change of vsync mode)
9580 ChangeVideoModeIfNeeded(setup.fullscreen);
9582 // set setup value according to successfully changed fullscreen mode
9583 setup.fullscreen = video.fullscreen_enabled;
9585 // restore backbuffer content from temporary backbuffer backup bitmap
9586 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9587 FreeBitmap(tmp_backbuffer);
9589 // update visible window/screen
9590 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9592 // when changing vsync mode, re-create textures for new renderer
9593 InitImageTextures();
9596 // set setup value according to successfully changed vsync mode
9597 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9600 static void JoinRectangles(int *x, int *y, int *width, int *height,
9601 int x2, int y2, int width2, int height2)
9603 // do not join with "off-screen" rectangle
9604 if (x2 == -1 || y2 == -1)
9609 *width = MAX(*width, width2);
9610 *height = MAX(*height, height2);
9613 void SetAnimStatus(int anim_status_new)
9615 if (anim_status_new == GAME_MODE_MAIN)
9616 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9617 else if (anim_status_new == GAME_MODE_NAMES)
9618 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9619 else if (anim_status_new == GAME_MODE_SCORES)
9620 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9622 global.anim_status_next = anim_status_new;
9624 // directly set screen modes that are entered without fading
9625 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9626 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9627 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9628 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9629 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9630 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9631 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9632 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9633 global.anim_status = global.anim_status_next;
9636 void SetGameStatus(int game_status_new)
9638 if (game_status_new != game_status)
9639 game_status_last_screen = game_status;
9641 game_status = game_status_new;
9643 SetAnimStatus(game_status_new);
9646 void SetFontStatus(int game_status_new)
9648 static int last_game_status = -1;
9650 if (game_status_new != -1)
9652 // set game status for font use after storing last game status
9653 last_game_status = game_status;
9654 game_status = game_status_new;
9658 // reset game status after font use from last stored game status
9659 game_status = last_game_status;
9663 void ResetFontStatus(void)
9668 void SetLevelSetInfo(char *identifier, int level_nr)
9670 setString(&levelset.identifier, identifier);
9672 levelset.level_nr = level_nr;
9675 boolean CheckIfAllViewportsHaveChanged(void)
9677 // if game status has not changed, viewports have not changed either
9678 if (game_status == game_status_last)
9681 // check if all viewports have changed with current game status
9683 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9684 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9685 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9686 int new_real_sx = vp_playfield->x;
9687 int new_real_sy = vp_playfield->y;
9688 int new_full_sxsize = vp_playfield->width;
9689 int new_full_sysize = vp_playfield->height;
9690 int new_dx = vp_door_1->x;
9691 int new_dy = vp_door_1->y;
9692 int new_dxsize = vp_door_1->width;
9693 int new_dysize = vp_door_1->height;
9694 int new_vx = vp_door_2->x;
9695 int new_vy = vp_door_2->y;
9696 int new_vxsize = vp_door_2->width;
9697 int new_vysize = vp_door_2->height;
9699 boolean playfield_viewport_has_changed =
9700 (new_real_sx != REAL_SX ||
9701 new_real_sy != REAL_SY ||
9702 new_full_sxsize != FULL_SXSIZE ||
9703 new_full_sysize != FULL_SYSIZE);
9705 boolean door_1_viewport_has_changed =
9708 new_dxsize != DXSIZE ||
9709 new_dysize != DYSIZE);
9711 boolean door_2_viewport_has_changed =
9714 new_vxsize != VXSIZE ||
9715 new_vysize != VYSIZE ||
9716 game_status_last == GAME_MODE_EDITOR);
9718 return (playfield_viewport_has_changed &&
9719 door_1_viewport_has_changed &&
9720 door_2_viewport_has_changed);
9723 boolean CheckFadeAll(void)
9725 return (CheckIfGlobalBorderHasChanged() ||
9726 CheckIfAllViewportsHaveChanged());
9729 void ChangeViewportPropertiesIfNeeded(void)
9731 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9732 FALSE : setup.small_game_graphics);
9733 int gfx_game_mode = game_status;
9734 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9736 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9737 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9738 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9739 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9740 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9741 int new_win_xsize = vp_window->width;
9742 int new_win_ysize = vp_window->height;
9743 int border_left = vp_playfield->border_left;
9744 int border_right = vp_playfield->border_right;
9745 int border_top = vp_playfield->border_top;
9746 int border_bottom = vp_playfield->border_bottom;
9747 int new_sx = vp_playfield->x + border_left;
9748 int new_sy = vp_playfield->y + border_top;
9749 int new_sxsize = vp_playfield->width - border_left - border_right;
9750 int new_sysize = vp_playfield->height - border_top - border_bottom;
9751 int new_real_sx = vp_playfield->x;
9752 int new_real_sy = vp_playfield->y;
9753 int new_full_sxsize = vp_playfield->width;
9754 int new_full_sysize = vp_playfield->height;
9755 int new_dx = vp_door_1->x;
9756 int new_dy = vp_door_1->y;
9757 int new_dxsize = vp_door_1->width;
9758 int new_dysize = vp_door_1->height;
9759 int new_vx = vp_door_2->x;
9760 int new_vy = vp_door_2->y;
9761 int new_vxsize = vp_door_2->width;
9762 int new_vysize = vp_door_2->height;
9763 int new_ex = vp_door_3->x;
9764 int new_ey = vp_door_3->y;
9765 int new_exsize = vp_door_3->width;
9766 int new_eysize = vp_door_3->height;
9767 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9768 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9769 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9770 int new_scr_fieldx = new_sxsize / tilesize;
9771 int new_scr_fieldy = new_sysize / tilesize;
9772 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9773 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9774 boolean init_gfx_buffers = FALSE;
9775 boolean init_video_buffer = FALSE;
9776 boolean init_gadgets_and_anims = FALSE;
9777 boolean init_em_graphics = FALSE;
9779 if (new_win_xsize != WIN_XSIZE ||
9780 new_win_ysize != WIN_YSIZE)
9782 WIN_XSIZE = new_win_xsize;
9783 WIN_YSIZE = new_win_ysize;
9785 init_video_buffer = TRUE;
9786 init_gfx_buffers = TRUE;
9787 init_gadgets_and_anims = TRUE;
9789 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9792 if (new_scr_fieldx != SCR_FIELDX ||
9793 new_scr_fieldy != SCR_FIELDY)
9795 // this always toggles between MAIN and GAME when using small tile size
9797 SCR_FIELDX = new_scr_fieldx;
9798 SCR_FIELDY = new_scr_fieldy;
9800 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9811 new_sxsize != SXSIZE ||
9812 new_sysize != SYSIZE ||
9813 new_dxsize != DXSIZE ||
9814 new_dysize != DYSIZE ||
9815 new_vxsize != VXSIZE ||
9816 new_vysize != VYSIZE ||
9817 new_exsize != EXSIZE ||
9818 new_eysize != EYSIZE ||
9819 new_real_sx != REAL_SX ||
9820 new_real_sy != REAL_SY ||
9821 new_full_sxsize != FULL_SXSIZE ||
9822 new_full_sysize != FULL_SYSIZE ||
9823 new_tilesize_var != TILESIZE_VAR
9826 // ------------------------------------------------------------------------
9827 // determine next fading area for changed viewport definitions
9828 // ------------------------------------------------------------------------
9830 // start with current playfield area (default fading area)
9833 FADE_SXSIZE = FULL_SXSIZE;
9834 FADE_SYSIZE = FULL_SYSIZE;
9836 // add new playfield area if position or size has changed
9837 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9838 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9840 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9841 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9844 // add current and new door 1 area if position or size has changed
9845 if (new_dx != DX || new_dy != DY ||
9846 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9848 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9849 DX, DY, DXSIZE, DYSIZE);
9850 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9851 new_dx, new_dy, new_dxsize, new_dysize);
9854 // add current and new door 2 area if position or size has changed
9855 if (new_vx != VX || new_vy != VY ||
9856 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9858 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9859 VX, VY, VXSIZE, VYSIZE);
9860 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9861 new_vx, new_vy, new_vxsize, new_vysize);
9864 // ------------------------------------------------------------------------
9865 // handle changed tile size
9866 // ------------------------------------------------------------------------
9868 if (new_tilesize_var != TILESIZE_VAR)
9870 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9872 // changing tile size invalidates scroll values of engine snapshots
9873 FreeEngineSnapshotSingle();
9875 // changing tile size requires update of graphic mapping for EM engine
9876 init_em_graphics = TRUE;
9887 SXSIZE = new_sxsize;
9888 SYSIZE = new_sysize;
9889 DXSIZE = new_dxsize;
9890 DYSIZE = new_dysize;
9891 VXSIZE = new_vxsize;
9892 VYSIZE = new_vysize;
9893 EXSIZE = new_exsize;
9894 EYSIZE = new_eysize;
9895 REAL_SX = new_real_sx;
9896 REAL_SY = new_real_sy;
9897 FULL_SXSIZE = new_full_sxsize;
9898 FULL_SYSIZE = new_full_sysize;
9899 TILESIZE_VAR = new_tilesize_var;
9901 init_gfx_buffers = TRUE;
9902 init_gadgets_and_anims = TRUE;
9904 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9905 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9908 if (init_gfx_buffers)
9910 // Debug("tools:viewport", "init_gfx_buffers");
9912 SCR_FIELDX = new_scr_fieldx_buffers;
9913 SCR_FIELDY = new_scr_fieldy_buffers;
9917 SCR_FIELDX = new_scr_fieldx;
9918 SCR_FIELDY = new_scr_fieldy;
9920 SetDrawDeactivationMask(REDRAW_NONE);
9921 SetDrawBackgroundMask(REDRAW_FIELD);
9924 if (init_video_buffer)
9926 // Debug("tools:viewport", "init_video_buffer");
9928 FreeAllImageTextures(); // needs old renderer to free the textures
9930 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9931 InitImageTextures();
9934 if (init_gadgets_and_anims)
9936 // Debug("tools:viewport", "init_gadgets_and_anims");
9939 InitGlobalAnimations();
9942 if (init_em_graphics)
9944 InitGraphicInfo_EM();
9949 // ============================================================================
9951 // ============================================================================
9953 #if defined(PLATFORM_WIN32)
9954 /* FILETIME of Jan 1 1970 00:00:00. */
9955 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9958 * timezone information is stored outside the kernel so tzp isn't used anymore.
9960 * Note: this function is not for Win32 high precision timing purpose. See
9963 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9966 SYSTEMTIME system_time;
9967 ULARGE_INTEGER ularge;
9969 GetSystemTime(&system_time);
9970 SystemTimeToFileTime(&system_time, &file_time);
9971 ularge.LowPart = file_time.dwLowDateTime;
9972 ularge.HighPart = file_time.dwHighDateTime;
9974 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9975 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9981 static char *test_init_uuid_random_function_simple(void)
9983 static char seed_text[100];
9984 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9986 sprintf(seed_text, "%d", seed);
9991 static char *test_init_uuid_random_function_better(void)
9993 static char seed_text[100];
9994 struct timeval current_time;
9996 gettimeofday(¤t_time, NULL);
9998 prng_seed_bytes(¤t_time, sizeof(current_time));
10000 sprintf(seed_text, "%ld.%ld",
10001 (long)current_time.tv_sec,
10002 (long)current_time.tv_usec);
10007 #if defined(PLATFORM_WIN32)
10008 static char *test_init_uuid_random_function_better_windows(void)
10010 static char seed_text[100];
10011 struct timeval current_time;
10013 gettimeofday_windows(¤t_time, NULL);
10015 prng_seed_bytes(¤t_time, sizeof(current_time));
10017 sprintf(seed_text, "%ld.%ld",
10018 (long)current_time.tv_sec,
10019 (long)current_time.tv_usec);
10025 static unsigned int test_uuid_random_function_simple(int max)
10027 return GetSimpleRandom(max);
10030 static unsigned int test_uuid_random_function_better(int max)
10032 return (max > 0 ? prng_get_uint() % max : 0);
10035 #if defined(PLATFORM_WIN32)
10036 #define NUM_UUID_TESTS 3
10038 #define NUM_UUID_TESTS 2
10041 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10043 struct hashtable *hash_seeds =
10044 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10045 struct hashtable *hash_uuids =
10046 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10047 static char message[100];
10050 char *random_name = (nr == 0 ? "simple" : "better");
10051 char *random_type = (always_seed ? "always" : "only once");
10052 char *(*init_random_function)(void) =
10054 test_init_uuid_random_function_simple :
10055 test_init_uuid_random_function_better);
10056 unsigned int (*random_function)(int) =
10058 test_uuid_random_function_simple :
10059 test_uuid_random_function_better);
10062 #if defined(PLATFORM_WIN32)
10065 random_name = "windows";
10066 init_random_function = test_init_uuid_random_function_better_windows;
10072 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10073 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10075 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10076 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10077 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10079 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10083 // always initialize random number generator at least once
10084 init_random_function();
10086 unsigned int time_start = SDL_GetTicks();
10088 for (i = 0; i < num_uuids; i++)
10092 char *seed = getStringCopy(init_random_function());
10094 hashtable_remove(hash_seeds, seed);
10095 hashtable_insert(hash_seeds, seed, "1");
10098 char *uuid = getStringCopy(getUUIDExt(random_function));
10100 hashtable_remove(hash_uuids, uuid);
10101 hashtable_insert(hash_uuids, uuid, "1");
10104 int num_unique_seeds = hashtable_count(hash_seeds);
10105 int num_unique_uuids = hashtable_count(hash_uuids);
10107 unsigned int time_needed = SDL_GetTicks() - time_start;
10109 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10111 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10114 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10116 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10117 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10119 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10121 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10123 Request(message, REQ_CONFIRM);
10125 hashtable_destroy(hash_seeds, 0);
10126 hashtable_destroy(hash_uuids, 0);
10129 void TestGeneratingUUIDs(void)
10131 int num_uuids = 1000000;
10134 for (i = 0; i < NUM_UUID_TESTS; i++)
10135 for (j = 0; j < 2; j++)
10136 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10138 CloseAllAndExit(0);