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 from_x, int from_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)
1426 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1427 static int safety = 0;
1429 // check if starting field still has the desired content
1430 if (field[from_x][from_y] == fill_element)
1435 if (safety > max_fieldx * max_fieldy)
1436 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1438 old_element = field[from_x][from_y];
1439 field[from_x][from_y] = fill_element;
1441 for (i = 0; i < 4; i++)
1443 x = from_x + check[i][0];
1444 y = from_y + check[i][1];
1446 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1447 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1448 field, max_fieldx, max_fieldy);
1454 void FloodFillLevel(int from_x, int from_y, int fill_element,
1455 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1456 int max_fieldx, int max_fieldy)
1458 FloodFillLevelExt(from_x, from_y, fill_element,
1459 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1460 max_fieldx, max_fieldy);
1463 void SetRandomAnimationValue(int x, int y)
1465 gfx.anim_random_frame = GfxRandom[x][y];
1468 int getGraphicAnimationFrame(int graphic, int sync_frame)
1470 // animation synchronized with global frame counter, not move position
1471 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1472 sync_frame = FrameCounter;
1474 return getAnimationFrame(graphic_info[graphic].anim_frames,
1475 graphic_info[graphic].anim_delay,
1476 graphic_info[graphic].anim_mode,
1477 graphic_info[graphic].anim_start_frame,
1481 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1483 struct GraphicInfo *g = &graphic_info[graphic];
1484 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1486 if (tilesize == gfx.standard_tile_size)
1487 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1488 else if (tilesize == game.tile_size)
1489 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1491 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1494 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1495 boolean get_backside)
1497 struct GraphicInfo *g = &graphic_info[graphic];
1498 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1499 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1501 if (g->offset_y == 0) // frames are ordered horizontally
1503 int max_width = g->anim_frames_per_line * g->width;
1504 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1506 *x = pos % max_width;
1507 *y = src_y % g->height + pos / max_width * g->height;
1509 else if (g->offset_x == 0) // frames are ordered vertically
1511 int max_height = g->anim_frames_per_line * g->height;
1512 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1514 *x = src_x % g->width + pos / max_height * g->width;
1515 *y = pos % max_height;
1517 else // frames are ordered diagonally
1519 *x = src_x + frame * g->offset_x;
1520 *y = src_y + frame * g->offset_y;
1524 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1525 Bitmap **bitmap, int *x, int *y,
1526 boolean get_backside)
1528 struct GraphicInfo *g = &graphic_info[graphic];
1530 // if no graphics defined at all, use fallback graphics
1531 if (g->bitmaps == NULL)
1532 *g = graphic_info[IMG_CHAR_EXCLAM];
1534 // if no in-game graphics defined, always use standard graphic size
1535 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1536 tilesize = TILESIZE;
1538 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1539 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1541 *x = *x * tilesize / g->tile_size;
1542 *y = *y * tilesize / g->tile_size;
1545 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1546 Bitmap **bitmap, int *x, int *y)
1548 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1551 void getFixedGraphicSource(int graphic, int frame,
1552 Bitmap **bitmap, int *x, int *y)
1554 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1557 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1559 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1562 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1563 int *x, int *y, boolean get_backside)
1565 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1569 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1571 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1574 void DrawGraphic(int x, int y, int graphic, int frame)
1577 if (!IN_SCR_FIELD(x, y))
1579 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1580 Debug("draw:DrawGraphic", "This should never happen!");
1586 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1589 MarkTileDirty(x, y);
1592 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1595 if (!IN_SCR_FIELD(x, y))
1597 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1599 Debug("draw:DrawFixedGraphic", "This should never happen!");
1605 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1607 MarkTileDirty(x, y);
1610 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1616 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1618 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1621 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1627 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1628 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1631 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1634 if (!IN_SCR_FIELD(x, y))
1636 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1638 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1644 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1647 MarkTileDirty(x, y);
1650 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1653 if (!IN_SCR_FIELD(x, y))
1655 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1657 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1663 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1665 MarkTileDirty(x, y);
1668 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1674 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1676 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1680 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1681 int graphic, int frame)
1686 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1688 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1692 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1694 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1696 MarkTileDirty(x / tilesize, y / tilesize);
1699 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1702 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1703 graphic, frame, tilesize);
1704 MarkTileDirty(x / tilesize, y / tilesize);
1707 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1713 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1714 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1717 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1718 int frame, int tilesize)
1723 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1724 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1727 void DrawMiniGraphic(int x, int y, int graphic)
1729 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1730 MarkTileDirty(x / 2, y / 2);
1733 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1738 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1739 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1742 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1743 int graphic, int frame,
1744 int cut_mode, int mask_mode)
1749 int width = TILEX, height = TILEY;
1752 if (dx || dy) // shifted graphic
1754 if (x < BX1) // object enters playfield from the left
1761 else if (x > BX2) // object enters playfield from the right
1767 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1773 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1775 else if (dx) // general horizontal movement
1776 MarkTileDirty(x + SIGN(dx), y);
1778 if (y < BY1) // object enters playfield from the top
1780 if (cut_mode == CUT_BELOW) // object completely above top border
1788 else if (y > BY2) // object enters playfield from the bottom
1794 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1800 else if (dy > 0 && cut_mode == CUT_ABOVE)
1802 if (y == BY2) // object completely above bottom border
1808 MarkTileDirty(x, y + 1);
1809 } // object leaves playfield to the bottom
1810 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1812 else if (dy) // general vertical movement
1813 MarkTileDirty(x, y + SIGN(dy));
1817 if (!IN_SCR_FIELD(x, y))
1819 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1821 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1827 width = width * TILESIZE_VAR / TILESIZE;
1828 height = height * TILESIZE_VAR / TILESIZE;
1829 cx = cx * TILESIZE_VAR / TILESIZE;
1830 cy = cy * TILESIZE_VAR / TILESIZE;
1831 dx = dx * TILESIZE_VAR / TILESIZE;
1832 dy = dy * TILESIZE_VAR / TILESIZE;
1834 if (width > 0 && height > 0)
1836 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1841 dst_x = FX + x * TILEX_VAR + dx;
1842 dst_y = FY + y * TILEY_VAR + dy;
1844 if (mask_mode == USE_MASKING)
1845 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1848 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1851 MarkTileDirty(x, y);
1855 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1856 int graphic, int frame,
1857 int cut_mode, int mask_mode)
1862 int width = TILEX_VAR, height = TILEY_VAR;
1865 int x2 = x + SIGN(dx);
1866 int y2 = y + SIGN(dy);
1868 // movement with two-tile animations must be sync'ed with movement position,
1869 // not with current GfxFrame (which can be higher when using slow movement)
1870 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1871 int anim_frames = graphic_info[graphic].anim_frames;
1873 // (we also need anim_delay here for movement animations with less frames)
1874 int anim_delay = graphic_info[graphic].anim_delay;
1875 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1877 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1878 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1880 // re-calculate animation frame for two-tile movement animation
1881 frame = getGraphicAnimationFrame(graphic, sync_frame);
1883 // check if movement start graphic inside screen area and should be drawn
1884 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1886 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1888 dst_x = FX + x1 * TILEX_VAR;
1889 dst_y = FY + y1 * TILEY_VAR;
1891 if (mask_mode == USE_MASKING)
1892 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1895 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1898 MarkTileDirty(x1, y1);
1901 // check if movement end graphic inside screen area and should be drawn
1902 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1904 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1906 dst_x = FX + x2 * TILEX_VAR;
1907 dst_y = FY + y2 * TILEY_VAR;
1909 if (mask_mode == USE_MASKING)
1910 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1913 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1916 MarkTileDirty(x2, y2);
1920 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1921 int graphic, int frame,
1922 int cut_mode, int mask_mode)
1926 DrawGraphic(x, y, graphic, frame);
1931 if (graphic_info[graphic].double_movement) // EM style movement images
1932 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1934 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1937 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1938 int graphic, int frame, int cut_mode)
1940 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1943 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1944 int cut_mode, int mask_mode)
1946 int lx = LEVELX(x), ly = LEVELY(y);
1950 if (IN_LEV_FIELD(lx, ly))
1952 SetRandomAnimationValue(lx, ly);
1954 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1955 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1957 // do not use double (EM style) movement graphic when not moving
1958 if (graphic_info[graphic].double_movement && !dx && !dy)
1960 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1961 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1964 else // border element
1966 graphic = el2img(element);
1967 frame = getGraphicAnimationFrame(graphic, -1);
1970 if (element == EL_EXPANDABLE_WALL)
1972 boolean left_stopped = FALSE, right_stopped = FALSE;
1974 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1975 left_stopped = TRUE;
1976 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
1977 right_stopped = TRUE;
1979 if (left_stopped && right_stopped)
1981 else if (left_stopped)
1983 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1984 frame = graphic_info[graphic].anim_frames - 1;
1986 else if (right_stopped)
1988 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1989 frame = graphic_info[graphic].anim_frames - 1;
1994 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1995 else if (mask_mode == USE_MASKING)
1996 DrawGraphicThruMask(x, y, graphic, frame);
1998 DrawGraphic(x, y, graphic, frame);
2001 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2002 int cut_mode, int mask_mode)
2004 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2005 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2006 cut_mode, mask_mode);
2009 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2012 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2015 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2018 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2021 void DrawLevelElementThruMask(int x, int y, int element)
2023 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2026 void DrawLevelFieldThruMask(int x, int y)
2028 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2031 // !!! implementation of quicksand is totally broken !!!
2032 #define IS_CRUMBLED_TILE(x, y, e) \
2033 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2034 !IS_MOVING(x, y) || \
2035 (e) == EL_QUICKSAND_EMPTYING || \
2036 (e) == EL_QUICKSAND_FAST_EMPTYING))
2038 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2043 int width, height, cx, cy;
2044 int sx = SCREENX(x), sy = SCREENY(y);
2045 int crumbled_border_size = graphic_info[graphic].border_size;
2046 int crumbled_tile_size = graphic_info[graphic].tile_size;
2047 int crumbled_border_size_var =
2048 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2051 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2053 for (i = 1; i < 4; i++)
2055 int dxx = (i & 1 ? dx : 0);
2056 int dyy = (i & 2 ? dy : 0);
2059 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2062 // check if neighbour field is of same crumble type
2063 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2064 graphic_info[graphic].class ==
2065 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2067 // return if check prevents inner corner
2068 if (same == (dxx == dx && dyy == dy))
2072 // if we reach this point, we have an inner corner
2074 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2076 width = crumbled_border_size_var;
2077 height = crumbled_border_size_var;
2078 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2079 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2081 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2082 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2085 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2090 int width, height, bx, by, cx, cy;
2091 int sx = SCREENX(x), sy = SCREENY(y);
2092 int crumbled_border_size = graphic_info[graphic].border_size;
2093 int crumbled_tile_size = graphic_info[graphic].tile_size;
2094 int crumbled_border_size_var =
2095 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2096 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2099 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2101 // draw simple, sloppy, non-corner-accurate crumbled border
2103 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2104 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2105 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2106 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2108 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2109 FX + sx * TILEX_VAR + cx,
2110 FY + sy * TILEY_VAR + cy);
2112 // (remaining middle border part must be at least as big as corner part)
2113 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2114 crumbled_border_size_var >= TILESIZE_VAR / 3)
2117 // correct corners of crumbled border, if needed
2119 for (i = -1; i <= 1; i += 2)
2121 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2122 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2123 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2126 // check if neighbour field is of same crumble type
2127 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2128 graphic_info[graphic].class ==
2129 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2131 // no crumbled corner, but continued crumbled border
2133 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2134 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2135 int b1 = (i == 1 ? crumbled_border_size_var :
2136 TILESIZE_VAR - 2 * crumbled_border_size_var);
2138 width = crumbled_border_size_var;
2139 height = crumbled_border_size_var;
2141 if (dir == 1 || dir == 2)
2156 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2158 FX + sx * TILEX_VAR + cx,
2159 FY + sy * TILEY_VAR + cy);
2164 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2166 int sx = SCREENX(x), sy = SCREENY(y);
2169 static int xy[4][2] =
2177 if (!IN_LEV_FIELD(x, y))
2180 element = TILE_GFX_ELEMENT(x, y);
2182 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2184 if (!IN_SCR_FIELD(sx, sy))
2187 // crumble field borders towards direct neighbour fields
2188 for (i = 0; i < 4; i++)
2190 int xx = x + xy[i][0];
2191 int yy = y + xy[i][1];
2193 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2196 // check if neighbour field is of same crumble type
2197 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2198 graphic_info[graphic].class ==
2199 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2202 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2205 // crumble inner field corners towards corner neighbour fields
2206 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2207 graphic_info[graphic].anim_frames == 2)
2209 for (i = 0; i < 4; i++)
2211 int dx = (i & 1 ? +1 : -1);
2212 int dy = (i & 2 ? +1 : -1);
2214 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2218 MarkTileDirty(sx, sy);
2220 else // center field is not crumbled -- crumble neighbour fields
2222 // crumble field borders of direct neighbour fields
2223 for (i = 0; i < 4; i++)
2225 int xx = x + xy[i][0];
2226 int yy = y + xy[i][1];
2227 int sxx = sx + xy[i][0];
2228 int syy = sy + xy[i][1];
2230 if (!IN_LEV_FIELD(xx, yy) ||
2231 !IN_SCR_FIELD(sxx, syy))
2234 // do not crumble fields that are being digged or snapped
2235 if (Tile[xx][yy] == EL_EMPTY ||
2236 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2239 element = TILE_GFX_ELEMENT(xx, yy);
2241 if (!IS_CRUMBLED_TILE(xx, yy, element))
2244 graphic = el_act2crm(element, ACTION_DEFAULT);
2246 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2248 MarkTileDirty(sxx, syy);
2251 // crumble inner field corners of corner neighbour fields
2252 for (i = 0; i < 4; i++)
2254 int dx = (i & 1 ? +1 : -1);
2255 int dy = (i & 2 ? +1 : -1);
2261 if (!IN_LEV_FIELD(xx, yy) ||
2262 !IN_SCR_FIELD(sxx, syy))
2265 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2268 element = TILE_GFX_ELEMENT(xx, yy);
2270 if (!IS_CRUMBLED_TILE(xx, yy, element))
2273 graphic = el_act2crm(element, ACTION_DEFAULT);
2275 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2276 graphic_info[graphic].anim_frames == 2)
2277 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2279 MarkTileDirty(sxx, syy);
2284 void DrawLevelFieldCrumbled(int x, int y)
2288 if (!IN_LEV_FIELD(x, y))
2291 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2292 GfxElement[x][y] != EL_UNDEFINED &&
2293 GFX_CRUMBLED(GfxElement[x][y]))
2295 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2300 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2302 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2305 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2308 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2309 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2310 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2311 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2312 int sx = SCREENX(x), sy = SCREENY(y);
2314 DrawGraphic(sx, sy, graphic1, frame1);
2315 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2318 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2320 int sx = SCREENX(x), sy = SCREENY(y);
2321 static int xy[4][2] =
2330 // crumble direct neighbour fields (required for field borders)
2331 for (i = 0; i < 4; i++)
2333 int xx = x + xy[i][0];
2334 int yy = y + xy[i][1];
2335 int sxx = sx + xy[i][0];
2336 int syy = sy + xy[i][1];
2338 if (!IN_LEV_FIELD(xx, yy) ||
2339 !IN_SCR_FIELD(sxx, syy) ||
2340 !GFX_CRUMBLED(Tile[xx][yy]) ||
2344 DrawLevelField(xx, yy);
2347 // crumble corner neighbour fields (required for inner field corners)
2348 for (i = 0; i < 4; i++)
2350 int dx = (i & 1 ? +1 : -1);
2351 int dy = (i & 2 ? +1 : -1);
2357 if (!IN_LEV_FIELD(xx, yy) ||
2358 !IN_SCR_FIELD(sxx, syy) ||
2359 !GFX_CRUMBLED(Tile[xx][yy]) ||
2363 int element = TILE_GFX_ELEMENT(xx, yy);
2364 int graphic = el_act2crm(element, ACTION_DEFAULT);
2366 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2367 graphic_info[graphic].anim_frames == 2)
2368 DrawLevelField(xx, yy);
2372 static int getBorderElement(int x, int y)
2376 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2377 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2378 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2379 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2380 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2381 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2382 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2384 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2385 int steel_position = (x == -1 && y == -1 ? 0 :
2386 x == lev_fieldx && y == -1 ? 1 :
2387 x == -1 && y == lev_fieldy ? 2 :
2388 x == lev_fieldx && y == lev_fieldy ? 3 :
2389 x == -1 || x == lev_fieldx ? 4 :
2390 y == -1 || y == lev_fieldy ? 5 : 6);
2392 return border[steel_position][steel_type];
2395 void DrawScreenElement(int x, int y, int element)
2397 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2398 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2401 void DrawLevelElement(int x, int y, int element)
2403 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2404 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2407 void DrawScreenField(int x, int y)
2409 int lx = LEVELX(x), ly = LEVELY(y);
2410 int element, content;
2412 if (!IN_LEV_FIELD(lx, ly))
2414 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2417 element = getBorderElement(lx, ly);
2419 DrawScreenElement(x, y, element);
2424 element = Tile[lx][ly];
2425 content = Store[lx][ly];
2427 if (IS_MOVING(lx, ly))
2429 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2430 boolean cut_mode = NO_CUTTING;
2432 if (element == EL_QUICKSAND_EMPTYING ||
2433 element == EL_QUICKSAND_FAST_EMPTYING ||
2434 element == EL_MAGIC_WALL_EMPTYING ||
2435 element == EL_BD_MAGIC_WALL_EMPTYING ||
2436 element == EL_DC_MAGIC_WALL_EMPTYING ||
2437 element == EL_AMOEBA_DROPPING)
2438 cut_mode = CUT_ABOVE;
2439 else if (element == EL_QUICKSAND_FILLING ||
2440 element == EL_QUICKSAND_FAST_FILLING ||
2441 element == EL_MAGIC_WALL_FILLING ||
2442 element == EL_BD_MAGIC_WALL_FILLING ||
2443 element == EL_DC_MAGIC_WALL_FILLING)
2444 cut_mode = CUT_BELOW;
2446 if (cut_mode == CUT_ABOVE)
2447 DrawScreenElement(x, y, element);
2449 DrawScreenElement(x, y, EL_EMPTY);
2452 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2453 else if (cut_mode == NO_CUTTING)
2454 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2457 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2459 if (cut_mode == CUT_BELOW &&
2460 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2461 DrawLevelElement(lx, ly + 1, element);
2464 if (content == EL_ACID)
2466 int dir = MovDir[lx][ly];
2467 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2468 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2470 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2472 // prevent target field from being drawn again (but without masking)
2473 // (this would happen if target field is scanned after moving element)
2474 Stop[newlx][newly] = TRUE;
2477 else if (IS_BLOCKED(lx, ly))
2482 boolean cut_mode = NO_CUTTING;
2483 int element_old, content_old;
2485 Blocked2Moving(lx, ly, &oldx, &oldy);
2488 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2489 MovDir[oldx][oldy] == MV_RIGHT);
2491 element_old = Tile[oldx][oldy];
2492 content_old = Store[oldx][oldy];
2494 if (element_old == EL_QUICKSAND_EMPTYING ||
2495 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2496 element_old == EL_MAGIC_WALL_EMPTYING ||
2497 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2498 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2499 element_old == EL_AMOEBA_DROPPING)
2500 cut_mode = CUT_ABOVE;
2502 DrawScreenElement(x, y, EL_EMPTY);
2505 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2507 else if (cut_mode == NO_CUTTING)
2508 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2511 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2514 else if (IS_DRAWABLE(element))
2515 DrawScreenElement(x, y, element);
2517 DrawScreenElement(x, y, EL_EMPTY);
2520 void DrawLevelField(int x, int y)
2522 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2523 DrawScreenField(SCREENX(x), SCREENY(y));
2524 else if (IS_MOVING(x, y))
2528 Moving2Blocked(x, y, &newx, &newy);
2529 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2530 DrawScreenField(SCREENX(newx), SCREENY(newy));
2532 else if (IS_BLOCKED(x, y))
2536 Blocked2Moving(x, y, &oldx, &oldy);
2537 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2538 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2542 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2543 int (*el2img_function)(int), boolean masked,
2544 int element_bits_draw)
2546 int element_base = map_mm_wall_element(element);
2547 int element_bits = (IS_DF_WALL(element) ?
2548 element - EL_DF_WALL_START :
2549 IS_MM_WALL(element) ?
2550 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2551 int graphic = el2img_function(element_base);
2552 int tilesize_draw = tilesize / 2;
2557 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2559 for (i = 0; i < 4; i++)
2561 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2562 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2564 if (!(element_bits_draw & (1 << i)))
2567 if (element_bits & (1 << i))
2570 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2571 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2573 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2574 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2579 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2580 tilesize_draw, tilesize_draw);
2585 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2586 boolean masked, int element_bits_draw)
2588 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2589 element, tilesize, el2edimg, masked, element_bits_draw);
2592 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2593 int (*el2img_function)(int))
2595 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2599 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2602 if (IS_MM_WALL(element))
2604 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2605 element, tilesize, el2edimg, masked, 0x000f);
2609 int graphic = el2edimg(element);
2612 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2614 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2618 void DrawSizedElement(int x, int y, int element, int tilesize)
2620 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2623 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2625 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2628 void DrawMiniElement(int x, int y, int element)
2632 graphic = el2edimg(element);
2633 DrawMiniGraphic(x, y, graphic);
2636 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2639 int x = sx + scroll_x, y = sy + scroll_y;
2641 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2642 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2643 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2644 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2646 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2649 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2651 int x = sx + scroll_x, y = sy + scroll_y;
2653 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2654 DrawMiniElement(sx, sy, EL_EMPTY);
2655 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2656 DrawMiniElement(sx, sy, Tile[x][y]);
2658 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2661 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2662 int x, int y, int xsize, int ysize,
2663 int tile_width, int tile_height)
2667 int dst_x = startx + x * tile_width;
2668 int dst_y = starty + y * tile_height;
2669 int width = graphic_info[graphic].width;
2670 int height = graphic_info[graphic].height;
2671 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2672 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2673 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2674 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2675 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2676 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2677 boolean draw_masked = graphic_info[graphic].draw_masked;
2679 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2681 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2683 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2687 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2688 inner_sx + (x - 1) * tile_width % inner_width);
2689 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2690 inner_sy + (y - 1) * tile_height % inner_height);
2693 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2696 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2700 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2701 int x, int y, int xsize, int ysize,
2704 int font_width = getFontWidth(font_nr);
2705 int font_height = getFontHeight(font_nr);
2707 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2708 font_width, font_height);
2711 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2713 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2714 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2715 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2716 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2717 boolean no_delay = (tape.warp_forward);
2718 unsigned int anim_delay = 0;
2719 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2720 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2721 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2722 int font_width = getFontWidth(font_nr);
2723 int font_height = getFontHeight(font_nr);
2724 int max_xsize = level.envelope[envelope_nr].xsize;
2725 int max_ysize = level.envelope[envelope_nr].ysize;
2726 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2727 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2728 int xend = max_xsize;
2729 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2730 int xstep = (xstart < xend ? 1 : 0);
2731 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2733 int end = MAX(xend - xstart, yend - ystart);
2736 for (i = start; i <= end; i++)
2738 int last_frame = end; // last frame of this "for" loop
2739 int x = xstart + i * xstep;
2740 int y = ystart + i * ystep;
2741 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2742 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2743 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2744 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2747 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2749 BlitScreenToBitmap(backbuffer);
2751 SetDrawtoField(DRAW_TO_BACKBUFFER);
2753 for (yy = 0; yy < ysize; yy++)
2754 for (xx = 0; xx < xsize; xx++)
2755 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2757 DrawTextBuffer(sx + font_width, sy + font_height,
2758 level.envelope[envelope_nr].text, font_nr, max_xsize,
2759 xsize - 2, ysize - 2, 0, mask_mode,
2760 level.envelope[envelope_nr].autowrap,
2761 level.envelope[envelope_nr].centered, FALSE);
2763 redraw_mask |= REDRAW_FIELD;
2766 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2769 ClearAutoRepeatKeyEvents();
2772 void ShowEnvelope(int envelope_nr)
2774 int element = EL_ENVELOPE_1 + envelope_nr;
2775 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2776 int sound_opening = element_info[element].sound[ACTION_OPENING];
2777 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2778 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2779 boolean no_delay = (tape.warp_forward);
2780 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2781 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2782 int anim_mode = graphic_info[graphic].anim_mode;
2783 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2784 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2785 boolean overlay_enabled = GetOverlayEnabled();
2787 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2789 SetOverlayEnabled(FALSE);
2792 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2794 if (anim_mode == ANIM_DEFAULT)
2795 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2797 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2800 Delay_WithScreenUpdates(wait_delay_value);
2802 WaitForEventToContinue();
2805 SetOverlayEnabled(overlay_enabled);
2807 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2809 if (anim_mode != ANIM_NONE)
2810 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2812 if (anim_mode == ANIM_DEFAULT)
2813 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2815 game.envelope_active = FALSE;
2817 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2819 redraw_mask |= REDRAW_FIELD;
2823 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2824 int xsize, int ysize)
2826 if (!global.use_envelope_request ||
2827 request.sort_priority <= 0)
2830 if (request.bitmap == NULL ||
2831 xsize > request.xsize ||
2832 ysize > request.ysize)
2834 if (request.bitmap != NULL)
2835 FreeBitmap(request.bitmap);
2837 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2839 SDL_Surface *surface = request.bitmap->surface;
2841 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2842 Fail("SDLGetNativeSurface() failed");
2845 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2847 SDLFreeBitmapTextures(request.bitmap);
2848 SDLCreateBitmapTextures(request.bitmap);
2850 // set envelope request run-time values
2853 request.xsize = xsize;
2854 request.ysize = ysize;
2857 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2859 if (global.use_envelope_request &&
2860 game.request_active_or_moving &&
2861 request.sort_priority > 0 &&
2862 drawing_target == DRAW_TO_SCREEN &&
2863 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2865 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2866 request.sx, request.sy);
2870 static void setRequestBasePosition(int *x, int *y)
2872 int sx_base, sy_base;
2874 if (request.x != -1)
2875 sx_base = request.x;
2876 else if (request.align == ALIGN_LEFT)
2878 else if (request.align == ALIGN_RIGHT)
2879 sx_base = SX + SXSIZE;
2881 sx_base = SX + SXSIZE / 2;
2883 if (request.y != -1)
2884 sy_base = request.y;
2885 else if (request.valign == VALIGN_TOP)
2887 else if (request.valign == VALIGN_BOTTOM)
2888 sy_base = SY + SYSIZE;
2890 sy_base = SY + SYSIZE / 2;
2896 static void setRequestPositionExt(int *x, int *y, int width, int height,
2897 boolean add_border_size)
2899 int border_size = request.border_size;
2900 int sx_base, sy_base;
2903 setRequestBasePosition(&sx_base, &sy_base);
2905 if (request.align == ALIGN_LEFT)
2907 else if (request.align == ALIGN_RIGHT)
2908 sx = sx_base - width;
2910 sx = sx_base - width / 2;
2912 if (request.valign == VALIGN_TOP)
2914 else if (request.valign == VALIGN_BOTTOM)
2915 sy = sy_base - height;
2917 sy = sy_base - height / 2;
2919 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2920 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2922 if (add_border_size)
2932 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2934 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2937 static void DrawEnvelopeRequest(char *text)
2939 char *text_final = text;
2940 char *text_door_style = NULL;
2941 int graphic = IMG_BACKGROUND_REQUEST;
2942 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2943 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2944 int font_nr = FONT_REQUEST;
2945 int font_width = getFontWidth(font_nr);
2946 int font_height = getFontHeight(font_nr);
2947 int border_size = request.border_size;
2948 int line_spacing = request.line_spacing;
2949 int line_height = font_height + line_spacing;
2950 int max_text_width = request.width - 2 * border_size;
2951 int max_text_height = request.height - 2 * border_size;
2952 int line_length = max_text_width / font_width;
2953 int max_lines = max_text_height / line_height;
2954 int text_width = line_length * font_width;
2955 int width = request.width;
2956 int height = request.height;
2957 int tile_size = MAX(request.step_offset, 1);
2958 int x_steps = width / tile_size;
2959 int y_steps = height / tile_size;
2960 int sx_offset = border_size;
2961 int sy_offset = border_size;
2965 if (request.centered)
2966 sx_offset = (request.width - text_width) / 2;
2968 if (request.wrap_single_words && !request.autowrap)
2970 char *src_text_ptr, *dst_text_ptr;
2972 text_door_style = checked_malloc(2 * strlen(text) + 1);
2974 src_text_ptr = text;
2975 dst_text_ptr = text_door_style;
2977 while (*src_text_ptr)
2979 if (*src_text_ptr == ' ' ||
2980 *src_text_ptr == '?' ||
2981 *src_text_ptr == '!')
2982 *dst_text_ptr++ = '\n';
2984 if (*src_text_ptr != ' ')
2985 *dst_text_ptr++ = *src_text_ptr;
2990 *dst_text_ptr = '\0';
2992 text_final = text_door_style;
2995 setRequestPosition(&sx, &sy, FALSE);
2997 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2999 for (y = 0; y < y_steps; y++)
3000 for (x = 0; x < x_steps; x++)
3001 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3002 x, y, x_steps, y_steps,
3003 tile_size, tile_size);
3005 // force DOOR font inside door area
3006 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3008 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3009 line_length, -1, max_lines, line_spacing, mask_mode,
3010 request.autowrap, request.centered, FALSE);
3014 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3015 RedrawGadget(tool_gadget[i]);
3017 // store readily prepared envelope request for later use when animating
3018 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3020 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3022 if (text_door_style)
3023 free(text_door_style);
3026 static void AnimateEnvelopeRequest(int anim_mode, int action)
3028 int graphic = IMG_BACKGROUND_REQUEST;
3029 boolean draw_masked = graphic_info[graphic].draw_masked;
3030 int delay_value_normal = request.step_delay;
3031 int delay_value_fast = delay_value_normal / 2;
3032 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3033 boolean no_delay = (tape.warp_forward);
3034 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3035 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3036 unsigned int anim_delay = 0;
3038 int tile_size = MAX(request.step_offset, 1);
3039 int max_xsize = request.width / tile_size;
3040 int max_ysize = request.height / tile_size;
3041 int max_xsize_inner = max_xsize - 2;
3042 int max_ysize_inner = max_ysize - 2;
3044 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3045 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3046 int xend = max_xsize_inner;
3047 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3048 int xstep = (xstart < xend ? 1 : 0);
3049 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3051 int end = MAX(xend - xstart, yend - ystart);
3054 if (setup.quick_doors)
3061 for (i = start; i <= end; i++)
3063 int last_frame = end; // last frame of this "for" loop
3064 int x = xstart + i * xstep;
3065 int y = ystart + i * ystep;
3066 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3067 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3068 int xsize_size_left = (xsize - 1) * tile_size;
3069 int ysize_size_top = (ysize - 1) * tile_size;
3070 int max_xsize_pos = (max_xsize - 1) * tile_size;
3071 int max_ysize_pos = (max_ysize - 1) * tile_size;
3072 int width = xsize * tile_size;
3073 int height = ysize * tile_size;
3078 setRequestPosition(&src_x, &src_y, FALSE);
3079 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3081 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3083 for (yy = 0; yy < 2; yy++)
3085 for (xx = 0; xx < 2; xx++)
3087 int src_xx = src_x + xx * max_xsize_pos;
3088 int src_yy = src_y + yy * max_ysize_pos;
3089 int dst_xx = dst_x + xx * xsize_size_left;
3090 int dst_yy = dst_y + yy * ysize_size_top;
3091 int xx_size = (xx ? tile_size : xsize_size_left);
3092 int yy_size = (yy ? tile_size : ysize_size_top);
3095 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3096 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3098 BlitBitmap(bitmap_db_store_2, backbuffer,
3099 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3103 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3105 redraw_mask |= REDRAW_FIELD;
3109 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3112 ClearAutoRepeatKeyEvents();
3115 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3117 int graphic = IMG_BACKGROUND_REQUEST;
3118 int sound_opening = SND_REQUEST_OPENING;
3119 int sound_closing = SND_REQUEST_CLOSING;
3120 int anim_mode_1 = request.anim_mode; // (higher priority)
3121 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3122 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3123 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3124 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3126 if (game_status == GAME_MODE_PLAYING)
3127 BlitScreenToBitmap(backbuffer);
3129 SetDrawtoField(DRAW_TO_BACKBUFFER);
3131 // SetDrawBackgroundMask(REDRAW_NONE);
3133 if (action == ACTION_OPENING)
3135 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3137 if (req_state & REQ_ASK)
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3140 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3141 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3142 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3144 else if (req_state & REQ_CONFIRM)
3146 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3147 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3149 else if (req_state & REQ_PLAYER)
3151 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3152 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3153 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3154 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3157 DrawEnvelopeRequest(text);
3160 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3162 if (action == ACTION_OPENING)
3164 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3166 if (anim_mode == ANIM_DEFAULT)
3167 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3169 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3173 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3175 if (anim_mode != ANIM_NONE)
3176 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3178 if (anim_mode == ANIM_DEFAULT)
3179 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3182 game.envelope_active = FALSE;
3184 if (action == ACTION_CLOSING)
3185 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3187 // SetDrawBackgroundMask(last_draw_background_mask);
3189 redraw_mask |= REDRAW_FIELD;
3193 if (action == ACTION_CLOSING &&
3194 game_status == GAME_MODE_PLAYING &&
3195 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3196 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3199 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3201 if (IS_MM_WALL(element))
3203 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3209 int graphic = el2preimg(element);
3211 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3212 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3217 void DrawLevel(int draw_background_mask)
3221 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3222 SetDrawBackgroundMask(draw_background_mask);
3226 for (x = BX1; x <= BX2; x++)
3227 for (y = BY1; y <= BY2; y++)
3228 DrawScreenField(x, y);
3230 redraw_mask |= REDRAW_FIELD;
3233 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3238 for (x = 0; x < size_x; x++)
3239 for (y = 0; y < size_y; y++)
3240 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3242 redraw_mask |= REDRAW_FIELD;
3245 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3249 for (x = 0; x < size_x; x++)
3250 for (y = 0; y < size_y; y++)
3251 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3253 redraw_mask |= REDRAW_FIELD;
3256 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3258 boolean show_level_border = (BorderElement != EL_EMPTY);
3259 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3260 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3261 int tile_size = preview.tile_size;
3262 int preview_width = preview.xsize * tile_size;
3263 int preview_height = preview.ysize * tile_size;
3264 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3265 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3266 int real_preview_width = real_preview_xsize * tile_size;
3267 int real_preview_height = real_preview_ysize * tile_size;
3268 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3269 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3272 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3275 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3277 dst_x += (preview_width - real_preview_width) / 2;
3278 dst_y += (preview_height - real_preview_height) / 2;
3280 for (x = 0; x < real_preview_xsize; x++)
3282 for (y = 0; y < real_preview_ysize; y++)
3284 int lx = from_x + x + (show_level_border ? -1 : 0);
3285 int ly = from_y + y + (show_level_border ? -1 : 0);
3286 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3287 getBorderElement(lx, ly));
3289 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3290 element, tile_size);
3294 redraw_mask |= REDRAW_FIELD;
3297 #define MICROLABEL_EMPTY 0
3298 #define MICROLABEL_LEVEL_NAME 1
3299 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3300 #define MICROLABEL_LEVEL_AUTHOR 3
3301 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3302 #define MICROLABEL_IMPORTED_FROM 5
3303 #define MICROLABEL_IMPORTED_BY_HEAD 6
3304 #define MICROLABEL_IMPORTED_BY 7
3306 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3308 int max_text_width = SXSIZE;
3309 int font_width = getFontWidth(font_nr);
3311 if (pos->align == ALIGN_CENTER)
3312 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3313 else if (pos->align == ALIGN_RIGHT)
3314 max_text_width = pos->x;
3316 max_text_width = SXSIZE - pos->x;
3318 return max_text_width / font_width;
3321 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3323 char label_text[MAX_OUTPUT_LINESIZE + 1];
3324 int max_len_label_text;
3325 int font_nr = pos->font;
3328 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3331 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3332 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3333 mode == MICROLABEL_IMPORTED_BY_HEAD)
3334 font_nr = pos->font_alt;
3336 max_len_label_text = getMaxTextLength(pos, font_nr);
3338 if (pos->size != -1)
3339 max_len_label_text = pos->size;
3341 for (i = 0; i < max_len_label_text; i++)
3342 label_text[i] = ' ';
3343 label_text[max_len_label_text] = '\0';
3345 if (strlen(label_text) > 0)
3346 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3349 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3350 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3351 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3352 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3353 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3354 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3355 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3356 max_len_label_text);
3357 label_text[max_len_label_text] = '\0';
3359 if (strlen(label_text) > 0)
3360 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3362 redraw_mask |= REDRAW_FIELD;
3365 static void DrawPreviewLevelLabel(int mode)
3367 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3370 static void DrawPreviewLevelInfo(int mode)
3372 if (mode == MICROLABEL_LEVEL_NAME)
3373 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3374 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3375 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3378 static void DrawPreviewLevelExt(boolean restart)
3380 static unsigned int scroll_delay = 0;
3381 static unsigned int label_delay = 0;
3382 static int from_x, from_y, scroll_direction;
3383 static int label_state, label_counter;
3384 unsigned int scroll_delay_value = preview.step_delay;
3385 boolean show_level_border = (BorderElement != EL_EMPTY);
3386 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3387 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3394 if (preview.anim_mode == ANIM_CENTERED)
3396 if (level_xsize > preview.xsize)
3397 from_x = (level_xsize - preview.xsize) / 2;
3398 if (level_ysize > preview.ysize)
3399 from_y = (level_ysize - preview.ysize) / 2;
3402 from_x += preview.xoffset;
3403 from_y += preview.yoffset;
3405 scroll_direction = MV_RIGHT;
3409 DrawPreviewLevelPlayfield(from_x, from_y);
3410 DrawPreviewLevelLabel(label_state);
3412 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3413 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3415 // initialize delay counters
3416 DelayReached(&scroll_delay, 0);
3417 DelayReached(&label_delay, 0);
3419 if (leveldir_current->name)
3421 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3422 char label_text[MAX_OUTPUT_LINESIZE + 1];
3423 int font_nr = pos->font;
3424 int max_len_label_text = getMaxTextLength(pos, font_nr);
3426 if (pos->size != -1)
3427 max_len_label_text = pos->size;
3429 strncpy(label_text, leveldir_current->name, max_len_label_text);
3430 label_text[max_len_label_text] = '\0';
3432 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3433 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3439 // scroll preview level, if needed
3440 if (preview.anim_mode != ANIM_NONE &&
3441 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3442 DelayReached(&scroll_delay, scroll_delay_value))
3444 switch (scroll_direction)
3449 from_x -= preview.step_offset;
3450 from_x = (from_x < 0 ? 0 : from_x);
3453 scroll_direction = MV_UP;
3457 if (from_x < level_xsize - preview.xsize)
3459 from_x += preview.step_offset;
3460 from_x = (from_x > level_xsize - preview.xsize ?
3461 level_xsize - preview.xsize : from_x);
3464 scroll_direction = MV_DOWN;
3470 from_y -= preview.step_offset;
3471 from_y = (from_y < 0 ? 0 : from_y);
3474 scroll_direction = MV_RIGHT;
3478 if (from_y < level_ysize - preview.ysize)
3480 from_y += preview.step_offset;
3481 from_y = (from_y > level_ysize - preview.ysize ?
3482 level_ysize - preview.ysize : from_y);
3485 scroll_direction = MV_LEFT;
3492 DrawPreviewLevelPlayfield(from_x, from_y);
3495 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3496 // redraw micro level label, if needed
3497 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3498 !strEqual(level.author, ANONYMOUS_NAME) &&
3499 !strEqual(level.author, leveldir_current->name) &&
3500 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3502 int max_label_counter = 23;
3504 if (leveldir_current->imported_from != NULL &&
3505 strlen(leveldir_current->imported_from) > 0)
3506 max_label_counter += 14;
3507 if (leveldir_current->imported_by != NULL &&
3508 strlen(leveldir_current->imported_by) > 0)
3509 max_label_counter += 14;
3511 label_counter = (label_counter + 1) % max_label_counter;
3512 label_state = (label_counter >= 0 && label_counter <= 7 ?
3513 MICROLABEL_LEVEL_NAME :
3514 label_counter >= 9 && label_counter <= 12 ?
3515 MICROLABEL_LEVEL_AUTHOR_HEAD :
3516 label_counter >= 14 && label_counter <= 21 ?
3517 MICROLABEL_LEVEL_AUTHOR :
3518 label_counter >= 23 && label_counter <= 26 ?
3519 MICROLABEL_IMPORTED_FROM_HEAD :
3520 label_counter >= 28 && label_counter <= 35 ?
3521 MICROLABEL_IMPORTED_FROM :
3522 label_counter >= 37 && label_counter <= 40 ?
3523 MICROLABEL_IMPORTED_BY_HEAD :
3524 label_counter >= 42 && label_counter <= 49 ?
3525 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3527 if (leveldir_current->imported_from == NULL &&
3528 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3529 label_state == MICROLABEL_IMPORTED_FROM))
3530 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3531 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3533 DrawPreviewLevelLabel(label_state);
3537 void DrawPreviewPlayers(void)
3539 if (game_status != GAME_MODE_MAIN)
3542 // do not draw preview players if level preview redefined, but players aren't
3543 if (preview.redefined && !menu.main.preview_players.redefined)
3546 boolean player_found[MAX_PLAYERS];
3547 int num_players = 0;
3550 for (i = 0; i < MAX_PLAYERS; i++)
3551 player_found[i] = FALSE;
3553 // check which players can be found in the level (simple approach)
3554 for (x = 0; x < lev_fieldx; x++)
3556 for (y = 0; y < lev_fieldy; y++)
3558 int element = level.field[x][y];
3560 if (ELEM_IS_PLAYER(element))
3562 int player_nr = GET_PLAYER_NR(element);
3564 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3566 if (!player_found[player_nr])
3569 player_found[player_nr] = TRUE;
3574 struct TextPosInfo *pos = &menu.main.preview_players;
3575 int tile_size = pos->tile_size;
3576 int border_size = pos->border_size;
3577 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3578 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3579 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3580 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3581 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3582 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3583 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3584 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3585 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3586 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3587 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3588 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3590 // clear area in which the players will be drawn
3591 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3592 max_players_width, max_players_height);
3594 if (!network.enabled && !setup.team_mode)
3597 // only draw players if level is suited for team mode
3598 if (num_players < 2)
3601 // draw all players that were found in the level
3602 for (i = 0; i < MAX_PLAYERS; i++)
3604 if (player_found[i])
3606 int graphic = el2img(EL_PLAYER_1 + i);
3608 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3610 xpos += player_xoffset;
3611 ypos += player_yoffset;
3616 void DrawPreviewLevelInitial(void)
3618 DrawPreviewLevelExt(TRUE);
3619 DrawPreviewPlayers();
3622 void DrawPreviewLevelAnimation(void)
3624 DrawPreviewLevelExt(FALSE);
3627 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3628 int border_size, int font_nr)
3630 int graphic = el2img(EL_PLAYER_1 + player_nr);
3631 int font_height = getFontHeight(font_nr);
3632 int player_height = MAX(tile_size, font_height);
3633 int xoffset_text = tile_size + border_size;
3634 int yoffset_text = (player_height - font_height) / 2;
3635 int yoffset_graphic = (player_height - tile_size) / 2;
3636 char *player_name = getNetworkPlayerName(player_nr + 1);
3638 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3640 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3643 static void DrawNetworkPlayersExt(boolean force)
3645 if (game_status != GAME_MODE_MAIN)
3648 if (!network.connected && !force)
3651 // do not draw network players if level preview redefined, but players aren't
3652 if (preview.redefined && !menu.main.network_players.redefined)
3655 int num_players = 0;
3658 for (i = 0; i < MAX_PLAYERS; i++)
3659 if (stored_player[i].connected_network)
3662 struct TextPosInfo *pos = &menu.main.network_players;
3663 int tile_size = pos->tile_size;
3664 int border_size = pos->border_size;
3665 int xoffset_text = tile_size + border_size;
3666 int font_nr = pos->font;
3667 int font_width = getFontWidth(font_nr);
3668 int font_height = getFontHeight(font_nr);
3669 int player_height = MAX(tile_size, font_height);
3670 int player_yoffset = player_height + border_size;
3671 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3672 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3673 int all_players_height = num_players * player_yoffset - border_size;
3674 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3675 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3676 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3678 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3679 max_players_width, max_players_height);
3681 // first draw local network player ...
3682 for (i = 0; i < MAX_PLAYERS; i++)
3684 if (stored_player[i].connected_network &&
3685 stored_player[i].connected_locally)
3687 char *player_name = getNetworkPlayerName(i + 1);
3688 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3689 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3691 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3693 ypos += player_yoffset;
3697 // ... then draw all other network players
3698 for (i = 0; i < MAX_PLAYERS; i++)
3700 if (stored_player[i].connected_network &&
3701 !stored_player[i].connected_locally)
3703 char *player_name = getNetworkPlayerName(i + 1);
3704 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3705 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3707 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3709 ypos += player_yoffset;
3714 void DrawNetworkPlayers(void)
3716 DrawNetworkPlayersExt(FALSE);
3719 void ClearNetworkPlayers(void)
3721 DrawNetworkPlayersExt(TRUE);
3724 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3725 int graphic, int sync_frame,
3728 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3730 if (mask_mode == USE_MASKING)
3731 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3733 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3736 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3737 int graphic, int sync_frame, int mask_mode)
3739 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3741 if (mask_mode == USE_MASKING)
3742 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3744 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3747 static void DrawGraphicAnimation(int x, int y, int graphic)
3749 int lx = LEVELX(x), ly = LEVELY(y);
3751 if (!IN_SCR_FIELD(x, y))
3754 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3755 graphic, GfxFrame[lx][ly], NO_MASKING);
3757 MarkTileDirty(x, y);
3760 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3762 int lx = LEVELX(x), ly = LEVELY(y);
3764 if (!IN_SCR_FIELD(x, y))
3767 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3768 graphic, GfxFrame[lx][ly], NO_MASKING);
3769 MarkTileDirty(x, y);
3772 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3774 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3777 void DrawLevelElementAnimation(int x, int y, int element)
3779 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3781 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3784 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3786 int sx = SCREENX(x), sy = SCREENY(y);
3788 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3791 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3794 DrawGraphicAnimation(sx, sy, graphic);
3797 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3798 DrawLevelFieldCrumbled(x, y);
3800 if (GFX_CRUMBLED(Tile[x][y]))
3801 DrawLevelFieldCrumbled(x, y);
3805 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3807 int sx = SCREENX(x), sy = SCREENY(y);
3810 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3813 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3815 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3818 DrawGraphicAnimation(sx, sy, graphic);
3820 if (GFX_CRUMBLED(element))
3821 DrawLevelFieldCrumbled(x, y);
3824 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3826 if (player->use_murphy)
3828 // this works only because currently only one player can be "murphy" ...
3829 static int last_horizontal_dir = MV_LEFT;
3830 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3832 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3833 last_horizontal_dir = move_dir;
3835 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3837 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3839 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3845 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3848 static boolean equalGraphics(int graphic1, int graphic2)
3850 struct GraphicInfo *g1 = &graphic_info[graphic1];
3851 struct GraphicInfo *g2 = &graphic_info[graphic2];
3853 return (g1->bitmap == g2->bitmap &&
3854 g1->src_x == g2->src_x &&
3855 g1->src_y == g2->src_y &&
3856 g1->anim_frames == g2->anim_frames &&
3857 g1->anim_delay == g2->anim_delay &&
3858 g1->anim_mode == g2->anim_mode);
3861 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3865 DRAW_PLAYER_STAGE_INIT = 0,
3866 DRAW_PLAYER_STAGE_LAST_FIELD,
3867 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3868 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3869 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3870 DRAW_PLAYER_STAGE_PLAYER,
3872 DRAW_PLAYER_STAGE_PLAYER,
3873 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3875 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3876 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3878 NUM_DRAW_PLAYER_STAGES
3881 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3883 static int static_last_player_graphic[MAX_PLAYERS];
3884 static int static_last_player_frame[MAX_PLAYERS];
3885 static boolean static_player_is_opaque[MAX_PLAYERS];
3886 static boolean draw_player[MAX_PLAYERS];
3887 int pnr = player->index_nr;
3889 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3891 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3892 static_last_player_frame[pnr] = player->Frame;
3893 static_player_is_opaque[pnr] = FALSE;
3895 draw_player[pnr] = TRUE;
3898 if (!draw_player[pnr])
3902 if (!IN_LEV_FIELD(player->jx, player->jy))
3904 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3905 Debug("draw:DrawPlayerExt", "This should never happen!");
3907 draw_player[pnr] = FALSE;
3913 int last_player_graphic = static_last_player_graphic[pnr];
3914 int last_player_frame = static_last_player_frame[pnr];
3915 boolean player_is_opaque = static_player_is_opaque[pnr];
3917 int jx = player->jx;
3918 int jy = player->jy;
3919 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3920 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3921 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3922 int last_jx = (player->is_moving ? jx - dx : jx);
3923 int last_jy = (player->is_moving ? jy - dy : jy);
3924 int next_jx = jx + dx;
3925 int next_jy = jy + dy;
3926 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3927 int sx = SCREENX(jx);
3928 int sy = SCREENY(jy);
3929 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3930 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3931 int element = Tile[jx][jy];
3932 int last_element = Tile[last_jx][last_jy];
3933 int action = (player->is_pushing ? ACTION_PUSHING :
3934 player->is_digging ? ACTION_DIGGING :
3935 player->is_collecting ? ACTION_COLLECTING :
3936 player->is_moving ? ACTION_MOVING :
3937 player->is_snapping ? ACTION_SNAPPING :
3938 player->is_dropping ? ACTION_DROPPING :
3939 player->is_waiting ? player->action_waiting :
3942 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3944 // ------------------------------------------------------------------------
3945 // initialize drawing the player
3946 // ------------------------------------------------------------------------
3948 draw_player[pnr] = FALSE;
3950 // GfxElement[][] is set to the element the player is digging or collecting;
3951 // remove also for off-screen player if the player is not moving anymore
3952 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3953 GfxElement[jx][jy] = EL_UNDEFINED;
3955 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3958 if (element == EL_EXPLOSION)
3961 InitPlayerGfxAnimation(player, action, move_dir);
3963 draw_player[pnr] = TRUE;
3965 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3967 // ------------------------------------------------------------------------
3968 // draw things in the field the player is leaving, if needed
3969 // ------------------------------------------------------------------------
3971 if (!IN_SCR_FIELD(sx, sy))
3972 draw_player[pnr] = FALSE;
3974 if (!player->is_moving)
3977 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3979 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3981 if (last_element == EL_DYNAMITE_ACTIVE ||
3982 last_element == EL_EM_DYNAMITE_ACTIVE ||
3983 last_element == EL_SP_DISK_RED_ACTIVE)
3984 DrawDynamite(last_jx, last_jy);
3986 DrawLevelFieldThruMask(last_jx, last_jy);
3988 else if (last_element == EL_DYNAMITE_ACTIVE ||
3989 last_element == EL_EM_DYNAMITE_ACTIVE ||
3990 last_element == EL_SP_DISK_RED_ACTIVE)
3991 DrawDynamite(last_jx, last_jy);
3993 DrawLevelField(last_jx, last_jy);
3995 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3996 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3998 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4000 // ------------------------------------------------------------------------
4001 // draw things behind the player, if needed
4002 // ------------------------------------------------------------------------
4006 DrawLevelElement(jx, jy, Back[jx][jy]);
4011 if (IS_ACTIVE_BOMB(element))
4013 DrawLevelElement(jx, jy, EL_EMPTY);
4018 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4020 int old_element = GfxElement[jx][jy];
4021 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4022 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4024 if (GFX_CRUMBLED(old_element))
4025 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4027 DrawGraphic(sx, sy, old_graphic, frame);
4029 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4030 static_player_is_opaque[pnr] = TRUE;
4034 GfxElement[jx][jy] = EL_UNDEFINED;
4036 // make sure that pushed elements are drawn with correct frame rate
4037 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4039 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4040 GfxFrame[jx][jy] = player->StepFrame;
4042 DrawLevelField(jx, jy);
4045 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4047 // ------------------------------------------------------------------------
4048 // draw things the player is pushing, if needed
4049 // ------------------------------------------------------------------------
4051 if (!player->is_pushing || !player->is_moving)
4054 int gfx_frame = GfxFrame[jx][jy];
4056 if (!IS_MOVING(jx, jy)) // push movement already finished
4058 element = Tile[next_jx][next_jy];
4059 gfx_frame = GfxFrame[next_jx][next_jy];
4062 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4063 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4064 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4066 // draw background element under pushed element (like the Sokoban field)
4067 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4069 // this allows transparent pushing animation over non-black background
4072 DrawLevelElement(jx, jy, Back[jx][jy]);
4074 DrawLevelElement(jx, jy, EL_EMPTY);
4076 if (Back[next_jx][next_jy])
4077 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4079 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4081 else if (Back[next_jx][next_jy])
4082 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4084 int px = SCREENX(jx), py = SCREENY(jy);
4085 int pxx = (TILEX - ABS(sxx)) * dx;
4086 int pyy = (TILEY - ABS(syy)) * dy;
4089 // do not draw (EM style) pushing animation when pushing is finished
4090 // (two-tile animations usually do not contain start and end frame)
4091 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4092 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4094 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4096 // masked drawing is needed for EMC style (double) movement graphics
4097 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4098 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4101 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4103 // ------------------------------------------------------------------------
4104 // draw player himself
4105 // ------------------------------------------------------------------------
4107 int graphic = getPlayerGraphic(player, move_dir);
4109 // in the case of changed player action or direction, prevent the current
4110 // animation frame from being restarted for identical animations
4111 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4112 player->Frame = last_player_frame;
4114 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4116 if (player_is_opaque)
4117 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4119 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4121 if (SHIELD_ON(player))
4123 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4124 IMG_SHIELD_NORMAL_ACTIVE);
4125 frame = getGraphicAnimationFrame(graphic, -1);
4127 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4130 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4132 // ------------------------------------------------------------------------
4133 // draw things in front of player (active dynamite or dynabombs)
4134 // ------------------------------------------------------------------------
4136 if (IS_ACTIVE_BOMB(element))
4138 int graphic = el2img(element);
4139 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4141 if (game.emulation == EMU_SUPAPLEX)
4142 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4144 DrawGraphicThruMask(sx, sy, graphic, frame);
4147 if (player_is_moving && last_element == EL_EXPLOSION)
4149 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4150 GfxElement[last_jx][last_jy] : EL_EMPTY);
4151 int graphic = el_act2img(element, ACTION_EXPLODING);
4152 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4153 int phase = ExplodePhase[last_jx][last_jy] - 1;
4154 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4157 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4160 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4162 // ------------------------------------------------------------------------
4163 // draw elements the player is just walking/passing through/under
4164 // ------------------------------------------------------------------------
4166 if (player_is_moving)
4168 // handle the field the player is leaving ...
4169 if (IS_ACCESSIBLE_INSIDE(last_element))
4170 DrawLevelField(last_jx, last_jy);
4171 else if (IS_ACCESSIBLE_UNDER(last_element))
4172 DrawLevelFieldThruMask(last_jx, last_jy);
4175 // do not redraw accessible elements if the player is just pushing them
4176 if (!player_is_moving || !player->is_pushing)
4178 // ... and the field the player is entering
4179 if (IS_ACCESSIBLE_INSIDE(element))
4180 DrawLevelField(jx, jy);
4181 else if (IS_ACCESSIBLE_UNDER(element))
4182 DrawLevelFieldThruMask(jx, jy);
4185 MarkTileDirty(sx, sy);
4189 void DrawPlayer(struct PlayerInfo *player)
4193 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4194 DrawPlayerExt(player, i);
4197 void DrawAllPlayers(void)
4201 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4202 for (j = 0; j < MAX_PLAYERS; j++)
4203 if (stored_player[j].active)
4204 DrawPlayerExt(&stored_player[j], i);
4207 void DrawPlayerField(int x, int y)
4209 if (!IS_PLAYER(x, y))
4212 DrawPlayer(PLAYERINFO(x, y));
4215 // ----------------------------------------------------------------------------
4217 void WaitForEventToContinue(void)
4219 boolean first_wait = TRUE;
4220 boolean still_wait = TRUE;
4222 if (program.headless)
4225 // simulate releasing mouse button over last gadget, if still pressed
4227 HandleGadgets(-1, -1, 0);
4229 button_status = MB_RELEASED;
4232 ClearPlayerAction();
4238 if (NextValidEvent(&event))
4242 case EVENT_BUTTONPRESS:
4243 case EVENT_FINGERPRESS:
4247 case EVENT_BUTTONRELEASE:
4248 case EVENT_FINGERRELEASE:
4249 still_wait = first_wait;
4252 case EVENT_KEYPRESS:
4253 case SDL_CONTROLLERBUTTONDOWN:
4254 case SDL_JOYBUTTONDOWN:
4259 HandleOtherEvents(&event);
4263 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4268 if (!PendingEvent())
4273 #define MAX_REQUEST_LINES 13
4274 #define MAX_REQUEST_LINE_FONT1_LEN 7
4275 #define MAX_REQUEST_LINE_FONT2_LEN 10
4277 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4279 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4281 int draw_buffer_last = GetDrawtoField();
4282 int width = request.width;
4283 int height = request.height;
4287 // when showing request dialog after game ended, deactivate game panel
4288 if (game_just_ended)
4289 game.panel.active = FALSE;
4291 game.request_active = TRUE;
4293 setRequestPosition(&sx, &sy, FALSE);
4295 button_status = MB_RELEASED;
4297 request_gadget_id = -1;
4302 boolean event_handled = FALSE;
4304 if (game_just_ended)
4306 SetDrawtoField(draw_buffer_game);
4308 HandleGameActions();
4310 SetDrawtoField(DRAW_TO_BACKBUFFER);
4312 if (global.use_envelope_request)
4314 // copy current state of request area to middle of playfield area
4315 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4323 while (NextValidEvent(&event))
4325 event_handled = TRUE;
4329 case EVENT_BUTTONPRESS:
4330 case EVENT_BUTTONRELEASE:
4331 case EVENT_MOTIONNOTIFY:
4335 if (event.type == EVENT_MOTIONNOTIFY)
4340 motion_status = TRUE;
4341 mx = ((MotionEvent *) &event)->x;
4342 my = ((MotionEvent *) &event)->y;
4346 motion_status = FALSE;
4347 mx = ((ButtonEvent *) &event)->x;
4348 my = ((ButtonEvent *) &event)->y;
4349 if (event.type == EVENT_BUTTONPRESS)
4350 button_status = ((ButtonEvent *) &event)->button;
4352 button_status = MB_RELEASED;
4355 // this sets 'request_gadget_id'
4356 HandleGadgets(mx, my, button_status);
4358 switch (request_gadget_id)
4360 case TOOL_CTRL_ID_YES:
4361 case TOOL_CTRL_ID_TOUCH_YES:
4364 case TOOL_CTRL_ID_NO:
4365 case TOOL_CTRL_ID_TOUCH_NO:
4368 case TOOL_CTRL_ID_CONFIRM:
4369 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4370 result = TRUE | FALSE;
4373 case TOOL_CTRL_ID_PLAYER_1:
4376 case TOOL_CTRL_ID_PLAYER_2:
4379 case TOOL_CTRL_ID_PLAYER_3:
4382 case TOOL_CTRL_ID_PLAYER_4:
4387 // only check clickable animations if no request gadget clicked
4388 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4395 case SDL_WINDOWEVENT:
4396 HandleWindowEvent((WindowEvent *) &event);
4399 case SDL_APP_WILLENTERBACKGROUND:
4400 case SDL_APP_DIDENTERBACKGROUND:
4401 case SDL_APP_WILLENTERFOREGROUND:
4402 case SDL_APP_DIDENTERFOREGROUND:
4403 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4406 case EVENT_KEYPRESS:
4408 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4413 if (req_state & REQ_CONFIRM)
4422 #if defined(KSYM_Rewind)
4423 case KSYM_Rewind: // for Amazon Fire TV remote
4432 #if defined(KSYM_FastForward)
4433 case KSYM_FastForward: // for Amazon Fire TV remote
4439 HandleKeysDebug(key, KEY_PRESSED);
4443 if (req_state & REQ_PLAYER)
4445 int old_player_nr = setup.network_player_nr;
4448 result = old_player_nr + 1;
4453 result = old_player_nr + 1;
4484 case EVENT_FINGERRELEASE:
4485 case EVENT_KEYRELEASE:
4486 ClearPlayerAction();
4489 case SDL_CONTROLLERBUTTONDOWN:
4490 switch (event.cbutton.button)
4492 case SDL_CONTROLLER_BUTTON_A:
4493 case SDL_CONTROLLER_BUTTON_X:
4494 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4495 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4499 case SDL_CONTROLLER_BUTTON_B:
4500 case SDL_CONTROLLER_BUTTON_Y:
4501 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4502 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4503 case SDL_CONTROLLER_BUTTON_BACK:
4508 if (req_state & REQ_PLAYER)
4510 int old_player_nr = setup.network_player_nr;
4513 result = old_player_nr + 1;
4515 switch (event.cbutton.button)
4517 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4518 case SDL_CONTROLLER_BUTTON_Y:
4522 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4523 case SDL_CONTROLLER_BUTTON_B:
4527 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4528 case SDL_CONTROLLER_BUTTON_A:
4532 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4533 case SDL_CONTROLLER_BUTTON_X:
4544 case SDL_CONTROLLERBUTTONUP:
4545 HandleJoystickEvent(&event);
4546 ClearPlayerAction();
4550 HandleOtherEvents(&event);
4555 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4557 int joy = AnyJoystick();
4559 if (joy & JOY_BUTTON_1)
4561 else if (joy & JOY_BUTTON_2)
4564 else if (AnyJoystick())
4566 int joy = AnyJoystick();
4568 if (req_state & REQ_PLAYER)
4572 else if (joy & JOY_RIGHT)
4574 else if (joy & JOY_DOWN)
4576 else if (joy & JOY_LEFT)
4583 if (game_just_ended)
4585 if (global.use_envelope_request)
4587 // copy back current state of pressed buttons inside request area
4588 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4592 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4598 SetDrawtoField(draw_buffer_last);
4600 game.request_active = FALSE;
4605 static boolean RequestDoor(char *text, unsigned int req_state)
4607 int draw_buffer_last = GetDrawtoField();
4608 unsigned int old_door_state;
4609 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4610 int font_nr = FONT_TEXT_2;
4615 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4617 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4618 font_nr = FONT_TEXT_1;
4621 if (game_status == GAME_MODE_PLAYING)
4622 BlitScreenToBitmap(backbuffer);
4624 // disable deactivated drawing when quick-loading level tape recording
4625 if (tape.playing && tape.deactivate_display)
4626 TapeDeactivateDisplayOff(TRUE);
4628 SetMouseCursor(CURSOR_DEFAULT);
4630 // pause network game while waiting for request to answer
4631 if (network.enabled &&
4632 game_status == GAME_MODE_PLAYING &&
4633 !game.all_players_gone &&
4634 req_state & REQUEST_WAIT_FOR_INPUT)
4635 SendToServer_PausePlaying();
4637 old_door_state = GetDoorState();
4639 // simulate releasing mouse button over last gadget, if still pressed
4641 HandleGadgets(-1, -1, 0);
4645 // draw released gadget before proceeding
4648 if (old_door_state & DOOR_OPEN_1)
4650 CloseDoor(DOOR_CLOSE_1);
4652 // save old door content
4653 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4654 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4657 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4658 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4660 // clear door drawing field
4661 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4663 // force DOOR font inside door area
4664 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4666 // write text for request
4667 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4669 char text_line[max_request_line_len + 1];
4675 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4677 tc = *(text_ptr + tx);
4678 // if (!tc || tc == ' ')
4679 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4683 if ((tc == '?' || tc == '!') && tl == 0)
4693 strncpy(text_line, text_ptr, tl);
4696 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4697 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4698 text_line, font_nr);
4700 text_ptr += tl + (tc == ' ' ? 1 : 0);
4701 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4706 if (req_state & REQ_ASK)
4708 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4709 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4710 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4711 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4713 else if (req_state & REQ_CONFIRM)
4715 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4716 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4718 else if (req_state & REQ_PLAYER)
4720 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4721 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4722 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4723 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4726 // copy request gadgets to door backbuffer
4727 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4729 OpenDoor(DOOR_OPEN_1);
4731 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4733 if (game_status == GAME_MODE_PLAYING)
4735 SetPanelBackground();
4736 SetDrawBackgroundMask(REDRAW_DOOR_1);
4740 SetDrawBackgroundMask(REDRAW_FIELD);
4746 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4748 // ---------- handle request buttons ----------
4749 result = RequestHandleEvents(req_state, draw_buffer_last);
4753 if (!(req_state & REQ_STAY_OPEN))
4755 CloseDoor(DOOR_CLOSE_1);
4757 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4758 (req_state & REQ_REOPEN))
4759 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4764 if (game_status == GAME_MODE_PLAYING)
4766 SetPanelBackground();
4767 SetDrawBackgroundMask(REDRAW_DOOR_1);
4771 SetDrawBackgroundMask(REDRAW_FIELD);
4774 // continue network game after request
4775 if (network.enabled &&
4776 game_status == GAME_MODE_PLAYING &&
4777 !game.all_players_gone &&
4778 req_state & REQUEST_WAIT_FOR_INPUT)
4779 SendToServer_ContinuePlaying();
4781 // restore deactivated drawing when quick-loading level tape recording
4782 if (tape.playing && tape.deactivate_display)
4783 TapeDeactivateDisplayOn();
4788 static boolean RequestEnvelope(char *text, unsigned int req_state)
4790 int draw_buffer_last = GetDrawtoField();
4793 if (game_status == GAME_MODE_PLAYING)
4794 BlitScreenToBitmap(backbuffer);
4796 // disable deactivated drawing when quick-loading level tape recording
4797 if (tape.playing && tape.deactivate_display)
4798 TapeDeactivateDisplayOff(TRUE);
4800 SetMouseCursor(CURSOR_DEFAULT);
4802 // pause network game while waiting for request to answer
4803 if (network.enabled &&
4804 game_status == GAME_MODE_PLAYING &&
4805 !game.all_players_gone &&
4806 req_state & REQUEST_WAIT_FOR_INPUT)
4807 SendToServer_PausePlaying();
4809 // simulate releasing mouse button over last gadget, if still pressed
4811 HandleGadgets(-1, -1, 0);
4815 // (replace with setting corresponding request background)
4816 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4817 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4819 // clear door drawing field
4820 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4822 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4824 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4826 if (game_status == GAME_MODE_PLAYING)
4828 SetPanelBackground();
4829 SetDrawBackgroundMask(REDRAW_DOOR_1);
4833 SetDrawBackgroundMask(REDRAW_FIELD);
4839 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4841 // ---------- handle request buttons ----------
4842 result = RequestHandleEvents(req_state, draw_buffer_last);
4846 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4850 if (game_status == GAME_MODE_PLAYING)
4852 SetPanelBackground();
4853 SetDrawBackgroundMask(REDRAW_DOOR_1);
4857 SetDrawBackgroundMask(REDRAW_FIELD);
4860 // continue network game after request
4861 if (network.enabled &&
4862 game_status == GAME_MODE_PLAYING &&
4863 !game.all_players_gone &&
4864 req_state & REQUEST_WAIT_FOR_INPUT)
4865 SendToServer_ContinuePlaying();
4867 // restore deactivated drawing when quick-loading level tape recording
4868 if (tape.playing && tape.deactivate_display)
4869 TapeDeactivateDisplayOn();
4874 boolean Request(char *text, unsigned int req_state)
4876 boolean overlay_enabled = GetOverlayEnabled();
4879 game.request_active_or_moving = TRUE;
4881 SetOverlayEnabled(FALSE);
4883 if (global.use_envelope_request)
4884 result = RequestEnvelope(text, req_state);
4886 result = RequestDoor(text, req_state);
4888 SetOverlayEnabled(overlay_enabled);
4890 game.request_active_or_moving = FALSE;
4895 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4897 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4898 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4901 if (dpo1->sort_priority != dpo2->sort_priority)
4902 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4904 compare_result = dpo1->nr - dpo2->nr;
4906 return compare_result;
4909 void InitGraphicCompatibilityInfo_Doors(void)
4915 struct DoorInfo *door;
4919 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4920 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4922 { -1, -1, -1, NULL }
4924 struct Rect door_rect_list[] =
4926 { DX, DY, DXSIZE, DYSIZE },
4927 { VX, VY, VXSIZE, VYSIZE }
4931 for (i = 0; doors[i].door_token != -1; i++)
4933 int door_token = doors[i].door_token;
4934 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4935 int part_1 = doors[i].part_1;
4936 int part_8 = doors[i].part_8;
4937 int part_2 = part_1 + 1;
4938 int part_3 = part_1 + 2;
4939 struct DoorInfo *door = doors[i].door;
4940 struct Rect *door_rect = &door_rect_list[door_index];
4941 boolean door_gfx_redefined = FALSE;
4943 // check if any door part graphic definitions have been redefined
4945 for (j = 0; door_part_controls[j].door_token != -1; j++)
4947 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4948 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4950 if (dpc->door_token == door_token && fi->redefined)
4951 door_gfx_redefined = TRUE;
4954 // check for old-style door graphic/animation modifications
4956 if (!door_gfx_redefined)
4958 if (door->anim_mode & ANIM_STATIC_PANEL)
4960 door->panel.step_xoffset = 0;
4961 door->panel.step_yoffset = 0;
4964 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4966 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4967 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4968 int num_door_steps, num_panel_steps;
4970 // remove door part graphics other than the two default wings
4972 for (j = 0; door_part_controls[j].door_token != -1; j++)
4974 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4975 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4977 if (dpc->graphic >= part_3 &&
4978 dpc->graphic <= part_8)
4982 // set graphics and screen positions of the default wings
4984 g_part_1->width = door_rect->width;
4985 g_part_1->height = door_rect->height;
4986 g_part_2->width = door_rect->width;
4987 g_part_2->height = door_rect->height;
4988 g_part_2->src_x = door_rect->width;
4989 g_part_2->src_y = g_part_1->src_y;
4991 door->part_2.x = door->part_1.x;
4992 door->part_2.y = door->part_1.y;
4994 if (door->width != -1)
4996 g_part_1->width = door->width;
4997 g_part_2->width = door->width;
4999 // special treatment for graphics and screen position of right wing
5000 g_part_2->src_x += door_rect->width - door->width;
5001 door->part_2.x += door_rect->width - door->width;
5004 if (door->height != -1)
5006 g_part_1->height = door->height;
5007 g_part_2->height = door->height;
5009 // special treatment for graphics and screen position of bottom wing
5010 g_part_2->src_y += door_rect->height - door->height;
5011 door->part_2.y += door_rect->height - door->height;
5014 // set animation delays for the default wings and panels
5016 door->part_1.step_delay = door->step_delay;
5017 door->part_2.step_delay = door->step_delay;
5018 door->panel.step_delay = door->step_delay;
5020 // set animation draw order for the default wings
5022 door->part_1.sort_priority = 2; // draw left wing over ...
5023 door->part_2.sort_priority = 1; // ... right wing
5025 // set animation draw offset for the default wings
5027 if (door->anim_mode & ANIM_HORIZONTAL)
5029 door->part_1.step_xoffset = door->step_offset;
5030 door->part_1.step_yoffset = 0;
5031 door->part_2.step_xoffset = door->step_offset * -1;
5032 door->part_2.step_yoffset = 0;
5034 num_door_steps = g_part_1->width / door->step_offset;
5036 else // ANIM_VERTICAL
5038 door->part_1.step_xoffset = 0;
5039 door->part_1.step_yoffset = door->step_offset;
5040 door->part_2.step_xoffset = 0;
5041 door->part_2.step_yoffset = door->step_offset * -1;
5043 num_door_steps = g_part_1->height / door->step_offset;
5046 // set animation draw offset for the default panels
5048 if (door->step_offset > 1)
5050 num_panel_steps = 2 * door_rect->height / door->step_offset;
5051 door->panel.start_step = num_panel_steps - num_door_steps;
5052 door->panel.start_step_closing = door->panel.start_step;
5056 num_panel_steps = door_rect->height / door->step_offset;
5057 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5058 door->panel.start_step_closing = door->panel.start_step;
5059 door->panel.step_delay *= 2;
5066 void InitDoors(void)
5070 for (i = 0; door_part_controls[i].door_token != -1; i++)
5072 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5073 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5075 // initialize "start_step_opening" and "start_step_closing", if needed
5076 if (dpc->pos->start_step_opening == 0 &&
5077 dpc->pos->start_step_closing == 0)
5079 // dpc->pos->start_step_opening = dpc->pos->start_step;
5080 dpc->pos->start_step_closing = dpc->pos->start_step;
5083 // fill structure for door part draw order (sorted below)
5085 dpo->sort_priority = dpc->pos->sort_priority;
5088 // sort door part controls according to sort_priority and graphic number
5089 qsort(door_part_order, MAX_DOOR_PARTS,
5090 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5093 unsigned int OpenDoor(unsigned int door_state)
5095 if (door_state & DOOR_COPY_BACK)
5097 if (door_state & DOOR_OPEN_1)
5098 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5099 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5101 if (door_state & DOOR_OPEN_2)
5102 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5103 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5105 door_state &= ~DOOR_COPY_BACK;
5108 return MoveDoor(door_state);
5111 unsigned int CloseDoor(unsigned int door_state)
5113 unsigned int old_door_state = GetDoorState();
5115 if (!(door_state & DOOR_NO_COPY_BACK))
5117 if (old_door_state & DOOR_OPEN_1)
5118 BlitBitmap(backbuffer, bitmap_db_door_1,
5119 DX, DY, DXSIZE, DYSIZE, 0, 0);
5121 if (old_door_state & DOOR_OPEN_2)
5122 BlitBitmap(backbuffer, bitmap_db_door_2,
5123 VX, VY, VXSIZE, VYSIZE, 0, 0);
5125 door_state &= ~DOOR_NO_COPY_BACK;
5128 return MoveDoor(door_state);
5131 unsigned int GetDoorState(void)
5133 return MoveDoor(DOOR_GET_STATE);
5136 unsigned int SetDoorState(unsigned int door_state)
5138 return MoveDoor(door_state | DOOR_SET_STATE);
5141 static int euclid(int a, int b)
5143 return (b ? euclid(b, a % b) : a);
5146 unsigned int MoveDoor(unsigned int door_state)
5148 struct Rect door_rect_list[] =
5150 { DX, DY, DXSIZE, DYSIZE },
5151 { VX, VY, VXSIZE, VYSIZE }
5153 static int door1 = DOOR_CLOSE_1;
5154 static int door2 = DOOR_CLOSE_2;
5155 unsigned int door_delay = 0;
5156 unsigned int door_delay_value;
5159 if (door_state == DOOR_GET_STATE)
5160 return (door1 | door2);
5162 if (door_state & DOOR_SET_STATE)
5164 if (door_state & DOOR_ACTION_1)
5165 door1 = door_state & DOOR_ACTION_1;
5166 if (door_state & DOOR_ACTION_2)
5167 door2 = door_state & DOOR_ACTION_2;
5169 return (door1 | door2);
5172 if (!(door_state & DOOR_FORCE_REDRAW))
5174 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5175 door_state &= ~DOOR_OPEN_1;
5176 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5177 door_state &= ~DOOR_CLOSE_1;
5178 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5179 door_state &= ~DOOR_OPEN_2;
5180 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5181 door_state &= ~DOOR_CLOSE_2;
5184 if (global.autoplay_leveldir)
5186 door_state |= DOOR_NO_DELAY;
5187 door_state &= ~DOOR_CLOSE_ALL;
5190 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5191 door_state |= DOOR_NO_DELAY;
5193 if (door_state & DOOR_ACTION)
5195 boolean door_panel_drawn[NUM_DOORS];
5196 boolean panel_has_doors[NUM_DOORS];
5197 boolean door_part_skip[MAX_DOOR_PARTS];
5198 boolean door_part_done[MAX_DOOR_PARTS];
5199 boolean door_part_done_all;
5200 int num_steps[MAX_DOOR_PARTS];
5201 int max_move_delay = 0; // delay for complete animations of all doors
5202 int max_step_delay = 0; // delay (ms) between two animation frames
5203 int num_move_steps = 0; // number of animation steps for all doors
5204 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5205 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5206 int current_move_delay = 0;
5210 for (i = 0; i < NUM_DOORS; i++)
5211 panel_has_doors[i] = FALSE;
5213 for (i = 0; i < MAX_DOOR_PARTS; i++)
5215 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5216 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5217 int door_token = dpc->door_token;
5219 door_part_done[i] = FALSE;
5220 door_part_skip[i] = (!(door_state & door_token) ||
5224 for (i = 0; i < MAX_DOOR_PARTS; i++)
5226 int nr = door_part_order[i].nr;
5227 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5228 struct DoorPartPosInfo *pos = dpc->pos;
5229 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5230 int door_token = dpc->door_token;
5231 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5232 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5233 int step_xoffset = ABS(pos->step_xoffset);
5234 int step_yoffset = ABS(pos->step_yoffset);
5235 int step_delay = pos->step_delay;
5236 int current_door_state = door_state & door_token;
5237 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5238 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5239 boolean part_opening = (is_panel ? door_closing : door_opening);
5240 int start_step = (part_opening ? pos->start_step_opening :
5241 pos->start_step_closing);
5242 float move_xsize = (step_xoffset ? g->width : 0);
5243 float move_ysize = (step_yoffset ? g->height : 0);
5244 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5245 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5246 int move_steps = (move_xsteps && move_ysteps ?
5247 MIN(move_xsteps, move_ysteps) :
5248 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5249 int move_delay = move_steps * step_delay;
5251 if (door_part_skip[nr])
5254 max_move_delay = MAX(max_move_delay, move_delay);
5255 max_step_delay = (max_step_delay == 0 ? step_delay :
5256 euclid(max_step_delay, step_delay));
5257 num_steps[nr] = move_steps;
5261 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5263 panel_has_doors[door_index] = TRUE;
5267 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5269 num_move_steps = max_move_delay / max_step_delay;
5270 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5272 door_delay_value = max_step_delay;
5274 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5276 start = num_move_steps - 1;
5280 // opening door sound has priority over simultaneously closing door
5281 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5283 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5285 if (door_state & DOOR_OPEN_1)
5286 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5287 if (door_state & DOOR_OPEN_2)
5288 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5290 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5292 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5294 if (door_state & DOOR_CLOSE_1)
5295 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5296 if (door_state & DOOR_CLOSE_2)
5297 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5301 for (k = start; k < num_move_steps; k++)
5303 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5305 door_part_done_all = TRUE;
5307 for (i = 0; i < NUM_DOORS; i++)
5308 door_panel_drawn[i] = FALSE;
5310 for (i = 0; i < MAX_DOOR_PARTS; i++)
5312 int nr = door_part_order[i].nr;
5313 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5314 struct DoorPartPosInfo *pos = dpc->pos;
5315 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5316 int door_token = dpc->door_token;
5317 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5318 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5319 boolean is_panel_and_door_has_closed = FALSE;
5320 struct Rect *door_rect = &door_rect_list[door_index];
5321 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5323 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5324 int current_door_state = door_state & door_token;
5325 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5326 boolean door_closing = !door_opening;
5327 boolean part_opening = (is_panel ? door_closing : door_opening);
5328 boolean part_closing = !part_opening;
5329 int start_step = (part_opening ? pos->start_step_opening :
5330 pos->start_step_closing);
5331 int step_delay = pos->step_delay;
5332 int step_factor = step_delay / max_step_delay;
5333 int k1 = (step_factor ? k / step_factor + 1 : k);
5334 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5335 int kk = MAX(0, k2);
5338 int src_x, src_y, src_xx, src_yy;
5339 int dst_x, dst_y, dst_xx, dst_yy;
5342 if (door_part_skip[nr])
5345 if (!(door_state & door_token))
5353 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5354 int kk_door = MAX(0, k2_door);
5355 int sync_frame = kk_door * door_delay_value;
5356 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5358 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5359 &g_src_x, &g_src_y);
5364 if (!door_panel_drawn[door_index])
5366 ClearRectangle(drawto, door_rect->x, door_rect->y,
5367 door_rect->width, door_rect->height);
5369 door_panel_drawn[door_index] = TRUE;
5372 // draw opening or closing door parts
5374 if (pos->step_xoffset < 0) // door part on right side
5377 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5380 if (dst_xx + width > door_rect->width)
5381 width = door_rect->width - dst_xx;
5383 else // door part on left side
5386 dst_xx = pos->x - kk * pos->step_xoffset;
5390 src_xx = ABS(dst_xx);
5394 width = g->width - src_xx;
5396 if (width > door_rect->width)
5397 width = door_rect->width;
5399 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5402 if (pos->step_yoffset < 0) // door part on bottom side
5405 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5408 if (dst_yy + height > door_rect->height)
5409 height = door_rect->height - dst_yy;
5411 else // door part on top side
5414 dst_yy = pos->y - kk * pos->step_yoffset;
5418 src_yy = ABS(dst_yy);
5422 height = g->height - src_yy;
5425 src_x = g_src_x + src_xx;
5426 src_y = g_src_y + src_yy;
5428 dst_x = door_rect->x + dst_xx;
5429 dst_y = door_rect->y + dst_yy;
5431 is_panel_and_door_has_closed =
5434 panel_has_doors[door_index] &&
5435 k >= num_move_steps_doors_only - 1);
5437 if (width >= 0 && width <= g->width &&
5438 height >= 0 && height <= g->height &&
5439 !is_panel_and_door_has_closed)
5441 if (is_panel || !pos->draw_masked)
5442 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5445 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5449 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5451 if ((part_opening && (width < 0 || height < 0)) ||
5452 (part_closing && (width >= g->width && height >= g->height)))
5453 door_part_done[nr] = TRUE;
5455 // continue door part animations, but not panel after door has closed
5456 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5457 door_part_done_all = FALSE;
5460 if (!(door_state & DOOR_NO_DELAY))
5464 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5466 current_move_delay += max_step_delay;
5468 // prevent OS (Windows) from complaining about program not responding
5472 if (door_part_done_all)
5476 if (!(door_state & DOOR_NO_DELAY))
5478 // wait for specified door action post delay
5479 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5480 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5481 else if (door_state & DOOR_ACTION_1)
5482 door_delay_value = door_1.post_delay;
5483 else if (door_state & DOOR_ACTION_2)
5484 door_delay_value = door_2.post_delay;
5486 while (!DelayReached(&door_delay, door_delay_value))
5491 if (door_state & DOOR_ACTION_1)
5492 door1 = door_state & DOOR_ACTION_1;
5493 if (door_state & DOOR_ACTION_2)
5494 door2 = door_state & DOOR_ACTION_2;
5496 // draw masked border over door area
5497 DrawMaskedBorder(REDRAW_DOOR_1);
5498 DrawMaskedBorder(REDRAW_DOOR_2);
5500 ClearAutoRepeatKeyEvents();
5502 return (door1 | door2);
5505 static boolean useSpecialEditorDoor(void)
5507 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5508 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5510 // do not draw special editor door if editor border defined or redefined
5511 if (graphic_info[graphic].bitmap != NULL || redefined)
5514 // do not draw special editor door if global border defined to be empty
5515 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5518 // do not draw special editor door if viewport definitions do not match
5522 EY + EYSIZE != VY + VYSIZE)
5528 void DrawSpecialEditorDoor(void)
5530 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5531 int top_border_width = gfx1->width;
5532 int top_border_height = gfx1->height;
5533 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5534 int ex = EX - outer_border;
5535 int ey = EY - outer_border;
5536 int vy = VY - outer_border;
5537 int exsize = EXSIZE + 2 * outer_border;
5539 if (!useSpecialEditorDoor())
5542 // draw bigger level editor toolbox window
5543 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5544 top_border_width, top_border_height, ex, ey - top_border_height);
5545 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5546 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5548 redraw_mask |= REDRAW_ALL;
5551 void UndrawSpecialEditorDoor(void)
5553 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5554 int top_border_width = gfx1->width;
5555 int top_border_height = gfx1->height;
5556 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5557 int ex = EX - outer_border;
5558 int ey = EY - outer_border;
5559 int ey_top = ey - top_border_height;
5560 int exsize = EXSIZE + 2 * outer_border;
5561 int eysize = EYSIZE + 2 * outer_border;
5563 if (!useSpecialEditorDoor())
5566 // draw normal tape recorder window
5567 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5569 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5570 ex, ey_top, top_border_width, top_border_height,
5572 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5573 ex, ey, exsize, eysize, ex, ey);
5577 // if screen background is set to "[NONE]", clear editor toolbox window
5578 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5579 ClearRectangle(drawto, ex, ey, exsize, eysize);
5582 redraw_mask |= REDRAW_ALL;
5586 // ---------- new tool button stuff -------------------------------------------
5591 struct TextPosInfo *pos;
5593 boolean is_touch_button;
5595 } toolbutton_info[NUM_TOOL_BUTTONS] =
5598 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5599 TOOL_CTRL_ID_YES, FALSE, "yes"
5602 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5603 TOOL_CTRL_ID_NO, FALSE, "no"
5606 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5607 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5610 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5611 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5614 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5615 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5618 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5619 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5622 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5623 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5626 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5627 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5630 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5631 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5634 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5635 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5639 void CreateToolButtons(void)
5643 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5645 int graphic = toolbutton_info[i].graphic;
5646 struct GraphicInfo *gfx = &graphic_info[graphic];
5647 struct TextPosInfo *pos = toolbutton_info[i].pos;
5648 struct GadgetInfo *gi;
5649 Bitmap *deco_bitmap = None;
5650 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5651 unsigned int event_mask = GD_EVENT_RELEASED;
5652 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5653 int base_x = (is_touch_button ? 0 : DX);
5654 int base_y = (is_touch_button ? 0 : DY);
5655 int gd_x = gfx->src_x;
5656 int gd_y = gfx->src_y;
5657 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5658 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5663 if (global.use_envelope_request && !is_touch_button)
5665 setRequestPosition(&base_x, &base_y, TRUE);
5667 // check if request buttons are outside of envelope and fix, if needed
5668 if (x < 0 || x + gfx->width > request.width ||
5669 y < 0 || y + gfx->height > request.height)
5671 if (id == TOOL_CTRL_ID_YES)
5674 y = request.height - 2 * request.border_size - gfx->height;
5676 else if (id == TOOL_CTRL_ID_NO)
5678 x = request.width - 2 * request.border_size - gfx->width;
5679 y = request.height - 2 * request.border_size - gfx->height;
5681 else if (id == TOOL_CTRL_ID_CONFIRM)
5683 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5684 y = request.height - 2 * request.border_size - gfx->height;
5686 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5688 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5690 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5691 y = request.height - 2 * request.border_size - gfx->height * 2;
5693 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5694 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5699 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5701 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5703 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5704 pos->size, &deco_bitmap, &deco_x, &deco_y);
5705 deco_xpos = (gfx->width - pos->size) / 2;
5706 deco_ypos = (gfx->height - pos->size) / 2;
5709 gi = CreateGadget(GDI_CUSTOM_ID, id,
5710 GDI_IMAGE_ID, graphic,
5711 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5714 GDI_WIDTH, gfx->width,
5715 GDI_HEIGHT, gfx->height,
5716 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5717 GDI_STATE, GD_BUTTON_UNPRESSED,
5718 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5719 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5720 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5721 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5722 GDI_DECORATION_SIZE, pos->size, pos->size,
5723 GDI_DECORATION_SHIFTING, 1, 1,
5724 GDI_DIRECT_DRAW, FALSE,
5725 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5726 GDI_EVENT_MASK, event_mask,
5727 GDI_CALLBACK_ACTION, HandleToolButtons,
5731 Fail("cannot create gadget");
5733 tool_gadget[id] = gi;
5737 void FreeToolButtons(void)
5741 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5742 FreeGadget(tool_gadget[i]);
5745 static void UnmapToolButtons(void)
5749 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5750 UnmapGadget(tool_gadget[i]);
5753 static void HandleToolButtons(struct GadgetInfo *gi)
5755 request_gadget_id = gi->custom_id;
5758 static struct Mapping_EM_to_RND_object
5761 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5762 boolean is_backside; // backside of moving element
5768 em_object_mapping_list[GAME_TILE_MAX + 1] =
5771 Zborder, FALSE, FALSE,
5775 Zplayer, FALSE, FALSE,
5784 Ztank, FALSE, FALSE,
5788 Zeater, FALSE, FALSE,
5792 Zdynamite, FALSE, FALSE,
5796 Zboom, FALSE, FALSE,
5801 Xchain, FALSE, FALSE,
5802 EL_DEFAULT, ACTION_EXPLODING, -1
5805 Xboom_bug, FALSE, FALSE,
5806 EL_BUG, ACTION_EXPLODING, -1
5809 Xboom_tank, FALSE, FALSE,
5810 EL_SPACESHIP, ACTION_EXPLODING, -1
5813 Xboom_android, FALSE, FALSE,
5814 EL_EMC_ANDROID, ACTION_OTHER, -1
5817 Xboom_1, FALSE, FALSE,
5818 EL_DEFAULT, ACTION_EXPLODING, -1
5821 Xboom_2, FALSE, FALSE,
5822 EL_DEFAULT, ACTION_EXPLODING, -1
5826 Xblank, TRUE, FALSE,
5831 Xsplash_e, FALSE, FALSE,
5832 EL_ACID_SPLASH_RIGHT, -1, -1
5835 Xsplash_w, FALSE, FALSE,
5836 EL_ACID_SPLASH_LEFT, -1, -1
5840 Xplant, TRUE, FALSE,
5841 EL_EMC_PLANT, -1, -1
5844 Yplant, FALSE, FALSE,
5845 EL_EMC_PLANT, -1, -1
5849 Xacid_1, TRUE, FALSE,
5853 Xacid_2, FALSE, FALSE,
5857 Xacid_3, FALSE, FALSE,
5861 Xacid_4, FALSE, FALSE,
5865 Xacid_5, FALSE, FALSE,
5869 Xacid_6, FALSE, FALSE,
5873 Xacid_7, FALSE, FALSE,
5877 Xacid_8, FALSE, FALSE,
5882 Xfake_acid_1, TRUE, FALSE,
5883 EL_EMC_FAKE_ACID, -1, -1
5886 Xfake_acid_2, FALSE, FALSE,
5887 EL_EMC_FAKE_ACID, -1, -1
5890 Xfake_acid_3, FALSE, FALSE,
5891 EL_EMC_FAKE_ACID, -1, -1
5894 Xfake_acid_4, FALSE, FALSE,
5895 EL_EMC_FAKE_ACID, -1, -1
5898 Xfake_acid_5, FALSE, FALSE,
5899 EL_EMC_FAKE_ACID, -1, -1
5902 Xfake_acid_6, FALSE, FALSE,
5903 EL_EMC_FAKE_ACID, -1, -1
5906 Xfake_acid_7, FALSE, FALSE,
5907 EL_EMC_FAKE_ACID, -1, -1
5910 Xfake_acid_8, FALSE, FALSE,
5911 EL_EMC_FAKE_ACID, -1, -1
5915 Xfake_acid_1_player, FALSE, FALSE,
5916 EL_EMC_FAKE_ACID, -1, -1
5919 Xfake_acid_2_player, FALSE, FALSE,
5920 EL_EMC_FAKE_ACID, -1, -1
5923 Xfake_acid_3_player, FALSE, FALSE,
5924 EL_EMC_FAKE_ACID, -1, -1
5927 Xfake_acid_4_player, FALSE, FALSE,
5928 EL_EMC_FAKE_ACID, -1, -1
5931 Xfake_acid_5_player, FALSE, FALSE,
5932 EL_EMC_FAKE_ACID, -1, -1
5935 Xfake_acid_6_player, FALSE, FALSE,
5936 EL_EMC_FAKE_ACID, -1, -1
5939 Xfake_acid_7_player, FALSE, FALSE,
5940 EL_EMC_FAKE_ACID, -1, -1
5943 Xfake_acid_8_player, FALSE, FALSE,
5944 EL_EMC_FAKE_ACID, -1, -1
5948 Xgrass, TRUE, FALSE,
5949 EL_EMC_GRASS, -1, -1
5952 Ygrass_nB, FALSE, FALSE,
5953 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5956 Ygrass_eB, FALSE, FALSE,
5957 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5960 Ygrass_sB, FALSE, FALSE,
5961 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5964 Ygrass_wB, FALSE, FALSE,
5965 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5973 Ydirt_nB, FALSE, FALSE,
5974 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5977 Ydirt_eB, FALSE, FALSE,
5978 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5981 Ydirt_sB, FALSE, FALSE,
5982 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5985 Ydirt_wB, FALSE, FALSE,
5986 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5990 Xandroid, TRUE, FALSE,
5991 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5994 Xandroid_1_n, FALSE, FALSE,
5995 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5998 Xandroid_2_n, FALSE, FALSE,
5999 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6002 Xandroid_1_e, FALSE, FALSE,
6003 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6006 Xandroid_2_e, FALSE, FALSE,
6007 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6010 Xandroid_1_w, FALSE, FALSE,
6011 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6014 Xandroid_2_w, FALSE, FALSE,
6015 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6018 Xandroid_1_s, FALSE, FALSE,
6019 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6022 Xandroid_2_s, FALSE, FALSE,
6023 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6026 Yandroid_n, FALSE, FALSE,
6027 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6030 Yandroid_nB, FALSE, TRUE,
6031 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6034 Yandroid_ne, FALSE, FALSE,
6035 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6038 Yandroid_neB, FALSE, TRUE,
6039 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6042 Yandroid_e, FALSE, FALSE,
6043 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6046 Yandroid_eB, FALSE, TRUE,
6047 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6050 Yandroid_se, FALSE, FALSE,
6051 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6054 Yandroid_seB, FALSE, TRUE,
6055 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6058 Yandroid_s, FALSE, FALSE,
6059 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6062 Yandroid_sB, FALSE, TRUE,
6063 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6066 Yandroid_sw, FALSE, FALSE,
6067 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6070 Yandroid_swB, FALSE, TRUE,
6071 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6074 Yandroid_w, FALSE, FALSE,
6075 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6078 Yandroid_wB, FALSE, TRUE,
6079 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6082 Yandroid_nw, FALSE, FALSE,
6083 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6086 Yandroid_nwB, FALSE, TRUE,
6087 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6091 Xeater_n, TRUE, FALSE,
6092 EL_YAMYAM_UP, -1, -1
6095 Xeater_e, TRUE, FALSE,
6096 EL_YAMYAM_RIGHT, -1, -1
6099 Xeater_w, TRUE, FALSE,
6100 EL_YAMYAM_LEFT, -1, -1
6103 Xeater_s, TRUE, FALSE,
6104 EL_YAMYAM_DOWN, -1, -1
6107 Yeater_n, FALSE, FALSE,
6108 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6111 Yeater_nB, FALSE, TRUE,
6112 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6115 Yeater_e, FALSE, FALSE,
6116 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6119 Yeater_eB, FALSE, TRUE,
6120 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6123 Yeater_s, FALSE, FALSE,
6124 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6127 Yeater_sB, FALSE, TRUE,
6128 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6131 Yeater_w, FALSE, FALSE,
6132 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6135 Yeater_wB, FALSE, TRUE,
6136 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6139 Yeater_stone, FALSE, FALSE,
6140 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6143 Yeater_spring, FALSE, FALSE,
6144 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6148 Xalien, TRUE, FALSE,
6152 Xalien_pause, FALSE, FALSE,
6156 Yalien_n, FALSE, FALSE,
6157 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6160 Yalien_nB, FALSE, TRUE,
6161 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6164 Yalien_e, FALSE, FALSE,
6165 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6168 Yalien_eB, FALSE, TRUE,
6169 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6172 Yalien_s, FALSE, FALSE,
6173 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6176 Yalien_sB, FALSE, TRUE,
6177 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6180 Yalien_w, FALSE, FALSE,
6181 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6184 Yalien_wB, FALSE, TRUE,
6185 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6188 Yalien_stone, FALSE, FALSE,
6189 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6192 Yalien_spring, FALSE, FALSE,
6193 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6197 Xbug_1_n, TRUE, FALSE,
6201 Xbug_1_e, TRUE, FALSE,
6202 EL_BUG_RIGHT, -1, -1
6205 Xbug_1_s, TRUE, FALSE,
6209 Xbug_1_w, TRUE, FALSE,
6213 Xbug_2_n, FALSE, FALSE,
6217 Xbug_2_e, FALSE, FALSE,
6218 EL_BUG_RIGHT, -1, -1
6221 Xbug_2_s, FALSE, FALSE,
6225 Xbug_2_w, FALSE, FALSE,
6229 Ybug_n, FALSE, FALSE,
6230 EL_BUG, ACTION_MOVING, MV_BIT_UP
6233 Ybug_nB, FALSE, TRUE,
6234 EL_BUG, ACTION_MOVING, MV_BIT_UP
6237 Ybug_e, FALSE, FALSE,
6238 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6241 Ybug_eB, FALSE, TRUE,
6242 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6245 Ybug_s, FALSE, FALSE,
6246 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6249 Ybug_sB, FALSE, TRUE,
6250 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6253 Ybug_w, FALSE, FALSE,
6254 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6257 Ybug_wB, FALSE, TRUE,
6258 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6261 Ybug_w_n, FALSE, FALSE,
6262 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6265 Ybug_n_e, FALSE, FALSE,
6266 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6269 Ybug_e_s, FALSE, FALSE,
6270 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6273 Ybug_s_w, FALSE, FALSE,
6274 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6277 Ybug_e_n, FALSE, FALSE,
6278 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6281 Ybug_s_e, FALSE, FALSE,
6282 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6285 Ybug_w_s, FALSE, FALSE,
6286 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6289 Ybug_n_w, FALSE, FALSE,
6290 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6293 Ybug_stone, FALSE, FALSE,
6294 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6297 Ybug_spring, FALSE, FALSE,
6298 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6302 Xtank_1_n, TRUE, FALSE,
6303 EL_SPACESHIP_UP, -1, -1
6306 Xtank_1_e, TRUE, FALSE,
6307 EL_SPACESHIP_RIGHT, -1, -1
6310 Xtank_1_s, TRUE, FALSE,
6311 EL_SPACESHIP_DOWN, -1, -1
6314 Xtank_1_w, TRUE, FALSE,
6315 EL_SPACESHIP_LEFT, -1, -1
6318 Xtank_2_n, FALSE, FALSE,
6319 EL_SPACESHIP_UP, -1, -1
6322 Xtank_2_e, FALSE, FALSE,
6323 EL_SPACESHIP_RIGHT, -1, -1
6326 Xtank_2_s, FALSE, FALSE,
6327 EL_SPACESHIP_DOWN, -1, -1
6330 Xtank_2_w, FALSE, FALSE,
6331 EL_SPACESHIP_LEFT, -1, -1
6334 Ytank_n, FALSE, FALSE,
6335 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6338 Ytank_nB, FALSE, TRUE,
6339 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6342 Ytank_e, FALSE, FALSE,
6343 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6346 Ytank_eB, FALSE, TRUE,
6347 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6350 Ytank_s, FALSE, FALSE,
6351 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6354 Ytank_sB, FALSE, TRUE,
6355 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6358 Ytank_w, FALSE, FALSE,
6359 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6362 Ytank_wB, FALSE, TRUE,
6363 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6366 Ytank_w_n, FALSE, FALSE,
6367 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6370 Ytank_n_e, FALSE, FALSE,
6371 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6374 Ytank_e_s, FALSE, FALSE,
6375 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6378 Ytank_s_w, FALSE, FALSE,
6379 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6382 Ytank_e_n, FALSE, FALSE,
6383 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6386 Ytank_s_e, FALSE, FALSE,
6387 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6390 Ytank_w_s, FALSE, FALSE,
6391 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6394 Ytank_n_w, FALSE, FALSE,
6395 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6398 Ytank_stone, FALSE, FALSE,
6399 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6402 Ytank_spring, FALSE, FALSE,
6403 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6407 Xemerald, TRUE, FALSE,
6411 Xemerald_pause, FALSE, FALSE,
6415 Xemerald_fall, FALSE, FALSE,
6419 Xemerald_shine, FALSE, FALSE,
6420 EL_EMERALD, ACTION_TWINKLING, -1
6423 Yemerald_s, FALSE, FALSE,
6424 EL_EMERALD, ACTION_FALLING, -1
6427 Yemerald_sB, FALSE, TRUE,
6428 EL_EMERALD, ACTION_FALLING, -1
6431 Yemerald_e, FALSE, FALSE,
6432 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6435 Yemerald_eB, FALSE, TRUE,
6436 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6439 Yemerald_w, FALSE, FALSE,
6440 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6443 Yemerald_wB, FALSE, TRUE,
6444 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6447 Yemerald_blank, FALSE, FALSE,
6448 EL_EMERALD, ACTION_COLLECTING, -1
6452 Xdiamond, TRUE, FALSE,
6456 Xdiamond_pause, FALSE, FALSE,
6460 Xdiamond_fall, FALSE, FALSE,
6464 Xdiamond_shine, FALSE, FALSE,
6465 EL_DIAMOND, ACTION_TWINKLING, -1
6468 Ydiamond_s, FALSE, FALSE,
6469 EL_DIAMOND, ACTION_FALLING, -1
6472 Ydiamond_sB, FALSE, TRUE,
6473 EL_DIAMOND, ACTION_FALLING, -1
6476 Ydiamond_e, FALSE, FALSE,
6477 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6480 Ydiamond_eB, FALSE, TRUE,
6481 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6484 Ydiamond_w, FALSE, FALSE,
6485 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6488 Ydiamond_wB, FALSE, TRUE,
6489 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6492 Ydiamond_blank, FALSE, FALSE,
6493 EL_DIAMOND, ACTION_COLLECTING, -1
6496 Ydiamond_stone, FALSE, FALSE,
6497 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6501 Xstone, TRUE, FALSE,
6505 Xstone_pause, FALSE, FALSE,
6509 Xstone_fall, FALSE, FALSE,
6513 Ystone_s, FALSE, FALSE,
6514 EL_ROCK, ACTION_FALLING, -1
6517 Ystone_sB, FALSE, TRUE,
6518 EL_ROCK, ACTION_FALLING, -1
6521 Ystone_e, FALSE, FALSE,
6522 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6525 Ystone_eB, FALSE, TRUE,
6526 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6529 Ystone_w, FALSE, FALSE,
6530 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6533 Ystone_wB, FALSE, TRUE,
6534 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6542 Xbomb_pause, FALSE, FALSE,
6546 Xbomb_fall, FALSE, FALSE,
6550 Ybomb_s, FALSE, FALSE,
6551 EL_BOMB, ACTION_FALLING, -1
6554 Ybomb_sB, FALSE, TRUE,
6555 EL_BOMB, ACTION_FALLING, -1
6558 Ybomb_e, FALSE, FALSE,
6559 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6562 Ybomb_eB, FALSE, TRUE,
6563 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6566 Ybomb_w, FALSE, FALSE,
6567 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6570 Ybomb_wB, FALSE, TRUE,
6571 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6574 Ybomb_blank, FALSE, FALSE,
6575 EL_BOMB, ACTION_ACTIVATING, -1
6583 Xnut_pause, FALSE, FALSE,
6587 Xnut_fall, FALSE, FALSE,
6591 Ynut_s, FALSE, FALSE,
6592 EL_NUT, ACTION_FALLING, -1
6595 Ynut_sB, FALSE, TRUE,
6596 EL_NUT, ACTION_FALLING, -1
6599 Ynut_e, FALSE, FALSE,
6600 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6603 Ynut_eB, FALSE, TRUE,
6604 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6607 Ynut_w, FALSE, FALSE,
6608 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6611 Ynut_wB, FALSE, TRUE,
6612 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6615 Ynut_stone, FALSE, FALSE,
6616 EL_NUT, ACTION_BREAKING, -1
6620 Xspring, TRUE, FALSE,
6624 Xspring_pause, FALSE, FALSE,
6628 Xspring_e, TRUE, FALSE,
6629 EL_SPRING_RIGHT, -1, -1
6632 Xspring_w, TRUE, FALSE,
6633 EL_SPRING_LEFT, -1, -1
6636 Xspring_fall, FALSE, FALSE,
6640 Yspring_s, FALSE, FALSE,
6641 EL_SPRING, ACTION_FALLING, -1
6644 Yspring_sB, FALSE, TRUE,
6645 EL_SPRING, ACTION_FALLING, -1
6648 Yspring_e, FALSE, FALSE,
6649 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6652 Yspring_eB, FALSE, TRUE,
6653 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6656 Yspring_w, FALSE, FALSE,
6657 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6660 Yspring_wB, FALSE, TRUE,
6661 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6664 Yspring_alien_e, FALSE, FALSE,
6665 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6668 Yspring_alien_eB, FALSE, TRUE,
6669 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6672 Yspring_alien_w, FALSE, FALSE,
6673 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6676 Yspring_alien_wB, FALSE, TRUE,
6677 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6681 Xpush_emerald_e, FALSE, FALSE,
6682 EL_EMERALD, -1, MV_BIT_RIGHT
6685 Xpush_emerald_w, FALSE, FALSE,
6686 EL_EMERALD, -1, MV_BIT_LEFT
6689 Xpush_diamond_e, FALSE, FALSE,
6690 EL_DIAMOND, -1, MV_BIT_RIGHT
6693 Xpush_diamond_w, FALSE, FALSE,
6694 EL_DIAMOND, -1, MV_BIT_LEFT
6697 Xpush_stone_e, FALSE, FALSE,
6698 EL_ROCK, -1, MV_BIT_RIGHT
6701 Xpush_stone_w, FALSE, FALSE,
6702 EL_ROCK, -1, MV_BIT_LEFT
6705 Xpush_bomb_e, FALSE, FALSE,
6706 EL_BOMB, -1, MV_BIT_RIGHT
6709 Xpush_bomb_w, FALSE, FALSE,
6710 EL_BOMB, -1, MV_BIT_LEFT
6713 Xpush_nut_e, FALSE, FALSE,
6714 EL_NUT, -1, MV_BIT_RIGHT
6717 Xpush_nut_w, FALSE, FALSE,
6718 EL_NUT, -1, MV_BIT_LEFT
6721 Xpush_spring_e, FALSE, FALSE,
6722 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6725 Xpush_spring_w, FALSE, FALSE,
6726 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6730 Xdynamite, TRUE, FALSE,
6731 EL_EM_DYNAMITE, -1, -1
6734 Ydynamite_blank, FALSE, FALSE,
6735 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6738 Xdynamite_1, TRUE, FALSE,
6739 EL_EM_DYNAMITE_ACTIVE, -1, -1
6742 Xdynamite_2, FALSE, FALSE,
6743 EL_EM_DYNAMITE_ACTIVE, -1, -1
6746 Xdynamite_3, FALSE, FALSE,
6747 EL_EM_DYNAMITE_ACTIVE, -1, -1
6750 Xdynamite_4, FALSE, FALSE,
6751 EL_EM_DYNAMITE_ACTIVE, -1, -1
6755 Xkey_1, TRUE, FALSE,
6759 Xkey_2, TRUE, FALSE,
6763 Xkey_3, TRUE, FALSE,
6767 Xkey_4, TRUE, FALSE,
6771 Xkey_5, TRUE, FALSE,
6772 EL_EMC_KEY_5, -1, -1
6775 Xkey_6, TRUE, FALSE,
6776 EL_EMC_KEY_6, -1, -1
6779 Xkey_7, TRUE, FALSE,
6780 EL_EMC_KEY_7, -1, -1
6783 Xkey_8, TRUE, FALSE,
6784 EL_EMC_KEY_8, -1, -1
6788 Xdoor_1, TRUE, FALSE,
6789 EL_EM_GATE_1, -1, -1
6792 Xdoor_2, TRUE, FALSE,
6793 EL_EM_GATE_2, -1, -1
6796 Xdoor_3, TRUE, FALSE,
6797 EL_EM_GATE_3, -1, -1
6800 Xdoor_4, TRUE, FALSE,
6801 EL_EM_GATE_4, -1, -1
6804 Xdoor_5, TRUE, FALSE,
6805 EL_EMC_GATE_5, -1, -1
6808 Xdoor_6, TRUE, FALSE,
6809 EL_EMC_GATE_6, -1, -1
6812 Xdoor_7, TRUE, FALSE,
6813 EL_EMC_GATE_7, -1, -1
6816 Xdoor_8, TRUE, FALSE,
6817 EL_EMC_GATE_8, -1, -1
6821 Xfake_door_1, TRUE, FALSE,
6822 EL_EM_GATE_1_GRAY, -1, -1
6825 Xfake_door_2, TRUE, FALSE,
6826 EL_EM_GATE_2_GRAY, -1, -1
6829 Xfake_door_3, TRUE, FALSE,
6830 EL_EM_GATE_3_GRAY, -1, -1
6833 Xfake_door_4, TRUE, FALSE,
6834 EL_EM_GATE_4_GRAY, -1, -1
6837 Xfake_door_5, TRUE, FALSE,
6838 EL_EMC_GATE_5_GRAY, -1, -1
6841 Xfake_door_6, TRUE, FALSE,
6842 EL_EMC_GATE_6_GRAY, -1, -1
6845 Xfake_door_7, TRUE, FALSE,
6846 EL_EMC_GATE_7_GRAY, -1, -1
6849 Xfake_door_8, TRUE, FALSE,
6850 EL_EMC_GATE_8_GRAY, -1, -1
6854 Xballoon, TRUE, FALSE,
6858 Yballoon_n, FALSE, FALSE,
6859 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6862 Yballoon_nB, FALSE, TRUE,
6863 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6866 Yballoon_e, FALSE, FALSE,
6867 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6870 Yballoon_eB, FALSE, TRUE,
6871 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6874 Yballoon_s, FALSE, FALSE,
6875 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6878 Yballoon_sB, FALSE, TRUE,
6879 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6882 Yballoon_w, FALSE, FALSE,
6883 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6886 Yballoon_wB, FALSE, TRUE,
6887 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6891 Xball_1, TRUE, FALSE,
6892 EL_EMC_MAGIC_BALL, -1, -1
6895 Yball_1, FALSE, FALSE,
6896 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6899 Xball_2, FALSE, FALSE,
6900 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6903 Yball_2, FALSE, FALSE,
6904 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6907 Yball_blank, FALSE, FALSE,
6908 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6912 Xamoeba_1, TRUE, FALSE,
6913 EL_AMOEBA_DRY, ACTION_OTHER, -1
6916 Xamoeba_2, FALSE, FALSE,
6917 EL_AMOEBA_DRY, ACTION_OTHER, -1
6920 Xamoeba_3, FALSE, FALSE,
6921 EL_AMOEBA_DRY, ACTION_OTHER, -1
6924 Xamoeba_4, FALSE, FALSE,
6925 EL_AMOEBA_DRY, ACTION_OTHER, -1
6928 Xamoeba_5, TRUE, FALSE,
6929 EL_AMOEBA_WET, ACTION_OTHER, -1
6932 Xamoeba_6, FALSE, FALSE,
6933 EL_AMOEBA_WET, ACTION_OTHER, -1
6936 Xamoeba_7, FALSE, FALSE,
6937 EL_AMOEBA_WET, ACTION_OTHER, -1
6940 Xamoeba_8, FALSE, FALSE,
6941 EL_AMOEBA_WET, ACTION_OTHER, -1
6946 EL_AMOEBA_DROP, ACTION_GROWING, -1
6949 Xdrip_fall, FALSE, FALSE,
6950 EL_AMOEBA_DROP, -1, -1
6953 Xdrip_stretch, FALSE, FALSE,
6954 EL_AMOEBA_DROP, ACTION_FALLING, -1
6957 Xdrip_stretchB, FALSE, TRUE,
6958 EL_AMOEBA_DROP, ACTION_FALLING, -1
6961 Ydrip_1_s, FALSE, FALSE,
6962 EL_AMOEBA_DROP, ACTION_FALLING, -1
6965 Ydrip_1_sB, FALSE, TRUE,
6966 EL_AMOEBA_DROP, ACTION_FALLING, -1
6969 Ydrip_2_s, FALSE, FALSE,
6970 EL_AMOEBA_DROP, ACTION_FALLING, -1
6973 Ydrip_2_sB, FALSE, TRUE,
6974 EL_AMOEBA_DROP, ACTION_FALLING, -1
6978 Xwonderwall, TRUE, FALSE,
6979 EL_MAGIC_WALL, -1, -1
6982 Ywonderwall, FALSE, FALSE,
6983 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6987 Xwheel, TRUE, FALSE,
6988 EL_ROBOT_WHEEL, -1, -1
6991 Ywheel, FALSE, FALSE,
6992 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6996 Xswitch, TRUE, FALSE,
6997 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7000 Yswitch, FALSE, FALSE,
7001 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7005 Xbumper, TRUE, FALSE,
7006 EL_EMC_SPRING_BUMPER, -1, -1
7009 Ybumper, FALSE, FALSE,
7010 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7014 Xacid_nw, TRUE, FALSE,
7015 EL_ACID_POOL_TOPLEFT, -1, -1
7018 Xacid_ne, TRUE, FALSE,
7019 EL_ACID_POOL_TOPRIGHT, -1, -1
7022 Xacid_sw, TRUE, FALSE,
7023 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7026 Xacid_s, TRUE, FALSE,
7027 EL_ACID_POOL_BOTTOM, -1, -1
7030 Xacid_se, TRUE, FALSE,
7031 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7035 Xfake_blank, TRUE, FALSE,
7036 EL_INVISIBLE_WALL, -1, -1
7039 Yfake_blank, FALSE, FALSE,
7040 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7044 Xfake_grass, TRUE, FALSE,
7045 EL_EMC_FAKE_GRASS, -1, -1
7048 Yfake_grass, FALSE, FALSE,
7049 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7053 Xfake_amoeba, TRUE, FALSE,
7054 EL_EMC_DRIPPER, -1, -1
7057 Yfake_amoeba, FALSE, FALSE,
7058 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7062 Xlenses, TRUE, FALSE,
7063 EL_EMC_LENSES, -1, -1
7067 Xmagnify, TRUE, FALSE,
7068 EL_EMC_MAGNIFIER, -1, -1
7073 EL_QUICKSAND_EMPTY, -1, -1
7076 Xsand_stone, TRUE, FALSE,
7077 EL_QUICKSAND_FULL, -1, -1
7080 Xsand_stonein_1, FALSE, TRUE,
7081 EL_ROCK, ACTION_FILLING, -1
7084 Xsand_stonein_2, FALSE, TRUE,
7085 EL_ROCK, ACTION_FILLING, -1
7088 Xsand_stonein_3, FALSE, TRUE,
7089 EL_ROCK, ACTION_FILLING, -1
7092 Xsand_stonein_4, FALSE, TRUE,
7093 EL_ROCK, ACTION_FILLING, -1
7096 Xsand_sandstone_1, FALSE, FALSE,
7097 EL_QUICKSAND_FILLING, -1, -1
7100 Xsand_sandstone_2, FALSE, FALSE,
7101 EL_QUICKSAND_FILLING, -1, -1
7104 Xsand_sandstone_3, FALSE, FALSE,
7105 EL_QUICKSAND_FILLING, -1, -1
7108 Xsand_sandstone_4, FALSE, FALSE,
7109 EL_QUICKSAND_FILLING, -1, -1
7112 Xsand_stonesand_1, FALSE, FALSE,
7113 EL_QUICKSAND_EMPTYING, -1, -1
7116 Xsand_stonesand_2, FALSE, FALSE,
7117 EL_QUICKSAND_EMPTYING, -1, -1
7120 Xsand_stonesand_3, FALSE, FALSE,
7121 EL_QUICKSAND_EMPTYING, -1, -1
7124 Xsand_stonesand_4, FALSE, FALSE,
7125 EL_QUICKSAND_EMPTYING, -1, -1
7128 Xsand_stoneout_1, FALSE, FALSE,
7129 EL_ROCK, ACTION_EMPTYING, -1
7132 Xsand_stoneout_2, FALSE, FALSE,
7133 EL_ROCK, ACTION_EMPTYING, -1
7136 Xsand_stonesand_quickout_1, FALSE, FALSE,
7137 EL_QUICKSAND_EMPTYING, -1, -1
7140 Xsand_stonesand_quickout_2, FALSE, FALSE,
7141 EL_QUICKSAND_EMPTYING, -1, -1
7145 Xslide_ns, TRUE, FALSE,
7146 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7149 Yslide_ns_blank, FALSE, FALSE,
7150 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7153 Xslide_ew, TRUE, FALSE,
7154 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7157 Yslide_ew_blank, FALSE, FALSE,
7158 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7162 Xwind_n, TRUE, FALSE,
7163 EL_BALLOON_SWITCH_UP, -1, -1
7166 Xwind_e, TRUE, FALSE,
7167 EL_BALLOON_SWITCH_RIGHT, -1, -1
7170 Xwind_s, TRUE, FALSE,
7171 EL_BALLOON_SWITCH_DOWN, -1, -1
7174 Xwind_w, TRUE, FALSE,
7175 EL_BALLOON_SWITCH_LEFT, -1, -1
7178 Xwind_any, TRUE, FALSE,
7179 EL_BALLOON_SWITCH_ANY, -1, -1
7182 Xwind_stop, TRUE, FALSE,
7183 EL_BALLOON_SWITCH_NONE, -1, -1
7188 EL_EM_EXIT_CLOSED, -1, -1
7191 Xexit_1, TRUE, FALSE,
7192 EL_EM_EXIT_OPEN, -1, -1
7195 Xexit_2, FALSE, FALSE,
7196 EL_EM_EXIT_OPEN, -1, -1
7199 Xexit_3, FALSE, FALSE,
7200 EL_EM_EXIT_OPEN, -1, -1
7204 Xpause, FALSE, FALSE,
7209 Xwall_1, TRUE, FALSE,
7213 Xwall_2, TRUE, FALSE,
7214 EL_EMC_WALL_14, -1, -1
7217 Xwall_3, TRUE, FALSE,
7218 EL_EMC_WALL_15, -1, -1
7221 Xwall_4, TRUE, FALSE,
7222 EL_EMC_WALL_16, -1, -1
7226 Xroundwall_1, TRUE, FALSE,
7227 EL_WALL_SLIPPERY, -1, -1
7230 Xroundwall_2, TRUE, FALSE,
7231 EL_EMC_WALL_SLIPPERY_2, -1, -1
7234 Xroundwall_3, TRUE, FALSE,
7235 EL_EMC_WALL_SLIPPERY_3, -1, -1
7238 Xroundwall_4, TRUE, FALSE,
7239 EL_EMC_WALL_SLIPPERY_4, -1, -1
7243 Xsteel_1, TRUE, FALSE,
7244 EL_STEELWALL, -1, -1
7247 Xsteel_2, TRUE, FALSE,
7248 EL_EMC_STEELWALL_2, -1, -1
7251 Xsteel_3, TRUE, FALSE,
7252 EL_EMC_STEELWALL_3, -1, -1
7255 Xsteel_4, TRUE, FALSE,
7256 EL_EMC_STEELWALL_4, -1, -1
7260 Xdecor_1, TRUE, FALSE,
7261 EL_EMC_WALL_8, -1, -1
7264 Xdecor_2, TRUE, FALSE,
7265 EL_EMC_WALL_6, -1, -1
7268 Xdecor_3, TRUE, FALSE,
7269 EL_EMC_WALL_4, -1, -1
7272 Xdecor_4, TRUE, FALSE,
7273 EL_EMC_WALL_7, -1, -1
7276 Xdecor_5, TRUE, FALSE,
7277 EL_EMC_WALL_5, -1, -1
7280 Xdecor_6, TRUE, FALSE,
7281 EL_EMC_WALL_9, -1, -1
7284 Xdecor_7, TRUE, FALSE,
7285 EL_EMC_WALL_10, -1, -1
7288 Xdecor_8, TRUE, FALSE,
7289 EL_EMC_WALL_1, -1, -1
7292 Xdecor_9, TRUE, FALSE,
7293 EL_EMC_WALL_2, -1, -1
7296 Xdecor_10, TRUE, FALSE,
7297 EL_EMC_WALL_3, -1, -1
7300 Xdecor_11, TRUE, FALSE,
7301 EL_EMC_WALL_11, -1, -1
7304 Xdecor_12, TRUE, FALSE,
7305 EL_EMC_WALL_12, -1, -1
7309 Xalpha_0, TRUE, FALSE,
7310 EL_CHAR('0'), -1, -1
7313 Xalpha_1, TRUE, FALSE,
7314 EL_CHAR('1'), -1, -1
7317 Xalpha_2, TRUE, FALSE,
7318 EL_CHAR('2'), -1, -1
7321 Xalpha_3, TRUE, FALSE,
7322 EL_CHAR('3'), -1, -1
7325 Xalpha_4, TRUE, FALSE,
7326 EL_CHAR('4'), -1, -1
7329 Xalpha_5, TRUE, FALSE,
7330 EL_CHAR('5'), -1, -1
7333 Xalpha_6, TRUE, FALSE,
7334 EL_CHAR('6'), -1, -1
7337 Xalpha_7, TRUE, FALSE,
7338 EL_CHAR('7'), -1, -1
7341 Xalpha_8, TRUE, FALSE,
7342 EL_CHAR('8'), -1, -1
7345 Xalpha_9, TRUE, FALSE,
7346 EL_CHAR('9'), -1, -1
7349 Xalpha_excla, TRUE, FALSE,
7350 EL_CHAR('!'), -1, -1
7353 Xalpha_apost, TRUE, FALSE,
7354 EL_CHAR('\''), -1, -1
7357 Xalpha_comma, TRUE, FALSE,
7358 EL_CHAR(','), -1, -1
7361 Xalpha_minus, TRUE, FALSE,
7362 EL_CHAR('-'), -1, -1
7365 Xalpha_perio, TRUE, FALSE,
7366 EL_CHAR('.'), -1, -1
7369 Xalpha_colon, TRUE, FALSE,
7370 EL_CHAR(':'), -1, -1
7373 Xalpha_quest, TRUE, FALSE,
7374 EL_CHAR('?'), -1, -1
7377 Xalpha_a, TRUE, FALSE,
7378 EL_CHAR('A'), -1, -1
7381 Xalpha_b, TRUE, FALSE,
7382 EL_CHAR('B'), -1, -1
7385 Xalpha_c, TRUE, FALSE,
7386 EL_CHAR('C'), -1, -1
7389 Xalpha_d, TRUE, FALSE,
7390 EL_CHAR('D'), -1, -1
7393 Xalpha_e, TRUE, FALSE,
7394 EL_CHAR('E'), -1, -1
7397 Xalpha_f, TRUE, FALSE,
7398 EL_CHAR('F'), -1, -1
7401 Xalpha_g, TRUE, FALSE,
7402 EL_CHAR('G'), -1, -1
7405 Xalpha_h, TRUE, FALSE,
7406 EL_CHAR('H'), -1, -1
7409 Xalpha_i, TRUE, FALSE,
7410 EL_CHAR('I'), -1, -1
7413 Xalpha_j, TRUE, FALSE,
7414 EL_CHAR('J'), -1, -1
7417 Xalpha_k, TRUE, FALSE,
7418 EL_CHAR('K'), -1, -1
7421 Xalpha_l, TRUE, FALSE,
7422 EL_CHAR('L'), -1, -1
7425 Xalpha_m, TRUE, FALSE,
7426 EL_CHAR('M'), -1, -1
7429 Xalpha_n, TRUE, FALSE,
7430 EL_CHAR('N'), -1, -1
7433 Xalpha_o, TRUE, FALSE,
7434 EL_CHAR('O'), -1, -1
7437 Xalpha_p, TRUE, FALSE,
7438 EL_CHAR('P'), -1, -1
7441 Xalpha_q, TRUE, FALSE,
7442 EL_CHAR('Q'), -1, -1
7445 Xalpha_r, TRUE, FALSE,
7446 EL_CHAR('R'), -1, -1
7449 Xalpha_s, TRUE, FALSE,
7450 EL_CHAR('S'), -1, -1
7453 Xalpha_t, TRUE, FALSE,
7454 EL_CHAR('T'), -1, -1
7457 Xalpha_u, TRUE, FALSE,
7458 EL_CHAR('U'), -1, -1
7461 Xalpha_v, TRUE, FALSE,
7462 EL_CHAR('V'), -1, -1
7465 Xalpha_w, TRUE, FALSE,
7466 EL_CHAR('W'), -1, -1
7469 Xalpha_x, TRUE, FALSE,
7470 EL_CHAR('X'), -1, -1
7473 Xalpha_y, TRUE, FALSE,
7474 EL_CHAR('Y'), -1, -1
7477 Xalpha_z, TRUE, FALSE,
7478 EL_CHAR('Z'), -1, -1
7481 Xalpha_arrow_e, TRUE, FALSE,
7482 EL_CHAR('>'), -1, -1
7485 Xalpha_arrow_w, TRUE, FALSE,
7486 EL_CHAR('<'), -1, -1
7489 Xalpha_copyr, TRUE, FALSE,
7490 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7494 Ykey_1_blank, FALSE, FALSE,
7495 EL_EM_KEY_1, ACTION_COLLECTING, -1
7498 Ykey_2_blank, FALSE, FALSE,
7499 EL_EM_KEY_2, ACTION_COLLECTING, -1
7502 Ykey_3_blank, FALSE, FALSE,
7503 EL_EM_KEY_3, ACTION_COLLECTING, -1
7506 Ykey_4_blank, FALSE, FALSE,
7507 EL_EM_KEY_4, ACTION_COLLECTING, -1
7510 Ykey_5_blank, FALSE, FALSE,
7511 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7514 Ykey_6_blank, FALSE, FALSE,
7515 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7518 Ykey_7_blank, FALSE, FALSE,
7519 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7522 Ykey_8_blank, FALSE, FALSE,
7523 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7526 Ylenses_blank, FALSE, FALSE,
7527 EL_EMC_LENSES, ACTION_COLLECTING, -1
7530 Ymagnify_blank, FALSE, FALSE,
7531 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7534 Ygrass_blank, FALSE, FALSE,
7535 EL_EMC_GRASS, ACTION_SNAPPING, -1
7538 Ydirt_blank, FALSE, FALSE,
7539 EL_SAND, ACTION_SNAPPING, -1
7548 static struct Mapping_EM_to_RND_player
7557 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7561 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7565 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7569 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7573 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7577 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7581 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7585 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7589 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7593 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7597 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7601 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7605 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7609 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7613 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7617 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7621 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7625 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7629 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7633 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7637 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7641 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7645 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7649 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7653 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7657 EL_PLAYER_1, ACTION_DEFAULT, -1,
7661 EL_PLAYER_2, ACTION_DEFAULT, -1,
7665 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7669 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7673 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7677 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7681 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7685 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7689 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7693 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7697 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7701 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7705 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7709 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7713 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7717 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7721 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7725 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7729 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7733 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7737 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7741 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7745 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7749 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7753 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7757 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7761 EL_PLAYER_3, ACTION_DEFAULT, -1,
7765 EL_PLAYER_4, ACTION_DEFAULT, -1,
7774 int map_element_RND_to_EM_cave(int element_rnd)
7776 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7777 static boolean mapping_initialized = FALSE;
7779 if (!mapping_initialized)
7783 // return "Xalpha_quest" for all undefined elements in mapping array
7784 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7785 mapping_RND_to_EM[i] = Xalpha_quest;
7787 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7788 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7789 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7790 em_object_mapping_list[i].element_em;
7792 mapping_initialized = TRUE;
7795 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7797 Warn("invalid RND level element %d", element_rnd);
7802 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7805 int map_element_EM_to_RND_cave(int element_em_cave)
7807 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7808 static boolean mapping_initialized = FALSE;
7810 if (!mapping_initialized)
7814 // return "EL_UNKNOWN" for all undefined elements in mapping array
7815 for (i = 0; i < GAME_TILE_MAX; i++)
7816 mapping_EM_to_RND[i] = EL_UNKNOWN;
7818 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7819 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7820 em_object_mapping_list[i].element_rnd;
7822 mapping_initialized = TRUE;
7825 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7827 Warn("invalid EM cave element %d", element_em_cave);
7832 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7835 int map_element_EM_to_RND_game(int element_em_game)
7837 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7838 static boolean mapping_initialized = FALSE;
7840 if (!mapping_initialized)
7844 // return "EL_UNKNOWN" for all undefined elements in mapping array
7845 for (i = 0; i < GAME_TILE_MAX; i++)
7846 mapping_EM_to_RND[i] = EL_UNKNOWN;
7848 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7849 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7850 em_object_mapping_list[i].element_rnd;
7852 mapping_initialized = TRUE;
7855 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7857 Warn("invalid EM game element %d", element_em_game);
7862 return mapping_EM_to_RND[element_em_game];
7865 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7867 struct LevelInfo_EM *level_em = level->native_em_level;
7868 struct CAVE *cav = level_em->cav;
7871 for (i = 0; i < GAME_TILE_MAX; i++)
7872 cav->android_array[i] = Cblank;
7874 for (i = 0; i < level->num_android_clone_elements; i++)
7876 int element_rnd = level->android_clone_element[i];
7877 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7879 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7880 if (em_object_mapping_list[j].element_rnd == element_rnd)
7881 cav->android_array[em_object_mapping_list[j].element_em] =
7886 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7888 struct LevelInfo_EM *level_em = level->native_em_level;
7889 struct CAVE *cav = level_em->cav;
7892 level->num_android_clone_elements = 0;
7894 for (i = 0; i < GAME_TILE_MAX; i++)
7896 int element_em_cave = cav->android_array[i];
7898 boolean element_found = FALSE;
7900 if (element_em_cave == Cblank)
7903 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7905 for (j = 0; j < level->num_android_clone_elements; j++)
7906 if (level->android_clone_element[j] == element_rnd)
7907 element_found = TRUE;
7911 level->android_clone_element[level->num_android_clone_elements++] =
7914 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7919 if (level->num_android_clone_elements == 0)
7921 level->num_android_clone_elements = 1;
7922 level->android_clone_element[0] = EL_EMPTY;
7926 int map_direction_RND_to_EM(int direction)
7928 return (direction == MV_UP ? 0 :
7929 direction == MV_RIGHT ? 1 :
7930 direction == MV_DOWN ? 2 :
7931 direction == MV_LEFT ? 3 :
7935 int map_direction_EM_to_RND(int direction)
7937 return (direction == 0 ? MV_UP :
7938 direction == 1 ? MV_RIGHT :
7939 direction == 2 ? MV_DOWN :
7940 direction == 3 ? MV_LEFT :
7944 int map_element_RND_to_SP(int element_rnd)
7946 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7948 if (element_rnd >= EL_SP_START &&
7949 element_rnd <= EL_SP_END)
7950 element_sp = element_rnd - EL_SP_START;
7951 else if (element_rnd == EL_EMPTY_SPACE)
7953 else if (element_rnd == EL_INVISIBLE_WALL)
7959 int map_element_SP_to_RND(int element_sp)
7961 int element_rnd = EL_UNKNOWN;
7963 if (element_sp >= 0x00 &&
7965 element_rnd = EL_SP_START + element_sp;
7966 else if (element_sp == 0x28)
7967 element_rnd = EL_INVISIBLE_WALL;
7972 int map_action_SP_to_RND(int action_sp)
7976 case actActive: return ACTION_ACTIVE;
7977 case actImpact: return ACTION_IMPACT;
7978 case actExploding: return ACTION_EXPLODING;
7979 case actDigging: return ACTION_DIGGING;
7980 case actSnapping: return ACTION_SNAPPING;
7981 case actCollecting: return ACTION_COLLECTING;
7982 case actPassing: return ACTION_PASSING;
7983 case actPushing: return ACTION_PUSHING;
7984 case actDropping: return ACTION_DROPPING;
7986 default: return ACTION_DEFAULT;
7990 int map_element_RND_to_MM(int element_rnd)
7992 return (element_rnd >= EL_MM_START_1 &&
7993 element_rnd <= EL_MM_END_1 ?
7994 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7996 element_rnd >= EL_MM_START_2 &&
7997 element_rnd <= EL_MM_END_2 ?
7998 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8000 element_rnd >= EL_CHAR_START &&
8001 element_rnd <= EL_CHAR_END ?
8002 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8004 element_rnd >= EL_MM_RUNTIME_START &&
8005 element_rnd <= EL_MM_RUNTIME_END ?
8006 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8008 element_rnd >= EL_MM_DUMMY_START &&
8009 element_rnd <= EL_MM_DUMMY_END ?
8010 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8012 EL_MM_EMPTY_NATIVE);
8015 int map_element_MM_to_RND(int element_mm)
8017 return (element_mm == EL_MM_EMPTY_NATIVE ||
8018 element_mm == EL_DF_EMPTY_NATIVE ?
8021 element_mm >= EL_MM_START_1_NATIVE &&
8022 element_mm <= EL_MM_END_1_NATIVE ?
8023 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8025 element_mm >= EL_MM_START_2_NATIVE &&
8026 element_mm <= EL_MM_END_2_NATIVE ?
8027 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8029 element_mm >= EL_MM_CHAR_START_NATIVE &&
8030 element_mm <= EL_MM_CHAR_END_NATIVE ?
8031 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8033 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8034 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8035 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8037 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8038 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8039 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8044 int map_action_MM_to_RND(int action_mm)
8046 // all MM actions are defined to exactly match their RND counterparts
8050 int map_sound_MM_to_RND(int sound_mm)
8054 case SND_MM_GAME_LEVELTIME_CHARGING:
8055 return SND_GAME_LEVELTIME_CHARGING;
8057 case SND_MM_GAME_HEALTH_CHARGING:
8058 return SND_GAME_HEALTH_CHARGING;
8061 return SND_UNDEFINED;
8065 int map_mm_wall_element(int element)
8067 return (element >= EL_MM_STEEL_WALL_START &&
8068 element <= EL_MM_STEEL_WALL_END ?
8071 element >= EL_MM_WOODEN_WALL_START &&
8072 element <= EL_MM_WOODEN_WALL_END ?
8075 element >= EL_MM_ICE_WALL_START &&
8076 element <= EL_MM_ICE_WALL_END ?
8079 element >= EL_MM_AMOEBA_WALL_START &&
8080 element <= EL_MM_AMOEBA_WALL_END ?
8083 element >= EL_DF_STEEL_WALL_START &&
8084 element <= EL_DF_STEEL_WALL_END ?
8087 element >= EL_DF_WOODEN_WALL_START &&
8088 element <= EL_DF_WOODEN_WALL_END ?
8094 int map_mm_wall_element_editor(int element)
8098 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8099 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8100 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8101 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8102 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8103 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8105 default: return element;
8109 int get_next_element(int element)
8113 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8114 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8115 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8116 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8117 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8118 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8119 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8120 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8121 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8122 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8123 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8125 default: return element;
8129 int el2img_mm(int element_mm)
8131 return el2img(map_element_MM_to_RND(element_mm));
8134 int el_act_dir2img(int element, int action, int direction)
8136 element = GFX_ELEMENT(element);
8137 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8139 // direction_graphic[][] == graphic[] for undefined direction graphics
8140 return element_info[element].direction_graphic[action][direction];
8143 static int el_act_dir2crm(int element, int action, int direction)
8145 element = GFX_ELEMENT(element);
8146 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8148 // direction_graphic[][] == graphic[] for undefined direction graphics
8149 return element_info[element].direction_crumbled[action][direction];
8152 int el_act2img(int element, int action)
8154 element = GFX_ELEMENT(element);
8156 return element_info[element].graphic[action];
8159 int el_act2crm(int element, int action)
8161 element = GFX_ELEMENT(element);
8163 return element_info[element].crumbled[action];
8166 int el_dir2img(int element, int direction)
8168 element = GFX_ELEMENT(element);
8170 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8173 int el2baseimg(int element)
8175 return element_info[element].graphic[ACTION_DEFAULT];
8178 int el2img(int element)
8180 element = GFX_ELEMENT(element);
8182 return element_info[element].graphic[ACTION_DEFAULT];
8185 int el2edimg(int element)
8187 element = GFX_ELEMENT(element);
8189 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8192 int el2preimg(int element)
8194 element = GFX_ELEMENT(element);
8196 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8199 int el2panelimg(int element)
8201 element = GFX_ELEMENT(element);
8203 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8206 int font2baseimg(int font_nr)
8208 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8211 int getBeltNrFromBeltElement(int element)
8213 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8214 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8215 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8218 int getBeltNrFromBeltActiveElement(int element)
8220 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8221 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8222 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8225 int getBeltNrFromBeltSwitchElement(int element)
8227 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8228 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8229 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8232 int getBeltDirNrFromBeltElement(int element)
8234 static int belt_base_element[4] =
8236 EL_CONVEYOR_BELT_1_LEFT,
8237 EL_CONVEYOR_BELT_2_LEFT,
8238 EL_CONVEYOR_BELT_3_LEFT,
8239 EL_CONVEYOR_BELT_4_LEFT
8242 int belt_nr = getBeltNrFromBeltElement(element);
8243 int belt_dir_nr = element - belt_base_element[belt_nr];
8245 return (belt_dir_nr % 3);
8248 int getBeltDirNrFromBeltSwitchElement(int element)
8250 static int belt_base_element[4] =
8252 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8253 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8254 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8255 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8258 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8259 int belt_dir_nr = element - belt_base_element[belt_nr];
8261 return (belt_dir_nr % 3);
8264 int getBeltDirFromBeltElement(int element)
8266 static int belt_move_dir[3] =
8273 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8275 return belt_move_dir[belt_dir_nr];
8278 int getBeltDirFromBeltSwitchElement(int element)
8280 static int belt_move_dir[3] =
8287 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8289 return belt_move_dir[belt_dir_nr];
8292 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8294 static int belt_base_element[4] =
8296 EL_CONVEYOR_BELT_1_LEFT,
8297 EL_CONVEYOR_BELT_2_LEFT,
8298 EL_CONVEYOR_BELT_3_LEFT,
8299 EL_CONVEYOR_BELT_4_LEFT
8302 return belt_base_element[belt_nr] + belt_dir_nr;
8305 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8307 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8309 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8312 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8314 static int belt_base_element[4] =
8316 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8317 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8318 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8319 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8322 return belt_base_element[belt_nr] + belt_dir_nr;
8325 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8327 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8329 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8332 boolean swapTiles_EM(boolean is_pre_emc_cave)
8334 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8337 boolean getTeamMode_EM(void)
8339 return game.team_mode || network_playing;
8342 boolean isActivePlayer_EM(int player_nr)
8344 return stored_player[player_nr].active;
8347 unsigned int InitRND(int seed)
8349 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8350 return InitEngineRandom_EM(seed);
8351 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8352 return InitEngineRandom_SP(seed);
8353 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8354 return InitEngineRandom_MM(seed);
8356 return InitEngineRandom_RND(seed);
8359 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8360 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8362 static int get_effective_element_EM(int tile, int frame_em)
8364 int element = object_mapping[tile].element_rnd;
8365 int action = object_mapping[tile].action;
8366 boolean is_backside = object_mapping[tile].is_backside;
8367 boolean action_removing = (action == ACTION_DIGGING ||
8368 action == ACTION_SNAPPING ||
8369 action == ACTION_COLLECTING);
8377 return (frame_em > 5 ? EL_EMPTY : element);
8383 else // frame_em == 7
8394 case Ydiamond_stone:
8398 case Xdrip_stretchB:
8414 case Ymagnify_blank:
8417 case Xsand_stonein_1:
8418 case Xsand_stonein_2:
8419 case Xsand_stonein_3:
8420 case Xsand_stonein_4:
8424 return (is_backside || action_removing ? EL_EMPTY : element);
8429 static boolean check_linear_animation_EM(int tile)
8433 case Xsand_stonesand_1:
8434 case Xsand_stonesand_quickout_1:
8435 case Xsand_sandstone_1:
8436 case Xsand_stonein_1:
8437 case Xsand_stoneout_1:
8465 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8466 boolean has_crumbled_graphics,
8467 int crumbled, int sync_frame)
8469 // if element can be crumbled, but certain action graphics are just empty
8470 // space (like instantly snapping sand to empty space in 1 frame), do not
8471 // treat these empty space graphics as crumbled graphics in EMC engine
8472 if (crumbled == IMG_EMPTY_SPACE)
8473 has_crumbled_graphics = FALSE;
8475 if (has_crumbled_graphics)
8477 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8478 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8479 g_crumbled->anim_delay,
8480 g_crumbled->anim_mode,
8481 g_crumbled->anim_start_frame,
8484 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8485 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8487 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8488 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8490 g_em->has_crumbled_graphics = TRUE;
8494 g_em->crumbled_bitmap = NULL;
8495 g_em->crumbled_src_x = 0;
8496 g_em->crumbled_src_y = 0;
8497 g_em->crumbled_border_size = 0;
8498 g_em->crumbled_tile_size = 0;
8500 g_em->has_crumbled_graphics = FALSE;
8505 void ResetGfxAnimation_EM(int x, int y, int tile)
8511 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8512 int tile, int frame_em, int x, int y)
8514 int action = object_mapping[tile].action;
8515 int direction = object_mapping[tile].direction;
8516 int effective_element = get_effective_element_EM(tile, frame_em);
8517 int graphic = (direction == MV_NONE ?
8518 el_act2img(effective_element, action) :
8519 el_act_dir2img(effective_element, action, direction));
8520 struct GraphicInfo *g = &graphic_info[graphic];
8522 boolean action_removing = (action == ACTION_DIGGING ||
8523 action == ACTION_SNAPPING ||
8524 action == ACTION_COLLECTING);
8525 boolean action_moving = (action == ACTION_FALLING ||
8526 action == ACTION_MOVING ||
8527 action == ACTION_PUSHING ||
8528 action == ACTION_EATING ||
8529 action == ACTION_FILLING ||
8530 action == ACTION_EMPTYING);
8531 boolean action_falling = (action == ACTION_FALLING ||
8532 action == ACTION_FILLING ||
8533 action == ACTION_EMPTYING);
8535 // special case: graphic uses "2nd movement tile" and has defined
8536 // 7 frames for movement animation (or less) => use default graphic
8537 // for last (8th) frame which ends the movement animation
8538 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8540 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8541 graphic = (direction == MV_NONE ?
8542 el_act2img(effective_element, action) :
8543 el_act_dir2img(effective_element, action, direction));
8545 g = &graphic_info[graphic];
8548 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8552 else if (action_moving)
8554 boolean is_backside = object_mapping[tile].is_backside;
8558 int direction = object_mapping[tile].direction;
8559 int move_dir = (action_falling ? MV_DOWN : direction);
8564 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8565 if (g->double_movement && frame_em == 0)
8569 if (move_dir == MV_LEFT)
8570 GfxFrame[x - 1][y] = GfxFrame[x][y];
8571 else if (move_dir == MV_RIGHT)
8572 GfxFrame[x + 1][y] = GfxFrame[x][y];
8573 else if (move_dir == MV_UP)
8574 GfxFrame[x][y - 1] = GfxFrame[x][y];
8575 else if (move_dir == MV_DOWN)
8576 GfxFrame[x][y + 1] = GfxFrame[x][y];
8583 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8584 if (tile == Xsand_stonesand_quickout_1 ||
8585 tile == Xsand_stonesand_quickout_2)
8589 if (graphic_info[graphic].anim_global_sync)
8590 sync_frame = FrameCounter;
8591 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8592 sync_frame = GfxFrame[x][y];
8594 sync_frame = 0; // playfield border (pseudo steel)
8596 SetRandomAnimationValue(x, y);
8598 int frame = getAnimationFrame(g->anim_frames,
8601 g->anim_start_frame,
8604 g_em->unique_identifier =
8605 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8608 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8609 int tile, int frame_em, int x, int y)
8611 int action = object_mapping[tile].action;
8612 int direction = object_mapping[tile].direction;
8613 boolean is_backside = object_mapping[tile].is_backside;
8614 int effective_element = get_effective_element_EM(tile, frame_em);
8615 int effective_action = action;
8616 int graphic = (direction == MV_NONE ?
8617 el_act2img(effective_element, effective_action) :
8618 el_act_dir2img(effective_element, effective_action,
8620 int crumbled = (direction == MV_NONE ?
8621 el_act2crm(effective_element, effective_action) :
8622 el_act_dir2crm(effective_element, effective_action,
8624 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8625 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8626 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8627 struct GraphicInfo *g = &graphic_info[graphic];
8630 // special case: graphic uses "2nd movement tile" and has defined
8631 // 7 frames for movement animation (or less) => use default graphic
8632 // for last (8th) frame which ends the movement animation
8633 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8635 effective_action = ACTION_DEFAULT;
8636 graphic = (direction == MV_NONE ?
8637 el_act2img(effective_element, effective_action) :
8638 el_act_dir2img(effective_element, effective_action,
8640 crumbled = (direction == MV_NONE ?
8641 el_act2crm(effective_element, effective_action) :
8642 el_act_dir2crm(effective_element, effective_action,
8645 g = &graphic_info[graphic];
8648 if (graphic_info[graphic].anim_global_sync)
8649 sync_frame = FrameCounter;
8650 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8651 sync_frame = GfxFrame[x][y];
8653 sync_frame = 0; // playfield border (pseudo steel)
8655 SetRandomAnimationValue(x, y);
8657 int frame = getAnimationFrame(g->anim_frames,
8660 g->anim_start_frame,
8663 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8664 g->double_movement && is_backside);
8666 // (updating the "crumbled" graphic definitions is probably not really needed,
8667 // as animations for crumbled graphics can't be longer than one EMC cycle)
8668 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8672 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8673 int player_nr, int anim, int frame_em)
8675 int element = player_mapping[player_nr][anim].element_rnd;
8676 int action = player_mapping[player_nr][anim].action;
8677 int direction = player_mapping[player_nr][anim].direction;
8678 int graphic = (direction == MV_NONE ?
8679 el_act2img(element, action) :
8680 el_act_dir2img(element, action, direction));
8681 struct GraphicInfo *g = &graphic_info[graphic];
8684 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8686 stored_player[player_nr].StepFrame = frame_em;
8688 sync_frame = stored_player[player_nr].Frame;
8690 int frame = getAnimationFrame(g->anim_frames,
8693 g->anim_start_frame,
8696 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8697 &g_em->src_x, &g_em->src_y, FALSE);
8700 void InitGraphicInfo_EM(void)
8704 // always start with reliable default values
8705 for (i = 0; i < GAME_TILE_MAX; i++)
8707 object_mapping[i].element_rnd = EL_UNKNOWN;
8708 object_mapping[i].is_backside = FALSE;
8709 object_mapping[i].action = ACTION_DEFAULT;
8710 object_mapping[i].direction = MV_NONE;
8713 // always start with reliable default values
8714 for (p = 0; p < MAX_PLAYERS; p++)
8716 for (i = 0; i < PLY_MAX; i++)
8718 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8719 player_mapping[p][i].action = ACTION_DEFAULT;
8720 player_mapping[p][i].direction = MV_NONE;
8724 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8726 int e = em_object_mapping_list[i].element_em;
8728 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8729 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8731 if (em_object_mapping_list[i].action != -1)
8732 object_mapping[e].action = em_object_mapping_list[i].action;
8734 if (em_object_mapping_list[i].direction != -1)
8735 object_mapping[e].direction =
8736 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8739 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8741 int a = em_player_mapping_list[i].action_em;
8742 int p = em_player_mapping_list[i].player_nr;
8744 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8746 if (em_player_mapping_list[i].action != -1)
8747 player_mapping[p][a].action = em_player_mapping_list[i].action;
8749 if (em_player_mapping_list[i].direction != -1)
8750 player_mapping[p][a].direction =
8751 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8754 for (i = 0; i < GAME_TILE_MAX; i++)
8756 int element = object_mapping[i].element_rnd;
8757 int action = object_mapping[i].action;
8758 int direction = object_mapping[i].direction;
8759 boolean is_backside = object_mapping[i].is_backside;
8760 boolean action_exploding = ((action == ACTION_EXPLODING ||
8761 action == ACTION_SMASHED_BY_ROCK ||
8762 action == ACTION_SMASHED_BY_SPRING) &&
8763 element != EL_DIAMOND);
8764 boolean action_active = (action == ACTION_ACTIVE);
8765 boolean action_other = (action == ACTION_OTHER);
8767 for (j = 0; j < 8; j++)
8769 int effective_element = get_effective_element_EM(i, j);
8770 int effective_action = (j < 7 ? action :
8771 i == Xdrip_stretch ? action :
8772 i == Xdrip_stretchB ? action :
8773 i == Ydrip_1_s ? action :
8774 i == Ydrip_1_sB ? action :
8775 i == Yball_1 ? action :
8776 i == Xball_2 ? action :
8777 i == Yball_2 ? action :
8778 i == Yball_blank ? action :
8779 i == Ykey_1_blank ? action :
8780 i == Ykey_2_blank ? action :
8781 i == Ykey_3_blank ? action :
8782 i == Ykey_4_blank ? action :
8783 i == Ykey_5_blank ? action :
8784 i == Ykey_6_blank ? action :
8785 i == Ykey_7_blank ? action :
8786 i == Ykey_8_blank ? action :
8787 i == Ylenses_blank ? action :
8788 i == Ymagnify_blank ? action :
8789 i == Ygrass_blank ? action :
8790 i == Ydirt_blank ? action :
8791 i == Xsand_stonein_1 ? action :
8792 i == Xsand_stonein_2 ? action :
8793 i == Xsand_stonein_3 ? action :
8794 i == Xsand_stonein_4 ? action :
8795 i == Xsand_stoneout_1 ? action :
8796 i == Xsand_stoneout_2 ? action :
8797 i == Xboom_android ? ACTION_EXPLODING :
8798 action_exploding ? ACTION_EXPLODING :
8799 action_active ? action :
8800 action_other ? action :
8802 int graphic = (el_act_dir2img(effective_element, effective_action,
8804 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8806 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8807 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8808 boolean has_action_graphics = (graphic != base_graphic);
8809 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8810 struct GraphicInfo *g = &graphic_info[graphic];
8811 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8814 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8815 boolean special_animation = (action != ACTION_DEFAULT &&
8816 g->anim_frames == 3 &&
8817 g->anim_delay == 2 &&
8818 g->anim_mode & ANIM_LINEAR);
8819 int sync_frame = (i == Xdrip_stretch ? 7 :
8820 i == Xdrip_stretchB ? 7 :
8821 i == Ydrip_2_s ? j + 8 :
8822 i == Ydrip_2_sB ? j + 8 :
8831 i == Xfake_acid_1 ? 0 :
8832 i == Xfake_acid_2 ? 10 :
8833 i == Xfake_acid_3 ? 20 :
8834 i == Xfake_acid_4 ? 30 :
8835 i == Xfake_acid_5 ? 40 :
8836 i == Xfake_acid_6 ? 50 :
8837 i == Xfake_acid_7 ? 60 :
8838 i == Xfake_acid_8 ? 70 :
8839 i == Xfake_acid_1_player ? 0 :
8840 i == Xfake_acid_2_player ? 10 :
8841 i == Xfake_acid_3_player ? 20 :
8842 i == Xfake_acid_4_player ? 30 :
8843 i == Xfake_acid_5_player ? 40 :
8844 i == Xfake_acid_6_player ? 50 :
8845 i == Xfake_acid_7_player ? 60 :
8846 i == Xfake_acid_8_player ? 70 :
8848 i == Yball_2 ? j + 8 :
8849 i == Yball_blank ? j + 1 :
8850 i == Ykey_1_blank ? j + 1 :
8851 i == Ykey_2_blank ? j + 1 :
8852 i == Ykey_3_blank ? j + 1 :
8853 i == Ykey_4_blank ? j + 1 :
8854 i == Ykey_5_blank ? j + 1 :
8855 i == Ykey_6_blank ? j + 1 :
8856 i == Ykey_7_blank ? j + 1 :
8857 i == Ykey_8_blank ? j + 1 :
8858 i == Ylenses_blank ? j + 1 :
8859 i == Ymagnify_blank ? j + 1 :
8860 i == Ygrass_blank ? j + 1 :
8861 i == Ydirt_blank ? j + 1 :
8862 i == Xamoeba_1 ? 0 :
8863 i == Xamoeba_2 ? 1 :
8864 i == Xamoeba_3 ? 2 :
8865 i == Xamoeba_4 ? 3 :
8866 i == Xamoeba_5 ? 0 :
8867 i == Xamoeba_6 ? 1 :
8868 i == Xamoeba_7 ? 2 :
8869 i == Xamoeba_8 ? 3 :
8870 i == Xexit_2 ? j + 8 :
8871 i == Xexit_3 ? j + 16 :
8872 i == Xdynamite_1 ? 0 :
8873 i == Xdynamite_2 ? 8 :
8874 i == Xdynamite_3 ? 16 :
8875 i == Xdynamite_4 ? 24 :
8876 i == Xsand_stonein_1 ? j + 1 :
8877 i == Xsand_stonein_2 ? j + 9 :
8878 i == Xsand_stonein_3 ? j + 17 :
8879 i == Xsand_stonein_4 ? j + 25 :
8880 i == Xsand_stoneout_1 && j == 0 ? 0 :
8881 i == Xsand_stoneout_1 && j == 1 ? 0 :
8882 i == Xsand_stoneout_1 && j == 2 ? 1 :
8883 i == Xsand_stoneout_1 && j == 3 ? 2 :
8884 i == Xsand_stoneout_1 && j == 4 ? 2 :
8885 i == Xsand_stoneout_1 && j == 5 ? 3 :
8886 i == Xsand_stoneout_1 && j == 6 ? 4 :
8887 i == Xsand_stoneout_1 && j == 7 ? 4 :
8888 i == Xsand_stoneout_2 && j == 0 ? 5 :
8889 i == Xsand_stoneout_2 && j == 1 ? 6 :
8890 i == Xsand_stoneout_2 && j == 2 ? 7 :
8891 i == Xsand_stoneout_2 && j == 3 ? 8 :
8892 i == Xsand_stoneout_2 && j == 4 ? 9 :
8893 i == Xsand_stoneout_2 && j == 5 ? 11 :
8894 i == Xsand_stoneout_2 && j == 6 ? 13 :
8895 i == Xsand_stoneout_2 && j == 7 ? 15 :
8896 i == Xboom_bug && j == 1 ? 2 :
8897 i == Xboom_bug && j == 2 ? 2 :
8898 i == Xboom_bug && j == 3 ? 4 :
8899 i == Xboom_bug && j == 4 ? 4 :
8900 i == Xboom_bug && j == 5 ? 2 :
8901 i == Xboom_bug && j == 6 ? 2 :
8902 i == Xboom_bug && j == 7 ? 0 :
8903 i == Xboom_tank && j == 1 ? 2 :
8904 i == Xboom_tank && j == 2 ? 2 :
8905 i == Xboom_tank && j == 3 ? 4 :
8906 i == Xboom_tank && j == 4 ? 4 :
8907 i == Xboom_tank && j == 5 ? 2 :
8908 i == Xboom_tank && j == 6 ? 2 :
8909 i == Xboom_tank && j == 7 ? 0 :
8910 i == Xboom_android && j == 7 ? 6 :
8911 i == Xboom_1 && j == 1 ? 2 :
8912 i == Xboom_1 && j == 2 ? 2 :
8913 i == Xboom_1 && j == 3 ? 4 :
8914 i == Xboom_1 && j == 4 ? 4 :
8915 i == Xboom_1 && j == 5 ? 6 :
8916 i == Xboom_1 && j == 6 ? 6 :
8917 i == Xboom_1 && j == 7 ? 8 :
8918 i == Xboom_2 && j == 0 ? 8 :
8919 i == Xboom_2 && j == 1 ? 8 :
8920 i == Xboom_2 && j == 2 ? 10 :
8921 i == Xboom_2 && j == 3 ? 10 :
8922 i == Xboom_2 && j == 4 ? 10 :
8923 i == Xboom_2 && j == 5 ? 12 :
8924 i == Xboom_2 && j == 6 ? 12 :
8925 i == Xboom_2 && j == 7 ? 12 :
8926 special_animation && j == 4 ? 3 :
8927 effective_action != action ? 0 :
8929 int frame = getAnimationFrame(g->anim_frames,
8932 g->anim_start_frame,
8935 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8936 g->double_movement && is_backside);
8938 g_em->bitmap = src_bitmap;
8939 g_em->src_x = src_x;
8940 g_em->src_y = src_y;
8941 g_em->src_offset_x = 0;
8942 g_em->src_offset_y = 0;
8943 g_em->dst_offset_x = 0;
8944 g_em->dst_offset_y = 0;
8945 g_em->width = TILEX;
8946 g_em->height = TILEY;
8948 g_em->preserve_background = FALSE;
8950 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8953 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8954 effective_action == ACTION_MOVING ||
8955 effective_action == ACTION_PUSHING ||
8956 effective_action == ACTION_EATING)) ||
8957 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8958 effective_action == ACTION_EMPTYING)))
8961 (effective_action == ACTION_FALLING ||
8962 effective_action == ACTION_FILLING ||
8963 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8964 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8965 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8966 int num_steps = (i == Ydrip_1_s ? 16 :
8967 i == Ydrip_1_sB ? 16 :
8968 i == Ydrip_2_s ? 16 :
8969 i == Ydrip_2_sB ? 16 :
8970 i == Xsand_stonein_1 ? 32 :
8971 i == Xsand_stonein_2 ? 32 :
8972 i == Xsand_stonein_3 ? 32 :
8973 i == Xsand_stonein_4 ? 32 :
8974 i == Xsand_stoneout_1 ? 16 :
8975 i == Xsand_stoneout_2 ? 16 : 8);
8976 int cx = ABS(dx) * (TILEX / num_steps);
8977 int cy = ABS(dy) * (TILEY / num_steps);
8978 int step_frame = (i == Ydrip_2_s ? j + 8 :
8979 i == Ydrip_2_sB ? j + 8 :
8980 i == Xsand_stonein_2 ? j + 8 :
8981 i == Xsand_stonein_3 ? j + 16 :
8982 i == Xsand_stonein_4 ? j + 24 :
8983 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8984 int step = (is_backside ? step_frame : num_steps - step_frame);
8986 if (is_backside) // tile where movement starts
8988 if (dx < 0 || dy < 0)
8990 g_em->src_offset_x = cx * step;
8991 g_em->src_offset_y = cy * step;
8995 g_em->dst_offset_x = cx * step;
8996 g_em->dst_offset_y = cy * step;
8999 else // tile where movement ends
9001 if (dx < 0 || dy < 0)
9003 g_em->dst_offset_x = cx * step;
9004 g_em->dst_offset_y = cy * step;
9008 g_em->src_offset_x = cx * step;
9009 g_em->src_offset_y = cy * step;
9013 g_em->width = TILEX - cx * step;
9014 g_em->height = TILEY - cy * step;
9017 // create unique graphic identifier to decide if tile must be redrawn
9018 /* bit 31 - 16 (16 bit): EM style graphic
9019 bit 15 - 12 ( 4 bit): EM style frame
9020 bit 11 - 6 ( 6 bit): graphic width
9021 bit 5 - 0 ( 6 bit): graphic height */
9022 g_em->unique_identifier =
9023 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9027 for (i = 0; i < GAME_TILE_MAX; i++)
9029 for (j = 0; j < 8; j++)
9031 int element = object_mapping[i].element_rnd;
9032 int action = object_mapping[i].action;
9033 int direction = object_mapping[i].direction;
9034 boolean is_backside = object_mapping[i].is_backside;
9035 int graphic_action = el_act_dir2img(element, action, direction);
9036 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9038 if ((action == ACTION_SMASHED_BY_ROCK ||
9039 action == ACTION_SMASHED_BY_SPRING ||
9040 action == ACTION_EATING) &&
9041 graphic_action == graphic_default)
9043 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9044 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9045 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9046 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9049 // no separate animation for "smashed by rock" -- use rock instead
9050 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9051 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9053 g_em->bitmap = g_xx->bitmap;
9054 g_em->src_x = g_xx->src_x;
9055 g_em->src_y = g_xx->src_y;
9056 g_em->src_offset_x = g_xx->src_offset_x;
9057 g_em->src_offset_y = g_xx->src_offset_y;
9058 g_em->dst_offset_x = g_xx->dst_offset_x;
9059 g_em->dst_offset_y = g_xx->dst_offset_y;
9060 g_em->width = g_xx->width;
9061 g_em->height = g_xx->height;
9062 g_em->unique_identifier = g_xx->unique_identifier;
9065 g_em->preserve_background = TRUE;
9070 for (p = 0; p < MAX_PLAYERS; p++)
9072 for (i = 0; i < PLY_MAX; i++)
9074 int element = player_mapping[p][i].element_rnd;
9075 int action = player_mapping[p][i].action;
9076 int direction = player_mapping[p][i].direction;
9078 for (j = 0; j < 8; j++)
9080 int effective_element = element;
9081 int effective_action = action;
9082 int graphic = (direction == MV_NONE ?
9083 el_act2img(effective_element, effective_action) :
9084 el_act_dir2img(effective_element, effective_action,
9086 struct GraphicInfo *g = &graphic_info[graphic];
9087 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9091 int frame = getAnimationFrame(g->anim_frames,
9094 g->anim_start_frame,
9097 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9099 g_em->bitmap = src_bitmap;
9100 g_em->src_x = src_x;
9101 g_em->src_y = src_y;
9102 g_em->src_offset_x = 0;
9103 g_em->src_offset_y = 0;
9104 g_em->dst_offset_x = 0;
9105 g_em->dst_offset_y = 0;
9106 g_em->width = TILEX;
9107 g_em->height = TILEY;
9113 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9114 boolean any_player_moving,
9115 boolean any_player_snapping,
9116 boolean any_player_dropping)
9118 if (frame == 7 && !any_player_dropping)
9120 if (!local_player->was_waiting)
9122 if (!CheckSaveEngineSnapshotToList())
9125 local_player->was_waiting = TRUE;
9128 else if (any_player_moving || any_player_snapping || any_player_dropping)
9130 local_player->was_waiting = FALSE;
9134 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9135 boolean murphy_is_dropping)
9137 if (murphy_is_waiting)
9139 if (!local_player->was_waiting)
9141 if (!CheckSaveEngineSnapshotToList())
9144 local_player->was_waiting = TRUE;
9149 local_player->was_waiting = FALSE;
9153 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9154 boolean button_released)
9156 if (button_released)
9158 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9159 CheckSaveEngineSnapshotToList();
9161 else if (element_clicked)
9163 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9164 CheckSaveEngineSnapshotToList();
9166 game.snapshot.changed_action = TRUE;
9170 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9171 boolean any_player_moving,
9172 boolean any_player_snapping,
9173 boolean any_player_dropping)
9175 if (tape.single_step && tape.recording && !tape.pausing)
9176 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9177 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9179 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9180 any_player_snapping, any_player_dropping);
9182 return tape.pausing;
9185 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9186 boolean murphy_is_dropping)
9188 boolean murphy_starts_dropping = FALSE;
9191 for (i = 0; i < MAX_PLAYERS; i++)
9192 if (stored_player[i].force_dropping)
9193 murphy_starts_dropping = TRUE;
9195 if (tape.single_step && tape.recording && !tape.pausing)
9196 if (murphy_is_waiting && !murphy_starts_dropping)
9197 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9199 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9202 void CheckSingleStepMode_MM(boolean element_clicked,
9203 boolean button_released)
9205 if (tape.single_step && tape.recording && !tape.pausing)
9206 if (button_released)
9207 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9209 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9212 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9213 int graphic, int sync_frame, int x, int y)
9215 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9217 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9220 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9222 return (IS_NEXT_FRAME(sync_frame, graphic));
9225 int getGraphicInfo_Delay(int graphic)
9227 return graphic_info[graphic].anim_delay;
9230 void PlayMenuSoundExt(int sound)
9232 if (sound == SND_UNDEFINED)
9235 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9236 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9239 if (IS_LOOP_SOUND(sound))
9240 PlaySoundLoop(sound);
9245 void PlayMenuSound(void)
9247 PlayMenuSoundExt(menu.sound[game_status]);
9250 void PlayMenuSoundStereo(int sound, int stereo_position)
9252 if (sound == SND_UNDEFINED)
9255 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9256 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9259 if (IS_LOOP_SOUND(sound))
9260 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9262 PlaySoundStereo(sound, stereo_position);
9265 void PlayMenuSoundIfLoopExt(int sound)
9267 if (sound == SND_UNDEFINED)
9270 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9271 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9274 if (IS_LOOP_SOUND(sound))
9275 PlaySoundLoop(sound);
9278 void PlayMenuSoundIfLoop(void)
9280 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9283 void PlayMenuMusicExt(int music)
9285 if (music == MUS_UNDEFINED)
9288 if (!setup.sound_music)
9291 if (IS_LOOP_MUSIC(music))
9292 PlayMusicLoop(music);
9297 void PlayMenuMusic(void)
9299 char *curr_music = getCurrentlyPlayingMusicFilename();
9300 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9302 if (!strEqual(curr_music, next_music))
9303 PlayMenuMusicExt(menu.music[game_status]);
9306 void PlayMenuSoundsAndMusic(void)
9312 static void FadeMenuSounds(void)
9317 static void FadeMenuMusic(void)
9319 char *curr_music = getCurrentlyPlayingMusicFilename();
9320 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9322 if (!strEqual(curr_music, next_music))
9326 void FadeMenuSoundsAndMusic(void)
9332 void PlaySoundActivating(void)
9335 PlaySound(SND_MENU_ITEM_ACTIVATING);
9339 void PlaySoundSelecting(void)
9342 PlaySound(SND_MENU_ITEM_SELECTING);
9346 void ToggleFullscreenIfNeeded(void)
9348 // if setup and video fullscreen state are already matching, nothing do do
9349 if (setup.fullscreen == video.fullscreen_enabled ||
9350 !video.fullscreen_available)
9353 SDLSetWindowFullscreen(setup.fullscreen);
9355 // set setup value according to successfully changed fullscreen mode
9356 setup.fullscreen = video.fullscreen_enabled;
9359 void ChangeWindowScalingIfNeeded(void)
9361 // if setup and video window scaling are already matching, nothing do do
9362 if (setup.window_scaling_percent == video.window_scaling_percent ||
9363 video.fullscreen_enabled)
9366 SDLSetWindowScaling(setup.window_scaling_percent);
9368 // set setup value according to successfully changed window scaling
9369 setup.window_scaling_percent = video.window_scaling_percent;
9372 void ChangeVsyncModeIfNeeded(void)
9374 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9375 int video_vsync_mode = video.vsync_mode;
9377 // if setup and video vsync mode are already matching, nothing do do
9378 if (setup_vsync_mode == video_vsync_mode)
9381 // if renderer is using OpenGL, vsync mode can directly be changed
9382 SDLSetScreenVsyncMode(setup.vsync_mode);
9384 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9385 if (video.vsync_mode == video_vsync_mode)
9387 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9389 // save backbuffer content which gets lost when re-creating screen
9390 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9392 // force re-creating screen and renderer to set new vsync mode
9393 video.fullscreen_enabled = !setup.fullscreen;
9395 // when creating new renderer, destroy textures linked to old renderer
9396 FreeAllImageTextures(); // needs old renderer to free the textures
9398 // re-create screen and renderer (including change of vsync mode)
9399 ChangeVideoModeIfNeeded(setup.fullscreen);
9401 // set setup value according to successfully changed fullscreen mode
9402 setup.fullscreen = video.fullscreen_enabled;
9404 // restore backbuffer content from temporary backbuffer backup bitmap
9405 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9406 FreeBitmap(tmp_backbuffer);
9408 // update visible window/screen
9409 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9411 // when changing vsync mode, re-create textures for new renderer
9412 InitImageTextures();
9415 // set setup value according to successfully changed vsync mode
9416 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9419 static void JoinRectangles(int *x, int *y, int *width, int *height,
9420 int x2, int y2, int width2, int height2)
9422 // do not join with "off-screen" rectangle
9423 if (x2 == -1 || y2 == -1)
9428 *width = MAX(*width, width2);
9429 *height = MAX(*height, height2);
9432 void SetAnimStatus(int anim_status_new)
9434 if (anim_status_new == GAME_MODE_MAIN)
9435 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9436 else if (anim_status_new == GAME_MODE_NAMES)
9437 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9438 else if (anim_status_new == GAME_MODE_SCORES)
9439 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9441 global.anim_status_next = anim_status_new;
9443 // directly set screen modes that are entered without fading
9444 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9445 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9446 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9447 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9448 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9449 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9450 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9451 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9452 global.anim_status = global.anim_status_next;
9455 void SetGameStatus(int game_status_new)
9457 if (game_status_new != game_status)
9458 game_status_last_screen = game_status;
9460 game_status = game_status_new;
9462 SetAnimStatus(game_status_new);
9465 void SetFontStatus(int game_status_new)
9467 static int last_game_status = -1;
9469 if (game_status_new != -1)
9471 // set game status for font use after storing last game status
9472 last_game_status = game_status;
9473 game_status = game_status_new;
9477 // reset game status after font use from last stored game status
9478 game_status = last_game_status;
9482 void ResetFontStatus(void)
9487 void SetLevelSetInfo(char *identifier, int level_nr)
9489 setString(&levelset.identifier, identifier);
9491 levelset.level_nr = level_nr;
9494 boolean CheckIfAllViewportsHaveChanged(void)
9496 // if game status has not changed, viewports have not changed either
9497 if (game_status == game_status_last)
9500 // check if all viewports have changed with current game status
9502 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9503 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9504 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9505 int new_real_sx = vp_playfield->x;
9506 int new_real_sy = vp_playfield->y;
9507 int new_full_sxsize = vp_playfield->width;
9508 int new_full_sysize = vp_playfield->height;
9509 int new_dx = vp_door_1->x;
9510 int new_dy = vp_door_1->y;
9511 int new_dxsize = vp_door_1->width;
9512 int new_dysize = vp_door_1->height;
9513 int new_vx = vp_door_2->x;
9514 int new_vy = vp_door_2->y;
9515 int new_vxsize = vp_door_2->width;
9516 int new_vysize = vp_door_2->height;
9518 boolean playfield_viewport_has_changed =
9519 (new_real_sx != REAL_SX ||
9520 new_real_sy != REAL_SY ||
9521 new_full_sxsize != FULL_SXSIZE ||
9522 new_full_sysize != FULL_SYSIZE);
9524 boolean door_1_viewport_has_changed =
9527 new_dxsize != DXSIZE ||
9528 new_dysize != DYSIZE);
9530 boolean door_2_viewport_has_changed =
9533 new_vxsize != VXSIZE ||
9534 new_vysize != VYSIZE ||
9535 game_status_last == GAME_MODE_EDITOR);
9537 return (playfield_viewport_has_changed &&
9538 door_1_viewport_has_changed &&
9539 door_2_viewport_has_changed);
9542 boolean CheckFadeAll(void)
9544 return (CheckIfGlobalBorderHasChanged() ||
9545 CheckIfAllViewportsHaveChanged());
9548 void ChangeViewportPropertiesIfNeeded(void)
9550 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9551 FALSE : setup.small_game_graphics);
9552 int gfx_game_mode = game_status;
9553 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9555 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9556 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9557 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9558 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9559 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9560 int new_win_xsize = vp_window->width;
9561 int new_win_ysize = vp_window->height;
9562 int border_left = vp_playfield->border_left;
9563 int border_right = vp_playfield->border_right;
9564 int border_top = vp_playfield->border_top;
9565 int border_bottom = vp_playfield->border_bottom;
9566 int new_sx = vp_playfield->x + border_left;
9567 int new_sy = vp_playfield->y + border_top;
9568 int new_sxsize = vp_playfield->width - border_left - border_right;
9569 int new_sysize = vp_playfield->height - border_top - border_bottom;
9570 int new_real_sx = vp_playfield->x;
9571 int new_real_sy = vp_playfield->y;
9572 int new_full_sxsize = vp_playfield->width;
9573 int new_full_sysize = vp_playfield->height;
9574 int new_dx = vp_door_1->x;
9575 int new_dy = vp_door_1->y;
9576 int new_dxsize = vp_door_1->width;
9577 int new_dysize = vp_door_1->height;
9578 int new_vx = vp_door_2->x;
9579 int new_vy = vp_door_2->y;
9580 int new_vxsize = vp_door_2->width;
9581 int new_vysize = vp_door_2->height;
9582 int new_ex = vp_door_3->x;
9583 int new_ey = vp_door_3->y;
9584 int new_exsize = vp_door_3->width;
9585 int new_eysize = vp_door_3->height;
9586 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9587 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9588 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9589 int new_scr_fieldx = new_sxsize / tilesize;
9590 int new_scr_fieldy = new_sysize / tilesize;
9591 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9592 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9593 boolean init_gfx_buffers = FALSE;
9594 boolean init_video_buffer = FALSE;
9595 boolean init_gadgets_and_anims = FALSE;
9596 boolean init_em_graphics = FALSE;
9598 if (new_win_xsize != WIN_XSIZE ||
9599 new_win_ysize != WIN_YSIZE)
9601 WIN_XSIZE = new_win_xsize;
9602 WIN_YSIZE = new_win_ysize;
9604 init_video_buffer = TRUE;
9605 init_gfx_buffers = TRUE;
9606 init_gadgets_and_anims = TRUE;
9608 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9611 if (new_scr_fieldx != SCR_FIELDX ||
9612 new_scr_fieldy != SCR_FIELDY)
9614 // this always toggles between MAIN and GAME when using small tile size
9616 SCR_FIELDX = new_scr_fieldx;
9617 SCR_FIELDY = new_scr_fieldy;
9619 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9630 new_sxsize != SXSIZE ||
9631 new_sysize != SYSIZE ||
9632 new_dxsize != DXSIZE ||
9633 new_dysize != DYSIZE ||
9634 new_vxsize != VXSIZE ||
9635 new_vysize != VYSIZE ||
9636 new_exsize != EXSIZE ||
9637 new_eysize != EYSIZE ||
9638 new_real_sx != REAL_SX ||
9639 new_real_sy != REAL_SY ||
9640 new_full_sxsize != FULL_SXSIZE ||
9641 new_full_sysize != FULL_SYSIZE ||
9642 new_tilesize_var != TILESIZE_VAR
9645 // ------------------------------------------------------------------------
9646 // determine next fading area for changed viewport definitions
9647 // ------------------------------------------------------------------------
9649 // start with current playfield area (default fading area)
9652 FADE_SXSIZE = FULL_SXSIZE;
9653 FADE_SYSIZE = FULL_SYSIZE;
9655 // add new playfield area if position or size has changed
9656 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9657 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9659 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9660 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9663 // add current and new door 1 area if position or size has changed
9664 if (new_dx != DX || new_dy != DY ||
9665 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9667 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9668 DX, DY, DXSIZE, DYSIZE);
9669 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9670 new_dx, new_dy, new_dxsize, new_dysize);
9673 // add current and new door 2 area if position or size has changed
9674 if (new_vx != VX || new_vy != VY ||
9675 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9677 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9678 VX, VY, VXSIZE, VYSIZE);
9679 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9680 new_vx, new_vy, new_vxsize, new_vysize);
9683 // ------------------------------------------------------------------------
9684 // handle changed tile size
9685 // ------------------------------------------------------------------------
9687 if (new_tilesize_var != TILESIZE_VAR)
9689 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9691 // changing tile size invalidates scroll values of engine snapshots
9692 FreeEngineSnapshotSingle();
9694 // changing tile size requires update of graphic mapping for EM engine
9695 init_em_graphics = TRUE;
9706 SXSIZE = new_sxsize;
9707 SYSIZE = new_sysize;
9708 DXSIZE = new_dxsize;
9709 DYSIZE = new_dysize;
9710 VXSIZE = new_vxsize;
9711 VYSIZE = new_vysize;
9712 EXSIZE = new_exsize;
9713 EYSIZE = new_eysize;
9714 REAL_SX = new_real_sx;
9715 REAL_SY = new_real_sy;
9716 FULL_SXSIZE = new_full_sxsize;
9717 FULL_SYSIZE = new_full_sysize;
9718 TILESIZE_VAR = new_tilesize_var;
9720 init_gfx_buffers = TRUE;
9721 init_gadgets_and_anims = TRUE;
9723 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9724 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9727 if (init_gfx_buffers)
9729 // Debug("tools:viewport", "init_gfx_buffers");
9731 SCR_FIELDX = new_scr_fieldx_buffers;
9732 SCR_FIELDY = new_scr_fieldy_buffers;
9736 SCR_FIELDX = new_scr_fieldx;
9737 SCR_FIELDY = new_scr_fieldy;
9739 SetDrawDeactivationMask(REDRAW_NONE);
9740 SetDrawBackgroundMask(REDRAW_FIELD);
9743 if (init_video_buffer)
9745 // Debug("tools:viewport", "init_video_buffer");
9747 FreeAllImageTextures(); // needs old renderer to free the textures
9749 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9750 InitImageTextures();
9753 if (init_gadgets_and_anims)
9755 // Debug("tools:viewport", "init_gadgets_and_anims");
9758 InitGlobalAnimations();
9761 if (init_em_graphics)
9763 InitGraphicInfo_EM();