1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 // currently not available
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 // never draw masked screen borders on borderless screens
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD(void)
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void DrawTileCursor(int draw_target)
634 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
639 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
644 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
647 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
652 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653 BlitScreenToBitmap_EM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655 BlitScreenToBitmap_SP(target_bitmap);
656 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657 BlitScreenToBitmap_MM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659 BlitScreenToBitmap_RND(target_bitmap);
661 redraw_mask |= REDRAW_FIELD;
664 static void DrawFramesPerSecond(void)
667 int font_nr = FONT_TEXT_2;
668 int font_width = getFontWidth(font_nr);
669 int draw_deactivation_mask = GetDrawDeactivationMask();
670 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
672 // draw FPS with leading space (needed if field buffer deactivated)
673 sprintf(text, " %04.1f fps", global.frames_per_second);
675 // override draw deactivation mask (required for invisible warp mode)
676 SetDrawDeactivationMask(REDRAW_NONE);
678 // draw opaque FPS if field buffer deactivated, else draw masked FPS
679 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
682 // set draw deactivation mask to previous value
683 SetDrawDeactivationMask(draw_deactivation_mask);
685 // force full-screen redraw in this frame
686 redraw_mask = REDRAW_ALL;
690 static void PrintFrameTimeDebugging(void)
692 static unsigned int last_counter = 0;
693 unsigned int counter = Counter();
694 int diff_1 = counter - last_counter;
695 int diff_2 = diff_1 - GAME_FRAME_DELAY;
697 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698 char diff_bar[2 * diff_2_max + 5];
702 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
704 for (i = 0; i < diff_2_max; i++)
705 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706 i >= diff_2_max - diff_2_cut ? '-' : ' ');
708 diff_bar[pos++] = '|';
710 for (i = 0; i < diff_2_max; i++)
711 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
713 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
715 diff_bar[pos++] = '\0';
717 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
720 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
723 last_counter = counter;
727 static int unifiedRedrawMask(int mask)
729 if (mask & REDRAW_ALL)
732 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
740 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
743 void BackToFront(void)
745 static int last_redraw_mask = REDRAW_NONE;
747 // force screen redraw in every frame to continue drawing global animations
748 // (but always use the last redraw mask to prevent unwanted side effects)
749 if (redraw_mask == REDRAW_NONE)
750 redraw_mask = last_redraw_mask;
752 last_redraw_mask = redraw_mask;
755 // masked border now drawn immediately when blitting backbuffer to window
757 // draw masked border to all viewports, if defined
758 DrawMaskedBorder(redraw_mask);
761 // draw frames per second (only if debug mode is enabled)
762 if (redraw_mask & REDRAW_FPS)
763 DrawFramesPerSecond();
765 // remove playfield redraw before potentially merging with doors redraw
766 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767 redraw_mask &= ~REDRAW_FIELD;
769 // redraw complete window if both playfield and (some) doors need redraw
770 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771 redraw_mask = REDRAW_ALL;
773 /* although redrawing the whole window would be fine for normal gameplay,
774 being able to only redraw the playfield is required for deactivating
775 certain drawing areas (mainly playfield) to work, which is needed for
776 warp-forward to be fast enough (by skipping redraw of most frames) */
778 if (redraw_mask & REDRAW_ALL)
780 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
782 else if (redraw_mask & REDRAW_FIELD)
784 BlitBitmap(backbuffer, window,
785 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
787 else if (redraw_mask & REDRAW_DOORS)
789 // merge door areas to prevent calling screen redraw more than once
795 if (redraw_mask & REDRAW_DOOR_1)
799 x2 = MAX(x2, DX + DXSIZE);
800 y2 = MAX(y2, DY + DYSIZE);
803 if (redraw_mask & REDRAW_DOOR_2)
807 x2 = MAX(x2, VX + VXSIZE);
808 y2 = MAX(y2, VY + VYSIZE);
811 if (redraw_mask & REDRAW_DOOR_3)
815 x2 = MAX(x2, EX + EXSIZE);
816 y2 = MAX(y2, EY + EYSIZE);
819 // make sure that at least one pixel is blitted, and inside the screen
820 // (else nothing is blitted, causing the animations not to be updated)
821 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823 x2 = MIN(MAX(1, x2), WIN_XSIZE);
824 y2 = MIN(MAX(1, y2), WIN_YSIZE);
826 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
829 redraw_mask = REDRAW_NONE;
832 PrintFrameTimeDebugging();
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
838 unsigned int frame_delay_value_old = GetVideoFrameDelay();
840 SetVideoFrameDelay(frame_delay_value);
844 SetVideoFrameDelay(frame_delay_value_old);
847 static int fade_type_skip = FADE_TYPE_NONE;
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
851 void (*draw_border_function)(void) = NULL;
852 int x, y, width, height;
853 int fade_delay, post_delay;
855 if (fade_type == FADE_TYPE_FADE_OUT)
857 if (fade_type_skip != FADE_TYPE_NONE)
859 // skip all fade operations until specified fade operation
860 if (fade_type & fade_type_skip)
861 fade_type_skip = FADE_TYPE_NONE;
866 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
870 redraw_mask |= fade_mask;
872 if (fade_type == FADE_TYPE_SKIP)
874 fade_type_skip = fade_mode;
879 fade_delay = fading.fade_delay;
880 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
882 if (fade_type_skip != FADE_TYPE_NONE)
884 // skip all fade operations until specified fade operation
885 if (fade_type & fade_type_skip)
886 fade_type_skip = FADE_TYPE_NONE;
891 if (global.autoplay_leveldir)
896 if (fade_mask == REDRAW_FIELD)
901 height = FADE_SYSIZE;
903 if (border.draw_masked_when_fading)
904 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
906 DrawMaskedBorder_FIELD(); // draw once
916 // when switching screens without fading, set fade delay to zero
917 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
920 // do not display black frame when fading out without fade delay
921 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
924 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925 draw_border_function);
927 redraw_mask &= ~fade_mask;
929 ClearAutoRepeatKeyEvents();
932 static void SetScreenStates_BeforeFadingIn(void)
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
940 // set screen mode for animations back to fading
941 global.anim_status = GAME_MODE_PSEUDO_FADING;
944 static void SetScreenStates_AfterFadingIn(void)
946 // store new source screen (to use correct masked border for fading)
947 gfx.fade_border_source_status = global.border_status;
949 global.anim_status = global.anim_status_next;
952 static void SetScreenStates_BeforeFadingOut(void)
954 // store new target screen (to use correct masked border for fading)
955 gfx.fade_border_target_status = game_status;
957 // set screen mode for animations to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 // store backbuffer with all animations that will be stopped for fading out
961 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
964 static void SetScreenStates_AfterFadingOut(void)
966 global.border_status = game_status;
969 void FadeIn(int fade_mask)
971 SetScreenStates_BeforeFadingIn();
974 DrawMaskedBorder(REDRAW_ALL);
977 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
980 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
984 FADE_SXSIZE = FULL_SXSIZE;
985 FADE_SYSIZE = FULL_SYSIZE;
987 // activate virtual buttons depending on upcoming game status
988 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989 game_status == GAME_MODE_PLAYING && !tape.playing)
990 SetOverlayActive(TRUE);
992 SetScreenStates_AfterFadingIn();
994 // force update of global animation status in case of rapid screen changes
995 redraw_mask = REDRAW_ALL;
999 void FadeOut(int fade_mask)
1001 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1006 SetScreenStates_BeforeFadingOut();
1008 SetTileCursorActive(FALSE);
1009 SetOverlayActive(FALSE);
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1018 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1020 SetScreenStates_AfterFadingOut();
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1025 static struct TitleFadingInfo fading_leave_stored;
1028 fading_leave_stored = fading_leave;
1030 fading = fading_leave_stored;
1033 void FadeSetEnterMenu(void)
1035 fading = menu.enter_menu;
1037 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1040 void FadeSetLeaveMenu(void)
1042 fading = menu.leave_menu;
1044 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetEnterScreen(void)
1049 fading = menu.enter_screen[game_status];
1051 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1054 void FadeSetNextScreen(void)
1056 fading = menu.next_screen[game_status];
1058 // (do not overwrite fade mode set by FadeSetEnterScreen)
1059 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1062 void FadeSetLeaveScreen(void)
1064 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1067 void FadeSetFromType(int type)
1069 if (type & TYPE_ENTER_SCREEN)
1070 FadeSetEnterScreen();
1071 else if (type & TYPE_ENTER)
1073 else if (type & TYPE_LEAVE)
1077 void FadeSetDisabled(void)
1079 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1081 fading = fading_none;
1084 void FadeSkipNextFadeIn(void)
1086 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1089 void FadeSkipNextFadeOut(void)
1091 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1096 if (graphic == IMG_UNDEFINED)
1099 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1101 return (graphic_info[graphic].bitmap != NULL || redefined ?
1102 graphic_info[graphic].bitmap :
1103 graphic_info[default_graphic].bitmap);
1106 static Bitmap *getBackgroundBitmap(int graphic)
1108 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1113 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1119 (status == GAME_MODE_MAIN ||
1120 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1121 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1122 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1123 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1126 return getGlobalBorderBitmap(graphic);
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1131 if (graphic_info[graphic].bitmap)
1132 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1135 void SetMainBackgroundImageIfDefined(int graphic)
1137 if (graphic_info[graphic].bitmap)
1138 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1143 if (graphic_info[graphic].bitmap)
1144 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1147 void SetWindowBackgroundImage(int graphic)
1149 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetMainBackgroundImage(int graphic)
1154 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetDoorBackgroundImage(int graphic)
1159 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetPanelBackground(void)
1164 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1166 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1169 SetDoorBackgroundBitmap(bitmap_db_panel);
1172 void DrawBackground(int x, int y, int width, int height)
1174 // "drawto" might still point to playfield buffer here (hall of fame)
1175 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1177 if (IN_GFX_FIELD_FULL(x, y))
1178 redraw_mask |= REDRAW_FIELD;
1179 else if (IN_GFX_DOOR_1(x, y))
1180 redraw_mask |= REDRAW_DOOR_1;
1181 else if (IN_GFX_DOOR_2(x, y))
1182 redraw_mask |= REDRAW_DOOR_2;
1183 else if (IN_GFX_DOOR_3(x, y))
1184 redraw_mask |= REDRAW_DOOR_3;
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1189 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1191 if (font->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1199 struct GraphicInfo *g = &graphic_info[graphic];
1201 if (g->bitmap == NULL)
1204 DrawBackground(x, y, width, height);
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1219 boolean CheckIfGlobalBorderHasChanged(void)
1221 // if game status has not changed, global border has not changed either
1222 if (game_status == game_status_last)
1225 // determine and store new global border bitmap for current game status
1226 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1228 return (global_border_bitmap_last != global_border_bitmap);
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1236 // if game status has not changed, nothing has to be redrawn
1237 if (game_status == game_status_last)
1240 // redraw if last screen was title screen
1241 if (game_status_last == GAME_MODE_TITLE)
1244 // redraw if global screen border has changed
1245 if (CheckIfGlobalBorderHasChanged())
1248 // redraw if position or size of playfield area has changed
1249 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1253 // redraw if position or size of door area has changed
1254 if (dx_last != DX || dy_last != DY ||
1255 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1258 // redraw if position or size of tape area has changed
1259 if (vx_last != VX || vy_last != VY ||
1260 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1263 // redraw if position or size of editor area has changed
1264 if (ex_last != EX || ey_last != EY ||
1265 exsize_last != EXSIZE || eysize_last != EYSIZE)
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1275 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1277 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1280 void RedrawGlobalBorder(void)
1282 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 RedrawGlobalBorderFromBitmap(bitmap);
1286 redraw_mask = REDRAW_ALL;
1289 static void RedrawGlobalBorderIfNeeded(void)
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 if (game_status == game_status_last)
1296 // copy current draw buffer to later copy back areas that have not changed
1297 if (game_status_last != GAME_MODE_TITLE)
1298 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301 if (CheckIfGlobalBorderRedrawIsNeeded())
1303 // determine and store new global border bitmap for current game status
1304 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1307 // redraw global screen border (or clear, if defined to be empty)
1308 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1310 if (game_status == GAME_MODE_EDITOR)
1311 DrawSpecialEditorDoor();
1313 // copy previous playfield and door areas, if they are defined on both
1314 // previous and current screen and if they still have the same size
1316 if (real_sx_last != -1 && real_sy_last != -1 &&
1317 REAL_SX != -1 && REAL_SY != -1 &&
1318 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1323 if (dx_last != -1 && dy_last != -1 &&
1324 DX != -1 && DY != -1 &&
1325 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326 BlitBitmap(bitmap_db_store_1, backbuffer,
1327 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1329 if (game_status != GAME_MODE_EDITOR)
1331 if (vx_last != -1 && vy_last != -1 &&
1332 VX != -1 && VY != -1 &&
1333 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334 BlitBitmap(bitmap_db_store_1, backbuffer,
1335 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1339 if (ex_last != -1 && ey_last != -1 &&
1340 EX != -1 && EY != -1 &&
1341 exsize_last == EXSIZE && eysize_last == EYSIZE)
1342 BlitBitmap(bitmap_db_store_1, backbuffer,
1343 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1346 redraw_mask = REDRAW_ALL;
1349 game_status_last = game_status;
1351 global_border_bitmap_last = global_border_bitmap;
1353 real_sx_last = REAL_SX;
1354 real_sy_last = REAL_SY;
1355 full_sxsize_last = FULL_SXSIZE;
1356 full_sysize_last = FULL_SYSIZE;
1359 dxsize_last = DXSIZE;
1360 dysize_last = DYSIZE;
1363 vxsize_last = VXSIZE;
1364 vysize_last = VYSIZE;
1367 exsize_last = EXSIZE;
1368 eysize_last = EYSIZE;
1371 void ClearField(void)
1373 RedrawGlobalBorderIfNeeded();
1375 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376 // (when entering hall of fame after playing)
1377 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1379 // !!! maybe this should be done before clearing the background !!!
1380 if (game_status == GAME_MODE_PLAYING)
1382 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1387 SetDrawtoField(DRAW_TO_BACKBUFFER);
1391 void MarkTileDirty(int x, int y)
1393 redraw_mask |= REDRAW_FIELD;
1396 void SetBorderElement(void)
1400 BorderElement = EL_EMPTY;
1402 // only the R'n'D game engine may use an additional steelwall border
1403 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1406 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1408 for (x = 0; x < lev_fieldx; x++)
1410 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411 BorderElement = EL_STEELWALL;
1413 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1419 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1420 int max_array_fieldx, int max_array_fieldy,
1421 short field[max_array_fieldx][max_array_fieldy],
1422 int max_fieldx, int max_fieldy)
1424 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1425 static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1426 int old_element = field[start_x][start_y];
1429 // do nothing if start field already has the desired content
1430 if (old_element == fill_element)
1433 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1435 while (stack_pos > 0)
1437 struct XY current = stack_buffer[--stack_pos];
1440 field[current.x][current.y] = fill_element;
1442 for (i = 0; i < 4; i++)
1444 int x = current.x + check[i].x;
1445 int y = current.y + check[i].y;
1447 // check for stack buffer overflow (should not happen)
1448 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1449 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1451 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1452 stack_buffer[stack_pos++] = (struct XY){ x, y };
1457 void FloodFillLevel(int from_x, int from_y, int fill_element,
1458 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1459 int max_fieldx, int max_fieldy)
1461 FloodFillLevelExt(from_x, from_y, fill_element,
1462 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1463 max_fieldx, max_fieldy);
1466 void SetRandomAnimationValue(int x, int y)
1468 gfx.anim_random_frame = GfxRandom[x][y];
1471 int getGraphicAnimationFrame(int graphic, int sync_frame)
1473 // animation synchronized with global frame counter, not move position
1474 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1475 sync_frame = FrameCounter;
1477 return getAnimationFrame(graphic_info[graphic].anim_frames,
1478 graphic_info[graphic].anim_delay,
1479 graphic_info[graphic].anim_mode,
1480 graphic_info[graphic].anim_start_frame,
1484 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1486 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1488 struct GraphicInfo *g = &graphic_info[graphic];
1489 int xsize = MAX(1, g->anim_frames_per_line);
1490 int ysize = MAX(1, g->anim_frames / xsize);
1491 int xoffset = g->anim_start_frame % xsize;
1492 int yoffset = g->anim_start_frame % ysize;
1493 int x = (lx + xoffset + xsize) % xsize;
1494 int y = (ly + yoffset + ysize) % ysize;
1495 int sync_frame = y * xsize + x;
1497 return sync_frame % g->anim_frames;
1499 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1501 struct GraphicInfo *g = &graphic_info[graphic];
1502 int x = (lx + lev_fieldx) % lev_fieldx;
1503 int y = (ly + lev_fieldy) % lev_fieldy;
1504 int sync_frame = GfxRandomStatic[x][y];
1506 return sync_frame % g->anim_frames;
1509 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1511 return getGraphicAnimationFrame(graphic, sync_frame);
1514 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1516 struct GraphicInfo *g = &graphic_info[graphic];
1517 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1519 if (tilesize == gfx.standard_tile_size)
1520 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1521 else if (tilesize == game.tile_size)
1522 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1524 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1527 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1528 boolean get_backside)
1530 struct GraphicInfo *g = &graphic_info[graphic];
1531 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1532 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1534 if (g->offset_y == 0) // frames are ordered horizontally
1536 int max_width = g->anim_frames_per_line * g->width;
1537 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1539 *x = pos % max_width;
1540 *y = src_y % g->height + pos / max_width * g->height;
1542 else if (g->offset_x == 0) // frames are ordered vertically
1544 int max_height = g->anim_frames_per_line * g->height;
1545 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1547 *x = src_x % g->width + pos / max_height * g->width;
1548 *y = pos % max_height;
1550 else // frames are ordered diagonally
1552 *x = src_x + frame * g->offset_x;
1553 *y = src_y + frame * g->offset_y;
1557 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1558 Bitmap **bitmap, int *x, int *y,
1559 boolean get_backside)
1561 struct GraphicInfo *g = &graphic_info[graphic];
1563 // if no graphics defined at all, use fallback graphics
1564 if (g->bitmaps == NULL)
1565 *g = graphic_info[IMG_CHAR_EXCLAM];
1567 // if no in-game graphics defined, always use standard graphic size
1568 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1569 tilesize = TILESIZE;
1571 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1572 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1574 *x = *x * tilesize / g->tile_size;
1575 *y = *y * tilesize / g->tile_size;
1578 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1579 Bitmap **bitmap, int *x, int *y)
1581 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1584 void getFixedGraphicSource(int graphic, int frame,
1585 Bitmap **bitmap, int *x, int *y)
1587 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1590 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1592 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1595 void getGlobalAnimGraphicSource(int graphic, int frame,
1596 Bitmap **bitmap, int *x, int *y)
1598 struct GraphicInfo *g = &graphic_info[graphic];
1600 // if no graphics defined at all, use fallback graphics
1601 if (g->bitmaps == NULL)
1602 *g = graphic_info[IMG_CHAR_EXCLAM];
1604 // use original size graphics, if existing, else use standard size graphics
1605 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1606 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1608 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1610 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1613 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1614 int *x, int *y, boolean get_backside)
1616 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1620 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1622 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1625 void DrawGraphic(int x, int y, int graphic, int frame)
1628 if (!IN_SCR_FIELD(x, y))
1630 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1631 Debug("draw:DrawGraphic", "This should never happen!");
1637 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1640 MarkTileDirty(x, y);
1643 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1646 if (!IN_SCR_FIELD(x, y))
1648 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1650 Debug("draw:DrawFixedGraphic", "This should never happen!");
1656 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1658 MarkTileDirty(x, y);
1661 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1667 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1669 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1672 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1678 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1679 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1682 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1685 if (!IN_SCR_FIELD(x, y))
1687 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1689 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1695 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1698 MarkTileDirty(x, y);
1701 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1704 if (!IN_SCR_FIELD(x, y))
1706 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1708 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1714 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1716 MarkTileDirty(x, y);
1719 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1725 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1727 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1731 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1732 int graphic, int frame)
1737 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1739 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1743 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1745 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1747 MarkTileDirty(x / tilesize, y / tilesize);
1750 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1753 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1754 graphic, frame, tilesize);
1755 MarkTileDirty(x / tilesize, y / tilesize);
1758 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1764 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1765 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1768 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1769 int frame, int tilesize)
1774 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1775 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1778 void DrawMiniGraphic(int x, int y, int graphic)
1780 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1781 MarkTileDirty(x / 2, y / 2);
1784 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1789 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1790 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1793 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1794 int graphic, int frame,
1795 int cut_mode, int mask_mode)
1800 int width = TILEX, height = TILEY;
1803 if (dx || dy) // shifted graphic
1805 if (x < BX1) // object enters playfield from the left
1812 else if (x > BX2) // object enters playfield from the right
1818 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1824 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1826 else if (dx) // general horizontal movement
1827 MarkTileDirty(x + SIGN(dx), y);
1829 if (y < BY1) // object enters playfield from the top
1831 if (cut_mode == CUT_BELOW) // object completely above top border
1839 else if (y > BY2) // object enters playfield from the bottom
1845 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1851 else if (dy > 0 && cut_mode == CUT_ABOVE)
1853 if (y == BY2) // object completely above bottom border
1859 MarkTileDirty(x, y + 1);
1860 } // object leaves playfield to the bottom
1861 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1863 else if (dy) // general vertical movement
1864 MarkTileDirty(x, y + SIGN(dy));
1868 if (!IN_SCR_FIELD(x, y))
1870 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1872 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1878 width = width * TILESIZE_VAR / TILESIZE;
1879 height = height * TILESIZE_VAR / TILESIZE;
1880 cx = cx * TILESIZE_VAR / TILESIZE;
1881 cy = cy * TILESIZE_VAR / TILESIZE;
1882 dx = dx * TILESIZE_VAR / TILESIZE;
1883 dy = dy * TILESIZE_VAR / TILESIZE;
1885 if (width > 0 && height > 0)
1887 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1892 dst_x = FX + x * TILEX_VAR + dx;
1893 dst_y = FY + y * TILEY_VAR + dy;
1895 if (mask_mode == USE_MASKING)
1896 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1899 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1902 MarkTileDirty(x, y);
1906 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1907 int graphic, int frame,
1908 int cut_mode, int mask_mode)
1913 int width = TILEX_VAR, height = TILEY_VAR;
1916 int x2 = x + SIGN(dx);
1917 int y2 = y + SIGN(dy);
1919 // movement with two-tile animations must be sync'ed with movement position,
1920 // not with current GfxFrame (which can be higher when using slow movement)
1921 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1922 int anim_frames = graphic_info[graphic].anim_frames;
1924 // (we also need anim_delay here for movement animations with less frames)
1925 int anim_delay = graphic_info[graphic].anim_delay;
1926 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1928 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1929 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1931 // re-calculate animation frame for two-tile movement animation
1932 frame = getGraphicAnimationFrame(graphic, sync_frame);
1934 // check if movement start graphic inside screen area and should be drawn
1935 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1937 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1939 dst_x = FX + x1 * TILEX_VAR;
1940 dst_y = FY + y1 * TILEY_VAR;
1942 if (mask_mode == USE_MASKING)
1943 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1946 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1949 MarkTileDirty(x1, y1);
1952 // check if movement end graphic inside screen area and should be drawn
1953 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1955 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1957 dst_x = FX + x2 * TILEX_VAR;
1958 dst_y = FY + y2 * TILEY_VAR;
1960 if (mask_mode == USE_MASKING)
1961 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1964 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1967 MarkTileDirty(x2, y2);
1971 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1972 int graphic, int frame,
1973 int cut_mode, int mask_mode)
1977 DrawGraphic(x, y, graphic, frame);
1982 if (graphic_info[graphic].double_movement) // EM style movement images
1983 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1985 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1988 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1989 int graphic, int frame, int cut_mode)
1991 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1994 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1995 int cut_mode, int mask_mode)
1997 int lx = LEVELX(x), ly = LEVELY(y);
2001 if (IN_LEV_FIELD(lx, ly))
2003 if (element == EL_EMPTY)
2004 element = GfxElementEmpty[lx][ly];
2006 SetRandomAnimationValue(lx, ly);
2008 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2009 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2011 // do not use double (EM style) movement graphic when not moving
2012 if (graphic_info[graphic].double_movement && !dx && !dy)
2014 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2015 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2018 if (game.use_masked_elements && (dx || dy))
2019 mask_mode = USE_MASKING;
2021 else // border element
2023 graphic = el2img(element);
2024 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2027 if (element == EL_EXPANDABLE_WALL)
2029 boolean left_stopped = FALSE, right_stopped = FALSE;
2031 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2032 left_stopped = TRUE;
2033 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2034 right_stopped = TRUE;
2036 if (left_stopped && right_stopped)
2038 else if (left_stopped)
2040 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2041 frame = graphic_info[graphic].anim_frames - 1;
2043 else if (right_stopped)
2045 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2046 frame = graphic_info[graphic].anim_frames - 1;
2051 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2052 else if (mask_mode == USE_MASKING)
2053 DrawGraphicThruMask(x, y, graphic, frame);
2055 DrawGraphic(x, y, graphic, frame);
2058 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2059 int cut_mode, int mask_mode)
2061 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2062 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2063 cut_mode, mask_mode);
2066 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2069 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2072 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2075 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2078 void DrawLevelElementThruMask(int x, int y, int element)
2080 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2083 void DrawLevelFieldThruMask(int x, int y)
2085 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2088 // !!! implementation of quicksand is totally broken !!!
2089 #define IS_CRUMBLED_TILE(x, y, e) \
2090 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2091 !IS_MOVING(x, y) || \
2092 (e) == EL_QUICKSAND_EMPTYING || \
2093 (e) == EL_QUICKSAND_FAST_EMPTYING))
2095 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2100 int width, height, cx, cy;
2101 int sx = SCREENX(x), sy = SCREENY(y);
2102 int crumbled_border_size = graphic_info[graphic].border_size;
2103 int crumbled_tile_size = graphic_info[graphic].tile_size;
2104 int crumbled_border_size_var =
2105 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2108 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2110 for (i = 1; i < 4; i++)
2112 int dxx = (i & 1 ? dx : 0);
2113 int dyy = (i & 2 ? dy : 0);
2116 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2119 // check if neighbour field is of same crumble type
2120 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2121 graphic_info[graphic].class ==
2122 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2124 // return if check prevents inner corner
2125 if (same == (dxx == dx && dyy == dy))
2129 // if we reach this point, we have an inner corner
2131 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2133 width = crumbled_border_size_var;
2134 height = crumbled_border_size_var;
2135 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2136 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2138 if (game.use_masked_elements)
2140 int graphic0 = el2img(EL_EMPTY);
2141 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2142 Bitmap *src_bitmap0;
2145 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2147 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2149 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2151 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2153 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2156 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2158 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2161 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2166 int width, height, bx, by, cx, cy;
2167 int sx = SCREENX(x), sy = SCREENY(y);
2168 int crumbled_border_size = graphic_info[graphic].border_size;
2169 int crumbled_tile_size = graphic_info[graphic].tile_size;
2170 int crumbled_border_size_var =
2171 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2172 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2175 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2177 // only needed when using masked elements
2178 int graphic0 = el2img(EL_EMPTY);
2179 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2180 Bitmap *src_bitmap0;
2183 if (game.use_masked_elements)
2184 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2186 // draw simple, sloppy, non-corner-accurate crumbled border
2188 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2189 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2190 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2191 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2193 if (game.use_masked_elements)
2195 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2197 FX + sx * TILEX_VAR + cx,
2198 FY + sy * TILEY_VAR + cy);
2200 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2202 FX + sx * TILEX_VAR + cx,
2203 FY + sy * TILEY_VAR + cy);
2206 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2208 FX + sx * TILEX_VAR + cx,
2209 FY + sy * TILEY_VAR + cy);
2211 // (remaining middle border part must be at least as big as corner part)
2212 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2213 crumbled_border_size_var >= TILESIZE_VAR / 3)
2216 // correct corners of crumbled border, if needed
2218 for (i = -1; i <= 1; i += 2)
2220 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2221 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2222 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2225 // check if neighbour field is of same crumble type
2226 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2227 graphic_info[graphic].class ==
2228 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2230 // no crumbled corner, but continued crumbled border
2232 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2233 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2234 int b1 = (i == 1 ? crumbled_border_size_var :
2235 TILESIZE_VAR - 2 * crumbled_border_size_var);
2237 width = crumbled_border_size_var;
2238 height = crumbled_border_size_var;
2240 if (dir == 1 || dir == 2)
2255 if (game.use_masked_elements)
2257 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2259 FX + sx * TILEX_VAR + cx,
2260 FY + sy * TILEY_VAR + cy);
2262 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2264 FX + sx * TILEX_VAR + cx,
2265 FY + sy * TILEY_VAR + cy);
2268 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2270 FX + sx * TILEX_VAR + cx,
2271 FY + sy * TILEY_VAR + cy);
2276 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2278 int sx = SCREENX(x), sy = SCREENY(y);
2281 static int xy[4][2] =
2289 if (!IN_LEV_FIELD(x, y))
2292 element = TILE_GFX_ELEMENT(x, y);
2294 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2296 if (!IN_SCR_FIELD(sx, sy))
2299 // crumble field borders towards direct neighbour fields
2300 for (i = 0; i < 4; i++)
2302 int xx = x + xy[i][0];
2303 int yy = y + xy[i][1];
2305 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2308 // check if neighbour field is of same crumble type
2309 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2310 graphic_info[graphic].class ==
2311 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2314 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2317 // crumble inner field corners towards corner neighbour fields
2318 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2319 graphic_info[graphic].anim_frames == 2)
2321 for (i = 0; i < 4; i++)
2323 int dx = (i & 1 ? +1 : -1);
2324 int dy = (i & 2 ? +1 : -1);
2326 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2330 MarkTileDirty(sx, sy);
2332 else // center field is not crumbled -- crumble neighbour fields
2334 // crumble field borders of direct neighbour fields
2335 for (i = 0; i < 4; i++)
2337 int xx = x + xy[i][0];
2338 int yy = y + xy[i][1];
2339 int sxx = sx + xy[i][0];
2340 int syy = sy + xy[i][1];
2342 if (!IN_LEV_FIELD(xx, yy) ||
2343 !IN_SCR_FIELD(sxx, syy))
2346 // do not crumble fields that are being digged or snapped
2347 if (Tile[xx][yy] == EL_EMPTY ||
2348 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2351 element = TILE_GFX_ELEMENT(xx, yy);
2353 if (!IS_CRUMBLED_TILE(xx, yy, element))
2356 graphic = el_act2crm(element, ACTION_DEFAULT);
2358 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2360 MarkTileDirty(sxx, syy);
2363 // crumble inner field corners of corner neighbour fields
2364 for (i = 0; i < 4; i++)
2366 int dx = (i & 1 ? +1 : -1);
2367 int dy = (i & 2 ? +1 : -1);
2373 if (!IN_LEV_FIELD(xx, yy) ||
2374 !IN_SCR_FIELD(sxx, syy))
2377 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2380 element = TILE_GFX_ELEMENT(xx, yy);
2382 if (!IS_CRUMBLED_TILE(xx, yy, element))
2385 graphic = el_act2crm(element, ACTION_DEFAULT);
2387 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2388 graphic_info[graphic].anim_frames == 2)
2389 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2391 MarkTileDirty(sxx, syy);
2396 void DrawLevelFieldCrumbled(int x, int y)
2400 if (!IN_LEV_FIELD(x, y))
2403 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2404 GfxElement[x][y] != EL_UNDEFINED &&
2405 GFX_CRUMBLED(GfxElement[x][y]))
2407 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2412 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2414 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2417 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2420 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2421 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2422 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2423 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2424 int sx = SCREENX(x), sy = SCREENY(y);
2426 DrawScreenGraphic(sx, sy, graphic1, frame1);
2427 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2430 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2432 int sx = SCREENX(x), sy = SCREENY(y);
2433 static int xy[4][2] =
2442 // crumble direct neighbour fields (required for field borders)
2443 for (i = 0; i < 4; i++)
2445 int xx = x + xy[i][0];
2446 int yy = y + xy[i][1];
2447 int sxx = sx + xy[i][0];
2448 int syy = sy + xy[i][1];
2450 if (!IN_LEV_FIELD(xx, yy) ||
2451 !IN_SCR_FIELD(sxx, syy) ||
2452 !GFX_CRUMBLED(Tile[xx][yy]) ||
2456 DrawLevelField(xx, yy);
2459 // crumble corner neighbour fields (required for inner field corners)
2460 for (i = 0; i < 4; i++)
2462 int dx = (i & 1 ? +1 : -1);
2463 int dy = (i & 2 ? +1 : -1);
2469 if (!IN_LEV_FIELD(xx, yy) ||
2470 !IN_SCR_FIELD(sxx, syy) ||
2471 !GFX_CRUMBLED(Tile[xx][yy]) ||
2475 int element = TILE_GFX_ELEMENT(xx, yy);
2476 int graphic = el_act2crm(element, ACTION_DEFAULT);
2478 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2479 graphic_info[graphic].anim_frames == 2)
2480 DrawLevelField(xx, yy);
2484 static int getBorderElement(int x, int y)
2488 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2489 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2490 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2491 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2492 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2493 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2494 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2496 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2497 int steel_position = (x == -1 && y == -1 ? 0 :
2498 x == lev_fieldx && y == -1 ? 1 :
2499 x == -1 && y == lev_fieldy ? 2 :
2500 x == lev_fieldx && y == lev_fieldy ? 3 :
2501 x == -1 || x == lev_fieldx ? 4 :
2502 y == -1 || y == lev_fieldy ? 5 : 6);
2504 return border[steel_position][steel_type];
2507 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2509 if (game.use_masked_elements)
2511 if (graphic != el2img(EL_EMPTY))
2512 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2514 DrawGraphicThruMask(x, y, graphic, frame);
2518 DrawGraphic(x, y, graphic, frame);
2522 void DrawScreenElement(int x, int y, int element)
2524 int mask_mode = NO_MASKING;
2526 if (game.use_masked_elements)
2528 int lx = LEVELX(x), ly = LEVELY(y);
2530 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2532 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2534 mask_mode = USE_MASKING;
2538 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2539 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2542 void DrawLevelElement(int x, int y, int element)
2544 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2545 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2548 void DrawScreenField(int x, int y)
2550 int lx = LEVELX(x), ly = LEVELY(y);
2551 int element, content;
2553 if (!IN_LEV_FIELD(lx, ly))
2555 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2558 element = getBorderElement(lx, ly);
2560 DrawScreenElement(x, y, element);
2565 element = Tile[lx][ly];
2566 content = Store[lx][ly];
2568 if (IS_MOVING(lx, ly))
2570 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2571 boolean cut_mode = NO_CUTTING;
2573 if (element == EL_QUICKSAND_EMPTYING ||
2574 element == EL_QUICKSAND_FAST_EMPTYING ||
2575 element == EL_MAGIC_WALL_EMPTYING ||
2576 element == EL_BD_MAGIC_WALL_EMPTYING ||
2577 element == EL_DC_MAGIC_WALL_EMPTYING ||
2578 element == EL_AMOEBA_DROPPING)
2579 cut_mode = CUT_ABOVE;
2580 else if (element == EL_QUICKSAND_FILLING ||
2581 element == EL_QUICKSAND_FAST_FILLING ||
2582 element == EL_MAGIC_WALL_FILLING ||
2583 element == EL_BD_MAGIC_WALL_FILLING ||
2584 element == EL_DC_MAGIC_WALL_FILLING)
2585 cut_mode = CUT_BELOW;
2587 if (cut_mode == CUT_ABOVE)
2588 DrawScreenElement(x, y, element);
2590 DrawScreenElement(x, y, EL_EMPTY);
2592 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2594 int dir = MovDir[lx][ly];
2595 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2596 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2598 if (IN_SCR_FIELD(newx, newy))
2599 DrawScreenElement(newx, newy, EL_EMPTY);
2603 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2604 else if (cut_mode == NO_CUTTING)
2605 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2608 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2610 if (cut_mode == CUT_BELOW &&
2611 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2612 DrawLevelElement(lx, ly + 1, element);
2615 if (content == EL_ACID)
2617 int dir = MovDir[lx][ly];
2618 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2619 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2621 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2623 // prevent target field from being drawn again (but without masking)
2624 // (this would happen if target field is scanned after moving element)
2625 Stop[newlx][newly] = TRUE;
2628 else if (IS_BLOCKED(lx, ly))
2633 boolean cut_mode = NO_CUTTING;
2634 int element_old, content_old;
2636 Blocked2Moving(lx, ly, &oldx, &oldy);
2639 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2640 MovDir[oldx][oldy] == MV_RIGHT);
2642 element_old = Tile[oldx][oldy];
2643 content_old = Store[oldx][oldy];
2645 if (element_old == EL_QUICKSAND_EMPTYING ||
2646 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2647 element_old == EL_MAGIC_WALL_EMPTYING ||
2648 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2649 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2650 element_old == EL_AMOEBA_DROPPING)
2651 cut_mode = CUT_ABOVE;
2653 DrawScreenElement(x, y, EL_EMPTY);
2656 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2658 else if (cut_mode == NO_CUTTING)
2659 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2662 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2665 else if (IS_DRAWABLE(element))
2666 DrawScreenElement(x, y, element);
2668 DrawScreenElement(x, y, EL_EMPTY);
2671 void DrawLevelField(int x, int y)
2673 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2674 DrawScreenField(SCREENX(x), SCREENY(y));
2675 else if (IS_MOVING(x, y))
2679 Moving2Blocked(x, y, &newx, &newy);
2680 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2681 DrawScreenField(SCREENX(newx), SCREENY(newy));
2683 else if (IS_BLOCKED(x, y))
2687 Blocked2Moving(x, y, &oldx, &oldy);
2688 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2689 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2693 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2694 int (*el2img_function)(int), boolean masked,
2695 int element_bits_draw)
2697 int element_base = map_mm_wall_element(element);
2698 int element_bits = (IS_DF_WALL(element) ?
2699 element - EL_DF_WALL_START :
2700 IS_MM_WALL(element) ?
2701 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2702 int graphic = el2img_function(element_base);
2703 int tilesize_draw = tilesize / 2;
2708 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2710 for (i = 0; i < 4; i++)
2712 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2713 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2715 if (!(element_bits_draw & (1 << i)))
2718 if (element_bits & (1 << i))
2721 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2722 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2724 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2725 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2730 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2731 tilesize_draw, tilesize_draw);
2736 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2737 boolean masked, int element_bits_draw)
2739 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2740 element, tilesize, el2edimg, masked, element_bits_draw);
2743 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2744 int (*el2img_function)(int))
2746 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2750 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2753 if (IS_MM_WALL(element))
2755 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2756 element, tilesize, el2edimg, masked, 0x000f);
2760 int graphic = el2edimg(element);
2763 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2765 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2769 void DrawSizedElement(int x, int y, int element, int tilesize)
2771 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2774 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2776 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2779 void DrawMiniElement(int x, int y, int element)
2783 graphic = el2edimg(element);
2784 DrawMiniGraphic(x, y, graphic);
2787 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2790 int x = sx + scroll_x, y = sy + scroll_y;
2792 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2793 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2794 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2795 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2797 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2800 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2802 int x = sx + scroll_x, y = sy + scroll_y;
2804 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2805 DrawMiniElement(sx, sy, EL_EMPTY);
2806 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2807 DrawMiniElement(sx, sy, Tile[x][y]);
2809 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2812 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2813 int x, int y, int xsize, int ysize,
2814 int tile_width, int tile_height)
2818 int dst_x = startx + x * tile_width;
2819 int dst_y = starty + y * tile_height;
2820 int width = graphic_info[graphic].width;
2821 int height = graphic_info[graphic].height;
2822 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2823 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2824 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2825 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2826 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2827 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2828 boolean draw_masked = graphic_info[graphic].draw_masked;
2830 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2832 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2834 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2838 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2839 inner_sx + (x - 1) * tile_width % inner_width);
2840 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2841 inner_sy + (y - 1) * tile_height % inner_height);
2844 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2847 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2851 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2852 int x, int y, int xsize, int ysize,
2855 int font_width = getFontWidth(font_nr);
2856 int font_height = getFontHeight(font_nr);
2858 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2859 font_width, font_height);
2862 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2864 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2865 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2866 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2867 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2868 boolean no_delay = (tape.warp_forward);
2869 unsigned int anim_delay = 0;
2870 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2871 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2872 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2873 int font_width = getFontWidth(font_nr);
2874 int font_height = getFontHeight(font_nr);
2875 int max_xsize = level.envelope[envelope_nr].xsize;
2876 int max_ysize = level.envelope[envelope_nr].ysize;
2877 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2878 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2879 int xend = max_xsize;
2880 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2881 int xstep = (xstart < xend ? 1 : 0);
2882 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2884 int end = MAX(xend - xstart, yend - ystart);
2887 for (i = start; i <= end; i++)
2889 int last_frame = end; // last frame of this "for" loop
2890 int x = xstart + i * xstep;
2891 int y = ystart + i * ystep;
2892 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2893 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2894 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2895 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2898 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2900 BlitScreenToBitmap(backbuffer);
2902 SetDrawtoField(DRAW_TO_BACKBUFFER);
2904 for (yy = 0; yy < ysize; yy++)
2905 for (xx = 0; xx < xsize; xx++)
2906 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2908 DrawTextBuffer(sx + font_width, sy + font_height,
2909 level.envelope[envelope_nr].text, font_nr, max_xsize,
2910 xsize - 2, ysize - 2, 0, mask_mode,
2911 level.envelope[envelope_nr].autowrap,
2912 level.envelope[envelope_nr].centered, FALSE);
2914 redraw_mask |= REDRAW_FIELD;
2917 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2920 ClearAutoRepeatKeyEvents();
2923 void ShowEnvelope(int envelope_nr)
2925 int element = EL_ENVELOPE_1 + envelope_nr;
2926 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2927 int sound_opening = element_info[element].sound[ACTION_OPENING];
2928 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2929 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2930 boolean no_delay = (tape.warp_forward);
2931 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2932 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2933 int anim_mode = graphic_info[graphic].anim_mode;
2934 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2935 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2936 boolean overlay_enabled = GetOverlayEnabled();
2938 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2940 SetOverlayEnabled(FALSE);
2943 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2945 if (anim_mode == ANIM_DEFAULT)
2946 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2948 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2951 Delay_WithScreenUpdates(wait_delay_value);
2953 WaitForEventToContinue();
2956 SetOverlayEnabled(overlay_enabled);
2958 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2960 if (anim_mode != ANIM_NONE)
2961 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2963 if (anim_mode == ANIM_DEFAULT)
2964 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2966 game.envelope_active = FALSE;
2968 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2970 redraw_mask |= REDRAW_FIELD;
2974 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2975 int xsize, int ysize)
2977 if (!global.use_envelope_request ||
2978 request.sort_priority <= 0)
2981 if (request.bitmap == NULL ||
2982 xsize > request.xsize ||
2983 ysize > request.ysize)
2985 if (request.bitmap != NULL)
2986 FreeBitmap(request.bitmap);
2988 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2990 SDL_Surface *surface = request.bitmap->surface;
2992 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2993 Fail("SDLGetNativeSurface() failed");
2996 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2998 SDLFreeBitmapTextures(request.bitmap);
2999 SDLCreateBitmapTextures(request.bitmap);
3001 // set envelope request run-time values
3004 request.xsize = xsize;
3005 request.ysize = ysize;
3008 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3010 if (global.use_envelope_request &&
3011 game.request_active_or_moving &&
3012 request.sort_priority > 0 &&
3013 drawing_target == DRAW_TO_SCREEN &&
3014 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3016 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3017 request.sx, request.sy);
3021 static void setRequestBasePosition(int *x, int *y)
3023 int sx_base, sy_base;
3025 if (request.x != -1)
3026 sx_base = request.x;
3027 else if (request.align == ALIGN_LEFT)
3029 else if (request.align == ALIGN_RIGHT)
3030 sx_base = SX + SXSIZE;
3032 sx_base = SX + SXSIZE / 2;
3034 if (request.y != -1)
3035 sy_base = request.y;
3036 else if (request.valign == VALIGN_TOP)
3038 else if (request.valign == VALIGN_BOTTOM)
3039 sy_base = SY + SYSIZE;
3041 sy_base = SY + SYSIZE / 2;
3047 static void setRequestPositionExt(int *x, int *y, int width, int height,
3048 boolean add_border_size)
3050 int border_size = request.border_size;
3051 int sx_base, sy_base;
3054 setRequestBasePosition(&sx_base, &sy_base);
3056 if (request.align == ALIGN_LEFT)
3058 else if (request.align == ALIGN_RIGHT)
3059 sx = sx_base - width;
3061 sx = sx_base - width / 2;
3063 if (request.valign == VALIGN_TOP)
3065 else if (request.valign == VALIGN_BOTTOM)
3066 sy = sy_base - height;
3068 sy = sy_base - height / 2;
3070 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3071 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3073 if (add_border_size)
3083 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3085 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3088 static void DrawEnvelopeRequest(char *text)
3090 char *text_final = text;
3091 char *text_door_style = NULL;
3092 int graphic = IMG_BACKGROUND_REQUEST;
3093 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3094 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3095 int font_nr = FONT_REQUEST;
3096 int font_width = getFontWidth(font_nr);
3097 int font_height = getFontHeight(font_nr);
3098 int border_size = request.border_size;
3099 int line_spacing = request.line_spacing;
3100 int line_height = font_height + line_spacing;
3101 int max_text_width = request.width - 2 * border_size;
3102 int max_text_height = request.height - 2 * border_size;
3103 int line_length = max_text_width / font_width;
3104 int max_lines = max_text_height / line_height;
3105 int text_width = line_length * font_width;
3106 int width = request.width;
3107 int height = request.height;
3108 int tile_size = MAX(request.step_offset, 1);
3109 int x_steps = width / tile_size;
3110 int y_steps = height / tile_size;
3111 int sx_offset = border_size;
3112 int sy_offset = border_size;
3116 if (request.centered)
3117 sx_offset = (request.width - text_width) / 2;
3119 if (request.wrap_single_words && !request.autowrap)
3121 char *src_text_ptr, *dst_text_ptr;
3123 text_door_style = checked_malloc(2 * strlen(text) + 1);
3125 src_text_ptr = text;
3126 dst_text_ptr = text_door_style;
3128 while (*src_text_ptr)
3130 if (*src_text_ptr == ' ' ||
3131 *src_text_ptr == '?' ||
3132 *src_text_ptr == '!')
3133 *dst_text_ptr++ = '\n';
3135 if (*src_text_ptr != ' ')
3136 *dst_text_ptr++ = *src_text_ptr;
3141 *dst_text_ptr = '\0';
3143 text_final = text_door_style;
3146 setRequestPosition(&sx, &sy, FALSE);
3148 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3150 for (y = 0; y < y_steps; y++)
3151 for (x = 0; x < x_steps; x++)
3152 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3153 x, y, x_steps, y_steps,
3154 tile_size, tile_size);
3156 // force DOOR font inside door area
3157 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3159 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3160 line_length, -1, max_lines, line_spacing, mask_mode,
3161 request.autowrap, request.centered, FALSE);
3165 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3166 RedrawGadget(tool_gadget[i]);
3168 // store readily prepared envelope request for later use when animating
3169 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3171 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3173 if (text_door_style)
3174 free(text_door_style);
3177 static void AnimateEnvelopeRequest(int anim_mode, int action)
3179 int graphic = IMG_BACKGROUND_REQUEST;
3180 boolean draw_masked = graphic_info[graphic].draw_masked;
3181 int delay_value_normal = request.step_delay;
3182 int delay_value_fast = delay_value_normal / 2;
3183 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3184 boolean no_delay = (tape.warp_forward);
3185 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3186 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3187 unsigned int anim_delay = 0;
3189 int tile_size = MAX(request.step_offset, 1);
3190 int max_xsize = request.width / tile_size;
3191 int max_ysize = request.height / tile_size;
3192 int max_xsize_inner = max_xsize - 2;
3193 int max_ysize_inner = max_ysize - 2;
3195 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3196 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3197 int xend = max_xsize_inner;
3198 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3199 int xstep = (xstart < xend ? 1 : 0);
3200 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3202 int end = MAX(xend - xstart, yend - ystart);
3205 if (setup.quick_doors)
3212 for (i = start; i <= end; i++)
3214 int last_frame = end; // last frame of this "for" loop
3215 int x = xstart + i * xstep;
3216 int y = ystart + i * ystep;
3217 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3218 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3219 int xsize_size_left = (xsize - 1) * tile_size;
3220 int ysize_size_top = (ysize - 1) * tile_size;
3221 int max_xsize_pos = (max_xsize - 1) * tile_size;
3222 int max_ysize_pos = (max_ysize - 1) * tile_size;
3223 int width = xsize * tile_size;
3224 int height = ysize * tile_size;
3229 setRequestPosition(&src_x, &src_y, FALSE);
3230 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3232 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3234 for (yy = 0; yy < 2; yy++)
3236 for (xx = 0; xx < 2; xx++)
3238 int src_xx = src_x + xx * max_xsize_pos;
3239 int src_yy = src_y + yy * max_ysize_pos;
3240 int dst_xx = dst_x + xx * xsize_size_left;
3241 int dst_yy = dst_y + yy * ysize_size_top;
3242 int xx_size = (xx ? tile_size : xsize_size_left);
3243 int yy_size = (yy ? tile_size : ysize_size_top);
3246 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3247 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3249 BlitBitmap(bitmap_db_store_2, backbuffer,
3250 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3254 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3256 redraw_mask |= REDRAW_FIELD;
3260 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3263 ClearAutoRepeatKeyEvents();
3266 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3268 int graphic = IMG_BACKGROUND_REQUEST;
3269 int sound_opening = SND_REQUEST_OPENING;
3270 int sound_closing = SND_REQUEST_CLOSING;
3271 int anim_mode_1 = request.anim_mode; // (higher priority)
3272 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3273 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3274 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3275 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3277 if (game_status == GAME_MODE_PLAYING)
3278 BlitScreenToBitmap(backbuffer);
3280 SetDrawtoField(DRAW_TO_BACKBUFFER);
3282 // SetDrawBackgroundMask(REDRAW_NONE);
3284 if (action == ACTION_OPENING)
3286 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3288 if (req_state & REQ_ASK)
3290 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3291 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3292 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3293 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3295 else if (req_state & REQ_CONFIRM)
3297 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3298 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3300 else if (req_state & REQ_PLAYER)
3302 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3303 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3304 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3305 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3308 DrawEnvelopeRequest(text);
3311 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3313 if (action == ACTION_OPENING)
3315 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3317 if (anim_mode == ANIM_DEFAULT)
3318 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3320 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3324 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3326 if (anim_mode != ANIM_NONE)
3327 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3329 if (anim_mode == ANIM_DEFAULT)
3330 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3333 game.envelope_active = FALSE;
3335 if (action == ACTION_CLOSING)
3336 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3338 // SetDrawBackgroundMask(last_draw_background_mask);
3340 redraw_mask |= REDRAW_FIELD;
3344 if (action == ACTION_CLOSING &&
3345 game_status == GAME_MODE_PLAYING &&
3346 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3347 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3350 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3352 if (IS_MM_WALL(element))
3354 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3360 int graphic = el2preimg(element);
3362 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3363 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3368 void DrawLevel(int draw_background_mask)
3372 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3373 SetDrawBackgroundMask(draw_background_mask);
3377 for (x = BX1; x <= BX2; x++)
3378 for (y = BY1; y <= BY2; y++)
3379 DrawScreenField(x, y);
3381 redraw_mask |= REDRAW_FIELD;
3384 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3389 for (x = 0; x < size_x; x++)
3390 for (y = 0; y < size_y; y++)
3391 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3393 redraw_mask |= REDRAW_FIELD;
3396 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3400 for (x = 0; x < size_x; x++)
3401 for (y = 0; y < size_y; y++)
3402 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3404 redraw_mask |= REDRAW_FIELD;
3407 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3409 boolean show_level_border = (BorderElement != EL_EMPTY);
3410 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3411 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3412 int tile_size = preview.tile_size;
3413 int preview_width = preview.xsize * tile_size;
3414 int preview_height = preview.ysize * tile_size;
3415 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3416 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3417 int real_preview_width = real_preview_xsize * tile_size;
3418 int real_preview_height = real_preview_ysize * tile_size;
3419 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3420 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3423 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3426 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3428 dst_x += (preview_width - real_preview_width) / 2;
3429 dst_y += (preview_height - real_preview_height) / 2;
3431 for (x = 0; x < real_preview_xsize; x++)
3433 for (y = 0; y < real_preview_ysize; y++)
3435 int lx = from_x + x + (show_level_border ? -1 : 0);
3436 int ly = from_y + y + (show_level_border ? -1 : 0);
3437 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3438 getBorderElement(lx, ly));
3440 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3441 element, tile_size);
3445 redraw_mask |= REDRAW_FIELD;
3448 #define MICROLABEL_EMPTY 0
3449 #define MICROLABEL_LEVEL_NAME 1
3450 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3451 #define MICROLABEL_LEVEL_AUTHOR 3
3452 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3453 #define MICROLABEL_IMPORTED_FROM 5
3454 #define MICROLABEL_IMPORTED_BY_HEAD 6
3455 #define MICROLABEL_IMPORTED_BY 7
3457 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3459 int max_text_width = SXSIZE;
3460 int font_width = getFontWidth(font_nr);
3462 if (pos->align == ALIGN_CENTER)
3463 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3464 else if (pos->align == ALIGN_RIGHT)
3465 max_text_width = pos->x;
3467 max_text_width = SXSIZE - pos->x;
3469 return max_text_width / font_width;
3472 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3474 char label_text[MAX_OUTPUT_LINESIZE + 1];
3475 int max_len_label_text;
3476 int font_nr = pos->font;
3479 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3482 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3483 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3484 mode == MICROLABEL_IMPORTED_BY_HEAD)
3485 font_nr = pos->font_alt;
3487 max_len_label_text = getMaxTextLength(pos, font_nr);
3489 if (pos->size != -1)
3490 max_len_label_text = pos->size;
3492 for (i = 0; i < max_len_label_text; i++)
3493 label_text[i] = ' ';
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);
3500 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3501 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3502 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3503 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3504 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3505 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3506 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3507 max_len_label_text);
3508 label_text[max_len_label_text] = '\0';
3510 if (strlen(label_text) > 0)
3511 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3513 redraw_mask |= REDRAW_FIELD;
3516 static void DrawPreviewLevelLabel(int mode)
3518 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3521 static void DrawPreviewLevelInfo(int mode)
3523 if (mode == MICROLABEL_LEVEL_NAME)
3524 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3525 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3526 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3529 static void DrawPreviewLevelExt(boolean restart)
3531 static unsigned int scroll_delay = 0;
3532 static unsigned int label_delay = 0;
3533 static int from_x, from_y, scroll_direction;
3534 static int label_state, label_counter;
3535 unsigned int scroll_delay_value = preview.step_delay;
3536 boolean show_level_border = (BorderElement != EL_EMPTY);
3537 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3538 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3545 if (preview.anim_mode == ANIM_CENTERED)
3547 if (level_xsize > preview.xsize)
3548 from_x = (level_xsize - preview.xsize) / 2;
3549 if (level_ysize > preview.ysize)
3550 from_y = (level_ysize - preview.ysize) / 2;
3553 from_x += preview.xoffset;
3554 from_y += preview.yoffset;
3556 scroll_direction = MV_RIGHT;
3560 DrawPreviewLevelPlayfield(from_x, from_y);
3561 DrawPreviewLevelLabel(label_state);
3563 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3564 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3566 // initialize delay counters
3567 ResetDelayCounter(&scroll_delay);
3568 ResetDelayCounter(&label_delay);
3570 if (leveldir_current->name)
3572 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3573 char label_text[MAX_OUTPUT_LINESIZE + 1];
3574 int font_nr = pos->font;
3575 int max_len_label_text = getMaxTextLength(pos, font_nr);
3577 if (pos->size != -1)
3578 max_len_label_text = pos->size;
3580 strncpy(label_text, leveldir_current->name, max_len_label_text);
3581 label_text[max_len_label_text] = '\0';
3583 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3584 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3590 // scroll preview level, if needed
3591 if (preview.anim_mode != ANIM_NONE &&
3592 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3593 DelayReached(&scroll_delay, scroll_delay_value))
3595 switch (scroll_direction)
3600 from_x -= preview.step_offset;
3601 from_x = (from_x < 0 ? 0 : from_x);
3604 scroll_direction = MV_UP;
3608 if (from_x < level_xsize - preview.xsize)
3610 from_x += preview.step_offset;
3611 from_x = (from_x > level_xsize - preview.xsize ?
3612 level_xsize - preview.xsize : from_x);
3615 scroll_direction = MV_DOWN;
3621 from_y -= preview.step_offset;
3622 from_y = (from_y < 0 ? 0 : from_y);
3625 scroll_direction = MV_RIGHT;
3629 if (from_y < level_ysize - preview.ysize)
3631 from_y += preview.step_offset;
3632 from_y = (from_y > level_ysize - preview.ysize ?
3633 level_ysize - preview.ysize : from_y);
3636 scroll_direction = MV_LEFT;
3643 DrawPreviewLevelPlayfield(from_x, from_y);
3646 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3647 // redraw micro level label, if needed
3648 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3649 !strEqual(level.author, ANONYMOUS_NAME) &&
3650 !strEqual(level.author, leveldir_current->name) &&
3651 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3653 int max_label_counter = 23;
3655 if (leveldir_current->imported_from != NULL &&
3656 strlen(leveldir_current->imported_from) > 0)
3657 max_label_counter += 14;
3658 if (leveldir_current->imported_by != NULL &&
3659 strlen(leveldir_current->imported_by) > 0)
3660 max_label_counter += 14;
3662 label_counter = (label_counter + 1) % max_label_counter;
3663 label_state = (label_counter >= 0 && label_counter <= 7 ?
3664 MICROLABEL_LEVEL_NAME :
3665 label_counter >= 9 && label_counter <= 12 ?
3666 MICROLABEL_LEVEL_AUTHOR_HEAD :
3667 label_counter >= 14 && label_counter <= 21 ?
3668 MICROLABEL_LEVEL_AUTHOR :
3669 label_counter >= 23 && label_counter <= 26 ?
3670 MICROLABEL_IMPORTED_FROM_HEAD :
3671 label_counter >= 28 && label_counter <= 35 ?
3672 MICROLABEL_IMPORTED_FROM :
3673 label_counter >= 37 && label_counter <= 40 ?
3674 MICROLABEL_IMPORTED_BY_HEAD :
3675 label_counter >= 42 && label_counter <= 49 ?
3676 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3678 if (leveldir_current->imported_from == NULL &&
3679 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3680 label_state == MICROLABEL_IMPORTED_FROM))
3681 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3682 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3684 DrawPreviewLevelLabel(label_state);
3688 void DrawPreviewPlayers(void)
3690 if (game_status != GAME_MODE_MAIN)
3693 // do not draw preview players if level preview redefined, but players aren't
3694 if (preview.redefined && !menu.main.preview_players.redefined)
3697 boolean player_found[MAX_PLAYERS];
3698 int num_players = 0;
3701 for (i = 0; i < MAX_PLAYERS; i++)
3702 player_found[i] = FALSE;
3704 // check which players can be found in the level (simple approach)
3705 for (x = 0; x < lev_fieldx; x++)
3707 for (y = 0; y < lev_fieldy; y++)
3709 int element = level.field[x][y];
3711 if (IS_PLAYER_ELEMENT(element))
3713 int player_nr = GET_PLAYER_NR(element);
3715 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3717 if (!player_found[player_nr])
3720 player_found[player_nr] = TRUE;
3725 struct TextPosInfo *pos = &menu.main.preview_players;
3726 int tile_size = pos->tile_size;
3727 int border_size = pos->border_size;
3728 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3729 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3730 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3731 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3732 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3733 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3734 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3735 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3736 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3737 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3738 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3739 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3741 // clear area in which the players will be drawn
3742 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3743 max_players_width, max_players_height);
3745 if (!network.enabled && !setup.team_mode)
3748 // only draw players if level is suited for team mode
3749 if (num_players < 2)
3752 // draw all players that were found in the level
3753 for (i = 0; i < MAX_PLAYERS; i++)
3755 if (player_found[i])
3757 int graphic = el2img(EL_PLAYER_1 + i);
3759 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3761 xpos += player_xoffset;
3762 ypos += player_yoffset;
3767 void DrawPreviewLevelInitial(void)
3769 DrawPreviewLevelExt(TRUE);
3770 DrawPreviewPlayers();
3773 void DrawPreviewLevelAnimation(void)
3775 DrawPreviewLevelExt(FALSE);
3778 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3779 int border_size, int font_nr)
3781 int graphic = el2img(EL_PLAYER_1 + player_nr);
3782 int font_height = getFontHeight(font_nr);
3783 int player_height = MAX(tile_size, font_height);
3784 int xoffset_text = tile_size + border_size;
3785 int yoffset_text = (player_height - font_height) / 2;
3786 int yoffset_graphic = (player_height - tile_size) / 2;
3787 char *player_name = getNetworkPlayerName(player_nr + 1);
3789 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3791 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3794 static void DrawNetworkPlayersExt(boolean force)
3796 if (game_status != GAME_MODE_MAIN)
3799 if (!network.connected && !force)
3802 // do not draw network players if level preview redefined, but players aren't
3803 if (preview.redefined && !menu.main.network_players.redefined)
3806 int num_players = 0;
3809 for (i = 0; i < MAX_PLAYERS; i++)
3810 if (stored_player[i].connected_network)
3813 struct TextPosInfo *pos = &menu.main.network_players;
3814 int tile_size = pos->tile_size;
3815 int border_size = pos->border_size;
3816 int xoffset_text = tile_size + border_size;
3817 int font_nr = pos->font;
3818 int font_width = getFontWidth(font_nr);
3819 int font_height = getFontHeight(font_nr);
3820 int player_height = MAX(tile_size, font_height);
3821 int player_yoffset = player_height + border_size;
3822 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3823 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3824 int all_players_height = num_players * player_yoffset - border_size;
3825 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3826 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3827 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3829 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3830 max_players_width, max_players_height);
3832 // first draw local network player ...
3833 for (i = 0; i < MAX_PLAYERS; i++)
3835 if (stored_player[i].connected_network &&
3836 stored_player[i].connected_locally)
3838 char *player_name = getNetworkPlayerName(i + 1);
3839 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3840 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3842 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3844 ypos += player_yoffset;
3848 // ... then draw all other network players
3849 for (i = 0; i < MAX_PLAYERS; i++)
3851 if (stored_player[i].connected_network &&
3852 !stored_player[i].connected_locally)
3854 char *player_name = getNetworkPlayerName(i + 1);
3855 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3856 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3858 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3860 ypos += player_yoffset;
3865 void DrawNetworkPlayers(void)
3867 DrawNetworkPlayersExt(FALSE);
3870 void ClearNetworkPlayers(void)
3872 DrawNetworkPlayersExt(TRUE);
3875 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3876 int graphic, int lx, int ly,
3879 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3881 if (mask_mode == USE_MASKING)
3882 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3884 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3887 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3888 int graphic, int sync_frame, int mask_mode)
3890 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3892 if (mask_mode == USE_MASKING)
3893 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3895 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3898 static void DrawGraphicAnimation(int x, int y, int graphic)
3900 int lx = LEVELX(x), ly = LEVELY(y);
3901 int mask_mode = NO_MASKING;
3903 if (!IN_SCR_FIELD(x, y))
3906 if (game.use_masked_elements)
3908 if (Tile[lx][ly] != EL_EMPTY)
3910 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3912 mask_mode = USE_MASKING;
3916 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3917 graphic, lx, ly, mask_mode);
3919 MarkTileDirty(x, y);
3922 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3924 int lx = LEVELX(x), ly = LEVELY(y);
3925 int mask_mode = NO_MASKING;
3927 if (!IN_SCR_FIELD(x, y))
3930 if (game.use_masked_elements)
3932 if (Tile[lx][ly] != EL_EMPTY)
3934 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3936 mask_mode = USE_MASKING;
3940 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3941 graphic, lx, ly, mask_mode);
3943 MarkTileDirty(x, y);
3946 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3948 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3951 void DrawLevelElementAnimation(int x, int y, int element)
3953 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3955 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3958 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3960 int sx = SCREENX(x), sy = SCREENY(y);
3962 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3965 if (Tile[x][y] == EL_EMPTY)
3966 graphic = el2img(GfxElementEmpty[x][y]);
3968 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3971 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3974 DrawGraphicAnimation(sx, sy, graphic);
3977 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3978 DrawLevelFieldCrumbled(x, y);
3980 if (GFX_CRUMBLED(Tile[x][y]))
3981 DrawLevelFieldCrumbled(x, y);
3985 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3987 int sx = SCREENX(x), sy = SCREENY(y);
3990 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3993 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3995 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3998 DrawGraphicAnimation(sx, sy, graphic);
4000 if (GFX_CRUMBLED(element))
4001 DrawLevelFieldCrumbled(x, y);
4004 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4006 if (player->use_murphy)
4008 // this works only because currently only one player can be "murphy" ...
4009 static int last_horizontal_dir = MV_LEFT;
4010 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4012 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4013 last_horizontal_dir = move_dir;
4015 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4017 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4019 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4025 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4028 static boolean equalGraphics(int graphic1, int graphic2)
4030 struct GraphicInfo *g1 = &graphic_info[graphic1];
4031 struct GraphicInfo *g2 = &graphic_info[graphic2];
4033 return (g1->bitmap == g2->bitmap &&
4034 g1->src_x == g2->src_x &&
4035 g1->src_y == g2->src_y &&
4036 g1->anim_frames == g2->anim_frames &&
4037 g1->anim_delay == g2->anim_delay &&
4038 g1->anim_mode == g2->anim_mode);
4041 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4045 DRAW_PLAYER_STAGE_INIT = 0,
4046 DRAW_PLAYER_STAGE_LAST_FIELD,
4047 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4048 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4049 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4050 DRAW_PLAYER_STAGE_PLAYER,
4052 DRAW_PLAYER_STAGE_PLAYER,
4053 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4055 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4056 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4058 NUM_DRAW_PLAYER_STAGES
4061 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4063 static int static_last_player_graphic[MAX_PLAYERS];
4064 static int static_last_player_frame[MAX_PLAYERS];
4065 static boolean static_player_is_opaque[MAX_PLAYERS];
4066 static boolean draw_player[MAX_PLAYERS];
4067 int pnr = player->index_nr;
4069 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4071 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4072 static_last_player_frame[pnr] = player->Frame;
4073 static_player_is_opaque[pnr] = FALSE;
4075 draw_player[pnr] = TRUE;
4078 if (!draw_player[pnr])
4082 if (!IN_LEV_FIELD(player->jx, player->jy))
4084 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4085 Debug("draw:DrawPlayerExt", "This should never happen!");
4087 draw_player[pnr] = FALSE;
4093 int last_player_graphic = static_last_player_graphic[pnr];
4094 int last_player_frame = static_last_player_frame[pnr];
4095 boolean player_is_opaque = static_player_is_opaque[pnr];
4097 int jx = player->jx;
4098 int jy = player->jy;
4099 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4100 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4101 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4102 int last_jx = (player->is_moving ? jx - dx : jx);
4103 int last_jy = (player->is_moving ? jy - dy : jy);
4104 int next_jx = jx + dx;
4105 int next_jy = jy + dy;
4106 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4107 int sx = SCREENX(jx);
4108 int sy = SCREENY(jy);
4109 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4110 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4111 int element = Tile[jx][jy];
4112 int last_element = Tile[last_jx][last_jy];
4113 int action = (player->is_pushing ? ACTION_PUSHING :
4114 player->is_digging ? ACTION_DIGGING :
4115 player->is_collecting ? ACTION_COLLECTING :
4116 player->is_moving ? ACTION_MOVING :
4117 player->is_snapping ? ACTION_SNAPPING :
4118 player->is_dropping ? ACTION_DROPPING :
4119 player->is_waiting ? player->action_waiting :
4122 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4124 // ------------------------------------------------------------------------
4125 // initialize drawing the player
4126 // ------------------------------------------------------------------------
4128 draw_player[pnr] = FALSE;
4130 // GfxElement[][] is set to the element the player is digging or collecting;
4131 // remove also for off-screen player if the player is not moving anymore
4132 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4133 GfxElement[jx][jy] = EL_UNDEFINED;
4135 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4138 if (element == EL_EXPLOSION)
4141 InitPlayerGfxAnimation(player, action, move_dir);
4143 draw_player[pnr] = TRUE;
4145 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4147 // ------------------------------------------------------------------------
4148 // draw things in the field the player is leaving, if needed
4149 // ------------------------------------------------------------------------
4151 if (!IN_SCR_FIELD(sx, sy))
4152 draw_player[pnr] = FALSE;
4154 if (!player->is_moving)
4157 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4159 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4161 if (last_element == EL_DYNAMITE_ACTIVE ||
4162 last_element == EL_EM_DYNAMITE_ACTIVE ||
4163 last_element == EL_SP_DISK_RED_ACTIVE)
4164 DrawDynamite(last_jx, last_jy);
4166 DrawLevelFieldThruMask(last_jx, last_jy);
4168 else if (last_element == EL_DYNAMITE_ACTIVE ||
4169 last_element == EL_EM_DYNAMITE_ACTIVE ||
4170 last_element == EL_SP_DISK_RED_ACTIVE)
4171 DrawDynamite(last_jx, last_jy);
4173 DrawLevelField(last_jx, last_jy);
4175 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4176 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4178 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4180 // ------------------------------------------------------------------------
4181 // draw things behind the player, if needed
4182 // ------------------------------------------------------------------------
4186 DrawLevelElement(jx, jy, Back[jx][jy]);
4191 if (IS_ACTIVE_BOMB(element))
4193 DrawLevelElement(jx, jy, EL_EMPTY);
4198 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4200 int old_element = GfxElement[jx][jy];
4201 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4202 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4204 if (GFX_CRUMBLED(old_element))
4205 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4207 DrawScreenGraphic(sx, sy, old_graphic, frame);
4209 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4210 static_player_is_opaque[pnr] = TRUE;
4214 GfxElement[jx][jy] = EL_UNDEFINED;
4216 // make sure that pushed elements are drawn with correct frame rate
4217 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4219 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4220 GfxFrame[jx][jy] = player->StepFrame;
4222 DrawLevelField(jx, jy);
4225 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4227 // ------------------------------------------------------------------------
4228 // draw things the player is pushing, if needed
4229 // ------------------------------------------------------------------------
4231 if (!player->is_pushing || !player->is_moving)
4234 int gfx_frame = GfxFrame[jx][jy];
4236 if (!IS_MOVING(jx, jy)) // push movement already finished
4238 element = Tile[next_jx][next_jy];
4239 gfx_frame = GfxFrame[next_jx][next_jy];
4242 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4243 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4244 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4246 // draw background element under pushed element (like the Sokoban field)
4247 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4249 // this allows transparent pushing animation over non-black background
4252 DrawLevelElement(jx, jy, Back[jx][jy]);
4254 DrawLevelElement(jx, jy, EL_EMPTY);
4256 if (Back[next_jx][next_jy])
4257 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4259 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4261 else if (Back[next_jx][next_jy])
4262 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4264 int px = SCREENX(jx), py = SCREENY(jy);
4265 int pxx = (TILEX - ABS(sxx)) * dx;
4266 int pyy = (TILEY - ABS(syy)) * dy;
4269 // do not draw (EM style) pushing animation when pushing is finished
4270 // (two-tile animations usually do not contain start and end frame)
4271 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4272 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4274 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4276 // masked drawing is needed for EMC style (double) movement graphics
4277 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4278 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4281 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4283 // ------------------------------------------------------------------------
4284 // draw player himself
4285 // ------------------------------------------------------------------------
4287 int graphic = getPlayerGraphic(player, move_dir);
4289 // in the case of changed player action or direction, prevent the current
4290 // animation frame from being restarted for identical animations
4291 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4292 player->Frame = last_player_frame;
4294 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4296 if (player_is_opaque)
4297 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4299 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4301 if (SHIELD_ON(player))
4303 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4304 IMG_SHIELD_NORMAL_ACTIVE);
4305 frame = getGraphicAnimationFrame(graphic, -1);
4307 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4310 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4312 // ------------------------------------------------------------------------
4313 // draw things in front of player (active dynamite or dynabombs)
4314 // ------------------------------------------------------------------------
4316 if (IS_ACTIVE_BOMB(element))
4318 int graphic = el2img(element);
4319 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4321 if (game.emulation == EMU_SUPAPLEX)
4322 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4324 DrawGraphicThruMask(sx, sy, graphic, frame);
4327 if (player_is_moving && last_element == EL_EXPLOSION)
4329 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4330 GfxElement[last_jx][last_jy] : EL_EMPTY);
4331 int graphic = el_act2img(element, ACTION_EXPLODING);
4332 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4333 int phase = ExplodePhase[last_jx][last_jy] - 1;
4334 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4337 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4340 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4342 // ------------------------------------------------------------------------
4343 // draw elements the player is just walking/passing through/under
4344 // ------------------------------------------------------------------------
4346 if (player_is_moving)
4348 // handle the field the player is leaving ...
4349 if (IS_ACCESSIBLE_INSIDE(last_element))
4350 DrawLevelField(last_jx, last_jy);
4351 else if (IS_ACCESSIBLE_UNDER(last_element))
4352 DrawLevelFieldThruMask(last_jx, last_jy);
4355 // do not redraw accessible elements if the player is just pushing them
4356 if (!player_is_moving || !player->is_pushing)
4358 // ... and the field the player is entering
4359 if (IS_ACCESSIBLE_INSIDE(element))
4360 DrawLevelField(jx, jy);
4361 else if (IS_ACCESSIBLE_UNDER(element))
4362 DrawLevelFieldThruMask(jx, jy);
4365 MarkTileDirty(sx, sy);
4369 void DrawPlayer(struct PlayerInfo *player)
4373 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4374 DrawPlayerExt(player, i);
4377 void DrawAllPlayers(void)
4381 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4382 for (j = 0; j < MAX_PLAYERS; j++)
4383 if (stored_player[j].active)
4384 DrawPlayerExt(&stored_player[j], i);
4387 void DrawPlayerField(int x, int y)
4389 if (!IS_PLAYER(x, y))
4392 DrawPlayer(PLAYERINFO(x, y));
4395 // ----------------------------------------------------------------------------
4397 void WaitForEventToContinue(void)
4399 boolean first_wait = TRUE;
4400 boolean still_wait = TRUE;
4402 if (program.headless)
4405 // simulate releasing mouse button over last gadget, if still pressed
4407 HandleGadgets(-1, -1, 0);
4409 button_status = MB_RELEASED;
4412 ClearPlayerAction();
4418 if (NextValidEvent(&event))
4422 case EVENT_BUTTONPRESS:
4423 case EVENT_FINGERPRESS:
4427 case EVENT_BUTTONRELEASE:
4428 case EVENT_FINGERRELEASE:
4429 still_wait = first_wait;
4432 case EVENT_KEYPRESS:
4433 case SDL_CONTROLLERBUTTONDOWN:
4434 case SDL_JOYBUTTONDOWN:
4439 HandleOtherEvents(&event);
4443 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4448 if (!PendingEvent())
4453 #define MAX_REQUEST_LINES 13
4454 #define MAX_REQUEST_LINE_FONT1_LEN 7
4455 #define MAX_REQUEST_LINE_FONT2_LEN 10
4457 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4459 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4461 int draw_buffer_last = GetDrawtoField();
4462 int width = request.width;
4463 int height = request.height;
4467 // when showing request dialog after game ended, deactivate game panel
4468 if (game_just_ended)
4469 game.panel.active = FALSE;
4471 game.request_active = TRUE;
4473 setRequestPosition(&sx, &sy, FALSE);
4475 button_status = MB_RELEASED;
4477 request_gadget_id = -1;
4482 boolean event_handled = FALSE;
4484 if (game_just_ended)
4486 SetDrawtoField(draw_buffer_game);
4488 HandleGameActions();
4490 SetDrawtoField(DRAW_TO_BACKBUFFER);
4492 if (global.use_envelope_request)
4494 // copy current state of request area to middle of playfield area
4495 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4503 while (NextValidEvent(&event))
4505 event_handled = TRUE;
4509 case EVENT_BUTTONPRESS:
4510 case EVENT_BUTTONRELEASE:
4511 case EVENT_MOTIONNOTIFY:
4515 if (event.type == EVENT_MOTIONNOTIFY)
4520 motion_status = TRUE;
4521 mx = ((MotionEvent *) &event)->x;
4522 my = ((MotionEvent *) &event)->y;
4526 motion_status = FALSE;
4527 mx = ((ButtonEvent *) &event)->x;
4528 my = ((ButtonEvent *) &event)->y;
4529 if (event.type == EVENT_BUTTONPRESS)
4530 button_status = ((ButtonEvent *) &event)->button;
4532 button_status = MB_RELEASED;
4535 // this sets 'request_gadget_id'
4536 HandleGadgets(mx, my, button_status);
4538 switch (request_gadget_id)
4540 case TOOL_CTRL_ID_YES:
4541 case TOOL_CTRL_ID_TOUCH_YES:
4544 case TOOL_CTRL_ID_NO:
4545 case TOOL_CTRL_ID_TOUCH_NO:
4548 case TOOL_CTRL_ID_CONFIRM:
4549 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4550 result = TRUE | FALSE;
4553 case TOOL_CTRL_ID_PLAYER_1:
4556 case TOOL_CTRL_ID_PLAYER_2:
4559 case TOOL_CTRL_ID_PLAYER_3:
4562 case TOOL_CTRL_ID_PLAYER_4:
4567 // only check clickable animations if no request gadget clicked
4568 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4575 case SDL_WINDOWEVENT:
4576 HandleWindowEvent((WindowEvent *) &event);
4579 case SDL_APP_WILLENTERBACKGROUND:
4580 case SDL_APP_DIDENTERBACKGROUND:
4581 case SDL_APP_WILLENTERFOREGROUND:
4582 case SDL_APP_DIDENTERFOREGROUND:
4583 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4586 case EVENT_KEYPRESS:
4588 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4593 if (req_state & REQ_CONFIRM)
4602 #if defined(KSYM_Rewind)
4603 case KSYM_Rewind: // for Amazon Fire TV remote
4612 #if defined(KSYM_FastForward)
4613 case KSYM_FastForward: // for Amazon Fire TV remote
4619 HandleKeysDebug(key, KEY_PRESSED);
4623 if (req_state & REQ_PLAYER)
4625 int old_player_nr = setup.network_player_nr;
4628 result = old_player_nr + 1;
4633 result = old_player_nr + 1;
4664 case EVENT_FINGERRELEASE:
4665 case EVENT_KEYRELEASE:
4666 ClearPlayerAction();
4669 case SDL_CONTROLLERBUTTONDOWN:
4670 switch (event.cbutton.button)
4672 case SDL_CONTROLLER_BUTTON_A:
4673 case SDL_CONTROLLER_BUTTON_X:
4674 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4675 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4679 case SDL_CONTROLLER_BUTTON_B:
4680 case SDL_CONTROLLER_BUTTON_Y:
4681 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4682 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4683 case SDL_CONTROLLER_BUTTON_BACK:
4688 if (req_state & REQ_PLAYER)
4690 int old_player_nr = setup.network_player_nr;
4693 result = old_player_nr + 1;
4695 switch (event.cbutton.button)
4697 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4698 case SDL_CONTROLLER_BUTTON_Y:
4702 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4703 case SDL_CONTROLLER_BUTTON_B:
4707 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4708 case SDL_CONTROLLER_BUTTON_A:
4712 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4713 case SDL_CONTROLLER_BUTTON_X:
4724 case SDL_CONTROLLERBUTTONUP:
4725 HandleJoystickEvent(&event);
4726 ClearPlayerAction();
4730 HandleOtherEvents(&event);
4735 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4737 int joy = AnyJoystick();
4739 if (joy & JOY_BUTTON_1)
4741 else if (joy & JOY_BUTTON_2)
4744 else if (AnyJoystick())
4746 int joy = AnyJoystick();
4748 if (req_state & REQ_PLAYER)
4752 else if (joy & JOY_RIGHT)
4754 else if (joy & JOY_DOWN)
4756 else if (joy & JOY_LEFT)
4763 if (game_just_ended)
4765 if (global.use_envelope_request)
4767 // copy back current state of pressed buttons inside request area
4768 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4772 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4778 SetDrawtoField(draw_buffer_last);
4780 game.request_active = FALSE;
4785 static boolean RequestDoor(char *text, unsigned int req_state)
4787 int draw_buffer_last = GetDrawtoField();
4788 unsigned int old_door_state;
4789 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4790 int font_nr = FONT_TEXT_2;
4795 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4797 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4798 font_nr = FONT_TEXT_1;
4801 if (game_status == GAME_MODE_PLAYING)
4802 BlitScreenToBitmap(backbuffer);
4804 // disable deactivated drawing when quick-loading level tape recording
4805 if (tape.playing && tape.deactivate_display)
4806 TapeDeactivateDisplayOff(TRUE);
4808 SetMouseCursor(CURSOR_DEFAULT);
4810 // pause network game while waiting for request to answer
4811 if (network.enabled &&
4812 game_status == GAME_MODE_PLAYING &&
4813 !game.all_players_gone &&
4814 req_state & REQUEST_WAIT_FOR_INPUT)
4815 SendToServer_PausePlaying();
4817 old_door_state = GetDoorState();
4819 // simulate releasing mouse button over last gadget, if still pressed
4821 HandleGadgets(-1, -1, 0);
4825 // draw released gadget before proceeding
4828 if (old_door_state & DOOR_OPEN_1)
4830 CloseDoor(DOOR_CLOSE_1);
4832 // save old door content
4833 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4834 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4837 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4838 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4840 // clear door drawing field
4841 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4843 // force DOOR font inside door area
4844 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4846 // write text for request
4847 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4849 char text_line[max_request_line_len + 1];
4855 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4857 tc = *(text_ptr + tx);
4858 // if (!tc || tc == ' ')
4859 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4863 if ((tc == '?' || tc == '!') && tl == 0)
4873 strncpy(text_line, text_ptr, tl);
4876 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4877 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4878 text_line, font_nr);
4880 text_ptr += tl + (tc == ' ' ? 1 : 0);
4881 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4886 if (req_state & REQ_ASK)
4888 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4889 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4890 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4891 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4893 else if (req_state & REQ_CONFIRM)
4895 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4896 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4898 else if (req_state & REQ_PLAYER)
4900 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4901 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4902 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4903 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4906 // copy request gadgets to door backbuffer
4907 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4909 OpenDoor(DOOR_OPEN_1);
4911 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4913 if (game_status == GAME_MODE_PLAYING)
4915 SetPanelBackground();
4916 SetDrawBackgroundMask(REDRAW_DOOR_1);
4920 SetDrawBackgroundMask(REDRAW_FIELD);
4926 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4928 // ---------- handle request buttons ----------
4929 result = RequestHandleEvents(req_state, draw_buffer_last);
4933 if (!(req_state & REQ_STAY_OPEN))
4935 CloseDoor(DOOR_CLOSE_1);
4937 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4938 (req_state & REQ_REOPEN))
4939 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4944 if (game_status == GAME_MODE_PLAYING)
4946 SetPanelBackground();
4947 SetDrawBackgroundMask(REDRAW_DOOR_1);
4951 SetDrawBackgroundMask(REDRAW_FIELD);
4954 // continue network game after request
4955 if (network.enabled &&
4956 game_status == GAME_MODE_PLAYING &&
4957 !game.all_players_gone &&
4958 req_state & REQUEST_WAIT_FOR_INPUT)
4959 SendToServer_ContinuePlaying();
4961 // restore deactivated drawing when quick-loading level tape recording
4962 if (tape.playing && tape.deactivate_display)
4963 TapeDeactivateDisplayOn();
4968 static boolean RequestEnvelope(char *text, unsigned int req_state)
4970 int draw_buffer_last = GetDrawtoField();
4973 if (game_status == GAME_MODE_PLAYING)
4974 BlitScreenToBitmap(backbuffer);
4976 // disable deactivated drawing when quick-loading level tape recording
4977 if (tape.playing && tape.deactivate_display)
4978 TapeDeactivateDisplayOff(TRUE);
4980 SetMouseCursor(CURSOR_DEFAULT);
4982 // pause network game while waiting for request to answer
4983 if (network.enabled &&
4984 game_status == GAME_MODE_PLAYING &&
4985 !game.all_players_gone &&
4986 req_state & REQUEST_WAIT_FOR_INPUT)
4987 SendToServer_PausePlaying();
4989 // simulate releasing mouse button over last gadget, if still pressed
4991 HandleGadgets(-1, -1, 0);
4995 // (replace with setting corresponding request background)
4996 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4997 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4999 // clear door drawing field
5000 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5002 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5004 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5006 if (game_status == GAME_MODE_PLAYING)
5008 SetPanelBackground();
5009 SetDrawBackgroundMask(REDRAW_DOOR_1);
5013 SetDrawBackgroundMask(REDRAW_FIELD);
5019 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5021 // ---------- handle request buttons ----------
5022 result = RequestHandleEvents(req_state, draw_buffer_last);
5026 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5030 if (game_status == GAME_MODE_PLAYING)
5032 SetPanelBackground();
5033 SetDrawBackgroundMask(REDRAW_DOOR_1);
5037 SetDrawBackgroundMask(REDRAW_FIELD);
5040 // continue network game after request
5041 if (network.enabled &&
5042 game_status == GAME_MODE_PLAYING &&
5043 !game.all_players_gone &&
5044 req_state & REQUEST_WAIT_FOR_INPUT)
5045 SendToServer_ContinuePlaying();
5047 // restore deactivated drawing when quick-loading level tape recording
5048 if (tape.playing && tape.deactivate_display)
5049 TapeDeactivateDisplayOn();
5054 boolean Request(char *text, unsigned int req_state)
5056 boolean overlay_enabled = GetOverlayEnabled();
5059 game.request_active_or_moving = TRUE;
5061 SetOverlayEnabled(FALSE);
5063 if (global.use_envelope_request)
5064 result = RequestEnvelope(text, req_state);
5066 result = RequestDoor(text, req_state);
5068 SetOverlayEnabled(overlay_enabled);
5070 game.request_active_or_moving = FALSE;
5075 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5077 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5078 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5081 if (dpo1->sort_priority != dpo2->sort_priority)
5082 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5084 compare_result = dpo1->nr - dpo2->nr;
5086 return compare_result;
5089 void InitGraphicCompatibilityInfo_Doors(void)
5095 struct DoorInfo *door;
5099 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5100 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5102 { -1, -1, -1, NULL }
5104 struct Rect door_rect_list[] =
5106 { DX, DY, DXSIZE, DYSIZE },
5107 { VX, VY, VXSIZE, VYSIZE }
5111 for (i = 0; doors[i].door_token != -1; i++)
5113 int door_token = doors[i].door_token;
5114 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5115 int part_1 = doors[i].part_1;
5116 int part_8 = doors[i].part_8;
5117 int part_2 = part_1 + 1;
5118 int part_3 = part_1 + 2;
5119 struct DoorInfo *door = doors[i].door;
5120 struct Rect *door_rect = &door_rect_list[door_index];
5121 boolean door_gfx_redefined = FALSE;
5123 // check if any door part graphic definitions have been redefined
5125 for (j = 0; door_part_controls[j].door_token != -1; j++)
5127 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5128 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5130 if (dpc->door_token == door_token && fi->redefined)
5131 door_gfx_redefined = TRUE;
5134 // check for old-style door graphic/animation modifications
5136 if (!door_gfx_redefined)
5138 if (door->anim_mode & ANIM_STATIC_PANEL)
5140 door->panel.step_xoffset = 0;
5141 door->panel.step_yoffset = 0;
5144 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5146 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5147 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5148 int num_door_steps, num_panel_steps;
5150 // remove door part graphics other than the two default wings
5152 for (j = 0; door_part_controls[j].door_token != -1; j++)
5154 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5155 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5157 if (dpc->graphic >= part_3 &&
5158 dpc->graphic <= part_8)
5162 // set graphics and screen positions of the default wings
5164 g_part_1->width = door_rect->width;
5165 g_part_1->height = door_rect->height;
5166 g_part_2->width = door_rect->width;
5167 g_part_2->height = door_rect->height;
5168 g_part_2->src_x = door_rect->width;
5169 g_part_2->src_y = g_part_1->src_y;
5171 door->part_2.x = door->part_1.x;
5172 door->part_2.y = door->part_1.y;
5174 if (door->width != -1)
5176 g_part_1->width = door->width;
5177 g_part_2->width = door->width;
5179 // special treatment for graphics and screen position of right wing
5180 g_part_2->src_x += door_rect->width - door->width;
5181 door->part_2.x += door_rect->width - door->width;
5184 if (door->height != -1)
5186 g_part_1->height = door->height;
5187 g_part_2->height = door->height;
5189 // special treatment for graphics and screen position of bottom wing
5190 g_part_2->src_y += door_rect->height - door->height;
5191 door->part_2.y += door_rect->height - door->height;
5194 // set animation delays for the default wings and panels
5196 door->part_1.step_delay = door->step_delay;
5197 door->part_2.step_delay = door->step_delay;
5198 door->panel.step_delay = door->step_delay;
5200 // set animation draw order for the default wings
5202 door->part_1.sort_priority = 2; // draw left wing over ...
5203 door->part_2.sort_priority = 1; // ... right wing
5205 // set animation draw offset for the default wings
5207 if (door->anim_mode & ANIM_HORIZONTAL)
5209 door->part_1.step_xoffset = door->step_offset;
5210 door->part_1.step_yoffset = 0;
5211 door->part_2.step_xoffset = door->step_offset * -1;
5212 door->part_2.step_yoffset = 0;
5214 num_door_steps = g_part_1->width / door->step_offset;
5216 else // ANIM_VERTICAL
5218 door->part_1.step_xoffset = 0;
5219 door->part_1.step_yoffset = door->step_offset;
5220 door->part_2.step_xoffset = 0;
5221 door->part_2.step_yoffset = door->step_offset * -1;
5223 num_door_steps = g_part_1->height / door->step_offset;
5226 // set animation draw offset for the default panels
5228 if (door->step_offset > 1)
5230 num_panel_steps = 2 * door_rect->height / door->step_offset;
5231 door->panel.start_step = num_panel_steps - num_door_steps;
5232 door->panel.start_step_closing = door->panel.start_step;
5236 num_panel_steps = door_rect->height / door->step_offset;
5237 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5238 door->panel.start_step_closing = door->panel.start_step;
5239 door->panel.step_delay *= 2;
5246 void InitDoors(void)
5250 for (i = 0; door_part_controls[i].door_token != -1; i++)
5252 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5253 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5255 // initialize "start_step_opening" and "start_step_closing", if needed
5256 if (dpc->pos->start_step_opening == 0 &&
5257 dpc->pos->start_step_closing == 0)
5259 // dpc->pos->start_step_opening = dpc->pos->start_step;
5260 dpc->pos->start_step_closing = dpc->pos->start_step;
5263 // fill structure for door part draw order (sorted below)
5265 dpo->sort_priority = dpc->pos->sort_priority;
5268 // sort door part controls according to sort_priority and graphic number
5269 qsort(door_part_order, MAX_DOOR_PARTS,
5270 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5273 unsigned int OpenDoor(unsigned int door_state)
5275 if (door_state & DOOR_COPY_BACK)
5277 if (door_state & DOOR_OPEN_1)
5278 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5279 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5281 if (door_state & DOOR_OPEN_2)
5282 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5283 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5285 door_state &= ~DOOR_COPY_BACK;
5288 return MoveDoor(door_state);
5291 unsigned int CloseDoor(unsigned int door_state)
5293 unsigned int old_door_state = GetDoorState();
5295 if (!(door_state & DOOR_NO_COPY_BACK))
5297 if (old_door_state & DOOR_OPEN_1)
5298 BlitBitmap(backbuffer, bitmap_db_door_1,
5299 DX, DY, DXSIZE, DYSIZE, 0, 0);
5301 if (old_door_state & DOOR_OPEN_2)
5302 BlitBitmap(backbuffer, bitmap_db_door_2,
5303 VX, VY, VXSIZE, VYSIZE, 0, 0);
5305 door_state &= ~DOOR_NO_COPY_BACK;
5308 return MoveDoor(door_state);
5311 unsigned int GetDoorState(void)
5313 return MoveDoor(DOOR_GET_STATE);
5316 unsigned int SetDoorState(unsigned int door_state)
5318 return MoveDoor(door_state | DOOR_SET_STATE);
5321 static int euclid(int a, int b)
5323 return (b ? euclid(b, a % b) : a);
5326 unsigned int MoveDoor(unsigned int door_state)
5328 struct Rect door_rect_list[] =
5330 { DX, DY, DXSIZE, DYSIZE },
5331 { VX, VY, VXSIZE, VYSIZE }
5333 static int door1 = DOOR_CLOSE_1;
5334 static int door2 = DOOR_CLOSE_2;
5335 unsigned int door_delay = 0;
5336 unsigned int door_delay_value;
5339 if (door_state == DOOR_GET_STATE)
5340 return (door1 | door2);
5342 if (door_state & DOOR_SET_STATE)
5344 if (door_state & DOOR_ACTION_1)
5345 door1 = door_state & DOOR_ACTION_1;
5346 if (door_state & DOOR_ACTION_2)
5347 door2 = door_state & DOOR_ACTION_2;
5349 return (door1 | door2);
5352 if (!(door_state & DOOR_FORCE_REDRAW))
5354 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5355 door_state &= ~DOOR_OPEN_1;
5356 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5357 door_state &= ~DOOR_CLOSE_1;
5358 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5359 door_state &= ~DOOR_OPEN_2;
5360 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5361 door_state &= ~DOOR_CLOSE_2;
5364 if (global.autoplay_leveldir)
5366 door_state |= DOOR_NO_DELAY;
5367 door_state &= ~DOOR_CLOSE_ALL;
5370 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5371 door_state |= DOOR_NO_DELAY;
5373 if (door_state & DOOR_ACTION)
5375 boolean door_panel_drawn[NUM_DOORS];
5376 boolean panel_has_doors[NUM_DOORS];
5377 boolean door_part_skip[MAX_DOOR_PARTS];
5378 boolean door_part_done[MAX_DOOR_PARTS];
5379 boolean door_part_done_all;
5380 int num_steps[MAX_DOOR_PARTS];
5381 int max_move_delay = 0; // delay for complete animations of all doors
5382 int max_step_delay = 0; // delay (ms) between two animation frames
5383 int num_move_steps = 0; // number of animation steps for all doors
5384 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5385 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5389 for (i = 0; i < NUM_DOORS; i++)
5390 panel_has_doors[i] = FALSE;
5392 for (i = 0; i < MAX_DOOR_PARTS; i++)
5394 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5395 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5396 int door_token = dpc->door_token;
5398 door_part_done[i] = FALSE;
5399 door_part_skip[i] = (!(door_state & door_token) ||
5403 for (i = 0; i < MAX_DOOR_PARTS; i++)
5405 int nr = door_part_order[i].nr;
5406 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5407 struct DoorPartPosInfo *pos = dpc->pos;
5408 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5409 int door_token = dpc->door_token;
5410 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5411 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5412 int step_xoffset = ABS(pos->step_xoffset);
5413 int step_yoffset = ABS(pos->step_yoffset);
5414 int step_delay = pos->step_delay;
5415 int current_door_state = door_state & door_token;
5416 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5417 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5418 boolean part_opening = (is_panel ? door_closing : door_opening);
5419 int start_step = (part_opening ? pos->start_step_opening :
5420 pos->start_step_closing);
5421 float move_xsize = (step_xoffset ? g->width : 0);
5422 float move_ysize = (step_yoffset ? g->height : 0);
5423 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5424 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5425 int move_steps = (move_xsteps && move_ysteps ?
5426 MIN(move_xsteps, move_ysteps) :
5427 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5428 int move_delay = move_steps * step_delay;
5430 if (door_part_skip[nr])
5433 max_move_delay = MAX(max_move_delay, move_delay);
5434 max_step_delay = (max_step_delay == 0 ? step_delay :
5435 euclid(max_step_delay, step_delay));
5436 num_steps[nr] = move_steps;
5440 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5442 panel_has_doors[door_index] = TRUE;
5446 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5448 num_move_steps = max_move_delay / max_step_delay;
5449 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5451 door_delay_value = max_step_delay;
5453 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5455 start = num_move_steps - 1;
5459 // opening door sound has priority over simultaneously closing door
5460 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5462 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5464 if (door_state & DOOR_OPEN_1)
5465 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5466 if (door_state & DOOR_OPEN_2)
5467 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5469 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5471 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5473 if (door_state & DOOR_CLOSE_1)
5474 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5475 if (door_state & DOOR_CLOSE_2)
5476 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5480 for (k = start; k < num_move_steps; k++)
5482 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5484 door_part_done_all = TRUE;
5486 for (i = 0; i < NUM_DOORS; i++)
5487 door_panel_drawn[i] = FALSE;
5489 for (i = 0; i < MAX_DOOR_PARTS; i++)
5491 int nr = door_part_order[i].nr;
5492 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5493 struct DoorPartPosInfo *pos = dpc->pos;
5494 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5495 int door_token = dpc->door_token;
5496 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5497 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5498 boolean is_panel_and_door_has_closed = FALSE;
5499 struct Rect *door_rect = &door_rect_list[door_index];
5500 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5502 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5503 int current_door_state = door_state & door_token;
5504 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5505 boolean door_closing = !door_opening;
5506 boolean part_opening = (is_panel ? door_closing : door_opening);
5507 boolean part_closing = !part_opening;
5508 int start_step = (part_opening ? pos->start_step_opening :
5509 pos->start_step_closing);
5510 int step_delay = pos->step_delay;
5511 int step_factor = step_delay / max_step_delay;
5512 int k1 = (step_factor ? k / step_factor + 1 : k);
5513 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5514 int kk = MAX(0, k2);
5517 int src_x, src_y, src_xx, src_yy;
5518 int dst_x, dst_y, dst_xx, dst_yy;
5521 if (door_part_skip[nr])
5524 if (!(door_state & door_token))
5532 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5533 int kk_door = MAX(0, k2_door);
5534 int sync_frame = kk_door * door_delay_value;
5535 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5537 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5538 &g_src_x, &g_src_y);
5543 if (!door_panel_drawn[door_index])
5545 ClearRectangle(drawto, door_rect->x, door_rect->y,
5546 door_rect->width, door_rect->height);
5548 door_panel_drawn[door_index] = TRUE;
5551 // draw opening or closing door parts
5553 if (pos->step_xoffset < 0) // door part on right side
5556 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5559 if (dst_xx + width > door_rect->width)
5560 width = door_rect->width - dst_xx;
5562 else // door part on left side
5565 dst_xx = pos->x - kk * pos->step_xoffset;
5569 src_xx = ABS(dst_xx);
5573 width = g->width - src_xx;
5575 if (width > door_rect->width)
5576 width = door_rect->width;
5578 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5581 if (pos->step_yoffset < 0) // door part on bottom side
5584 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5587 if (dst_yy + height > door_rect->height)
5588 height = door_rect->height - dst_yy;
5590 else // door part on top side
5593 dst_yy = pos->y - kk * pos->step_yoffset;
5597 src_yy = ABS(dst_yy);
5601 height = g->height - src_yy;
5604 src_x = g_src_x + src_xx;
5605 src_y = g_src_y + src_yy;
5607 dst_x = door_rect->x + dst_xx;
5608 dst_y = door_rect->y + dst_yy;
5610 is_panel_and_door_has_closed =
5613 panel_has_doors[door_index] &&
5614 k >= num_move_steps_doors_only - 1);
5616 if (width >= 0 && width <= g->width &&
5617 height >= 0 && height <= g->height &&
5618 !is_panel_and_door_has_closed)
5620 if (is_panel || !pos->draw_masked)
5621 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5624 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5628 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5630 if ((part_opening && (width < 0 || height < 0)) ||
5631 (part_closing && (width >= g->width && height >= g->height)))
5632 door_part_done[nr] = TRUE;
5634 // continue door part animations, but not panel after door has closed
5635 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5636 door_part_done_all = FALSE;
5639 if (!(door_state & DOOR_NO_DELAY))
5643 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5645 // prevent OS (Windows) from complaining about program not responding
5649 if (door_part_done_all)
5653 if (!(door_state & DOOR_NO_DELAY))
5655 // wait for specified door action post delay
5656 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5657 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5658 else if (door_state & DOOR_ACTION_1)
5659 door_delay_value = door_1.post_delay;
5660 else if (door_state & DOOR_ACTION_2)
5661 door_delay_value = door_2.post_delay;
5663 while (!DelayReached(&door_delay, door_delay_value))
5668 if (door_state & DOOR_ACTION_1)
5669 door1 = door_state & DOOR_ACTION_1;
5670 if (door_state & DOOR_ACTION_2)
5671 door2 = door_state & DOOR_ACTION_2;
5673 // draw masked border over door area
5674 DrawMaskedBorder(REDRAW_DOOR_1);
5675 DrawMaskedBorder(REDRAW_DOOR_2);
5677 ClearAutoRepeatKeyEvents();
5679 return (door1 | door2);
5682 static boolean useSpecialEditorDoor(void)
5684 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5685 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5687 // do not draw special editor door if editor border defined or redefined
5688 if (graphic_info[graphic].bitmap != NULL || redefined)
5691 // do not draw special editor door if global border defined to be empty
5692 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5695 // do not draw special editor door if viewport definitions do not match
5699 EY + EYSIZE != VY + VYSIZE)
5705 void DrawSpecialEditorDoor(void)
5707 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5708 int top_border_width = gfx1->width;
5709 int top_border_height = gfx1->height;
5710 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5711 int ex = EX - outer_border;
5712 int ey = EY - outer_border;
5713 int vy = VY - outer_border;
5714 int exsize = EXSIZE + 2 * outer_border;
5716 if (!useSpecialEditorDoor())
5719 // draw bigger level editor toolbox window
5720 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5721 top_border_width, top_border_height, ex, ey - top_border_height);
5722 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5723 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5725 redraw_mask |= REDRAW_ALL;
5728 void UndrawSpecialEditorDoor(void)
5730 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5731 int top_border_width = gfx1->width;
5732 int top_border_height = gfx1->height;
5733 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5734 int ex = EX - outer_border;
5735 int ey = EY - outer_border;
5736 int ey_top = ey - top_border_height;
5737 int exsize = EXSIZE + 2 * outer_border;
5738 int eysize = EYSIZE + 2 * outer_border;
5740 if (!useSpecialEditorDoor())
5743 // draw normal tape recorder window
5744 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5746 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5747 ex, ey_top, top_border_width, top_border_height,
5749 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5750 ex, ey, exsize, eysize, ex, ey);
5754 // if screen background is set to "[NONE]", clear editor toolbox window
5755 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5756 ClearRectangle(drawto, ex, ey, exsize, eysize);
5759 redraw_mask |= REDRAW_ALL;
5763 // ---------- new tool button stuff -------------------------------------------
5768 struct TextPosInfo *pos;
5770 boolean is_touch_button;
5772 } toolbutton_info[NUM_TOOL_BUTTONS] =
5775 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5776 TOOL_CTRL_ID_YES, FALSE, "yes"
5779 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5780 TOOL_CTRL_ID_NO, FALSE, "no"
5783 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5784 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5787 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5788 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5791 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5792 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5795 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5796 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5799 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5800 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5803 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5804 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5807 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5808 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5811 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5812 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5816 void CreateToolButtons(void)
5820 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5822 int graphic = toolbutton_info[i].graphic;
5823 struct GraphicInfo *gfx = &graphic_info[graphic];
5824 struct TextPosInfo *pos = toolbutton_info[i].pos;
5825 struct GadgetInfo *gi;
5826 Bitmap *deco_bitmap = None;
5827 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5828 unsigned int event_mask = GD_EVENT_RELEASED;
5829 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5830 int base_x = (is_touch_button ? 0 : DX);
5831 int base_y = (is_touch_button ? 0 : DY);
5832 int gd_x = gfx->src_x;
5833 int gd_y = gfx->src_y;
5834 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5835 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5840 if (global.use_envelope_request && !is_touch_button)
5842 setRequestPosition(&base_x, &base_y, TRUE);
5844 // check if request buttons are outside of envelope and fix, if needed
5845 if (x < 0 || x + gfx->width > request.width ||
5846 y < 0 || y + gfx->height > request.height)
5848 if (id == TOOL_CTRL_ID_YES)
5851 y = request.height - 2 * request.border_size - gfx->height;
5853 else if (id == TOOL_CTRL_ID_NO)
5855 x = request.width - 2 * request.border_size - gfx->width;
5856 y = request.height - 2 * request.border_size - gfx->height;
5858 else if (id == TOOL_CTRL_ID_CONFIRM)
5860 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5861 y = request.height - 2 * request.border_size - gfx->height;
5863 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5865 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5867 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5868 y = request.height - 2 * request.border_size - gfx->height * 2;
5870 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5871 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5876 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5878 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5880 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5881 pos->size, &deco_bitmap, &deco_x, &deco_y);
5882 deco_xpos = (gfx->width - pos->size) / 2;
5883 deco_ypos = (gfx->height - pos->size) / 2;
5886 gi = CreateGadget(GDI_CUSTOM_ID, id,
5887 GDI_IMAGE_ID, graphic,
5888 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5891 GDI_WIDTH, gfx->width,
5892 GDI_HEIGHT, gfx->height,
5893 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5894 GDI_STATE, GD_BUTTON_UNPRESSED,
5895 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5896 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5897 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5898 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5899 GDI_DECORATION_SIZE, pos->size, pos->size,
5900 GDI_DECORATION_SHIFTING, 1, 1,
5901 GDI_DIRECT_DRAW, FALSE,
5902 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5903 GDI_EVENT_MASK, event_mask,
5904 GDI_CALLBACK_ACTION, HandleToolButtons,
5908 Fail("cannot create gadget");
5910 tool_gadget[id] = gi;
5914 void FreeToolButtons(void)
5918 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5919 FreeGadget(tool_gadget[i]);
5922 static void UnmapToolButtons(void)
5926 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5927 UnmapGadget(tool_gadget[i]);
5930 static void HandleToolButtons(struct GadgetInfo *gi)
5932 request_gadget_id = gi->custom_id;
5935 static struct Mapping_EM_to_RND_object
5938 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5939 boolean is_backside; // backside of moving element
5945 em_object_mapping_list[GAME_TILE_MAX + 1] =
5948 Zborder, FALSE, FALSE,
5952 Zplayer, FALSE, FALSE,
5961 Ztank, FALSE, FALSE,
5965 Zeater, FALSE, FALSE,
5969 Zdynamite, FALSE, FALSE,
5973 Zboom, FALSE, FALSE,
5978 Xchain, FALSE, FALSE,
5979 EL_DEFAULT, ACTION_EXPLODING, -1
5982 Xboom_bug, FALSE, FALSE,
5983 EL_BUG, ACTION_EXPLODING, -1
5986 Xboom_tank, FALSE, FALSE,
5987 EL_SPACESHIP, ACTION_EXPLODING, -1
5990 Xboom_android, FALSE, FALSE,
5991 EL_EMC_ANDROID, ACTION_OTHER, -1
5994 Xboom_1, FALSE, FALSE,
5995 EL_DEFAULT, ACTION_EXPLODING, -1
5998 Xboom_2, FALSE, FALSE,
5999 EL_DEFAULT, ACTION_EXPLODING, -1
6003 Xblank, TRUE, FALSE,
6008 Xsplash_e, FALSE, FALSE,
6009 EL_ACID_SPLASH_RIGHT, -1, -1
6012 Xsplash_w, FALSE, FALSE,
6013 EL_ACID_SPLASH_LEFT, -1, -1
6017 Xplant, TRUE, FALSE,
6018 EL_EMC_PLANT, -1, -1
6021 Yplant, FALSE, FALSE,
6022 EL_EMC_PLANT, -1, -1
6026 Xacid_1, TRUE, FALSE,
6030 Xacid_2, FALSE, FALSE,
6034 Xacid_3, FALSE, FALSE,
6038 Xacid_4, FALSE, FALSE,
6042 Xacid_5, FALSE, FALSE,
6046 Xacid_6, FALSE, FALSE,
6050 Xacid_7, FALSE, FALSE,
6054 Xacid_8, FALSE, FALSE,
6059 Xfake_acid_1, TRUE, FALSE,
6060 EL_EMC_FAKE_ACID, -1, -1
6063 Xfake_acid_2, FALSE, FALSE,
6064 EL_EMC_FAKE_ACID, -1, -1
6067 Xfake_acid_3, FALSE, FALSE,
6068 EL_EMC_FAKE_ACID, -1, -1
6071 Xfake_acid_4, FALSE, FALSE,
6072 EL_EMC_FAKE_ACID, -1, -1
6075 Xfake_acid_5, FALSE, FALSE,
6076 EL_EMC_FAKE_ACID, -1, -1
6079 Xfake_acid_6, FALSE, FALSE,
6080 EL_EMC_FAKE_ACID, -1, -1
6083 Xfake_acid_7, FALSE, FALSE,
6084 EL_EMC_FAKE_ACID, -1, -1
6087 Xfake_acid_8, FALSE, FALSE,
6088 EL_EMC_FAKE_ACID, -1, -1
6092 Xfake_acid_1_player, FALSE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_2_player, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_3_player, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_4_player, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_5_player, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_6_player, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_7_player, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_8_player, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6125 Xgrass, TRUE, FALSE,
6126 EL_EMC_GRASS, -1, -1
6129 Ygrass_nB, FALSE, FALSE,
6130 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6133 Ygrass_eB, FALSE, FALSE,
6134 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6137 Ygrass_sB, FALSE, FALSE,
6138 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6141 Ygrass_wB, FALSE, FALSE,
6142 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6150 Ydirt_nB, FALSE, FALSE,
6151 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6154 Ydirt_eB, FALSE, FALSE,
6155 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6158 Ydirt_sB, FALSE, FALSE,
6159 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6162 Ydirt_wB, FALSE, FALSE,
6163 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6167 Xandroid, TRUE, FALSE,
6168 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6171 Xandroid_1_n, FALSE, FALSE,
6172 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6175 Xandroid_2_n, FALSE, FALSE,
6176 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6179 Xandroid_1_e, FALSE, FALSE,
6180 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6183 Xandroid_2_e, FALSE, FALSE,
6184 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6187 Xandroid_1_w, FALSE, FALSE,
6188 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6191 Xandroid_2_w, FALSE, FALSE,
6192 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6195 Xandroid_1_s, FALSE, FALSE,
6196 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6199 Xandroid_2_s, FALSE, FALSE,
6200 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6203 Yandroid_n, FALSE, FALSE,
6204 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6207 Yandroid_nB, FALSE, TRUE,
6208 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6211 Yandroid_ne, FALSE, FALSE,
6212 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6215 Yandroid_neB, FALSE, TRUE,
6216 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6219 Yandroid_e, FALSE, FALSE,
6220 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6223 Yandroid_eB, FALSE, TRUE,
6224 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6227 Yandroid_se, FALSE, FALSE,
6228 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6231 Yandroid_seB, FALSE, TRUE,
6232 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6235 Yandroid_s, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6239 Yandroid_sB, FALSE, TRUE,
6240 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6243 Yandroid_sw, FALSE, FALSE,
6244 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6247 Yandroid_swB, FALSE, TRUE,
6248 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6251 Yandroid_w, FALSE, FALSE,
6252 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6255 Yandroid_wB, FALSE, TRUE,
6256 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6259 Yandroid_nw, FALSE, FALSE,
6260 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6263 Yandroid_nwB, FALSE, TRUE,
6264 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6268 Xeater_n, TRUE, FALSE,
6269 EL_YAMYAM_UP, -1, -1
6272 Xeater_e, TRUE, FALSE,
6273 EL_YAMYAM_RIGHT, -1, -1
6276 Xeater_w, TRUE, FALSE,
6277 EL_YAMYAM_LEFT, -1, -1
6280 Xeater_s, TRUE, FALSE,
6281 EL_YAMYAM_DOWN, -1, -1
6284 Yeater_n, FALSE, FALSE,
6285 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6288 Yeater_nB, FALSE, TRUE,
6289 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6292 Yeater_e, FALSE, FALSE,
6293 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6296 Yeater_eB, FALSE, TRUE,
6297 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6300 Yeater_s, FALSE, FALSE,
6301 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6304 Yeater_sB, FALSE, TRUE,
6305 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6308 Yeater_w, FALSE, FALSE,
6309 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6312 Yeater_wB, FALSE, TRUE,
6313 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6316 Yeater_stone, FALSE, FALSE,
6317 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6320 Yeater_spring, FALSE, FALSE,
6321 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6325 Xalien, TRUE, FALSE,
6329 Xalien_pause, FALSE, FALSE,
6333 Yalien_n, FALSE, FALSE,
6334 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6337 Yalien_nB, FALSE, TRUE,
6338 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6341 Yalien_e, FALSE, FALSE,
6342 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6345 Yalien_eB, FALSE, TRUE,
6346 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6349 Yalien_s, FALSE, FALSE,
6350 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6353 Yalien_sB, FALSE, TRUE,
6354 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6357 Yalien_w, FALSE, FALSE,
6358 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6361 Yalien_wB, FALSE, TRUE,
6362 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6365 Yalien_stone, FALSE, FALSE,
6366 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6369 Yalien_spring, FALSE, FALSE,
6370 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6374 Xbug_1_n, TRUE, FALSE,
6378 Xbug_1_e, TRUE, FALSE,
6379 EL_BUG_RIGHT, -1, -1
6382 Xbug_1_s, TRUE, FALSE,
6386 Xbug_1_w, TRUE, FALSE,
6390 Xbug_2_n, FALSE, FALSE,
6394 Xbug_2_e, FALSE, FALSE,
6395 EL_BUG_RIGHT, -1, -1
6398 Xbug_2_s, FALSE, FALSE,
6402 Xbug_2_w, FALSE, FALSE,
6406 Ybug_n, FALSE, FALSE,
6407 EL_BUG, ACTION_MOVING, MV_BIT_UP
6410 Ybug_nB, FALSE, TRUE,
6411 EL_BUG, ACTION_MOVING, MV_BIT_UP
6414 Ybug_e, FALSE, FALSE,
6415 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6418 Ybug_eB, FALSE, TRUE,
6419 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6422 Ybug_s, FALSE, FALSE,
6423 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6426 Ybug_sB, FALSE, TRUE,
6427 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6430 Ybug_w, FALSE, FALSE,
6431 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6434 Ybug_wB, FALSE, TRUE,
6435 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6438 Ybug_w_n, FALSE, FALSE,
6439 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6442 Ybug_n_e, FALSE, FALSE,
6443 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6446 Ybug_e_s, FALSE, FALSE,
6447 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6450 Ybug_s_w, FALSE, FALSE,
6451 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6454 Ybug_e_n, FALSE, FALSE,
6455 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6458 Ybug_s_e, FALSE, FALSE,
6459 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6462 Ybug_w_s, FALSE, FALSE,
6463 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6466 Ybug_n_w, FALSE, FALSE,
6467 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6470 Ybug_stone, FALSE, FALSE,
6471 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6474 Ybug_spring, FALSE, FALSE,
6475 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6479 Xtank_1_n, TRUE, FALSE,
6480 EL_SPACESHIP_UP, -1, -1
6483 Xtank_1_e, TRUE, FALSE,
6484 EL_SPACESHIP_RIGHT, -1, -1
6487 Xtank_1_s, TRUE, FALSE,
6488 EL_SPACESHIP_DOWN, -1, -1
6491 Xtank_1_w, TRUE, FALSE,
6492 EL_SPACESHIP_LEFT, -1, -1
6495 Xtank_2_n, FALSE, FALSE,
6496 EL_SPACESHIP_UP, -1, -1
6499 Xtank_2_e, FALSE, FALSE,
6500 EL_SPACESHIP_RIGHT, -1, -1
6503 Xtank_2_s, FALSE, FALSE,
6504 EL_SPACESHIP_DOWN, -1, -1
6507 Xtank_2_w, FALSE, FALSE,
6508 EL_SPACESHIP_LEFT, -1, -1
6511 Ytank_n, FALSE, FALSE,
6512 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6515 Ytank_nB, FALSE, TRUE,
6516 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6519 Ytank_e, FALSE, FALSE,
6520 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6523 Ytank_eB, FALSE, TRUE,
6524 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6527 Ytank_s, FALSE, FALSE,
6528 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6531 Ytank_sB, FALSE, TRUE,
6532 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6535 Ytank_w, FALSE, FALSE,
6536 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6539 Ytank_wB, FALSE, TRUE,
6540 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6543 Ytank_w_n, FALSE, FALSE,
6544 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6547 Ytank_n_e, FALSE, FALSE,
6548 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6551 Ytank_e_s, FALSE, FALSE,
6552 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6555 Ytank_s_w, FALSE, FALSE,
6556 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6559 Ytank_e_n, FALSE, FALSE,
6560 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6563 Ytank_s_e, FALSE, FALSE,
6564 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6567 Ytank_w_s, FALSE, FALSE,
6568 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6571 Ytank_n_w, FALSE, FALSE,
6572 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6575 Ytank_stone, FALSE, FALSE,
6576 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6579 Ytank_spring, FALSE, FALSE,
6580 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6584 Xemerald, TRUE, FALSE,
6588 Xemerald_pause, FALSE, FALSE,
6592 Xemerald_fall, FALSE, FALSE,
6596 Xemerald_shine, FALSE, FALSE,
6597 EL_EMERALD, ACTION_TWINKLING, -1
6600 Yemerald_s, FALSE, FALSE,
6601 EL_EMERALD, ACTION_FALLING, -1
6604 Yemerald_sB, FALSE, TRUE,
6605 EL_EMERALD, ACTION_FALLING, -1
6608 Yemerald_e, FALSE, FALSE,
6609 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6612 Yemerald_eB, FALSE, TRUE,
6613 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6616 Yemerald_w, FALSE, FALSE,
6617 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6620 Yemerald_wB, FALSE, TRUE,
6621 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6624 Yemerald_blank, FALSE, FALSE,
6625 EL_EMERALD, ACTION_COLLECTING, -1
6629 Xdiamond, TRUE, FALSE,
6633 Xdiamond_pause, FALSE, FALSE,
6637 Xdiamond_fall, FALSE, FALSE,
6641 Xdiamond_shine, FALSE, FALSE,
6642 EL_DIAMOND, ACTION_TWINKLING, -1
6645 Ydiamond_s, FALSE, FALSE,
6646 EL_DIAMOND, ACTION_FALLING, -1
6649 Ydiamond_sB, FALSE, TRUE,
6650 EL_DIAMOND, ACTION_FALLING, -1
6653 Ydiamond_e, FALSE, FALSE,
6654 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6657 Ydiamond_eB, FALSE, TRUE,
6658 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6661 Ydiamond_w, FALSE, FALSE,
6662 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6665 Ydiamond_wB, FALSE, TRUE,
6666 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6669 Ydiamond_blank, FALSE, FALSE,
6670 EL_DIAMOND, ACTION_COLLECTING, -1
6673 Ydiamond_stone, FALSE, FALSE,
6674 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6678 Xstone, TRUE, FALSE,
6682 Xstone_pause, FALSE, FALSE,
6686 Xstone_fall, FALSE, FALSE,
6690 Ystone_s, FALSE, FALSE,
6691 EL_ROCK, ACTION_FALLING, -1
6694 Ystone_sB, FALSE, TRUE,
6695 EL_ROCK, ACTION_FALLING, -1
6698 Ystone_e, FALSE, FALSE,
6699 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6702 Ystone_eB, FALSE, TRUE,
6703 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6706 Ystone_w, FALSE, FALSE,
6707 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6710 Ystone_wB, FALSE, TRUE,
6711 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6719 Xbomb_pause, FALSE, FALSE,
6723 Xbomb_fall, FALSE, FALSE,
6727 Ybomb_s, FALSE, FALSE,
6728 EL_BOMB, ACTION_FALLING, -1
6731 Ybomb_sB, FALSE, TRUE,
6732 EL_BOMB, ACTION_FALLING, -1
6735 Ybomb_e, FALSE, FALSE,
6736 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6739 Ybomb_eB, FALSE, TRUE,
6740 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6743 Ybomb_w, FALSE, FALSE,
6744 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6747 Ybomb_wB, FALSE, TRUE,
6748 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6751 Ybomb_blank, FALSE, FALSE,
6752 EL_BOMB, ACTION_ACTIVATING, -1
6760 Xnut_pause, FALSE, FALSE,
6764 Xnut_fall, FALSE, FALSE,
6768 Ynut_s, FALSE, FALSE,
6769 EL_NUT, ACTION_FALLING, -1
6772 Ynut_sB, FALSE, TRUE,
6773 EL_NUT, ACTION_FALLING, -1
6776 Ynut_e, FALSE, FALSE,
6777 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6780 Ynut_eB, FALSE, TRUE,
6781 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6784 Ynut_w, FALSE, FALSE,
6785 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6788 Ynut_wB, FALSE, TRUE,
6789 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6792 Ynut_stone, FALSE, FALSE,
6793 EL_NUT, ACTION_BREAKING, -1
6797 Xspring, TRUE, FALSE,
6801 Xspring_pause, FALSE, FALSE,
6805 Xspring_e, TRUE, FALSE,
6806 EL_SPRING_RIGHT, -1, -1
6809 Xspring_w, TRUE, FALSE,
6810 EL_SPRING_LEFT, -1, -1
6813 Xspring_fall, FALSE, FALSE,
6817 Yspring_s, FALSE, FALSE,
6818 EL_SPRING, ACTION_FALLING, -1
6821 Yspring_sB, FALSE, TRUE,
6822 EL_SPRING, ACTION_FALLING, -1
6825 Yspring_e, FALSE, FALSE,
6826 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6829 Yspring_eB, FALSE, TRUE,
6830 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6833 Yspring_w, FALSE, FALSE,
6834 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6837 Yspring_wB, FALSE, TRUE,
6838 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6841 Yspring_alien_e, FALSE, FALSE,
6842 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6845 Yspring_alien_eB, FALSE, TRUE,
6846 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6849 Yspring_alien_w, FALSE, FALSE,
6850 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6853 Yspring_alien_wB, FALSE, TRUE,
6854 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6858 Xpush_emerald_e, FALSE, FALSE,
6859 EL_EMERALD, -1, MV_BIT_RIGHT
6862 Xpush_emerald_w, FALSE, FALSE,
6863 EL_EMERALD, -1, MV_BIT_LEFT
6866 Xpush_diamond_e, FALSE, FALSE,
6867 EL_DIAMOND, -1, MV_BIT_RIGHT
6870 Xpush_diamond_w, FALSE, FALSE,
6871 EL_DIAMOND, -1, MV_BIT_LEFT
6874 Xpush_stone_e, FALSE, FALSE,
6875 EL_ROCK, -1, MV_BIT_RIGHT
6878 Xpush_stone_w, FALSE, FALSE,
6879 EL_ROCK, -1, MV_BIT_LEFT
6882 Xpush_bomb_e, FALSE, FALSE,
6883 EL_BOMB, -1, MV_BIT_RIGHT
6886 Xpush_bomb_w, FALSE, FALSE,
6887 EL_BOMB, -1, MV_BIT_LEFT
6890 Xpush_nut_e, FALSE, FALSE,
6891 EL_NUT, -1, MV_BIT_RIGHT
6894 Xpush_nut_w, FALSE, FALSE,
6895 EL_NUT, -1, MV_BIT_LEFT
6898 Xpush_spring_e, FALSE, FALSE,
6899 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6902 Xpush_spring_w, FALSE, FALSE,
6903 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6907 Xdynamite, TRUE, FALSE,
6908 EL_EM_DYNAMITE, -1, -1
6911 Ydynamite_blank, FALSE, FALSE,
6912 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6915 Xdynamite_1, TRUE, FALSE,
6916 EL_EM_DYNAMITE_ACTIVE, -1, -1
6919 Xdynamite_2, FALSE, FALSE,
6920 EL_EM_DYNAMITE_ACTIVE, -1, -1
6923 Xdynamite_3, FALSE, FALSE,
6924 EL_EM_DYNAMITE_ACTIVE, -1, -1
6927 Xdynamite_4, FALSE, FALSE,
6928 EL_EM_DYNAMITE_ACTIVE, -1, -1
6932 Xkey_1, TRUE, FALSE,
6936 Xkey_2, TRUE, FALSE,
6940 Xkey_3, TRUE, FALSE,
6944 Xkey_4, TRUE, FALSE,
6948 Xkey_5, TRUE, FALSE,
6949 EL_EMC_KEY_5, -1, -1
6952 Xkey_6, TRUE, FALSE,
6953 EL_EMC_KEY_6, -1, -1
6956 Xkey_7, TRUE, FALSE,
6957 EL_EMC_KEY_7, -1, -1
6960 Xkey_8, TRUE, FALSE,
6961 EL_EMC_KEY_8, -1, -1
6965 Xdoor_1, TRUE, FALSE,
6966 EL_EM_GATE_1, -1, -1
6969 Xdoor_2, TRUE, FALSE,
6970 EL_EM_GATE_2, -1, -1
6973 Xdoor_3, TRUE, FALSE,
6974 EL_EM_GATE_3, -1, -1
6977 Xdoor_4, TRUE, FALSE,
6978 EL_EM_GATE_4, -1, -1
6981 Xdoor_5, TRUE, FALSE,
6982 EL_EMC_GATE_5, -1, -1
6985 Xdoor_6, TRUE, FALSE,
6986 EL_EMC_GATE_6, -1, -1
6989 Xdoor_7, TRUE, FALSE,
6990 EL_EMC_GATE_7, -1, -1
6993 Xdoor_8, TRUE, FALSE,
6994 EL_EMC_GATE_8, -1, -1
6998 Xfake_door_1, TRUE, FALSE,
6999 EL_EM_GATE_1_GRAY, -1, -1
7002 Xfake_door_2, TRUE, FALSE,
7003 EL_EM_GATE_2_GRAY, -1, -1
7006 Xfake_door_3, TRUE, FALSE,
7007 EL_EM_GATE_3_GRAY, -1, -1
7010 Xfake_door_4, TRUE, FALSE,
7011 EL_EM_GATE_4_GRAY, -1, -1
7014 Xfake_door_5, TRUE, FALSE,
7015 EL_EMC_GATE_5_GRAY, -1, -1
7018 Xfake_door_6, TRUE, FALSE,
7019 EL_EMC_GATE_6_GRAY, -1, -1
7022 Xfake_door_7, TRUE, FALSE,
7023 EL_EMC_GATE_7_GRAY, -1, -1
7026 Xfake_door_8, TRUE, FALSE,
7027 EL_EMC_GATE_8_GRAY, -1, -1
7031 Xballoon, TRUE, FALSE,
7035 Yballoon_n, FALSE, FALSE,
7036 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7039 Yballoon_nB, FALSE, TRUE,
7040 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7043 Yballoon_e, FALSE, FALSE,
7044 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7047 Yballoon_eB, FALSE, TRUE,
7048 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7051 Yballoon_s, FALSE, FALSE,
7052 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7055 Yballoon_sB, FALSE, TRUE,
7056 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7059 Yballoon_w, FALSE, FALSE,
7060 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7063 Yballoon_wB, FALSE, TRUE,
7064 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7068 Xball_1, TRUE, FALSE,
7069 EL_EMC_MAGIC_BALL, -1, -1
7072 Yball_1, FALSE, FALSE,
7073 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7076 Xball_2, FALSE, FALSE,
7077 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7080 Yball_2, FALSE, FALSE,
7081 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7084 Yball_blank, FALSE, FALSE,
7085 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7089 Xamoeba_1, TRUE, FALSE,
7090 EL_AMOEBA_DRY, ACTION_OTHER, -1
7093 Xamoeba_2, FALSE, FALSE,
7094 EL_AMOEBA_DRY, ACTION_OTHER, -1
7097 Xamoeba_3, FALSE, FALSE,
7098 EL_AMOEBA_DRY, ACTION_OTHER, -1
7101 Xamoeba_4, FALSE, FALSE,
7102 EL_AMOEBA_DRY, ACTION_OTHER, -1
7105 Xamoeba_5, TRUE, FALSE,
7106 EL_AMOEBA_WET, ACTION_OTHER, -1
7109 Xamoeba_6, FALSE, FALSE,
7110 EL_AMOEBA_WET, ACTION_OTHER, -1
7113 Xamoeba_7, FALSE, FALSE,
7114 EL_AMOEBA_WET, ACTION_OTHER, -1
7117 Xamoeba_8, FALSE, FALSE,
7118 EL_AMOEBA_WET, ACTION_OTHER, -1
7123 EL_AMOEBA_DROP, ACTION_GROWING, -1
7126 Xdrip_fall, FALSE, FALSE,
7127 EL_AMOEBA_DROP, -1, -1
7130 Xdrip_stretch, FALSE, FALSE,
7131 EL_AMOEBA_DROP, ACTION_FALLING, -1
7134 Xdrip_stretchB, FALSE, TRUE,
7135 EL_AMOEBA_DROP, ACTION_FALLING, -1
7138 Ydrip_1_s, FALSE, FALSE,
7139 EL_AMOEBA_DROP, ACTION_FALLING, -1
7142 Ydrip_1_sB, FALSE, TRUE,
7143 EL_AMOEBA_DROP, ACTION_FALLING, -1
7146 Ydrip_2_s, FALSE, FALSE,
7147 EL_AMOEBA_DROP, ACTION_FALLING, -1
7150 Ydrip_2_sB, FALSE, TRUE,
7151 EL_AMOEBA_DROP, ACTION_FALLING, -1
7155 Xwonderwall, TRUE, FALSE,
7156 EL_MAGIC_WALL, -1, -1
7159 Ywonderwall, FALSE, FALSE,
7160 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7164 Xwheel, TRUE, FALSE,
7165 EL_ROBOT_WHEEL, -1, -1
7168 Ywheel, FALSE, FALSE,
7169 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7173 Xswitch, TRUE, FALSE,
7174 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7177 Yswitch, FALSE, FALSE,
7178 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7182 Xbumper, TRUE, FALSE,
7183 EL_EMC_SPRING_BUMPER, -1, -1
7186 Ybumper, FALSE, FALSE,
7187 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7191 Xacid_nw, TRUE, FALSE,
7192 EL_ACID_POOL_TOPLEFT, -1, -1
7195 Xacid_ne, TRUE, FALSE,
7196 EL_ACID_POOL_TOPRIGHT, -1, -1
7199 Xacid_sw, TRUE, FALSE,
7200 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7203 Xacid_s, TRUE, FALSE,
7204 EL_ACID_POOL_BOTTOM, -1, -1
7207 Xacid_se, TRUE, FALSE,
7208 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7212 Xfake_blank, TRUE, FALSE,
7213 EL_INVISIBLE_WALL, -1, -1
7216 Yfake_blank, FALSE, FALSE,
7217 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7221 Xfake_grass, TRUE, FALSE,
7222 EL_EMC_FAKE_GRASS, -1, -1
7225 Yfake_grass, FALSE, FALSE,
7226 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7230 Xfake_amoeba, TRUE, FALSE,
7231 EL_EMC_DRIPPER, -1, -1
7234 Yfake_amoeba, FALSE, FALSE,
7235 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7239 Xlenses, TRUE, FALSE,
7240 EL_EMC_LENSES, -1, -1
7244 Xmagnify, TRUE, FALSE,
7245 EL_EMC_MAGNIFIER, -1, -1
7250 EL_QUICKSAND_EMPTY, -1, -1
7253 Xsand_stone, TRUE, FALSE,
7254 EL_QUICKSAND_FULL, -1, -1
7257 Xsand_stonein_1, FALSE, TRUE,
7258 EL_ROCK, ACTION_FILLING, -1
7261 Xsand_stonein_2, FALSE, TRUE,
7262 EL_ROCK, ACTION_FILLING, -1
7265 Xsand_stonein_3, FALSE, TRUE,
7266 EL_ROCK, ACTION_FILLING, -1
7269 Xsand_stonein_4, FALSE, TRUE,
7270 EL_ROCK, ACTION_FILLING, -1
7273 Xsand_sandstone_1, FALSE, FALSE,
7274 EL_QUICKSAND_FILLING, -1, -1
7277 Xsand_sandstone_2, FALSE, FALSE,
7278 EL_QUICKSAND_FILLING, -1, -1
7281 Xsand_sandstone_3, FALSE, FALSE,
7282 EL_QUICKSAND_FILLING, -1, -1
7285 Xsand_sandstone_4, FALSE, FALSE,
7286 EL_QUICKSAND_FILLING, -1, -1
7289 Xsand_stonesand_1, FALSE, FALSE,
7290 EL_QUICKSAND_EMPTYING, -1, -1
7293 Xsand_stonesand_2, FALSE, FALSE,
7294 EL_QUICKSAND_EMPTYING, -1, -1
7297 Xsand_stonesand_3, FALSE, FALSE,
7298 EL_QUICKSAND_EMPTYING, -1, -1
7301 Xsand_stonesand_4, FALSE, FALSE,
7302 EL_QUICKSAND_EMPTYING, -1, -1
7305 Xsand_stoneout_1, FALSE, FALSE,
7306 EL_ROCK, ACTION_EMPTYING, -1
7309 Xsand_stoneout_2, FALSE, FALSE,
7310 EL_ROCK, ACTION_EMPTYING, -1
7313 Xsand_stonesand_quickout_1, FALSE, FALSE,
7314 EL_QUICKSAND_EMPTYING, -1, -1
7317 Xsand_stonesand_quickout_2, FALSE, FALSE,
7318 EL_QUICKSAND_EMPTYING, -1, -1
7322 Xslide_ns, TRUE, FALSE,
7323 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7326 Yslide_ns_blank, FALSE, FALSE,
7327 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7330 Xslide_ew, TRUE, FALSE,
7331 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7334 Yslide_ew_blank, FALSE, FALSE,
7335 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7339 Xwind_n, TRUE, FALSE,
7340 EL_BALLOON_SWITCH_UP, -1, -1
7343 Xwind_e, TRUE, FALSE,
7344 EL_BALLOON_SWITCH_RIGHT, -1, -1
7347 Xwind_s, TRUE, FALSE,
7348 EL_BALLOON_SWITCH_DOWN, -1, -1
7351 Xwind_w, TRUE, FALSE,
7352 EL_BALLOON_SWITCH_LEFT, -1, -1
7355 Xwind_any, TRUE, FALSE,
7356 EL_BALLOON_SWITCH_ANY, -1, -1
7359 Xwind_stop, TRUE, FALSE,
7360 EL_BALLOON_SWITCH_NONE, -1, -1
7365 EL_EM_EXIT_CLOSED, -1, -1
7368 Xexit_1, TRUE, FALSE,
7369 EL_EM_EXIT_OPEN, -1, -1
7372 Xexit_2, FALSE, FALSE,
7373 EL_EM_EXIT_OPEN, -1, -1
7376 Xexit_3, FALSE, FALSE,
7377 EL_EM_EXIT_OPEN, -1, -1
7381 Xpause, FALSE, FALSE,
7386 Xwall_1, TRUE, FALSE,
7390 Xwall_2, TRUE, FALSE,
7391 EL_EMC_WALL_14, -1, -1
7394 Xwall_3, TRUE, FALSE,
7395 EL_EMC_WALL_15, -1, -1
7398 Xwall_4, TRUE, FALSE,
7399 EL_EMC_WALL_16, -1, -1
7403 Xroundwall_1, TRUE, FALSE,
7404 EL_WALL_SLIPPERY, -1, -1
7407 Xroundwall_2, TRUE, FALSE,
7408 EL_EMC_WALL_SLIPPERY_2, -1, -1
7411 Xroundwall_3, TRUE, FALSE,
7412 EL_EMC_WALL_SLIPPERY_3, -1, -1
7415 Xroundwall_4, TRUE, FALSE,
7416 EL_EMC_WALL_SLIPPERY_4, -1, -1
7420 Xsteel_1, TRUE, FALSE,
7421 EL_STEELWALL, -1, -1
7424 Xsteel_2, TRUE, FALSE,
7425 EL_EMC_STEELWALL_2, -1, -1
7428 Xsteel_3, TRUE, FALSE,
7429 EL_EMC_STEELWALL_3, -1, -1
7432 Xsteel_4, TRUE, FALSE,
7433 EL_EMC_STEELWALL_4, -1, -1
7437 Xdecor_1, TRUE, FALSE,
7438 EL_EMC_WALL_8, -1, -1
7441 Xdecor_2, TRUE, FALSE,
7442 EL_EMC_WALL_6, -1, -1
7445 Xdecor_3, TRUE, FALSE,
7446 EL_EMC_WALL_4, -1, -1
7449 Xdecor_4, TRUE, FALSE,
7450 EL_EMC_WALL_7, -1, -1
7453 Xdecor_5, TRUE, FALSE,
7454 EL_EMC_WALL_5, -1, -1
7457 Xdecor_6, TRUE, FALSE,
7458 EL_EMC_WALL_9, -1, -1
7461 Xdecor_7, TRUE, FALSE,
7462 EL_EMC_WALL_10, -1, -1
7465 Xdecor_8, TRUE, FALSE,
7466 EL_EMC_WALL_1, -1, -1
7469 Xdecor_9, TRUE, FALSE,
7470 EL_EMC_WALL_2, -1, -1
7473 Xdecor_10, TRUE, FALSE,
7474 EL_EMC_WALL_3, -1, -1
7477 Xdecor_11, TRUE, FALSE,
7478 EL_EMC_WALL_11, -1, -1
7481 Xdecor_12, TRUE, FALSE,
7482 EL_EMC_WALL_12, -1, -1
7486 Xalpha_0, TRUE, FALSE,
7487 EL_CHAR('0'), -1, -1
7490 Xalpha_1, TRUE, FALSE,
7491 EL_CHAR('1'), -1, -1
7494 Xalpha_2, TRUE, FALSE,
7495 EL_CHAR('2'), -1, -1
7498 Xalpha_3, TRUE, FALSE,
7499 EL_CHAR('3'), -1, -1
7502 Xalpha_4, TRUE, FALSE,
7503 EL_CHAR('4'), -1, -1
7506 Xalpha_5, TRUE, FALSE,
7507 EL_CHAR('5'), -1, -1
7510 Xalpha_6, TRUE, FALSE,
7511 EL_CHAR('6'), -1, -1
7514 Xalpha_7, TRUE, FALSE,
7515 EL_CHAR('7'), -1, -1
7518 Xalpha_8, TRUE, FALSE,
7519 EL_CHAR('8'), -1, -1
7522 Xalpha_9, TRUE, FALSE,
7523 EL_CHAR('9'), -1, -1
7526 Xalpha_excla, TRUE, FALSE,
7527 EL_CHAR('!'), -1, -1
7530 Xalpha_apost, TRUE, FALSE,
7531 EL_CHAR('\''), -1, -1
7534 Xalpha_comma, TRUE, FALSE,
7535 EL_CHAR(','), -1, -1
7538 Xalpha_minus, TRUE, FALSE,
7539 EL_CHAR('-'), -1, -1
7542 Xalpha_perio, TRUE, FALSE,
7543 EL_CHAR('.'), -1, -1
7546 Xalpha_colon, TRUE, FALSE,
7547 EL_CHAR(':'), -1, -1
7550 Xalpha_quest, TRUE, FALSE,
7551 EL_CHAR('?'), -1, -1
7554 Xalpha_a, TRUE, FALSE,
7555 EL_CHAR('A'), -1, -1
7558 Xalpha_b, TRUE, FALSE,
7559 EL_CHAR('B'), -1, -1
7562 Xalpha_c, TRUE, FALSE,
7563 EL_CHAR('C'), -1, -1
7566 Xalpha_d, TRUE, FALSE,
7567 EL_CHAR('D'), -1, -1
7570 Xalpha_e, TRUE, FALSE,
7571 EL_CHAR('E'), -1, -1
7574 Xalpha_f, TRUE, FALSE,
7575 EL_CHAR('F'), -1, -1
7578 Xalpha_g, TRUE, FALSE,
7579 EL_CHAR('G'), -1, -1
7582 Xalpha_h, TRUE, FALSE,
7583 EL_CHAR('H'), -1, -1
7586 Xalpha_i, TRUE, FALSE,
7587 EL_CHAR('I'), -1, -1
7590 Xalpha_j, TRUE, FALSE,
7591 EL_CHAR('J'), -1, -1
7594 Xalpha_k, TRUE, FALSE,
7595 EL_CHAR('K'), -1, -1
7598 Xalpha_l, TRUE, FALSE,
7599 EL_CHAR('L'), -1, -1
7602 Xalpha_m, TRUE, FALSE,
7603 EL_CHAR('M'), -1, -1
7606 Xalpha_n, TRUE, FALSE,
7607 EL_CHAR('N'), -1, -1
7610 Xalpha_o, TRUE, FALSE,
7611 EL_CHAR('O'), -1, -1
7614 Xalpha_p, TRUE, FALSE,
7615 EL_CHAR('P'), -1, -1
7618 Xalpha_q, TRUE, FALSE,
7619 EL_CHAR('Q'), -1, -1
7622 Xalpha_r, TRUE, FALSE,
7623 EL_CHAR('R'), -1, -1
7626 Xalpha_s, TRUE, FALSE,
7627 EL_CHAR('S'), -1, -1
7630 Xalpha_t, TRUE, FALSE,
7631 EL_CHAR('T'), -1, -1
7634 Xalpha_u, TRUE, FALSE,
7635 EL_CHAR('U'), -1, -1
7638 Xalpha_v, TRUE, FALSE,
7639 EL_CHAR('V'), -1, -1
7642 Xalpha_w, TRUE, FALSE,
7643 EL_CHAR('W'), -1, -1
7646 Xalpha_x, TRUE, FALSE,
7647 EL_CHAR('X'), -1, -1
7650 Xalpha_y, TRUE, FALSE,
7651 EL_CHAR('Y'), -1, -1
7654 Xalpha_z, TRUE, FALSE,
7655 EL_CHAR('Z'), -1, -1
7658 Xalpha_arrow_e, TRUE, FALSE,
7659 EL_CHAR('>'), -1, -1
7662 Xalpha_arrow_w, TRUE, FALSE,
7663 EL_CHAR('<'), -1, -1
7666 Xalpha_copyr, TRUE, FALSE,
7667 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7671 Ykey_1_blank, FALSE, FALSE,
7672 EL_EM_KEY_1, ACTION_COLLECTING, -1
7675 Ykey_2_blank, FALSE, FALSE,
7676 EL_EM_KEY_2, ACTION_COLLECTING, -1
7679 Ykey_3_blank, FALSE, FALSE,
7680 EL_EM_KEY_3, ACTION_COLLECTING, -1
7683 Ykey_4_blank, FALSE, FALSE,
7684 EL_EM_KEY_4, ACTION_COLLECTING, -1
7687 Ykey_5_blank, FALSE, FALSE,
7688 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7691 Ykey_6_blank, FALSE, FALSE,
7692 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7695 Ykey_7_blank, FALSE, FALSE,
7696 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7699 Ykey_8_blank, FALSE, FALSE,
7700 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7703 Ylenses_blank, FALSE, FALSE,
7704 EL_EMC_LENSES, ACTION_COLLECTING, -1
7707 Ymagnify_blank, FALSE, FALSE,
7708 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7711 Ygrass_blank, FALSE, FALSE,
7712 EL_EMC_GRASS, ACTION_SNAPPING, -1
7715 Ydirt_blank, FALSE, FALSE,
7716 EL_SAND, ACTION_SNAPPING, -1
7725 static struct Mapping_EM_to_RND_player
7734 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7738 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7742 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7746 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7750 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7754 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7758 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7762 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7766 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7770 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7774 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7778 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7782 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7786 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7790 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7794 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7798 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7802 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7806 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7810 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7814 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7818 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7822 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7826 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7830 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7834 EL_PLAYER_1, ACTION_DEFAULT, -1,
7838 EL_PLAYER_2, ACTION_DEFAULT, -1,
7842 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7846 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7850 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7854 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7858 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7862 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7866 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7870 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7874 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7878 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7882 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7886 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7890 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7894 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7898 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7902 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7906 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7910 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7914 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7918 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7922 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7926 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7930 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7934 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7938 EL_PLAYER_3, ACTION_DEFAULT, -1,
7942 EL_PLAYER_4, ACTION_DEFAULT, -1,
7951 int map_element_RND_to_EM_cave(int element_rnd)
7953 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7954 static boolean mapping_initialized = FALSE;
7956 if (!mapping_initialized)
7960 // return "Xalpha_quest" for all undefined elements in mapping array
7961 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7962 mapping_RND_to_EM[i] = Xalpha_quest;
7964 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7965 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7966 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7967 em_object_mapping_list[i].element_em;
7969 mapping_initialized = TRUE;
7972 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7974 Warn("invalid RND level element %d", element_rnd);
7979 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7982 int map_element_EM_to_RND_cave(int element_em_cave)
7984 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7985 static boolean mapping_initialized = FALSE;
7987 if (!mapping_initialized)
7991 // return "EL_UNKNOWN" for all undefined elements in mapping array
7992 for (i = 0; i < GAME_TILE_MAX; i++)
7993 mapping_EM_to_RND[i] = EL_UNKNOWN;
7995 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7996 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7997 em_object_mapping_list[i].element_rnd;
7999 mapping_initialized = TRUE;
8002 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8004 Warn("invalid EM cave element %d", element_em_cave);
8009 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8012 int map_element_EM_to_RND_game(int element_em_game)
8014 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8015 static boolean mapping_initialized = FALSE;
8017 if (!mapping_initialized)
8021 // return "EL_UNKNOWN" for all undefined elements in mapping array
8022 for (i = 0; i < GAME_TILE_MAX; i++)
8023 mapping_EM_to_RND[i] = EL_UNKNOWN;
8025 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8026 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8027 em_object_mapping_list[i].element_rnd;
8029 mapping_initialized = TRUE;
8032 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8034 Warn("invalid EM game element %d", element_em_game);
8039 return mapping_EM_to_RND[element_em_game];
8042 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8044 struct LevelInfo_EM *level_em = level->native_em_level;
8045 struct CAVE *cav = level_em->cav;
8048 for (i = 0; i < GAME_TILE_MAX; i++)
8049 cav->android_array[i] = Cblank;
8051 for (i = 0; i < level->num_android_clone_elements; i++)
8053 int element_rnd = level->android_clone_element[i];
8054 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8056 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8057 if (em_object_mapping_list[j].element_rnd == element_rnd)
8058 cav->android_array[em_object_mapping_list[j].element_em] =
8063 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8065 struct LevelInfo_EM *level_em = level->native_em_level;
8066 struct CAVE *cav = level_em->cav;
8069 level->num_android_clone_elements = 0;
8071 for (i = 0; i < GAME_TILE_MAX; i++)
8073 int element_em_cave = cav->android_array[i];
8075 boolean element_found = FALSE;
8077 if (element_em_cave == Cblank)
8080 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8082 for (j = 0; j < level->num_android_clone_elements; j++)
8083 if (level->android_clone_element[j] == element_rnd)
8084 element_found = TRUE;
8088 level->android_clone_element[level->num_android_clone_elements++] =
8091 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8096 if (level->num_android_clone_elements == 0)
8098 level->num_android_clone_elements = 1;
8099 level->android_clone_element[0] = EL_EMPTY;
8103 int map_direction_RND_to_EM(int direction)
8105 return (direction == MV_UP ? 0 :
8106 direction == MV_RIGHT ? 1 :
8107 direction == MV_DOWN ? 2 :
8108 direction == MV_LEFT ? 3 :
8112 int map_direction_EM_to_RND(int direction)
8114 return (direction == 0 ? MV_UP :
8115 direction == 1 ? MV_RIGHT :
8116 direction == 2 ? MV_DOWN :
8117 direction == 3 ? MV_LEFT :
8121 int map_element_RND_to_SP(int element_rnd)
8123 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8125 if (element_rnd >= EL_SP_START &&
8126 element_rnd <= EL_SP_END)
8127 element_sp = element_rnd - EL_SP_START;
8128 else if (element_rnd == EL_EMPTY_SPACE)
8130 else if (element_rnd == EL_INVISIBLE_WALL)
8136 int map_element_SP_to_RND(int element_sp)
8138 int element_rnd = EL_UNKNOWN;
8140 if (element_sp >= 0x00 &&
8142 element_rnd = EL_SP_START + element_sp;
8143 else if (element_sp == 0x28)
8144 element_rnd = EL_INVISIBLE_WALL;
8149 int map_action_SP_to_RND(int action_sp)
8153 case actActive: return ACTION_ACTIVE;
8154 case actImpact: return ACTION_IMPACT;
8155 case actExploding: return ACTION_EXPLODING;
8156 case actDigging: return ACTION_DIGGING;
8157 case actSnapping: return ACTION_SNAPPING;
8158 case actCollecting: return ACTION_COLLECTING;
8159 case actPassing: return ACTION_PASSING;
8160 case actPushing: return ACTION_PUSHING;
8161 case actDropping: return ACTION_DROPPING;
8163 default: return ACTION_DEFAULT;
8167 int map_element_RND_to_MM(int element_rnd)
8169 return (element_rnd >= EL_MM_START_1 &&
8170 element_rnd <= EL_MM_END_1 ?
8171 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8173 element_rnd >= EL_MM_START_2 &&
8174 element_rnd <= EL_MM_END_2 ?
8175 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8177 element_rnd >= EL_CHAR_START &&
8178 element_rnd <= EL_CHAR_END ?
8179 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8181 element_rnd >= EL_MM_RUNTIME_START &&
8182 element_rnd <= EL_MM_RUNTIME_END ?
8183 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8185 element_rnd >= EL_MM_DUMMY_START &&
8186 element_rnd <= EL_MM_DUMMY_END ?
8187 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8189 EL_MM_EMPTY_NATIVE);
8192 int map_element_MM_to_RND(int element_mm)
8194 return (element_mm == EL_MM_EMPTY_NATIVE ||
8195 element_mm == EL_DF_EMPTY_NATIVE ?
8198 element_mm >= EL_MM_START_1_NATIVE &&
8199 element_mm <= EL_MM_END_1_NATIVE ?
8200 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8202 element_mm >= EL_MM_START_2_NATIVE &&
8203 element_mm <= EL_MM_END_2_NATIVE ?
8204 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8206 element_mm >= EL_MM_CHAR_START_NATIVE &&
8207 element_mm <= EL_MM_CHAR_END_NATIVE ?
8208 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8210 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8211 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8212 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8214 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8215 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8216 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8221 int map_action_MM_to_RND(int action_mm)
8223 // all MM actions are defined to exactly match their RND counterparts
8227 int map_sound_MM_to_RND(int sound_mm)
8231 case SND_MM_GAME_LEVELTIME_CHARGING:
8232 return SND_GAME_LEVELTIME_CHARGING;
8234 case SND_MM_GAME_HEALTH_CHARGING:
8235 return SND_GAME_HEALTH_CHARGING;
8238 return SND_UNDEFINED;
8242 int map_mm_wall_element(int element)
8244 return (element >= EL_MM_STEEL_WALL_START &&
8245 element <= EL_MM_STEEL_WALL_END ?
8248 element >= EL_MM_WOODEN_WALL_START &&
8249 element <= EL_MM_WOODEN_WALL_END ?
8252 element >= EL_MM_ICE_WALL_START &&
8253 element <= EL_MM_ICE_WALL_END ?
8256 element >= EL_MM_AMOEBA_WALL_START &&
8257 element <= EL_MM_AMOEBA_WALL_END ?
8260 element >= EL_DF_STEEL_WALL_START &&
8261 element <= EL_DF_STEEL_WALL_END ?
8264 element >= EL_DF_WOODEN_WALL_START &&
8265 element <= EL_DF_WOODEN_WALL_END ?
8271 int map_mm_wall_element_editor(int element)
8275 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8276 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8277 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8278 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8279 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8280 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8282 default: return element;
8286 int get_next_element(int element)
8290 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8291 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8292 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8293 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8294 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8295 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8296 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8297 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8298 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8299 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8300 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8302 default: return element;
8306 int el2img_mm(int element_mm)
8308 return el2img(map_element_MM_to_RND(element_mm));
8311 int el_act_dir2img(int element, int action, int direction)
8313 element = GFX_ELEMENT(element);
8314 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8316 // direction_graphic[][] == graphic[] for undefined direction graphics
8317 return element_info[element].direction_graphic[action][direction];
8320 static int el_act_dir2crm(int element, int action, int direction)
8322 element = GFX_ELEMENT(element);
8323 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8325 // direction_graphic[][] == graphic[] for undefined direction graphics
8326 return element_info[element].direction_crumbled[action][direction];
8329 int el_act2img(int element, int action)
8331 element = GFX_ELEMENT(element);
8333 return element_info[element].graphic[action];
8336 int el_act2crm(int element, int action)
8338 element = GFX_ELEMENT(element);
8340 return element_info[element].crumbled[action];
8343 int el_dir2img(int element, int direction)
8345 element = GFX_ELEMENT(element);
8347 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8350 int el2baseimg(int element)
8352 return element_info[element].graphic[ACTION_DEFAULT];
8355 int el2img(int element)
8357 element = GFX_ELEMENT(element);
8359 return element_info[element].graphic[ACTION_DEFAULT];
8362 int el2edimg(int element)
8364 element = GFX_ELEMENT(element);
8366 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8369 int el2preimg(int element)
8371 element = GFX_ELEMENT(element);
8373 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8376 int el2panelimg(int element)
8378 element = GFX_ELEMENT(element);
8380 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8383 int font2baseimg(int font_nr)
8385 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8388 int getBeltNrFromBeltElement(int element)
8390 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8391 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8392 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8395 int getBeltNrFromBeltActiveElement(int element)
8397 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8398 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8399 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8402 int getBeltNrFromBeltSwitchElement(int element)
8404 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8405 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8406 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8409 int getBeltDirNrFromBeltElement(int element)
8411 static int belt_base_element[4] =
8413 EL_CONVEYOR_BELT_1_LEFT,
8414 EL_CONVEYOR_BELT_2_LEFT,
8415 EL_CONVEYOR_BELT_3_LEFT,
8416 EL_CONVEYOR_BELT_4_LEFT
8419 int belt_nr = getBeltNrFromBeltElement(element);
8420 int belt_dir_nr = element - belt_base_element[belt_nr];
8422 return (belt_dir_nr % 3);
8425 int getBeltDirNrFromBeltSwitchElement(int element)
8427 static int belt_base_element[4] =
8429 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8430 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8431 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8432 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8435 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8436 int belt_dir_nr = element - belt_base_element[belt_nr];
8438 return (belt_dir_nr % 3);
8441 int getBeltDirFromBeltElement(int element)
8443 static int belt_move_dir[3] =
8450 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8452 return belt_move_dir[belt_dir_nr];
8455 int getBeltDirFromBeltSwitchElement(int element)
8457 static int belt_move_dir[3] =
8464 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8466 return belt_move_dir[belt_dir_nr];
8469 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8471 static int belt_base_element[4] =
8473 EL_CONVEYOR_BELT_1_LEFT,
8474 EL_CONVEYOR_BELT_2_LEFT,
8475 EL_CONVEYOR_BELT_3_LEFT,
8476 EL_CONVEYOR_BELT_4_LEFT
8479 return belt_base_element[belt_nr] + belt_dir_nr;
8482 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8484 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8486 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8489 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8491 static int belt_base_element[4] =
8493 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8494 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8495 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8496 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8499 return belt_base_element[belt_nr] + belt_dir_nr;
8502 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8504 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8506 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8509 boolean swapTiles_EM(boolean is_pre_emc_cave)
8511 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8514 boolean getTeamMode_EM(void)
8516 return game.team_mode || network_playing;
8519 boolean isActivePlayer_EM(int player_nr)
8521 return stored_player[player_nr].active;
8524 unsigned int InitRND(int seed)
8526 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8527 return InitEngineRandom_EM(seed);
8528 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8529 return InitEngineRandom_SP(seed);
8530 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8531 return InitEngineRandom_MM(seed);
8533 return InitEngineRandom_RND(seed);
8536 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8537 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8539 static int get_effective_element_EM(int tile, int frame_em)
8541 int element = object_mapping[tile].element_rnd;
8542 int action = object_mapping[tile].action;
8543 boolean is_backside = object_mapping[tile].is_backside;
8544 boolean action_removing = (action == ACTION_DIGGING ||
8545 action == ACTION_SNAPPING ||
8546 action == ACTION_COLLECTING);
8554 return (frame_em > 5 ? EL_EMPTY : element);
8560 else // frame_em == 7
8571 case Ydiamond_stone:
8575 case Xdrip_stretchB:
8591 case Ymagnify_blank:
8594 case Xsand_stonein_1:
8595 case Xsand_stonein_2:
8596 case Xsand_stonein_3:
8597 case Xsand_stonein_4:
8601 return (is_backside || action_removing ? EL_EMPTY : element);
8606 static boolean check_linear_animation_EM(int tile)
8610 case Xsand_stonesand_1:
8611 case Xsand_stonesand_quickout_1:
8612 case Xsand_sandstone_1:
8613 case Xsand_stonein_1:
8614 case Xsand_stoneout_1:
8642 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8643 boolean has_crumbled_graphics,
8644 int crumbled, int sync_frame)
8646 // if element can be crumbled, but certain action graphics are just empty
8647 // space (like instantly snapping sand to empty space in 1 frame), do not
8648 // treat these empty space graphics as crumbled graphics in EMC engine
8649 if (crumbled == IMG_EMPTY_SPACE)
8650 has_crumbled_graphics = FALSE;
8652 if (has_crumbled_graphics)
8654 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8655 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8656 g_crumbled->anim_delay,
8657 g_crumbled->anim_mode,
8658 g_crumbled->anim_start_frame,
8661 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8662 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8664 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8665 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8667 g_em->has_crumbled_graphics = TRUE;
8671 g_em->crumbled_bitmap = NULL;
8672 g_em->crumbled_src_x = 0;
8673 g_em->crumbled_src_y = 0;
8674 g_em->crumbled_border_size = 0;
8675 g_em->crumbled_tile_size = 0;
8677 g_em->has_crumbled_graphics = FALSE;
8682 void ResetGfxAnimation_EM(int x, int y, int tile)
8688 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8689 int tile, int frame_em, int x, int y)
8691 int action = object_mapping[tile].action;
8692 int direction = object_mapping[tile].direction;
8693 int effective_element = get_effective_element_EM(tile, frame_em);
8694 int graphic = (direction == MV_NONE ?
8695 el_act2img(effective_element, action) :
8696 el_act_dir2img(effective_element, action, direction));
8697 struct GraphicInfo *g = &graphic_info[graphic];
8699 boolean action_removing = (action == ACTION_DIGGING ||
8700 action == ACTION_SNAPPING ||
8701 action == ACTION_COLLECTING);
8702 boolean action_moving = (action == ACTION_FALLING ||
8703 action == ACTION_MOVING ||
8704 action == ACTION_PUSHING ||
8705 action == ACTION_EATING ||
8706 action == ACTION_FILLING ||
8707 action == ACTION_EMPTYING);
8708 boolean action_falling = (action == ACTION_FALLING ||
8709 action == ACTION_FILLING ||
8710 action == ACTION_EMPTYING);
8712 // special case: graphic uses "2nd movement tile" and has defined
8713 // 7 frames for movement animation (or less) => use default graphic
8714 // for last (8th) frame which ends the movement animation
8715 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8717 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8718 graphic = (direction == MV_NONE ?
8719 el_act2img(effective_element, action) :
8720 el_act_dir2img(effective_element, action, direction));
8722 g = &graphic_info[graphic];
8725 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8729 else if (action_moving)
8731 boolean is_backside = object_mapping[tile].is_backside;
8735 int direction = object_mapping[tile].direction;
8736 int move_dir = (action_falling ? MV_DOWN : direction);
8741 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8742 if (g->double_movement && frame_em == 0)
8746 if (move_dir == MV_LEFT)
8747 GfxFrame[x - 1][y] = GfxFrame[x][y];
8748 else if (move_dir == MV_RIGHT)
8749 GfxFrame[x + 1][y] = GfxFrame[x][y];
8750 else if (move_dir == MV_UP)
8751 GfxFrame[x][y - 1] = GfxFrame[x][y];
8752 else if (move_dir == MV_DOWN)
8753 GfxFrame[x][y + 1] = GfxFrame[x][y];
8760 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8761 if (tile == Xsand_stonesand_quickout_1 ||
8762 tile == Xsand_stonesand_quickout_2)
8766 if (graphic_info[graphic].anim_global_sync)
8767 sync_frame = FrameCounter;
8768 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8769 sync_frame = GfxFrame[x][y];
8771 sync_frame = 0; // playfield border (pseudo steel)
8773 SetRandomAnimationValue(x, y);
8775 int frame = getAnimationFrame(g->anim_frames,
8778 g->anim_start_frame,
8781 g_em->unique_identifier =
8782 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8785 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8786 int tile, int frame_em, int x, int y)
8788 int action = object_mapping[tile].action;
8789 int direction = object_mapping[tile].direction;
8790 boolean is_backside = object_mapping[tile].is_backside;
8791 int effective_element = get_effective_element_EM(tile, frame_em);
8792 int effective_action = action;
8793 int graphic = (direction == MV_NONE ?
8794 el_act2img(effective_element, effective_action) :
8795 el_act_dir2img(effective_element, effective_action,
8797 int crumbled = (direction == MV_NONE ?
8798 el_act2crm(effective_element, effective_action) :
8799 el_act_dir2crm(effective_element, effective_action,
8801 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8802 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8803 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8804 struct GraphicInfo *g = &graphic_info[graphic];
8807 // special case: graphic uses "2nd movement tile" and has defined
8808 // 7 frames for movement animation (or less) => use default graphic
8809 // for last (8th) frame which ends the movement animation
8810 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8812 effective_action = ACTION_DEFAULT;
8813 graphic = (direction == MV_NONE ?
8814 el_act2img(effective_element, effective_action) :
8815 el_act_dir2img(effective_element, effective_action,
8817 crumbled = (direction == MV_NONE ?
8818 el_act2crm(effective_element, effective_action) :
8819 el_act_dir2crm(effective_element, effective_action,
8822 g = &graphic_info[graphic];
8825 if (graphic_info[graphic].anim_global_sync)
8826 sync_frame = FrameCounter;
8827 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8828 sync_frame = GfxFrame[x][y];
8830 sync_frame = 0; // playfield border (pseudo steel)
8832 SetRandomAnimationValue(x, y);
8834 int frame = getAnimationFrame(g->anim_frames,
8837 g->anim_start_frame,
8840 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8841 g->double_movement && is_backside);
8843 // (updating the "crumbled" graphic definitions is probably not really needed,
8844 // as animations for crumbled graphics can't be longer than one EMC cycle)
8845 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8849 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8850 int player_nr, int anim, int frame_em)
8852 int element = player_mapping[player_nr][anim].element_rnd;
8853 int action = player_mapping[player_nr][anim].action;
8854 int direction = player_mapping[player_nr][anim].direction;
8855 int graphic = (direction == MV_NONE ?
8856 el_act2img(element, action) :
8857 el_act_dir2img(element, action, direction));
8858 struct GraphicInfo *g = &graphic_info[graphic];
8861 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8863 stored_player[player_nr].StepFrame = frame_em;
8865 sync_frame = stored_player[player_nr].Frame;
8867 int frame = getAnimationFrame(g->anim_frames,
8870 g->anim_start_frame,
8873 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8874 &g_em->src_x, &g_em->src_y, FALSE);
8877 void InitGraphicInfo_EM(void)
8881 // always start with reliable default values
8882 for (i = 0; i < GAME_TILE_MAX; i++)
8884 object_mapping[i].element_rnd = EL_UNKNOWN;
8885 object_mapping[i].is_backside = FALSE;
8886 object_mapping[i].action = ACTION_DEFAULT;
8887 object_mapping[i].direction = MV_NONE;
8890 // always start with reliable default values
8891 for (p = 0; p < MAX_PLAYERS; p++)
8893 for (i = 0; i < PLY_MAX; i++)
8895 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8896 player_mapping[p][i].action = ACTION_DEFAULT;
8897 player_mapping[p][i].direction = MV_NONE;
8901 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8903 int e = em_object_mapping_list[i].element_em;
8905 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8906 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8908 if (em_object_mapping_list[i].action != -1)
8909 object_mapping[e].action = em_object_mapping_list[i].action;
8911 if (em_object_mapping_list[i].direction != -1)
8912 object_mapping[e].direction =
8913 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8916 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8918 int a = em_player_mapping_list[i].action_em;
8919 int p = em_player_mapping_list[i].player_nr;
8921 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8923 if (em_player_mapping_list[i].action != -1)
8924 player_mapping[p][a].action = em_player_mapping_list[i].action;
8926 if (em_player_mapping_list[i].direction != -1)
8927 player_mapping[p][a].direction =
8928 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8931 for (i = 0; i < GAME_TILE_MAX; i++)
8933 int element = object_mapping[i].element_rnd;
8934 int action = object_mapping[i].action;
8935 int direction = object_mapping[i].direction;
8936 boolean is_backside = object_mapping[i].is_backside;
8937 boolean action_exploding = ((action == ACTION_EXPLODING ||
8938 action == ACTION_SMASHED_BY_ROCK ||
8939 action == ACTION_SMASHED_BY_SPRING) &&
8940 element != EL_DIAMOND);
8941 boolean action_active = (action == ACTION_ACTIVE);
8942 boolean action_other = (action == ACTION_OTHER);
8944 for (j = 0; j < 8; j++)
8946 int effective_element = get_effective_element_EM(i, j);
8947 int effective_action = (j < 7 ? action :
8948 i == Xdrip_stretch ? action :
8949 i == Xdrip_stretchB ? action :
8950 i == Ydrip_1_s ? action :
8951 i == Ydrip_1_sB ? action :
8952 i == Yball_1 ? action :
8953 i == Xball_2 ? action :
8954 i == Yball_2 ? action :
8955 i == Yball_blank ? action :
8956 i == Ykey_1_blank ? action :
8957 i == Ykey_2_blank ? action :
8958 i == Ykey_3_blank ? action :
8959 i == Ykey_4_blank ? action :
8960 i == Ykey_5_blank ? action :
8961 i == Ykey_6_blank ? action :
8962 i == Ykey_7_blank ? action :
8963 i == Ykey_8_blank ? action :
8964 i == Ylenses_blank ? action :
8965 i == Ymagnify_blank ? action :
8966 i == Ygrass_blank ? action :
8967 i == Ydirt_blank ? action :
8968 i == Xsand_stonein_1 ? action :
8969 i == Xsand_stonein_2 ? action :
8970 i == Xsand_stonein_3 ? action :
8971 i == Xsand_stonein_4 ? action :
8972 i == Xsand_stoneout_1 ? action :
8973 i == Xsand_stoneout_2 ? action :
8974 i == Xboom_android ? ACTION_EXPLODING :
8975 action_exploding ? ACTION_EXPLODING :
8976 action_active ? action :
8977 action_other ? action :
8979 int graphic = (el_act_dir2img(effective_element, effective_action,
8981 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8983 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8984 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8985 boolean has_action_graphics = (graphic != base_graphic);
8986 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8987 struct GraphicInfo *g = &graphic_info[graphic];
8988 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8991 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8992 boolean special_animation = (action != ACTION_DEFAULT &&
8993 g->anim_frames == 3 &&
8994 g->anim_delay == 2 &&
8995 g->anim_mode & ANIM_LINEAR);
8996 int sync_frame = (i == Xdrip_stretch ? 7 :
8997 i == Xdrip_stretchB ? 7 :
8998 i == Ydrip_2_s ? j + 8 :
8999 i == Ydrip_2_sB ? j + 8 :
9008 i == Xfake_acid_1 ? 0 :
9009 i == Xfake_acid_2 ? 10 :
9010 i == Xfake_acid_3 ? 20 :
9011 i == Xfake_acid_4 ? 30 :
9012 i == Xfake_acid_5 ? 40 :
9013 i == Xfake_acid_6 ? 50 :
9014 i == Xfake_acid_7 ? 60 :
9015 i == Xfake_acid_8 ? 70 :
9016 i == Xfake_acid_1_player ? 0 :
9017 i == Xfake_acid_2_player ? 10 :
9018 i == Xfake_acid_3_player ? 20 :
9019 i == Xfake_acid_4_player ? 30 :
9020 i == Xfake_acid_5_player ? 40 :
9021 i == Xfake_acid_6_player ? 50 :
9022 i == Xfake_acid_7_player ? 60 :
9023 i == Xfake_acid_8_player ? 70 :
9025 i == Yball_2 ? j + 8 :
9026 i == Yball_blank ? j + 1 :
9027 i == Ykey_1_blank ? j + 1 :
9028 i == Ykey_2_blank ? j + 1 :
9029 i == Ykey_3_blank ? j + 1 :
9030 i == Ykey_4_blank ? j + 1 :
9031 i == Ykey_5_blank ? j + 1 :
9032 i == Ykey_6_blank ? j + 1 :
9033 i == Ykey_7_blank ? j + 1 :
9034 i == Ykey_8_blank ? j + 1 :
9035 i == Ylenses_blank ? j + 1 :
9036 i == Ymagnify_blank ? j + 1 :
9037 i == Ygrass_blank ? j + 1 :
9038 i == Ydirt_blank ? j + 1 :
9039 i == Xamoeba_1 ? 0 :
9040 i == Xamoeba_2 ? 1 :
9041 i == Xamoeba_3 ? 2 :
9042 i == Xamoeba_4 ? 3 :
9043 i == Xamoeba_5 ? 0 :
9044 i == Xamoeba_6 ? 1 :
9045 i == Xamoeba_7 ? 2 :
9046 i == Xamoeba_8 ? 3 :
9047 i == Xexit_2 ? j + 8 :
9048 i == Xexit_3 ? j + 16 :
9049 i == Xdynamite_1 ? 0 :
9050 i == Xdynamite_2 ? 8 :
9051 i == Xdynamite_3 ? 16 :
9052 i == Xdynamite_4 ? 24 :
9053 i == Xsand_stonein_1 ? j + 1 :
9054 i == Xsand_stonein_2 ? j + 9 :
9055 i == Xsand_stonein_3 ? j + 17 :
9056 i == Xsand_stonein_4 ? j + 25 :
9057 i == Xsand_stoneout_1 && j == 0 ? 0 :
9058 i == Xsand_stoneout_1 && j == 1 ? 0 :
9059 i == Xsand_stoneout_1 && j == 2 ? 1 :
9060 i == Xsand_stoneout_1 && j == 3 ? 2 :
9061 i == Xsand_stoneout_1 && j == 4 ? 2 :
9062 i == Xsand_stoneout_1 && j == 5 ? 3 :
9063 i == Xsand_stoneout_1 && j == 6 ? 4 :
9064 i == Xsand_stoneout_1 && j == 7 ? 4 :
9065 i == Xsand_stoneout_2 && j == 0 ? 5 :
9066 i == Xsand_stoneout_2 && j == 1 ? 6 :
9067 i == Xsand_stoneout_2 && j == 2 ? 7 :
9068 i == Xsand_stoneout_2 && j == 3 ? 8 :
9069 i == Xsand_stoneout_2 && j == 4 ? 9 :
9070 i == Xsand_stoneout_2 && j == 5 ? 11 :
9071 i == Xsand_stoneout_2 && j == 6 ? 13 :
9072 i == Xsand_stoneout_2 && j == 7 ? 15 :
9073 i == Xboom_bug && j == 1 ? 2 :
9074 i == Xboom_bug && j == 2 ? 2 :
9075 i == Xboom_bug && j == 3 ? 4 :
9076 i == Xboom_bug && j == 4 ? 4 :
9077 i == Xboom_bug && j == 5 ? 2 :
9078 i == Xboom_bug && j == 6 ? 2 :
9079 i == Xboom_bug && j == 7 ? 0 :
9080 i == Xboom_tank && j == 1 ? 2 :
9081 i == Xboom_tank && j == 2 ? 2 :
9082 i == Xboom_tank && j == 3 ? 4 :
9083 i == Xboom_tank && j == 4 ? 4 :
9084 i == Xboom_tank && j == 5 ? 2 :
9085 i == Xboom_tank && j == 6 ? 2 :
9086 i == Xboom_tank && j == 7 ? 0 :
9087 i == Xboom_android && j == 7 ? 6 :
9088 i == Xboom_1 && j == 1 ? 2 :
9089 i == Xboom_1 && j == 2 ? 2 :
9090 i == Xboom_1 && j == 3 ? 4 :
9091 i == Xboom_1 && j == 4 ? 4 :
9092 i == Xboom_1 && j == 5 ? 6 :
9093 i == Xboom_1 && j == 6 ? 6 :
9094 i == Xboom_1 && j == 7 ? 8 :
9095 i == Xboom_2 && j == 0 ? 8 :
9096 i == Xboom_2 && j == 1 ? 8 :
9097 i == Xboom_2 && j == 2 ? 10 :
9098 i == Xboom_2 && j == 3 ? 10 :
9099 i == Xboom_2 && j == 4 ? 10 :
9100 i == Xboom_2 && j == 5 ? 12 :
9101 i == Xboom_2 && j == 6 ? 12 :
9102 i == Xboom_2 && j == 7 ? 12 :
9103 special_animation && j == 4 ? 3 :
9104 effective_action != action ? 0 :
9106 int frame = getAnimationFrame(g->anim_frames,
9109 g->anim_start_frame,
9112 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9113 g->double_movement && is_backside);
9115 g_em->bitmap = src_bitmap;
9116 g_em->src_x = src_x;
9117 g_em->src_y = src_y;
9118 g_em->src_offset_x = 0;
9119 g_em->src_offset_y = 0;
9120 g_em->dst_offset_x = 0;
9121 g_em->dst_offset_y = 0;
9122 g_em->width = TILEX;
9123 g_em->height = TILEY;
9125 g_em->preserve_background = FALSE;
9127 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9130 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9131 effective_action == ACTION_MOVING ||
9132 effective_action == ACTION_PUSHING ||
9133 effective_action == ACTION_EATING)) ||
9134 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9135 effective_action == ACTION_EMPTYING)))
9138 (effective_action == ACTION_FALLING ||
9139 effective_action == ACTION_FILLING ||
9140 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9141 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9142 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9143 int num_steps = (i == Ydrip_1_s ? 16 :
9144 i == Ydrip_1_sB ? 16 :
9145 i == Ydrip_2_s ? 16 :
9146 i == Ydrip_2_sB ? 16 :
9147 i == Xsand_stonein_1 ? 32 :
9148 i == Xsand_stonein_2 ? 32 :
9149 i == Xsand_stonein_3 ? 32 :
9150 i == Xsand_stonein_4 ? 32 :
9151 i == Xsand_stoneout_1 ? 16 :
9152 i == Xsand_stoneout_2 ? 16 : 8);
9153 int cx = ABS(dx) * (TILEX / num_steps);
9154 int cy = ABS(dy) * (TILEY / num_steps);
9155 int step_frame = (i == Ydrip_2_s ? j + 8 :
9156 i == Ydrip_2_sB ? j + 8 :
9157 i == Xsand_stonein_2 ? j + 8 :
9158 i == Xsand_stonein_3 ? j + 16 :
9159 i == Xsand_stonein_4 ? j + 24 :
9160 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9161 int step = (is_backside ? step_frame : num_steps - step_frame);
9163 if (is_backside) // tile where movement starts
9165 if (dx < 0 || dy < 0)
9167 g_em->src_offset_x = cx * step;
9168 g_em->src_offset_y = cy * step;
9172 g_em->dst_offset_x = cx * step;
9173 g_em->dst_offset_y = cy * step;
9176 else // tile where movement ends
9178 if (dx < 0 || dy < 0)
9180 g_em->dst_offset_x = cx * step;
9181 g_em->dst_offset_y = cy * step;
9185 g_em->src_offset_x = cx * step;
9186 g_em->src_offset_y = cy * step;
9190 g_em->width = TILEX - cx * step;
9191 g_em->height = TILEY - cy * step;
9194 // create unique graphic identifier to decide if tile must be redrawn
9195 /* bit 31 - 16 (16 bit): EM style graphic
9196 bit 15 - 12 ( 4 bit): EM style frame
9197 bit 11 - 6 ( 6 bit): graphic width
9198 bit 5 - 0 ( 6 bit): graphic height */
9199 g_em->unique_identifier =
9200 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9204 for (i = 0; i < GAME_TILE_MAX; i++)
9206 for (j = 0; j < 8; j++)
9208 int element = object_mapping[i].element_rnd;
9209 int action = object_mapping[i].action;
9210 int direction = object_mapping[i].direction;
9211 boolean is_backside = object_mapping[i].is_backside;
9212 int graphic_action = el_act_dir2img(element, action, direction);
9213 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9215 if ((action == ACTION_SMASHED_BY_ROCK ||
9216 action == ACTION_SMASHED_BY_SPRING ||
9217 action == ACTION_EATING) &&
9218 graphic_action == graphic_default)
9220 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9221 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9222 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9223 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9226 // no separate animation for "smashed by rock" -- use rock instead
9227 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9228 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9230 g_em->bitmap = g_xx->bitmap;
9231 g_em->src_x = g_xx->src_x;
9232 g_em->src_y = g_xx->src_y;
9233 g_em->src_offset_x = g_xx->src_offset_x;
9234 g_em->src_offset_y = g_xx->src_offset_y;
9235 g_em->dst_offset_x = g_xx->dst_offset_x;
9236 g_em->dst_offset_y = g_xx->dst_offset_y;
9237 g_em->width = g_xx->width;
9238 g_em->height = g_xx->height;
9239 g_em->unique_identifier = g_xx->unique_identifier;
9242 g_em->preserve_background = TRUE;
9247 for (p = 0; p < MAX_PLAYERS; p++)
9249 for (i = 0; i < PLY_MAX; i++)
9251 int element = player_mapping[p][i].element_rnd;
9252 int action = player_mapping[p][i].action;
9253 int direction = player_mapping[p][i].direction;
9255 for (j = 0; j < 8; j++)
9257 int effective_element = element;
9258 int effective_action = action;
9259 int graphic = (direction == MV_NONE ?
9260 el_act2img(effective_element, effective_action) :
9261 el_act_dir2img(effective_element, effective_action,
9263 struct GraphicInfo *g = &graphic_info[graphic];
9264 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9268 int frame = getAnimationFrame(g->anim_frames,
9271 g->anim_start_frame,
9274 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9276 g_em->bitmap = src_bitmap;
9277 g_em->src_x = src_x;
9278 g_em->src_y = src_y;
9279 g_em->src_offset_x = 0;
9280 g_em->src_offset_y = 0;
9281 g_em->dst_offset_x = 0;
9282 g_em->dst_offset_y = 0;
9283 g_em->width = TILEX;
9284 g_em->height = TILEY;
9290 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9291 boolean any_player_moving,
9292 boolean any_player_snapping,
9293 boolean any_player_dropping)
9295 if (frame == 7 && !any_player_dropping)
9297 if (!local_player->was_waiting)
9299 if (!CheckSaveEngineSnapshotToList())
9302 local_player->was_waiting = TRUE;
9305 else if (any_player_moving || any_player_snapping || any_player_dropping)
9307 local_player->was_waiting = FALSE;
9311 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9312 boolean murphy_is_dropping)
9314 if (murphy_is_waiting)
9316 if (!local_player->was_waiting)
9318 if (!CheckSaveEngineSnapshotToList())
9321 local_player->was_waiting = TRUE;
9326 local_player->was_waiting = FALSE;
9330 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9331 boolean button_released)
9333 if (button_released)
9335 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9336 CheckSaveEngineSnapshotToList();
9338 else if (element_clicked)
9340 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9341 CheckSaveEngineSnapshotToList();
9343 game.snapshot.changed_action = TRUE;
9347 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9348 boolean any_player_moving,
9349 boolean any_player_snapping,
9350 boolean any_player_dropping)
9352 if (tape.single_step && tape.recording && !tape.pausing)
9353 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9354 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9356 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9357 any_player_snapping, any_player_dropping);
9359 return tape.pausing;
9362 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9363 boolean murphy_is_dropping)
9365 boolean murphy_starts_dropping = FALSE;
9368 for (i = 0; i < MAX_PLAYERS; i++)
9369 if (stored_player[i].force_dropping)
9370 murphy_starts_dropping = TRUE;
9372 if (tape.single_step && tape.recording && !tape.pausing)
9373 if (murphy_is_waiting && !murphy_starts_dropping)
9374 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9376 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9379 void CheckSingleStepMode_MM(boolean element_clicked,
9380 boolean button_released)
9382 if (tape.single_step && tape.recording && !tape.pausing)
9383 if (button_released)
9384 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9386 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9389 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9390 int graphic, int sync_frame, int x, int y)
9392 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9394 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9397 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9399 return (IS_NEXT_FRAME(sync_frame, graphic));
9402 int getGraphicInfo_Delay(int graphic)
9404 return graphic_info[graphic].anim_delay;
9407 void PlayMenuSoundExt(int sound)
9409 if (sound == SND_UNDEFINED)
9412 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9413 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9416 if (IS_LOOP_SOUND(sound))
9417 PlaySoundLoop(sound);
9422 void PlayMenuSound(void)
9424 PlayMenuSoundExt(menu.sound[game_status]);
9427 void PlayMenuSoundStereo(int sound, int stereo_position)
9429 if (sound == SND_UNDEFINED)
9432 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9433 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9436 if (IS_LOOP_SOUND(sound))
9437 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9439 PlaySoundStereo(sound, stereo_position);
9442 void PlayMenuSoundIfLoopExt(int sound)
9444 if (sound == SND_UNDEFINED)
9447 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9448 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9451 if (IS_LOOP_SOUND(sound))
9452 PlaySoundLoop(sound);
9455 void PlayMenuSoundIfLoop(void)
9457 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9460 void PlayMenuMusicExt(int music)
9462 if (music == MUS_UNDEFINED)
9465 if (!setup.sound_music)
9468 if (IS_LOOP_MUSIC(music))
9469 PlayMusicLoop(music);
9474 void PlayMenuMusic(void)
9476 char *curr_music = getCurrentlyPlayingMusicFilename();
9477 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9479 if (!strEqual(curr_music, next_music))
9480 PlayMenuMusicExt(menu.music[game_status]);
9483 void PlayMenuSoundsAndMusic(void)
9489 static void FadeMenuSounds(void)
9494 static void FadeMenuMusic(void)
9496 char *curr_music = getCurrentlyPlayingMusicFilename();
9497 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9499 if (!strEqual(curr_music, next_music))
9503 void FadeMenuSoundsAndMusic(void)
9509 void PlaySoundActivating(void)
9512 PlaySound(SND_MENU_ITEM_ACTIVATING);
9516 void PlaySoundSelecting(void)
9519 PlaySound(SND_MENU_ITEM_SELECTING);
9523 void ToggleFullscreenIfNeeded(void)
9525 // if setup and video fullscreen state are already matching, nothing do do
9526 if (setup.fullscreen == video.fullscreen_enabled ||
9527 !video.fullscreen_available)
9530 SDLSetWindowFullscreen(setup.fullscreen);
9532 // set setup value according to successfully changed fullscreen mode
9533 setup.fullscreen = video.fullscreen_enabled;
9536 void ChangeWindowScalingIfNeeded(void)
9538 // if setup and video window scaling are already matching, nothing do do
9539 if (setup.window_scaling_percent == video.window_scaling_percent ||
9540 video.fullscreen_enabled)
9543 SDLSetWindowScaling(setup.window_scaling_percent);
9545 // set setup value according to successfully changed window scaling
9546 setup.window_scaling_percent = video.window_scaling_percent;
9549 void ChangeVsyncModeIfNeeded(void)
9551 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9552 int video_vsync_mode = video.vsync_mode;
9554 // if setup and video vsync mode are already matching, nothing do do
9555 if (setup_vsync_mode == video_vsync_mode)
9558 // if renderer is using OpenGL, vsync mode can directly be changed
9559 SDLSetScreenVsyncMode(setup.vsync_mode);
9561 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9562 if (video.vsync_mode == video_vsync_mode)
9564 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9566 // save backbuffer content which gets lost when re-creating screen
9567 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9569 // force re-creating screen and renderer to set new vsync mode
9570 video.fullscreen_enabled = !setup.fullscreen;
9572 // when creating new renderer, destroy textures linked to old renderer
9573 FreeAllImageTextures(); // needs old renderer to free the textures
9575 // re-create screen and renderer (including change of vsync mode)
9576 ChangeVideoModeIfNeeded(setup.fullscreen);
9578 // set setup value according to successfully changed fullscreen mode
9579 setup.fullscreen = video.fullscreen_enabled;
9581 // restore backbuffer content from temporary backbuffer backup bitmap
9582 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9583 FreeBitmap(tmp_backbuffer);
9585 // update visible window/screen
9586 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9588 // when changing vsync mode, re-create textures for new renderer
9589 InitImageTextures();
9592 // set setup value according to successfully changed vsync mode
9593 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9596 static void JoinRectangles(int *x, int *y, int *width, int *height,
9597 int x2, int y2, int width2, int height2)
9599 // do not join with "off-screen" rectangle
9600 if (x2 == -1 || y2 == -1)
9605 *width = MAX(*width, width2);
9606 *height = MAX(*height, height2);
9609 void SetAnimStatus(int anim_status_new)
9611 if (anim_status_new == GAME_MODE_MAIN)
9612 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9613 else if (anim_status_new == GAME_MODE_NAMES)
9614 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9615 else if (anim_status_new == GAME_MODE_SCORES)
9616 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9618 global.anim_status_next = anim_status_new;
9620 // directly set screen modes that are entered without fading
9621 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9622 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9623 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9624 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9625 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9626 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9627 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9628 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9629 global.anim_status = global.anim_status_next;
9632 void SetGameStatus(int game_status_new)
9634 if (game_status_new != game_status)
9635 game_status_last_screen = game_status;
9637 game_status = game_status_new;
9639 SetAnimStatus(game_status_new);
9642 void SetFontStatus(int game_status_new)
9644 static int last_game_status = -1;
9646 if (game_status_new != -1)
9648 // set game status for font use after storing last game status
9649 last_game_status = game_status;
9650 game_status = game_status_new;
9654 // reset game status after font use from last stored game status
9655 game_status = last_game_status;
9659 void ResetFontStatus(void)
9664 void SetLevelSetInfo(char *identifier, int level_nr)
9666 setString(&levelset.identifier, identifier);
9668 levelset.level_nr = level_nr;
9671 boolean CheckIfAllViewportsHaveChanged(void)
9673 // if game status has not changed, viewports have not changed either
9674 if (game_status == game_status_last)
9677 // check if all viewports have changed with current game status
9679 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9680 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9681 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9682 int new_real_sx = vp_playfield->x;
9683 int new_real_sy = vp_playfield->y;
9684 int new_full_sxsize = vp_playfield->width;
9685 int new_full_sysize = vp_playfield->height;
9686 int new_dx = vp_door_1->x;
9687 int new_dy = vp_door_1->y;
9688 int new_dxsize = vp_door_1->width;
9689 int new_dysize = vp_door_1->height;
9690 int new_vx = vp_door_2->x;
9691 int new_vy = vp_door_2->y;
9692 int new_vxsize = vp_door_2->width;
9693 int new_vysize = vp_door_2->height;
9695 boolean playfield_viewport_has_changed =
9696 (new_real_sx != REAL_SX ||
9697 new_real_sy != REAL_SY ||
9698 new_full_sxsize != FULL_SXSIZE ||
9699 new_full_sysize != FULL_SYSIZE);
9701 boolean door_1_viewport_has_changed =
9704 new_dxsize != DXSIZE ||
9705 new_dysize != DYSIZE);
9707 boolean door_2_viewport_has_changed =
9710 new_vxsize != VXSIZE ||
9711 new_vysize != VYSIZE ||
9712 game_status_last == GAME_MODE_EDITOR);
9714 return (playfield_viewport_has_changed &&
9715 door_1_viewport_has_changed &&
9716 door_2_viewport_has_changed);
9719 boolean CheckFadeAll(void)
9721 return (CheckIfGlobalBorderHasChanged() ||
9722 CheckIfAllViewportsHaveChanged());
9725 void ChangeViewportPropertiesIfNeeded(void)
9727 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9728 FALSE : setup.small_game_graphics);
9729 int gfx_game_mode = game_status;
9730 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9732 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9733 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9734 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9735 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9736 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9737 int new_win_xsize = vp_window->width;
9738 int new_win_ysize = vp_window->height;
9739 int border_left = vp_playfield->border_left;
9740 int border_right = vp_playfield->border_right;
9741 int border_top = vp_playfield->border_top;
9742 int border_bottom = vp_playfield->border_bottom;
9743 int new_sx = vp_playfield->x + border_left;
9744 int new_sy = vp_playfield->y + border_top;
9745 int new_sxsize = vp_playfield->width - border_left - border_right;
9746 int new_sysize = vp_playfield->height - border_top - border_bottom;
9747 int new_real_sx = vp_playfield->x;
9748 int new_real_sy = vp_playfield->y;
9749 int new_full_sxsize = vp_playfield->width;
9750 int new_full_sysize = vp_playfield->height;
9751 int new_dx = vp_door_1->x;
9752 int new_dy = vp_door_1->y;
9753 int new_dxsize = vp_door_1->width;
9754 int new_dysize = vp_door_1->height;
9755 int new_vx = vp_door_2->x;
9756 int new_vy = vp_door_2->y;
9757 int new_vxsize = vp_door_2->width;
9758 int new_vysize = vp_door_2->height;
9759 int new_ex = vp_door_3->x;
9760 int new_ey = vp_door_3->y;
9761 int new_exsize = vp_door_3->width;
9762 int new_eysize = vp_door_3->height;
9763 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9764 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9765 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9766 int new_scr_fieldx = new_sxsize / tilesize;
9767 int new_scr_fieldy = new_sysize / tilesize;
9768 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9769 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9770 boolean init_gfx_buffers = FALSE;
9771 boolean init_video_buffer = FALSE;
9772 boolean init_gadgets_and_anims = FALSE;
9773 boolean init_em_graphics = FALSE;
9775 if (new_win_xsize != WIN_XSIZE ||
9776 new_win_ysize != WIN_YSIZE)
9778 WIN_XSIZE = new_win_xsize;
9779 WIN_YSIZE = new_win_ysize;
9781 init_video_buffer = TRUE;
9782 init_gfx_buffers = TRUE;
9783 init_gadgets_and_anims = TRUE;
9785 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9788 if (new_scr_fieldx != SCR_FIELDX ||
9789 new_scr_fieldy != SCR_FIELDY)
9791 // this always toggles between MAIN and GAME when using small tile size
9793 SCR_FIELDX = new_scr_fieldx;
9794 SCR_FIELDY = new_scr_fieldy;
9796 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9807 new_sxsize != SXSIZE ||
9808 new_sysize != SYSIZE ||
9809 new_dxsize != DXSIZE ||
9810 new_dysize != DYSIZE ||
9811 new_vxsize != VXSIZE ||
9812 new_vysize != VYSIZE ||
9813 new_exsize != EXSIZE ||
9814 new_eysize != EYSIZE ||
9815 new_real_sx != REAL_SX ||
9816 new_real_sy != REAL_SY ||
9817 new_full_sxsize != FULL_SXSIZE ||
9818 new_full_sysize != FULL_SYSIZE ||
9819 new_tilesize_var != TILESIZE_VAR
9822 // ------------------------------------------------------------------------
9823 // determine next fading area for changed viewport definitions
9824 // ------------------------------------------------------------------------
9826 // start with current playfield area (default fading area)
9829 FADE_SXSIZE = FULL_SXSIZE;
9830 FADE_SYSIZE = FULL_SYSIZE;
9832 // add new playfield area if position or size has changed
9833 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9834 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9836 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9837 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9840 // add current and new door 1 area if position or size has changed
9841 if (new_dx != DX || new_dy != DY ||
9842 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9844 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9845 DX, DY, DXSIZE, DYSIZE);
9846 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9847 new_dx, new_dy, new_dxsize, new_dysize);
9850 // add current and new door 2 area if position or size has changed
9851 if (new_vx != VX || new_vy != VY ||
9852 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9854 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9855 VX, VY, VXSIZE, VYSIZE);
9856 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9857 new_vx, new_vy, new_vxsize, new_vysize);
9860 // ------------------------------------------------------------------------
9861 // handle changed tile size
9862 // ------------------------------------------------------------------------
9864 if (new_tilesize_var != TILESIZE_VAR)
9866 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9868 // changing tile size invalidates scroll values of engine snapshots
9869 FreeEngineSnapshotSingle();
9871 // changing tile size requires update of graphic mapping for EM engine
9872 init_em_graphics = TRUE;
9883 SXSIZE = new_sxsize;
9884 SYSIZE = new_sysize;
9885 DXSIZE = new_dxsize;
9886 DYSIZE = new_dysize;
9887 VXSIZE = new_vxsize;
9888 VYSIZE = new_vysize;
9889 EXSIZE = new_exsize;
9890 EYSIZE = new_eysize;
9891 REAL_SX = new_real_sx;
9892 REAL_SY = new_real_sy;
9893 FULL_SXSIZE = new_full_sxsize;
9894 FULL_SYSIZE = new_full_sysize;
9895 TILESIZE_VAR = new_tilesize_var;
9897 init_gfx_buffers = TRUE;
9898 init_gadgets_and_anims = TRUE;
9900 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9901 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9904 if (init_gfx_buffers)
9906 // Debug("tools:viewport", "init_gfx_buffers");
9908 SCR_FIELDX = new_scr_fieldx_buffers;
9909 SCR_FIELDY = new_scr_fieldy_buffers;
9913 SCR_FIELDX = new_scr_fieldx;
9914 SCR_FIELDY = new_scr_fieldy;
9916 SetDrawDeactivationMask(REDRAW_NONE);
9917 SetDrawBackgroundMask(REDRAW_FIELD);
9920 if (init_video_buffer)
9922 // Debug("tools:viewport", "init_video_buffer");
9924 FreeAllImageTextures(); // needs old renderer to free the textures
9926 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9927 InitImageTextures();
9930 if (init_gadgets_and_anims)
9932 // Debug("tools:viewport", "init_gadgets_and_anims");
9935 InitGlobalAnimations();
9938 if (init_em_graphics)
9940 InitGraphicInfo_EM();
9945 // ============================================================================
9947 // ============================================================================
9949 #if defined(PLATFORM_WIN32)
9950 /* FILETIME of Jan 1 1970 00:00:00. */
9951 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9954 * timezone information is stored outside the kernel so tzp isn't used anymore.
9956 * Note: this function is not for Win32 high precision timing purpose. See
9959 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9962 SYSTEMTIME system_time;
9963 ULARGE_INTEGER ularge;
9965 GetSystemTime(&system_time);
9966 SystemTimeToFileTime(&system_time, &file_time);
9967 ularge.LowPart = file_time.dwLowDateTime;
9968 ularge.HighPart = file_time.dwHighDateTime;
9970 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
9971 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
9977 static char *test_init_uuid_random_function_simple(void)
9979 static char seed_text[100];
9980 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
9982 sprintf(seed_text, "%d", seed);
9987 static char *test_init_uuid_random_function_better(void)
9989 static char seed_text[100];
9990 struct timeval current_time;
9992 gettimeofday(¤t_time, NULL);
9994 prng_seed_bytes(¤t_time, sizeof(current_time));
9996 sprintf(seed_text, "%ld.%ld",
9997 (long)current_time.tv_sec,
9998 (long)current_time.tv_usec);
10003 #if defined(PLATFORM_WIN32)
10004 static char *test_init_uuid_random_function_better_windows(void)
10006 static char seed_text[100];
10007 struct timeval current_time;
10009 gettimeofday_windows(¤t_time, NULL);
10011 prng_seed_bytes(¤t_time, sizeof(current_time));
10013 sprintf(seed_text, "%ld.%ld",
10014 (long)current_time.tv_sec,
10015 (long)current_time.tv_usec);
10021 static unsigned int test_uuid_random_function_simple(int max)
10023 return GetSimpleRandom(max);
10026 static unsigned int test_uuid_random_function_better(int max)
10028 return (max > 0 ? prng_get_uint() % max : 0);
10031 #if defined(PLATFORM_WIN32)
10032 #define NUM_UUID_TESTS 3
10034 #define NUM_UUID_TESTS 2
10037 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10039 struct hashtable *hash_seeds =
10040 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10041 struct hashtable *hash_uuids =
10042 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10043 static char message[100];
10046 char *random_name = (nr == 0 ? "simple" : "better");
10047 char *random_type = (always_seed ? "always" : "only once");
10048 char *(*init_random_function)(void) =
10050 test_init_uuid_random_function_simple :
10051 test_init_uuid_random_function_better);
10052 unsigned int (*random_function)(int) =
10054 test_uuid_random_function_simple :
10055 test_uuid_random_function_better);
10058 #if defined(PLATFORM_WIN32)
10061 random_name = "windows";
10062 init_random_function = test_init_uuid_random_function_better_windows;
10068 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10069 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10071 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10072 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10073 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10075 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10079 // always initialize random number generator at least once
10080 init_random_function();
10082 unsigned int time_start = SDL_GetTicks();
10084 for (i = 0; i < num_uuids; i++)
10088 char *seed = getStringCopy(init_random_function());
10090 hashtable_remove(hash_seeds, seed);
10091 hashtable_insert(hash_seeds, seed, "1");
10094 char *uuid = getStringCopy(getUUIDExt(random_function));
10096 hashtable_remove(hash_uuids, uuid);
10097 hashtable_insert(hash_uuids, uuid, "1");
10100 int num_unique_seeds = hashtable_count(hash_seeds);
10101 int num_unique_uuids = hashtable_count(hash_uuids);
10103 unsigned int time_needed = SDL_GetTicks() - time_start;
10105 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10107 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10110 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10112 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10113 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10115 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10117 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10119 Request(message, REQ_CONFIRM);
10121 hashtable_destroy(hash_seeds, 0);
10122 hashtable_destroy(hash_uuids, 0);
10125 void TestGeneratingUUIDs(void)
10127 int num_uuids = 1000000;
10130 for (i = 0; i < NUM_UUID_TESTS; i++)
10131 for (j = 0; j < 2; j++)
10132 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10134 CloseAllAndExit(0);