1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 // currently not available
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 // never draw masked screen borders on borderless screens
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD(void)
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void DrawTileCursor(int draw_target)
634 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
639 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
644 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
647 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
652 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653 BlitScreenToBitmap_EM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655 BlitScreenToBitmap_SP(target_bitmap);
656 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657 BlitScreenToBitmap_MM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659 BlitScreenToBitmap_RND(target_bitmap);
661 redraw_mask |= REDRAW_FIELD;
664 static void DrawFramesPerSecond(void)
667 int font_nr = FONT_TEXT_2;
668 int font_width = getFontWidth(font_nr);
669 int draw_deactivation_mask = GetDrawDeactivationMask();
670 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
672 // draw FPS with leading space (needed if field buffer deactivated)
673 sprintf(text, " %04.1f fps", global.frames_per_second);
675 // override draw deactivation mask (required for invisible warp mode)
676 SetDrawDeactivationMask(REDRAW_NONE);
678 // draw opaque FPS if field buffer deactivated, else draw masked FPS
679 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
682 // set draw deactivation mask to previous value
683 SetDrawDeactivationMask(draw_deactivation_mask);
685 // force full-screen redraw in this frame
686 redraw_mask = REDRAW_ALL;
690 static void PrintFrameTimeDebugging(void)
692 static unsigned int last_counter = 0;
693 unsigned int counter = Counter();
694 int diff_1 = counter - last_counter;
695 int diff_2 = diff_1 - GAME_FRAME_DELAY;
697 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698 char diff_bar[2 * diff_2_max + 5];
702 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
704 for (i = 0; i < diff_2_max; i++)
705 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706 i >= diff_2_max - diff_2_cut ? '-' : ' ');
708 diff_bar[pos++] = '|';
710 for (i = 0; i < diff_2_max; i++)
711 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
713 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
715 diff_bar[pos++] = '\0';
717 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
720 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
723 last_counter = counter;
727 static int unifiedRedrawMask(int mask)
729 if (mask & REDRAW_ALL)
732 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
740 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
743 void BackToFront(void)
745 static int last_redraw_mask = REDRAW_NONE;
747 // force screen redraw in every frame to continue drawing global animations
748 // (but always use the last redraw mask to prevent unwanted side effects)
749 if (redraw_mask == REDRAW_NONE)
750 redraw_mask = last_redraw_mask;
752 last_redraw_mask = redraw_mask;
755 // masked border now drawn immediately when blitting backbuffer to window
757 // draw masked border to all viewports, if defined
758 DrawMaskedBorder(redraw_mask);
761 // draw frames per second (only if debug mode is enabled)
762 if (redraw_mask & REDRAW_FPS)
763 DrawFramesPerSecond();
765 // remove playfield redraw before potentially merging with doors redraw
766 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767 redraw_mask &= ~REDRAW_FIELD;
769 // redraw complete window if both playfield and (some) doors need redraw
770 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771 redraw_mask = REDRAW_ALL;
773 /* although redrawing the whole window would be fine for normal gameplay,
774 being able to only redraw the playfield is required for deactivating
775 certain drawing areas (mainly playfield) to work, which is needed for
776 warp-forward to be fast enough (by skipping redraw of most frames) */
778 if (redraw_mask & REDRAW_ALL)
780 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
782 else if (redraw_mask & REDRAW_FIELD)
784 BlitBitmap(backbuffer, window,
785 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
787 else if (redraw_mask & REDRAW_DOORS)
789 // merge door areas to prevent calling screen redraw more than once
795 if (redraw_mask & REDRAW_DOOR_1)
799 x2 = MAX(x2, DX + DXSIZE);
800 y2 = MAX(y2, DY + DYSIZE);
803 if (redraw_mask & REDRAW_DOOR_2)
807 x2 = MAX(x2, VX + VXSIZE);
808 y2 = MAX(y2, VY + VYSIZE);
811 if (redraw_mask & REDRAW_DOOR_3)
815 x2 = MAX(x2, EX + EXSIZE);
816 y2 = MAX(y2, EY + EYSIZE);
819 // make sure that at least one pixel is blitted, and inside the screen
820 // (else nothing is blitted, causing the animations not to be updated)
821 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823 x2 = MIN(MAX(1, x2), WIN_XSIZE);
824 y2 = MIN(MAX(1, y2), WIN_YSIZE);
826 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
829 redraw_mask = REDRAW_NONE;
832 PrintFrameTimeDebugging();
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
838 unsigned int frame_delay_value_old = GetVideoFrameDelay();
840 SetVideoFrameDelay(frame_delay_value);
844 SetVideoFrameDelay(frame_delay_value_old);
847 static int fade_type_skip = FADE_TYPE_NONE;
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
851 void (*draw_border_function)(void) = NULL;
852 int x, y, width, height;
853 int fade_delay, post_delay;
855 if (fade_type == FADE_TYPE_FADE_OUT)
857 if (fade_type_skip != FADE_TYPE_NONE)
859 // skip all fade operations until specified fade operation
860 if (fade_type & fade_type_skip)
861 fade_type_skip = FADE_TYPE_NONE;
866 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
870 redraw_mask |= fade_mask;
872 if (fade_type == FADE_TYPE_SKIP)
874 fade_type_skip = fade_mode;
879 fade_delay = fading.fade_delay;
880 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
882 if (fade_type_skip != FADE_TYPE_NONE)
884 // skip all fade operations until specified fade operation
885 if (fade_type & fade_type_skip)
886 fade_type_skip = FADE_TYPE_NONE;
891 if (global.autoplay_leveldir)
896 if (fade_mask == REDRAW_FIELD)
901 height = FADE_SYSIZE;
903 if (border.draw_masked_when_fading)
904 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
906 DrawMaskedBorder_FIELD(); // draw once
916 // when switching screens without fading, set fade delay to zero
917 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
920 // do not display black frame when fading out without fade delay
921 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
924 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925 draw_border_function);
927 redraw_mask &= ~fade_mask;
929 ClearAutoRepeatKeyEvents();
932 static void SetScreenStates_BeforeFadingIn(void)
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
940 // set screen mode for animations back to fading
941 global.anim_status = GAME_MODE_PSEUDO_FADING;
944 static void SetScreenStates_AfterFadingIn(void)
946 // store new source screen (to use correct masked border for fading)
947 gfx.fade_border_source_status = global.border_status;
949 global.anim_status = global.anim_status_next;
952 static void SetScreenStates_BeforeFadingOut(void)
954 // store new target screen (to use correct masked border for fading)
955 gfx.fade_border_target_status = game_status;
957 // set screen mode for animations to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 // store backbuffer with all animations that will be stopped for fading out
961 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
964 static void SetScreenStates_AfterFadingOut(void)
966 global.border_status = game_status;
969 void FadeIn(int fade_mask)
971 SetScreenStates_BeforeFadingIn();
974 DrawMaskedBorder(REDRAW_ALL);
977 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
980 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
984 FADE_SXSIZE = FULL_SXSIZE;
985 FADE_SYSIZE = FULL_SYSIZE;
987 // activate virtual buttons depending on upcoming game status
988 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989 game_status == GAME_MODE_PLAYING && !tape.playing)
990 SetOverlayActive(TRUE);
992 SetScreenStates_AfterFadingIn();
994 // force update of global animation status in case of rapid screen changes
995 redraw_mask = REDRAW_ALL;
999 void FadeOut(int fade_mask)
1001 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1006 SetScreenStates_BeforeFadingOut();
1008 SetTileCursorActive(FALSE);
1009 SetOverlayActive(FALSE);
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1018 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1020 SetScreenStates_AfterFadingOut();
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1025 static struct TitleFadingInfo fading_leave_stored;
1028 fading_leave_stored = fading_leave;
1030 fading = fading_leave_stored;
1033 void FadeSetEnterMenu(void)
1035 fading = menu.enter_menu;
1037 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1040 void FadeSetLeaveMenu(void)
1042 fading = menu.leave_menu;
1044 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetEnterScreen(void)
1049 fading = menu.enter_screen[game_status];
1051 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1054 void FadeSetNextScreen(void)
1056 fading = menu.next_screen[game_status];
1058 // (do not overwrite fade mode set by FadeSetEnterScreen)
1059 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1062 void FadeSetLeaveScreen(void)
1064 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1067 void FadeSetFromType(int type)
1069 if (type & TYPE_ENTER_SCREEN)
1070 FadeSetEnterScreen();
1071 else if (type & TYPE_ENTER)
1073 else if (type & TYPE_LEAVE)
1077 void FadeSetDisabled(void)
1079 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1081 fading = fading_none;
1084 void FadeSkipNextFadeIn(void)
1086 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1089 void FadeSkipNextFadeOut(void)
1091 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1096 if (graphic == IMG_UNDEFINED)
1099 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1101 return (graphic_info[graphic].bitmap != NULL || redefined ?
1102 graphic_info[graphic].bitmap :
1103 graphic_info[default_graphic].bitmap);
1106 static Bitmap *getBackgroundBitmap(int graphic)
1108 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1113 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1119 (status == GAME_MODE_MAIN ||
1120 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1121 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1122 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1123 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1126 return getGlobalBorderBitmap(graphic);
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1131 if (graphic_info[graphic].bitmap)
1132 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1135 void SetMainBackgroundImageIfDefined(int graphic)
1137 if (graphic_info[graphic].bitmap)
1138 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1143 if (graphic_info[graphic].bitmap)
1144 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1147 void SetWindowBackgroundImage(int graphic)
1149 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetMainBackgroundImage(int graphic)
1154 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetDoorBackgroundImage(int graphic)
1159 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetPanelBackground(void)
1164 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1166 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1169 SetDoorBackgroundBitmap(bitmap_db_panel);
1172 void DrawBackground(int x, int y, int width, int height)
1174 // "drawto" might still point to playfield buffer here (hall of fame)
1175 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1177 if (IN_GFX_FIELD_FULL(x, y))
1178 redraw_mask |= REDRAW_FIELD;
1179 else if (IN_GFX_DOOR_1(x, y))
1180 redraw_mask |= REDRAW_DOOR_1;
1181 else if (IN_GFX_DOOR_2(x, y))
1182 redraw_mask |= REDRAW_DOOR_2;
1183 else if (IN_GFX_DOOR_3(x, y))
1184 redraw_mask |= REDRAW_DOOR_3;
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1189 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1191 if (font->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1199 struct GraphicInfo *g = &graphic_info[graphic];
1201 if (g->bitmap == NULL)
1204 DrawBackground(x, y, width, height);
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1219 boolean CheckIfGlobalBorderHasChanged(void)
1221 // if game status has not changed, global border has not changed either
1222 if (game_status == game_status_last)
1225 // determine and store new global border bitmap for current game status
1226 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1228 return (global_border_bitmap_last != global_border_bitmap);
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1236 // if game status has not changed, nothing has to be redrawn
1237 if (game_status == game_status_last)
1240 // redraw if last screen was title screen
1241 if (game_status_last == GAME_MODE_TITLE)
1244 // redraw if global screen border has changed
1245 if (CheckIfGlobalBorderHasChanged())
1248 // redraw if position or size of playfield area has changed
1249 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1253 // redraw if position or size of door area has changed
1254 if (dx_last != DX || dy_last != DY ||
1255 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1258 // redraw if position or size of tape area has changed
1259 if (vx_last != VX || vy_last != VY ||
1260 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1263 // redraw if position or size of editor area has changed
1264 if (ex_last != EX || ey_last != EY ||
1265 exsize_last != EXSIZE || eysize_last != EYSIZE)
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1275 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1277 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1280 void RedrawGlobalBorder(void)
1282 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 RedrawGlobalBorderFromBitmap(bitmap);
1286 redraw_mask = REDRAW_ALL;
1289 static void RedrawGlobalBorderIfNeeded(void)
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 if (game_status == game_status_last)
1296 // copy current draw buffer to later copy back areas that have not changed
1297 if (game_status_last != GAME_MODE_TITLE)
1298 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301 if (CheckIfGlobalBorderRedrawIsNeeded())
1303 // determine and store new global border bitmap for current game status
1304 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1307 // redraw global screen border (or clear, if defined to be empty)
1308 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1310 if (game_status == GAME_MODE_EDITOR)
1311 DrawSpecialEditorDoor();
1313 // copy previous playfield and door areas, if they are defined on both
1314 // previous and current screen and if they still have the same size
1316 if (real_sx_last != -1 && real_sy_last != -1 &&
1317 REAL_SX != -1 && REAL_SY != -1 &&
1318 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1323 if (dx_last != -1 && dy_last != -1 &&
1324 DX != -1 && DY != -1 &&
1325 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326 BlitBitmap(bitmap_db_store_1, backbuffer,
1327 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1329 if (game_status != GAME_MODE_EDITOR)
1331 if (vx_last != -1 && vy_last != -1 &&
1332 VX != -1 && VY != -1 &&
1333 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334 BlitBitmap(bitmap_db_store_1, backbuffer,
1335 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1339 if (ex_last != -1 && ey_last != -1 &&
1340 EX != -1 && EY != -1 &&
1341 exsize_last == EXSIZE && eysize_last == EYSIZE)
1342 BlitBitmap(bitmap_db_store_1, backbuffer,
1343 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1346 redraw_mask = REDRAW_ALL;
1349 game_status_last = game_status;
1351 global_border_bitmap_last = global_border_bitmap;
1353 real_sx_last = REAL_SX;
1354 real_sy_last = REAL_SY;
1355 full_sxsize_last = FULL_SXSIZE;
1356 full_sysize_last = FULL_SYSIZE;
1359 dxsize_last = DXSIZE;
1360 dysize_last = DYSIZE;
1363 vxsize_last = VXSIZE;
1364 vysize_last = VYSIZE;
1367 exsize_last = EXSIZE;
1368 eysize_last = EYSIZE;
1371 void ClearField(void)
1373 RedrawGlobalBorderIfNeeded();
1375 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376 // (when entering hall of fame after playing)
1377 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1379 // !!! maybe this should be done before clearing the background !!!
1380 if (game_status == GAME_MODE_PLAYING)
1382 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1387 SetDrawtoField(DRAW_TO_BACKBUFFER);
1391 void MarkTileDirty(int x, int y)
1393 redraw_mask |= REDRAW_FIELD;
1396 void SetBorderElement(void)
1400 BorderElement = EL_EMPTY;
1402 // only the R'n'D game engine may use an additional steelwall border
1403 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1406 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1408 for (x = 0; x < lev_fieldx; x++)
1410 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411 BorderElement = EL_STEELWALL;
1413 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1419 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1420 int max_array_fieldx, int max_array_fieldy,
1421 short field[max_array_fieldx][max_array_fieldy],
1422 int max_fieldx, int max_fieldy)
1424 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1425 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1426 int old_element = field[start_x][start_y];
1429 // do nothing if start field already has the desired content
1430 if (old_element == fill_element)
1433 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1435 while (stack_pos > 0)
1437 struct XY current = stack_buffer[--stack_pos];
1440 field[current.x][current.y] = fill_element;
1442 for (i = 0; i < 4; i++)
1444 int x = current.x + check[i].x;
1445 int y = current.y + check[i].y;
1447 // check for stack buffer overflow (should not happen)
1448 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1449 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1451 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1452 stack_buffer[stack_pos++] = (struct XY){ x, y };
1457 void FloodFillLevel(int from_x, int from_y, int fill_element,
1458 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1459 int max_fieldx, int max_fieldy)
1461 FloodFillLevelExt(from_x, from_y, fill_element,
1462 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1463 max_fieldx, max_fieldy);
1466 void SetRandomAnimationValue(int x, int y)
1468 gfx.anim_random_frame = GfxRandom[x][y];
1471 int getGraphicAnimationFrame(int graphic, int sync_frame)
1473 // animation synchronized with global frame counter, not move position
1474 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1475 sync_frame = FrameCounter;
1477 return getAnimationFrame(graphic_info[graphic].anim_frames,
1478 graphic_info[graphic].anim_delay,
1479 graphic_info[graphic].anim_mode,
1480 graphic_info[graphic].anim_start_frame,
1484 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1486 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1488 struct GraphicInfo *g = &graphic_info[graphic];
1489 int xsize = MAX(1, g->anim_frames_per_line);
1490 int ysize = MAX(1, g->anim_frames / xsize);
1491 int xoffset = g->anim_start_frame % xsize;
1492 int yoffset = g->anim_start_frame % ysize;
1493 int x = (lx + xoffset) % xsize;
1494 int y = (ly + yoffset) % ysize;
1495 int sync_frame = y * xsize + x;
1497 return sync_frame % g->anim_frames;
1500 return getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1503 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1505 struct GraphicInfo *g = &graphic_info[graphic];
1506 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1508 if (tilesize == gfx.standard_tile_size)
1509 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1510 else if (tilesize == game.tile_size)
1511 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1513 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1516 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1517 boolean get_backside)
1519 struct GraphicInfo *g = &graphic_info[graphic];
1520 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1521 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1523 if (g->offset_y == 0) // frames are ordered horizontally
1525 int max_width = g->anim_frames_per_line * g->width;
1526 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1528 *x = pos % max_width;
1529 *y = src_y % g->height + pos / max_width * g->height;
1531 else if (g->offset_x == 0) // frames are ordered vertically
1533 int max_height = g->anim_frames_per_line * g->height;
1534 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1536 *x = src_x % g->width + pos / max_height * g->width;
1537 *y = pos % max_height;
1539 else // frames are ordered diagonally
1541 *x = src_x + frame * g->offset_x;
1542 *y = src_y + frame * g->offset_y;
1546 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1547 Bitmap **bitmap, int *x, int *y,
1548 boolean get_backside)
1550 struct GraphicInfo *g = &graphic_info[graphic];
1552 // if no graphics defined at all, use fallback graphics
1553 if (g->bitmaps == NULL)
1554 *g = graphic_info[IMG_CHAR_EXCLAM];
1556 // if no in-game graphics defined, always use standard graphic size
1557 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1558 tilesize = TILESIZE;
1560 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1561 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1563 *x = *x * tilesize / g->tile_size;
1564 *y = *y * tilesize / g->tile_size;
1567 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1568 Bitmap **bitmap, int *x, int *y)
1570 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1573 void getFixedGraphicSource(int graphic, int frame,
1574 Bitmap **bitmap, int *x, int *y)
1576 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1579 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1581 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1584 void getGlobalAnimGraphicSource(int graphic, int frame,
1585 Bitmap **bitmap, int *x, int *y)
1587 struct GraphicInfo *g = &graphic_info[graphic];
1589 // if no graphics defined at all, use fallback graphics
1590 if (g->bitmaps == NULL)
1591 *g = graphic_info[IMG_CHAR_EXCLAM];
1593 // use original size graphics, if existing, else use standard size graphics
1594 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1595 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1597 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1599 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1602 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1603 int *x, int *y, boolean get_backside)
1605 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1609 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1611 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1614 void DrawGraphic(int x, int y, int graphic, int frame)
1617 if (!IN_SCR_FIELD(x, y))
1619 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1620 Debug("draw:DrawGraphic", "This should never happen!");
1626 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1629 MarkTileDirty(x, y);
1632 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1635 if (!IN_SCR_FIELD(x, y))
1637 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1639 Debug("draw:DrawFixedGraphic", "This should never happen!");
1645 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1647 MarkTileDirty(x, y);
1650 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1656 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1658 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1661 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1667 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1668 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1671 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1674 if (!IN_SCR_FIELD(x, y))
1676 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1678 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1684 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1687 MarkTileDirty(x, y);
1690 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1693 if (!IN_SCR_FIELD(x, y))
1695 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1697 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1703 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1705 MarkTileDirty(x, y);
1708 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1714 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1716 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1720 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1721 int graphic, int frame)
1726 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1728 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1732 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1734 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1736 MarkTileDirty(x / tilesize, y / tilesize);
1739 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1742 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1743 graphic, frame, tilesize);
1744 MarkTileDirty(x / tilesize, y / tilesize);
1747 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1753 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1754 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1757 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1758 int frame, int tilesize)
1763 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1764 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1767 void DrawMiniGraphic(int x, int y, int graphic)
1769 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1770 MarkTileDirty(x / 2, y / 2);
1773 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1778 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1779 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1782 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1783 int graphic, int frame,
1784 int cut_mode, int mask_mode)
1789 int width = TILEX, height = TILEY;
1792 if (dx || dy) // shifted graphic
1794 if (x < BX1) // object enters playfield from the left
1801 else if (x > BX2) // object enters playfield from the right
1807 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1813 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1815 else if (dx) // general horizontal movement
1816 MarkTileDirty(x + SIGN(dx), y);
1818 if (y < BY1) // object enters playfield from the top
1820 if (cut_mode == CUT_BELOW) // object completely above top border
1828 else if (y > BY2) // object enters playfield from the bottom
1834 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1840 else if (dy > 0 && cut_mode == CUT_ABOVE)
1842 if (y == BY2) // object completely above bottom border
1848 MarkTileDirty(x, y + 1);
1849 } // object leaves playfield to the bottom
1850 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1852 else if (dy) // general vertical movement
1853 MarkTileDirty(x, y + SIGN(dy));
1857 if (!IN_SCR_FIELD(x, y))
1859 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1861 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1867 width = width * TILESIZE_VAR / TILESIZE;
1868 height = height * TILESIZE_VAR / TILESIZE;
1869 cx = cx * TILESIZE_VAR / TILESIZE;
1870 cy = cy * TILESIZE_VAR / TILESIZE;
1871 dx = dx * TILESIZE_VAR / TILESIZE;
1872 dy = dy * TILESIZE_VAR / TILESIZE;
1874 if (width > 0 && height > 0)
1876 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1881 dst_x = FX + x * TILEX_VAR + dx;
1882 dst_y = FY + y * TILEY_VAR + dy;
1884 if (mask_mode == USE_MASKING)
1885 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1888 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1891 MarkTileDirty(x, y);
1895 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1896 int graphic, int frame,
1897 int cut_mode, int mask_mode)
1902 int width = TILEX_VAR, height = TILEY_VAR;
1905 int x2 = x + SIGN(dx);
1906 int y2 = y + SIGN(dy);
1908 // movement with two-tile animations must be sync'ed with movement position,
1909 // not with current GfxFrame (which can be higher when using slow movement)
1910 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1911 int anim_frames = graphic_info[graphic].anim_frames;
1913 // (we also need anim_delay here for movement animations with less frames)
1914 int anim_delay = graphic_info[graphic].anim_delay;
1915 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1917 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1918 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1920 // re-calculate animation frame for two-tile movement animation
1921 frame = getGraphicAnimationFrame(graphic, sync_frame);
1923 // check if movement start graphic inside screen area and should be drawn
1924 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1926 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1928 dst_x = FX + x1 * TILEX_VAR;
1929 dst_y = FY + y1 * TILEY_VAR;
1931 if (mask_mode == USE_MASKING)
1932 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1935 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1938 MarkTileDirty(x1, y1);
1941 // check if movement end graphic inside screen area and should be drawn
1942 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1944 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1946 dst_x = FX + x2 * TILEX_VAR;
1947 dst_y = FY + y2 * TILEY_VAR;
1949 if (mask_mode == USE_MASKING)
1950 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1953 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1956 MarkTileDirty(x2, y2);
1960 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1961 int graphic, int frame,
1962 int cut_mode, int mask_mode)
1966 DrawGraphic(x, y, graphic, frame);
1971 if (graphic_info[graphic].double_movement) // EM style movement images
1972 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1974 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1977 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1978 int graphic, int frame, int cut_mode)
1980 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1983 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1984 int cut_mode, int mask_mode)
1986 int lx = LEVELX(x), ly = LEVELY(y);
1990 if (IN_LEV_FIELD(lx, ly))
1992 SetRandomAnimationValue(lx, ly);
1994 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1995 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
1997 // do not use double (EM style) movement graphic when not moving
1998 if (graphic_info[graphic].double_movement && !dx && !dy)
2000 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2001 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2004 if (game.use_masked_elements && (dx || dy))
2005 mask_mode = USE_MASKING;
2007 else // border element
2009 graphic = el2img(element);
2010 frame = getGraphicAnimationFrame(graphic, -1);
2013 if (element == EL_EXPANDABLE_WALL)
2015 boolean left_stopped = FALSE, right_stopped = FALSE;
2017 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2018 left_stopped = TRUE;
2019 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2020 right_stopped = TRUE;
2022 if (left_stopped && right_stopped)
2024 else if (left_stopped)
2026 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2027 frame = graphic_info[graphic].anim_frames - 1;
2029 else if (right_stopped)
2031 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2032 frame = graphic_info[graphic].anim_frames - 1;
2037 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2038 else if (mask_mode == USE_MASKING)
2039 DrawGraphicThruMask(x, y, graphic, frame);
2041 DrawGraphic(x, y, graphic, frame);
2044 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2045 int cut_mode, int mask_mode)
2047 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2048 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2049 cut_mode, mask_mode);
2052 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2055 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2058 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2061 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2064 void DrawLevelElementThruMask(int x, int y, int element)
2066 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2069 void DrawLevelFieldThruMask(int x, int y)
2071 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2074 // !!! implementation of quicksand is totally broken !!!
2075 #define IS_CRUMBLED_TILE(x, y, e) \
2076 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2077 !IS_MOVING(x, y) || \
2078 (e) == EL_QUICKSAND_EMPTYING || \
2079 (e) == EL_QUICKSAND_FAST_EMPTYING))
2081 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2086 int width, height, cx, cy;
2087 int sx = SCREENX(x), sy = SCREENY(y);
2088 int crumbled_border_size = graphic_info[graphic].border_size;
2089 int crumbled_tile_size = graphic_info[graphic].tile_size;
2090 int crumbled_border_size_var =
2091 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2094 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2096 for (i = 1; i < 4; i++)
2098 int dxx = (i & 1 ? dx : 0);
2099 int dyy = (i & 2 ? dy : 0);
2102 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2105 // check if neighbour field is of same crumble type
2106 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2107 graphic_info[graphic].class ==
2108 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2110 // return if check prevents inner corner
2111 if (same == (dxx == dx && dyy == dy))
2115 // if we reach this point, we have an inner corner
2117 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2119 width = crumbled_border_size_var;
2120 height = crumbled_border_size_var;
2121 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2122 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2124 if (game.use_masked_elements)
2126 int graphic0 = el2img(EL_EMPTY);
2127 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2128 Bitmap *src_bitmap0;
2131 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2133 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2135 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2137 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2139 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2142 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2144 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2147 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2152 int width, height, bx, by, cx, cy;
2153 int sx = SCREENX(x), sy = SCREENY(y);
2154 int crumbled_border_size = graphic_info[graphic].border_size;
2155 int crumbled_tile_size = graphic_info[graphic].tile_size;
2156 int crumbled_border_size_var =
2157 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2158 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2161 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2163 // only needed when using masked elements
2164 int graphic0 = el2img(EL_EMPTY);
2165 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2166 Bitmap *src_bitmap0;
2169 if (game.use_masked_elements)
2170 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2172 // draw simple, sloppy, non-corner-accurate crumbled border
2174 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2175 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2176 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2177 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2179 if (game.use_masked_elements)
2181 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2183 FX + sx * TILEX_VAR + cx,
2184 FY + sy * TILEY_VAR + cy);
2186 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2188 FX + sx * TILEX_VAR + cx,
2189 FY + sy * TILEY_VAR + cy);
2192 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2194 FX + sx * TILEX_VAR + cx,
2195 FY + sy * TILEY_VAR + cy);
2197 // (remaining middle border part must be at least as big as corner part)
2198 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2199 crumbled_border_size_var >= TILESIZE_VAR / 3)
2202 // correct corners of crumbled border, if needed
2204 for (i = -1; i <= 1; i += 2)
2206 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2207 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2208 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2211 // check if neighbour field is of same crumble type
2212 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2213 graphic_info[graphic].class ==
2214 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2216 // no crumbled corner, but continued crumbled border
2218 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2219 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2220 int b1 = (i == 1 ? crumbled_border_size_var :
2221 TILESIZE_VAR - 2 * crumbled_border_size_var);
2223 width = crumbled_border_size_var;
2224 height = crumbled_border_size_var;
2226 if (dir == 1 || dir == 2)
2241 if (game.use_masked_elements)
2243 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2245 FX + sx * TILEX_VAR + cx,
2246 FY + sy * TILEY_VAR + cy);
2248 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2250 FX + sx * TILEX_VAR + cx,
2251 FY + sy * TILEY_VAR + cy);
2254 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2256 FX + sx * TILEX_VAR + cx,
2257 FY + sy * TILEY_VAR + cy);
2262 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2264 int sx = SCREENX(x), sy = SCREENY(y);
2267 static int xy[4][2] =
2275 if (!IN_LEV_FIELD(x, y))
2278 element = TILE_GFX_ELEMENT(x, y);
2280 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2282 if (!IN_SCR_FIELD(sx, sy))
2285 // crumble field borders towards direct neighbour fields
2286 for (i = 0; i < 4; i++)
2288 int xx = x + xy[i][0];
2289 int yy = y + xy[i][1];
2291 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2294 // check if neighbour field is of same crumble type
2295 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2296 graphic_info[graphic].class ==
2297 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2300 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2303 // crumble inner field corners towards corner neighbour fields
2304 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2305 graphic_info[graphic].anim_frames == 2)
2307 for (i = 0; i < 4; i++)
2309 int dx = (i & 1 ? +1 : -1);
2310 int dy = (i & 2 ? +1 : -1);
2312 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2316 MarkTileDirty(sx, sy);
2318 else // center field is not crumbled -- crumble neighbour fields
2320 // crumble field borders of direct neighbour fields
2321 for (i = 0; i < 4; i++)
2323 int xx = x + xy[i][0];
2324 int yy = y + xy[i][1];
2325 int sxx = sx + xy[i][0];
2326 int syy = sy + xy[i][1];
2328 if (!IN_LEV_FIELD(xx, yy) ||
2329 !IN_SCR_FIELD(sxx, syy))
2332 // do not crumble fields that are being digged or snapped
2333 if (Tile[xx][yy] == EL_EMPTY ||
2334 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2337 element = TILE_GFX_ELEMENT(xx, yy);
2339 if (!IS_CRUMBLED_TILE(xx, yy, element))
2342 graphic = el_act2crm(element, ACTION_DEFAULT);
2344 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2346 MarkTileDirty(sxx, syy);
2349 // crumble inner field corners of corner neighbour fields
2350 for (i = 0; i < 4; i++)
2352 int dx = (i & 1 ? +1 : -1);
2353 int dy = (i & 2 ? +1 : -1);
2359 if (!IN_LEV_FIELD(xx, yy) ||
2360 !IN_SCR_FIELD(sxx, syy))
2363 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2366 element = TILE_GFX_ELEMENT(xx, yy);
2368 if (!IS_CRUMBLED_TILE(xx, yy, element))
2371 graphic = el_act2crm(element, ACTION_DEFAULT);
2373 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2374 graphic_info[graphic].anim_frames == 2)
2375 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2377 MarkTileDirty(sxx, syy);
2382 void DrawLevelFieldCrumbled(int x, int y)
2386 if (!IN_LEV_FIELD(x, y))
2389 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2390 GfxElement[x][y] != EL_UNDEFINED &&
2391 GFX_CRUMBLED(GfxElement[x][y]))
2393 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2398 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2400 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2403 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2406 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2407 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2408 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2409 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2410 int sx = SCREENX(x), sy = SCREENY(y);
2412 DrawScreenGraphic(sx, sy, graphic1, frame1);
2413 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2416 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2418 int sx = SCREENX(x), sy = SCREENY(y);
2419 static int xy[4][2] =
2428 // crumble direct neighbour fields (required for field borders)
2429 for (i = 0; i < 4; i++)
2431 int xx = x + xy[i][0];
2432 int yy = y + xy[i][1];
2433 int sxx = sx + xy[i][0];
2434 int syy = sy + xy[i][1];
2436 if (!IN_LEV_FIELD(xx, yy) ||
2437 !IN_SCR_FIELD(sxx, syy) ||
2438 !GFX_CRUMBLED(Tile[xx][yy]) ||
2442 DrawLevelField(xx, yy);
2445 // crumble corner neighbour fields (required for inner field corners)
2446 for (i = 0; i < 4; i++)
2448 int dx = (i & 1 ? +1 : -1);
2449 int dy = (i & 2 ? +1 : -1);
2455 if (!IN_LEV_FIELD(xx, yy) ||
2456 !IN_SCR_FIELD(sxx, syy) ||
2457 !GFX_CRUMBLED(Tile[xx][yy]) ||
2461 int element = TILE_GFX_ELEMENT(xx, yy);
2462 int graphic = el_act2crm(element, ACTION_DEFAULT);
2464 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2465 graphic_info[graphic].anim_frames == 2)
2466 DrawLevelField(xx, yy);
2470 static int getBorderElement(int x, int y)
2474 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2475 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2476 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2477 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2478 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2479 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2480 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2482 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2483 int steel_position = (x == -1 && y == -1 ? 0 :
2484 x == lev_fieldx && y == -1 ? 1 :
2485 x == -1 && y == lev_fieldy ? 2 :
2486 x == lev_fieldx && y == lev_fieldy ? 3 :
2487 x == -1 || x == lev_fieldx ? 4 :
2488 y == -1 || y == lev_fieldy ? 5 : 6);
2490 return border[steel_position][steel_type];
2493 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2495 if (game.use_masked_elements)
2497 if (graphic != el2img(EL_EMPTY))
2498 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2500 DrawGraphicThruMask(x, y, graphic, frame);
2504 DrawGraphic(x, y, graphic, frame);
2508 void DrawScreenElement(int x, int y, int element)
2510 int mask_mode = NO_MASKING;
2512 if (game.use_masked_elements)
2514 int lx = LEVELX(x), ly = LEVELY(y);
2516 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2518 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2520 mask_mode = USE_MASKING;
2524 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2525 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2528 void DrawLevelElement(int x, int y, int element)
2530 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2531 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2534 void DrawScreenField(int x, int y)
2536 int lx = LEVELX(x), ly = LEVELY(y);
2537 int element, content;
2539 if (!IN_LEV_FIELD(lx, ly))
2541 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2544 element = getBorderElement(lx, ly);
2546 DrawScreenElement(x, y, element);
2551 element = Tile[lx][ly];
2552 content = Store[lx][ly];
2554 if (IS_MOVING(lx, ly))
2556 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2557 boolean cut_mode = NO_CUTTING;
2559 if (element == EL_QUICKSAND_EMPTYING ||
2560 element == EL_QUICKSAND_FAST_EMPTYING ||
2561 element == EL_MAGIC_WALL_EMPTYING ||
2562 element == EL_BD_MAGIC_WALL_EMPTYING ||
2563 element == EL_DC_MAGIC_WALL_EMPTYING ||
2564 element == EL_AMOEBA_DROPPING)
2565 cut_mode = CUT_ABOVE;
2566 else if (element == EL_QUICKSAND_FILLING ||
2567 element == EL_QUICKSAND_FAST_FILLING ||
2568 element == EL_MAGIC_WALL_FILLING ||
2569 element == EL_BD_MAGIC_WALL_FILLING ||
2570 element == EL_DC_MAGIC_WALL_FILLING)
2571 cut_mode = CUT_BELOW;
2573 if (cut_mode == CUT_ABOVE)
2574 DrawScreenElement(x, y, element);
2576 DrawScreenElement(x, y, EL_EMPTY);
2578 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2580 int dir = MovDir[lx][ly];
2581 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2582 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2584 if (IN_SCR_FIELD(newx, newy))
2585 DrawScreenElement(newx, newy, EL_EMPTY);
2589 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2590 else if (cut_mode == NO_CUTTING)
2591 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2594 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2596 if (cut_mode == CUT_BELOW &&
2597 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2598 DrawLevelElement(lx, ly + 1, element);
2601 if (content == EL_ACID)
2603 int dir = MovDir[lx][ly];
2604 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2605 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2607 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2609 // prevent target field from being drawn again (but without masking)
2610 // (this would happen if target field is scanned after moving element)
2611 Stop[newlx][newly] = TRUE;
2614 else if (IS_BLOCKED(lx, ly))
2619 boolean cut_mode = NO_CUTTING;
2620 int element_old, content_old;
2622 Blocked2Moving(lx, ly, &oldx, &oldy);
2625 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2626 MovDir[oldx][oldy] == MV_RIGHT);
2628 element_old = Tile[oldx][oldy];
2629 content_old = Store[oldx][oldy];
2631 if (element_old == EL_QUICKSAND_EMPTYING ||
2632 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2633 element_old == EL_MAGIC_WALL_EMPTYING ||
2634 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2635 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2636 element_old == EL_AMOEBA_DROPPING)
2637 cut_mode = CUT_ABOVE;
2639 DrawScreenElement(x, y, EL_EMPTY);
2642 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2644 else if (cut_mode == NO_CUTTING)
2645 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2648 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2651 else if (IS_DRAWABLE(element))
2652 DrawScreenElement(x, y, element);
2654 DrawScreenElement(x, y, EL_EMPTY);
2657 void DrawLevelField(int x, int y)
2659 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2660 DrawScreenField(SCREENX(x), SCREENY(y));
2661 else if (IS_MOVING(x, y))
2665 Moving2Blocked(x, y, &newx, &newy);
2666 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2667 DrawScreenField(SCREENX(newx), SCREENY(newy));
2669 else if (IS_BLOCKED(x, y))
2673 Blocked2Moving(x, y, &oldx, &oldy);
2674 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2675 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2679 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2680 int (*el2img_function)(int), boolean masked,
2681 int element_bits_draw)
2683 int element_base = map_mm_wall_element(element);
2684 int element_bits = (IS_DF_WALL(element) ?
2685 element - EL_DF_WALL_START :
2686 IS_MM_WALL(element) ?
2687 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2688 int graphic = el2img_function(element_base);
2689 int tilesize_draw = tilesize / 2;
2694 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2696 for (i = 0; i < 4; i++)
2698 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2699 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2701 if (!(element_bits_draw & (1 << i)))
2704 if (element_bits & (1 << i))
2707 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2708 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2710 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2711 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2716 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2717 tilesize_draw, tilesize_draw);
2722 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2723 boolean masked, int element_bits_draw)
2725 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2726 element, tilesize, el2edimg, masked, element_bits_draw);
2729 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2730 int (*el2img_function)(int))
2732 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2736 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2739 if (IS_MM_WALL(element))
2741 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2742 element, tilesize, el2edimg, masked, 0x000f);
2746 int graphic = el2edimg(element);
2749 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2751 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2755 void DrawSizedElement(int x, int y, int element, int tilesize)
2757 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2760 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2762 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2765 void DrawMiniElement(int x, int y, int element)
2769 graphic = el2edimg(element);
2770 DrawMiniGraphic(x, y, graphic);
2773 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2776 int x = sx + scroll_x, y = sy + scroll_y;
2778 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2779 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2780 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2781 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2783 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2786 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2788 int x = sx + scroll_x, y = sy + scroll_y;
2790 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2791 DrawMiniElement(sx, sy, EL_EMPTY);
2792 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2793 DrawMiniElement(sx, sy, Tile[x][y]);
2795 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2798 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2799 int x, int y, int xsize, int ysize,
2800 int tile_width, int tile_height)
2804 int dst_x = startx + x * tile_width;
2805 int dst_y = starty + y * tile_height;
2806 int width = graphic_info[graphic].width;
2807 int height = graphic_info[graphic].height;
2808 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2809 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2810 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2811 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2812 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2813 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2814 boolean draw_masked = graphic_info[graphic].draw_masked;
2816 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2818 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2820 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2824 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2825 inner_sx + (x - 1) * tile_width % inner_width);
2826 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2827 inner_sy + (y - 1) * tile_height % inner_height);
2830 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2833 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2837 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2838 int x, int y, int xsize, int ysize,
2841 int font_width = getFontWidth(font_nr);
2842 int font_height = getFontHeight(font_nr);
2844 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2845 font_width, font_height);
2848 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2850 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2851 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2852 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2853 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2854 boolean no_delay = (tape.warp_forward);
2855 unsigned int anim_delay = 0;
2856 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2857 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2858 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2859 int font_width = getFontWidth(font_nr);
2860 int font_height = getFontHeight(font_nr);
2861 int max_xsize = level.envelope[envelope_nr].xsize;
2862 int max_ysize = level.envelope[envelope_nr].ysize;
2863 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2864 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2865 int xend = max_xsize;
2866 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2867 int xstep = (xstart < xend ? 1 : 0);
2868 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2870 int end = MAX(xend - xstart, yend - ystart);
2873 for (i = start; i <= end; i++)
2875 int last_frame = end; // last frame of this "for" loop
2876 int x = xstart + i * xstep;
2877 int y = ystart + i * ystep;
2878 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2879 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2880 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2881 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2884 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2886 BlitScreenToBitmap(backbuffer);
2888 SetDrawtoField(DRAW_TO_BACKBUFFER);
2890 for (yy = 0; yy < ysize; yy++)
2891 for (xx = 0; xx < xsize; xx++)
2892 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2894 DrawTextBuffer(sx + font_width, sy + font_height,
2895 level.envelope[envelope_nr].text, font_nr, max_xsize,
2896 xsize - 2, ysize - 2, 0, mask_mode,
2897 level.envelope[envelope_nr].autowrap,
2898 level.envelope[envelope_nr].centered, FALSE);
2900 redraw_mask |= REDRAW_FIELD;
2903 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2906 ClearAutoRepeatKeyEvents();
2909 void ShowEnvelope(int envelope_nr)
2911 int element = EL_ENVELOPE_1 + envelope_nr;
2912 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2913 int sound_opening = element_info[element].sound[ACTION_OPENING];
2914 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2915 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2916 boolean no_delay = (tape.warp_forward);
2917 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2918 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2919 int anim_mode = graphic_info[graphic].anim_mode;
2920 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2921 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2922 boolean overlay_enabled = GetOverlayEnabled();
2924 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2926 SetOverlayEnabled(FALSE);
2929 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2931 if (anim_mode == ANIM_DEFAULT)
2932 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2934 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2937 Delay_WithScreenUpdates(wait_delay_value);
2939 WaitForEventToContinue();
2942 SetOverlayEnabled(overlay_enabled);
2944 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2946 if (anim_mode != ANIM_NONE)
2947 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2949 if (anim_mode == ANIM_DEFAULT)
2950 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2952 game.envelope_active = FALSE;
2954 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2956 redraw_mask |= REDRAW_FIELD;
2960 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2961 int xsize, int ysize)
2963 if (!global.use_envelope_request ||
2964 request.sort_priority <= 0)
2967 if (request.bitmap == NULL ||
2968 xsize > request.xsize ||
2969 ysize > request.ysize)
2971 if (request.bitmap != NULL)
2972 FreeBitmap(request.bitmap);
2974 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2976 SDL_Surface *surface = request.bitmap->surface;
2978 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2979 Fail("SDLGetNativeSurface() failed");
2982 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2984 SDLFreeBitmapTextures(request.bitmap);
2985 SDLCreateBitmapTextures(request.bitmap);
2987 // set envelope request run-time values
2990 request.xsize = xsize;
2991 request.ysize = ysize;
2994 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2996 if (global.use_envelope_request &&
2997 game.request_active_or_moving &&
2998 request.sort_priority > 0 &&
2999 drawing_target == DRAW_TO_SCREEN &&
3000 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3002 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3003 request.sx, request.sy);
3007 static void setRequestBasePosition(int *x, int *y)
3009 int sx_base, sy_base;
3011 if (request.x != -1)
3012 sx_base = request.x;
3013 else if (request.align == ALIGN_LEFT)
3015 else if (request.align == ALIGN_RIGHT)
3016 sx_base = SX + SXSIZE;
3018 sx_base = SX + SXSIZE / 2;
3020 if (request.y != -1)
3021 sy_base = request.y;
3022 else if (request.valign == VALIGN_TOP)
3024 else if (request.valign == VALIGN_BOTTOM)
3025 sy_base = SY + SYSIZE;
3027 sy_base = SY + SYSIZE / 2;
3033 static void setRequestPositionExt(int *x, int *y, int width, int height,
3034 boolean add_border_size)
3036 int border_size = request.border_size;
3037 int sx_base, sy_base;
3040 setRequestBasePosition(&sx_base, &sy_base);
3042 if (request.align == ALIGN_LEFT)
3044 else if (request.align == ALIGN_RIGHT)
3045 sx = sx_base - width;
3047 sx = sx_base - width / 2;
3049 if (request.valign == VALIGN_TOP)
3051 else if (request.valign == VALIGN_BOTTOM)
3052 sy = sy_base - height;
3054 sy = sy_base - height / 2;
3056 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3057 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3059 if (add_border_size)
3069 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3071 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3074 static void DrawEnvelopeRequest(char *text)
3076 char *text_final = text;
3077 char *text_door_style = NULL;
3078 int graphic = IMG_BACKGROUND_REQUEST;
3079 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3080 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3081 int font_nr = FONT_REQUEST;
3082 int font_width = getFontWidth(font_nr);
3083 int font_height = getFontHeight(font_nr);
3084 int border_size = request.border_size;
3085 int line_spacing = request.line_spacing;
3086 int line_height = font_height + line_spacing;
3087 int max_text_width = request.width - 2 * border_size;
3088 int max_text_height = request.height - 2 * border_size;
3089 int line_length = max_text_width / font_width;
3090 int max_lines = max_text_height / line_height;
3091 int text_width = line_length * font_width;
3092 int width = request.width;
3093 int height = request.height;
3094 int tile_size = MAX(request.step_offset, 1);
3095 int x_steps = width / tile_size;
3096 int y_steps = height / tile_size;
3097 int sx_offset = border_size;
3098 int sy_offset = border_size;
3102 if (request.centered)
3103 sx_offset = (request.width - text_width) / 2;
3105 if (request.wrap_single_words && !request.autowrap)
3107 char *src_text_ptr, *dst_text_ptr;
3109 text_door_style = checked_malloc(2 * strlen(text) + 1);
3111 src_text_ptr = text;
3112 dst_text_ptr = text_door_style;
3114 while (*src_text_ptr)
3116 if (*src_text_ptr == ' ' ||
3117 *src_text_ptr == '?' ||
3118 *src_text_ptr == '!')
3119 *dst_text_ptr++ = '\n';
3121 if (*src_text_ptr != ' ')
3122 *dst_text_ptr++ = *src_text_ptr;
3127 *dst_text_ptr = '\0';
3129 text_final = text_door_style;
3132 setRequestPosition(&sx, &sy, FALSE);
3134 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3136 for (y = 0; y < y_steps; y++)
3137 for (x = 0; x < x_steps; x++)
3138 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3139 x, y, x_steps, y_steps,
3140 tile_size, tile_size);
3142 // force DOOR font inside door area
3143 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3145 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3146 line_length, -1, max_lines, line_spacing, mask_mode,
3147 request.autowrap, request.centered, FALSE);
3151 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3152 RedrawGadget(tool_gadget[i]);
3154 // store readily prepared envelope request for later use when animating
3155 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3157 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3159 if (text_door_style)
3160 free(text_door_style);
3163 static void AnimateEnvelopeRequest(int anim_mode, int action)
3165 int graphic = IMG_BACKGROUND_REQUEST;
3166 boolean draw_masked = graphic_info[graphic].draw_masked;
3167 int delay_value_normal = request.step_delay;
3168 int delay_value_fast = delay_value_normal / 2;
3169 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3170 boolean no_delay = (tape.warp_forward);
3171 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3172 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3173 unsigned int anim_delay = 0;
3175 int tile_size = MAX(request.step_offset, 1);
3176 int max_xsize = request.width / tile_size;
3177 int max_ysize = request.height / tile_size;
3178 int max_xsize_inner = max_xsize - 2;
3179 int max_ysize_inner = max_ysize - 2;
3181 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3182 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3183 int xend = max_xsize_inner;
3184 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3185 int xstep = (xstart < xend ? 1 : 0);
3186 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3188 int end = MAX(xend - xstart, yend - ystart);
3191 if (setup.quick_doors)
3198 for (i = start; i <= end; i++)
3200 int last_frame = end; // last frame of this "for" loop
3201 int x = xstart + i * xstep;
3202 int y = ystart + i * ystep;
3203 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3204 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3205 int xsize_size_left = (xsize - 1) * tile_size;
3206 int ysize_size_top = (ysize - 1) * tile_size;
3207 int max_xsize_pos = (max_xsize - 1) * tile_size;
3208 int max_ysize_pos = (max_ysize - 1) * tile_size;
3209 int width = xsize * tile_size;
3210 int height = ysize * tile_size;
3215 setRequestPosition(&src_x, &src_y, FALSE);
3216 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3218 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3220 for (yy = 0; yy < 2; yy++)
3222 for (xx = 0; xx < 2; xx++)
3224 int src_xx = src_x + xx * max_xsize_pos;
3225 int src_yy = src_y + yy * max_ysize_pos;
3226 int dst_xx = dst_x + xx * xsize_size_left;
3227 int dst_yy = dst_y + yy * ysize_size_top;
3228 int xx_size = (xx ? tile_size : xsize_size_left);
3229 int yy_size = (yy ? tile_size : ysize_size_top);
3232 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3233 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3235 BlitBitmap(bitmap_db_store_2, backbuffer,
3236 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3240 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3242 redraw_mask |= REDRAW_FIELD;
3246 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3249 ClearAutoRepeatKeyEvents();
3252 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3254 int graphic = IMG_BACKGROUND_REQUEST;
3255 int sound_opening = SND_REQUEST_OPENING;
3256 int sound_closing = SND_REQUEST_CLOSING;
3257 int anim_mode_1 = request.anim_mode; // (higher priority)
3258 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3259 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3260 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3261 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3263 if (game_status == GAME_MODE_PLAYING)
3264 BlitScreenToBitmap(backbuffer);
3266 SetDrawtoField(DRAW_TO_BACKBUFFER);
3268 // SetDrawBackgroundMask(REDRAW_NONE);
3270 if (action == ACTION_OPENING)
3272 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3274 if (req_state & REQ_ASK)
3276 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3277 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3278 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3279 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3281 else if (req_state & REQ_CONFIRM)
3283 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3284 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3286 else if (req_state & REQ_PLAYER)
3288 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3289 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3290 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3291 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3294 DrawEnvelopeRequest(text);
3297 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3299 if (action == ACTION_OPENING)
3301 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3303 if (anim_mode == ANIM_DEFAULT)
3304 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3306 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3310 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3312 if (anim_mode != ANIM_NONE)
3313 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3315 if (anim_mode == ANIM_DEFAULT)
3316 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3319 game.envelope_active = FALSE;
3321 if (action == ACTION_CLOSING)
3322 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3324 // SetDrawBackgroundMask(last_draw_background_mask);
3326 redraw_mask |= REDRAW_FIELD;
3330 if (action == ACTION_CLOSING &&
3331 game_status == GAME_MODE_PLAYING &&
3332 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3333 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3336 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3338 if (IS_MM_WALL(element))
3340 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3346 int graphic = el2preimg(element);
3348 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3349 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3354 void DrawLevel(int draw_background_mask)
3358 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3359 SetDrawBackgroundMask(draw_background_mask);
3363 for (x = BX1; x <= BX2; x++)
3364 for (y = BY1; y <= BY2; y++)
3365 DrawScreenField(x, y);
3367 redraw_mask |= REDRAW_FIELD;
3370 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3375 for (x = 0; x < size_x; x++)
3376 for (y = 0; y < size_y; y++)
3377 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3379 redraw_mask |= REDRAW_FIELD;
3382 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3386 for (x = 0; x < size_x; x++)
3387 for (y = 0; y < size_y; y++)
3388 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3390 redraw_mask |= REDRAW_FIELD;
3393 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3395 boolean show_level_border = (BorderElement != EL_EMPTY);
3396 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3397 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3398 int tile_size = preview.tile_size;
3399 int preview_width = preview.xsize * tile_size;
3400 int preview_height = preview.ysize * tile_size;
3401 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3402 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3403 int real_preview_width = real_preview_xsize * tile_size;
3404 int real_preview_height = real_preview_ysize * tile_size;
3405 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3406 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3409 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3412 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3414 dst_x += (preview_width - real_preview_width) / 2;
3415 dst_y += (preview_height - real_preview_height) / 2;
3417 for (x = 0; x < real_preview_xsize; x++)
3419 for (y = 0; y < real_preview_ysize; y++)
3421 int lx = from_x + x + (show_level_border ? -1 : 0);
3422 int ly = from_y + y + (show_level_border ? -1 : 0);
3423 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3424 getBorderElement(lx, ly));
3426 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3427 element, tile_size);
3431 redraw_mask |= REDRAW_FIELD;
3434 #define MICROLABEL_EMPTY 0
3435 #define MICROLABEL_LEVEL_NAME 1
3436 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3437 #define MICROLABEL_LEVEL_AUTHOR 3
3438 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3439 #define MICROLABEL_IMPORTED_FROM 5
3440 #define MICROLABEL_IMPORTED_BY_HEAD 6
3441 #define MICROLABEL_IMPORTED_BY 7
3443 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3445 int max_text_width = SXSIZE;
3446 int font_width = getFontWidth(font_nr);
3448 if (pos->align == ALIGN_CENTER)
3449 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3450 else if (pos->align == ALIGN_RIGHT)
3451 max_text_width = pos->x;
3453 max_text_width = SXSIZE - pos->x;
3455 return max_text_width / font_width;
3458 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3460 char label_text[MAX_OUTPUT_LINESIZE + 1];
3461 int max_len_label_text;
3462 int font_nr = pos->font;
3465 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3468 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3469 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3470 mode == MICROLABEL_IMPORTED_BY_HEAD)
3471 font_nr = pos->font_alt;
3473 max_len_label_text = getMaxTextLength(pos, font_nr);
3475 if (pos->size != -1)
3476 max_len_label_text = pos->size;
3478 for (i = 0; i < max_len_label_text; i++)
3479 label_text[i] = ' ';
3480 label_text[max_len_label_text] = '\0';
3482 if (strlen(label_text) > 0)
3483 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3486 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3487 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3488 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3489 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3490 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3491 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3492 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3493 max_len_label_text);
3494 label_text[max_len_label_text] = '\0';
3496 if (strlen(label_text) > 0)
3497 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3499 redraw_mask |= REDRAW_FIELD;
3502 static void DrawPreviewLevelLabel(int mode)
3504 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3507 static void DrawPreviewLevelInfo(int mode)
3509 if (mode == MICROLABEL_LEVEL_NAME)
3510 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3511 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3512 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3515 static void DrawPreviewLevelExt(boolean restart)
3517 static unsigned int scroll_delay = 0;
3518 static unsigned int label_delay = 0;
3519 static int from_x, from_y, scroll_direction;
3520 static int label_state, label_counter;
3521 unsigned int scroll_delay_value = preview.step_delay;
3522 boolean show_level_border = (BorderElement != EL_EMPTY);
3523 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3524 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3531 if (preview.anim_mode == ANIM_CENTERED)
3533 if (level_xsize > preview.xsize)
3534 from_x = (level_xsize - preview.xsize) / 2;
3535 if (level_ysize > preview.ysize)
3536 from_y = (level_ysize - preview.ysize) / 2;
3539 from_x += preview.xoffset;
3540 from_y += preview.yoffset;
3542 scroll_direction = MV_RIGHT;
3546 DrawPreviewLevelPlayfield(from_x, from_y);
3547 DrawPreviewLevelLabel(label_state);
3549 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3550 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3552 // initialize delay counters
3553 ResetDelayCounter(&scroll_delay);
3554 ResetDelayCounter(&label_delay);
3556 if (leveldir_current->name)
3558 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3559 char label_text[MAX_OUTPUT_LINESIZE + 1];
3560 int font_nr = pos->font;
3561 int max_len_label_text = getMaxTextLength(pos, font_nr);
3563 if (pos->size != -1)
3564 max_len_label_text = pos->size;
3566 strncpy(label_text, leveldir_current->name, max_len_label_text);
3567 label_text[max_len_label_text] = '\0';
3569 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3570 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3576 // scroll preview level, if needed
3577 if (preview.anim_mode != ANIM_NONE &&
3578 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3579 DelayReached(&scroll_delay, scroll_delay_value))
3581 switch (scroll_direction)
3586 from_x -= preview.step_offset;
3587 from_x = (from_x < 0 ? 0 : from_x);
3590 scroll_direction = MV_UP;
3594 if (from_x < level_xsize - preview.xsize)
3596 from_x += preview.step_offset;
3597 from_x = (from_x > level_xsize - preview.xsize ?
3598 level_xsize - preview.xsize : from_x);
3601 scroll_direction = MV_DOWN;
3607 from_y -= preview.step_offset;
3608 from_y = (from_y < 0 ? 0 : from_y);
3611 scroll_direction = MV_RIGHT;
3615 if (from_y < level_ysize - preview.ysize)
3617 from_y += preview.step_offset;
3618 from_y = (from_y > level_ysize - preview.ysize ?
3619 level_ysize - preview.ysize : from_y);
3622 scroll_direction = MV_LEFT;
3629 DrawPreviewLevelPlayfield(from_x, from_y);
3632 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3633 // redraw micro level label, if needed
3634 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3635 !strEqual(level.author, ANONYMOUS_NAME) &&
3636 !strEqual(level.author, leveldir_current->name) &&
3637 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3639 int max_label_counter = 23;
3641 if (leveldir_current->imported_from != NULL &&
3642 strlen(leveldir_current->imported_from) > 0)
3643 max_label_counter += 14;
3644 if (leveldir_current->imported_by != NULL &&
3645 strlen(leveldir_current->imported_by) > 0)
3646 max_label_counter += 14;
3648 label_counter = (label_counter + 1) % max_label_counter;
3649 label_state = (label_counter >= 0 && label_counter <= 7 ?
3650 MICROLABEL_LEVEL_NAME :
3651 label_counter >= 9 && label_counter <= 12 ?
3652 MICROLABEL_LEVEL_AUTHOR_HEAD :
3653 label_counter >= 14 && label_counter <= 21 ?
3654 MICROLABEL_LEVEL_AUTHOR :
3655 label_counter >= 23 && label_counter <= 26 ?
3656 MICROLABEL_IMPORTED_FROM_HEAD :
3657 label_counter >= 28 && label_counter <= 35 ?
3658 MICROLABEL_IMPORTED_FROM :
3659 label_counter >= 37 && label_counter <= 40 ?
3660 MICROLABEL_IMPORTED_BY_HEAD :
3661 label_counter >= 42 && label_counter <= 49 ?
3662 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3664 if (leveldir_current->imported_from == NULL &&
3665 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3666 label_state == MICROLABEL_IMPORTED_FROM))
3667 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3668 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3670 DrawPreviewLevelLabel(label_state);
3674 void DrawPreviewPlayers(void)
3676 if (game_status != GAME_MODE_MAIN)
3679 // do not draw preview players if level preview redefined, but players aren't
3680 if (preview.redefined && !menu.main.preview_players.redefined)
3683 boolean player_found[MAX_PLAYERS];
3684 int num_players = 0;
3687 for (i = 0; i < MAX_PLAYERS; i++)
3688 player_found[i] = FALSE;
3690 // check which players can be found in the level (simple approach)
3691 for (x = 0; x < lev_fieldx; x++)
3693 for (y = 0; y < lev_fieldy; y++)
3695 int element = level.field[x][y];
3697 if (IS_PLAYER_ELEMENT(element))
3699 int player_nr = GET_PLAYER_NR(element);
3701 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3703 if (!player_found[player_nr])
3706 player_found[player_nr] = TRUE;
3711 struct TextPosInfo *pos = &menu.main.preview_players;
3712 int tile_size = pos->tile_size;
3713 int border_size = pos->border_size;
3714 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3715 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3716 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3717 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3718 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3719 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3720 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3721 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3722 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3723 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3724 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3725 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3727 // clear area in which the players will be drawn
3728 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3729 max_players_width, max_players_height);
3731 if (!network.enabled && !setup.team_mode)
3734 // only draw players if level is suited for team mode
3735 if (num_players < 2)
3738 // draw all players that were found in the level
3739 for (i = 0; i < MAX_PLAYERS; i++)
3741 if (player_found[i])
3743 int graphic = el2img(EL_PLAYER_1 + i);
3745 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3747 xpos += player_xoffset;
3748 ypos += player_yoffset;
3753 void DrawPreviewLevelInitial(void)
3755 DrawPreviewLevelExt(TRUE);
3756 DrawPreviewPlayers();
3759 void DrawPreviewLevelAnimation(void)
3761 DrawPreviewLevelExt(FALSE);
3764 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3765 int border_size, int font_nr)
3767 int graphic = el2img(EL_PLAYER_1 + player_nr);
3768 int font_height = getFontHeight(font_nr);
3769 int player_height = MAX(tile_size, font_height);
3770 int xoffset_text = tile_size + border_size;
3771 int yoffset_text = (player_height - font_height) / 2;
3772 int yoffset_graphic = (player_height - tile_size) / 2;
3773 char *player_name = getNetworkPlayerName(player_nr + 1);
3775 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3777 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3780 static void DrawNetworkPlayersExt(boolean force)
3782 if (game_status != GAME_MODE_MAIN)
3785 if (!network.connected && !force)
3788 // do not draw network players if level preview redefined, but players aren't
3789 if (preview.redefined && !menu.main.network_players.redefined)
3792 int num_players = 0;
3795 for (i = 0; i < MAX_PLAYERS; i++)
3796 if (stored_player[i].connected_network)
3799 struct TextPosInfo *pos = &menu.main.network_players;
3800 int tile_size = pos->tile_size;
3801 int border_size = pos->border_size;
3802 int xoffset_text = tile_size + border_size;
3803 int font_nr = pos->font;
3804 int font_width = getFontWidth(font_nr);
3805 int font_height = getFontHeight(font_nr);
3806 int player_height = MAX(tile_size, font_height);
3807 int player_yoffset = player_height + border_size;
3808 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3809 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3810 int all_players_height = num_players * player_yoffset - border_size;
3811 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3812 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3813 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3815 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3816 max_players_width, max_players_height);
3818 // first draw local network player ...
3819 for (i = 0; i < MAX_PLAYERS; i++)
3821 if (stored_player[i].connected_network &&
3822 stored_player[i].connected_locally)
3824 char *player_name = getNetworkPlayerName(i + 1);
3825 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3826 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3828 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3830 ypos += player_yoffset;
3834 // ... then draw all other network players
3835 for (i = 0; i < MAX_PLAYERS; i++)
3837 if (stored_player[i].connected_network &&
3838 !stored_player[i].connected_locally)
3840 char *player_name = getNetworkPlayerName(i + 1);
3841 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3842 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3844 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3846 ypos += player_yoffset;
3851 void DrawNetworkPlayers(void)
3853 DrawNetworkPlayersExt(FALSE);
3856 void ClearNetworkPlayers(void)
3858 DrawNetworkPlayersExt(TRUE);
3861 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3862 int graphic, int lx, int ly,
3865 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3867 if (mask_mode == USE_MASKING)
3868 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3870 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3873 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3874 int graphic, int sync_frame, int mask_mode)
3876 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3878 if (mask_mode == USE_MASKING)
3879 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3881 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3884 static void DrawGraphicAnimation(int x, int y, int graphic)
3886 int lx = LEVELX(x), ly = LEVELY(y);
3887 int mask_mode = NO_MASKING;
3889 if (!IN_SCR_FIELD(x, y))
3892 if (game.use_masked_elements)
3894 if (Tile[lx][ly] != EL_EMPTY)
3896 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3898 mask_mode = USE_MASKING;
3902 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3903 graphic, lx, ly, mask_mode);
3905 MarkTileDirty(x, y);
3908 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3910 int lx = LEVELX(x), ly = LEVELY(y);
3911 int mask_mode = NO_MASKING;
3913 if (!IN_SCR_FIELD(x, y))
3916 if (game.use_masked_elements)
3918 if (Tile[lx][ly] != EL_EMPTY)
3920 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3922 mask_mode = USE_MASKING;
3926 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3927 graphic, lx, ly, mask_mode);
3929 MarkTileDirty(x, y);
3932 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3934 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3937 void DrawLevelElementAnimation(int x, int y, int element)
3939 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3941 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3944 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3946 int sx = SCREENX(x), sy = SCREENY(y);
3948 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3951 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3954 DrawGraphicAnimation(sx, sy, graphic);
3957 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3958 DrawLevelFieldCrumbled(x, y);
3960 if (GFX_CRUMBLED(Tile[x][y]))
3961 DrawLevelFieldCrumbled(x, y);
3965 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3967 int sx = SCREENX(x), sy = SCREENY(y);
3970 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3973 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3975 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3978 DrawGraphicAnimation(sx, sy, graphic);
3980 if (GFX_CRUMBLED(element))
3981 DrawLevelFieldCrumbled(x, y);
3984 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3986 if (player->use_murphy)
3988 // this works only because currently only one player can be "murphy" ...
3989 static int last_horizontal_dir = MV_LEFT;
3990 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3992 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3993 last_horizontal_dir = move_dir;
3995 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3997 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3999 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4005 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4008 static boolean equalGraphics(int graphic1, int graphic2)
4010 struct GraphicInfo *g1 = &graphic_info[graphic1];
4011 struct GraphicInfo *g2 = &graphic_info[graphic2];
4013 return (g1->bitmap == g2->bitmap &&
4014 g1->src_x == g2->src_x &&
4015 g1->src_y == g2->src_y &&
4016 g1->anim_frames == g2->anim_frames &&
4017 g1->anim_delay == g2->anim_delay &&
4018 g1->anim_mode == g2->anim_mode);
4021 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4025 DRAW_PLAYER_STAGE_INIT = 0,
4026 DRAW_PLAYER_STAGE_LAST_FIELD,
4027 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4028 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4029 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4030 DRAW_PLAYER_STAGE_PLAYER,
4032 DRAW_PLAYER_STAGE_PLAYER,
4033 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4035 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4036 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4038 NUM_DRAW_PLAYER_STAGES
4041 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4043 static int static_last_player_graphic[MAX_PLAYERS];
4044 static int static_last_player_frame[MAX_PLAYERS];
4045 static boolean static_player_is_opaque[MAX_PLAYERS];
4046 static boolean draw_player[MAX_PLAYERS];
4047 int pnr = player->index_nr;
4049 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4051 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4052 static_last_player_frame[pnr] = player->Frame;
4053 static_player_is_opaque[pnr] = FALSE;
4055 draw_player[pnr] = TRUE;
4058 if (!draw_player[pnr])
4062 if (!IN_LEV_FIELD(player->jx, player->jy))
4064 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4065 Debug("draw:DrawPlayerExt", "This should never happen!");
4067 draw_player[pnr] = FALSE;
4073 int last_player_graphic = static_last_player_graphic[pnr];
4074 int last_player_frame = static_last_player_frame[pnr];
4075 boolean player_is_opaque = static_player_is_opaque[pnr];
4077 int jx = player->jx;
4078 int jy = player->jy;
4079 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4080 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4081 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4082 int last_jx = (player->is_moving ? jx - dx : jx);
4083 int last_jy = (player->is_moving ? jy - dy : jy);
4084 int next_jx = jx + dx;
4085 int next_jy = jy + dy;
4086 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4087 int sx = SCREENX(jx);
4088 int sy = SCREENY(jy);
4089 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4090 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4091 int element = Tile[jx][jy];
4092 int last_element = Tile[last_jx][last_jy];
4093 int action = (player->is_pushing ? ACTION_PUSHING :
4094 player->is_digging ? ACTION_DIGGING :
4095 player->is_collecting ? ACTION_COLLECTING :
4096 player->is_moving ? ACTION_MOVING :
4097 player->is_snapping ? ACTION_SNAPPING :
4098 player->is_dropping ? ACTION_DROPPING :
4099 player->is_waiting ? player->action_waiting :
4102 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4104 // ------------------------------------------------------------------------
4105 // initialize drawing the player
4106 // ------------------------------------------------------------------------
4108 draw_player[pnr] = FALSE;
4110 // GfxElement[][] is set to the element the player is digging or collecting;
4111 // remove also for off-screen player if the player is not moving anymore
4112 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4113 GfxElement[jx][jy] = EL_UNDEFINED;
4115 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4118 if (element == EL_EXPLOSION)
4121 InitPlayerGfxAnimation(player, action, move_dir);
4123 draw_player[pnr] = TRUE;
4125 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4127 // ------------------------------------------------------------------------
4128 // draw things in the field the player is leaving, if needed
4129 // ------------------------------------------------------------------------
4131 if (!IN_SCR_FIELD(sx, sy))
4132 draw_player[pnr] = FALSE;
4134 if (!player->is_moving)
4137 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4139 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4141 if (last_element == EL_DYNAMITE_ACTIVE ||
4142 last_element == EL_EM_DYNAMITE_ACTIVE ||
4143 last_element == EL_SP_DISK_RED_ACTIVE)
4144 DrawDynamite(last_jx, last_jy);
4146 DrawLevelFieldThruMask(last_jx, last_jy);
4148 else if (last_element == EL_DYNAMITE_ACTIVE ||
4149 last_element == EL_EM_DYNAMITE_ACTIVE ||
4150 last_element == EL_SP_DISK_RED_ACTIVE)
4151 DrawDynamite(last_jx, last_jy);
4153 DrawLevelField(last_jx, last_jy);
4155 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4156 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4158 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4160 // ------------------------------------------------------------------------
4161 // draw things behind the player, if needed
4162 // ------------------------------------------------------------------------
4166 DrawLevelElement(jx, jy, Back[jx][jy]);
4171 if (IS_ACTIVE_BOMB(element))
4173 DrawLevelElement(jx, jy, EL_EMPTY);
4178 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4180 int old_element = GfxElement[jx][jy];
4181 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4182 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4184 if (GFX_CRUMBLED(old_element))
4185 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4187 DrawScreenGraphic(sx, sy, old_graphic, frame);
4189 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4190 static_player_is_opaque[pnr] = TRUE;
4194 GfxElement[jx][jy] = EL_UNDEFINED;
4196 // make sure that pushed elements are drawn with correct frame rate
4197 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4199 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4200 GfxFrame[jx][jy] = player->StepFrame;
4202 DrawLevelField(jx, jy);
4205 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4207 // ------------------------------------------------------------------------
4208 // draw things the player is pushing, if needed
4209 // ------------------------------------------------------------------------
4211 if (!player->is_pushing || !player->is_moving)
4214 int gfx_frame = GfxFrame[jx][jy];
4216 if (!IS_MOVING(jx, jy)) // push movement already finished
4218 element = Tile[next_jx][next_jy];
4219 gfx_frame = GfxFrame[next_jx][next_jy];
4222 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4223 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4224 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4226 // draw background element under pushed element (like the Sokoban field)
4227 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4229 // this allows transparent pushing animation over non-black background
4232 DrawLevelElement(jx, jy, Back[jx][jy]);
4234 DrawLevelElement(jx, jy, EL_EMPTY);
4236 if (Back[next_jx][next_jy])
4237 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4239 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4241 else if (Back[next_jx][next_jy])
4242 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4244 int px = SCREENX(jx), py = SCREENY(jy);
4245 int pxx = (TILEX - ABS(sxx)) * dx;
4246 int pyy = (TILEY - ABS(syy)) * dy;
4249 // do not draw (EM style) pushing animation when pushing is finished
4250 // (two-tile animations usually do not contain start and end frame)
4251 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4252 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4254 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4256 // masked drawing is needed for EMC style (double) movement graphics
4257 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4258 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4261 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4263 // ------------------------------------------------------------------------
4264 // draw player himself
4265 // ------------------------------------------------------------------------
4267 int graphic = getPlayerGraphic(player, move_dir);
4269 // in the case of changed player action or direction, prevent the current
4270 // animation frame from being restarted for identical animations
4271 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4272 player->Frame = last_player_frame;
4274 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4276 if (player_is_opaque)
4277 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4279 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4281 if (SHIELD_ON(player))
4283 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4284 IMG_SHIELD_NORMAL_ACTIVE);
4285 frame = getGraphicAnimationFrame(graphic, -1);
4287 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4290 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4292 // ------------------------------------------------------------------------
4293 // draw things in front of player (active dynamite or dynabombs)
4294 // ------------------------------------------------------------------------
4296 if (IS_ACTIVE_BOMB(element))
4298 int graphic = el2img(element);
4299 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4301 if (game.emulation == EMU_SUPAPLEX)
4302 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4304 DrawGraphicThruMask(sx, sy, graphic, frame);
4307 if (player_is_moving && last_element == EL_EXPLOSION)
4309 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4310 GfxElement[last_jx][last_jy] : EL_EMPTY);
4311 int graphic = el_act2img(element, ACTION_EXPLODING);
4312 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4313 int phase = ExplodePhase[last_jx][last_jy] - 1;
4314 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4317 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4320 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4322 // ------------------------------------------------------------------------
4323 // draw elements the player is just walking/passing through/under
4324 // ------------------------------------------------------------------------
4326 if (player_is_moving)
4328 // handle the field the player is leaving ...
4329 if (IS_ACCESSIBLE_INSIDE(last_element))
4330 DrawLevelField(last_jx, last_jy);
4331 else if (IS_ACCESSIBLE_UNDER(last_element))
4332 DrawLevelFieldThruMask(last_jx, last_jy);
4335 // do not redraw accessible elements if the player is just pushing them
4336 if (!player_is_moving || !player->is_pushing)
4338 // ... and the field the player is entering
4339 if (IS_ACCESSIBLE_INSIDE(element))
4340 DrawLevelField(jx, jy);
4341 else if (IS_ACCESSIBLE_UNDER(element))
4342 DrawLevelFieldThruMask(jx, jy);
4345 MarkTileDirty(sx, sy);
4349 void DrawPlayer(struct PlayerInfo *player)
4353 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4354 DrawPlayerExt(player, i);
4357 void DrawAllPlayers(void)
4361 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4362 for (j = 0; j < MAX_PLAYERS; j++)
4363 if (stored_player[j].active)
4364 DrawPlayerExt(&stored_player[j], i);
4367 void DrawPlayerField(int x, int y)
4369 if (!IS_PLAYER(x, y))
4372 DrawPlayer(PLAYERINFO(x, y));
4375 // ----------------------------------------------------------------------------
4377 void WaitForEventToContinue(void)
4379 boolean first_wait = TRUE;
4380 boolean still_wait = TRUE;
4382 if (program.headless)
4385 // simulate releasing mouse button over last gadget, if still pressed
4387 HandleGadgets(-1, -1, 0);
4389 button_status = MB_RELEASED;
4392 ClearPlayerAction();
4398 if (NextValidEvent(&event))
4402 case EVENT_BUTTONPRESS:
4403 case EVENT_FINGERPRESS:
4407 case EVENT_BUTTONRELEASE:
4408 case EVENT_FINGERRELEASE:
4409 still_wait = first_wait;
4412 case EVENT_KEYPRESS:
4413 case SDL_CONTROLLERBUTTONDOWN:
4414 case SDL_JOYBUTTONDOWN:
4419 HandleOtherEvents(&event);
4423 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4428 if (!PendingEvent())
4433 #define MAX_REQUEST_LINES 13
4434 #define MAX_REQUEST_LINE_FONT1_LEN 7
4435 #define MAX_REQUEST_LINE_FONT2_LEN 10
4437 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4439 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4441 int draw_buffer_last = GetDrawtoField();
4442 int width = request.width;
4443 int height = request.height;
4447 // when showing request dialog after game ended, deactivate game panel
4448 if (game_just_ended)
4449 game.panel.active = FALSE;
4451 game.request_active = TRUE;
4453 setRequestPosition(&sx, &sy, FALSE);
4455 button_status = MB_RELEASED;
4457 request_gadget_id = -1;
4462 boolean event_handled = FALSE;
4464 if (game_just_ended)
4466 SetDrawtoField(draw_buffer_game);
4468 HandleGameActions();
4470 SetDrawtoField(DRAW_TO_BACKBUFFER);
4472 if (global.use_envelope_request)
4474 // copy current state of request area to middle of playfield area
4475 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4483 while (NextValidEvent(&event))
4485 event_handled = TRUE;
4489 case EVENT_BUTTONPRESS:
4490 case EVENT_BUTTONRELEASE:
4491 case EVENT_MOTIONNOTIFY:
4495 if (event.type == EVENT_MOTIONNOTIFY)
4500 motion_status = TRUE;
4501 mx = ((MotionEvent *) &event)->x;
4502 my = ((MotionEvent *) &event)->y;
4506 motion_status = FALSE;
4507 mx = ((ButtonEvent *) &event)->x;
4508 my = ((ButtonEvent *) &event)->y;
4509 if (event.type == EVENT_BUTTONPRESS)
4510 button_status = ((ButtonEvent *) &event)->button;
4512 button_status = MB_RELEASED;
4515 // this sets 'request_gadget_id'
4516 HandleGadgets(mx, my, button_status);
4518 switch (request_gadget_id)
4520 case TOOL_CTRL_ID_YES:
4521 case TOOL_CTRL_ID_TOUCH_YES:
4524 case TOOL_CTRL_ID_NO:
4525 case TOOL_CTRL_ID_TOUCH_NO:
4528 case TOOL_CTRL_ID_CONFIRM:
4529 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4530 result = TRUE | FALSE;
4533 case TOOL_CTRL_ID_PLAYER_1:
4536 case TOOL_CTRL_ID_PLAYER_2:
4539 case TOOL_CTRL_ID_PLAYER_3:
4542 case TOOL_CTRL_ID_PLAYER_4:
4547 // only check clickable animations if no request gadget clicked
4548 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4555 case SDL_WINDOWEVENT:
4556 HandleWindowEvent((WindowEvent *) &event);
4559 case SDL_APP_WILLENTERBACKGROUND:
4560 case SDL_APP_DIDENTERBACKGROUND:
4561 case SDL_APP_WILLENTERFOREGROUND:
4562 case SDL_APP_DIDENTERFOREGROUND:
4563 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4566 case EVENT_KEYPRESS:
4568 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4573 if (req_state & REQ_CONFIRM)
4582 #if defined(KSYM_Rewind)
4583 case KSYM_Rewind: // for Amazon Fire TV remote
4592 #if defined(KSYM_FastForward)
4593 case KSYM_FastForward: // for Amazon Fire TV remote
4599 HandleKeysDebug(key, KEY_PRESSED);
4603 if (req_state & REQ_PLAYER)
4605 int old_player_nr = setup.network_player_nr;
4608 result = old_player_nr + 1;
4613 result = old_player_nr + 1;
4644 case EVENT_FINGERRELEASE:
4645 case EVENT_KEYRELEASE:
4646 ClearPlayerAction();
4649 case SDL_CONTROLLERBUTTONDOWN:
4650 switch (event.cbutton.button)
4652 case SDL_CONTROLLER_BUTTON_A:
4653 case SDL_CONTROLLER_BUTTON_X:
4654 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4655 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4659 case SDL_CONTROLLER_BUTTON_B:
4660 case SDL_CONTROLLER_BUTTON_Y:
4661 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4662 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4663 case SDL_CONTROLLER_BUTTON_BACK:
4668 if (req_state & REQ_PLAYER)
4670 int old_player_nr = setup.network_player_nr;
4673 result = old_player_nr + 1;
4675 switch (event.cbutton.button)
4677 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4678 case SDL_CONTROLLER_BUTTON_Y:
4682 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4683 case SDL_CONTROLLER_BUTTON_B:
4687 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4688 case SDL_CONTROLLER_BUTTON_A:
4692 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4693 case SDL_CONTROLLER_BUTTON_X:
4704 case SDL_CONTROLLERBUTTONUP:
4705 HandleJoystickEvent(&event);
4706 ClearPlayerAction();
4710 HandleOtherEvents(&event);
4715 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4717 int joy = AnyJoystick();
4719 if (joy & JOY_BUTTON_1)
4721 else if (joy & JOY_BUTTON_2)
4724 else if (AnyJoystick())
4726 int joy = AnyJoystick();
4728 if (req_state & REQ_PLAYER)
4732 else if (joy & JOY_RIGHT)
4734 else if (joy & JOY_DOWN)
4736 else if (joy & JOY_LEFT)
4743 if (game_just_ended)
4745 if (global.use_envelope_request)
4747 // copy back current state of pressed buttons inside request area
4748 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4752 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4758 SetDrawtoField(draw_buffer_last);
4760 game.request_active = FALSE;
4765 static boolean RequestDoor(char *text, unsigned int req_state)
4767 int draw_buffer_last = GetDrawtoField();
4768 unsigned int old_door_state;
4769 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4770 int font_nr = FONT_TEXT_2;
4775 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4777 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4778 font_nr = FONT_TEXT_1;
4781 if (game_status == GAME_MODE_PLAYING)
4782 BlitScreenToBitmap(backbuffer);
4784 // disable deactivated drawing when quick-loading level tape recording
4785 if (tape.playing && tape.deactivate_display)
4786 TapeDeactivateDisplayOff(TRUE);
4788 SetMouseCursor(CURSOR_DEFAULT);
4790 // pause network game while waiting for request to answer
4791 if (network.enabled &&
4792 game_status == GAME_MODE_PLAYING &&
4793 !game.all_players_gone &&
4794 req_state & REQUEST_WAIT_FOR_INPUT)
4795 SendToServer_PausePlaying();
4797 old_door_state = GetDoorState();
4799 // simulate releasing mouse button over last gadget, if still pressed
4801 HandleGadgets(-1, -1, 0);
4805 // draw released gadget before proceeding
4808 if (old_door_state & DOOR_OPEN_1)
4810 CloseDoor(DOOR_CLOSE_1);
4812 // save old door content
4813 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4814 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4817 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4818 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4820 // clear door drawing field
4821 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4823 // force DOOR font inside door area
4824 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4826 // write text for request
4827 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4829 char text_line[max_request_line_len + 1];
4835 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4837 tc = *(text_ptr + tx);
4838 // if (!tc || tc == ' ')
4839 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4843 if ((tc == '?' || tc == '!') && tl == 0)
4853 strncpy(text_line, text_ptr, tl);
4856 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4857 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4858 text_line, font_nr);
4860 text_ptr += tl + (tc == ' ' ? 1 : 0);
4861 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4866 if (req_state & REQ_ASK)
4868 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4869 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4870 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4871 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4873 else if (req_state & REQ_CONFIRM)
4875 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4876 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4878 else if (req_state & REQ_PLAYER)
4880 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4881 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4882 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4883 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4886 // copy request gadgets to door backbuffer
4887 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4889 OpenDoor(DOOR_OPEN_1);
4891 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4893 if (game_status == GAME_MODE_PLAYING)
4895 SetPanelBackground();
4896 SetDrawBackgroundMask(REDRAW_DOOR_1);
4900 SetDrawBackgroundMask(REDRAW_FIELD);
4906 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4908 // ---------- handle request buttons ----------
4909 result = RequestHandleEvents(req_state, draw_buffer_last);
4913 if (!(req_state & REQ_STAY_OPEN))
4915 CloseDoor(DOOR_CLOSE_1);
4917 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4918 (req_state & REQ_REOPEN))
4919 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4924 if (game_status == GAME_MODE_PLAYING)
4926 SetPanelBackground();
4927 SetDrawBackgroundMask(REDRAW_DOOR_1);
4931 SetDrawBackgroundMask(REDRAW_FIELD);
4934 // continue network game after request
4935 if (network.enabled &&
4936 game_status == GAME_MODE_PLAYING &&
4937 !game.all_players_gone &&
4938 req_state & REQUEST_WAIT_FOR_INPUT)
4939 SendToServer_ContinuePlaying();
4941 // restore deactivated drawing when quick-loading level tape recording
4942 if (tape.playing && tape.deactivate_display)
4943 TapeDeactivateDisplayOn();
4948 static boolean RequestEnvelope(char *text, unsigned int req_state)
4950 int draw_buffer_last = GetDrawtoField();
4953 if (game_status == GAME_MODE_PLAYING)
4954 BlitScreenToBitmap(backbuffer);
4956 // disable deactivated drawing when quick-loading level tape recording
4957 if (tape.playing && tape.deactivate_display)
4958 TapeDeactivateDisplayOff(TRUE);
4960 SetMouseCursor(CURSOR_DEFAULT);
4962 // pause network game while waiting for request to answer
4963 if (network.enabled &&
4964 game_status == GAME_MODE_PLAYING &&
4965 !game.all_players_gone &&
4966 req_state & REQUEST_WAIT_FOR_INPUT)
4967 SendToServer_PausePlaying();
4969 // simulate releasing mouse button over last gadget, if still pressed
4971 HandleGadgets(-1, -1, 0);
4975 // (replace with setting corresponding request background)
4976 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4977 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4979 // clear door drawing field
4980 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4982 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4984 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4986 if (game_status == GAME_MODE_PLAYING)
4988 SetPanelBackground();
4989 SetDrawBackgroundMask(REDRAW_DOOR_1);
4993 SetDrawBackgroundMask(REDRAW_FIELD);
4999 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5001 // ---------- handle request buttons ----------
5002 result = RequestHandleEvents(req_state, draw_buffer_last);
5006 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5010 if (game_status == GAME_MODE_PLAYING)
5012 SetPanelBackground();
5013 SetDrawBackgroundMask(REDRAW_DOOR_1);
5017 SetDrawBackgroundMask(REDRAW_FIELD);
5020 // continue network game after request
5021 if (network.enabled &&
5022 game_status == GAME_MODE_PLAYING &&
5023 !game.all_players_gone &&
5024 req_state & REQUEST_WAIT_FOR_INPUT)
5025 SendToServer_ContinuePlaying();
5027 // restore deactivated drawing when quick-loading level tape recording
5028 if (tape.playing && tape.deactivate_display)
5029 TapeDeactivateDisplayOn();
5034 boolean Request(char *text, unsigned int req_state)
5036 boolean overlay_enabled = GetOverlayEnabled();
5039 game.request_active_or_moving = TRUE;
5041 SetOverlayEnabled(FALSE);
5043 if (global.use_envelope_request)
5044 result = RequestEnvelope(text, req_state);
5046 result = RequestDoor(text, req_state);
5048 SetOverlayEnabled(overlay_enabled);
5050 game.request_active_or_moving = FALSE;
5055 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5057 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5058 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5061 if (dpo1->sort_priority != dpo2->sort_priority)
5062 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5064 compare_result = dpo1->nr - dpo2->nr;
5066 return compare_result;
5069 void InitGraphicCompatibilityInfo_Doors(void)
5075 struct DoorInfo *door;
5079 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5080 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5082 { -1, -1, -1, NULL }
5084 struct Rect door_rect_list[] =
5086 { DX, DY, DXSIZE, DYSIZE },
5087 { VX, VY, VXSIZE, VYSIZE }
5091 for (i = 0; doors[i].door_token != -1; i++)
5093 int door_token = doors[i].door_token;
5094 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5095 int part_1 = doors[i].part_1;
5096 int part_8 = doors[i].part_8;
5097 int part_2 = part_1 + 1;
5098 int part_3 = part_1 + 2;
5099 struct DoorInfo *door = doors[i].door;
5100 struct Rect *door_rect = &door_rect_list[door_index];
5101 boolean door_gfx_redefined = FALSE;
5103 // check if any door part graphic definitions have been redefined
5105 for (j = 0; door_part_controls[j].door_token != -1; j++)
5107 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5108 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5110 if (dpc->door_token == door_token && fi->redefined)
5111 door_gfx_redefined = TRUE;
5114 // check for old-style door graphic/animation modifications
5116 if (!door_gfx_redefined)
5118 if (door->anim_mode & ANIM_STATIC_PANEL)
5120 door->panel.step_xoffset = 0;
5121 door->panel.step_yoffset = 0;
5124 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5126 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5127 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5128 int num_door_steps, num_panel_steps;
5130 // remove door part graphics other than the two default wings
5132 for (j = 0; door_part_controls[j].door_token != -1; j++)
5134 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5135 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5137 if (dpc->graphic >= part_3 &&
5138 dpc->graphic <= part_8)
5142 // set graphics and screen positions of the default wings
5144 g_part_1->width = door_rect->width;
5145 g_part_1->height = door_rect->height;
5146 g_part_2->width = door_rect->width;
5147 g_part_2->height = door_rect->height;
5148 g_part_2->src_x = door_rect->width;
5149 g_part_2->src_y = g_part_1->src_y;
5151 door->part_2.x = door->part_1.x;
5152 door->part_2.y = door->part_1.y;
5154 if (door->width != -1)
5156 g_part_1->width = door->width;
5157 g_part_2->width = door->width;
5159 // special treatment for graphics and screen position of right wing
5160 g_part_2->src_x += door_rect->width - door->width;
5161 door->part_2.x += door_rect->width - door->width;
5164 if (door->height != -1)
5166 g_part_1->height = door->height;
5167 g_part_2->height = door->height;
5169 // special treatment for graphics and screen position of bottom wing
5170 g_part_2->src_y += door_rect->height - door->height;
5171 door->part_2.y += door_rect->height - door->height;
5174 // set animation delays for the default wings and panels
5176 door->part_1.step_delay = door->step_delay;
5177 door->part_2.step_delay = door->step_delay;
5178 door->panel.step_delay = door->step_delay;
5180 // set animation draw order for the default wings
5182 door->part_1.sort_priority = 2; // draw left wing over ...
5183 door->part_2.sort_priority = 1; // ... right wing
5185 // set animation draw offset for the default wings
5187 if (door->anim_mode & ANIM_HORIZONTAL)
5189 door->part_1.step_xoffset = door->step_offset;
5190 door->part_1.step_yoffset = 0;
5191 door->part_2.step_xoffset = door->step_offset * -1;
5192 door->part_2.step_yoffset = 0;
5194 num_door_steps = g_part_1->width / door->step_offset;
5196 else // ANIM_VERTICAL
5198 door->part_1.step_xoffset = 0;
5199 door->part_1.step_yoffset = door->step_offset;
5200 door->part_2.step_xoffset = 0;
5201 door->part_2.step_yoffset = door->step_offset * -1;
5203 num_door_steps = g_part_1->height / door->step_offset;
5206 // set animation draw offset for the default panels
5208 if (door->step_offset > 1)
5210 num_panel_steps = 2 * door_rect->height / door->step_offset;
5211 door->panel.start_step = num_panel_steps - num_door_steps;
5212 door->panel.start_step_closing = door->panel.start_step;
5216 num_panel_steps = door_rect->height / door->step_offset;
5217 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5218 door->panel.start_step_closing = door->panel.start_step;
5219 door->panel.step_delay *= 2;
5226 void InitDoors(void)
5230 for (i = 0; door_part_controls[i].door_token != -1; i++)
5232 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5233 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5235 // initialize "start_step_opening" and "start_step_closing", if needed
5236 if (dpc->pos->start_step_opening == 0 &&
5237 dpc->pos->start_step_closing == 0)
5239 // dpc->pos->start_step_opening = dpc->pos->start_step;
5240 dpc->pos->start_step_closing = dpc->pos->start_step;
5243 // fill structure for door part draw order (sorted below)
5245 dpo->sort_priority = dpc->pos->sort_priority;
5248 // sort door part controls according to sort_priority and graphic number
5249 qsort(door_part_order, MAX_DOOR_PARTS,
5250 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5253 unsigned int OpenDoor(unsigned int door_state)
5255 if (door_state & DOOR_COPY_BACK)
5257 if (door_state & DOOR_OPEN_1)
5258 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5259 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5261 if (door_state & DOOR_OPEN_2)
5262 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5263 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5265 door_state &= ~DOOR_COPY_BACK;
5268 return MoveDoor(door_state);
5271 unsigned int CloseDoor(unsigned int door_state)
5273 unsigned int old_door_state = GetDoorState();
5275 if (!(door_state & DOOR_NO_COPY_BACK))
5277 if (old_door_state & DOOR_OPEN_1)
5278 BlitBitmap(backbuffer, bitmap_db_door_1,
5279 DX, DY, DXSIZE, DYSIZE, 0, 0);
5281 if (old_door_state & DOOR_OPEN_2)
5282 BlitBitmap(backbuffer, bitmap_db_door_2,
5283 VX, VY, VXSIZE, VYSIZE, 0, 0);
5285 door_state &= ~DOOR_NO_COPY_BACK;
5288 return MoveDoor(door_state);
5291 unsigned int GetDoorState(void)
5293 return MoveDoor(DOOR_GET_STATE);
5296 unsigned int SetDoorState(unsigned int door_state)
5298 return MoveDoor(door_state | DOOR_SET_STATE);
5301 static int euclid(int a, int b)
5303 return (b ? euclid(b, a % b) : a);
5306 unsigned int MoveDoor(unsigned int door_state)
5308 struct Rect door_rect_list[] =
5310 { DX, DY, DXSIZE, DYSIZE },
5311 { VX, VY, VXSIZE, VYSIZE }
5313 static int door1 = DOOR_CLOSE_1;
5314 static int door2 = DOOR_CLOSE_2;
5315 unsigned int door_delay = 0;
5316 unsigned int door_delay_value;
5319 if (door_state == DOOR_GET_STATE)
5320 return (door1 | door2);
5322 if (door_state & DOOR_SET_STATE)
5324 if (door_state & DOOR_ACTION_1)
5325 door1 = door_state & DOOR_ACTION_1;
5326 if (door_state & DOOR_ACTION_2)
5327 door2 = door_state & DOOR_ACTION_2;
5329 return (door1 | door2);
5332 if (!(door_state & DOOR_FORCE_REDRAW))
5334 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5335 door_state &= ~DOOR_OPEN_1;
5336 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5337 door_state &= ~DOOR_CLOSE_1;
5338 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5339 door_state &= ~DOOR_OPEN_2;
5340 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5341 door_state &= ~DOOR_CLOSE_2;
5344 if (global.autoplay_leveldir)
5346 door_state |= DOOR_NO_DELAY;
5347 door_state &= ~DOOR_CLOSE_ALL;
5350 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5351 door_state |= DOOR_NO_DELAY;
5353 if (door_state & DOOR_ACTION)
5355 boolean door_panel_drawn[NUM_DOORS];
5356 boolean panel_has_doors[NUM_DOORS];
5357 boolean door_part_skip[MAX_DOOR_PARTS];
5358 boolean door_part_done[MAX_DOOR_PARTS];
5359 boolean door_part_done_all;
5360 int num_steps[MAX_DOOR_PARTS];
5361 int max_move_delay = 0; // delay for complete animations of all doors
5362 int max_step_delay = 0; // delay (ms) between two animation frames
5363 int num_move_steps = 0; // number of animation steps for all doors
5364 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5365 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5366 int current_move_delay = 0;
5370 for (i = 0; i < NUM_DOORS; i++)
5371 panel_has_doors[i] = FALSE;
5373 for (i = 0; i < MAX_DOOR_PARTS; i++)
5375 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5376 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5377 int door_token = dpc->door_token;
5379 door_part_done[i] = FALSE;
5380 door_part_skip[i] = (!(door_state & door_token) ||
5384 for (i = 0; i < MAX_DOOR_PARTS; i++)
5386 int nr = door_part_order[i].nr;
5387 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5388 struct DoorPartPosInfo *pos = dpc->pos;
5389 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5390 int door_token = dpc->door_token;
5391 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5392 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5393 int step_xoffset = ABS(pos->step_xoffset);
5394 int step_yoffset = ABS(pos->step_yoffset);
5395 int step_delay = pos->step_delay;
5396 int current_door_state = door_state & door_token;
5397 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5398 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5399 boolean part_opening = (is_panel ? door_closing : door_opening);
5400 int start_step = (part_opening ? pos->start_step_opening :
5401 pos->start_step_closing);
5402 float move_xsize = (step_xoffset ? g->width : 0);
5403 float move_ysize = (step_yoffset ? g->height : 0);
5404 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5405 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5406 int move_steps = (move_xsteps && move_ysteps ?
5407 MIN(move_xsteps, move_ysteps) :
5408 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5409 int move_delay = move_steps * step_delay;
5411 if (door_part_skip[nr])
5414 max_move_delay = MAX(max_move_delay, move_delay);
5415 max_step_delay = (max_step_delay == 0 ? step_delay :
5416 euclid(max_step_delay, step_delay));
5417 num_steps[nr] = move_steps;
5421 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5423 panel_has_doors[door_index] = TRUE;
5427 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5429 num_move_steps = max_move_delay / max_step_delay;
5430 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5432 door_delay_value = max_step_delay;
5434 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5436 start = num_move_steps - 1;
5440 // opening door sound has priority over simultaneously closing door
5441 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5443 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5445 if (door_state & DOOR_OPEN_1)
5446 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5447 if (door_state & DOOR_OPEN_2)
5448 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5450 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5452 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5454 if (door_state & DOOR_CLOSE_1)
5455 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5456 if (door_state & DOOR_CLOSE_2)
5457 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5461 for (k = start; k < num_move_steps; k++)
5463 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5465 door_part_done_all = TRUE;
5467 for (i = 0; i < NUM_DOORS; i++)
5468 door_panel_drawn[i] = FALSE;
5470 for (i = 0; i < MAX_DOOR_PARTS; i++)
5472 int nr = door_part_order[i].nr;
5473 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5474 struct DoorPartPosInfo *pos = dpc->pos;
5475 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5476 int door_token = dpc->door_token;
5477 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5478 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5479 boolean is_panel_and_door_has_closed = FALSE;
5480 struct Rect *door_rect = &door_rect_list[door_index];
5481 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5483 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5484 int current_door_state = door_state & door_token;
5485 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5486 boolean door_closing = !door_opening;
5487 boolean part_opening = (is_panel ? door_closing : door_opening);
5488 boolean part_closing = !part_opening;
5489 int start_step = (part_opening ? pos->start_step_opening :
5490 pos->start_step_closing);
5491 int step_delay = pos->step_delay;
5492 int step_factor = step_delay / max_step_delay;
5493 int k1 = (step_factor ? k / step_factor + 1 : k);
5494 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5495 int kk = MAX(0, k2);
5498 int src_x, src_y, src_xx, src_yy;
5499 int dst_x, dst_y, dst_xx, dst_yy;
5502 if (door_part_skip[nr])
5505 if (!(door_state & door_token))
5513 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5514 int kk_door = MAX(0, k2_door);
5515 int sync_frame = kk_door * door_delay_value;
5516 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5518 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5519 &g_src_x, &g_src_y);
5524 if (!door_panel_drawn[door_index])
5526 ClearRectangle(drawto, door_rect->x, door_rect->y,
5527 door_rect->width, door_rect->height);
5529 door_panel_drawn[door_index] = TRUE;
5532 // draw opening or closing door parts
5534 if (pos->step_xoffset < 0) // door part on right side
5537 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5540 if (dst_xx + width > door_rect->width)
5541 width = door_rect->width - dst_xx;
5543 else // door part on left side
5546 dst_xx = pos->x - kk * pos->step_xoffset;
5550 src_xx = ABS(dst_xx);
5554 width = g->width - src_xx;
5556 if (width > door_rect->width)
5557 width = door_rect->width;
5559 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5562 if (pos->step_yoffset < 0) // door part on bottom side
5565 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5568 if (dst_yy + height > door_rect->height)
5569 height = door_rect->height - dst_yy;
5571 else // door part on top side
5574 dst_yy = pos->y - kk * pos->step_yoffset;
5578 src_yy = ABS(dst_yy);
5582 height = g->height - src_yy;
5585 src_x = g_src_x + src_xx;
5586 src_y = g_src_y + src_yy;
5588 dst_x = door_rect->x + dst_xx;
5589 dst_y = door_rect->y + dst_yy;
5591 is_panel_and_door_has_closed =
5594 panel_has_doors[door_index] &&
5595 k >= num_move_steps_doors_only - 1);
5597 if (width >= 0 && width <= g->width &&
5598 height >= 0 && height <= g->height &&
5599 !is_panel_and_door_has_closed)
5601 if (is_panel || !pos->draw_masked)
5602 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5605 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5609 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5611 if ((part_opening && (width < 0 || height < 0)) ||
5612 (part_closing && (width >= g->width && height >= g->height)))
5613 door_part_done[nr] = TRUE;
5615 // continue door part animations, but not panel after door has closed
5616 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5617 door_part_done_all = FALSE;
5620 if (!(door_state & DOOR_NO_DELAY))
5624 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5626 current_move_delay += max_step_delay;
5628 // prevent OS (Windows) from complaining about program not responding
5632 if (door_part_done_all)
5636 if (!(door_state & DOOR_NO_DELAY))
5638 // wait for specified door action post delay
5639 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5640 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5641 else if (door_state & DOOR_ACTION_1)
5642 door_delay_value = door_1.post_delay;
5643 else if (door_state & DOOR_ACTION_2)
5644 door_delay_value = door_2.post_delay;
5646 while (!DelayReached(&door_delay, door_delay_value))
5651 if (door_state & DOOR_ACTION_1)
5652 door1 = door_state & DOOR_ACTION_1;
5653 if (door_state & DOOR_ACTION_2)
5654 door2 = door_state & DOOR_ACTION_2;
5656 // draw masked border over door area
5657 DrawMaskedBorder(REDRAW_DOOR_1);
5658 DrawMaskedBorder(REDRAW_DOOR_2);
5660 ClearAutoRepeatKeyEvents();
5662 return (door1 | door2);
5665 static boolean useSpecialEditorDoor(void)
5667 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5668 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5670 // do not draw special editor door if editor border defined or redefined
5671 if (graphic_info[graphic].bitmap != NULL || redefined)
5674 // do not draw special editor door if global border defined to be empty
5675 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5678 // do not draw special editor door if viewport definitions do not match
5682 EY + EYSIZE != VY + VYSIZE)
5688 void DrawSpecialEditorDoor(void)
5690 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5691 int top_border_width = gfx1->width;
5692 int top_border_height = gfx1->height;
5693 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5694 int ex = EX - outer_border;
5695 int ey = EY - outer_border;
5696 int vy = VY - outer_border;
5697 int exsize = EXSIZE + 2 * outer_border;
5699 if (!useSpecialEditorDoor())
5702 // draw bigger level editor toolbox window
5703 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5704 top_border_width, top_border_height, ex, ey - top_border_height);
5705 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5706 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5708 redraw_mask |= REDRAW_ALL;
5711 void UndrawSpecialEditorDoor(void)
5713 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5714 int top_border_width = gfx1->width;
5715 int top_border_height = gfx1->height;
5716 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5717 int ex = EX - outer_border;
5718 int ey = EY - outer_border;
5719 int ey_top = ey - top_border_height;
5720 int exsize = EXSIZE + 2 * outer_border;
5721 int eysize = EYSIZE + 2 * outer_border;
5723 if (!useSpecialEditorDoor())
5726 // draw normal tape recorder window
5727 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5729 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5730 ex, ey_top, top_border_width, top_border_height,
5732 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5733 ex, ey, exsize, eysize, ex, ey);
5737 // if screen background is set to "[NONE]", clear editor toolbox window
5738 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5739 ClearRectangle(drawto, ex, ey, exsize, eysize);
5742 redraw_mask |= REDRAW_ALL;
5746 // ---------- new tool button stuff -------------------------------------------
5751 struct TextPosInfo *pos;
5753 boolean is_touch_button;
5755 } toolbutton_info[NUM_TOOL_BUTTONS] =
5758 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5759 TOOL_CTRL_ID_YES, FALSE, "yes"
5762 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5763 TOOL_CTRL_ID_NO, FALSE, "no"
5766 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5767 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5770 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5771 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5774 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5775 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5778 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5779 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5782 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5783 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5786 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5787 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5790 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5791 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5794 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5795 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5799 void CreateToolButtons(void)
5803 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5805 int graphic = toolbutton_info[i].graphic;
5806 struct GraphicInfo *gfx = &graphic_info[graphic];
5807 struct TextPosInfo *pos = toolbutton_info[i].pos;
5808 struct GadgetInfo *gi;
5809 Bitmap *deco_bitmap = None;
5810 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5811 unsigned int event_mask = GD_EVENT_RELEASED;
5812 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5813 int base_x = (is_touch_button ? 0 : DX);
5814 int base_y = (is_touch_button ? 0 : DY);
5815 int gd_x = gfx->src_x;
5816 int gd_y = gfx->src_y;
5817 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5818 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5823 if (global.use_envelope_request && !is_touch_button)
5825 setRequestPosition(&base_x, &base_y, TRUE);
5827 // check if request buttons are outside of envelope and fix, if needed
5828 if (x < 0 || x + gfx->width > request.width ||
5829 y < 0 || y + gfx->height > request.height)
5831 if (id == TOOL_CTRL_ID_YES)
5834 y = request.height - 2 * request.border_size - gfx->height;
5836 else if (id == TOOL_CTRL_ID_NO)
5838 x = request.width - 2 * request.border_size - gfx->width;
5839 y = request.height - 2 * request.border_size - gfx->height;
5841 else if (id == TOOL_CTRL_ID_CONFIRM)
5843 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5844 y = request.height - 2 * request.border_size - gfx->height;
5846 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5848 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5850 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5851 y = request.height - 2 * request.border_size - gfx->height * 2;
5853 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5854 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5859 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5861 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5863 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5864 pos->size, &deco_bitmap, &deco_x, &deco_y);
5865 deco_xpos = (gfx->width - pos->size) / 2;
5866 deco_ypos = (gfx->height - pos->size) / 2;
5869 gi = CreateGadget(GDI_CUSTOM_ID, id,
5870 GDI_IMAGE_ID, graphic,
5871 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5874 GDI_WIDTH, gfx->width,
5875 GDI_HEIGHT, gfx->height,
5876 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5877 GDI_STATE, GD_BUTTON_UNPRESSED,
5878 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5879 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5880 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5881 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5882 GDI_DECORATION_SIZE, pos->size, pos->size,
5883 GDI_DECORATION_SHIFTING, 1, 1,
5884 GDI_DIRECT_DRAW, FALSE,
5885 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5886 GDI_EVENT_MASK, event_mask,
5887 GDI_CALLBACK_ACTION, HandleToolButtons,
5891 Fail("cannot create gadget");
5893 tool_gadget[id] = gi;
5897 void FreeToolButtons(void)
5901 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5902 FreeGadget(tool_gadget[i]);
5905 static void UnmapToolButtons(void)
5909 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5910 UnmapGadget(tool_gadget[i]);
5913 static void HandleToolButtons(struct GadgetInfo *gi)
5915 request_gadget_id = gi->custom_id;
5918 static struct Mapping_EM_to_RND_object
5921 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5922 boolean is_backside; // backside of moving element
5928 em_object_mapping_list[GAME_TILE_MAX + 1] =
5931 Zborder, FALSE, FALSE,
5935 Zplayer, FALSE, FALSE,
5944 Ztank, FALSE, FALSE,
5948 Zeater, FALSE, FALSE,
5952 Zdynamite, FALSE, FALSE,
5956 Zboom, FALSE, FALSE,
5961 Xchain, FALSE, FALSE,
5962 EL_DEFAULT, ACTION_EXPLODING, -1
5965 Xboom_bug, FALSE, FALSE,
5966 EL_BUG, ACTION_EXPLODING, -1
5969 Xboom_tank, FALSE, FALSE,
5970 EL_SPACESHIP, ACTION_EXPLODING, -1
5973 Xboom_android, FALSE, FALSE,
5974 EL_EMC_ANDROID, ACTION_OTHER, -1
5977 Xboom_1, FALSE, FALSE,
5978 EL_DEFAULT, ACTION_EXPLODING, -1
5981 Xboom_2, FALSE, FALSE,
5982 EL_DEFAULT, ACTION_EXPLODING, -1
5986 Xblank, TRUE, FALSE,
5991 Xsplash_e, FALSE, FALSE,
5992 EL_ACID_SPLASH_RIGHT, -1, -1
5995 Xsplash_w, FALSE, FALSE,
5996 EL_ACID_SPLASH_LEFT, -1, -1
6000 Xplant, TRUE, FALSE,
6001 EL_EMC_PLANT, -1, -1
6004 Yplant, FALSE, FALSE,
6005 EL_EMC_PLANT, -1, -1
6009 Xacid_1, TRUE, FALSE,
6013 Xacid_2, FALSE, FALSE,
6017 Xacid_3, FALSE, FALSE,
6021 Xacid_4, FALSE, FALSE,
6025 Xacid_5, FALSE, FALSE,
6029 Xacid_6, FALSE, FALSE,
6033 Xacid_7, FALSE, FALSE,
6037 Xacid_8, FALSE, FALSE,
6042 Xfake_acid_1, TRUE, FALSE,
6043 EL_EMC_FAKE_ACID, -1, -1
6046 Xfake_acid_2, FALSE, FALSE,
6047 EL_EMC_FAKE_ACID, -1, -1
6050 Xfake_acid_3, FALSE, FALSE,
6051 EL_EMC_FAKE_ACID, -1, -1
6054 Xfake_acid_4, FALSE, FALSE,
6055 EL_EMC_FAKE_ACID, -1, -1
6058 Xfake_acid_5, FALSE, FALSE,
6059 EL_EMC_FAKE_ACID, -1, -1
6062 Xfake_acid_6, FALSE, FALSE,
6063 EL_EMC_FAKE_ACID, -1, -1
6066 Xfake_acid_7, FALSE, FALSE,
6067 EL_EMC_FAKE_ACID, -1, -1
6070 Xfake_acid_8, FALSE, FALSE,
6071 EL_EMC_FAKE_ACID, -1, -1
6075 Xfake_acid_1_player, FALSE, FALSE,
6076 EL_EMC_FAKE_ACID, -1, -1
6079 Xfake_acid_2_player, FALSE, FALSE,
6080 EL_EMC_FAKE_ACID, -1, -1
6083 Xfake_acid_3_player, FALSE, FALSE,
6084 EL_EMC_FAKE_ACID, -1, -1
6087 Xfake_acid_4_player, FALSE, FALSE,
6088 EL_EMC_FAKE_ACID, -1, -1
6091 Xfake_acid_5_player, FALSE, FALSE,
6092 EL_EMC_FAKE_ACID, -1, -1
6095 Xfake_acid_6_player, FALSE, FALSE,
6096 EL_EMC_FAKE_ACID, -1, -1
6099 Xfake_acid_7_player, FALSE, FALSE,
6100 EL_EMC_FAKE_ACID, -1, -1
6103 Xfake_acid_8_player, FALSE, FALSE,
6104 EL_EMC_FAKE_ACID, -1, -1
6108 Xgrass, TRUE, FALSE,
6109 EL_EMC_GRASS, -1, -1
6112 Ygrass_nB, FALSE, FALSE,
6113 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6116 Ygrass_eB, FALSE, FALSE,
6117 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6120 Ygrass_sB, FALSE, FALSE,
6121 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6124 Ygrass_wB, FALSE, FALSE,
6125 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6133 Ydirt_nB, FALSE, FALSE,
6134 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6137 Ydirt_eB, FALSE, FALSE,
6138 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6141 Ydirt_sB, FALSE, FALSE,
6142 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6145 Ydirt_wB, FALSE, FALSE,
6146 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6150 Xandroid, TRUE, FALSE,
6151 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6154 Xandroid_1_n, FALSE, FALSE,
6155 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6158 Xandroid_2_n, FALSE, FALSE,
6159 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6162 Xandroid_1_e, FALSE, FALSE,
6163 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6166 Xandroid_2_e, FALSE, FALSE,
6167 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6170 Xandroid_1_w, FALSE, FALSE,
6171 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6174 Xandroid_2_w, FALSE, FALSE,
6175 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6178 Xandroid_1_s, FALSE, FALSE,
6179 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6182 Xandroid_2_s, FALSE, FALSE,
6183 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6186 Yandroid_n, FALSE, FALSE,
6187 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6190 Yandroid_nB, FALSE, TRUE,
6191 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6194 Yandroid_ne, FALSE, FALSE,
6195 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6198 Yandroid_neB, FALSE, TRUE,
6199 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6202 Yandroid_e, FALSE, FALSE,
6203 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6206 Yandroid_eB, FALSE, TRUE,
6207 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6210 Yandroid_se, FALSE, FALSE,
6211 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6214 Yandroid_seB, FALSE, TRUE,
6215 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6218 Yandroid_s, FALSE, FALSE,
6219 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6222 Yandroid_sB, FALSE, TRUE,
6223 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6226 Yandroid_sw, FALSE, FALSE,
6227 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6230 Yandroid_swB, FALSE, TRUE,
6231 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6234 Yandroid_w, FALSE, FALSE,
6235 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6238 Yandroid_wB, FALSE, TRUE,
6239 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6242 Yandroid_nw, FALSE, FALSE,
6243 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6246 Yandroid_nwB, FALSE, TRUE,
6247 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6251 Xeater_n, TRUE, FALSE,
6252 EL_YAMYAM_UP, -1, -1
6255 Xeater_e, TRUE, FALSE,
6256 EL_YAMYAM_RIGHT, -1, -1
6259 Xeater_w, TRUE, FALSE,
6260 EL_YAMYAM_LEFT, -1, -1
6263 Xeater_s, TRUE, FALSE,
6264 EL_YAMYAM_DOWN, -1, -1
6267 Yeater_n, FALSE, FALSE,
6268 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6271 Yeater_nB, FALSE, TRUE,
6272 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6275 Yeater_e, FALSE, FALSE,
6276 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6279 Yeater_eB, FALSE, TRUE,
6280 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6283 Yeater_s, FALSE, FALSE,
6284 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6287 Yeater_sB, FALSE, TRUE,
6288 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6291 Yeater_w, FALSE, FALSE,
6292 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6295 Yeater_wB, FALSE, TRUE,
6296 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6299 Yeater_stone, FALSE, FALSE,
6300 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6303 Yeater_spring, FALSE, FALSE,
6304 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6308 Xalien, TRUE, FALSE,
6312 Xalien_pause, FALSE, FALSE,
6316 Yalien_n, FALSE, FALSE,
6317 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6320 Yalien_nB, FALSE, TRUE,
6321 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6324 Yalien_e, FALSE, FALSE,
6325 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6328 Yalien_eB, FALSE, TRUE,
6329 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6332 Yalien_s, FALSE, FALSE,
6333 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6336 Yalien_sB, FALSE, TRUE,
6337 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6340 Yalien_w, FALSE, FALSE,
6341 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6344 Yalien_wB, FALSE, TRUE,
6345 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6348 Yalien_stone, FALSE, FALSE,
6349 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6352 Yalien_spring, FALSE, FALSE,
6353 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6357 Xbug_1_n, TRUE, FALSE,
6361 Xbug_1_e, TRUE, FALSE,
6362 EL_BUG_RIGHT, -1, -1
6365 Xbug_1_s, TRUE, FALSE,
6369 Xbug_1_w, TRUE, FALSE,
6373 Xbug_2_n, FALSE, FALSE,
6377 Xbug_2_e, FALSE, FALSE,
6378 EL_BUG_RIGHT, -1, -1
6381 Xbug_2_s, FALSE, FALSE,
6385 Xbug_2_w, FALSE, FALSE,
6389 Ybug_n, FALSE, FALSE,
6390 EL_BUG, ACTION_MOVING, MV_BIT_UP
6393 Ybug_nB, FALSE, TRUE,
6394 EL_BUG, ACTION_MOVING, MV_BIT_UP
6397 Ybug_e, FALSE, FALSE,
6398 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6401 Ybug_eB, FALSE, TRUE,
6402 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6405 Ybug_s, FALSE, FALSE,
6406 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6409 Ybug_sB, FALSE, TRUE,
6410 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6413 Ybug_w, FALSE, FALSE,
6414 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6417 Ybug_wB, FALSE, TRUE,
6418 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6421 Ybug_w_n, FALSE, FALSE,
6422 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6425 Ybug_n_e, FALSE, FALSE,
6426 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6429 Ybug_e_s, FALSE, FALSE,
6430 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6433 Ybug_s_w, FALSE, FALSE,
6434 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6437 Ybug_e_n, FALSE, FALSE,
6438 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6441 Ybug_s_e, FALSE, FALSE,
6442 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6445 Ybug_w_s, FALSE, FALSE,
6446 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6449 Ybug_n_w, FALSE, FALSE,
6450 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6453 Ybug_stone, FALSE, FALSE,
6454 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6457 Ybug_spring, FALSE, FALSE,
6458 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6462 Xtank_1_n, TRUE, FALSE,
6463 EL_SPACESHIP_UP, -1, -1
6466 Xtank_1_e, TRUE, FALSE,
6467 EL_SPACESHIP_RIGHT, -1, -1
6470 Xtank_1_s, TRUE, FALSE,
6471 EL_SPACESHIP_DOWN, -1, -1
6474 Xtank_1_w, TRUE, FALSE,
6475 EL_SPACESHIP_LEFT, -1, -1
6478 Xtank_2_n, FALSE, FALSE,
6479 EL_SPACESHIP_UP, -1, -1
6482 Xtank_2_e, FALSE, FALSE,
6483 EL_SPACESHIP_RIGHT, -1, -1
6486 Xtank_2_s, FALSE, FALSE,
6487 EL_SPACESHIP_DOWN, -1, -1
6490 Xtank_2_w, FALSE, FALSE,
6491 EL_SPACESHIP_LEFT, -1, -1
6494 Ytank_n, FALSE, FALSE,
6495 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6498 Ytank_nB, FALSE, TRUE,
6499 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6502 Ytank_e, FALSE, FALSE,
6503 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6506 Ytank_eB, FALSE, TRUE,
6507 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6510 Ytank_s, FALSE, FALSE,
6511 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6514 Ytank_sB, FALSE, TRUE,
6515 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6518 Ytank_w, FALSE, FALSE,
6519 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6522 Ytank_wB, FALSE, TRUE,
6523 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6526 Ytank_w_n, FALSE, FALSE,
6527 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6530 Ytank_n_e, FALSE, FALSE,
6531 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6534 Ytank_e_s, FALSE, FALSE,
6535 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6538 Ytank_s_w, FALSE, FALSE,
6539 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6542 Ytank_e_n, FALSE, FALSE,
6543 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6546 Ytank_s_e, FALSE, FALSE,
6547 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6550 Ytank_w_s, FALSE, FALSE,
6551 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6554 Ytank_n_w, FALSE, FALSE,
6555 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6558 Ytank_stone, FALSE, FALSE,
6559 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6562 Ytank_spring, FALSE, FALSE,
6563 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6567 Xemerald, TRUE, FALSE,
6571 Xemerald_pause, FALSE, FALSE,
6575 Xemerald_fall, FALSE, FALSE,
6579 Xemerald_shine, FALSE, FALSE,
6580 EL_EMERALD, ACTION_TWINKLING, -1
6583 Yemerald_s, FALSE, FALSE,
6584 EL_EMERALD, ACTION_FALLING, -1
6587 Yemerald_sB, FALSE, TRUE,
6588 EL_EMERALD, ACTION_FALLING, -1
6591 Yemerald_e, FALSE, FALSE,
6592 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6595 Yemerald_eB, FALSE, TRUE,
6596 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6599 Yemerald_w, FALSE, FALSE,
6600 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6603 Yemerald_wB, FALSE, TRUE,
6604 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6607 Yemerald_blank, FALSE, FALSE,
6608 EL_EMERALD, ACTION_COLLECTING, -1
6612 Xdiamond, TRUE, FALSE,
6616 Xdiamond_pause, FALSE, FALSE,
6620 Xdiamond_fall, FALSE, FALSE,
6624 Xdiamond_shine, FALSE, FALSE,
6625 EL_DIAMOND, ACTION_TWINKLING, -1
6628 Ydiamond_s, FALSE, FALSE,
6629 EL_DIAMOND, ACTION_FALLING, -1
6632 Ydiamond_sB, FALSE, TRUE,
6633 EL_DIAMOND, ACTION_FALLING, -1
6636 Ydiamond_e, FALSE, FALSE,
6637 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6640 Ydiamond_eB, FALSE, TRUE,
6641 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6644 Ydiamond_w, FALSE, FALSE,
6645 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6648 Ydiamond_wB, FALSE, TRUE,
6649 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6652 Ydiamond_blank, FALSE, FALSE,
6653 EL_DIAMOND, ACTION_COLLECTING, -1
6656 Ydiamond_stone, FALSE, FALSE,
6657 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6661 Xstone, TRUE, FALSE,
6665 Xstone_pause, FALSE, FALSE,
6669 Xstone_fall, FALSE, FALSE,
6673 Ystone_s, FALSE, FALSE,
6674 EL_ROCK, ACTION_FALLING, -1
6677 Ystone_sB, FALSE, TRUE,
6678 EL_ROCK, ACTION_FALLING, -1
6681 Ystone_e, FALSE, FALSE,
6682 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6685 Ystone_eB, FALSE, TRUE,
6686 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6689 Ystone_w, FALSE, FALSE,
6690 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6693 Ystone_wB, FALSE, TRUE,
6694 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6702 Xbomb_pause, FALSE, FALSE,
6706 Xbomb_fall, FALSE, FALSE,
6710 Ybomb_s, FALSE, FALSE,
6711 EL_BOMB, ACTION_FALLING, -1
6714 Ybomb_sB, FALSE, TRUE,
6715 EL_BOMB, ACTION_FALLING, -1
6718 Ybomb_e, FALSE, FALSE,
6719 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6722 Ybomb_eB, FALSE, TRUE,
6723 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6726 Ybomb_w, FALSE, FALSE,
6727 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6730 Ybomb_wB, FALSE, TRUE,
6731 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6734 Ybomb_blank, FALSE, FALSE,
6735 EL_BOMB, ACTION_ACTIVATING, -1
6743 Xnut_pause, FALSE, FALSE,
6747 Xnut_fall, FALSE, FALSE,
6751 Ynut_s, FALSE, FALSE,
6752 EL_NUT, ACTION_FALLING, -1
6755 Ynut_sB, FALSE, TRUE,
6756 EL_NUT, ACTION_FALLING, -1
6759 Ynut_e, FALSE, FALSE,
6760 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6763 Ynut_eB, FALSE, TRUE,
6764 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6767 Ynut_w, FALSE, FALSE,
6768 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6771 Ynut_wB, FALSE, TRUE,
6772 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6775 Ynut_stone, FALSE, FALSE,
6776 EL_NUT, ACTION_BREAKING, -1
6780 Xspring, TRUE, FALSE,
6784 Xspring_pause, FALSE, FALSE,
6788 Xspring_e, TRUE, FALSE,
6789 EL_SPRING_RIGHT, -1, -1
6792 Xspring_w, TRUE, FALSE,
6793 EL_SPRING_LEFT, -1, -1
6796 Xspring_fall, FALSE, FALSE,
6800 Yspring_s, FALSE, FALSE,
6801 EL_SPRING, ACTION_FALLING, -1
6804 Yspring_sB, FALSE, TRUE,
6805 EL_SPRING, ACTION_FALLING, -1
6808 Yspring_e, FALSE, FALSE,
6809 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6812 Yspring_eB, FALSE, TRUE,
6813 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6816 Yspring_w, FALSE, FALSE,
6817 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6820 Yspring_wB, FALSE, TRUE,
6821 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6824 Yspring_alien_e, FALSE, FALSE,
6825 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6828 Yspring_alien_eB, FALSE, TRUE,
6829 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6832 Yspring_alien_w, FALSE, FALSE,
6833 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6836 Yspring_alien_wB, FALSE, TRUE,
6837 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6841 Xpush_emerald_e, FALSE, FALSE,
6842 EL_EMERALD, -1, MV_BIT_RIGHT
6845 Xpush_emerald_w, FALSE, FALSE,
6846 EL_EMERALD, -1, MV_BIT_LEFT
6849 Xpush_diamond_e, FALSE, FALSE,
6850 EL_DIAMOND, -1, MV_BIT_RIGHT
6853 Xpush_diamond_w, FALSE, FALSE,
6854 EL_DIAMOND, -1, MV_BIT_LEFT
6857 Xpush_stone_e, FALSE, FALSE,
6858 EL_ROCK, -1, MV_BIT_RIGHT
6861 Xpush_stone_w, FALSE, FALSE,
6862 EL_ROCK, -1, MV_BIT_LEFT
6865 Xpush_bomb_e, FALSE, FALSE,
6866 EL_BOMB, -1, MV_BIT_RIGHT
6869 Xpush_bomb_w, FALSE, FALSE,
6870 EL_BOMB, -1, MV_BIT_LEFT
6873 Xpush_nut_e, FALSE, FALSE,
6874 EL_NUT, -1, MV_BIT_RIGHT
6877 Xpush_nut_w, FALSE, FALSE,
6878 EL_NUT, -1, MV_BIT_LEFT
6881 Xpush_spring_e, FALSE, FALSE,
6882 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6885 Xpush_spring_w, FALSE, FALSE,
6886 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6890 Xdynamite, TRUE, FALSE,
6891 EL_EM_DYNAMITE, -1, -1
6894 Ydynamite_blank, FALSE, FALSE,
6895 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6898 Xdynamite_1, TRUE, FALSE,
6899 EL_EM_DYNAMITE_ACTIVE, -1, -1
6902 Xdynamite_2, FALSE, FALSE,
6903 EL_EM_DYNAMITE_ACTIVE, -1, -1
6906 Xdynamite_3, FALSE, FALSE,
6907 EL_EM_DYNAMITE_ACTIVE, -1, -1
6910 Xdynamite_4, FALSE, FALSE,
6911 EL_EM_DYNAMITE_ACTIVE, -1, -1
6915 Xkey_1, TRUE, FALSE,
6919 Xkey_2, TRUE, FALSE,
6923 Xkey_3, TRUE, FALSE,
6927 Xkey_4, TRUE, FALSE,
6931 Xkey_5, TRUE, FALSE,
6932 EL_EMC_KEY_5, -1, -1
6935 Xkey_6, TRUE, FALSE,
6936 EL_EMC_KEY_6, -1, -1
6939 Xkey_7, TRUE, FALSE,
6940 EL_EMC_KEY_7, -1, -1
6943 Xkey_8, TRUE, FALSE,
6944 EL_EMC_KEY_8, -1, -1
6948 Xdoor_1, TRUE, FALSE,
6949 EL_EM_GATE_1, -1, -1
6952 Xdoor_2, TRUE, FALSE,
6953 EL_EM_GATE_2, -1, -1
6956 Xdoor_3, TRUE, FALSE,
6957 EL_EM_GATE_3, -1, -1
6960 Xdoor_4, TRUE, FALSE,
6961 EL_EM_GATE_4, -1, -1
6964 Xdoor_5, TRUE, FALSE,
6965 EL_EMC_GATE_5, -1, -1
6968 Xdoor_6, TRUE, FALSE,
6969 EL_EMC_GATE_6, -1, -1
6972 Xdoor_7, TRUE, FALSE,
6973 EL_EMC_GATE_7, -1, -1
6976 Xdoor_8, TRUE, FALSE,
6977 EL_EMC_GATE_8, -1, -1
6981 Xfake_door_1, TRUE, FALSE,
6982 EL_EM_GATE_1_GRAY, -1, -1
6985 Xfake_door_2, TRUE, FALSE,
6986 EL_EM_GATE_2_GRAY, -1, -1
6989 Xfake_door_3, TRUE, FALSE,
6990 EL_EM_GATE_3_GRAY, -1, -1
6993 Xfake_door_4, TRUE, FALSE,
6994 EL_EM_GATE_4_GRAY, -1, -1
6997 Xfake_door_5, TRUE, FALSE,
6998 EL_EMC_GATE_5_GRAY, -1, -1
7001 Xfake_door_6, TRUE, FALSE,
7002 EL_EMC_GATE_6_GRAY, -1, -1
7005 Xfake_door_7, TRUE, FALSE,
7006 EL_EMC_GATE_7_GRAY, -1, -1
7009 Xfake_door_8, TRUE, FALSE,
7010 EL_EMC_GATE_8_GRAY, -1, -1
7014 Xballoon, TRUE, FALSE,
7018 Yballoon_n, FALSE, FALSE,
7019 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7022 Yballoon_nB, FALSE, TRUE,
7023 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7026 Yballoon_e, FALSE, FALSE,
7027 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7030 Yballoon_eB, FALSE, TRUE,
7031 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7034 Yballoon_s, FALSE, FALSE,
7035 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7038 Yballoon_sB, FALSE, TRUE,
7039 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7042 Yballoon_w, FALSE, FALSE,
7043 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7046 Yballoon_wB, FALSE, TRUE,
7047 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7051 Xball_1, TRUE, FALSE,
7052 EL_EMC_MAGIC_BALL, -1, -1
7055 Yball_1, FALSE, FALSE,
7056 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7059 Xball_2, FALSE, FALSE,
7060 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7063 Yball_2, FALSE, FALSE,
7064 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7067 Yball_blank, FALSE, FALSE,
7068 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7072 Xamoeba_1, TRUE, FALSE,
7073 EL_AMOEBA_DRY, ACTION_OTHER, -1
7076 Xamoeba_2, FALSE, FALSE,
7077 EL_AMOEBA_DRY, ACTION_OTHER, -1
7080 Xamoeba_3, FALSE, FALSE,
7081 EL_AMOEBA_DRY, ACTION_OTHER, -1
7084 Xamoeba_4, FALSE, FALSE,
7085 EL_AMOEBA_DRY, ACTION_OTHER, -1
7088 Xamoeba_5, TRUE, FALSE,
7089 EL_AMOEBA_WET, ACTION_OTHER, -1
7092 Xamoeba_6, FALSE, FALSE,
7093 EL_AMOEBA_WET, ACTION_OTHER, -1
7096 Xamoeba_7, FALSE, FALSE,
7097 EL_AMOEBA_WET, ACTION_OTHER, -1
7100 Xamoeba_8, FALSE, FALSE,
7101 EL_AMOEBA_WET, ACTION_OTHER, -1
7106 EL_AMOEBA_DROP, ACTION_GROWING, -1
7109 Xdrip_fall, FALSE, FALSE,
7110 EL_AMOEBA_DROP, -1, -1
7113 Xdrip_stretch, FALSE, FALSE,
7114 EL_AMOEBA_DROP, ACTION_FALLING, -1
7117 Xdrip_stretchB, FALSE, TRUE,
7118 EL_AMOEBA_DROP, ACTION_FALLING, -1
7121 Ydrip_1_s, FALSE, FALSE,
7122 EL_AMOEBA_DROP, ACTION_FALLING, -1
7125 Ydrip_1_sB, FALSE, TRUE,
7126 EL_AMOEBA_DROP, ACTION_FALLING, -1
7129 Ydrip_2_s, FALSE, FALSE,
7130 EL_AMOEBA_DROP, ACTION_FALLING, -1
7133 Ydrip_2_sB, FALSE, TRUE,
7134 EL_AMOEBA_DROP, ACTION_FALLING, -1
7138 Xwonderwall, TRUE, FALSE,
7139 EL_MAGIC_WALL, -1, -1
7142 Ywonderwall, FALSE, FALSE,
7143 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7147 Xwheel, TRUE, FALSE,
7148 EL_ROBOT_WHEEL, -1, -1
7151 Ywheel, FALSE, FALSE,
7152 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7156 Xswitch, TRUE, FALSE,
7157 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7160 Yswitch, FALSE, FALSE,
7161 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7165 Xbumper, TRUE, FALSE,
7166 EL_EMC_SPRING_BUMPER, -1, -1
7169 Ybumper, FALSE, FALSE,
7170 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7174 Xacid_nw, TRUE, FALSE,
7175 EL_ACID_POOL_TOPLEFT, -1, -1
7178 Xacid_ne, TRUE, FALSE,
7179 EL_ACID_POOL_TOPRIGHT, -1, -1
7182 Xacid_sw, TRUE, FALSE,
7183 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7186 Xacid_s, TRUE, FALSE,
7187 EL_ACID_POOL_BOTTOM, -1, -1
7190 Xacid_se, TRUE, FALSE,
7191 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7195 Xfake_blank, TRUE, FALSE,
7196 EL_INVISIBLE_WALL, -1, -1
7199 Yfake_blank, FALSE, FALSE,
7200 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7204 Xfake_grass, TRUE, FALSE,
7205 EL_EMC_FAKE_GRASS, -1, -1
7208 Yfake_grass, FALSE, FALSE,
7209 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7213 Xfake_amoeba, TRUE, FALSE,
7214 EL_EMC_DRIPPER, -1, -1
7217 Yfake_amoeba, FALSE, FALSE,
7218 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7222 Xlenses, TRUE, FALSE,
7223 EL_EMC_LENSES, -1, -1
7227 Xmagnify, TRUE, FALSE,
7228 EL_EMC_MAGNIFIER, -1, -1
7233 EL_QUICKSAND_EMPTY, -1, -1
7236 Xsand_stone, TRUE, FALSE,
7237 EL_QUICKSAND_FULL, -1, -1
7240 Xsand_stonein_1, FALSE, TRUE,
7241 EL_ROCK, ACTION_FILLING, -1
7244 Xsand_stonein_2, FALSE, TRUE,
7245 EL_ROCK, ACTION_FILLING, -1
7248 Xsand_stonein_3, FALSE, TRUE,
7249 EL_ROCK, ACTION_FILLING, -1
7252 Xsand_stonein_4, FALSE, TRUE,
7253 EL_ROCK, ACTION_FILLING, -1
7256 Xsand_sandstone_1, FALSE, FALSE,
7257 EL_QUICKSAND_FILLING, -1, -1
7260 Xsand_sandstone_2, FALSE, FALSE,
7261 EL_QUICKSAND_FILLING, -1, -1
7264 Xsand_sandstone_3, FALSE, FALSE,
7265 EL_QUICKSAND_FILLING, -1, -1
7268 Xsand_sandstone_4, FALSE, FALSE,
7269 EL_QUICKSAND_FILLING, -1, -1
7272 Xsand_stonesand_1, FALSE, FALSE,
7273 EL_QUICKSAND_EMPTYING, -1, -1
7276 Xsand_stonesand_2, FALSE, FALSE,
7277 EL_QUICKSAND_EMPTYING, -1, -1
7280 Xsand_stonesand_3, FALSE, FALSE,
7281 EL_QUICKSAND_EMPTYING, -1, -1
7284 Xsand_stonesand_4, FALSE, FALSE,
7285 EL_QUICKSAND_EMPTYING, -1, -1
7288 Xsand_stoneout_1, FALSE, FALSE,
7289 EL_ROCK, ACTION_EMPTYING, -1
7292 Xsand_stoneout_2, FALSE, FALSE,
7293 EL_ROCK, ACTION_EMPTYING, -1
7296 Xsand_stonesand_quickout_1, FALSE, FALSE,
7297 EL_QUICKSAND_EMPTYING, -1, -1
7300 Xsand_stonesand_quickout_2, FALSE, FALSE,
7301 EL_QUICKSAND_EMPTYING, -1, -1
7305 Xslide_ns, TRUE, FALSE,
7306 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7309 Yslide_ns_blank, FALSE, FALSE,
7310 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7313 Xslide_ew, TRUE, FALSE,
7314 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7317 Yslide_ew_blank, FALSE, FALSE,
7318 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7322 Xwind_n, TRUE, FALSE,
7323 EL_BALLOON_SWITCH_UP, -1, -1
7326 Xwind_e, TRUE, FALSE,
7327 EL_BALLOON_SWITCH_RIGHT, -1, -1
7330 Xwind_s, TRUE, FALSE,
7331 EL_BALLOON_SWITCH_DOWN, -1, -1
7334 Xwind_w, TRUE, FALSE,
7335 EL_BALLOON_SWITCH_LEFT, -1, -1
7338 Xwind_any, TRUE, FALSE,
7339 EL_BALLOON_SWITCH_ANY, -1, -1
7342 Xwind_stop, TRUE, FALSE,
7343 EL_BALLOON_SWITCH_NONE, -1, -1
7348 EL_EM_EXIT_CLOSED, -1, -1
7351 Xexit_1, TRUE, FALSE,
7352 EL_EM_EXIT_OPEN, -1, -1
7355 Xexit_2, FALSE, FALSE,
7356 EL_EM_EXIT_OPEN, -1, -1
7359 Xexit_3, FALSE, FALSE,
7360 EL_EM_EXIT_OPEN, -1, -1
7364 Xpause, FALSE, FALSE,
7369 Xwall_1, TRUE, FALSE,
7373 Xwall_2, TRUE, FALSE,
7374 EL_EMC_WALL_14, -1, -1
7377 Xwall_3, TRUE, FALSE,
7378 EL_EMC_WALL_15, -1, -1
7381 Xwall_4, TRUE, FALSE,
7382 EL_EMC_WALL_16, -1, -1
7386 Xroundwall_1, TRUE, FALSE,
7387 EL_WALL_SLIPPERY, -1, -1
7390 Xroundwall_2, TRUE, FALSE,
7391 EL_EMC_WALL_SLIPPERY_2, -1, -1
7394 Xroundwall_3, TRUE, FALSE,
7395 EL_EMC_WALL_SLIPPERY_3, -1, -1
7398 Xroundwall_4, TRUE, FALSE,
7399 EL_EMC_WALL_SLIPPERY_4, -1, -1
7403 Xsteel_1, TRUE, FALSE,
7404 EL_STEELWALL, -1, -1
7407 Xsteel_2, TRUE, FALSE,
7408 EL_EMC_STEELWALL_2, -1, -1
7411 Xsteel_3, TRUE, FALSE,
7412 EL_EMC_STEELWALL_3, -1, -1
7415 Xsteel_4, TRUE, FALSE,
7416 EL_EMC_STEELWALL_4, -1, -1
7420 Xdecor_1, TRUE, FALSE,
7421 EL_EMC_WALL_8, -1, -1
7424 Xdecor_2, TRUE, FALSE,
7425 EL_EMC_WALL_6, -1, -1
7428 Xdecor_3, TRUE, FALSE,
7429 EL_EMC_WALL_4, -1, -1
7432 Xdecor_4, TRUE, FALSE,
7433 EL_EMC_WALL_7, -1, -1
7436 Xdecor_5, TRUE, FALSE,
7437 EL_EMC_WALL_5, -1, -1
7440 Xdecor_6, TRUE, FALSE,
7441 EL_EMC_WALL_9, -1, -1
7444 Xdecor_7, TRUE, FALSE,
7445 EL_EMC_WALL_10, -1, -1
7448 Xdecor_8, TRUE, FALSE,
7449 EL_EMC_WALL_1, -1, -1
7452 Xdecor_9, TRUE, FALSE,
7453 EL_EMC_WALL_2, -1, -1
7456 Xdecor_10, TRUE, FALSE,
7457 EL_EMC_WALL_3, -1, -1
7460 Xdecor_11, TRUE, FALSE,
7461 EL_EMC_WALL_11, -1, -1
7464 Xdecor_12, TRUE, FALSE,
7465 EL_EMC_WALL_12, -1, -1
7469 Xalpha_0, TRUE, FALSE,
7470 EL_CHAR('0'), -1, -1
7473 Xalpha_1, TRUE, FALSE,
7474 EL_CHAR('1'), -1, -1
7477 Xalpha_2, TRUE, FALSE,
7478 EL_CHAR('2'), -1, -1
7481 Xalpha_3, TRUE, FALSE,
7482 EL_CHAR('3'), -1, -1
7485 Xalpha_4, TRUE, FALSE,
7486 EL_CHAR('4'), -1, -1
7489 Xalpha_5, TRUE, FALSE,
7490 EL_CHAR('5'), -1, -1
7493 Xalpha_6, TRUE, FALSE,
7494 EL_CHAR('6'), -1, -1
7497 Xalpha_7, TRUE, FALSE,
7498 EL_CHAR('7'), -1, -1
7501 Xalpha_8, TRUE, FALSE,
7502 EL_CHAR('8'), -1, -1
7505 Xalpha_9, TRUE, FALSE,
7506 EL_CHAR('9'), -1, -1
7509 Xalpha_excla, TRUE, FALSE,
7510 EL_CHAR('!'), -1, -1
7513 Xalpha_apost, TRUE, FALSE,
7514 EL_CHAR('\''), -1, -1
7517 Xalpha_comma, TRUE, FALSE,
7518 EL_CHAR(','), -1, -1
7521 Xalpha_minus, TRUE, FALSE,
7522 EL_CHAR('-'), -1, -1
7525 Xalpha_perio, TRUE, FALSE,
7526 EL_CHAR('.'), -1, -1
7529 Xalpha_colon, TRUE, FALSE,
7530 EL_CHAR(':'), -1, -1
7533 Xalpha_quest, TRUE, FALSE,
7534 EL_CHAR('?'), -1, -1
7537 Xalpha_a, TRUE, FALSE,
7538 EL_CHAR('A'), -1, -1
7541 Xalpha_b, TRUE, FALSE,
7542 EL_CHAR('B'), -1, -1
7545 Xalpha_c, TRUE, FALSE,
7546 EL_CHAR('C'), -1, -1
7549 Xalpha_d, TRUE, FALSE,
7550 EL_CHAR('D'), -1, -1
7553 Xalpha_e, TRUE, FALSE,
7554 EL_CHAR('E'), -1, -1
7557 Xalpha_f, TRUE, FALSE,
7558 EL_CHAR('F'), -1, -1
7561 Xalpha_g, TRUE, FALSE,
7562 EL_CHAR('G'), -1, -1
7565 Xalpha_h, TRUE, FALSE,
7566 EL_CHAR('H'), -1, -1
7569 Xalpha_i, TRUE, FALSE,
7570 EL_CHAR('I'), -1, -1
7573 Xalpha_j, TRUE, FALSE,
7574 EL_CHAR('J'), -1, -1
7577 Xalpha_k, TRUE, FALSE,
7578 EL_CHAR('K'), -1, -1
7581 Xalpha_l, TRUE, FALSE,
7582 EL_CHAR('L'), -1, -1
7585 Xalpha_m, TRUE, FALSE,
7586 EL_CHAR('M'), -1, -1
7589 Xalpha_n, TRUE, FALSE,
7590 EL_CHAR('N'), -1, -1
7593 Xalpha_o, TRUE, FALSE,
7594 EL_CHAR('O'), -1, -1
7597 Xalpha_p, TRUE, FALSE,
7598 EL_CHAR('P'), -1, -1
7601 Xalpha_q, TRUE, FALSE,
7602 EL_CHAR('Q'), -1, -1
7605 Xalpha_r, TRUE, FALSE,
7606 EL_CHAR('R'), -1, -1
7609 Xalpha_s, TRUE, FALSE,
7610 EL_CHAR('S'), -1, -1
7613 Xalpha_t, TRUE, FALSE,
7614 EL_CHAR('T'), -1, -1
7617 Xalpha_u, TRUE, FALSE,
7618 EL_CHAR('U'), -1, -1
7621 Xalpha_v, TRUE, FALSE,
7622 EL_CHAR('V'), -1, -1
7625 Xalpha_w, TRUE, FALSE,
7626 EL_CHAR('W'), -1, -1
7629 Xalpha_x, TRUE, FALSE,
7630 EL_CHAR('X'), -1, -1
7633 Xalpha_y, TRUE, FALSE,
7634 EL_CHAR('Y'), -1, -1
7637 Xalpha_z, TRUE, FALSE,
7638 EL_CHAR('Z'), -1, -1
7641 Xalpha_arrow_e, TRUE, FALSE,
7642 EL_CHAR('>'), -1, -1
7645 Xalpha_arrow_w, TRUE, FALSE,
7646 EL_CHAR('<'), -1, -1
7649 Xalpha_copyr, TRUE, FALSE,
7650 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7654 Ykey_1_blank, FALSE, FALSE,
7655 EL_EM_KEY_1, ACTION_COLLECTING, -1
7658 Ykey_2_blank, FALSE, FALSE,
7659 EL_EM_KEY_2, ACTION_COLLECTING, -1
7662 Ykey_3_blank, FALSE, FALSE,
7663 EL_EM_KEY_3, ACTION_COLLECTING, -1
7666 Ykey_4_blank, FALSE, FALSE,
7667 EL_EM_KEY_4, ACTION_COLLECTING, -1
7670 Ykey_5_blank, FALSE, FALSE,
7671 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7674 Ykey_6_blank, FALSE, FALSE,
7675 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7678 Ykey_7_blank, FALSE, FALSE,
7679 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7682 Ykey_8_blank, FALSE, FALSE,
7683 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7686 Ylenses_blank, FALSE, FALSE,
7687 EL_EMC_LENSES, ACTION_COLLECTING, -1
7690 Ymagnify_blank, FALSE, FALSE,
7691 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7694 Ygrass_blank, FALSE, FALSE,
7695 EL_EMC_GRASS, ACTION_SNAPPING, -1
7698 Ydirt_blank, FALSE, FALSE,
7699 EL_SAND, ACTION_SNAPPING, -1
7708 static struct Mapping_EM_to_RND_player
7717 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7721 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7725 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7729 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7733 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7737 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7741 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7745 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7749 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7753 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7757 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7761 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7765 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7769 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7773 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7777 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7781 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7785 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7789 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7793 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7797 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7801 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7805 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7809 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7813 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7817 EL_PLAYER_1, ACTION_DEFAULT, -1,
7821 EL_PLAYER_2, ACTION_DEFAULT, -1,
7825 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7829 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7833 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7837 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7841 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7845 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7849 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7853 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7857 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7861 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7865 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7869 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7873 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7877 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7881 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7885 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7889 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7893 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7897 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7901 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7905 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7909 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7913 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7917 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7921 EL_PLAYER_3, ACTION_DEFAULT, -1,
7925 EL_PLAYER_4, ACTION_DEFAULT, -1,
7934 int map_element_RND_to_EM_cave(int element_rnd)
7936 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7937 static boolean mapping_initialized = FALSE;
7939 if (!mapping_initialized)
7943 // return "Xalpha_quest" for all undefined elements in mapping array
7944 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7945 mapping_RND_to_EM[i] = Xalpha_quest;
7947 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7948 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7949 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7950 em_object_mapping_list[i].element_em;
7952 mapping_initialized = TRUE;
7955 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7957 Warn("invalid RND level element %d", element_rnd);
7962 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7965 int map_element_EM_to_RND_cave(int element_em_cave)
7967 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7968 static boolean mapping_initialized = FALSE;
7970 if (!mapping_initialized)
7974 // return "EL_UNKNOWN" for all undefined elements in mapping array
7975 for (i = 0; i < GAME_TILE_MAX; i++)
7976 mapping_EM_to_RND[i] = EL_UNKNOWN;
7978 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7979 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7980 em_object_mapping_list[i].element_rnd;
7982 mapping_initialized = TRUE;
7985 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7987 Warn("invalid EM cave element %d", element_em_cave);
7992 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7995 int map_element_EM_to_RND_game(int element_em_game)
7997 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7998 static boolean mapping_initialized = FALSE;
8000 if (!mapping_initialized)
8004 // return "EL_UNKNOWN" for all undefined elements in mapping array
8005 for (i = 0; i < GAME_TILE_MAX; i++)
8006 mapping_EM_to_RND[i] = EL_UNKNOWN;
8008 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8009 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8010 em_object_mapping_list[i].element_rnd;
8012 mapping_initialized = TRUE;
8015 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8017 Warn("invalid EM game element %d", element_em_game);
8022 return mapping_EM_to_RND[element_em_game];
8025 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8027 struct LevelInfo_EM *level_em = level->native_em_level;
8028 struct CAVE *cav = level_em->cav;
8031 for (i = 0; i < GAME_TILE_MAX; i++)
8032 cav->android_array[i] = Cblank;
8034 for (i = 0; i < level->num_android_clone_elements; i++)
8036 int element_rnd = level->android_clone_element[i];
8037 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8039 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8040 if (em_object_mapping_list[j].element_rnd == element_rnd)
8041 cav->android_array[em_object_mapping_list[j].element_em] =
8046 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8048 struct LevelInfo_EM *level_em = level->native_em_level;
8049 struct CAVE *cav = level_em->cav;
8052 level->num_android_clone_elements = 0;
8054 for (i = 0; i < GAME_TILE_MAX; i++)
8056 int element_em_cave = cav->android_array[i];
8058 boolean element_found = FALSE;
8060 if (element_em_cave == Cblank)
8063 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8065 for (j = 0; j < level->num_android_clone_elements; j++)
8066 if (level->android_clone_element[j] == element_rnd)
8067 element_found = TRUE;
8071 level->android_clone_element[level->num_android_clone_elements++] =
8074 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8079 if (level->num_android_clone_elements == 0)
8081 level->num_android_clone_elements = 1;
8082 level->android_clone_element[0] = EL_EMPTY;
8086 int map_direction_RND_to_EM(int direction)
8088 return (direction == MV_UP ? 0 :
8089 direction == MV_RIGHT ? 1 :
8090 direction == MV_DOWN ? 2 :
8091 direction == MV_LEFT ? 3 :
8095 int map_direction_EM_to_RND(int direction)
8097 return (direction == 0 ? MV_UP :
8098 direction == 1 ? MV_RIGHT :
8099 direction == 2 ? MV_DOWN :
8100 direction == 3 ? MV_LEFT :
8104 int map_element_RND_to_SP(int element_rnd)
8106 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8108 if (element_rnd >= EL_SP_START &&
8109 element_rnd <= EL_SP_END)
8110 element_sp = element_rnd - EL_SP_START;
8111 else if (element_rnd == EL_EMPTY_SPACE)
8113 else if (element_rnd == EL_INVISIBLE_WALL)
8119 int map_element_SP_to_RND(int element_sp)
8121 int element_rnd = EL_UNKNOWN;
8123 if (element_sp >= 0x00 &&
8125 element_rnd = EL_SP_START + element_sp;
8126 else if (element_sp == 0x28)
8127 element_rnd = EL_INVISIBLE_WALL;
8132 int map_action_SP_to_RND(int action_sp)
8136 case actActive: return ACTION_ACTIVE;
8137 case actImpact: return ACTION_IMPACT;
8138 case actExploding: return ACTION_EXPLODING;
8139 case actDigging: return ACTION_DIGGING;
8140 case actSnapping: return ACTION_SNAPPING;
8141 case actCollecting: return ACTION_COLLECTING;
8142 case actPassing: return ACTION_PASSING;
8143 case actPushing: return ACTION_PUSHING;
8144 case actDropping: return ACTION_DROPPING;
8146 default: return ACTION_DEFAULT;
8150 int map_element_RND_to_MM(int element_rnd)
8152 return (element_rnd >= EL_MM_START_1 &&
8153 element_rnd <= EL_MM_END_1 ?
8154 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8156 element_rnd >= EL_MM_START_2 &&
8157 element_rnd <= EL_MM_END_2 ?
8158 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8160 element_rnd >= EL_CHAR_START &&
8161 element_rnd <= EL_CHAR_END ?
8162 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8164 element_rnd >= EL_MM_RUNTIME_START &&
8165 element_rnd <= EL_MM_RUNTIME_END ?
8166 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8168 element_rnd >= EL_MM_DUMMY_START &&
8169 element_rnd <= EL_MM_DUMMY_END ?
8170 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8172 EL_MM_EMPTY_NATIVE);
8175 int map_element_MM_to_RND(int element_mm)
8177 return (element_mm == EL_MM_EMPTY_NATIVE ||
8178 element_mm == EL_DF_EMPTY_NATIVE ?
8181 element_mm >= EL_MM_START_1_NATIVE &&
8182 element_mm <= EL_MM_END_1_NATIVE ?
8183 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8185 element_mm >= EL_MM_START_2_NATIVE &&
8186 element_mm <= EL_MM_END_2_NATIVE ?
8187 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8189 element_mm >= EL_MM_CHAR_START_NATIVE &&
8190 element_mm <= EL_MM_CHAR_END_NATIVE ?
8191 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8193 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8194 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8195 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8197 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8198 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8199 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8204 int map_action_MM_to_RND(int action_mm)
8206 // all MM actions are defined to exactly match their RND counterparts
8210 int map_sound_MM_to_RND(int sound_mm)
8214 case SND_MM_GAME_LEVELTIME_CHARGING:
8215 return SND_GAME_LEVELTIME_CHARGING;
8217 case SND_MM_GAME_HEALTH_CHARGING:
8218 return SND_GAME_HEALTH_CHARGING;
8221 return SND_UNDEFINED;
8225 int map_mm_wall_element(int element)
8227 return (element >= EL_MM_STEEL_WALL_START &&
8228 element <= EL_MM_STEEL_WALL_END ?
8231 element >= EL_MM_WOODEN_WALL_START &&
8232 element <= EL_MM_WOODEN_WALL_END ?
8235 element >= EL_MM_ICE_WALL_START &&
8236 element <= EL_MM_ICE_WALL_END ?
8239 element >= EL_MM_AMOEBA_WALL_START &&
8240 element <= EL_MM_AMOEBA_WALL_END ?
8243 element >= EL_DF_STEEL_WALL_START &&
8244 element <= EL_DF_STEEL_WALL_END ?
8247 element >= EL_DF_WOODEN_WALL_START &&
8248 element <= EL_DF_WOODEN_WALL_END ?
8254 int map_mm_wall_element_editor(int element)
8258 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8259 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8260 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8261 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8262 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8263 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8265 default: return element;
8269 int get_next_element(int element)
8273 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8274 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8275 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8276 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8277 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8278 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8279 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8280 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8281 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8282 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8283 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8285 default: return element;
8289 int el2img_mm(int element_mm)
8291 return el2img(map_element_MM_to_RND(element_mm));
8294 int el_act_dir2img(int element, int action, int direction)
8296 element = GFX_ELEMENT(element);
8297 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8299 // direction_graphic[][] == graphic[] for undefined direction graphics
8300 return element_info[element].direction_graphic[action][direction];
8303 static int el_act_dir2crm(int element, int action, int direction)
8305 element = GFX_ELEMENT(element);
8306 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8308 // direction_graphic[][] == graphic[] for undefined direction graphics
8309 return element_info[element].direction_crumbled[action][direction];
8312 int el_act2img(int element, int action)
8314 element = GFX_ELEMENT(element);
8316 return element_info[element].graphic[action];
8319 int el_act2crm(int element, int action)
8321 element = GFX_ELEMENT(element);
8323 return element_info[element].crumbled[action];
8326 int el_dir2img(int element, int direction)
8328 element = GFX_ELEMENT(element);
8330 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8333 int el2baseimg(int element)
8335 return element_info[element].graphic[ACTION_DEFAULT];
8338 int el2img(int element)
8340 element = GFX_ELEMENT(element);
8342 return element_info[element].graphic[ACTION_DEFAULT];
8345 int el2edimg(int element)
8347 element = GFX_ELEMENT(element);
8349 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8352 int el2preimg(int element)
8354 element = GFX_ELEMENT(element);
8356 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8359 int el2panelimg(int element)
8361 element = GFX_ELEMENT(element);
8363 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8366 int font2baseimg(int font_nr)
8368 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8371 int getBeltNrFromBeltElement(int element)
8373 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8374 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8375 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8378 int getBeltNrFromBeltActiveElement(int element)
8380 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8381 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8382 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8385 int getBeltNrFromBeltSwitchElement(int element)
8387 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8388 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8389 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8392 int getBeltDirNrFromBeltElement(int element)
8394 static int belt_base_element[4] =
8396 EL_CONVEYOR_BELT_1_LEFT,
8397 EL_CONVEYOR_BELT_2_LEFT,
8398 EL_CONVEYOR_BELT_3_LEFT,
8399 EL_CONVEYOR_BELT_4_LEFT
8402 int belt_nr = getBeltNrFromBeltElement(element);
8403 int belt_dir_nr = element - belt_base_element[belt_nr];
8405 return (belt_dir_nr % 3);
8408 int getBeltDirNrFromBeltSwitchElement(int element)
8410 static int belt_base_element[4] =
8412 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8413 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8414 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8415 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8418 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8419 int belt_dir_nr = element - belt_base_element[belt_nr];
8421 return (belt_dir_nr % 3);
8424 int getBeltDirFromBeltElement(int element)
8426 static int belt_move_dir[3] =
8433 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8435 return belt_move_dir[belt_dir_nr];
8438 int getBeltDirFromBeltSwitchElement(int element)
8440 static int belt_move_dir[3] =
8447 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8449 return belt_move_dir[belt_dir_nr];
8452 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8454 static int belt_base_element[4] =
8456 EL_CONVEYOR_BELT_1_LEFT,
8457 EL_CONVEYOR_BELT_2_LEFT,
8458 EL_CONVEYOR_BELT_3_LEFT,
8459 EL_CONVEYOR_BELT_4_LEFT
8462 return belt_base_element[belt_nr] + belt_dir_nr;
8465 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8467 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8469 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8472 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8474 static int belt_base_element[4] =
8476 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8477 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8478 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8479 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8482 return belt_base_element[belt_nr] + belt_dir_nr;
8485 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8487 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8489 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8492 boolean swapTiles_EM(boolean is_pre_emc_cave)
8494 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8497 boolean getTeamMode_EM(void)
8499 return game.team_mode || network_playing;
8502 boolean isActivePlayer_EM(int player_nr)
8504 return stored_player[player_nr].active;
8507 unsigned int InitRND(int seed)
8509 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8510 return InitEngineRandom_EM(seed);
8511 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8512 return InitEngineRandom_SP(seed);
8513 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8514 return InitEngineRandom_MM(seed);
8516 return InitEngineRandom_RND(seed);
8519 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8520 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8522 static int get_effective_element_EM(int tile, int frame_em)
8524 int element = object_mapping[tile].element_rnd;
8525 int action = object_mapping[tile].action;
8526 boolean is_backside = object_mapping[tile].is_backside;
8527 boolean action_removing = (action == ACTION_DIGGING ||
8528 action == ACTION_SNAPPING ||
8529 action == ACTION_COLLECTING);
8537 return (frame_em > 5 ? EL_EMPTY : element);
8543 else // frame_em == 7
8554 case Ydiamond_stone:
8558 case Xdrip_stretchB:
8574 case Ymagnify_blank:
8577 case Xsand_stonein_1:
8578 case Xsand_stonein_2:
8579 case Xsand_stonein_3:
8580 case Xsand_stonein_4:
8584 return (is_backside || action_removing ? EL_EMPTY : element);
8589 static boolean check_linear_animation_EM(int tile)
8593 case Xsand_stonesand_1:
8594 case Xsand_stonesand_quickout_1:
8595 case Xsand_sandstone_1:
8596 case Xsand_stonein_1:
8597 case Xsand_stoneout_1:
8625 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8626 boolean has_crumbled_graphics,
8627 int crumbled, int sync_frame)
8629 // if element can be crumbled, but certain action graphics are just empty
8630 // space (like instantly snapping sand to empty space in 1 frame), do not
8631 // treat these empty space graphics as crumbled graphics in EMC engine
8632 if (crumbled == IMG_EMPTY_SPACE)
8633 has_crumbled_graphics = FALSE;
8635 if (has_crumbled_graphics)
8637 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8638 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8639 g_crumbled->anim_delay,
8640 g_crumbled->anim_mode,
8641 g_crumbled->anim_start_frame,
8644 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8645 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8647 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8648 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8650 g_em->has_crumbled_graphics = TRUE;
8654 g_em->crumbled_bitmap = NULL;
8655 g_em->crumbled_src_x = 0;
8656 g_em->crumbled_src_y = 0;
8657 g_em->crumbled_border_size = 0;
8658 g_em->crumbled_tile_size = 0;
8660 g_em->has_crumbled_graphics = FALSE;
8665 void ResetGfxAnimation_EM(int x, int y, int tile)
8671 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8672 int tile, int frame_em, int x, int y)
8674 int action = object_mapping[tile].action;
8675 int direction = object_mapping[tile].direction;
8676 int effective_element = get_effective_element_EM(tile, frame_em);
8677 int graphic = (direction == MV_NONE ?
8678 el_act2img(effective_element, action) :
8679 el_act_dir2img(effective_element, action, direction));
8680 struct GraphicInfo *g = &graphic_info[graphic];
8682 boolean action_removing = (action == ACTION_DIGGING ||
8683 action == ACTION_SNAPPING ||
8684 action == ACTION_COLLECTING);
8685 boolean action_moving = (action == ACTION_FALLING ||
8686 action == ACTION_MOVING ||
8687 action == ACTION_PUSHING ||
8688 action == ACTION_EATING ||
8689 action == ACTION_FILLING ||
8690 action == ACTION_EMPTYING);
8691 boolean action_falling = (action == ACTION_FALLING ||
8692 action == ACTION_FILLING ||
8693 action == ACTION_EMPTYING);
8695 // special case: graphic uses "2nd movement tile" and has defined
8696 // 7 frames for movement animation (or less) => use default graphic
8697 // for last (8th) frame which ends the movement animation
8698 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8700 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8701 graphic = (direction == MV_NONE ?
8702 el_act2img(effective_element, action) :
8703 el_act_dir2img(effective_element, action, direction));
8705 g = &graphic_info[graphic];
8708 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8712 else if (action_moving)
8714 boolean is_backside = object_mapping[tile].is_backside;
8718 int direction = object_mapping[tile].direction;
8719 int move_dir = (action_falling ? MV_DOWN : direction);
8724 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8725 if (g->double_movement && frame_em == 0)
8729 if (move_dir == MV_LEFT)
8730 GfxFrame[x - 1][y] = GfxFrame[x][y];
8731 else if (move_dir == MV_RIGHT)
8732 GfxFrame[x + 1][y] = GfxFrame[x][y];
8733 else if (move_dir == MV_UP)
8734 GfxFrame[x][y - 1] = GfxFrame[x][y];
8735 else if (move_dir == MV_DOWN)
8736 GfxFrame[x][y + 1] = GfxFrame[x][y];
8743 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8744 if (tile == Xsand_stonesand_quickout_1 ||
8745 tile == Xsand_stonesand_quickout_2)
8749 if (graphic_info[graphic].anim_global_sync)
8750 sync_frame = FrameCounter;
8751 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8752 sync_frame = GfxFrame[x][y];
8754 sync_frame = 0; // playfield border (pseudo steel)
8756 SetRandomAnimationValue(x, y);
8758 int frame = getAnimationFrame(g->anim_frames,
8761 g->anim_start_frame,
8764 g_em->unique_identifier =
8765 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8768 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8769 int tile, int frame_em, int x, int y)
8771 int action = object_mapping[tile].action;
8772 int direction = object_mapping[tile].direction;
8773 boolean is_backside = object_mapping[tile].is_backside;
8774 int effective_element = get_effective_element_EM(tile, frame_em);
8775 int effective_action = action;
8776 int graphic = (direction == MV_NONE ?
8777 el_act2img(effective_element, effective_action) :
8778 el_act_dir2img(effective_element, effective_action,
8780 int crumbled = (direction == MV_NONE ?
8781 el_act2crm(effective_element, effective_action) :
8782 el_act_dir2crm(effective_element, effective_action,
8784 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8785 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8786 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8787 struct GraphicInfo *g = &graphic_info[graphic];
8790 // special case: graphic uses "2nd movement tile" and has defined
8791 // 7 frames for movement animation (or less) => use default graphic
8792 // for last (8th) frame which ends the movement animation
8793 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8795 effective_action = ACTION_DEFAULT;
8796 graphic = (direction == MV_NONE ?
8797 el_act2img(effective_element, effective_action) :
8798 el_act_dir2img(effective_element, effective_action,
8800 crumbled = (direction == MV_NONE ?
8801 el_act2crm(effective_element, effective_action) :
8802 el_act_dir2crm(effective_element, effective_action,
8805 g = &graphic_info[graphic];
8808 if (graphic_info[graphic].anim_global_sync)
8809 sync_frame = FrameCounter;
8810 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8811 sync_frame = GfxFrame[x][y];
8813 sync_frame = 0; // playfield border (pseudo steel)
8815 SetRandomAnimationValue(x, y);
8817 int frame = getAnimationFrame(g->anim_frames,
8820 g->anim_start_frame,
8823 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8824 g->double_movement && is_backside);
8826 // (updating the "crumbled" graphic definitions is probably not really needed,
8827 // as animations for crumbled graphics can't be longer than one EMC cycle)
8828 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8832 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8833 int player_nr, int anim, int frame_em)
8835 int element = player_mapping[player_nr][anim].element_rnd;
8836 int action = player_mapping[player_nr][anim].action;
8837 int direction = player_mapping[player_nr][anim].direction;
8838 int graphic = (direction == MV_NONE ?
8839 el_act2img(element, action) :
8840 el_act_dir2img(element, action, direction));
8841 struct GraphicInfo *g = &graphic_info[graphic];
8844 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8846 stored_player[player_nr].StepFrame = frame_em;
8848 sync_frame = stored_player[player_nr].Frame;
8850 int frame = getAnimationFrame(g->anim_frames,
8853 g->anim_start_frame,
8856 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8857 &g_em->src_x, &g_em->src_y, FALSE);
8860 void InitGraphicInfo_EM(void)
8864 // always start with reliable default values
8865 for (i = 0; i < GAME_TILE_MAX; i++)
8867 object_mapping[i].element_rnd = EL_UNKNOWN;
8868 object_mapping[i].is_backside = FALSE;
8869 object_mapping[i].action = ACTION_DEFAULT;
8870 object_mapping[i].direction = MV_NONE;
8873 // always start with reliable default values
8874 for (p = 0; p < MAX_PLAYERS; p++)
8876 for (i = 0; i < PLY_MAX; i++)
8878 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8879 player_mapping[p][i].action = ACTION_DEFAULT;
8880 player_mapping[p][i].direction = MV_NONE;
8884 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8886 int e = em_object_mapping_list[i].element_em;
8888 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8889 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8891 if (em_object_mapping_list[i].action != -1)
8892 object_mapping[e].action = em_object_mapping_list[i].action;
8894 if (em_object_mapping_list[i].direction != -1)
8895 object_mapping[e].direction =
8896 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8899 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8901 int a = em_player_mapping_list[i].action_em;
8902 int p = em_player_mapping_list[i].player_nr;
8904 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8906 if (em_player_mapping_list[i].action != -1)
8907 player_mapping[p][a].action = em_player_mapping_list[i].action;
8909 if (em_player_mapping_list[i].direction != -1)
8910 player_mapping[p][a].direction =
8911 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8914 for (i = 0; i < GAME_TILE_MAX; i++)
8916 int element = object_mapping[i].element_rnd;
8917 int action = object_mapping[i].action;
8918 int direction = object_mapping[i].direction;
8919 boolean is_backside = object_mapping[i].is_backside;
8920 boolean action_exploding = ((action == ACTION_EXPLODING ||
8921 action == ACTION_SMASHED_BY_ROCK ||
8922 action == ACTION_SMASHED_BY_SPRING) &&
8923 element != EL_DIAMOND);
8924 boolean action_active = (action == ACTION_ACTIVE);
8925 boolean action_other = (action == ACTION_OTHER);
8927 for (j = 0; j < 8; j++)
8929 int effective_element = get_effective_element_EM(i, j);
8930 int effective_action = (j < 7 ? action :
8931 i == Xdrip_stretch ? action :
8932 i == Xdrip_stretchB ? action :
8933 i == Ydrip_1_s ? action :
8934 i == Ydrip_1_sB ? action :
8935 i == Yball_1 ? action :
8936 i == Xball_2 ? action :
8937 i == Yball_2 ? action :
8938 i == Yball_blank ? action :
8939 i == Ykey_1_blank ? action :
8940 i == Ykey_2_blank ? action :
8941 i == Ykey_3_blank ? action :
8942 i == Ykey_4_blank ? action :
8943 i == Ykey_5_blank ? action :
8944 i == Ykey_6_blank ? action :
8945 i == Ykey_7_blank ? action :
8946 i == Ykey_8_blank ? action :
8947 i == Ylenses_blank ? action :
8948 i == Ymagnify_blank ? action :
8949 i == Ygrass_blank ? action :
8950 i == Ydirt_blank ? action :
8951 i == Xsand_stonein_1 ? action :
8952 i == Xsand_stonein_2 ? action :
8953 i == Xsand_stonein_3 ? action :
8954 i == Xsand_stonein_4 ? action :
8955 i == Xsand_stoneout_1 ? action :
8956 i == Xsand_stoneout_2 ? action :
8957 i == Xboom_android ? ACTION_EXPLODING :
8958 action_exploding ? ACTION_EXPLODING :
8959 action_active ? action :
8960 action_other ? action :
8962 int graphic = (el_act_dir2img(effective_element, effective_action,
8964 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8966 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8967 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8968 boolean has_action_graphics = (graphic != base_graphic);
8969 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8970 struct GraphicInfo *g = &graphic_info[graphic];
8971 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8974 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8975 boolean special_animation = (action != ACTION_DEFAULT &&
8976 g->anim_frames == 3 &&
8977 g->anim_delay == 2 &&
8978 g->anim_mode & ANIM_LINEAR);
8979 int sync_frame = (i == Xdrip_stretch ? 7 :
8980 i == Xdrip_stretchB ? 7 :
8981 i == Ydrip_2_s ? j + 8 :
8982 i == Ydrip_2_sB ? j + 8 :
8991 i == Xfake_acid_1 ? 0 :
8992 i == Xfake_acid_2 ? 10 :
8993 i == Xfake_acid_3 ? 20 :
8994 i == Xfake_acid_4 ? 30 :
8995 i == Xfake_acid_5 ? 40 :
8996 i == Xfake_acid_6 ? 50 :
8997 i == Xfake_acid_7 ? 60 :
8998 i == Xfake_acid_8 ? 70 :
8999 i == Xfake_acid_1_player ? 0 :
9000 i == Xfake_acid_2_player ? 10 :
9001 i == Xfake_acid_3_player ? 20 :
9002 i == Xfake_acid_4_player ? 30 :
9003 i == Xfake_acid_5_player ? 40 :
9004 i == Xfake_acid_6_player ? 50 :
9005 i == Xfake_acid_7_player ? 60 :
9006 i == Xfake_acid_8_player ? 70 :
9008 i == Yball_2 ? j + 8 :
9009 i == Yball_blank ? j + 1 :
9010 i == Ykey_1_blank ? j + 1 :
9011 i == Ykey_2_blank ? j + 1 :
9012 i == Ykey_3_blank ? j + 1 :
9013 i == Ykey_4_blank ? j + 1 :
9014 i == Ykey_5_blank ? j + 1 :
9015 i == Ykey_6_blank ? j + 1 :
9016 i == Ykey_7_blank ? j + 1 :
9017 i == Ykey_8_blank ? j + 1 :
9018 i == Ylenses_blank ? j + 1 :
9019 i == Ymagnify_blank ? j + 1 :
9020 i == Ygrass_blank ? j + 1 :
9021 i == Ydirt_blank ? j + 1 :
9022 i == Xamoeba_1 ? 0 :
9023 i == Xamoeba_2 ? 1 :
9024 i == Xamoeba_3 ? 2 :
9025 i == Xamoeba_4 ? 3 :
9026 i == Xamoeba_5 ? 0 :
9027 i == Xamoeba_6 ? 1 :
9028 i == Xamoeba_7 ? 2 :
9029 i == Xamoeba_8 ? 3 :
9030 i == Xexit_2 ? j + 8 :
9031 i == Xexit_3 ? j + 16 :
9032 i == Xdynamite_1 ? 0 :
9033 i == Xdynamite_2 ? 8 :
9034 i == Xdynamite_3 ? 16 :
9035 i == Xdynamite_4 ? 24 :
9036 i == Xsand_stonein_1 ? j + 1 :
9037 i == Xsand_stonein_2 ? j + 9 :
9038 i == Xsand_stonein_3 ? j + 17 :
9039 i == Xsand_stonein_4 ? j + 25 :
9040 i == Xsand_stoneout_1 && j == 0 ? 0 :
9041 i == Xsand_stoneout_1 && j == 1 ? 0 :
9042 i == Xsand_stoneout_1 && j == 2 ? 1 :
9043 i == Xsand_stoneout_1 && j == 3 ? 2 :
9044 i == Xsand_stoneout_1 && j == 4 ? 2 :
9045 i == Xsand_stoneout_1 && j == 5 ? 3 :
9046 i == Xsand_stoneout_1 && j == 6 ? 4 :
9047 i == Xsand_stoneout_1 && j == 7 ? 4 :
9048 i == Xsand_stoneout_2 && j == 0 ? 5 :
9049 i == Xsand_stoneout_2 && j == 1 ? 6 :
9050 i == Xsand_stoneout_2 && j == 2 ? 7 :
9051 i == Xsand_stoneout_2 && j == 3 ? 8 :
9052 i == Xsand_stoneout_2 && j == 4 ? 9 :
9053 i == Xsand_stoneout_2 && j == 5 ? 11 :
9054 i == Xsand_stoneout_2 && j == 6 ? 13 :
9055 i == Xsand_stoneout_2 && j == 7 ? 15 :
9056 i == Xboom_bug && j == 1 ? 2 :
9057 i == Xboom_bug && j == 2 ? 2 :
9058 i == Xboom_bug && j == 3 ? 4 :
9059 i == Xboom_bug && j == 4 ? 4 :
9060 i == Xboom_bug && j == 5 ? 2 :
9061 i == Xboom_bug && j == 6 ? 2 :
9062 i == Xboom_bug && j == 7 ? 0 :
9063 i == Xboom_tank && j == 1 ? 2 :
9064 i == Xboom_tank && j == 2 ? 2 :
9065 i == Xboom_tank && j == 3 ? 4 :
9066 i == Xboom_tank && j == 4 ? 4 :
9067 i == Xboom_tank && j == 5 ? 2 :
9068 i == Xboom_tank && j == 6 ? 2 :
9069 i == Xboom_tank && j == 7 ? 0 :
9070 i == Xboom_android && j == 7 ? 6 :
9071 i == Xboom_1 && j == 1 ? 2 :
9072 i == Xboom_1 && j == 2 ? 2 :
9073 i == Xboom_1 && j == 3 ? 4 :
9074 i == Xboom_1 && j == 4 ? 4 :
9075 i == Xboom_1 && j == 5 ? 6 :
9076 i == Xboom_1 && j == 6 ? 6 :
9077 i == Xboom_1 && j == 7 ? 8 :
9078 i == Xboom_2 && j == 0 ? 8 :
9079 i == Xboom_2 && j == 1 ? 8 :
9080 i == Xboom_2 && j == 2 ? 10 :
9081 i == Xboom_2 && j == 3 ? 10 :
9082 i == Xboom_2 && j == 4 ? 10 :
9083 i == Xboom_2 && j == 5 ? 12 :
9084 i == Xboom_2 && j == 6 ? 12 :
9085 i == Xboom_2 && j == 7 ? 12 :
9086 special_animation && j == 4 ? 3 :
9087 effective_action != action ? 0 :
9089 int frame = getAnimationFrame(g->anim_frames,
9092 g->anim_start_frame,
9095 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9096 g->double_movement && is_backside);
9098 g_em->bitmap = src_bitmap;
9099 g_em->src_x = src_x;
9100 g_em->src_y = src_y;
9101 g_em->src_offset_x = 0;
9102 g_em->src_offset_y = 0;
9103 g_em->dst_offset_x = 0;
9104 g_em->dst_offset_y = 0;
9105 g_em->width = TILEX;
9106 g_em->height = TILEY;
9108 g_em->preserve_background = FALSE;
9110 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9113 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9114 effective_action == ACTION_MOVING ||
9115 effective_action == ACTION_PUSHING ||
9116 effective_action == ACTION_EATING)) ||
9117 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9118 effective_action == ACTION_EMPTYING)))
9121 (effective_action == ACTION_FALLING ||
9122 effective_action == ACTION_FILLING ||
9123 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9124 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9125 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9126 int num_steps = (i == Ydrip_1_s ? 16 :
9127 i == Ydrip_1_sB ? 16 :
9128 i == Ydrip_2_s ? 16 :
9129 i == Ydrip_2_sB ? 16 :
9130 i == Xsand_stonein_1 ? 32 :
9131 i == Xsand_stonein_2 ? 32 :
9132 i == Xsand_stonein_3 ? 32 :
9133 i == Xsand_stonein_4 ? 32 :
9134 i == Xsand_stoneout_1 ? 16 :
9135 i == Xsand_stoneout_2 ? 16 : 8);
9136 int cx = ABS(dx) * (TILEX / num_steps);
9137 int cy = ABS(dy) * (TILEY / num_steps);
9138 int step_frame = (i == Ydrip_2_s ? j + 8 :
9139 i == Ydrip_2_sB ? j + 8 :
9140 i == Xsand_stonein_2 ? j + 8 :
9141 i == Xsand_stonein_3 ? j + 16 :
9142 i == Xsand_stonein_4 ? j + 24 :
9143 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9144 int step = (is_backside ? step_frame : num_steps - step_frame);
9146 if (is_backside) // tile where movement starts
9148 if (dx < 0 || dy < 0)
9150 g_em->src_offset_x = cx * step;
9151 g_em->src_offset_y = cy * step;
9155 g_em->dst_offset_x = cx * step;
9156 g_em->dst_offset_y = cy * step;
9159 else // tile where movement ends
9161 if (dx < 0 || dy < 0)
9163 g_em->dst_offset_x = cx * step;
9164 g_em->dst_offset_y = cy * step;
9168 g_em->src_offset_x = cx * step;
9169 g_em->src_offset_y = cy * step;
9173 g_em->width = TILEX - cx * step;
9174 g_em->height = TILEY - cy * step;
9177 // create unique graphic identifier to decide if tile must be redrawn
9178 /* bit 31 - 16 (16 bit): EM style graphic
9179 bit 15 - 12 ( 4 bit): EM style frame
9180 bit 11 - 6 ( 6 bit): graphic width
9181 bit 5 - 0 ( 6 bit): graphic height */
9182 g_em->unique_identifier =
9183 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9187 for (i = 0; i < GAME_TILE_MAX; i++)
9189 for (j = 0; j < 8; j++)
9191 int element = object_mapping[i].element_rnd;
9192 int action = object_mapping[i].action;
9193 int direction = object_mapping[i].direction;
9194 boolean is_backside = object_mapping[i].is_backside;
9195 int graphic_action = el_act_dir2img(element, action, direction);
9196 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9198 if ((action == ACTION_SMASHED_BY_ROCK ||
9199 action == ACTION_SMASHED_BY_SPRING ||
9200 action == ACTION_EATING) &&
9201 graphic_action == graphic_default)
9203 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9204 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9205 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9206 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9209 // no separate animation for "smashed by rock" -- use rock instead
9210 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9211 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9213 g_em->bitmap = g_xx->bitmap;
9214 g_em->src_x = g_xx->src_x;
9215 g_em->src_y = g_xx->src_y;
9216 g_em->src_offset_x = g_xx->src_offset_x;
9217 g_em->src_offset_y = g_xx->src_offset_y;
9218 g_em->dst_offset_x = g_xx->dst_offset_x;
9219 g_em->dst_offset_y = g_xx->dst_offset_y;
9220 g_em->width = g_xx->width;
9221 g_em->height = g_xx->height;
9222 g_em->unique_identifier = g_xx->unique_identifier;
9225 g_em->preserve_background = TRUE;
9230 for (p = 0; p < MAX_PLAYERS; p++)
9232 for (i = 0; i < PLY_MAX; i++)
9234 int element = player_mapping[p][i].element_rnd;
9235 int action = player_mapping[p][i].action;
9236 int direction = player_mapping[p][i].direction;
9238 for (j = 0; j < 8; j++)
9240 int effective_element = element;
9241 int effective_action = action;
9242 int graphic = (direction == MV_NONE ?
9243 el_act2img(effective_element, effective_action) :
9244 el_act_dir2img(effective_element, effective_action,
9246 struct GraphicInfo *g = &graphic_info[graphic];
9247 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9251 int frame = getAnimationFrame(g->anim_frames,
9254 g->anim_start_frame,
9257 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9259 g_em->bitmap = src_bitmap;
9260 g_em->src_x = src_x;
9261 g_em->src_y = src_y;
9262 g_em->src_offset_x = 0;
9263 g_em->src_offset_y = 0;
9264 g_em->dst_offset_x = 0;
9265 g_em->dst_offset_y = 0;
9266 g_em->width = TILEX;
9267 g_em->height = TILEY;
9273 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9274 boolean any_player_moving,
9275 boolean any_player_snapping,
9276 boolean any_player_dropping)
9278 if (frame == 7 && !any_player_dropping)
9280 if (!local_player->was_waiting)
9282 if (!CheckSaveEngineSnapshotToList())
9285 local_player->was_waiting = TRUE;
9288 else if (any_player_moving || any_player_snapping || any_player_dropping)
9290 local_player->was_waiting = FALSE;
9294 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9295 boolean murphy_is_dropping)
9297 if (murphy_is_waiting)
9299 if (!local_player->was_waiting)
9301 if (!CheckSaveEngineSnapshotToList())
9304 local_player->was_waiting = TRUE;
9309 local_player->was_waiting = FALSE;
9313 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9314 boolean button_released)
9316 if (button_released)
9318 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9319 CheckSaveEngineSnapshotToList();
9321 else if (element_clicked)
9323 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9324 CheckSaveEngineSnapshotToList();
9326 game.snapshot.changed_action = TRUE;
9330 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9331 boolean any_player_moving,
9332 boolean any_player_snapping,
9333 boolean any_player_dropping)
9335 if (tape.single_step && tape.recording && !tape.pausing)
9336 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9337 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9339 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9340 any_player_snapping, any_player_dropping);
9342 return tape.pausing;
9345 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9346 boolean murphy_is_dropping)
9348 boolean murphy_starts_dropping = FALSE;
9351 for (i = 0; i < MAX_PLAYERS; i++)
9352 if (stored_player[i].force_dropping)
9353 murphy_starts_dropping = TRUE;
9355 if (tape.single_step && tape.recording && !tape.pausing)
9356 if (murphy_is_waiting && !murphy_starts_dropping)
9357 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9359 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9362 void CheckSingleStepMode_MM(boolean element_clicked,
9363 boolean button_released)
9365 if (tape.single_step && tape.recording && !tape.pausing)
9366 if (button_released)
9367 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9369 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9372 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9373 int graphic, int sync_frame, int x, int y)
9375 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9377 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9380 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9382 return (IS_NEXT_FRAME(sync_frame, graphic));
9385 int getGraphicInfo_Delay(int graphic)
9387 return graphic_info[graphic].anim_delay;
9390 void PlayMenuSoundExt(int sound)
9392 if (sound == SND_UNDEFINED)
9395 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9396 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9399 if (IS_LOOP_SOUND(sound))
9400 PlaySoundLoop(sound);
9405 void PlayMenuSound(void)
9407 PlayMenuSoundExt(menu.sound[game_status]);
9410 void PlayMenuSoundStereo(int sound, int stereo_position)
9412 if (sound == SND_UNDEFINED)
9415 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9416 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9419 if (IS_LOOP_SOUND(sound))
9420 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9422 PlaySoundStereo(sound, stereo_position);
9425 void PlayMenuSoundIfLoopExt(int sound)
9427 if (sound == SND_UNDEFINED)
9430 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9431 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9434 if (IS_LOOP_SOUND(sound))
9435 PlaySoundLoop(sound);
9438 void PlayMenuSoundIfLoop(void)
9440 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9443 void PlayMenuMusicExt(int music)
9445 if (music == MUS_UNDEFINED)
9448 if (!setup.sound_music)
9451 if (IS_LOOP_MUSIC(music))
9452 PlayMusicLoop(music);
9457 void PlayMenuMusic(void)
9459 char *curr_music = getCurrentlyPlayingMusicFilename();
9460 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9462 if (!strEqual(curr_music, next_music))
9463 PlayMenuMusicExt(menu.music[game_status]);
9466 void PlayMenuSoundsAndMusic(void)
9472 static void FadeMenuSounds(void)
9477 static void FadeMenuMusic(void)
9479 char *curr_music = getCurrentlyPlayingMusicFilename();
9480 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9482 if (!strEqual(curr_music, next_music))
9486 void FadeMenuSoundsAndMusic(void)
9492 void PlaySoundActivating(void)
9495 PlaySound(SND_MENU_ITEM_ACTIVATING);
9499 void PlaySoundSelecting(void)
9502 PlaySound(SND_MENU_ITEM_SELECTING);
9506 void ToggleFullscreenIfNeeded(void)
9508 // if setup and video fullscreen state are already matching, nothing do do
9509 if (setup.fullscreen == video.fullscreen_enabled ||
9510 !video.fullscreen_available)
9513 SDLSetWindowFullscreen(setup.fullscreen);
9515 // set setup value according to successfully changed fullscreen mode
9516 setup.fullscreen = video.fullscreen_enabled;
9519 void ChangeWindowScalingIfNeeded(void)
9521 // if setup and video window scaling are already matching, nothing do do
9522 if (setup.window_scaling_percent == video.window_scaling_percent ||
9523 video.fullscreen_enabled)
9526 SDLSetWindowScaling(setup.window_scaling_percent);
9528 // set setup value according to successfully changed window scaling
9529 setup.window_scaling_percent = video.window_scaling_percent;
9532 void ChangeVsyncModeIfNeeded(void)
9534 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9535 int video_vsync_mode = video.vsync_mode;
9537 // if setup and video vsync mode are already matching, nothing do do
9538 if (setup_vsync_mode == video_vsync_mode)
9541 // if renderer is using OpenGL, vsync mode can directly be changed
9542 SDLSetScreenVsyncMode(setup.vsync_mode);
9544 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9545 if (video.vsync_mode == video_vsync_mode)
9547 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9549 // save backbuffer content which gets lost when re-creating screen
9550 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9552 // force re-creating screen and renderer to set new vsync mode
9553 video.fullscreen_enabled = !setup.fullscreen;
9555 // when creating new renderer, destroy textures linked to old renderer
9556 FreeAllImageTextures(); // needs old renderer to free the textures
9558 // re-create screen and renderer (including change of vsync mode)
9559 ChangeVideoModeIfNeeded(setup.fullscreen);
9561 // set setup value according to successfully changed fullscreen mode
9562 setup.fullscreen = video.fullscreen_enabled;
9564 // restore backbuffer content from temporary backbuffer backup bitmap
9565 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9566 FreeBitmap(tmp_backbuffer);
9568 // update visible window/screen
9569 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9571 // when changing vsync mode, re-create textures for new renderer
9572 InitImageTextures();
9575 // set setup value according to successfully changed vsync mode
9576 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9579 static void JoinRectangles(int *x, int *y, int *width, int *height,
9580 int x2, int y2, int width2, int height2)
9582 // do not join with "off-screen" rectangle
9583 if (x2 == -1 || y2 == -1)
9588 *width = MAX(*width, width2);
9589 *height = MAX(*height, height2);
9592 void SetAnimStatus(int anim_status_new)
9594 if (anim_status_new == GAME_MODE_MAIN)
9595 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9596 else if (anim_status_new == GAME_MODE_NAMES)
9597 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9598 else if (anim_status_new == GAME_MODE_SCORES)
9599 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9601 global.anim_status_next = anim_status_new;
9603 // directly set screen modes that are entered without fading
9604 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9605 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9606 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9607 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9608 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9609 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9610 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9611 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9612 global.anim_status = global.anim_status_next;
9615 void SetGameStatus(int game_status_new)
9617 if (game_status_new != game_status)
9618 game_status_last_screen = game_status;
9620 game_status = game_status_new;
9622 SetAnimStatus(game_status_new);
9625 void SetFontStatus(int game_status_new)
9627 static int last_game_status = -1;
9629 if (game_status_new != -1)
9631 // set game status for font use after storing last game status
9632 last_game_status = game_status;
9633 game_status = game_status_new;
9637 // reset game status after font use from last stored game status
9638 game_status = last_game_status;
9642 void ResetFontStatus(void)
9647 void SetLevelSetInfo(char *identifier, int level_nr)
9649 setString(&levelset.identifier, identifier);
9651 levelset.level_nr = level_nr;
9654 boolean CheckIfAllViewportsHaveChanged(void)
9656 // if game status has not changed, viewports have not changed either
9657 if (game_status == game_status_last)
9660 // check if all viewports have changed with current game status
9662 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9663 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9664 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9665 int new_real_sx = vp_playfield->x;
9666 int new_real_sy = vp_playfield->y;
9667 int new_full_sxsize = vp_playfield->width;
9668 int new_full_sysize = vp_playfield->height;
9669 int new_dx = vp_door_1->x;
9670 int new_dy = vp_door_1->y;
9671 int new_dxsize = vp_door_1->width;
9672 int new_dysize = vp_door_1->height;
9673 int new_vx = vp_door_2->x;
9674 int new_vy = vp_door_2->y;
9675 int new_vxsize = vp_door_2->width;
9676 int new_vysize = vp_door_2->height;
9678 boolean playfield_viewport_has_changed =
9679 (new_real_sx != REAL_SX ||
9680 new_real_sy != REAL_SY ||
9681 new_full_sxsize != FULL_SXSIZE ||
9682 new_full_sysize != FULL_SYSIZE);
9684 boolean door_1_viewport_has_changed =
9687 new_dxsize != DXSIZE ||
9688 new_dysize != DYSIZE);
9690 boolean door_2_viewport_has_changed =
9693 new_vxsize != VXSIZE ||
9694 new_vysize != VYSIZE ||
9695 game_status_last == GAME_MODE_EDITOR);
9697 return (playfield_viewport_has_changed &&
9698 door_1_viewport_has_changed &&
9699 door_2_viewport_has_changed);
9702 boolean CheckFadeAll(void)
9704 return (CheckIfGlobalBorderHasChanged() ||
9705 CheckIfAllViewportsHaveChanged());
9708 void ChangeViewportPropertiesIfNeeded(void)
9710 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9711 FALSE : setup.small_game_graphics);
9712 int gfx_game_mode = game_status;
9713 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9715 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9716 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9717 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9718 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9719 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9720 int new_win_xsize = vp_window->width;
9721 int new_win_ysize = vp_window->height;
9722 int border_left = vp_playfield->border_left;
9723 int border_right = vp_playfield->border_right;
9724 int border_top = vp_playfield->border_top;
9725 int border_bottom = vp_playfield->border_bottom;
9726 int new_sx = vp_playfield->x + border_left;
9727 int new_sy = vp_playfield->y + border_top;
9728 int new_sxsize = vp_playfield->width - border_left - border_right;
9729 int new_sysize = vp_playfield->height - border_top - border_bottom;
9730 int new_real_sx = vp_playfield->x;
9731 int new_real_sy = vp_playfield->y;
9732 int new_full_sxsize = vp_playfield->width;
9733 int new_full_sysize = vp_playfield->height;
9734 int new_dx = vp_door_1->x;
9735 int new_dy = vp_door_1->y;
9736 int new_dxsize = vp_door_1->width;
9737 int new_dysize = vp_door_1->height;
9738 int new_vx = vp_door_2->x;
9739 int new_vy = vp_door_2->y;
9740 int new_vxsize = vp_door_2->width;
9741 int new_vysize = vp_door_2->height;
9742 int new_ex = vp_door_3->x;
9743 int new_ey = vp_door_3->y;
9744 int new_exsize = vp_door_3->width;
9745 int new_eysize = vp_door_3->height;
9746 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9747 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9748 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9749 int new_scr_fieldx = new_sxsize / tilesize;
9750 int new_scr_fieldy = new_sysize / tilesize;
9751 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9752 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9753 boolean init_gfx_buffers = FALSE;
9754 boolean init_video_buffer = FALSE;
9755 boolean init_gadgets_and_anims = FALSE;
9756 boolean init_em_graphics = FALSE;
9758 if (new_win_xsize != WIN_XSIZE ||
9759 new_win_ysize != WIN_YSIZE)
9761 WIN_XSIZE = new_win_xsize;
9762 WIN_YSIZE = new_win_ysize;
9764 init_video_buffer = TRUE;
9765 init_gfx_buffers = TRUE;
9766 init_gadgets_and_anims = TRUE;
9768 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9771 if (new_scr_fieldx != SCR_FIELDX ||
9772 new_scr_fieldy != SCR_FIELDY)
9774 // this always toggles between MAIN and GAME when using small tile size
9776 SCR_FIELDX = new_scr_fieldx;
9777 SCR_FIELDY = new_scr_fieldy;
9779 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9790 new_sxsize != SXSIZE ||
9791 new_sysize != SYSIZE ||
9792 new_dxsize != DXSIZE ||
9793 new_dysize != DYSIZE ||
9794 new_vxsize != VXSIZE ||
9795 new_vysize != VYSIZE ||
9796 new_exsize != EXSIZE ||
9797 new_eysize != EYSIZE ||
9798 new_real_sx != REAL_SX ||
9799 new_real_sy != REAL_SY ||
9800 new_full_sxsize != FULL_SXSIZE ||
9801 new_full_sysize != FULL_SYSIZE ||
9802 new_tilesize_var != TILESIZE_VAR
9805 // ------------------------------------------------------------------------
9806 // determine next fading area for changed viewport definitions
9807 // ------------------------------------------------------------------------
9809 // start with current playfield area (default fading area)
9812 FADE_SXSIZE = FULL_SXSIZE;
9813 FADE_SYSIZE = FULL_SYSIZE;
9815 // add new playfield area if position or size has changed
9816 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9817 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9819 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9820 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9823 // add current and new door 1 area if position or size has changed
9824 if (new_dx != DX || new_dy != DY ||
9825 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9827 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9828 DX, DY, DXSIZE, DYSIZE);
9829 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9830 new_dx, new_dy, new_dxsize, new_dysize);
9833 // add current and new door 2 area if position or size has changed
9834 if (new_vx != VX || new_vy != VY ||
9835 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9837 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9838 VX, VY, VXSIZE, VYSIZE);
9839 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9840 new_vx, new_vy, new_vxsize, new_vysize);
9843 // ------------------------------------------------------------------------
9844 // handle changed tile size
9845 // ------------------------------------------------------------------------
9847 if (new_tilesize_var != TILESIZE_VAR)
9849 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9851 // changing tile size invalidates scroll values of engine snapshots
9852 FreeEngineSnapshotSingle();
9854 // changing tile size requires update of graphic mapping for EM engine
9855 init_em_graphics = TRUE;
9866 SXSIZE = new_sxsize;
9867 SYSIZE = new_sysize;
9868 DXSIZE = new_dxsize;
9869 DYSIZE = new_dysize;
9870 VXSIZE = new_vxsize;
9871 VYSIZE = new_vysize;
9872 EXSIZE = new_exsize;
9873 EYSIZE = new_eysize;
9874 REAL_SX = new_real_sx;
9875 REAL_SY = new_real_sy;
9876 FULL_SXSIZE = new_full_sxsize;
9877 FULL_SYSIZE = new_full_sysize;
9878 TILESIZE_VAR = new_tilesize_var;
9880 init_gfx_buffers = TRUE;
9881 init_gadgets_and_anims = TRUE;
9883 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9884 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9887 if (init_gfx_buffers)
9889 // Debug("tools:viewport", "init_gfx_buffers");
9891 SCR_FIELDX = new_scr_fieldx_buffers;
9892 SCR_FIELDY = new_scr_fieldy_buffers;
9896 SCR_FIELDX = new_scr_fieldx;
9897 SCR_FIELDY = new_scr_fieldy;
9899 SetDrawDeactivationMask(REDRAW_NONE);
9900 SetDrawBackgroundMask(REDRAW_FIELD);
9903 if (init_video_buffer)
9905 // Debug("tools:viewport", "init_video_buffer");
9907 FreeAllImageTextures(); // needs old renderer to free the textures
9909 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9910 InitImageTextures();
9913 if (init_gadgets_and_anims)
9915 // Debug("tools:viewport", "init_gadgets_and_anims");
9918 InitGlobalAnimations();
9921 if (init_em_graphics)
9923 InitGraphicInfo_EM();
9928 // ============================================================================
9930 // ============================================================================
9932 #if defined(PLATFORM_WIN32)
9933 /* FILETIME of Jan 1 1970 00:00:00. */
9934 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9937 * timezone information is stored outside the kernel so tzp isn't used anymore.
9939 * Note: this function is not for Win32 high precision timing purpose. See
9942 int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9945 SYSTEMTIME system_time;
9946 ULARGE_INTEGER ularge;
9948 GetSystemTime(&system_time);
9949 SystemTimeToFileTime(&system_time, &file_time);
9950 ularge.LowPart = file_time.dwLowDateTime;
9951 ularge.HighPart = file_time.dwHighDateTime;
9953 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9954 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9960 static char *test_init_uuid_random_function_simple(void)
9962 static char seed_text[100];
9963 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9965 sprintf(seed_text, "%d", seed);
9970 static char *test_init_uuid_random_function_better(void)
9972 static char seed_text[100];
9973 struct timeval current_time;
9975 gettimeofday(¤t_time, NULL);
9977 prng_seed_bytes(¤t_time, sizeof(current_time));
9979 sprintf(seed_text, "%ld.%ld",
9980 (long)current_time.tv_sec,
9981 (long)current_time.tv_usec);
9986 #if defined(PLATFORM_WIN32)
9987 static char *test_init_uuid_random_function_better_windows(void)
9989 static char seed_text[100];
9990 struct timeval current_time;
9992 gettimeofday_windows(¤t_time, NULL);
9994 prng_seed_bytes(¤t_time, sizeof(current_time));
9996 sprintf(seed_text, "%ld.%ld",
9997 (long)current_time.tv_sec,
9998 (long)current_time.tv_usec);
10004 static unsigned int test_uuid_random_function_simple(int max)
10006 return GetSimpleRandom(max);
10009 static unsigned int test_uuid_random_function_better(int max)
10011 return (max > 0 ? prng_get_uint() % max : 0);
10014 #if defined(PLATFORM_WIN32)
10015 #define NUM_UUID_TESTS 3
10017 #define NUM_UUID_TESTS 2
10020 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10022 struct hashtable *hash_seeds =
10023 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10024 struct hashtable *hash_uuids =
10025 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10026 static char message[100];
10029 char *random_name = (nr == 0 ? "simple" : "better");
10030 char *random_type = (always_seed ? "always" : "only once");
10031 char *(*init_random_function)(void) =
10033 test_init_uuid_random_function_simple :
10034 test_init_uuid_random_function_better);
10035 unsigned int (*random_function)(int) =
10037 test_uuid_random_function_simple :
10038 test_uuid_random_function_better);
10041 #if defined(PLATFORM_WIN32)
10044 random_name = "windows";
10045 init_random_function = test_init_uuid_random_function_better_windows;
10051 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10052 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10054 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10055 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10056 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10058 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10062 // always initialize random number generator at least once
10063 init_random_function();
10065 unsigned int time_start = SDL_GetTicks();
10067 for (i = 0; i < num_uuids; i++)
10071 char *seed = getStringCopy(init_random_function());
10073 hashtable_remove(hash_seeds, seed);
10074 hashtable_insert(hash_seeds, seed, "1");
10077 char *uuid = getStringCopy(getUUIDExt(random_function));
10079 hashtable_remove(hash_uuids, uuid);
10080 hashtable_insert(hash_uuids, uuid, "1");
10083 int num_unique_seeds = hashtable_count(hash_seeds);
10084 int num_unique_uuids = hashtable_count(hash_uuids);
10086 unsigned int time_needed = SDL_GetTicks() - time_start;
10088 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10090 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10093 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10095 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10096 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10098 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10100 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10102 Request(message, REQ_CONFIRM);
10104 hashtable_destroy(hash_seeds, 0);
10105 hashtable_destroy(hash_uuids, 0);
10108 void TestGeneratingUUIDs(void)
10110 int num_uuids = 1000000;
10113 for (i = 0; i < NUM_UUID_TESTS; i++)
10114 for (j = 0; j < 2; j++)
10115 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10117 CloseAllAndExit(0);