1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 // currently not available
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 // never draw masked screen borders on borderless screens
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD(void)
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void DrawTileCursor(int draw_target)
634 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
639 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
644 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
647 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
652 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653 BlitScreenToBitmap_EM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655 BlitScreenToBitmap_SP(target_bitmap);
656 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657 BlitScreenToBitmap_MM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659 BlitScreenToBitmap_RND(target_bitmap);
661 redraw_mask |= REDRAW_FIELD;
664 static void DrawFramesPerSecond(void)
667 int font_nr = FONT_TEXT_2;
668 int font_width = getFontWidth(font_nr);
669 int draw_deactivation_mask = GetDrawDeactivationMask();
670 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
672 // draw FPS with leading space (needed if field buffer deactivated)
673 sprintf(text, " %04.1f fps", global.frames_per_second);
675 // override draw deactivation mask (required for invisible warp mode)
676 SetDrawDeactivationMask(REDRAW_NONE);
678 // draw opaque FPS if field buffer deactivated, else draw masked FPS
679 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
682 // set draw deactivation mask to previous value
683 SetDrawDeactivationMask(draw_deactivation_mask);
685 // force full-screen redraw in this frame
686 redraw_mask = REDRAW_ALL;
690 static void PrintFrameTimeDebugging(void)
692 static unsigned int last_counter = 0;
693 unsigned int counter = Counter();
694 int diff_1 = counter - last_counter;
695 int diff_2 = diff_1 - GAME_FRAME_DELAY;
697 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698 char diff_bar[2 * diff_2_max + 5];
702 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
704 for (i = 0; i < diff_2_max; i++)
705 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706 i >= diff_2_max - diff_2_cut ? '-' : ' ');
708 diff_bar[pos++] = '|';
710 for (i = 0; i < diff_2_max; i++)
711 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
713 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
715 diff_bar[pos++] = '\0';
717 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
720 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
723 last_counter = counter;
727 static int unifiedRedrawMask(int mask)
729 if (mask & REDRAW_ALL)
732 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
740 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
743 void BackToFront(void)
745 static int last_redraw_mask = REDRAW_NONE;
747 // force screen redraw in every frame to continue drawing global animations
748 // (but always use the last redraw mask to prevent unwanted side effects)
749 if (redraw_mask == REDRAW_NONE)
750 redraw_mask = last_redraw_mask;
752 last_redraw_mask = redraw_mask;
755 // masked border now drawn immediately when blitting backbuffer to window
757 // draw masked border to all viewports, if defined
758 DrawMaskedBorder(redraw_mask);
761 // draw frames per second (only if debug mode is enabled)
762 if (redraw_mask & REDRAW_FPS)
763 DrawFramesPerSecond();
765 // remove playfield redraw before potentially merging with doors redraw
766 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767 redraw_mask &= ~REDRAW_FIELD;
769 // redraw complete window if both playfield and (some) doors need redraw
770 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771 redraw_mask = REDRAW_ALL;
773 /* although redrawing the whole window would be fine for normal gameplay,
774 being able to only redraw the playfield is required for deactivating
775 certain drawing areas (mainly playfield) to work, which is needed for
776 warp-forward to be fast enough (by skipping redraw of most frames) */
778 if (redraw_mask & REDRAW_ALL)
780 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
782 else if (redraw_mask & REDRAW_FIELD)
784 BlitBitmap(backbuffer, window,
785 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
787 else if (redraw_mask & REDRAW_DOORS)
789 // merge door areas to prevent calling screen redraw more than once
795 if (redraw_mask & REDRAW_DOOR_1)
799 x2 = MAX(x2, DX + DXSIZE);
800 y2 = MAX(y2, DY + DYSIZE);
803 if (redraw_mask & REDRAW_DOOR_2)
807 x2 = MAX(x2, VX + VXSIZE);
808 y2 = MAX(y2, VY + VYSIZE);
811 if (redraw_mask & REDRAW_DOOR_3)
815 x2 = MAX(x2, EX + EXSIZE);
816 y2 = MAX(y2, EY + EYSIZE);
819 // make sure that at least one pixel is blitted, and inside the screen
820 // (else nothing is blitted, causing the animations not to be updated)
821 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823 x2 = MIN(MAX(1, x2), WIN_XSIZE);
824 y2 = MIN(MAX(1, y2), WIN_YSIZE);
826 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
829 redraw_mask = REDRAW_NONE;
832 PrintFrameTimeDebugging();
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
838 unsigned int frame_delay_value_old = GetVideoFrameDelay();
840 SetVideoFrameDelay(frame_delay_value);
844 SetVideoFrameDelay(frame_delay_value_old);
847 static int fade_type_skip = FADE_TYPE_NONE;
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
851 void (*draw_border_function)(void) = NULL;
852 int x, y, width, height;
853 int fade_delay, post_delay;
855 if (fade_type == FADE_TYPE_FADE_OUT)
857 if (fade_type_skip != FADE_TYPE_NONE)
859 // skip all fade operations until specified fade operation
860 if (fade_type & fade_type_skip)
861 fade_type_skip = FADE_TYPE_NONE;
866 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
870 redraw_mask |= fade_mask;
872 if (fade_type == FADE_TYPE_SKIP)
874 fade_type_skip = fade_mode;
879 fade_delay = fading.fade_delay;
880 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
882 if (fade_type_skip != FADE_TYPE_NONE)
884 // skip all fade operations until specified fade operation
885 if (fade_type & fade_type_skip)
886 fade_type_skip = FADE_TYPE_NONE;
891 if (global.autoplay_leveldir)
896 if (fade_mask == REDRAW_FIELD)
901 height = FADE_SYSIZE;
903 if (border.draw_masked_when_fading)
904 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
906 DrawMaskedBorder_FIELD(); // draw once
916 // when switching screens without fading, set fade delay to zero
917 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
920 // do not display black frame when fading out without fade delay
921 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
924 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925 draw_border_function);
927 redraw_mask &= ~fade_mask;
929 ClearAutoRepeatKeyEvents();
932 static void SetScreenStates_BeforeFadingIn(void)
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
940 // set screen mode for animations back to fading
941 global.anim_status = GAME_MODE_PSEUDO_FADING;
944 static void SetScreenStates_AfterFadingIn(void)
946 // store new source screen (to use correct masked border for fading)
947 gfx.fade_border_source_status = global.border_status;
949 global.anim_status = global.anim_status_next;
952 static void SetScreenStates_BeforeFadingOut(void)
954 // store new target screen (to use correct masked border for fading)
955 gfx.fade_border_target_status = game_status;
957 // set screen mode for animations to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 // store backbuffer with all animations that will be stopped for fading out
961 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
964 static void SetScreenStates_AfterFadingOut(void)
966 global.border_status = game_status;
969 void FadeIn(int fade_mask)
971 SetScreenStates_BeforeFadingIn();
974 DrawMaskedBorder(REDRAW_ALL);
977 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
980 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
984 FADE_SXSIZE = FULL_SXSIZE;
985 FADE_SYSIZE = FULL_SYSIZE;
987 // activate virtual buttons depending on upcoming game status
988 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989 game_status == GAME_MODE_PLAYING && !tape.playing)
990 SetOverlayActive(TRUE);
992 SetScreenStates_AfterFadingIn();
994 // force update of global animation status in case of rapid screen changes
995 redraw_mask = REDRAW_ALL;
999 void FadeOut(int fade_mask)
1001 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1006 SetScreenStates_BeforeFadingOut();
1008 SetTileCursorActive(FALSE);
1009 SetOverlayActive(FALSE);
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1018 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1020 SetScreenStates_AfterFadingOut();
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1025 static struct TitleFadingInfo fading_leave_stored;
1028 fading_leave_stored = fading_leave;
1030 fading = fading_leave_stored;
1033 void FadeSetEnterMenu(void)
1035 fading = menu.enter_menu;
1037 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1040 void FadeSetLeaveMenu(void)
1042 fading = menu.leave_menu;
1044 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetEnterScreen(void)
1049 fading = menu.enter_screen[game_status];
1051 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1054 void FadeSetNextScreen(void)
1056 fading = menu.next_screen[game_status];
1058 // (do not overwrite fade mode set by FadeSetEnterScreen)
1059 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1062 void FadeSetLeaveScreen(void)
1064 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1067 void FadeSetFromType(int type)
1069 if (type & TYPE_ENTER_SCREEN)
1070 FadeSetEnterScreen();
1071 else if (type & TYPE_ENTER)
1073 else if (type & TYPE_LEAVE)
1077 void FadeSetDisabled(void)
1079 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1081 fading = fading_none;
1084 void FadeSkipNextFadeIn(void)
1086 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1089 void FadeSkipNextFadeOut(void)
1091 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1096 if (graphic == IMG_UNDEFINED)
1099 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1101 return (graphic_info[graphic].bitmap != NULL || redefined ?
1102 graphic_info[graphic].bitmap :
1103 graphic_info[default_graphic].bitmap);
1106 static Bitmap *getBackgroundBitmap(int graphic)
1108 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1113 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1119 (status == GAME_MODE_MAIN ||
1120 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1121 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1122 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1123 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1126 return getGlobalBorderBitmap(graphic);
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1131 if (graphic_info[graphic].bitmap)
1132 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1135 void SetMainBackgroundImageIfDefined(int graphic)
1137 if (graphic_info[graphic].bitmap)
1138 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1143 if (graphic_info[graphic].bitmap)
1144 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1147 void SetWindowBackgroundImage(int graphic)
1149 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetMainBackgroundImage(int graphic)
1154 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetDoorBackgroundImage(int graphic)
1159 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetPanelBackground(void)
1164 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1166 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1169 SetDoorBackgroundBitmap(bitmap_db_panel);
1172 void DrawBackground(int x, int y, int width, int height)
1174 // "drawto" might still point to playfield buffer here (hall of fame)
1175 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1177 if (IN_GFX_FIELD_FULL(x, y))
1178 redraw_mask |= REDRAW_FIELD;
1179 else if (IN_GFX_DOOR_1(x, y))
1180 redraw_mask |= REDRAW_DOOR_1;
1181 else if (IN_GFX_DOOR_2(x, y))
1182 redraw_mask |= REDRAW_DOOR_2;
1183 else if (IN_GFX_DOOR_3(x, y))
1184 redraw_mask |= REDRAW_DOOR_3;
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1189 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1191 if (font->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1199 struct GraphicInfo *g = &graphic_info[graphic];
1201 if (g->bitmap == NULL)
1204 DrawBackground(x, y, width, height);
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1219 boolean CheckIfGlobalBorderHasChanged(void)
1221 // if game status has not changed, global border has not changed either
1222 if (game_status == game_status_last)
1225 // determine and store new global border bitmap for current game status
1226 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1228 return (global_border_bitmap_last != global_border_bitmap);
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1236 // if game status has not changed, nothing has to be redrawn
1237 if (game_status == game_status_last)
1240 // redraw if last screen was title screen
1241 if (game_status_last == GAME_MODE_TITLE)
1244 // redraw if global screen border has changed
1245 if (CheckIfGlobalBorderHasChanged())
1248 // redraw if position or size of playfield area has changed
1249 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1253 // redraw if position or size of door area has changed
1254 if (dx_last != DX || dy_last != DY ||
1255 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1258 // redraw if position or size of tape area has changed
1259 if (vx_last != VX || vy_last != VY ||
1260 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1263 // redraw if position or size of editor area has changed
1264 if (ex_last != EX || ey_last != EY ||
1265 exsize_last != EXSIZE || eysize_last != EYSIZE)
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1275 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1277 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1280 void RedrawGlobalBorder(void)
1282 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 RedrawGlobalBorderFromBitmap(bitmap);
1286 redraw_mask = REDRAW_ALL;
1289 static void RedrawGlobalBorderIfNeeded(void)
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 if (game_status == game_status_last)
1296 // copy current draw buffer to later copy back areas that have not changed
1297 if (game_status_last != GAME_MODE_TITLE)
1298 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301 if (CheckIfGlobalBorderRedrawIsNeeded())
1303 // determine and store new global border bitmap for current game status
1304 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1307 // redraw global screen border (or clear, if defined to be empty)
1308 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1310 if (game_status == GAME_MODE_EDITOR)
1311 DrawSpecialEditorDoor();
1313 // copy previous playfield and door areas, if they are defined on both
1314 // previous and current screen and if they still have the same size
1316 if (real_sx_last != -1 && real_sy_last != -1 &&
1317 REAL_SX != -1 && REAL_SY != -1 &&
1318 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1323 if (dx_last != -1 && dy_last != -1 &&
1324 DX != -1 && DY != -1 &&
1325 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326 BlitBitmap(bitmap_db_store_1, backbuffer,
1327 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1329 if (game_status != GAME_MODE_EDITOR)
1331 if (vx_last != -1 && vy_last != -1 &&
1332 VX != -1 && VY != -1 &&
1333 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334 BlitBitmap(bitmap_db_store_1, backbuffer,
1335 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1339 if (ex_last != -1 && ey_last != -1 &&
1340 EX != -1 && EY != -1 &&
1341 exsize_last == EXSIZE && eysize_last == EYSIZE)
1342 BlitBitmap(bitmap_db_store_1, backbuffer,
1343 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1346 redraw_mask = REDRAW_ALL;
1349 game_status_last = game_status;
1351 global_border_bitmap_last = global_border_bitmap;
1353 real_sx_last = REAL_SX;
1354 real_sy_last = REAL_SY;
1355 full_sxsize_last = FULL_SXSIZE;
1356 full_sysize_last = FULL_SYSIZE;
1359 dxsize_last = DXSIZE;
1360 dysize_last = DYSIZE;
1363 vxsize_last = VXSIZE;
1364 vysize_last = VYSIZE;
1367 exsize_last = EXSIZE;
1368 eysize_last = EYSIZE;
1371 void ClearField(void)
1373 RedrawGlobalBorderIfNeeded();
1375 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376 // (when entering hall of fame after playing)
1377 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1379 // !!! maybe this should be done before clearing the background !!!
1380 if (game_status == GAME_MODE_PLAYING)
1382 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1387 SetDrawtoField(DRAW_TO_BACKBUFFER);
1391 void MarkTileDirty(int x, int y)
1393 redraw_mask |= REDRAW_FIELD;
1396 void SetBorderElement(void)
1400 BorderElement = EL_EMPTY;
1402 // only the R'n'D game engine may use an additional steelwall border
1403 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1406 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1408 for (x = 0; x < lev_fieldx; x++)
1410 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411 BorderElement = EL_STEELWALL;
1413 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1419 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1420 int max_array_fieldx, int max_array_fieldy,
1421 short field[max_array_fieldx][max_array_fieldy],
1422 int max_fieldx, int max_fieldy)
1424 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1425 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1426 int old_element = field[start_x][start_y];
1429 // do nothing if start field already has the desired content
1430 if (old_element == fill_element)
1433 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1435 while (stack_pos > 0)
1437 struct XY current = stack_buffer[--stack_pos];
1440 field[current.x][current.y] = fill_element;
1442 for (i = 0; i < 4; i++)
1444 int x = current.x + check[i].x;
1445 int y = current.y + check[i].y;
1447 // check for stack buffer overflow (should not happen)
1448 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1449 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1451 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1452 stack_buffer[stack_pos++] = (struct XY){ x, y };
1457 void FloodFillLevel(int from_x, int from_y, int fill_element,
1458 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1459 int max_fieldx, int max_fieldy)
1461 FloodFillLevelExt(from_x, from_y, fill_element,
1462 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1463 max_fieldx, max_fieldy);
1466 void SetRandomAnimationValue(int x, int y)
1468 gfx.anim_random_frame = GfxRandom[x][y];
1471 int getGraphicAnimationFrame(int graphic, int sync_frame)
1473 // animation synchronized with global frame counter, not move position
1474 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1475 sync_frame = FrameCounter;
1477 return getAnimationFrame(graphic_info[graphic].anim_frames,
1478 graphic_info[graphic].anim_delay,
1479 graphic_info[graphic].anim_mode,
1480 graphic_info[graphic].anim_start_frame,
1484 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1486 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1488 struct GraphicInfo *g = &graphic_info[graphic];
1489 int xsize = MAX(1, g->anim_frames_per_line);
1490 int ysize = MAX(1, g->anim_frames / xsize);
1491 int xoffset = g->anim_start_frame % xsize;
1492 int yoffset = g->anim_start_frame % ysize;
1493 int x = (lx + xoffset + xsize) % xsize;
1494 int y = (ly + yoffset + ysize) % ysize;
1495 int sync_frame = y * xsize + x;
1497 return sync_frame % g->anim_frames;
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 = getGraphicAnimationFrameXY(graphic, lx, ly);
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 if (ANIM_MODE(graphic) & ANIM_TILED)
3957 DrawGraphicAnimation(sx, sy, graphic);
3960 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3961 DrawLevelFieldCrumbled(x, y);
3963 if (GFX_CRUMBLED(Tile[x][y]))
3964 DrawLevelFieldCrumbled(x, y);
3968 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3970 int sx = SCREENX(x), sy = SCREENY(y);
3973 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3976 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3978 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3981 DrawGraphicAnimation(sx, sy, graphic);
3983 if (GFX_CRUMBLED(element))
3984 DrawLevelFieldCrumbled(x, y);
3987 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3989 if (player->use_murphy)
3991 // this works only because currently only one player can be "murphy" ...
3992 static int last_horizontal_dir = MV_LEFT;
3993 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3995 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3996 last_horizontal_dir = move_dir;
3998 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4000 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4002 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4008 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4011 static boolean equalGraphics(int graphic1, int graphic2)
4013 struct GraphicInfo *g1 = &graphic_info[graphic1];
4014 struct GraphicInfo *g2 = &graphic_info[graphic2];
4016 return (g1->bitmap == g2->bitmap &&
4017 g1->src_x == g2->src_x &&
4018 g1->src_y == g2->src_y &&
4019 g1->anim_frames == g2->anim_frames &&
4020 g1->anim_delay == g2->anim_delay &&
4021 g1->anim_mode == g2->anim_mode);
4024 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4028 DRAW_PLAYER_STAGE_INIT = 0,
4029 DRAW_PLAYER_STAGE_LAST_FIELD,
4030 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4031 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4032 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4033 DRAW_PLAYER_STAGE_PLAYER,
4035 DRAW_PLAYER_STAGE_PLAYER,
4036 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4038 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4039 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4041 NUM_DRAW_PLAYER_STAGES
4044 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4046 static int static_last_player_graphic[MAX_PLAYERS];
4047 static int static_last_player_frame[MAX_PLAYERS];
4048 static boolean static_player_is_opaque[MAX_PLAYERS];
4049 static boolean draw_player[MAX_PLAYERS];
4050 int pnr = player->index_nr;
4052 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4054 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4055 static_last_player_frame[pnr] = player->Frame;
4056 static_player_is_opaque[pnr] = FALSE;
4058 draw_player[pnr] = TRUE;
4061 if (!draw_player[pnr])
4065 if (!IN_LEV_FIELD(player->jx, player->jy))
4067 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4068 Debug("draw:DrawPlayerExt", "This should never happen!");
4070 draw_player[pnr] = FALSE;
4076 int last_player_graphic = static_last_player_graphic[pnr];
4077 int last_player_frame = static_last_player_frame[pnr];
4078 boolean player_is_opaque = static_player_is_opaque[pnr];
4080 int jx = player->jx;
4081 int jy = player->jy;
4082 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4083 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4084 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4085 int last_jx = (player->is_moving ? jx - dx : jx);
4086 int last_jy = (player->is_moving ? jy - dy : jy);
4087 int next_jx = jx + dx;
4088 int next_jy = jy + dy;
4089 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4090 int sx = SCREENX(jx);
4091 int sy = SCREENY(jy);
4092 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4093 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4094 int element = Tile[jx][jy];
4095 int last_element = Tile[last_jx][last_jy];
4096 int action = (player->is_pushing ? ACTION_PUSHING :
4097 player->is_digging ? ACTION_DIGGING :
4098 player->is_collecting ? ACTION_COLLECTING :
4099 player->is_moving ? ACTION_MOVING :
4100 player->is_snapping ? ACTION_SNAPPING :
4101 player->is_dropping ? ACTION_DROPPING :
4102 player->is_waiting ? player->action_waiting :
4105 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4107 // ------------------------------------------------------------------------
4108 // initialize drawing the player
4109 // ------------------------------------------------------------------------
4111 draw_player[pnr] = FALSE;
4113 // GfxElement[][] is set to the element the player is digging or collecting;
4114 // remove also for off-screen player if the player is not moving anymore
4115 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4116 GfxElement[jx][jy] = EL_UNDEFINED;
4118 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4121 if (element == EL_EXPLOSION)
4124 InitPlayerGfxAnimation(player, action, move_dir);
4126 draw_player[pnr] = TRUE;
4128 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4130 // ------------------------------------------------------------------------
4131 // draw things in the field the player is leaving, if needed
4132 // ------------------------------------------------------------------------
4134 if (!IN_SCR_FIELD(sx, sy))
4135 draw_player[pnr] = FALSE;
4137 if (!player->is_moving)
4140 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4142 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4144 if (last_element == EL_DYNAMITE_ACTIVE ||
4145 last_element == EL_EM_DYNAMITE_ACTIVE ||
4146 last_element == EL_SP_DISK_RED_ACTIVE)
4147 DrawDynamite(last_jx, last_jy);
4149 DrawLevelFieldThruMask(last_jx, last_jy);
4151 else if (last_element == EL_DYNAMITE_ACTIVE ||
4152 last_element == EL_EM_DYNAMITE_ACTIVE ||
4153 last_element == EL_SP_DISK_RED_ACTIVE)
4154 DrawDynamite(last_jx, last_jy);
4156 DrawLevelField(last_jx, last_jy);
4158 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4159 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4161 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4163 // ------------------------------------------------------------------------
4164 // draw things behind the player, if needed
4165 // ------------------------------------------------------------------------
4169 DrawLevelElement(jx, jy, Back[jx][jy]);
4174 if (IS_ACTIVE_BOMB(element))
4176 DrawLevelElement(jx, jy, EL_EMPTY);
4181 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4183 int old_element = GfxElement[jx][jy];
4184 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4185 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4187 if (GFX_CRUMBLED(old_element))
4188 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4190 DrawScreenGraphic(sx, sy, old_graphic, frame);
4192 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4193 static_player_is_opaque[pnr] = TRUE;
4197 GfxElement[jx][jy] = EL_UNDEFINED;
4199 // make sure that pushed elements are drawn with correct frame rate
4200 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4202 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4203 GfxFrame[jx][jy] = player->StepFrame;
4205 DrawLevelField(jx, jy);
4208 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4210 // ------------------------------------------------------------------------
4211 // draw things the player is pushing, if needed
4212 // ------------------------------------------------------------------------
4214 if (!player->is_pushing || !player->is_moving)
4217 int gfx_frame = GfxFrame[jx][jy];
4219 if (!IS_MOVING(jx, jy)) // push movement already finished
4221 element = Tile[next_jx][next_jy];
4222 gfx_frame = GfxFrame[next_jx][next_jy];
4225 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4226 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4227 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4229 // draw background element under pushed element (like the Sokoban field)
4230 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4232 // this allows transparent pushing animation over non-black background
4235 DrawLevelElement(jx, jy, Back[jx][jy]);
4237 DrawLevelElement(jx, jy, EL_EMPTY);
4239 if (Back[next_jx][next_jy])
4240 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4242 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4244 else if (Back[next_jx][next_jy])
4245 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4247 int px = SCREENX(jx), py = SCREENY(jy);
4248 int pxx = (TILEX - ABS(sxx)) * dx;
4249 int pyy = (TILEY - ABS(syy)) * dy;
4252 // do not draw (EM style) pushing animation when pushing is finished
4253 // (two-tile animations usually do not contain start and end frame)
4254 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4255 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4257 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4259 // masked drawing is needed for EMC style (double) movement graphics
4260 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4261 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4264 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4266 // ------------------------------------------------------------------------
4267 // draw player himself
4268 // ------------------------------------------------------------------------
4270 int graphic = getPlayerGraphic(player, move_dir);
4272 // in the case of changed player action or direction, prevent the current
4273 // animation frame from being restarted for identical animations
4274 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4275 player->Frame = last_player_frame;
4277 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4279 if (player_is_opaque)
4280 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4282 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4284 if (SHIELD_ON(player))
4286 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4287 IMG_SHIELD_NORMAL_ACTIVE);
4288 frame = getGraphicAnimationFrame(graphic, -1);
4290 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4293 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4295 // ------------------------------------------------------------------------
4296 // draw things in front of player (active dynamite or dynabombs)
4297 // ------------------------------------------------------------------------
4299 if (IS_ACTIVE_BOMB(element))
4301 int graphic = el2img(element);
4302 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4304 if (game.emulation == EMU_SUPAPLEX)
4305 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4307 DrawGraphicThruMask(sx, sy, graphic, frame);
4310 if (player_is_moving && last_element == EL_EXPLOSION)
4312 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4313 GfxElement[last_jx][last_jy] : EL_EMPTY);
4314 int graphic = el_act2img(element, ACTION_EXPLODING);
4315 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4316 int phase = ExplodePhase[last_jx][last_jy] - 1;
4317 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4320 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4323 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4325 // ------------------------------------------------------------------------
4326 // draw elements the player is just walking/passing through/under
4327 // ------------------------------------------------------------------------
4329 if (player_is_moving)
4331 // handle the field the player is leaving ...
4332 if (IS_ACCESSIBLE_INSIDE(last_element))
4333 DrawLevelField(last_jx, last_jy);
4334 else if (IS_ACCESSIBLE_UNDER(last_element))
4335 DrawLevelFieldThruMask(last_jx, last_jy);
4338 // do not redraw accessible elements if the player is just pushing them
4339 if (!player_is_moving || !player->is_pushing)
4341 // ... and the field the player is entering
4342 if (IS_ACCESSIBLE_INSIDE(element))
4343 DrawLevelField(jx, jy);
4344 else if (IS_ACCESSIBLE_UNDER(element))
4345 DrawLevelFieldThruMask(jx, jy);
4348 MarkTileDirty(sx, sy);
4352 void DrawPlayer(struct PlayerInfo *player)
4356 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4357 DrawPlayerExt(player, i);
4360 void DrawAllPlayers(void)
4364 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4365 for (j = 0; j < MAX_PLAYERS; j++)
4366 if (stored_player[j].active)
4367 DrawPlayerExt(&stored_player[j], i);
4370 void DrawPlayerField(int x, int y)
4372 if (!IS_PLAYER(x, y))
4375 DrawPlayer(PLAYERINFO(x, y));
4378 // ----------------------------------------------------------------------------
4380 void WaitForEventToContinue(void)
4382 boolean first_wait = TRUE;
4383 boolean still_wait = TRUE;
4385 if (program.headless)
4388 // simulate releasing mouse button over last gadget, if still pressed
4390 HandleGadgets(-1, -1, 0);
4392 button_status = MB_RELEASED;
4395 ClearPlayerAction();
4401 if (NextValidEvent(&event))
4405 case EVENT_BUTTONPRESS:
4406 case EVENT_FINGERPRESS:
4410 case EVENT_BUTTONRELEASE:
4411 case EVENT_FINGERRELEASE:
4412 still_wait = first_wait;
4415 case EVENT_KEYPRESS:
4416 case SDL_CONTROLLERBUTTONDOWN:
4417 case SDL_JOYBUTTONDOWN:
4422 HandleOtherEvents(&event);
4426 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4431 if (!PendingEvent())
4436 #define MAX_REQUEST_LINES 13
4437 #define MAX_REQUEST_LINE_FONT1_LEN 7
4438 #define MAX_REQUEST_LINE_FONT2_LEN 10
4440 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4442 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4444 int draw_buffer_last = GetDrawtoField();
4445 int width = request.width;
4446 int height = request.height;
4450 // when showing request dialog after game ended, deactivate game panel
4451 if (game_just_ended)
4452 game.panel.active = FALSE;
4454 game.request_active = TRUE;
4456 setRequestPosition(&sx, &sy, FALSE);
4458 button_status = MB_RELEASED;
4460 request_gadget_id = -1;
4465 boolean event_handled = FALSE;
4467 if (game_just_ended)
4469 SetDrawtoField(draw_buffer_game);
4471 HandleGameActions();
4473 SetDrawtoField(DRAW_TO_BACKBUFFER);
4475 if (global.use_envelope_request)
4477 // copy current state of request area to middle of playfield area
4478 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4486 while (NextValidEvent(&event))
4488 event_handled = TRUE;
4492 case EVENT_BUTTONPRESS:
4493 case EVENT_BUTTONRELEASE:
4494 case EVENT_MOTIONNOTIFY:
4498 if (event.type == EVENT_MOTIONNOTIFY)
4503 motion_status = TRUE;
4504 mx = ((MotionEvent *) &event)->x;
4505 my = ((MotionEvent *) &event)->y;
4509 motion_status = FALSE;
4510 mx = ((ButtonEvent *) &event)->x;
4511 my = ((ButtonEvent *) &event)->y;
4512 if (event.type == EVENT_BUTTONPRESS)
4513 button_status = ((ButtonEvent *) &event)->button;
4515 button_status = MB_RELEASED;
4518 // this sets 'request_gadget_id'
4519 HandleGadgets(mx, my, button_status);
4521 switch (request_gadget_id)
4523 case TOOL_CTRL_ID_YES:
4524 case TOOL_CTRL_ID_TOUCH_YES:
4527 case TOOL_CTRL_ID_NO:
4528 case TOOL_CTRL_ID_TOUCH_NO:
4531 case TOOL_CTRL_ID_CONFIRM:
4532 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4533 result = TRUE | FALSE;
4536 case TOOL_CTRL_ID_PLAYER_1:
4539 case TOOL_CTRL_ID_PLAYER_2:
4542 case TOOL_CTRL_ID_PLAYER_3:
4545 case TOOL_CTRL_ID_PLAYER_4:
4550 // only check clickable animations if no request gadget clicked
4551 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4558 case SDL_WINDOWEVENT:
4559 HandleWindowEvent((WindowEvent *) &event);
4562 case SDL_APP_WILLENTERBACKGROUND:
4563 case SDL_APP_DIDENTERBACKGROUND:
4564 case SDL_APP_WILLENTERFOREGROUND:
4565 case SDL_APP_DIDENTERFOREGROUND:
4566 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4569 case EVENT_KEYPRESS:
4571 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4576 if (req_state & REQ_CONFIRM)
4585 #if defined(KSYM_Rewind)
4586 case KSYM_Rewind: // for Amazon Fire TV remote
4595 #if defined(KSYM_FastForward)
4596 case KSYM_FastForward: // for Amazon Fire TV remote
4602 HandleKeysDebug(key, KEY_PRESSED);
4606 if (req_state & REQ_PLAYER)
4608 int old_player_nr = setup.network_player_nr;
4611 result = old_player_nr + 1;
4616 result = old_player_nr + 1;
4647 case EVENT_FINGERRELEASE:
4648 case EVENT_KEYRELEASE:
4649 ClearPlayerAction();
4652 case SDL_CONTROLLERBUTTONDOWN:
4653 switch (event.cbutton.button)
4655 case SDL_CONTROLLER_BUTTON_A:
4656 case SDL_CONTROLLER_BUTTON_X:
4657 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4658 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4662 case SDL_CONTROLLER_BUTTON_B:
4663 case SDL_CONTROLLER_BUTTON_Y:
4664 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4665 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4666 case SDL_CONTROLLER_BUTTON_BACK:
4671 if (req_state & REQ_PLAYER)
4673 int old_player_nr = setup.network_player_nr;
4676 result = old_player_nr + 1;
4678 switch (event.cbutton.button)
4680 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4681 case SDL_CONTROLLER_BUTTON_Y:
4685 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4686 case SDL_CONTROLLER_BUTTON_B:
4690 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4691 case SDL_CONTROLLER_BUTTON_A:
4695 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4696 case SDL_CONTROLLER_BUTTON_X:
4707 case SDL_CONTROLLERBUTTONUP:
4708 HandleJoystickEvent(&event);
4709 ClearPlayerAction();
4713 HandleOtherEvents(&event);
4718 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4720 int joy = AnyJoystick();
4722 if (joy & JOY_BUTTON_1)
4724 else if (joy & JOY_BUTTON_2)
4727 else if (AnyJoystick())
4729 int joy = AnyJoystick();
4731 if (req_state & REQ_PLAYER)
4735 else if (joy & JOY_RIGHT)
4737 else if (joy & JOY_DOWN)
4739 else if (joy & JOY_LEFT)
4746 if (game_just_ended)
4748 if (global.use_envelope_request)
4750 // copy back current state of pressed buttons inside request area
4751 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4755 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4761 SetDrawtoField(draw_buffer_last);
4763 game.request_active = FALSE;
4768 static boolean RequestDoor(char *text, unsigned int req_state)
4770 int draw_buffer_last = GetDrawtoField();
4771 unsigned int old_door_state;
4772 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4773 int font_nr = FONT_TEXT_2;
4778 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4780 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4781 font_nr = FONT_TEXT_1;
4784 if (game_status == GAME_MODE_PLAYING)
4785 BlitScreenToBitmap(backbuffer);
4787 // disable deactivated drawing when quick-loading level tape recording
4788 if (tape.playing && tape.deactivate_display)
4789 TapeDeactivateDisplayOff(TRUE);
4791 SetMouseCursor(CURSOR_DEFAULT);
4793 // pause network game while waiting for request to answer
4794 if (network.enabled &&
4795 game_status == GAME_MODE_PLAYING &&
4796 !game.all_players_gone &&
4797 req_state & REQUEST_WAIT_FOR_INPUT)
4798 SendToServer_PausePlaying();
4800 old_door_state = GetDoorState();
4802 // simulate releasing mouse button over last gadget, if still pressed
4804 HandleGadgets(-1, -1, 0);
4808 // draw released gadget before proceeding
4811 if (old_door_state & DOOR_OPEN_1)
4813 CloseDoor(DOOR_CLOSE_1);
4815 // save old door content
4816 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4817 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4820 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4821 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4823 // clear door drawing field
4824 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4826 // force DOOR font inside door area
4827 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4829 // write text for request
4830 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4832 char text_line[max_request_line_len + 1];
4838 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4840 tc = *(text_ptr + tx);
4841 // if (!tc || tc == ' ')
4842 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4846 if ((tc == '?' || tc == '!') && tl == 0)
4856 strncpy(text_line, text_ptr, tl);
4859 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4860 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4861 text_line, font_nr);
4863 text_ptr += tl + (tc == ' ' ? 1 : 0);
4864 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4869 if (req_state & REQ_ASK)
4871 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4872 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4873 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4874 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4876 else if (req_state & REQ_CONFIRM)
4878 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4879 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4881 else if (req_state & REQ_PLAYER)
4883 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4884 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4885 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4886 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4889 // copy request gadgets to door backbuffer
4890 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4892 OpenDoor(DOOR_OPEN_1);
4894 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4896 if (game_status == GAME_MODE_PLAYING)
4898 SetPanelBackground();
4899 SetDrawBackgroundMask(REDRAW_DOOR_1);
4903 SetDrawBackgroundMask(REDRAW_FIELD);
4909 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4911 // ---------- handle request buttons ----------
4912 result = RequestHandleEvents(req_state, draw_buffer_last);
4916 if (!(req_state & REQ_STAY_OPEN))
4918 CloseDoor(DOOR_CLOSE_1);
4920 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4921 (req_state & REQ_REOPEN))
4922 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4927 if (game_status == GAME_MODE_PLAYING)
4929 SetPanelBackground();
4930 SetDrawBackgroundMask(REDRAW_DOOR_1);
4934 SetDrawBackgroundMask(REDRAW_FIELD);
4937 // continue network game after request
4938 if (network.enabled &&
4939 game_status == GAME_MODE_PLAYING &&
4940 !game.all_players_gone &&
4941 req_state & REQUEST_WAIT_FOR_INPUT)
4942 SendToServer_ContinuePlaying();
4944 // restore deactivated drawing when quick-loading level tape recording
4945 if (tape.playing && tape.deactivate_display)
4946 TapeDeactivateDisplayOn();
4951 static boolean RequestEnvelope(char *text, unsigned int req_state)
4953 int draw_buffer_last = GetDrawtoField();
4956 if (game_status == GAME_MODE_PLAYING)
4957 BlitScreenToBitmap(backbuffer);
4959 // disable deactivated drawing when quick-loading level tape recording
4960 if (tape.playing && tape.deactivate_display)
4961 TapeDeactivateDisplayOff(TRUE);
4963 SetMouseCursor(CURSOR_DEFAULT);
4965 // pause network game while waiting for request to answer
4966 if (network.enabled &&
4967 game_status == GAME_MODE_PLAYING &&
4968 !game.all_players_gone &&
4969 req_state & REQUEST_WAIT_FOR_INPUT)
4970 SendToServer_PausePlaying();
4972 // simulate releasing mouse button over last gadget, if still pressed
4974 HandleGadgets(-1, -1, 0);
4978 // (replace with setting corresponding request background)
4979 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4980 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4982 // clear door drawing field
4983 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4985 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4987 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4989 if (game_status == GAME_MODE_PLAYING)
4991 SetPanelBackground();
4992 SetDrawBackgroundMask(REDRAW_DOOR_1);
4996 SetDrawBackgroundMask(REDRAW_FIELD);
5002 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5004 // ---------- handle request buttons ----------
5005 result = RequestHandleEvents(req_state, draw_buffer_last);
5009 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5013 if (game_status == GAME_MODE_PLAYING)
5015 SetPanelBackground();
5016 SetDrawBackgroundMask(REDRAW_DOOR_1);
5020 SetDrawBackgroundMask(REDRAW_FIELD);
5023 // continue network game after request
5024 if (network.enabled &&
5025 game_status == GAME_MODE_PLAYING &&
5026 !game.all_players_gone &&
5027 req_state & REQUEST_WAIT_FOR_INPUT)
5028 SendToServer_ContinuePlaying();
5030 // restore deactivated drawing when quick-loading level tape recording
5031 if (tape.playing && tape.deactivate_display)
5032 TapeDeactivateDisplayOn();
5037 boolean Request(char *text, unsigned int req_state)
5039 boolean overlay_enabled = GetOverlayEnabled();
5042 game.request_active_or_moving = TRUE;
5044 SetOverlayEnabled(FALSE);
5046 if (global.use_envelope_request)
5047 result = RequestEnvelope(text, req_state);
5049 result = RequestDoor(text, req_state);
5051 SetOverlayEnabled(overlay_enabled);
5053 game.request_active_or_moving = FALSE;
5058 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5060 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5061 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5064 if (dpo1->sort_priority != dpo2->sort_priority)
5065 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5067 compare_result = dpo1->nr - dpo2->nr;
5069 return compare_result;
5072 void InitGraphicCompatibilityInfo_Doors(void)
5078 struct DoorInfo *door;
5082 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5083 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5085 { -1, -1, -1, NULL }
5087 struct Rect door_rect_list[] =
5089 { DX, DY, DXSIZE, DYSIZE },
5090 { VX, VY, VXSIZE, VYSIZE }
5094 for (i = 0; doors[i].door_token != -1; i++)
5096 int door_token = doors[i].door_token;
5097 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5098 int part_1 = doors[i].part_1;
5099 int part_8 = doors[i].part_8;
5100 int part_2 = part_1 + 1;
5101 int part_3 = part_1 + 2;
5102 struct DoorInfo *door = doors[i].door;
5103 struct Rect *door_rect = &door_rect_list[door_index];
5104 boolean door_gfx_redefined = FALSE;
5106 // check if any door part graphic definitions have been redefined
5108 for (j = 0; door_part_controls[j].door_token != -1; j++)
5110 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5111 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5113 if (dpc->door_token == door_token && fi->redefined)
5114 door_gfx_redefined = TRUE;
5117 // check for old-style door graphic/animation modifications
5119 if (!door_gfx_redefined)
5121 if (door->anim_mode & ANIM_STATIC_PANEL)
5123 door->panel.step_xoffset = 0;
5124 door->panel.step_yoffset = 0;
5127 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5129 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5130 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5131 int num_door_steps, num_panel_steps;
5133 // remove door part graphics other than the two default wings
5135 for (j = 0; door_part_controls[j].door_token != -1; j++)
5137 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5138 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5140 if (dpc->graphic >= part_3 &&
5141 dpc->graphic <= part_8)
5145 // set graphics and screen positions of the default wings
5147 g_part_1->width = door_rect->width;
5148 g_part_1->height = door_rect->height;
5149 g_part_2->width = door_rect->width;
5150 g_part_2->height = door_rect->height;
5151 g_part_2->src_x = door_rect->width;
5152 g_part_2->src_y = g_part_1->src_y;
5154 door->part_2.x = door->part_1.x;
5155 door->part_2.y = door->part_1.y;
5157 if (door->width != -1)
5159 g_part_1->width = door->width;
5160 g_part_2->width = door->width;
5162 // special treatment for graphics and screen position of right wing
5163 g_part_2->src_x += door_rect->width - door->width;
5164 door->part_2.x += door_rect->width - door->width;
5167 if (door->height != -1)
5169 g_part_1->height = door->height;
5170 g_part_2->height = door->height;
5172 // special treatment for graphics and screen position of bottom wing
5173 g_part_2->src_y += door_rect->height - door->height;
5174 door->part_2.y += door_rect->height - door->height;
5177 // set animation delays for the default wings and panels
5179 door->part_1.step_delay = door->step_delay;
5180 door->part_2.step_delay = door->step_delay;
5181 door->panel.step_delay = door->step_delay;
5183 // set animation draw order for the default wings
5185 door->part_1.sort_priority = 2; // draw left wing over ...
5186 door->part_2.sort_priority = 1; // ... right wing
5188 // set animation draw offset for the default wings
5190 if (door->anim_mode & ANIM_HORIZONTAL)
5192 door->part_1.step_xoffset = door->step_offset;
5193 door->part_1.step_yoffset = 0;
5194 door->part_2.step_xoffset = door->step_offset * -1;
5195 door->part_2.step_yoffset = 0;
5197 num_door_steps = g_part_1->width / door->step_offset;
5199 else // ANIM_VERTICAL
5201 door->part_1.step_xoffset = 0;
5202 door->part_1.step_yoffset = door->step_offset;
5203 door->part_2.step_xoffset = 0;
5204 door->part_2.step_yoffset = door->step_offset * -1;
5206 num_door_steps = g_part_1->height / door->step_offset;
5209 // set animation draw offset for the default panels
5211 if (door->step_offset > 1)
5213 num_panel_steps = 2 * door_rect->height / door->step_offset;
5214 door->panel.start_step = num_panel_steps - num_door_steps;
5215 door->panel.start_step_closing = door->panel.start_step;
5219 num_panel_steps = door_rect->height / door->step_offset;
5220 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5221 door->panel.start_step_closing = door->panel.start_step;
5222 door->panel.step_delay *= 2;
5229 void InitDoors(void)
5233 for (i = 0; door_part_controls[i].door_token != -1; i++)
5235 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5236 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5238 // initialize "start_step_opening" and "start_step_closing", if needed
5239 if (dpc->pos->start_step_opening == 0 &&
5240 dpc->pos->start_step_closing == 0)
5242 // dpc->pos->start_step_opening = dpc->pos->start_step;
5243 dpc->pos->start_step_closing = dpc->pos->start_step;
5246 // fill structure for door part draw order (sorted below)
5248 dpo->sort_priority = dpc->pos->sort_priority;
5251 // sort door part controls according to sort_priority and graphic number
5252 qsort(door_part_order, MAX_DOOR_PARTS,
5253 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5256 unsigned int OpenDoor(unsigned int door_state)
5258 if (door_state & DOOR_COPY_BACK)
5260 if (door_state & DOOR_OPEN_1)
5261 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5262 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5264 if (door_state & DOOR_OPEN_2)
5265 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5266 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5268 door_state &= ~DOOR_COPY_BACK;
5271 return MoveDoor(door_state);
5274 unsigned int CloseDoor(unsigned int door_state)
5276 unsigned int old_door_state = GetDoorState();
5278 if (!(door_state & DOOR_NO_COPY_BACK))
5280 if (old_door_state & DOOR_OPEN_1)
5281 BlitBitmap(backbuffer, bitmap_db_door_1,
5282 DX, DY, DXSIZE, DYSIZE, 0, 0);
5284 if (old_door_state & DOOR_OPEN_2)
5285 BlitBitmap(backbuffer, bitmap_db_door_2,
5286 VX, VY, VXSIZE, VYSIZE, 0, 0);
5288 door_state &= ~DOOR_NO_COPY_BACK;
5291 return MoveDoor(door_state);
5294 unsigned int GetDoorState(void)
5296 return MoveDoor(DOOR_GET_STATE);
5299 unsigned int SetDoorState(unsigned int door_state)
5301 return MoveDoor(door_state | DOOR_SET_STATE);
5304 static int euclid(int a, int b)
5306 return (b ? euclid(b, a % b) : a);
5309 unsigned int MoveDoor(unsigned int door_state)
5311 struct Rect door_rect_list[] =
5313 { DX, DY, DXSIZE, DYSIZE },
5314 { VX, VY, VXSIZE, VYSIZE }
5316 static int door1 = DOOR_CLOSE_1;
5317 static int door2 = DOOR_CLOSE_2;
5318 unsigned int door_delay = 0;
5319 unsigned int door_delay_value;
5322 if (door_state == DOOR_GET_STATE)
5323 return (door1 | door2);
5325 if (door_state & DOOR_SET_STATE)
5327 if (door_state & DOOR_ACTION_1)
5328 door1 = door_state & DOOR_ACTION_1;
5329 if (door_state & DOOR_ACTION_2)
5330 door2 = door_state & DOOR_ACTION_2;
5332 return (door1 | door2);
5335 if (!(door_state & DOOR_FORCE_REDRAW))
5337 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5338 door_state &= ~DOOR_OPEN_1;
5339 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5340 door_state &= ~DOOR_CLOSE_1;
5341 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5342 door_state &= ~DOOR_OPEN_2;
5343 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5344 door_state &= ~DOOR_CLOSE_2;
5347 if (global.autoplay_leveldir)
5349 door_state |= DOOR_NO_DELAY;
5350 door_state &= ~DOOR_CLOSE_ALL;
5353 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5354 door_state |= DOOR_NO_DELAY;
5356 if (door_state & DOOR_ACTION)
5358 boolean door_panel_drawn[NUM_DOORS];
5359 boolean panel_has_doors[NUM_DOORS];
5360 boolean door_part_skip[MAX_DOOR_PARTS];
5361 boolean door_part_done[MAX_DOOR_PARTS];
5362 boolean door_part_done_all;
5363 int num_steps[MAX_DOOR_PARTS];
5364 int max_move_delay = 0; // delay for complete animations of all doors
5365 int max_step_delay = 0; // delay (ms) between two animation frames
5366 int num_move_steps = 0; // number of animation steps for all doors
5367 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5368 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5369 int current_move_delay = 0;
5373 for (i = 0; i < NUM_DOORS; i++)
5374 panel_has_doors[i] = FALSE;
5376 for (i = 0; i < MAX_DOOR_PARTS; i++)
5378 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5379 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5380 int door_token = dpc->door_token;
5382 door_part_done[i] = FALSE;
5383 door_part_skip[i] = (!(door_state & door_token) ||
5387 for (i = 0; i < MAX_DOOR_PARTS; i++)
5389 int nr = door_part_order[i].nr;
5390 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5391 struct DoorPartPosInfo *pos = dpc->pos;
5392 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5393 int door_token = dpc->door_token;
5394 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5395 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5396 int step_xoffset = ABS(pos->step_xoffset);
5397 int step_yoffset = ABS(pos->step_yoffset);
5398 int step_delay = pos->step_delay;
5399 int current_door_state = door_state & door_token;
5400 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5401 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5402 boolean part_opening = (is_panel ? door_closing : door_opening);
5403 int start_step = (part_opening ? pos->start_step_opening :
5404 pos->start_step_closing);
5405 float move_xsize = (step_xoffset ? g->width : 0);
5406 float move_ysize = (step_yoffset ? g->height : 0);
5407 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5408 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5409 int move_steps = (move_xsteps && move_ysteps ?
5410 MIN(move_xsteps, move_ysteps) :
5411 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5412 int move_delay = move_steps * step_delay;
5414 if (door_part_skip[nr])
5417 max_move_delay = MAX(max_move_delay, move_delay);
5418 max_step_delay = (max_step_delay == 0 ? step_delay :
5419 euclid(max_step_delay, step_delay));
5420 num_steps[nr] = move_steps;
5424 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5426 panel_has_doors[door_index] = TRUE;
5430 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5432 num_move_steps = max_move_delay / max_step_delay;
5433 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5435 door_delay_value = max_step_delay;
5437 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5439 start = num_move_steps - 1;
5443 // opening door sound has priority over simultaneously closing door
5444 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5446 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5448 if (door_state & DOOR_OPEN_1)
5449 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5450 if (door_state & DOOR_OPEN_2)
5451 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5453 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5455 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5457 if (door_state & DOOR_CLOSE_1)
5458 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5459 if (door_state & DOOR_CLOSE_2)
5460 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5464 for (k = start; k < num_move_steps; k++)
5466 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5468 door_part_done_all = TRUE;
5470 for (i = 0; i < NUM_DOORS; i++)
5471 door_panel_drawn[i] = FALSE;
5473 for (i = 0; i < MAX_DOOR_PARTS; i++)
5475 int nr = door_part_order[i].nr;
5476 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5477 struct DoorPartPosInfo *pos = dpc->pos;
5478 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5479 int door_token = dpc->door_token;
5480 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5481 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5482 boolean is_panel_and_door_has_closed = FALSE;
5483 struct Rect *door_rect = &door_rect_list[door_index];
5484 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5486 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5487 int current_door_state = door_state & door_token;
5488 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5489 boolean door_closing = !door_opening;
5490 boolean part_opening = (is_panel ? door_closing : door_opening);
5491 boolean part_closing = !part_opening;
5492 int start_step = (part_opening ? pos->start_step_opening :
5493 pos->start_step_closing);
5494 int step_delay = pos->step_delay;
5495 int step_factor = step_delay / max_step_delay;
5496 int k1 = (step_factor ? k / step_factor + 1 : k);
5497 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5498 int kk = MAX(0, k2);
5501 int src_x, src_y, src_xx, src_yy;
5502 int dst_x, dst_y, dst_xx, dst_yy;
5505 if (door_part_skip[nr])
5508 if (!(door_state & door_token))
5516 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5517 int kk_door = MAX(0, k2_door);
5518 int sync_frame = kk_door * door_delay_value;
5519 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5521 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5522 &g_src_x, &g_src_y);
5527 if (!door_panel_drawn[door_index])
5529 ClearRectangle(drawto, door_rect->x, door_rect->y,
5530 door_rect->width, door_rect->height);
5532 door_panel_drawn[door_index] = TRUE;
5535 // draw opening or closing door parts
5537 if (pos->step_xoffset < 0) // door part on right side
5540 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5543 if (dst_xx + width > door_rect->width)
5544 width = door_rect->width - dst_xx;
5546 else // door part on left side
5549 dst_xx = pos->x - kk * pos->step_xoffset;
5553 src_xx = ABS(dst_xx);
5557 width = g->width - src_xx;
5559 if (width > door_rect->width)
5560 width = door_rect->width;
5562 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5565 if (pos->step_yoffset < 0) // door part on bottom side
5568 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5571 if (dst_yy + height > door_rect->height)
5572 height = door_rect->height - dst_yy;
5574 else // door part on top side
5577 dst_yy = pos->y - kk * pos->step_yoffset;
5581 src_yy = ABS(dst_yy);
5585 height = g->height - src_yy;
5588 src_x = g_src_x + src_xx;
5589 src_y = g_src_y + src_yy;
5591 dst_x = door_rect->x + dst_xx;
5592 dst_y = door_rect->y + dst_yy;
5594 is_panel_and_door_has_closed =
5597 panel_has_doors[door_index] &&
5598 k >= num_move_steps_doors_only - 1);
5600 if (width >= 0 && width <= g->width &&
5601 height >= 0 && height <= g->height &&
5602 !is_panel_and_door_has_closed)
5604 if (is_panel || !pos->draw_masked)
5605 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5608 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5612 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5614 if ((part_opening && (width < 0 || height < 0)) ||
5615 (part_closing && (width >= g->width && height >= g->height)))
5616 door_part_done[nr] = TRUE;
5618 // continue door part animations, but not panel after door has closed
5619 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5620 door_part_done_all = FALSE;
5623 if (!(door_state & DOOR_NO_DELAY))
5627 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5629 current_move_delay += max_step_delay;
5631 // prevent OS (Windows) from complaining about program not responding
5635 if (door_part_done_all)
5639 if (!(door_state & DOOR_NO_DELAY))
5641 // wait for specified door action post delay
5642 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5643 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5644 else if (door_state & DOOR_ACTION_1)
5645 door_delay_value = door_1.post_delay;
5646 else if (door_state & DOOR_ACTION_2)
5647 door_delay_value = door_2.post_delay;
5649 while (!DelayReached(&door_delay, door_delay_value))
5654 if (door_state & DOOR_ACTION_1)
5655 door1 = door_state & DOOR_ACTION_1;
5656 if (door_state & DOOR_ACTION_2)
5657 door2 = door_state & DOOR_ACTION_2;
5659 // draw masked border over door area
5660 DrawMaskedBorder(REDRAW_DOOR_1);
5661 DrawMaskedBorder(REDRAW_DOOR_2);
5663 ClearAutoRepeatKeyEvents();
5665 return (door1 | door2);
5668 static boolean useSpecialEditorDoor(void)
5670 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5671 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5673 // do not draw special editor door if editor border defined or redefined
5674 if (graphic_info[graphic].bitmap != NULL || redefined)
5677 // do not draw special editor door if global border defined to be empty
5678 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5681 // do not draw special editor door if viewport definitions do not match
5685 EY + EYSIZE != VY + VYSIZE)
5691 void DrawSpecialEditorDoor(void)
5693 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5694 int top_border_width = gfx1->width;
5695 int top_border_height = gfx1->height;
5696 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5697 int ex = EX - outer_border;
5698 int ey = EY - outer_border;
5699 int vy = VY - outer_border;
5700 int exsize = EXSIZE + 2 * outer_border;
5702 if (!useSpecialEditorDoor())
5705 // draw bigger level editor toolbox window
5706 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5707 top_border_width, top_border_height, ex, ey - top_border_height);
5708 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5709 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5711 redraw_mask |= REDRAW_ALL;
5714 void UndrawSpecialEditorDoor(void)
5716 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5717 int top_border_width = gfx1->width;
5718 int top_border_height = gfx1->height;
5719 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5720 int ex = EX - outer_border;
5721 int ey = EY - outer_border;
5722 int ey_top = ey - top_border_height;
5723 int exsize = EXSIZE + 2 * outer_border;
5724 int eysize = EYSIZE + 2 * outer_border;
5726 if (!useSpecialEditorDoor())
5729 // draw normal tape recorder window
5730 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5732 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5733 ex, ey_top, top_border_width, top_border_height,
5735 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5736 ex, ey, exsize, eysize, ex, ey);
5740 // if screen background is set to "[NONE]", clear editor toolbox window
5741 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5742 ClearRectangle(drawto, ex, ey, exsize, eysize);
5745 redraw_mask |= REDRAW_ALL;
5749 // ---------- new tool button stuff -------------------------------------------
5754 struct TextPosInfo *pos;
5756 boolean is_touch_button;
5758 } toolbutton_info[NUM_TOOL_BUTTONS] =
5761 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5762 TOOL_CTRL_ID_YES, FALSE, "yes"
5765 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5766 TOOL_CTRL_ID_NO, FALSE, "no"
5769 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5770 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5773 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5774 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5777 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5778 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5781 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5782 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5785 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5786 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5789 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5790 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5793 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5794 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5797 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5798 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5802 void CreateToolButtons(void)
5806 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5808 int graphic = toolbutton_info[i].graphic;
5809 struct GraphicInfo *gfx = &graphic_info[graphic];
5810 struct TextPosInfo *pos = toolbutton_info[i].pos;
5811 struct GadgetInfo *gi;
5812 Bitmap *deco_bitmap = None;
5813 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5814 unsigned int event_mask = GD_EVENT_RELEASED;
5815 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5816 int base_x = (is_touch_button ? 0 : DX);
5817 int base_y = (is_touch_button ? 0 : DY);
5818 int gd_x = gfx->src_x;
5819 int gd_y = gfx->src_y;
5820 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5821 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5826 if (global.use_envelope_request && !is_touch_button)
5828 setRequestPosition(&base_x, &base_y, TRUE);
5830 // check if request buttons are outside of envelope and fix, if needed
5831 if (x < 0 || x + gfx->width > request.width ||
5832 y < 0 || y + gfx->height > request.height)
5834 if (id == TOOL_CTRL_ID_YES)
5837 y = request.height - 2 * request.border_size - gfx->height;
5839 else if (id == TOOL_CTRL_ID_NO)
5841 x = request.width - 2 * request.border_size - gfx->width;
5842 y = request.height - 2 * request.border_size - gfx->height;
5844 else if (id == TOOL_CTRL_ID_CONFIRM)
5846 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5847 y = request.height - 2 * request.border_size - gfx->height;
5849 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5851 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5853 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5854 y = request.height - 2 * request.border_size - gfx->height * 2;
5856 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5857 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5862 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5864 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5866 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5867 pos->size, &deco_bitmap, &deco_x, &deco_y);
5868 deco_xpos = (gfx->width - pos->size) / 2;
5869 deco_ypos = (gfx->height - pos->size) / 2;
5872 gi = CreateGadget(GDI_CUSTOM_ID, id,
5873 GDI_IMAGE_ID, graphic,
5874 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5877 GDI_WIDTH, gfx->width,
5878 GDI_HEIGHT, gfx->height,
5879 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5880 GDI_STATE, GD_BUTTON_UNPRESSED,
5881 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5882 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5883 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5884 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5885 GDI_DECORATION_SIZE, pos->size, pos->size,
5886 GDI_DECORATION_SHIFTING, 1, 1,
5887 GDI_DIRECT_DRAW, FALSE,
5888 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5889 GDI_EVENT_MASK, event_mask,
5890 GDI_CALLBACK_ACTION, HandleToolButtons,
5894 Fail("cannot create gadget");
5896 tool_gadget[id] = gi;
5900 void FreeToolButtons(void)
5904 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5905 FreeGadget(tool_gadget[i]);
5908 static void UnmapToolButtons(void)
5912 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5913 UnmapGadget(tool_gadget[i]);
5916 static void HandleToolButtons(struct GadgetInfo *gi)
5918 request_gadget_id = gi->custom_id;
5921 static struct Mapping_EM_to_RND_object
5924 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5925 boolean is_backside; // backside of moving element
5931 em_object_mapping_list[GAME_TILE_MAX + 1] =
5934 Zborder, FALSE, FALSE,
5938 Zplayer, FALSE, FALSE,
5947 Ztank, FALSE, FALSE,
5951 Zeater, FALSE, FALSE,
5955 Zdynamite, FALSE, FALSE,
5959 Zboom, FALSE, FALSE,
5964 Xchain, FALSE, FALSE,
5965 EL_DEFAULT, ACTION_EXPLODING, -1
5968 Xboom_bug, FALSE, FALSE,
5969 EL_BUG, ACTION_EXPLODING, -1
5972 Xboom_tank, FALSE, FALSE,
5973 EL_SPACESHIP, ACTION_EXPLODING, -1
5976 Xboom_android, FALSE, FALSE,
5977 EL_EMC_ANDROID, ACTION_OTHER, -1
5980 Xboom_1, FALSE, FALSE,
5981 EL_DEFAULT, ACTION_EXPLODING, -1
5984 Xboom_2, FALSE, FALSE,
5985 EL_DEFAULT, ACTION_EXPLODING, -1
5989 Xblank, TRUE, FALSE,
5994 Xsplash_e, FALSE, FALSE,
5995 EL_ACID_SPLASH_RIGHT, -1, -1
5998 Xsplash_w, FALSE, FALSE,
5999 EL_ACID_SPLASH_LEFT, -1, -1
6003 Xplant, TRUE, FALSE,
6004 EL_EMC_PLANT, -1, -1
6007 Yplant, FALSE, FALSE,
6008 EL_EMC_PLANT, -1, -1
6012 Xacid_1, TRUE, FALSE,
6016 Xacid_2, FALSE, FALSE,
6020 Xacid_3, FALSE, FALSE,
6024 Xacid_4, FALSE, FALSE,
6028 Xacid_5, FALSE, FALSE,
6032 Xacid_6, FALSE, FALSE,
6036 Xacid_7, FALSE, FALSE,
6040 Xacid_8, FALSE, FALSE,
6045 Xfake_acid_1, TRUE, FALSE,
6046 EL_EMC_FAKE_ACID, -1, -1
6049 Xfake_acid_2, FALSE, FALSE,
6050 EL_EMC_FAKE_ACID, -1, -1
6053 Xfake_acid_3, FALSE, FALSE,
6054 EL_EMC_FAKE_ACID, -1, -1
6057 Xfake_acid_4, FALSE, FALSE,
6058 EL_EMC_FAKE_ACID, -1, -1
6061 Xfake_acid_5, FALSE, FALSE,
6062 EL_EMC_FAKE_ACID, -1, -1
6065 Xfake_acid_6, FALSE, FALSE,
6066 EL_EMC_FAKE_ACID, -1, -1
6069 Xfake_acid_7, FALSE, FALSE,
6070 EL_EMC_FAKE_ACID, -1, -1
6073 Xfake_acid_8, FALSE, FALSE,
6074 EL_EMC_FAKE_ACID, -1, -1
6078 Xfake_acid_1_player, FALSE, FALSE,
6079 EL_EMC_FAKE_ACID, -1, -1
6082 Xfake_acid_2_player, FALSE, FALSE,
6083 EL_EMC_FAKE_ACID, -1, -1
6086 Xfake_acid_3_player, FALSE, FALSE,
6087 EL_EMC_FAKE_ACID, -1, -1
6090 Xfake_acid_4_player, FALSE, FALSE,
6091 EL_EMC_FAKE_ACID, -1, -1
6094 Xfake_acid_5_player, FALSE, FALSE,
6095 EL_EMC_FAKE_ACID, -1, -1
6098 Xfake_acid_6_player, FALSE, FALSE,
6099 EL_EMC_FAKE_ACID, -1, -1
6102 Xfake_acid_7_player, FALSE, FALSE,
6103 EL_EMC_FAKE_ACID, -1, -1
6106 Xfake_acid_8_player, FALSE, FALSE,
6107 EL_EMC_FAKE_ACID, -1, -1
6111 Xgrass, TRUE, FALSE,
6112 EL_EMC_GRASS, -1, -1
6115 Ygrass_nB, FALSE, FALSE,
6116 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6119 Ygrass_eB, FALSE, FALSE,
6120 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6123 Ygrass_sB, FALSE, FALSE,
6124 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6127 Ygrass_wB, FALSE, FALSE,
6128 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6136 Ydirt_nB, FALSE, FALSE,
6137 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6140 Ydirt_eB, FALSE, FALSE,
6141 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6144 Ydirt_sB, FALSE, FALSE,
6145 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6148 Ydirt_wB, FALSE, FALSE,
6149 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6153 Xandroid, TRUE, FALSE,
6154 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6157 Xandroid_1_n, FALSE, FALSE,
6158 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6161 Xandroid_2_n, FALSE, FALSE,
6162 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6165 Xandroid_1_e, FALSE, FALSE,
6166 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6169 Xandroid_2_e, FALSE, FALSE,
6170 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6173 Xandroid_1_w, FALSE, FALSE,
6174 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6177 Xandroid_2_w, FALSE, FALSE,
6178 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6181 Xandroid_1_s, FALSE, FALSE,
6182 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6185 Xandroid_2_s, FALSE, FALSE,
6186 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6189 Yandroid_n, FALSE, FALSE,
6190 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6193 Yandroid_nB, FALSE, TRUE,
6194 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6197 Yandroid_ne, FALSE, FALSE,
6198 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6201 Yandroid_neB, FALSE, TRUE,
6202 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6205 Yandroid_e, FALSE, FALSE,
6206 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6209 Yandroid_eB, FALSE, TRUE,
6210 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6213 Yandroid_se, FALSE, FALSE,
6214 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6217 Yandroid_seB, FALSE, TRUE,
6218 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6221 Yandroid_s, FALSE, FALSE,
6222 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6225 Yandroid_sB, FALSE, TRUE,
6226 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6229 Yandroid_sw, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6233 Yandroid_swB, FALSE, TRUE,
6234 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6237 Yandroid_w, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6241 Yandroid_wB, FALSE, TRUE,
6242 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6245 Yandroid_nw, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6249 Yandroid_nwB, FALSE, TRUE,
6250 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6254 Xeater_n, TRUE, FALSE,
6255 EL_YAMYAM_UP, -1, -1
6258 Xeater_e, TRUE, FALSE,
6259 EL_YAMYAM_RIGHT, -1, -1
6262 Xeater_w, TRUE, FALSE,
6263 EL_YAMYAM_LEFT, -1, -1
6266 Xeater_s, TRUE, FALSE,
6267 EL_YAMYAM_DOWN, -1, -1
6270 Yeater_n, FALSE, FALSE,
6271 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6274 Yeater_nB, FALSE, TRUE,
6275 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6278 Yeater_e, FALSE, FALSE,
6279 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6282 Yeater_eB, FALSE, TRUE,
6283 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6286 Yeater_s, FALSE, FALSE,
6287 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6290 Yeater_sB, FALSE, TRUE,
6291 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6294 Yeater_w, FALSE, FALSE,
6295 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6298 Yeater_wB, FALSE, TRUE,
6299 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6302 Yeater_stone, FALSE, FALSE,
6303 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6306 Yeater_spring, FALSE, FALSE,
6307 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6311 Xalien, TRUE, FALSE,
6315 Xalien_pause, FALSE, FALSE,
6319 Yalien_n, FALSE, FALSE,
6320 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6323 Yalien_nB, FALSE, TRUE,
6324 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6327 Yalien_e, FALSE, FALSE,
6328 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6331 Yalien_eB, FALSE, TRUE,
6332 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6335 Yalien_s, FALSE, FALSE,
6336 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6339 Yalien_sB, FALSE, TRUE,
6340 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6343 Yalien_w, FALSE, FALSE,
6344 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6347 Yalien_wB, FALSE, TRUE,
6348 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6351 Yalien_stone, FALSE, FALSE,
6352 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6355 Yalien_spring, FALSE, FALSE,
6356 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6360 Xbug_1_n, TRUE, FALSE,
6364 Xbug_1_e, TRUE, FALSE,
6365 EL_BUG_RIGHT, -1, -1
6368 Xbug_1_s, TRUE, FALSE,
6372 Xbug_1_w, TRUE, FALSE,
6376 Xbug_2_n, FALSE, FALSE,
6380 Xbug_2_e, FALSE, FALSE,
6381 EL_BUG_RIGHT, -1, -1
6384 Xbug_2_s, FALSE, FALSE,
6388 Xbug_2_w, FALSE, FALSE,
6392 Ybug_n, FALSE, FALSE,
6393 EL_BUG, ACTION_MOVING, MV_BIT_UP
6396 Ybug_nB, FALSE, TRUE,
6397 EL_BUG, ACTION_MOVING, MV_BIT_UP
6400 Ybug_e, FALSE, FALSE,
6401 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6404 Ybug_eB, FALSE, TRUE,
6405 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6408 Ybug_s, FALSE, FALSE,
6409 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6412 Ybug_sB, FALSE, TRUE,
6413 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6416 Ybug_w, FALSE, FALSE,
6417 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6420 Ybug_wB, FALSE, TRUE,
6421 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6424 Ybug_w_n, FALSE, FALSE,
6425 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6428 Ybug_n_e, FALSE, FALSE,
6429 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6432 Ybug_e_s, FALSE, FALSE,
6433 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6436 Ybug_s_w, FALSE, FALSE,
6437 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6440 Ybug_e_n, FALSE, FALSE,
6441 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6444 Ybug_s_e, FALSE, FALSE,
6445 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6448 Ybug_w_s, FALSE, FALSE,
6449 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6452 Ybug_n_w, FALSE, FALSE,
6453 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6456 Ybug_stone, FALSE, FALSE,
6457 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6460 Ybug_spring, FALSE, FALSE,
6461 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6465 Xtank_1_n, TRUE, FALSE,
6466 EL_SPACESHIP_UP, -1, -1
6469 Xtank_1_e, TRUE, FALSE,
6470 EL_SPACESHIP_RIGHT, -1, -1
6473 Xtank_1_s, TRUE, FALSE,
6474 EL_SPACESHIP_DOWN, -1, -1
6477 Xtank_1_w, TRUE, FALSE,
6478 EL_SPACESHIP_LEFT, -1, -1
6481 Xtank_2_n, FALSE, FALSE,
6482 EL_SPACESHIP_UP, -1, -1
6485 Xtank_2_e, FALSE, FALSE,
6486 EL_SPACESHIP_RIGHT, -1, -1
6489 Xtank_2_s, FALSE, FALSE,
6490 EL_SPACESHIP_DOWN, -1, -1
6493 Xtank_2_w, FALSE, FALSE,
6494 EL_SPACESHIP_LEFT, -1, -1
6497 Ytank_n, FALSE, FALSE,
6498 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6501 Ytank_nB, FALSE, TRUE,
6502 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6505 Ytank_e, FALSE, FALSE,
6506 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6509 Ytank_eB, FALSE, TRUE,
6510 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6513 Ytank_s, FALSE, FALSE,
6514 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6517 Ytank_sB, FALSE, TRUE,
6518 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6521 Ytank_w, FALSE, FALSE,
6522 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6525 Ytank_wB, FALSE, TRUE,
6526 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6529 Ytank_w_n, FALSE, FALSE,
6530 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6533 Ytank_n_e, FALSE, FALSE,
6534 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6537 Ytank_e_s, FALSE, FALSE,
6538 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6541 Ytank_s_w, FALSE, FALSE,
6542 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6545 Ytank_e_n, FALSE, FALSE,
6546 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6549 Ytank_s_e, FALSE, FALSE,
6550 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6553 Ytank_w_s, FALSE, FALSE,
6554 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6557 Ytank_n_w, FALSE, FALSE,
6558 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6561 Ytank_stone, FALSE, FALSE,
6562 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6565 Ytank_spring, FALSE, FALSE,
6566 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6570 Xemerald, TRUE, FALSE,
6574 Xemerald_pause, FALSE, FALSE,
6578 Xemerald_fall, FALSE, FALSE,
6582 Xemerald_shine, FALSE, FALSE,
6583 EL_EMERALD, ACTION_TWINKLING, -1
6586 Yemerald_s, FALSE, FALSE,
6587 EL_EMERALD, ACTION_FALLING, -1
6590 Yemerald_sB, FALSE, TRUE,
6591 EL_EMERALD, ACTION_FALLING, -1
6594 Yemerald_e, FALSE, FALSE,
6595 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6598 Yemerald_eB, FALSE, TRUE,
6599 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6602 Yemerald_w, FALSE, FALSE,
6603 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6606 Yemerald_wB, FALSE, TRUE,
6607 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6610 Yemerald_blank, FALSE, FALSE,
6611 EL_EMERALD, ACTION_COLLECTING, -1
6615 Xdiamond, TRUE, FALSE,
6619 Xdiamond_pause, FALSE, FALSE,
6623 Xdiamond_fall, FALSE, FALSE,
6627 Xdiamond_shine, FALSE, FALSE,
6628 EL_DIAMOND, ACTION_TWINKLING, -1
6631 Ydiamond_s, FALSE, FALSE,
6632 EL_DIAMOND, ACTION_FALLING, -1
6635 Ydiamond_sB, FALSE, TRUE,
6636 EL_DIAMOND, ACTION_FALLING, -1
6639 Ydiamond_e, FALSE, FALSE,
6640 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6643 Ydiamond_eB, FALSE, TRUE,
6644 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6647 Ydiamond_w, FALSE, FALSE,
6648 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6651 Ydiamond_wB, FALSE, TRUE,
6652 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6655 Ydiamond_blank, FALSE, FALSE,
6656 EL_DIAMOND, ACTION_COLLECTING, -1
6659 Ydiamond_stone, FALSE, FALSE,
6660 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6664 Xstone, TRUE, FALSE,
6668 Xstone_pause, FALSE, FALSE,
6672 Xstone_fall, FALSE, FALSE,
6676 Ystone_s, FALSE, FALSE,
6677 EL_ROCK, ACTION_FALLING, -1
6680 Ystone_sB, FALSE, TRUE,
6681 EL_ROCK, ACTION_FALLING, -1
6684 Ystone_e, FALSE, FALSE,
6685 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6688 Ystone_eB, FALSE, TRUE,
6689 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6692 Ystone_w, FALSE, FALSE,
6693 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6696 Ystone_wB, FALSE, TRUE,
6697 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6705 Xbomb_pause, FALSE, FALSE,
6709 Xbomb_fall, FALSE, FALSE,
6713 Ybomb_s, FALSE, FALSE,
6714 EL_BOMB, ACTION_FALLING, -1
6717 Ybomb_sB, FALSE, TRUE,
6718 EL_BOMB, ACTION_FALLING, -1
6721 Ybomb_e, FALSE, FALSE,
6722 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6725 Ybomb_eB, FALSE, TRUE,
6726 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6729 Ybomb_w, FALSE, FALSE,
6730 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6733 Ybomb_wB, FALSE, TRUE,
6734 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6737 Ybomb_blank, FALSE, FALSE,
6738 EL_BOMB, ACTION_ACTIVATING, -1
6746 Xnut_pause, FALSE, FALSE,
6750 Xnut_fall, FALSE, FALSE,
6754 Ynut_s, FALSE, FALSE,
6755 EL_NUT, ACTION_FALLING, -1
6758 Ynut_sB, FALSE, TRUE,
6759 EL_NUT, ACTION_FALLING, -1
6762 Ynut_e, FALSE, FALSE,
6763 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6766 Ynut_eB, FALSE, TRUE,
6767 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6770 Ynut_w, FALSE, FALSE,
6771 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6774 Ynut_wB, FALSE, TRUE,
6775 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6778 Ynut_stone, FALSE, FALSE,
6779 EL_NUT, ACTION_BREAKING, -1
6783 Xspring, TRUE, FALSE,
6787 Xspring_pause, FALSE, FALSE,
6791 Xspring_e, TRUE, FALSE,
6792 EL_SPRING_RIGHT, -1, -1
6795 Xspring_w, TRUE, FALSE,
6796 EL_SPRING_LEFT, -1, -1
6799 Xspring_fall, FALSE, FALSE,
6803 Yspring_s, FALSE, FALSE,
6804 EL_SPRING, ACTION_FALLING, -1
6807 Yspring_sB, FALSE, TRUE,
6808 EL_SPRING, ACTION_FALLING, -1
6811 Yspring_e, FALSE, FALSE,
6812 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6815 Yspring_eB, FALSE, TRUE,
6816 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6819 Yspring_w, FALSE, FALSE,
6820 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6823 Yspring_wB, FALSE, TRUE,
6824 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6827 Yspring_alien_e, FALSE, FALSE,
6828 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6831 Yspring_alien_eB, FALSE, TRUE,
6832 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6835 Yspring_alien_w, FALSE, FALSE,
6836 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6839 Yspring_alien_wB, FALSE, TRUE,
6840 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6844 Xpush_emerald_e, FALSE, FALSE,
6845 EL_EMERALD, -1, MV_BIT_RIGHT
6848 Xpush_emerald_w, FALSE, FALSE,
6849 EL_EMERALD, -1, MV_BIT_LEFT
6852 Xpush_diamond_e, FALSE, FALSE,
6853 EL_DIAMOND, -1, MV_BIT_RIGHT
6856 Xpush_diamond_w, FALSE, FALSE,
6857 EL_DIAMOND, -1, MV_BIT_LEFT
6860 Xpush_stone_e, FALSE, FALSE,
6861 EL_ROCK, -1, MV_BIT_RIGHT
6864 Xpush_stone_w, FALSE, FALSE,
6865 EL_ROCK, -1, MV_BIT_LEFT
6868 Xpush_bomb_e, FALSE, FALSE,
6869 EL_BOMB, -1, MV_BIT_RIGHT
6872 Xpush_bomb_w, FALSE, FALSE,
6873 EL_BOMB, -1, MV_BIT_LEFT
6876 Xpush_nut_e, FALSE, FALSE,
6877 EL_NUT, -1, MV_BIT_RIGHT
6880 Xpush_nut_w, FALSE, FALSE,
6881 EL_NUT, -1, MV_BIT_LEFT
6884 Xpush_spring_e, FALSE, FALSE,
6885 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6888 Xpush_spring_w, FALSE, FALSE,
6889 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6893 Xdynamite, TRUE, FALSE,
6894 EL_EM_DYNAMITE, -1, -1
6897 Ydynamite_blank, FALSE, FALSE,
6898 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6901 Xdynamite_1, TRUE, FALSE,
6902 EL_EM_DYNAMITE_ACTIVE, -1, -1
6905 Xdynamite_2, FALSE, FALSE,
6906 EL_EM_DYNAMITE_ACTIVE, -1, -1
6909 Xdynamite_3, FALSE, FALSE,
6910 EL_EM_DYNAMITE_ACTIVE, -1, -1
6913 Xdynamite_4, FALSE, FALSE,
6914 EL_EM_DYNAMITE_ACTIVE, -1, -1
6918 Xkey_1, TRUE, FALSE,
6922 Xkey_2, TRUE, FALSE,
6926 Xkey_3, TRUE, FALSE,
6930 Xkey_4, TRUE, FALSE,
6934 Xkey_5, TRUE, FALSE,
6935 EL_EMC_KEY_5, -1, -1
6938 Xkey_6, TRUE, FALSE,
6939 EL_EMC_KEY_6, -1, -1
6942 Xkey_7, TRUE, FALSE,
6943 EL_EMC_KEY_7, -1, -1
6946 Xkey_8, TRUE, FALSE,
6947 EL_EMC_KEY_8, -1, -1
6951 Xdoor_1, TRUE, FALSE,
6952 EL_EM_GATE_1, -1, -1
6955 Xdoor_2, TRUE, FALSE,
6956 EL_EM_GATE_2, -1, -1
6959 Xdoor_3, TRUE, FALSE,
6960 EL_EM_GATE_3, -1, -1
6963 Xdoor_4, TRUE, FALSE,
6964 EL_EM_GATE_4, -1, -1
6967 Xdoor_5, TRUE, FALSE,
6968 EL_EMC_GATE_5, -1, -1
6971 Xdoor_6, TRUE, FALSE,
6972 EL_EMC_GATE_6, -1, -1
6975 Xdoor_7, TRUE, FALSE,
6976 EL_EMC_GATE_7, -1, -1
6979 Xdoor_8, TRUE, FALSE,
6980 EL_EMC_GATE_8, -1, -1
6984 Xfake_door_1, TRUE, FALSE,
6985 EL_EM_GATE_1_GRAY, -1, -1
6988 Xfake_door_2, TRUE, FALSE,
6989 EL_EM_GATE_2_GRAY, -1, -1
6992 Xfake_door_3, TRUE, FALSE,
6993 EL_EM_GATE_3_GRAY, -1, -1
6996 Xfake_door_4, TRUE, FALSE,
6997 EL_EM_GATE_4_GRAY, -1, -1
7000 Xfake_door_5, TRUE, FALSE,
7001 EL_EMC_GATE_5_GRAY, -1, -1
7004 Xfake_door_6, TRUE, FALSE,
7005 EL_EMC_GATE_6_GRAY, -1, -1
7008 Xfake_door_7, TRUE, FALSE,
7009 EL_EMC_GATE_7_GRAY, -1, -1
7012 Xfake_door_8, TRUE, FALSE,
7013 EL_EMC_GATE_8_GRAY, -1, -1
7017 Xballoon, TRUE, FALSE,
7021 Yballoon_n, FALSE, FALSE,
7022 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7025 Yballoon_nB, FALSE, TRUE,
7026 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7029 Yballoon_e, FALSE, FALSE,
7030 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7033 Yballoon_eB, FALSE, TRUE,
7034 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7037 Yballoon_s, FALSE, FALSE,
7038 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7041 Yballoon_sB, FALSE, TRUE,
7042 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7045 Yballoon_w, FALSE, FALSE,
7046 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7049 Yballoon_wB, FALSE, TRUE,
7050 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7054 Xball_1, TRUE, FALSE,
7055 EL_EMC_MAGIC_BALL, -1, -1
7058 Yball_1, FALSE, FALSE,
7059 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7062 Xball_2, FALSE, FALSE,
7063 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7066 Yball_2, FALSE, FALSE,
7067 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7070 Yball_blank, FALSE, FALSE,
7071 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7075 Xamoeba_1, TRUE, FALSE,
7076 EL_AMOEBA_DRY, ACTION_OTHER, -1
7079 Xamoeba_2, FALSE, FALSE,
7080 EL_AMOEBA_DRY, ACTION_OTHER, -1
7083 Xamoeba_3, FALSE, FALSE,
7084 EL_AMOEBA_DRY, ACTION_OTHER, -1
7087 Xamoeba_4, FALSE, FALSE,
7088 EL_AMOEBA_DRY, ACTION_OTHER, -1
7091 Xamoeba_5, TRUE, FALSE,
7092 EL_AMOEBA_WET, ACTION_OTHER, -1
7095 Xamoeba_6, FALSE, FALSE,
7096 EL_AMOEBA_WET, ACTION_OTHER, -1
7099 Xamoeba_7, FALSE, FALSE,
7100 EL_AMOEBA_WET, ACTION_OTHER, -1
7103 Xamoeba_8, FALSE, FALSE,
7104 EL_AMOEBA_WET, ACTION_OTHER, -1
7109 EL_AMOEBA_DROP, ACTION_GROWING, -1
7112 Xdrip_fall, FALSE, FALSE,
7113 EL_AMOEBA_DROP, -1, -1
7116 Xdrip_stretch, FALSE, FALSE,
7117 EL_AMOEBA_DROP, ACTION_FALLING, -1
7120 Xdrip_stretchB, FALSE, TRUE,
7121 EL_AMOEBA_DROP, ACTION_FALLING, -1
7124 Ydrip_1_s, FALSE, FALSE,
7125 EL_AMOEBA_DROP, ACTION_FALLING, -1
7128 Ydrip_1_sB, FALSE, TRUE,
7129 EL_AMOEBA_DROP, ACTION_FALLING, -1
7132 Ydrip_2_s, FALSE, FALSE,
7133 EL_AMOEBA_DROP, ACTION_FALLING, -1
7136 Ydrip_2_sB, FALSE, TRUE,
7137 EL_AMOEBA_DROP, ACTION_FALLING, -1
7141 Xwonderwall, TRUE, FALSE,
7142 EL_MAGIC_WALL, -1, -1
7145 Ywonderwall, FALSE, FALSE,
7146 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7150 Xwheel, TRUE, FALSE,
7151 EL_ROBOT_WHEEL, -1, -1
7154 Ywheel, FALSE, FALSE,
7155 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7159 Xswitch, TRUE, FALSE,
7160 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7163 Yswitch, FALSE, FALSE,
7164 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7168 Xbumper, TRUE, FALSE,
7169 EL_EMC_SPRING_BUMPER, -1, -1
7172 Ybumper, FALSE, FALSE,
7173 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7177 Xacid_nw, TRUE, FALSE,
7178 EL_ACID_POOL_TOPLEFT, -1, -1
7181 Xacid_ne, TRUE, FALSE,
7182 EL_ACID_POOL_TOPRIGHT, -1, -1
7185 Xacid_sw, TRUE, FALSE,
7186 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7189 Xacid_s, TRUE, FALSE,
7190 EL_ACID_POOL_BOTTOM, -1, -1
7193 Xacid_se, TRUE, FALSE,
7194 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7198 Xfake_blank, TRUE, FALSE,
7199 EL_INVISIBLE_WALL, -1, -1
7202 Yfake_blank, FALSE, FALSE,
7203 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7207 Xfake_grass, TRUE, FALSE,
7208 EL_EMC_FAKE_GRASS, -1, -1
7211 Yfake_grass, FALSE, FALSE,
7212 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7216 Xfake_amoeba, TRUE, FALSE,
7217 EL_EMC_DRIPPER, -1, -1
7220 Yfake_amoeba, FALSE, FALSE,
7221 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7225 Xlenses, TRUE, FALSE,
7226 EL_EMC_LENSES, -1, -1
7230 Xmagnify, TRUE, FALSE,
7231 EL_EMC_MAGNIFIER, -1, -1
7236 EL_QUICKSAND_EMPTY, -1, -1
7239 Xsand_stone, TRUE, FALSE,
7240 EL_QUICKSAND_FULL, -1, -1
7243 Xsand_stonein_1, FALSE, TRUE,
7244 EL_ROCK, ACTION_FILLING, -1
7247 Xsand_stonein_2, FALSE, TRUE,
7248 EL_ROCK, ACTION_FILLING, -1
7251 Xsand_stonein_3, FALSE, TRUE,
7252 EL_ROCK, ACTION_FILLING, -1
7255 Xsand_stonein_4, FALSE, TRUE,
7256 EL_ROCK, ACTION_FILLING, -1
7259 Xsand_sandstone_1, FALSE, FALSE,
7260 EL_QUICKSAND_FILLING, -1, -1
7263 Xsand_sandstone_2, FALSE, FALSE,
7264 EL_QUICKSAND_FILLING, -1, -1
7267 Xsand_sandstone_3, FALSE, FALSE,
7268 EL_QUICKSAND_FILLING, -1, -1
7271 Xsand_sandstone_4, FALSE, FALSE,
7272 EL_QUICKSAND_FILLING, -1, -1
7275 Xsand_stonesand_1, FALSE, FALSE,
7276 EL_QUICKSAND_EMPTYING, -1, -1
7279 Xsand_stonesand_2, FALSE, FALSE,
7280 EL_QUICKSAND_EMPTYING, -1, -1
7283 Xsand_stonesand_3, FALSE, FALSE,
7284 EL_QUICKSAND_EMPTYING, -1, -1
7287 Xsand_stonesand_4, FALSE, FALSE,
7288 EL_QUICKSAND_EMPTYING, -1, -1
7291 Xsand_stoneout_1, FALSE, FALSE,
7292 EL_ROCK, ACTION_EMPTYING, -1
7295 Xsand_stoneout_2, FALSE, FALSE,
7296 EL_ROCK, ACTION_EMPTYING, -1
7299 Xsand_stonesand_quickout_1, FALSE, FALSE,
7300 EL_QUICKSAND_EMPTYING, -1, -1
7303 Xsand_stonesand_quickout_2, FALSE, FALSE,
7304 EL_QUICKSAND_EMPTYING, -1, -1
7308 Xslide_ns, TRUE, FALSE,
7309 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7312 Yslide_ns_blank, FALSE, FALSE,
7313 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7316 Xslide_ew, TRUE, FALSE,
7317 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7320 Yslide_ew_blank, FALSE, FALSE,
7321 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7325 Xwind_n, TRUE, FALSE,
7326 EL_BALLOON_SWITCH_UP, -1, -1
7329 Xwind_e, TRUE, FALSE,
7330 EL_BALLOON_SWITCH_RIGHT, -1, -1
7333 Xwind_s, TRUE, FALSE,
7334 EL_BALLOON_SWITCH_DOWN, -1, -1
7337 Xwind_w, TRUE, FALSE,
7338 EL_BALLOON_SWITCH_LEFT, -1, -1
7341 Xwind_any, TRUE, FALSE,
7342 EL_BALLOON_SWITCH_ANY, -1, -1
7345 Xwind_stop, TRUE, FALSE,
7346 EL_BALLOON_SWITCH_NONE, -1, -1
7351 EL_EM_EXIT_CLOSED, -1, -1
7354 Xexit_1, TRUE, FALSE,
7355 EL_EM_EXIT_OPEN, -1, -1
7358 Xexit_2, FALSE, FALSE,
7359 EL_EM_EXIT_OPEN, -1, -1
7362 Xexit_3, FALSE, FALSE,
7363 EL_EM_EXIT_OPEN, -1, -1
7367 Xpause, FALSE, FALSE,
7372 Xwall_1, TRUE, FALSE,
7376 Xwall_2, TRUE, FALSE,
7377 EL_EMC_WALL_14, -1, -1
7380 Xwall_3, TRUE, FALSE,
7381 EL_EMC_WALL_15, -1, -1
7384 Xwall_4, TRUE, FALSE,
7385 EL_EMC_WALL_16, -1, -1
7389 Xroundwall_1, TRUE, FALSE,
7390 EL_WALL_SLIPPERY, -1, -1
7393 Xroundwall_2, TRUE, FALSE,
7394 EL_EMC_WALL_SLIPPERY_2, -1, -1
7397 Xroundwall_3, TRUE, FALSE,
7398 EL_EMC_WALL_SLIPPERY_3, -1, -1
7401 Xroundwall_4, TRUE, FALSE,
7402 EL_EMC_WALL_SLIPPERY_4, -1, -1
7406 Xsteel_1, TRUE, FALSE,
7407 EL_STEELWALL, -1, -1
7410 Xsteel_2, TRUE, FALSE,
7411 EL_EMC_STEELWALL_2, -1, -1
7414 Xsteel_3, TRUE, FALSE,
7415 EL_EMC_STEELWALL_3, -1, -1
7418 Xsteel_4, TRUE, FALSE,
7419 EL_EMC_STEELWALL_4, -1, -1
7423 Xdecor_1, TRUE, FALSE,
7424 EL_EMC_WALL_8, -1, -1
7427 Xdecor_2, TRUE, FALSE,
7428 EL_EMC_WALL_6, -1, -1
7431 Xdecor_3, TRUE, FALSE,
7432 EL_EMC_WALL_4, -1, -1
7435 Xdecor_4, TRUE, FALSE,
7436 EL_EMC_WALL_7, -1, -1
7439 Xdecor_5, TRUE, FALSE,
7440 EL_EMC_WALL_5, -1, -1
7443 Xdecor_6, TRUE, FALSE,
7444 EL_EMC_WALL_9, -1, -1
7447 Xdecor_7, TRUE, FALSE,
7448 EL_EMC_WALL_10, -1, -1
7451 Xdecor_8, TRUE, FALSE,
7452 EL_EMC_WALL_1, -1, -1
7455 Xdecor_9, TRUE, FALSE,
7456 EL_EMC_WALL_2, -1, -1
7459 Xdecor_10, TRUE, FALSE,
7460 EL_EMC_WALL_3, -1, -1
7463 Xdecor_11, TRUE, FALSE,
7464 EL_EMC_WALL_11, -1, -1
7467 Xdecor_12, TRUE, FALSE,
7468 EL_EMC_WALL_12, -1, -1
7472 Xalpha_0, TRUE, FALSE,
7473 EL_CHAR('0'), -1, -1
7476 Xalpha_1, TRUE, FALSE,
7477 EL_CHAR('1'), -1, -1
7480 Xalpha_2, TRUE, FALSE,
7481 EL_CHAR('2'), -1, -1
7484 Xalpha_3, TRUE, FALSE,
7485 EL_CHAR('3'), -1, -1
7488 Xalpha_4, TRUE, FALSE,
7489 EL_CHAR('4'), -1, -1
7492 Xalpha_5, TRUE, FALSE,
7493 EL_CHAR('5'), -1, -1
7496 Xalpha_6, TRUE, FALSE,
7497 EL_CHAR('6'), -1, -1
7500 Xalpha_7, TRUE, FALSE,
7501 EL_CHAR('7'), -1, -1
7504 Xalpha_8, TRUE, FALSE,
7505 EL_CHAR('8'), -1, -1
7508 Xalpha_9, TRUE, FALSE,
7509 EL_CHAR('9'), -1, -1
7512 Xalpha_excla, TRUE, FALSE,
7513 EL_CHAR('!'), -1, -1
7516 Xalpha_apost, TRUE, FALSE,
7517 EL_CHAR('\''), -1, -1
7520 Xalpha_comma, TRUE, FALSE,
7521 EL_CHAR(','), -1, -1
7524 Xalpha_minus, TRUE, FALSE,
7525 EL_CHAR('-'), -1, -1
7528 Xalpha_perio, TRUE, FALSE,
7529 EL_CHAR('.'), -1, -1
7532 Xalpha_colon, TRUE, FALSE,
7533 EL_CHAR(':'), -1, -1
7536 Xalpha_quest, TRUE, FALSE,
7537 EL_CHAR('?'), -1, -1
7540 Xalpha_a, TRUE, FALSE,
7541 EL_CHAR('A'), -1, -1
7544 Xalpha_b, TRUE, FALSE,
7545 EL_CHAR('B'), -1, -1
7548 Xalpha_c, TRUE, FALSE,
7549 EL_CHAR('C'), -1, -1
7552 Xalpha_d, TRUE, FALSE,
7553 EL_CHAR('D'), -1, -1
7556 Xalpha_e, TRUE, FALSE,
7557 EL_CHAR('E'), -1, -1
7560 Xalpha_f, TRUE, FALSE,
7561 EL_CHAR('F'), -1, -1
7564 Xalpha_g, TRUE, FALSE,
7565 EL_CHAR('G'), -1, -1
7568 Xalpha_h, TRUE, FALSE,
7569 EL_CHAR('H'), -1, -1
7572 Xalpha_i, TRUE, FALSE,
7573 EL_CHAR('I'), -1, -1
7576 Xalpha_j, TRUE, FALSE,
7577 EL_CHAR('J'), -1, -1
7580 Xalpha_k, TRUE, FALSE,
7581 EL_CHAR('K'), -1, -1
7584 Xalpha_l, TRUE, FALSE,
7585 EL_CHAR('L'), -1, -1
7588 Xalpha_m, TRUE, FALSE,
7589 EL_CHAR('M'), -1, -1
7592 Xalpha_n, TRUE, FALSE,
7593 EL_CHAR('N'), -1, -1
7596 Xalpha_o, TRUE, FALSE,
7597 EL_CHAR('O'), -1, -1
7600 Xalpha_p, TRUE, FALSE,
7601 EL_CHAR('P'), -1, -1
7604 Xalpha_q, TRUE, FALSE,
7605 EL_CHAR('Q'), -1, -1
7608 Xalpha_r, TRUE, FALSE,
7609 EL_CHAR('R'), -1, -1
7612 Xalpha_s, TRUE, FALSE,
7613 EL_CHAR('S'), -1, -1
7616 Xalpha_t, TRUE, FALSE,
7617 EL_CHAR('T'), -1, -1
7620 Xalpha_u, TRUE, FALSE,
7621 EL_CHAR('U'), -1, -1
7624 Xalpha_v, TRUE, FALSE,
7625 EL_CHAR('V'), -1, -1
7628 Xalpha_w, TRUE, FALSE,
7629 EL_CHAR('W'), -1, -1
7632 Xalpha_x, TRUE, FALSE,
7633 EL_CHAR('X'), -1, -1
7636 Xalpha_y, TRUE, FALSE,
7637 EL_CHAR('Y'), -1, -1
7640 Xalpha_z, TRUE, FALSE,
7641 EL_CHAR('Z'), -1, -1
7644 Xalpha_arrow_e, TRUE, FALSE,
7645 EL_CHAR('>'), -1, -1
7648 Xalpha_arrow_w, TRUE, FALSE,
7649 EL_CHAR('<'), -1, -1
7652 Xalpha_copyr, TRUE, FALSE,
7653 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7657 Ykey_1_blank, FALSE, FALSE,
7658 EL_EM_KEY_1, ACTION_COLLECTING, -1
7661 Ykey_2_blank, FALSE, FALSE,
7662 EL_EM_KEY_2, ACTION_COLLECTING, -1
7665 Ykey_3_blank, FALSE, FALSE,
7666 EL_EM_KEY_3, ACTION_COLLECTING, -1
7669 Ykey_4_blank, FALSE, FALSE,
7670 EL_EM_KEY_4, ACTION_COLLECTING, -1
7673 Ykey_5_blank, FALSE, FALSE,
7674 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7677 Ykey_6_blank, FALSE, FALSE,
7678 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7681 Ykey_7_blank, FALSE, FALSE,
7682 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7685 Ykey_8_blank, FALSE, FALSE,
7686 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7689 Ylenses_blank, FALSE, FALSE,
7690 EL_EMC_LENSES, ACTION_COLLECTING, -1
7693 Ymagnify_blank, FALSE, FALSE,
7694 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7697 Ygrass_blank, FALSE, FALSE,
7698 EL_EMC_GRASS, ACTION_SNAPPING, -1
7701 Ydirt_blank, FALSE, FALSE,
7702 EL_SAND, ACTION_SNAPPING, -1
7711 static struct Mapping_EM_to_RND_player
7720 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7724 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7728 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7732 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7736 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7740 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7744 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7748 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7752 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7756 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7760 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7764 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7768 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7772 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7776 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7780 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7784 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7788 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7792 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7796 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7800 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7804 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7808 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7812 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7816 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7820 EL_PLAYER_1, ACTION_DEFAULT, -1,
7824 EL_PLAYER_2, ACTION_DEFAULT, -1,
7828 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7832 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7836 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7840 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7844 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7848 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7852 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7856 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7860 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7864 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7868 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7872 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7876 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7880 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7884 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7888 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7892 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7896 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7900 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7904 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7908 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7912 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7916 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7920 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7924 EL_PLAYER_3, ACTION_DEFAULT, -1,
7928 EL_PLAYER_4, ACTION_DEFAULT, -1,
7937 int map_element_RND_to_EM_cave(int element_rnd)
7939 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7940 static boolean mapping_initialized = FALSE;
7942 if (!mapping_initialized)
7946 // return "Xalpha_quest" for all undefined elements in mapping array
7947 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7948 mapping_RND_to_EM[i] = Xalpha_quest;
7950 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7951 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7952 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7953 em_object_mapping_list[i].element_em;
7955 mapping_initialized = TRUE;
7958 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7960 Warn("invalid RND level element %d", element_rnd);
7965 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7968 int map_element_EM_to_RND_cave(int element_em_cave)
7970 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7971 static boolean mapping_initialized = FALSE;
7973 if (!mapping_initialized)
7977 // return "EL_UNKNOWN" for all undefined elements in mapping array
7978 for (i = 0; i < GAME_TILE_MAX; i++)
7979 mapping_EM_to_RND[i] = EL_UNKNOWN;
7981 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7982 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7983 em_object_mapping_list[i].element_rnd;
7985 mapping_initialized = TRUE;
7988 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7990 Warn("invalid EM cave element %d", element_em_cave);
7995 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7998 int map_element_EM_to_RND_game(int element_em_game)
8000 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8001 static boolean mapping_initialized = FALSE;
8003 if (!mapping_initialized)
8007 // return "EL_UNKNOWN" for all undefined elements in mapping array
8008 for (i = 0; i < GAME_TILE_MAX; i++)
8009 mapping_EM_to_RND[i] = EL_UNKNOWN;
8011 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8012 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8013 em_object_mapping_list[i].element_rnd;
8015 mapping_initialized = TRUE;
8018 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8020 Warn("invalid EM game element %d", element_em_game);
8025 return mapping_EM_to_RND[element_em_game];
8028 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8030 struct LevelInfo_EM *level_em = level->native_em_level;
8031 struct CAVE *cav = level_em->cav;
8034 for (i = 0; i < GAME_TILE_MAX; i++)
8035 cav->android_array[i] = Cblank;
8037 for (i = 0; i < level->num_android_clone_elements; i++)
8039 int element_rnd = level->android_clone_element[i];
8040 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8042 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8043 if (em_object_mapping_list[j].element_rnd == element_rnd)
8044 cav->android_array[em_object_mapping_list[j].element_em] =
8049 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8051 struct LevelInfo_EM *level_em = level->native_em_level;
8052 struct CAVE *cav = level_em->cav;
8055 level->num_android_clone_elements = 0;
8057 for (i = 0; i < GAME_TILE_MAX; i++)
8059 int element_em_cave = cav->android_array[i];
8061 boolean element_found = FALSE;
8063 if (element_em_cave == Cblank)
8066 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8068 for (j = 0; j < level->num_android_clone_elements; j++)
8069 if (level->android_clone_element[j] == element_rnd)
8070 element_found = TRUE;
8074 level->android_clone_element[level->num_android_clone_elements++] =
8077 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8082 if (level->num_android_clone_elements == 0)
8084 level->num_android_clone_elements = 1;
8085 level->android_clone_element[0] = EL_EMPTY;
8089 int map_direction_RND_to_EM(int direction)
8091 return (direction == MV_UP ? 0 :
8092 direction == MV_RIGHT ? 1 :
8093 direction == MV_DOWN ? 2 :
8094 direction == MV_LEFT ? 3 :
8098 int map_direction_EM_to_RND(int direction)
8100 return (direction == 0 ? MV_UP :
8101 direction == 1 ? MV_RIGHT :
8102 direction == 2 ? MV_DOWN :
8103 direction == 3 ? MV_LEFT :
8107 int map_element_RND_to_SP(int element_rnd)
8109 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8111 if (element_rnd >= EL_SP_START &&
8112 element_rnd <= EL_SP_END)
8113 element_sp = element_rnd - EL_SP_START;
8114 else if (element_rnd == EL_EMPTY_SPACE)
8116 else if (element_rnd == EL_INVISIBLE_WALL)
8122 int map_element_SP_to_RND(int element_sp)
8124 int element_rnd = EL_UNKNOWN;
8126 if (element_sp >= 0x00 &&
8128 element_rnd = EL_SP_START + element_sp;
8129 else if (element_sp == 0x28)
8130 element_rnd = EL_INVISIBLE_WALL;
8135 int map_action_SP_to_RND(int action_sp)
8139 case actActive: return ACTION_ACTIVE;
8140 case actImpact: return ACTION_IMPACT;
8141 case actExploding: return ACTION_EXPLODING;
8142 case actDigging: return ACTION_DIGGING;
8143 case actSnapping: return ACTION_SNAPPING;
8144 case actCollecting: return ACTION_COLLECTING;
8145 case actPassing: return ACTION_PASSING;
8146 case actPushing: return ACTION_PUSHING;
8147 case actDropping: return ACTION_DROPPING;
8149 default: return ACTION_DEFAULT;
8153 int map_element_RND_to_MM(int element_rnd)
8155 return (element_rnd >= EL_MM_START_1 &&
8156 element_rnd <= EL_MM_END_1 ?
8157 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8159 element_rnd >= EL_MM_START_2 &&
8160 element_rnd <= EL_MM_END_2 ?
8161 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8163 element_rnd >= EL_CHAR_START &&
8164 element_rnd <= EL_CHAR_END ?
8165 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8167 element_rnd >= EL_MM_RUNTIME_START &&
8168 element_rnd <= EL_MM_RUNTIME_END ?
8169 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8171 element_rnd >= EL_MM_DUMMY_START &&
8172 element_rnd <= EL_MM_DUMMY_END ?
8173 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8175 EL_MM_EMPTY_NATIVE);
8178 int map_element_MM_to_RND(int element_mm)
8180 return (element_mm == EL_MM_EMPTY_NATIVE ||
8181 element_mm == EL_DF_EMPTY_NATIVE ?
8184 element_mm >= EL_MM_START_1_NATIVE &&
8185 element_mm <= EL_MM_END_1_NATIVE ?
8186 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8188 element_mm >= EL_MM_START_2_NATIVE &&
8189 element_mm <= EL_MM_END_2_NATIVE ?
8190 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8192 element_mm >= EL_MM_CHAR_START_NATIVE &&
8193 element_mm <= EL_MM_CHAR_END_NATIVE ?
8194 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8196 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8197 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8198 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8200 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8201 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8202 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8207 int map_action_MM_to_RND(int action_mm)
8209 // all MM actions are defined to exactly match their RND counterparts
8213 int map_sound_MM_to_RND(int sound_mm)
8217 case SND_MM_GAME_LEVELTIME_CHARGING:
8218 return SND_GAME_LEVELTIME_CHARGING;
8220 case SND_MM_GAME_HEALTH_CHARGING:
8221 return SND_GAME_HEALTH_CHARGING;
8224 return SND_UNDEFINED;
8228 int map_mm_wall_element(int element)
8230 return (element >= EL_MM_STEEL_WALL_START &&
8231 element <= EL_MM_STEEL_WALL_END ?
8234 element >= EL_MM_WOODEN_WALL_START &&
8235 element <= EL_MM_WOODEN_WALL_END ?
8238 element >= EL_MM_ICE_WALL_START &&
8239 element <= EL_MM_ICE_WALL_END ?
8242 element >= EL_MM_AMOEBA_WALL_START &&
8243 element <= EL_MM_AMOEBA_WALL_END ?
8246 element >= EL_DF_STEEL_WALL_START &&
8247 element <= EL_DF_STEEL_WALL_END ?
8250 element >= EL_DF_WOODEN_WALL_START &&
8251 element <= EL_DF_WOODEN_WALL_END ?
8257 int map_mm_wall_element_editor(int element)
8261 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8262 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8263 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8264 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8265 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8266 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8268 default: return element;
8272 int get_next_element(int element)
8276 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8277 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8278 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8279 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8280 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8281 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8282 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8283 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8284 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8285 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8286 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8288 default: return element;
8292 int el2img_mm(int element_mm)
8294 return el2img(map_element_MM_to_RND(element_mm));
8297 int el_act_dir2img(int element, int action, int direction)
8299 element = GFX_ELEMENT(element);
8300 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8302 // direction_graphic[][] == graphic[] for undefined direction graphics
8303 return element_info[element].direction_graphic[action][direction];
8306 static int el_act_dir2crm(int element, int action, int direction)
8308 element = GFX_ELEMENT(element);
8309 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8311 // direction_graphic[][] == graphic[] for undefined direction graphics
8312 return element_info[element].direction_crumbled[action][direction];
8315 int el_act2img(int element, int action)
8317 element = GFX_ELEMENT(element);
8319 return element_info[element].graphic[action];
8322 int el_act2crm(int element, int action)
8324 element = GFX_ELEMENT(element);
8326 return element_info[element].crumbled[action];
8329 int el_dir2img(int element, int direction)
8331 element = GFX_ELEMENT(element);
8333 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8336 int el2baseimg(int element)
8338 return element_info[element].graphic[ACTION_DEFAULT];
8341 int el2img(int element)
8343 element = GFX_ELEMENT(element);
8345 return element_info[element].graphic[ACTION_DEFAULT];
8348 int el2edimg(int element)
8350 element = GFX_ELEMENT(element);
8352 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8355 int el2preimg(int element)
8357 element = GFX_ELEMENT(element);
8359 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8362 int el2panelimg(int element)
8364 element = GFX_ELEMENT(element);
8366 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8369 int font2baseimg(int font_nr)
8371 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8374 int getBeltNrFromBeltElement(int element)
8376 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8377 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8378 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8381 int getBeltNrFromBeltActiveElement(int element)
8383 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8384 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8385 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8388 int getBeltNrFromBeltSwitchElement(int element)
8390 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8391 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8392 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8395 int getBeltDirNrFromBeltElement(int element)
8397 static int belt_base_element[4] =
8399 EL_CONVEYOR_BELT_1_LEFT,
8400 EL_CONVEYOR_BELT_2_LEFT,
8401 EL_CONVEYOR_BELT_3_LEFT,
8402 EL_CONVEYOR_BELT_4_LEFT
8405 int belt_nr = getBeltNrFromBeltElement(element);
8406 int belt_dir_nr = element - belt_base_element[belt_nr];
8408 return (belt_dir_nr % 3);
8411 int getBeltDirNrFromBeltSwitchElement(int element)
8413 static int belt_base_element[4] =
8415 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8416 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8417 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8418 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8421 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8422 int belt_dir_nr = element - belt_base_element[belt_nr];
8424 return (belt_dir_nr % 3);
8427 int getBeltDirFromBeltElement(int element)
8429 static int belt_move_dir[3] =
8436 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8438 return belt_move_dir[belt_dir_nr];
8441 int getBeltDirFromBeltSwitchElement(int element)
8443 static int belt_move_dir[3] =
8450 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8452 return belt_move_dir[belt_dir_nr];
8455 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8457 static int belt_base_element[4] =
8459 EL_CONVEYOR_BELT_1_LEFT,
8460 EL_CONVEYOR_BELT_2_LEFT,
8461 EL_CONVEYOR_BELT_3_LEFT,
8462 EL_CONVEYOR_BELT_4_LEFT
8465 return belt_base_element[belt_nr] + belt_dir_nr;
8468 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8470 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8472 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8475 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8477 static int belt_base_element[4] =
8479 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8480 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8481 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8482 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8485 return belt_base_element[belt_nr] + belt_dir_nr;
8488 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8490 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8492 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8495 boolean swapTiles_EM(boolean is_pre_emc_cave)
8497 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8500 boolean getTeamMode_EM(void)
8502 return game.team_mode || network_playing;
8505 boolean isActivePlayer_EM(int player_nr)
8507 return stored_player[player_nr].active;
8510 unsigned int InitRND(int seed)
8512 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8513 return InitEngineRandom_EM(seed);
8514 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8515 return InitEngineRandom_SP(seed);
8516 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8517 return InitEngineRandom_MM(seed);
8519 return InitEngineRandom_RND(seed);
8522 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8523 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8525 static int get_effective_element_EM(int tile, int frame_em)
8527 int element = object_mapping[tile].element_rnd;
8528 int action = object_mapping[tile].action;
8529 boolean is_backside = object_mapping[tile].is_backside;
8530 boolean action_removing = (action == ACTION_DIGGING ||
8531 action == ACTION_SNAPPING ||
8532 action == ACTION_COLLECTING);
8540 return (frame_em > 5 ? EL_EMPTY : element);
8546 else // frame_em == 7
8557 case Ydiamond_stone:
8561 case Xdrip_stretchB:
8577 case Ymagnify_blank:
8580 case Xsand_stonein_1:
8581 case Xsand_stonein_2:
8582 case Xsand_stonein_3:
8583 case Xsand_stonein_4:
8587 return (is_backside || action_removing ? EL_EMPTY : element);
8592 static boolean check_linear_animation_EM(int tile)
8596 case Xsand_stonesand_1:
8597 case Xsand_stonesand_quickout_1:
8598 case Xsand_sandstone_1:
8599 case Xsand_stonein_1:
8600 case Xsand_stoneout_1:
8628 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8629 boolean has_crumbled_graphics,
8630 int crumbled, int sync_frame)
8632 // if element can be crumbled, but certain action graphics are just empty
8633 // space (like instantly snapping sand to empty space in 1 frame), do not
8634 // treat these empty space graphics as crumbled graphics in EMC engine
8635 if (crumbled == IMG_EMPTY_SPACE)
8636 has_crumbled_graphics = FALSE;
8638 if (has_crumbled_graphics)
8640 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8641 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8642 g_crumbled->anim_delay,
8643 g_crumbled->anim_mode,
8644 g_crumbled->anim_start_frame,
8647 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8648 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8650 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8651 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8653 g_em->has_crumbled_graphics = TRUE;
8657 g_em->crumbled_bitmap = NULL;
8658 g_em->crumbled_src_x = 0;
8659 g_em->crumbled_src_y = 0;
8660 g_em->crumbled_border_size = 0;
8661 g_em->crumbled_tile_size = 0;
8663 g_em->has_crumbled_graphics = FALSE;
8668 void ResetGfxAnimation_EM(int x, int y, int tile)
8674 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8675 int tile, int frame_em, int x, int y)
8677 int action = object_mapping[tile].action;
8678 int direction = object_mapping[tile].direction;
8679 int effective_element = get_effective_element_EM(tile, frame_em);
8680 int graphic = (direction == MV_NONE ?
8681 el_act2img(effective_element, action) :
8682 el_act_dir2img(effective_element, action, direction));
8683 struct GraphicInfo *g = &graphic_info[graphic];
8685 boolean action_removing = (action == ACTION_DIGGING ||
8686 action == ACTION_SNAPPING ||
8687 action == ACTION_COLLECTING);
8688 boolean action_moving = (action == ACTION_FALLING ||
8689 action == ACTION_MOVING ||
8690 action == ACTION_PUSHING ||
8691 action == ACTION_EATING ||
8692 action == ACTION_FILLING ||
8693 action == ACTION_EMPTYING);
8694 boolean action_falling = (action == ACTION_FALLING ||
8695 action == ACTION_FILLING ||
8696 action == ACTION_EMPTYING);
8698 // special case: graphic uses "2nd movement tile" and has defined
8699 // 7 frames for movement animation (or less) => use default graphic
8700 // for last (8th) frame which ends the movement animation
8701 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8703 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8704 graphic = (direction == MV_NONE ?
8705 el_act2img(effective_element, action) :
8706 el_act_dir2img(effective_element, action, direction));
8708 g = &graphic_info[graphic];
8711 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8715 else if (action_moving)
8717 boolean is_backside = object_mapping[tile].is_backside;
8721 int direction = object_mapping[tile].direction;
8722 int move_dir = (action_falling ? MV_DOWN : direction);
8727 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8728 if (g->double_movement && frame_em == 0)
8732 if (move_dir == MV_LEFT)
8733 GfxFrame[x - 1][y] = GfxFrame[x][y];
8734 else if (move_dir == MV_RIGHT)
8735 GfxFrame[x + 1][y] = GfxFrame[x][y];
8736 else if (move_dir == MV_UP)
8737 GfxFrame[x][y - 1] = GfxFrame[x][y];
8738 else if (move_dir == MV_DOWN)
8739 GfxFrame[x][y + 1] = GfxFrame[x][y];
8746 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8747 if (tile == Xsand_stonesand_quickout_1 ||
8748 tile == Xsand_stonesand_quickout_2)
8752 if (graphic_info[graphic].anim_global_sync)
8753 sync_frame = FrameCounter;
8754 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8755 sync_frame = GfxFrame[x][y];
8757 sync_frame = 0; // playfield border (pseudo steel)
8759 SetRandomAnimationValue(x, y);
8761 int frame = getAnimationFrame(g->anim_frames,
8764 g->anim_start_frame,
8767 g_em->unique_identifier =
8768 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8771 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8772 int tile, int frame_em, int x, int y)
8774 int action = object_mapping[tile].action;
8775 int direction = object_mapping[tile].direction;
8776 boolean is_backside = object_mapping[tile].is_backside;
8777 int effective_element = get_effective_element_EM(tile, frame_em);
8778 int effective_action = action;
8779 int graphic = (direction == MV_NONE ?
8780 el_act2img(effective_element, effective_action) :
8781 el_act_dir2img(effective_element, effective_action,
8783 int crumbled = (direction == MV_NONE ?
8784 el_act2crm(effective_element, effective_action) :
8785 el_act_dir2crm(effective_element, effective_action,
8787 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8788 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8789 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8790 struct GraphicInfo *g = &graphic_info[graphic];
8793 // special case: graphic uses "2nd movement tile" and has defined
8794 // 7 frames for movement animation (or less) => use default graphic
8795 // for last (8th) frame which ends the movement animation
8796 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8798 effective_action = ACTION_DEFAULT;
8799 graphic = (direction == MV_NONE ?
8800 el_act2img(effective_element, effective_action) :
8801 el_act_dir2img(effective_element, effective_action,
8803 crumbled = (direction == MV_NONE ?
8804 el_act2crm(effective_element, effective_action) :
8805 el_act_dir2crm(effective_element, effective_action,
8808 g = &graphic_info[graphic];
8811 if (graphic_info[graphic].anim_global_sync)
8812 sync_frame = FrameCounter;
8813 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8814 sync_frame = GfxFrame[x][y];
8816 sync_frame = 0; // playfield border (pseudo steel)
8818 SetRandomAnimationValue(x, y);
8820 int frame = getAnimationFrame(g->anim_frames,
8823 g->anim_start_frame,
8826 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8827 g->double_movement && is_backside);
8829 // (updating the "crumbled" graphic definitions is probably not really needed,
8830 // as animations for crumbled graphics can't be longer than one EMC cycle)
8831 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8835 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8836 int player_nr, int anim, int frame_em)
8838 int element = player_mapping[player_nr][anim].element_rnd;
8839 int action = player_mapping[player_nr][anim].action;
8840 int direction = player_mapping[player_nr][anim].direction;
8841 int graphic = (direction == MV_NONE ?
8842 el_act2img(element, action) :
8843 el_act_dir2img(element, action, direction));
8844 struct GraphicInfo *g = &graphic_info[graphic];
8847 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8849 stored_player[player_nr].StepFrame = frame_em;
8851 sync_frame = stored_player[player_nr].Frame;
8853 int frame = getAnimationFrame(g->anim_frames,
8856 g->anim_start_frame,
8859 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8860 &g_em->src_x, &g_em->src_y, FALSE);
8863 void InitGraphicInfo_EM(void)
8867 // always start with reliable default values
8868 for (i = 0; i < GAME_TILE_MAX; i++)
8870 object_mapping[i].element_rnd = EL_UNKNOWN;
8871 object_mapping[i].is_backside = FALSE;
8872 object_mapping[i].action = ACTION_DEFAULT;
8873 object_mapping[i].direction = MV_NONE;
8876 // always start with reliable default values
8877 for (p = 0; p < MAX_PLAYERS; p++)
8879 for (i = 0; i < PLY_MAX; i++)
8881 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8882 player_mapping[p][i].action = ACTION_DEFAULT;
8883 player_mapping[p][i].direction = MV_NONE;
8887 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8889 int e = em_object_mapping_list[i].element_em;
8891 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8892 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8894 if (em_object_mapping_list[i].action != -1)
8895 object_mapping[e].action = em_object_mapping_list[i].action;
8897 if (em_object_mapping_list[i].direction != -1)
8898 object_mapping[e].direction =
8899 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8902 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8904 int a = em_player_mapping_list[i].action_em;
8905 int p = em_player_mapping_list[i].player_nr;
8907 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8909 if (em_player_mapping_list[i].action != -1)
8910 player_mapping[p][a].action = em_player_mapping_list[i].action;
8912 if (em_player_mapping_list[i].direction != -1)
8913 player_mapping[p][a].direction =
8914 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8917 for (i = 0; i < GAME_TILE_MAX; i++)
8919 int element = object_mapping[i].element_rnd;
8920 int action = object_mapping[i].action;
8921 int direction = object_mapping[i].direction;
8922 boolean is_backside = object_mapping[i].is_backside;
8923 boolean action_exploding = ((action == ACTION_EXPLODING ||
8924 action == ACTION_SMASHED_BY_ROCK ||
8925 action == ACTION_SMASHED_BY_SPRING) &&
8926 element != EL_DIAMOND);
8927 boolean action_active = (action == ACTION_ACTIVE);
8928 boolean action_other = (action == ACTION_OTHER);
8930 for (j = 0; j < 8; j++)
8932 int effective_element = get_effective_element_EM(i, j);
8933 int effective_action = (j < 7 ? action :
8934 i == Xdrip_stretch ? action :
8935 i == Xdrip_stretchB ? action :
8936 i == Ydrip_1_s ? action :
8937 i == Ydrip_1_sB ? action :
8938 i == Yball_1 ? action :
8939 i == Xball_2 ? action :
8940 i == Yball_2 ? action :
8941 i == Yball_blank ? action :
8942 i == Ykey_1_blank ? action :
8943 i == Ykey_2_blank ? action :
8944 i == Ykey_3_blank ? action :
8945 i == Ykey_4_blank ? action :
8946 i == Ykey_5_blank ? action :
8947 i == Ykey_6_blank ? action :
8948 i == Ykey_7_blank ? action :
8949 i == Ykey_8_blank ? action :
8950 i == Ylenses_blank ? action :
8951 i == Ymagnify_blank ? action :
8952 i == Ygrass_blank ? action :
8953 i == Ydirt_blank ? action :
8954 i == Xsand_stonein_1 ? action :
8955 i == Xsand_stonein_2 ? action :
8956 i == Xsand_stonein_3 ? action :
8957 i == Xsand_stonein_4 ? action :
8958 i == Xsand_stoneout_1 ? action :
8959 i == Xsand_stoneout_2 ? action :
8960 i == Xboom_android ? ACTION_EXPLODING :
8961 action_exploding ? ACTION_EXPLODING :
8962 action_active ? action :
8963 action_other ? action :
8965 int graphic = (el_act_dir2img(effective_element, effective_action,
8967 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8969 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8970 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8971 boolean has_action_graphics = (graphic != base_graphic);
8972 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8973 struct GraphicInfo *g = &graphic_info[graphic];
8974 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8977 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8978 boolean special_animation = (action != ACTION_DEFAULT &&
8979 g->anim_frames == 3 &&
8980 g->anim_delay == 2 &&
8981 g->anim_mode & ANIM_LINEAR);
8982 int sync_frame = (i == Xdrip_stretch ? 7 :
8983 i == Xdrip_stretchB ? 7 :
8984 i == Ydrip_2_s ? j + 8 :
8985 i == Ydrip_2_sB ? j + 8 :
8994 i == Xfake_acid_1 ? 0 :
8995 i == Xfake_acid_2 ? 10 :
8996 i == Xfake_acid_3 ? 20 :
8997 i == Xfake_acid_4 ? 30 :
8998 i == Xfake_acid_5 ? 40 :
8999 i == Xfake_acid_6 ? 50 :
9000 i == Xfake_acid_7 ? 60 :
9001 i == Xfake_acid_8 ? 70 :
9002 i == Xfake_acid_1_player ? 0 :
9003 i == Xfake_acid_2_player ? 10 :
9004 i == Xfake_acid_3_player ? 20 :
9005 i == Xfake_acid_4_player ? 30 :
9006 i == Xfake_acid_5_player ? 40 :
9007 i == Xfake_acid_6_player ? 50 :
9008 i == Xfake_acid_7_player ? 60 :
9009 i == Xfake_acid_8_player ? 70 :
9011 i == Yball_2 ? j + 8 :
9012 i == Yball_blank ? j + 1 :
9013 i == Ykey_1_blank ? j + 1 :
9014 i == Ykey_2_blank ? j + 1 :
9015 i == Ykey_3_blank ? j + 1 :
9016 i == Ykey_4_blank ? j + 1 :
9017 i == Ykey_5_blank ? j + 1 :
9018 i == Ykey_6_blank ? j + 1 :
9019 i == Ykey_7_blank ? j + 1 :
9020 i == Ykey_8_blank ? j + 1 :
9021 i == Ylenses_blank ? j + 1 :
9022 i == Ymagnify_blank ? j + 1 :
9023 i == Ygrass_blank ? j + 1 :
9024 i == Ydirt_blank ? j + 1 :
9025 i == Xamoeba_1 ? 0 :
9026 i == Xamoeba_2 ? 1 :
9027 i == Xamoeba_3 ? 2 :
9028 i == Xamoeba_4 ? 3 :
9029 i == Xamoeba_5 ? 0 :
9030 i == Xamoeba_6 ? 1 :
9031 i == Xamoeba_7 ? 2 :
9032 i == Xamoeba_8 ? 3 :
9033 i == Xexit_2 ? j + 8 :
9034 i == Xexit_3 ? j + 16 :
9035 i == Xdynamite_1 ? 0 :
9036 i == Xdynamite_2 ? 8 :
9037 i == Xdynamite_3 ? 16 :
9038 i == Xdynamite_4 ? 24 :
9039 i == Xsand_stonein_1 ? j + 1 :
9040 i == Xsand_stonein_2 ? j + 9 :
9041 i == Xsand_stonein_3 ? j + 17 :
9042 i == Xsand_stonein_4 ? j + 25 :
9043 i == Xsand_stoneout_1 && j == 0 ? 0 :
9044 i == Xsand_stoneout_1 && j == 1 ? 0 :
9045 i == Xsand_stoneout_1 && j == 2 ? 1 :
9046 i == Xsand_stoneout_1 && j == 3 ? 2 :
9047 i == Xsand_stoneout_1 && j == 4 ? 2 :
9048 i == Xsand_stoneout_1 && j == 5 ? 3 :
9049 i == Xsand_stoneout_1 && j == 6 ? 4 :
9050 i == Xsand_stoneout_1 && j == 7 ? 4 :
9051 i == Xsand_stoneout_2 && j == 0 ? 5 :
9052 i == Xsand_stoneout_2 && j == 1 ? 6 :
9053 i == Xsand_stoneout_2 && j == 2 ? 7 :
9054 i == Xsand_stoneout_2 && j == 3 ? 8 :
9055 i == Xsand_stoneout_2 && j == 4 ? 9 :
9056 i == Xsand_stoneout_2 && j == 5 ? 11 :
9057 i == Xsand_stoneout_2 && j == 6 ? 13 :
9058 i == Xsand_stoneout_2 && j == 7 ? 15 :
9059 i == Xboom_bug && j == 1 ? 2 :
9060 i == Xboom_bug && j == 2 ? 2 :
9061 i == Xboom_bug && j == 3 ? 4 :
9062 i == Xboom_bug && j == 4 ? 4 :
9063 i == Xboom_bug && j == 5 ? 2 :
9064 i == Xboom_bug && j == 6 ? 2 :
9065 i == Xboom_bug && j == 7 ? 0 :
9066 i == Xboom_tank && j == 1 ? 2 :
9067 i == Xboom_tank && j == 2 ? 2 :
9068 i == Xboom_tank && j == 3 ? 4 :
9069 i == Xboom_tank && j == 4 ? 4 :
9070 i == Xboom_tank && j == 5 ? 2 :
9071 i == Xboom_tank && j == 6 ? 2 :
9072 i == Xboom_tank && j == 7 ? 0 :
9073 i == Xboom_android && j == 7 ? 6 :
9074 i == Xboom_1 && j == 1 ? 2 :
9075 i == Xboom_1 && j == 2 ? 2 :
9076 i == Xboom_1 && j == 3 ? 4 :
9077 i == Xboom_1 && j == 4 ? 4 :
9078 i == Xboom_1 && j == 5 ? 6 :
9079 i == Xboom_1 && j == 6 ? 6 :
9080 i == Xboom_1 && j == 7 ? 8 :
9081 i == Xboom_2 && j == 0 ? 8 :
9082 i == Xboom_2 && j == 1 ? 8 :
9083 i == Xboom_2 && j == 2 ? 10 :
9084 i == Xboom_2 && j == 3 ? 10 :
9085 i == Xboom_2 && j == 4 ? 10 :
9086 i == Xboom_2 && j == 5 ? 12 :
9087 i == Xboom_2 && j == 6 ? 12 :
9088 i == Xboom_2 && j == 7 ? 12 :
9089 special_animation && j == 4 ? 3 :
9090 effective_action != action ? 0 :
9092 int frame = getAnimationFrame(g->anim_frames,
9095 g->anim_start_frame,
9098 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9099 g->double_movement && is_backside);
9101 g_em->bitmap = src_bitmap;
9102 g_em->src_x = src_x;
9103 g_em->src_y = src_y;
9104 g_em->src_offset_x = 0;
9105 g_em->src_offset_y = 0;
9106 g_em->dst_offset_x = 0;
9107 g_em->dst_offset_y = 0;
9108 g_em->width = TILEX;
9109 g_em->height = TILEY;
9111 g_em->preserve_background = FALSE;
9113 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9116 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9117 effective_action == ACTION_MOVING ||
9118 effective_action == ACTION_PUSHING ||
9119 effective_action == ACTION_EATING)) ||
9120 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9121 effective_action == ACTION_EMPTYING)))
9124 (effective_action == ACTION_FALLING ||
9125 effective_action == ACTION_FILLING ||
9126 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9127 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9128 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9129 int num_steps = (i == Ydrip_1_s ? 16 :
9130 i == Ydrip_1_sB ? 16 :
9131 i == Ydrip_2_s ? 16 :
9132 i == Ydrip_2_sB ? 16 :
9133 i == Xsand_stonein_1 ? 32 :
9134 i == Xsand_stonein_2 ? 32 :
9135 i == Xsand_stonein_3 ? 32 :
9136 i == Xsand_stonein_4 ? 32 :
9137 i == Xsand_stoneout_1 ? 16 :
9138 i == Xsand_stoneout_2 ? 16 : 8);
9139 int cx = ABS(dx) * (TILEX / num_steps);
9140 int cy = ABS(dy) * (TILEY / num_steps);
9141 int step_frame = (i == Ydrip_2_s ? j + 8 :
9142 i == Ydrip_2_sB ? j + 8 :
9143 i == Xsand_stonein_2 ? j + 8 :
9144 i == Xsand_stonein_3 ? j + 16 :
9145 i == Xsand_stonein_4 ? j + 24 :
9146 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9147 int step = (is_backside ? step_frame : num_steps - step_frame);
9149 if (is_backside) // tile where movement starts
9151 if (dx < 0 || dy < 0)
9153 g_em->src_offset_x = cx * step;
9154 g_em->src_offset_y = cy * step;
9158 g_em->dst_offset_x = cx * step;
9159 g_em->dst_offset_y = cy * step;
9162 else // tile where movement ends
9164 if (dx < 0 || dy < 0)
9166 g_em->dst_offset_x = cx * step;
9167 g_em->dst_offset_y = cy * step;
9171 g_em->src_offset_x = cx * step;
9172 g_em->src_offset_y = cy * step;
9176 g_em->width = TILEX - cx * step;
9177 g_em->height = TILEY - cy * step;
9180 // create unique graphic identifier to decide if tile must be redrawn
9181 /* bit 31 - 16 (16 bit): EM style graphic
9182 bit 15 - 12 ( 4 bit): EM style frame
9183 bit 11 - 6 ( 6 bit): graphic width
9184 bit 5 - 0 ( 6 bit): graphic height */
9185 g_em->unique_identifier =
9186 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9190 for (i = 0; i < GAME_TILE_MAX; i++)
9192 for (j = 0; j < 8; j++)
9194 int element = object_mapping[i].element_rnd;
9195 int action = object_mapping[i].action;
9196 int direction = object_mapping[i].direction;
9197 boolean is_backside = object_mapping[i].is_backside;
9198 int graphic_action = el_act_dir2img(element, action, direction);
9199 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9201 if ((action == ACTION_SMASHED_BY_ROCK ||
9202 action == ACTION_SMASHED_BY_SPRING ||
9203 action == ACTION_EATING) &&
9204 graphic_action == graphic_default)
9206 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9207 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9208 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9209 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9212 // no separate animation for "smashed by rock" -- use rock instead
9213 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9214 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9216 g_em->bitmap = g_xx->bitmap;
9217 g_em->src_x = g_xx->src_x;
9218 g_em->src_y = g_xx->src_y;
9219 g_em->src_offset_x = g_xx->src_offset_x;
9220 g_em->src_offset_y = g_xx->src_offset_y;
9221 g_em->dst_offset_x = g_xx->dst_offset_x;
9222 g_em->dst_offset_y = g_xx->dst_offset_y;
9223 g_em->width = g_xx->width;
9224 g_em->height = g_xx->height;
9225 g_em->unique_identifier = g_xx->unique_identifier;
9228 g_em->preserve_background = TRUE;
9233 for (p = 0; p < MAX_PLAYERS; p++)
9235 for (i = 0; i < PLY_MAX; i++)
9237 int element = player_mapping[p][i].element_rnd;
9238 int action = player_mapping[p][i].action;
9239 int direction = player_mapping[p][i].direction;
9241 for (j = 0; j < 8; j++)
9243 int effective_element = element;
9244 int effective_action = action;
9245 int graphic = (direction == MV_NONE ?
9246 el_act2img(effective_element, effective_action) :
9247 el_act_dir2img(effective_element, effective_action,
9249 struct GraphicInfo *g = &graphic_info[graphic];
9250 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9254 int frame = getAnimationFrame(g->anim_frames,
9257 g->anim_start_frame,
9260 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9262 g_em->bitmap = src_bitmap;
9263 g_em->src_x = src_x;
9264 g_em->src_y = src_y;
9265 g_em->src_offset_x = 0;
9266 g_em->src_offset_y = 0;
9267 g_em->dst_offset_x = 0;
9268 g_em->dst_offset_y = 0;
9269 g_em->width = TILEX;
9270 g_em->height = TILEY;
9276 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9277 boolean any_player_moving,
9278 boolean any_player_snapping,
9279 boolean any_player_dropping)
9281 if (frame == 7 && !any_player_dropping)
9283 if (!local_player->was_waiting)
9285 if (!CheckSaveEngineSnapshotToList())
9288 local_player->was_waiting = TRUE;
9291 else if (any_player_moving || any_player_snapping || any_player_dropping)
9293 local_player->was_waiting = FALSE;
9297 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9298 boolean murphy_is_dropping)
9300 if (murphy_is_waiting)
9302 if (!local_player->was_waiting)
9304 if (!CheckSaveEngineSnapshotToList())
9307 local_player->was_waiting = TRUE;
9312 local_player->was_waiting = FALSE;
9316 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9317 boolean button_released)
9319 if (button_released)
9321 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9322 CheckSaveEngineSnapshotToList();
9324 else if (element_clicked)
9326 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9327 CheckSaveEngineSnapshotToList();
9329 game.snapshot.changed_action = TRUE;
9333 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9334 boolean any_player_moving,
9335 boolean any_player_snapping,
9336 boolean any_player_dropping)
9338 if (tape.single_step && tape.recording && !tape.pausing)
9339 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9340 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9342 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9343 any_player_snapping, any_player_dropping);
9345 return tape.pausing;
9348 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9349 boolean murphy_is_dropping)
9351 boolean murphy_starts_dropping = FALSE;
9354 for (i = 0; i < MAX_PLAYERS; i++)
9355 if (stored_player[i].force_dropping)
9356 murphy_starts_dropping = TRUE;
9358 if (tape.single_step && tape.recording && !tape.pausing)
9359 if (murphy_is_waiting && !murphy_starts_dropping)
9360 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9362 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9365 void CheckSingleStepMode_MM(boolean element_clicked,
9366 boolean button_released)
9368 if (tape.single_step && tape.recording && !tape.pausing)
9369 if (button_released)
9370 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9372 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9375 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9376 int graphic, int sync_frame, int x, int y)
9378 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9380 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9383 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9385 return (IS_NEXT_FRAME(sync_frame, graphic));
9388 int getGraphicInfo_Delay(int graphic)
9390 return graphic_info[graphic].anim_delay;
9393 void PlayMenuSoundExt(int sound)
9395 if (sound == SND_UNDEFINED)
9398 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9399 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9402 if (IS_LOOP_SOUND(sound))
9403 PlaySoundLoop(sound);
9408 void PlayMenuSound(void)
9410 PlayMenuSoundExt(menu.sound[game_status]);
9413 void PlayMenuSoundStereo(int sound, int stereo_position)
9415 if (sound == SND_UNDEFINED)
9418 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9419 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9422 if (IS_LOOP_SOUND(sound))
9423 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9425 PlaySoundStereo(sound, stereo_position);
9428 void PlayMenuSoundIfLoopExt(int sound)
9430 if (sound == SND_UNDEFINED)
9433 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9434 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9437 if (IS_LOOP_SOUND(sound))
9438 PlaySoundLoop(sound);
9441 void PlayMenuSoundIfLoop(void)
9443 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9446 void PlayMenuMusicExt(int music)
9448 if (music == MUS_UNDEFINED)
9451 if (!setup.sound_music)
9454 if (IS_LOOP_MUSIC(music))
9455 PlayMusicLoop(music);
9460 void PlayMenuMusic(void)
9462 char *curr_music = getCurrentlyPlayingMusicFilename();
9463 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9465 if (!strEqual(curr_music, next_music))
9466 PlayMenuMusicExt(menu.music[game_status]);
9469 void PlayMenuSoundsAndMusic(void)
9475 static void FadeMenuSounds(void)
9480 static void FadeMenuMusic(void)
9482 char *curr_music = getCurrentlyPlayingMusicFilename();
9483 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9485 if (!strEqual(curr_music, next_music))
9489 void FadeMenuSoundsAndMusic(void)
9495 void PlaySoundActivating(void)
9498 PlaySound(SND_MENU_ITEM_ACTIVATING);
9502 void PlaySoundSelecting(void)
9505 PlaySound(SND_MENU_ITEM_SELECTING);
9509 void ToggleFullscreenIfNeeded(void)
9511 // if setup and video fullscreen state are already matching, nothing do do
9512 if (setup.fullscreen == video.fullscreen_enabled ||
9513 !video.fullscreen_available)
9516 SDLSetWindowFullscreen(setup.fullscreen);
9518 // set setup value according to successfully changed fullscreen mode
9519 setup.fullscreen = video.fullscreen_enabled;
9522 void ChangeWindowScalingIfNeeded(void)
9524 // if setup and video window scaling are already matching, nothing do do
9525 if (setup.window_scaling_percent == video.window_scaling_percent ||
9526 video.fullscreen_enabled)
9529 SDLSetWindowScaling(setup.window_scaling_percent);
9531 // set setup value according to successfully changed window scaling
9532 setup.window_scaling_percent = video.window_scaling_percent;
9535 void ChangeVsyncModeIfNeeded(void)
9537 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9538 int video_vsync_mode = video.vsync_mode;
9540 // if setup and video vsync mode are already matching, nothing do do
9541 if (setup_vsync_mode == video_vsync_mode)
9544 // if renderer is using OpenGL, vsync mode can directly be changed
9545 SDLSetScreenVsyncMode(setup.vsync_mode);
9547 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9548 if (video.vsync_mode == video_vsync_mode)
9550 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9552 // save backbuffer content which gets lost when re-creating screen
9553 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9555 // force re-creating screen and renderer to set new vsync mode
9556 video.fullscreen_enabled = !setup.fullscreen;
9558 // when creating new renderer, destroy textures linked to old renderer
9559 FreeAllImageTextures(); // needs old renderer to free the textures
9561 // re-create screen and renderer (including change of vsync mode)
9562 ChangeVideoModeIfNeeded(setup.fullscreen);
9564 // set setup value according to successfully changed fullscreen mode
9565 setup.fullscreen = video.fullscreen_enabled;
9567 // restore backbuffer content from temporary backbuffer backup bitmap
9568 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9569 FreeBitmap(tmp_backbuffer);
9571 // update visible window/screen
9572 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9574 // when changing vsync mode, re-create textures for new renderer
9575 InitImageTextures();
9578 // set setup value according to successfully changed vsync mode
9579 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9582 static void JoinRectangles(int *x, int *y, int *width, int *height,
9583 int x2, int y2, int width2, int height2)
9585 // do not join with "off-screen" rectangle
9586 if (x2 == -1 || y2 == -1)
9591 *width = MAX(*width, width2);
9592 *height = MAX(*height, height2);
9595 void SetAnimStatus(int anim_status_new)
9597 if (anim_status_new == GAME_MODE_MAIN)
9598 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9599 else if (anim_status_new == GAME_MODE_NAMES)
9600 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9601 else if (anim_status_new == GAME_MODE_SCORES)
9602 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9604 global.anim_status_next = anim_status_new;
9606 // directly set screen modes that are entered without fading
9607 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9608 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9609 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9610 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9611 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9612 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9613 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9614 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9615 global.anim_status = global.anim_status_next;
9618 void SetGameStatus(int game_status_new)
9620 if (game_status_new != game_status)
9621 game_status_last_screen = game_status;
9623 game_status = game_status_new;
9625 SetAnimStatus(game_status_new);
9628 void SetFontStatus(int game_status_new)
9630 static int last_game_status = -1;
9632 if (game_status_new != -1)
9634 // set game status for font use after storing last game status
9635 last_game_status = game_status;
9636 game_status = game_status_new;
9640 // reset game status after font use from last stored game status
9641 game_status = last_game_status;
9645 void ResetFontStatus(void)
9650 void SetLevelSetInfo(char *identifier, int level_nr)
9652 setString(&levelset.identifier, identifier);
9654 levelset.level_nr = level_nr;
9657 boolean CheckIfAllViewportsHaveChanged(void)
9659 // if game status has not changed, viewports have not changed either
9660 if (game_status == game_status_last)
9663 // check if all viewports have changed with current game status
9665 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9666 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9667 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9668 int new_real_sx = vp_playfield->x;
9669 int new_real_sy = vp_playfield->y;
9670 int new_full_sxsize = vp_playfield->width;
9671 int new_full_sysize = vp_playfield->height;
9672 int new_dx = vp_door_1->x;
9673 int new_dy = vp_door_1->y;
9674 int new_dxsize = vp_door_1->width;
9675 int new_dysize = vp_door_1->height;
9676 int new_vx = vp_door_2->x;
9677 int new_vy = vp_door_2->y;
9678 int new_vxsize = vp_door_2->width;
9679 int new_vysize = vp_door_2->height;
9681 boolean playfield_viewport_has_changed =
9682 (new_real_sx != REAL_SX ||
9683 new_real_sy != REAL_SY ||
9684 new_full_sxsize != FULL_SXSIZE ||
9685 new_full_sysize != FULL_SYSIZE);
9687 boolean door_1_viewport_has_changed =
9690 new_dxsize != DXSIZE ||
9691 new_dysize != DYSIZE);
9693 boolean door_2_viewport_has_changed =
9696 new_vxsize != VXSIZE ||
9697 new_vysize != VYSIZE ||
9698 game_status_last == GAME_MODE_EDITOR);
9700 return (playfield_viewport_has_changed &&
9701 door_1_viewport_has_changed &&
9702 door_2_viewport_has_changed);
9705 boolean CheckFadeAll(void)
9707 return (CheckIfGlobalBorderHasChanged() ||
9708 CheckIfAllViewportsHaveChanged());
9711 void ChangeViewportPropertiesIfNeeded(void)
9713 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9714 FALSE : setup.small_game_graphics);
9715 int gfx_game_mode = game_status;
9716 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9718 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9719 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9720 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9721 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9722 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9723 int new_win_xsize = vp_window->width;
9724 int new_win_ysize = vp_window->height;
9725 int border_left = vp_playfield->border_left;
9726 int border_right = vp_playfield->border_right;
9727 int border_top = vp_playfield->border_top;
9728 int border_bottom = vp_playfield->border_bottom;
9729 int new_sx = vp_playfield->x + border_left;
9730 int new_sy = vp_playfield->y + border_top;
9731 int new_sxsize = vp_playfield->width - border_left - border_right;
9732 int new_sysize = vp_playfield->height - border_top - border_bottom;
9733 int new_real_sx = vp_playfield->x;
9734 int new_real_sy = vp_playfield->y;
9735 int new_full_sxsize = vp_playfield->width;
9736 int new_full_sysize = vp_playfield->height;
9737 int new_dx = vp_door_1->x;
9738 int new_dy = vp_door_1->y;
9739 int new_dxsize = vp_door_1->width;
9740 int new_dysize = vp_door_1->height;
9741 int new_vx = vp_door_2->x;
9742 int new_vy = vp_door_2->y;
9743 int new_vxsize = vp_door_2->width;
9744 int new_vysize = vp_door_2->height;
9745 int new_ex = vp_door_3->x;
9746 int new_ey = vp_door_3->y;
9747 int new_exsize = vp_door_3->width;
9748 int new_eysize = vp_door_3->height;
9749 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9750 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9751 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9752 int new_scr_fieldx = new_sxsize / tilesize;
9753 int new_scr_fieldy = new_sysize / tilesize;
9754 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9755 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9756 boolean init_gfx_buffers = FALSE;
9757 boolean init_video_buffer = FALSE;
9758 boolean init_gadgets_and_anims = FALSE;
9759 boolean init_em_graphics = FALSE;
9761 if (new_win_xsize != WIN_XSIZE ||
9762 new_win_ysize != WIN_YSIZE)
9764 WIN_XSIZE = new_win_xsize;
9765 WIN_YSIZE = new_win_ysize;
9767 init_video_buffer = TRUE;
9768 init_gfx_buffers = TRUE;
9769 init_gadgets_and_anims = TRUE;
9771 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9774 if (new_scr_fieldx != SCR_FIELDX ||
9775 new_scr_fieldy != SCR_FIELDY)
9777 // this always toggles between MAIN and GAME when using small tile size
9779 SCR_FIELDX = new_scr_fieldx;
9780 SCR_FIELDY = new_scr_fieldy;
9782 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9793 new_sxsize != SXSIZE ||
9794 new_sysize != SYSIZE ||
9795 new_dxsize != DXSIZE ||
9796 new_dysize != DYSIZE ||
9797 new_vxsize != VXSIZE ||
9798 new_vysize != VYSIZE ||
9799 new_exsize != EXSIZE ||
9800 new_eysize != EYSIZE ||
9801 new_real_sx != REAL_SX ||
9802 new_real_sy != REAL_SY ||
9803 new_full_sxsize != FULL_SXSIZE ||
9804 new_full_sysize != FULL_SYSIZE ||
9805 new_tilesize_var != TILESIZE_VAR
9808 // ------------------------------------------------------------------------
9809 // determine next fading area for changed viewport definitions
9810 // ------------------------------------------------------------------------
9812 // start with current playfield area (default fading area)
9815 FADE_SXSIZE = FULL_SXSIZE;
9816 FADE_SYSIZE = FULL_SYSIZE;
9818 // add new playfield area if position or size has changed
9819 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9820 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9822 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9823 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9826 // add current and new door 1 area if position or size has changed
9827 if (new_dx != DX || new_dy != DY ||
9828 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9830 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9831 DX, DY, DXSIZE, DYSIZE);
9832 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9833 new_dx, new_dy, new_dxsize, new_dysize);
9836 // add current and new door 2 area if position or size has changed
9837 if (new_vx != VX || new_vy != VY ||
9838 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9840 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9841 VX, VY, VXSIZE, VYSIZE);
9842 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9843 new_vx, new_vy, new_vxsize, new_vysize);
9846 // ------------------------------------------------------------------------
9847 // handle changed tile size
9848 // ------------------------------------------------------------------------
9850 if (new_tilesize_var != TILESIZE_VAR)
9852 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9854 // changing tile size invalidates scroll values of engine snapshots
9855 FreeEngineSnapshotSingle();
9857 // changing tile size requires update of graphic mapping for EM engine
9858 init_em_graphics = TRUE;
9869 SXSIZE = new_sxsize;
9870 SYSIZE = new_sysize;
9871 DXSIZE = new_dxsize;
9872 DYSIZE = new_dysize;
9873 VXSIZE = new_vxsize;
9874 VYSIZE = new_vysize;
9875 EXSIZE = new_exsize;
9876 EYSIZE = new_eysize;
9877 REAL_SX = new_real_sx;
9878 REAL_SY = new_real_sy;
9879 FULL_SXSIZE = new_full_sxsize;
9880 FULL_SYSIZE = new_full_sysize;
9881 TILESIZE_VAR = new_tilesize_var;
9883 init_gfx_buffers = TRUE;
9884 init_gadgets_and_anims = TRUE;
9886 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9887 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9890 if (init_gfx_buffers)
9892 // Debug("tools:viewport", "init_gfx_buffers");
9894 SCR_FIELDX = new_scr_fieldx_buffers;
9895 SCR_FIELDY = new_scr_fieldy_buffers;
9899 SCR_FIELDX = new_scr_fieldx;
9900 SCR_FIELDY = new_scr_fieldy;
9902 SetDrawDeactivationMask(REDRAW_NONE);
9903 SetDrawBackgroundMask(REDRAW_FIELD);
9906 if (init_video_buffer)
9908 // Debug("tools:viewport", "init_video_buffer");
9910 FreeAllImageTextures(); // needs old renderer to free the textures
9912 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9913 InitImageTextures();
9916 if (init_gadgets_and_anims)
9918 // Debug("tools:viewport", "init_gadgets_and_anims");
9921 InitGlobalAnimations();
9924 if (init_em_graphics)
9926 InitGraphicInfo_EM();
9931 // ============================================================================
9933 // ============================================================================
9935 #if defined(PLATFORM_WIN32)
9936 /* FILETIME of Jan 1 1970 00:00:00. */
9937 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9940 * timezone information is stored outside the kernel so tzp isn't used anymore.
9942 * Note: this function is not for Win32 high precision timing purpose. See
9945 int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9948 SYSTEMTIME system_time;
9949 ULARGE_INTEGER ularge;
9951 GetSystemTime(&system_time);
9952 SystemTimeToFileTime(&system_time, &file_time);
9953 ularge.LowPart = file_time.dwLowDateTime;
9954 ularge.HighPart = file_time.dwHighDateTime;
9956 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9957 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9963 static char *test_init_uuid_random_function_simple(void)
9965 static char seed_text[100];
9966 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9968 sprintf(seed_text, "%d", seed);
9973 static char *test_init_uuid_random_function_better(void)
9975 static char seed_text[100];
9976 struct timeval current_time;
9978 gettimeofday(¤t_time, NULL);
9980 prng_seed_bytes(¤t_time, sizeof(current_time));
9982 sprintf(seed_text, "%ld.%ld",
9983 (long)current_time.tv_sec,
9984 (long)current_time.tv_usec);
9989 #if defined(PLATFORM_WIN32)
9990 static char *test_init_uuid_random_function_better_windows(void)
9992 static char seed_text[100];
9993 struct timeval current_time;
9995 gettimeofday_windows(¤t_time, NULL);
9997 prng_seed_bytes(¤t_time, sizeof(current_time));
9999 sprintf(seed_text, "%ld.%ld",
10000 (long)current_time.tv_sec,
10001 (long)current_time.tv_usec);
10007 static unsigned int test_uuid_random_function_simple(int max)
10009 return GetSimpleRandom(max);
10012 static unsigned int test_uuid_random_function_better(int max)
10014 return (max > 0 ? prng_get_uint() % max : 0);
10017 #if defined(PLATFORM_WIN32)
10018 #define NUM_UUID_TESTS 3
10020 #define NUM_UUID_TESTS 2
10023 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10025 struct hashtable *hash_seeds =
10026 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10027 struct hashtable *hash_uuids =
10028 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10029 static char message[100];
10032 char *random_name = (nr == 0 ? "simple" : "better");
10033 char *random_type = (always_seed ? "always" : "only once");
10034 char *(*init_random_function)(void) =
10036 test_init_uuid_random_function_simple :
10037 test_init_uuid_random_function_better);
10038 unsigned int (*random_function)(int) =
10040 test_uuid_random_function_simple :
10041 test_uuid_random_function_better);
10044 #if defined(PLATFORM_WIN32)
10047 random_name = "windows";
10048 init_random_function = test_init_uuid_random_function_better_windows;
10054 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10055 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10057 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10058 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10059 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10061 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10065 // always initialize random number generator at least once
10066 init_random_function();
10068 unsigned int time_start = SDL_GetTicks();
10070 for (i = 0; i < num_uuids; i++)
10074 char *seed = getStringCopy(init_random_function());
10076 hashtable_remove(hash_seeds, seed);
10077 hashtable_insert(hash_seeds, seed, "1");
10080 char *uuid = getStringCopy(getUUIDExt(random_function));
10082 hashtable_remove(hash_uuids, uuid);
10083 hashtable_insert(hash_uuids, uuid, "1");
10086 int num_unique_seeds = hashtable_count(hash_seeds);
10087 int num_unique_uuids = hashtable_count(hash_uuids);
10089 unsigned int time_needed = SDL_GetTicks() - time_start;
10091 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10093 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10096 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10098 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10099 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10101 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10103 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10105 Request(message, REQ_CONFIRM);
10107 hashtable_destroy(hash_seeds, 0);
10108 hashtable_destroy(hash_uuids, 0);
10111 void TestGeneratingUUIDs(void)
10113 int num_uuids = 1000000;
10116 for (i = 0; i < NUM_UUID_TESTS; i++)
10117 for (j = 0; j < 2; j++)
10118 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10120 CloseAllAndExit(0);