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)
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_last);
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 unsigned int old_door_state;
4608 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4609 int font_nr = FONT_TEXT_2;
4614 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4616 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4617 font_nr = FONT_TEXT_1;
4620 if (game_status == GAME_MODE_PLAYING)
4621 BlitScreenToBitmap(backbuffer);
4623 // disable deactivated drawing when quick-loading level tape recording
4624 if (tape.playing && tape.deactivate_display)
4625 TapeDeactivateDisplayOff(TRUE);
4627 SetMouseCursor(CURSOR_DEFAULT);
4629 // pause network game while waiting for request to answer
4630 if (network.enabled &&
4631 game_status == GAME_MODE_PLAYING &&
4632 !game.all_players_gone &&
4633 req_state & REQUEST_WAIT_FOR_INPUT)
4634 SendToServer_PausePlaying();
4636 old_door_state = GetDoorState();
4638 // simulate releasing mouse button over last gadget, if still pressed
4640 HandleGadgets(-1, -1, 0);
4644 // draw released gadget before proceeding
4647 if (old_door_state & DOOR_OPEN_1)
4649 CloseDoor(DOOR_CLOSE_1);
4651 // save old door content
4652 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4653 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4656 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4657 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4659 // clear door drawing field
4660 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4662 // force DOOR font inside door area
4663 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4665 // write text for request
4666 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4668 char text_line[max_request_line_len + 1];
4674 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4676 tc = *(text_ptr + tx);
4677 // if (!tc || tc == ' ')
4678 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4682 if ((tc == '?' || tc == '!') && tl == 0)
4692 strncpy(text_line, text_ptr, tl);
4695 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4696 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4697 text_line, font_nr);
4699 text_ptr += tl + (tc == ' ' ? 1 : 0);
4700 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4705 if (req_state & REQ_ASK)
4707 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4708 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4709 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4710 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4712 else if (req_state & REQ_CONFIRM)
4714 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4715 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4717 else if (req_state & REQ_PLAYER)
4719 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4720 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4721 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4722 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4725 // copy request gadgets to door backbuffer
4726 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4728 OpenDoor(DOOR_OPEN_1);
4730 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4732 if (game_status == GAME_MODE_PLAYING)
4734 SetPanelBackground();
4735 SetDrawBackgroundMask(REDRAW_DOOR_1);
4739 SetDrawBackgroundMask(REDRAW_FIELD);
4745 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4747 // ---------- handle request buttons ----------
4748 result = RequestHandleEvents(req_state);
4752 if (!(req_state & REQ_STAY_OPEN))
4754 CloseDoor(DOOR_CLOSE_1);
4756 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4757 (req_state & REQ_REOPEN))
4758 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4763 if (game_status == GAME_MODE_PLAYING)
4765 SetPanelBackground();
4766 SetDrawBackgroundMask(REDRAW_DOOR_1);
4770 SetDrawBackgroundMask(REDRAW_FIELD);
4773 // continue network game after request
4774 if (network.enabled &&
4775 game_status == GAME_MODE_PLAYING &&
4776 !game.all_players_gone &&
4777 req_state & REQUEST_WAIT_FOR_INPUT)
4778 SendToServer_ContinuePlaying();
4780 // restore deactivated drawing when quick-loading level tape recording
4781 if (tape.playing && tape.deactivate_display)
4782 TapeDeactivateDisplayOn();
4787 static boolean RequestEnvelope(char *text, unsigned int req_state)
4791 if (game_status == GAME_MODE_PLAYING)
4792 BlitScreenToBitmap(backbuffer);
4794 // disable deactivated drawing when quick-loading level tape recording
4795 if (tape.playing && tape.deactivate_display)
4796 TapeDeactivateDisplayOff(TRUE);
4798 SetMouseCursor(CURSOR_DEFAULT);
4800 // pause network game while waiting for request to answer
4801 if (network.enabled &&
4802 game_status == GAME_MODE_PLAYING &&
4803 !game.all_players_gone &&
4804 req_state & REQUEST_WAIT_FOR_INPUT)
4805 SendToServer_PausePlaying();
4807 // simulate releasing mouse button over last gadget, if still pressed
4809 HandleGadgets(-1, -1, 0);
4813 // (replace with setting corresponding request background)
4814 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4815 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4817 // clear door drawing field
4818 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4820 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4822 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4824 if (game_status == GAME_MODE_PLAYING)
4826 SetPanelBackground();
4827 SetDrawBackgroundMask(REDRAW_DOOR_1);
4831 SetDrawBackgroundMask(REDRAW_FIELD);
4837 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4839 // ---------- handle request buttons ----------
4840 result = RequestHandleEvents(req_state);
4844 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4848 if (game_status == GAME_MODE_PLAYING)
4850 SetPanelBackground();
4851 SetDrawBackgroundMask(REDRAW_DOOR_1);
4855 SetDrawBackgroundMask(REDRAW_FIELD);
4858 // continue network game after request
4859 if (network.enabled &&
4860 game_status == GAME_MODE_PLAYING &&
4861 !game.all_players_gone &&
4862 req_state & REQUEST_WAIT_FOR_INPUT)
4863 SendToServer_ContinuePlaying();
4865 // restore deactivated drawing when quick-loading level tape recording
4866 if (tape.playing && tape.deactivate_display)
4867 TapeDeactivateDisplayOn();
4872 boolean Request(char *text, unsigned int req_state)
4874 boolean overlay_enabled = GetOverlayEnabled();
4877 game.request_active_or_moving = TRUE;
4879 SetOverlayEnabled(FALSE);
4881 if (global.use_envelope_request)
4882 result = RequestEnvelope(text, req_state);
4884 result = RequestDoor(text, req_state);
4886 SetOverlayEnabled(overlay_enabled);
4888 game.request_active_or_moving = FALSE;
4893 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4895 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4896 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4899 if (dpo1->sort_priority != dpo2->sort_priority)
4900 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4902 compare_result = dpo1->nr - dpo2->nr;
4904 return compare_result;
4907 void InitGraphicCompatibilityInfo_Doors(void)
4913 struct DoorInfo *door;
4917 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4918 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4920 { -1, -1, -1, NULL }
4922 struct Rect door_rect_list[] =
4924 { DX, DY, DXSIZE, DYSIZE },
4925 { VX, VY, VXSIZE, VYSIZE }
4929 for (i = 0; doors[i].door_token != -1; i++)
4931 int door_token = doors[i].door_token;
4932 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4933 int part_1 = doors[i].part_1;
4934 int part_8 = doors[i].part_8;
4935 int part_2 = part_1 + 1;
4936 int part_3 = part_1 + 2;
4937 struct DoorInfo *door = doors[i].door;
4938 struct Rect *door_rect = &door_rect_list[door_index];
4939 boolean door_gfx_redefined = FALSE;
4941 // check if any door part graphic definitions have been redefined
4943 for (j = 0; door_part_controls[j].door_token != -1; j++)
4945 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4946 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4948 if (dpc->door_token == door_token && fi->redefined)
4949 door_gfx_redefined = TRUE;
4952 // check for old-style door graphic/animation modifications
4954 if (!door_gfx_redefined)
4956 if (door->anim_mode & ANIM_STATIC_PANEL)
4958 door->panel.step_xoffset = 0;
4959 door->panel.step_yoffset = 0;
4962 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4964 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4965 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4966 int num_door_steps, num_panel_steps;
4968 // remove door part graphics other than the two default wings
4970 for (j = 0; door_part_controls[j].door_token != -1; j++)
4972 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4973 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4975 if (dpc->graphic >= part_3 &&
4976 dpc->graphic <= part_8)
4980 // set graphics and screen positions of the default wings
4982 g_part_1->width = door_rect->width;
4983 g_part_1->height = door_rect->height;
4984 g_part_2->width = door_rect->width;
4985 g_part_2->height = door_rect->height;
4986 g_part_2->src_x = door_rect->width;
4987 g_part_2->src_y = g_part_1->src_y;
4989 door->part_2.x = door->part_1.x;
4990 door->part_2.y = door->part_1.y;
4992 if (door->width != -1)
4994 g_part_1->width = door->width;
4995 g_part_2->width = door->width;
4997 // special treatment for graphics and screen position of right wing
4998 g_part_2->src_x += door_rect->width - door->width;
4999 door->part_2.x += door_rect->width - door->width;
5002 if (door->height != -1)
5004 g_part_1->height = door->height;
5005 g_part_2->height = door->height;
5007 // special treatment for graphics and screen position of bottom wing
5008 g_part_2->src_y += door_rect->height - door->height;
5009 door->part_2.y += door_rect->height - door->height;
5012 // set animation delays for the default wings and panels
5014 door->part_1.step_delay = door->step_delay;
5015 door->part_2.step_delay = door->step_delay;
5016 door->panel.step_delay = door->step_delay;
5018 // set animation draw order for the default wings
5020 door->part_1.sort_priority = 2; // draw left wing over ...
5021 door->part_2.sort_priority = 1; // ... right wing
5023 // set animation draw offset for the default wings
5025 if (door->anim_mode & ANIM_HORIZONTAL)
5027 door->part_1.step_xoffset = door->step_offset;
5028 door->part_1.step_yoffset = 0;
5029 door->part_2.step_xoffset = door->step_offset * -1;
5030 door->part_2.step_yoffset = 0;
5032 num_door_steps = g_part_1->width / door->step_offset;
5034 else // ANIM_VERTICAL
5036 door->part_1.step_xoffset = 0;
5037 door->part_1.step_yoffset = door->step_offset;
5038 door->part_2.step_xoffset = 0;
5039 door->part_2.step_yoffset = door->step_offset * -1;
5041 num_door_steps = g_part_1->height / door->step_offset;
5044 // set animation draw offset for the default panels
5046 if (door->step_offset > 1)
5048 num_panel_steps = 2 * door_rect->height / door->step_offset;
5049 door->panel.start_step = num_panel_steps - num_door_steps;
5050 door->panel.start_step_closing = door->panel.start_step;
5054 num_panel_steps = door_rect->height / door->step_offset;
5055 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5056 door->panel.start_step_closing = door->panel.start_step;
5057 door->panel.step_delay *= 2;
5064 void InitDoors(void)
5068 for (i = 0; door_part_controls[i].door_token != -1; i++)
5070 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5071 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5073 // initialize "start_step_opening" and "start_step_closing", if needed
5074 if (dpc->pos->start_step_opening == 0 &&
5075 dpc->pos->start_step_closing == 0)
5077 // dpc->pos->start_step_opening = dpc->pos->start_step;
5078 dpc->pos->start_step_closing = dpc->pos->start_step;
5081 // fill structure for door part draw order (sorted below)
5083 dpo->sort_priority = dpc->pos->sort_priority;
5086 // sort door part controls according to sort_priority and graphic number
5087 qsort(door_part_order, MAX_DOOR_PARTS,
5088 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5091 unsigned int OpenDoor(unsigned int door_state)
5093 if (door_state & DOOR_COPY_BACK)
5095 if (door_state & DOOR_OPEN_1)
5096 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5097 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5099 if (door_state & DOOR_OPEN_2)
5100 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5101 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5103 door_state &= ~DOOR_COPY_BACK;
5106 return MoveDoor(door_state);
5109 unsigned int CloseDoor(unsigned int door_state)
5111 unsigned int old_door_state = GetDoorState();
5113 if (!(door_state & DOOR_NO_COPY_BACK))
5115 if (old_door_state & DOOR_OPEN_1)
5116 BlitBitmap(backbuffer, bitmap_db_door_1,
5117 DX, DY, DXSIZE, DYSIZE, 0, 0);
5119 if (old_door_state & DOOR_OPEN_2)
5120 BlitBitmap(backbuffer, bitmap_db_door_2,
5121 VX, VY, VXSIZE, VYSIZE, 0, 0);
5123 door_state &= ~DOOR_NO_COPY_BACK;
5126 return MoveDoor(door_state);
5129 unsigned int GetDoorState(void)
5131 return MoveDoor(DOOR_GET_STATE);
5134 unsigned int SetDoorState(unsigned int door_state)
5136 return MoveDoor(door_state | DOOR_SET_STATE);
5139 static int euclid(int a, int b)
5141 return (b ? euclid(b, a % b) : a);
5144 unsigned int MoveDoor(unsigned int door_state)
5146 struct Rect door_rect_list[] =
5148 { DX, DY, DXSIZE, DYSIZE },
5149 { VX, VY, VXSIZE, VYSIZE }
5151 static int door1 = DOOR_CLOSE_1;
5152 static int door2 = DOOR_CLOSE_2;
5153 unsigned int door_delay = 0;
5154 unsigned int door_delay_value;
5157 if (door_state == DOOR_GET_STATE)
5158 return (door1 | door2);
5160 if (door_state & DOOR_SET_STATE)
5162 if (door_state & DOOR_ACTION_1)
5163 door1 = door_state & DOOR_ACTION_1;
5164 if (door_state & DOOR_ACTION_2)
5165 door2 = door_state & DOOR_ACTION_2;
5167 return (door1 | door2);
5170 if (!(door_state & DOOR_FORCE_REDRAW))
5172 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5173 door_state &= ~DOOR_OPEN_1;
5174 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5175 door_state &= ~DOOR_CLOSE_1;
5176 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5177 door_state &= ~DOOR_OPEN_2;
5178 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5179 door_state &= ~DOOR_CLOSE_2;
5182 if (global.autoplay_leveldir)
5184 door_state |= DOOR_NO_DELAY;
5185 door_state &= ~DOOR_CLOSE_ALL;
5188 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5189 door_state |= DOOR_NO_DELAY;
5191 if (door_state & DOOR_ACTION)
5193 boolean door_panel_drawn[NUM_DOORS];
5194 boolean panel_has_doors[NUM_DOORS];
5195 boolean door_part_skip[MAX_DOOR_PARTS];
5196 boolean door_part_done[MAX_DOOR_PARTS];
5197 boolean door_part_done_all;
5198 int num_steps[MAX_DOOR_PARTS];
5199 int max_move_delay = 0; // delay for complete animations of all doors
5200 int max_step_delay = 0; // delay (ms) between two animation frames
5201 int num_move_steps = 0; // number of animation steps for all doors
5202 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5203 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5204 int current_move_delay = 0;
5208 for (i = 0; i < NUM_DOORS; i++)
5209 panel_has_doors[i] = FALSE;
5211 for (i = 0; i < MAX_DOOR_PARTS; i++)
5213 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5214 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5215 int door_token = dpc->door_token;
5217 door_part_done[i] = FALSE;
5218 door_part_skip[i] = (!(door_state & door_token) ||
5222 for (i = 0; i < MAX_DOOR_PARTS; i++)
5224 int nr = door_part_order[i].nr;
5225 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5226 struct DoorPartPosInfo *pos = dpc->pos;
5227 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5228 int door_token = dpc->door_token;
5229 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5230 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5231 int step_xoffset = ABS(pos->step_xoffset);
5232 int step_yoffset = ABS(pos->step_yoffset);
5233 int step_delay = pos->step_delay;
5234 int current_door_state = door_state & door_token;
5235 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5236 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5237 boolean part_opening = (is_panel ? door_closing : door_opening);
5238 int start_step = (part_opening ? pos->start_step_opening :
5239 pos->start_step_closing);
5240 float move_xsize = (step_xoffset ? g->width : 0);
5241 float move_ysize = (step_yoffset ? g->height : 0);
5242 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5243 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5244 int move_steps = (move_xsteps && move_ysteps ?
5245 MIN(move_xsteps, move_ysteps) :
5246 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5247 int move_delay = move_steps * step_delay;
5249 if (door_part_skip[nr])
5252 max_move_delay = MAX(max_move_delay, move_delay);
5253 max_step_delay = (max_step_delay == 0 ? step_delay :
5254 euclid(max_step_delay, step_delay));
5255 num_steps[nr] = move_steps;
5259 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5261 panel_has_doors[door_index] = TRUE;
5265 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5267 num_move_steps = max_move_delay / max_step_delay;
5268 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5270 door_delay_value = max_step_delay;
5272 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5274 start = num_move_steps - 1;
5278 // opening door sound has priority over simultaneously closing door
5279 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5281 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5283 if (door_state & DOOR_OPEN_1)
5284 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5285 if (door_state & DOOR_OPEN_2)
5286 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5288 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5290 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5292 if (door_state & DOOR_CLOSE_1)
5293 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5294 if (door_state & DOOR_CLOSE_2)
5295 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5299 for (k = start; k < num_move_steps; k++)
5301 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5303 door_part_done_all = TRUE;
5305 for (i = 0; i < NUM_DOORS; i++)
5306 door_panel_drawn[i] = FALSE;
5308 for (i = 0; i < MAX_DOOR_PARTS; i++)
5310 int nr = door_part_order[i].nr;
5311 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5312 struct DoorPartPosInfo *pos = dpc->pos;
5313 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5314 int door_token = dpc->door_token;
5315 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5316 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5317 boolean is_panel_and_door_has_closed = FALSE;
5318 struct Rect *door_rect = &door_rect_list[door_index];
5319 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5321 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5322 int current_door_state = door_state & door_token;
5323 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5324 boolean door_closing = !door_opening;
5325 boolean part_opening = (is_panel ? door_closing : door_opening);
5326 boolean part_closing = !part_opening;
5327 int start_step = (part_opening ? pos->start_step_opening :
5328 pos->start_step_closing);
5329 int step_delay = pos->step_delay;
5330 int step_factor = step_delay / max_step_delay;
5331 int k1 = (step_factor ? k / step_factor + 1 : k);
5332 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5333 int kk = MAX(0, k2);
5336 int src_x, src_y, src_xx, src_yy;
5337 int dst_x, dst_y, dst_xx, dst_yy;
5340 if (door_part_skip[nr])
5343 if (!(door_state & door_token))
5351 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5352 int kk_door = MAX(0, k2_door);
5353 int sync_frame = kk_door * door_delay_value;
5354 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5356 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5357 &g_src_x, &g_src_y);
5362 if (!door_panel_drawn[door_index])
5364 ClearRectangle(drawto, door_rect->x, door_rect->y,
5365 door_rect->width, door_rect->height);
5367 door_panel_drawn[door_index] = TRUE;
5370 // draw opening or closing door parts
5372 if (pos->step_xoffset < 0) // door part on right side
5375 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5378 if (dst_xx + width > door_rect->width)
5379 width = door_rect->width - dst_xx;
5381 else // door part on left side
5384 dst_xx = pos->x - kk * pos->step_xoffset;
5388 src_xx = ABS(dst_xx);
5392 width = g->width - src_xx;
5394 if (width > door_rect->width)
5395 width = door_rect->width;
5397 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5400 if (pos->step_yoffset < 0) // door part on bottom side
5403 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5406 if (dst_yy + height > door_rect->height)
5407 height = door_rect->height - dst_yy;
5409 else // door part on top side
5412 dst_yy = pos->y - kk * pos->step_yoffset;
5416 src_yy = ABS(dst_yy);
5420 height = g->height - src_yy;
5423 src_x = g_src_x + src_xx;
5424 src_y = g_src_y + src_yy;
5426 dst_x = door_rect->x + dst_xx;
5427 dst_y = door_rect->y + dst_yy;
5429 is_panel_and_door_has_closed =
5432 panel_has_doors[door_index] &&
5433 k >= num_move_steps_doors_only - 1);
5435 if (width >= 0 && width <= g->width &&
5436 height >= 0 && height <= g->height &&
5437 !is_panel_and_door_has_closed)
5439 if (is_panel || !pos->draw_masked)
5440 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5443 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5447 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5449 if ((part_opening && (width < 0 || height < 0)) ||
5450 (part_closing && (width >= g->width && height >= g->height)))
5451 door_part_done[nr] = TRUE;
5453 // continue door part animations, but not panel after door has closed
5454 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5455 door_part_done_all = FALSE;
5458 if (!(door_state & DOOR_NO_DELAY))
5462 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5464 current_move_delay += max_step_delay;
5466 // prevent OS (Windows) from complaining about program not responding
5470 if (door_part_done_all)
5474 if (!(door_state & DOOR_NO_DELAY))
5476 // wait for specified door action post delay
5477 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5478 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5479 else if (door_state & DOOR_ACTION_1)
5480 door_delay_value = door_1.post_delay;
5481 else if (door_state & DOOR_ACTION_2)
5482 door_delay_value = door_2.post_delay;
5484 while (!DelayReached(&door_delay, door_delay_value))
5489 if (door_state & DOOR_ACTION_1)
5490 door1 = door_state & DOOR_ACTION_1;
5491 if (door_state & DOOR_ACTION_2)
5492 door2 = door_state & DOOR_ACTION_2;
5494 // draw masked border over door area
5495 DrawMaskedBorder(REDRAW_DOOR_1);
5496 DrawMaskedBorder(REDRAW_DOOR_2);
5498 ClearAutoRepeatKeyEvents();
5500 return (door1 | door2);
5503 static boolean useSpecialEditorDoor(void)
5505 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5506 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5508 // do not draw special editor door if editor border defined or redefined
5509 if (graphic_info[graphic].bitmap != NULL || redefined)
5512 // do not draw special editor door if global border defined to be empty
5513 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5516 // do not draw special editor door if viewport definitions do not match
5520 EY + EYSIZE != VY + VYSIZE)
5526 void DrawSpecialEditorDoor(void)
5528 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5529 int top_border_width = gfx1->width;
5530 int top_border_height = gfx1->height;
5531 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5532 int ex = EX - outer_border;
5533 int ey = EY - outer_border;
5534 int vy = VY - outer_border;
5535 int exsize = EXSIZE + 2 * outer_border;
5537 if (!useSpecialEditorDoor())
5540 // draw bigger level editor toolbox window
5541 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5542 top_border_width, top_border_height, ex, ey - top_border_height);
5543 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5544 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5546 redraw_mask |= REDRAW_ALL;
5549 void UndrawSpecialEditorDoor(void)
5551 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5552 int top_border_width = gfx1->width;
5553 int top_border_height = gfx1->height;
5554 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5555 int ex = EX - outer_border;
5556 int ey = EY - outer_border;
5557 int ey_top = ey - top_border_height;
5558 int exsize = EXSIZE + 2 * outer_border;
5559 int eysize = EYSIZE + 2 * outer_border;
5561 if (!useSpecialEditorDoor())
5564 // draw normal tape recorder window
5565 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5567 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5568 ex, ey_top, top_border_width, top_border_height,
5570 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5571 ex, ey, exsize, eysize, ex, ey);
5575 // if screen background is set to "[NONE]", clear editor toolbox window
5576 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5577 ClearRectangle(drawto, ex, ey, exsize, eysize);
5580 redraw_mask |= REDRAW_ALL;
5584 // ---------- new tool button stuff -------------------------------------------
5589 struct TextPosInfo *pos;
5591 boolean is_touch_button;
5593 } toolbutton_info[NUM_TOOL_BUTTONS] =
5596 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5597 TOOL_CTRL_ID_YES, FALSE, "yes"
5600 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5601 TOOL_CTRL_ID_NO, FALSE, "no"
5604 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5605 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5608 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5609 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5612 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5613 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5616 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5617 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5620 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5621 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5624 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5625 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5628 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5629 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5632 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5633 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5637 void CreateToolButtons(void)
5641 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5643 int graphic = toolbutton_info[i].graphic;
5644 struct GraphicInfo *gfx = &graphic_info[graphic];
5645 struct TextPosInfo *pos = toolbutton_info[i].pos;
5646 struct GadgetInfo *gi;
5647 Bitmap *deco_bitmap = None;
5648 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5649 unsigned int event_mask = GD_EVENT_RELEASED;
5650 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5651 int base_x = (is_touch_button ? 0 : DX);
5652 int base_y = (is_touch_button ? 0 : DY);
5653 int gd_x = gfx->src_x;
5654 int gd_y = gfx->src_y;
5655 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5656 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5661 if (global.use_envelope_request && !is_touch_button)
5663 setRequestPosition(&base_x, &base_y, TRUE);
5665 // check if request buttons are outside of envelope and fix, if needed
5666 if (x < 0 || x + gfx->width > request.width ||
5667 y < 0 || y + gfx->height > request.height)
5669 if (id == TOOL_CTRL_ID_YES)
5672 y = request.height - 2 * request.border_size - gfx->height;
5674 else if (id == TOOL_CTRL_ID_NO)
5676 x = request.width - 2 * request.border_size - gfx->width;
5677 y = request.height - 2 * request.border_size - gfx->height;
5679 else if (id == TOOL_CTRL_ID_CONFIRM)
5681 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5682 y = request.height - 2 * request.border_size - gfx->height;
5684 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5686 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5688 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5689 y = request.height - 2 * request.border_size - gfx->height * 2;
5691 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5692 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5697 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5699 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5701 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5702 pos->size, &deco_bitmap, &deco_x, &deco_y);
5703 deco_xpos = (gfx->width - pos->size) / 2;
5704 deco_ypos = (gfx->height - pos->size) / 2;
5707 gi = CreateGadget(GDI_CUSTOM_ID, id,
5708 GDI_IMAGE_ID, graphic,
5709 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5712 GDI_WIDTH, gfx->width,
5713 GDI_HEIGHT, gfx->height,
5714 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5715 GDI_STATE, GD_BUTTON_UNPRESSED,
5716 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5717 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5718 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5719 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5720 GDI_DECORATION_SIZE, pos->size, pos->size,
5721 GDI_DECORATION_SHIFTING, 1, 1,
5722 GDI_DIRECT_DRAW, FALSE,
5723 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5724 GDI_EVENT_MASK, event_mask,
5725 GDI_CALLBACK_ACTION, HandleToolButtons,
5729 Fail("cannot create gadget");
5731 tool_gadget[id] = gi;
5735 void FreeToolButtons(void)
5739 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5740 FreeGadget(tool_gadget[i]);
5743 static void UnmapToolButtons(void)
5747 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5748 UnmapGadget(tool_gadget[i]);
5751 static void HandleToolButtons(struct GadgetInfo *gi)
5753 request_gadget_id = gi->custom_id;
5756 static struct Mapping_EM_to_RND_object
5759 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5760 boolean is_backside; // backside of moving element
5766 em_object_mapping_list[GAME_TILE_MAX + 1] =
5769 Zborder, FALSE, FALSE,
5773 Zplayer, FALSE, FALSE,
5782 Ztank, FALSE, FALSE,
5786 Zeater, FALSE, FALSE,
5790 Zdynamite, FALSE, FALSE,
5794 Zboom, FALSE, FALSE,
5799 Xchain, FALSE, FALSE,
5800 EL_DEFAULT, ACTION_EXPLODING, -1
5803 Xboom_bug, FALSE, FALSE,
5804 EL_BUG, ACTION_EXPLODING, -1
5807 Xboom_tank, FALSE, FALSE,
5808 EL_SPACESHIP, ACTION_EXPLODING, -1
5811 Xboom_android, FALSE, FALSE,
5812 EL_EMC_ANDROID, ACTION_OTHER, -1
5815 Xboom_1, FALSE, FALSE,
5816 EL_DEFAULT, ACTION_EXPLODING, -1
5819 Xboom_2, FALSE, FALSE,
5820 EL_DEFAULT, ACTION_EXPLODING, -1
5824 Xblank, TRUE, FALSE,
5829 Xsplash_e, FALSE, FALSE,
5830 EL_ACID_SPLASH_RIGHT, -1, -1
5833 Xsplash_w, FALSE, FALSE,
5834 EL_ACID_SPLASH_LEFT, -1, -1
5838 Xplant, TRUE, FALSE,
5839 EL_EMC_PLANT, -1, -1
5842 Yplant, FALSE, FALSE,
5843 EL_EMC_PLANT, -1, -1
5847 Xacid_1, TRUE, FALSE,
5851 Xacid_2, FALSE, FALSE,
5855 Xacid_3, FALSE, FALSE,
5859 Xacid_4, FALSE, FALSE,
5863 Xacid_5, FALSE, FALSE,
5867 Xacid_6, FALSE, FALSE,
5871 Xacid_7, FALSE, FALSE,
5875 Xacid_8, FALSE, FALSE,
5880 Xfake_acid_1, TRUE, FALSE,
5881 EL_EMC_FAKE_ACID, -1, -1
5884 Xfake_acid_2, FALSE, FALSE,
5885 EL_EMC_FAKE_ACID, -1, -1
5888 Xfake_acid_3, FALSE, FALSE,
5889 EL_EMC_FAKE_ACID, -1, -1
5892 Xfake_acid_4, FALSE, FALSE,
5893 EL_EMC_FAKE_ACID, -1, -1
5896 Xfake_acid_5, FALSE, FALSE,
5897 EL_EMC_FAKE_ACID, -1, -1
5900 Xfake_acid_6, FALSE, FALSE,
5901 EL_EMC_FAKE_ACID, -1, -1
5904 Xfake_acid_7, FALSE, FALSE,
5905 EL_EMC_FAKE_ACID, -1, -1
5908 Xfake_acid_8, FALSE, FALSE,
5909 EL_EMC_FAKE_ACID, -1, -1
5913 Xfake_acid_1_player, FALSE, FALSE,
5914 EL_EMC_FAKE_ACID, -1, -1
5917 Xfake_acid_2_player, FALSE, FALSE,
5918 EL_EMC_FAKE_ACID, -1, -1
5921 Xfake_acid_3_player, FALSE, FALSE,
5922 EL_EMC_FAKE_ACID, -1, -1
5925 Xfake_acid_4_player, FALSE, FALSE,
5926 EL_EMC_FAKE_ACID, -1, -1
5929 Xfake_acid_5_player, FALSE, FALSE,
5930 EL_EMC_FAKE_ACID, -1, -1
5933 Xfake_acid_6_player, FALSE, FALSE,
5934 EL_EMC_FAKE_ACID, -1, -1
5937 Xfake_acid_7_player, FALSE, FALSE,
5938 EL_EMC_FAKE_ACID, -1, -1
5941 Xfake_acid_8_player, FALSE, FALSE,
5942 EL_EMC_FAKE_ACID, -1, -1
5946 Xgrass, TRUE, FALSE,
5947 EL_EMC_GRASS, -1, -1
5950 Ygrass_nB, FALSE, FALSE,
5951 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5954 Ygrass_eB, FALSE, FALSE,
5955 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5958 Ygrass_sB, FALSE, FALSE,
5959 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5962 Ygrass_wB, FALSE, FALSE,
5963 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5971 Ydirt_nB, FALSE, FALSE,
5972 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5975 Ydirt_eB, FALSE, FALSE,
5976 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5979 Ydirt_sB, FALSE, FALSE,
5980 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5983 Ydirt_wB, FALSE, FALSE,
5984 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5988 Xandroid, TRUE, FALSE,
5989 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5992 Xandroid_1_n, FALSE, FALSE,
5993 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5996 Xandroid_2_n, FALSE, FALSE,
5997 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6000 Xandroid_1_e, FALSE, FALSE,
6001 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6004 Xandroid_2_e, FALSE, FALSE,
6005 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6008 Xandroid_1_w, FALSE, FALSE,
6009 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6012 Xandroid_2_w, FALSE, FALSE,
6013 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6016 Xandroid_1_s, FALSE, FALSE,
6017 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6020 Xandroid_2_s, FALSE, FALSE,
6021 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6024 Yandroid_n, FALSE, FALSE,
6025 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6028 Yandroid_nB, FALSE, TRUE,
6029 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6032 Yandroid_ne, FALSE, FALSE,
6033 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6036 Yandroid_neB, FALSE, TRUE,
6037 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6040 Yandroid_e, FALSE, FALSE,
6041 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6044 Yandroid_eB, FALSE, TRUE,
6045 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6048 Yandroid_se, FALSE, FALSE,
6049 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6052 Yandroid_seB, FALSE, TRUE,
6053 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6056 Yandroid_s, FALSE, FALSE,
6057 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6060 Yandroid_sB, FALSE, TRUE,
6061 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6064 Yandroid_sw, FALSE, FALSE,
6065 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6068 Yandroid_swB, FALSE, TRUE,
6069 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6072 Yandroid_w, FALSE, FALSE,
6073 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6076 Yandroid_wB, FALSE, TRUE,
6077 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6080 Yandroid_nw, FALSE, FALSE,
6081 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6084 Yandroid_nwB, FALSE, TRUE,
6085 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6089 Xeater_n, TRUE, FALSE,
6090 EL_YAMYAM_UP, -1, -1
6093 Xeater_e, TRUE, FALSE,
6094 EL_YAMYAM_RIGHT, -1, -1
6097 Xeater_w, TRUE, FALSE,
6098 EL_YAMYAM_LEFT, -1, -1
6101 Xeater_s, TRUE, FALSE,
6102 EL_YAMYAM_DOWN, -1, -1
6105 Yeater_n, FALSE, FALSE,
6106 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6109 Yeater_nB, FALSE, TRUE,
6110 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6113 Yeater_e, FALSE, FALSE,
6114 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6117 Yeater_eB, FALSE, TRUE,
6118 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6121 Yeater_s, FALSE, FALSE,
6122 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6125 Yeater_sB, FALSE, TRUE,
6126 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6129 Yeater_w, FALSE, FALSE,
6130 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6133 Yeater_wB, FALSE, TRUE,
6134 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6137 Yeater_stone, FALSE, FALSE,
6138 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6141 Yeater_spring, FALSE, FALSE,
6142 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6146 Xalien, TRUE, FALSE,
6150 Xalien_pause, FALSE, FALSE,
6154 Yalien_n, FALSE, FALSE,
6155 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6158 Yalien_nB, FALSE, TRUE,
6159 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6162 Yalien_e, FALSE, FALSE,
6163 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6166 Yalien_eB, FALSE, TRUE,
6167 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6170 Yalien_s, FALSE, FALSE,
6171 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6174 Yalien_sB, FALSE, TRUE,
6175 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6178 Yalien_w, FALSE, FALSE,
6179 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6182 Yalien_wB, FALSE, TRUE,
6183 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6186 Yalien_stone, FALSE, FALSE,
6187 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6190 Yalien_spring, FALSE, FALSE,
6191 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6195 Xbug_1_n, TRUE, FALSE,
6199 Xbug_1_e, TRUE, FALSE,
6200 EL_BUG_RIGHT, -1, -1
6203 Xbug_1_s, TRUE, FALSE,
6207 Xbug_1_w, TRUE, FALSE,
6211 Xbug_2_n, FALSE, FALSE,
6215 Xbug_2_e, FALSE, FALSE,
6216 EL_BUG_RIGHT, -1, -1
6219 Xbug_2_s, FALSE, FALSE,
6223 Xbug_2_w, FALSE, FALSE,
6227 Ybug_n, FALSE, FALSE,
6228 EL_BUG, ACTION_MOVING, MV_BIT_UP
6231 Ybug_nB, FALSE, TRUE,
6232 EL_BUG, ACTION_MOVING, MV_BIT_UP
6235 Ybug_e, FALSE, FALSE,
6236 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6239 Ybug_eB, FALSE, TRUE,
6240 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6243 Ybug_s, FALSE, FALSE,
6244 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6247 Ybug_sB, FALSE, TRUE,
6248 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6251 Ybug_w, FALSE, FALSE,
6252 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6255 Ybug_wB, FALSE, TRUE,
6256 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6259 Ybug_w_n, FALSE, FALSE,
6260 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6263 Ybug_n_e, FALSE, FALSE,
6264 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6267 Ybug_e_s, FALSE, FALSE,
6268 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6271 Ybug_s_w, FALSE, FALSE,
6272 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6275 Ybug_e_n, FALSE, FALSE,
6276 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6279 Ybug_s_e, FALSE, FALSE,
6280 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6283 Ybug_w_s, FALSE, FALSE,
6284 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6287 Ybug_n_w, FALSE, FALSE,
6288 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6291 Ybug_stone, FALSE, FALSE,
6292 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6295 Ybug_spring, FALSE, FALSE,
6296 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6300 Xtank_1_n, TRUE, FALSE,
6301 EL_SPACESHIP_UP, -1, -1
6304 Xtank_1_e, TRUE, FALSE,
6305 EL_SPACESHIP_RIGHT, -1, -1
6308 Xtank_1_s, TRUE, FALSE,
6309 EL_SPACESHIP_DOWN, -1, -1
6312 Xtank_1_w, TRUE, FALSE,
6313 EL_SPACESHIP_LEFT, -1, -1
6316 Xtank_2_n, FALSE, FALSE,
6317 EL_SPACESHIP_UP, -1, -1
6320 Xtank_2_e, FALSE, FALSE,
6321 EL_SPACESHIP_RIGHT, -1, -1
6324 Xtank_2_s, FALSE, FALSE,
6325 EL_SPACESHIP_DOWN, -1, -1
6328 Xtank_2_w, FALSE, FALSE,
6329 EL_SPACESHIP_LEFT, -1, -1
6332 Ytank_n, FALSE, FALSE,
6333 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6336 Ytank_nB, FALSE, TRUE,
6337 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6340 Ytank_e, FALSE, FALSE,
6341 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6344 Ytank_eB, FALSE, TRUE,
6345 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6348 Ytank_s, FALSE, FALSE,
6349 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6352 Ytank_sB, FALSE, TRUE,
6353 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6356 Ytank_w, FALSE, FALSE,
6357 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6360 Ytank_wB, FALSE, TRUE,
6361 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6364 Ytank_w_n, FALSE, FALSE,
6365 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6368 Ytank_n_e, FALSE, FALSE,
6369 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6372 Ytank_e_s, FALSE, FALSE,
6373 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6376 Ytank_s_w, FALSE, FALSE,
6377 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6380 Ytank_e_n, FALSE, FALSE,
6381 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6384 Ytank_s_e, FALSE, FALSE,
6385 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6388 Ytank_w_s, FALSE, FALSE,
6389 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6392 Ytank_n_w, FALSE, FALSE,
6393 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6396 Ytank_stone, FALSE, FALSE,
6397 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6400 Ytank_spring, FALSE, FALSE,
6401 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6405 Xemerald, TRUE, FALSE,
6409 Xemerald_pause, FALSE, FALSE,
6413 Xemerald_fall, FALSE, FALSE,
6417 Xemerald_shine, FALSE, FALSE,
6418 EL_EMERALD, ACTION_TWINKLING, -1
6421 Yemerald_s, FALSE, FALSE,
6422 EL_EMERALD, ACTION_FALLING, -1
6425 Yemerald_sB, FALSE, TRUE,
6426 EL_EMERALD, ACTION_FALLING, -1
6429 Yemerald_e, FALSE, FALSE,
6430 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6433 Yemerald_eB, FALSE, TRUE,
6434 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6437 Yemerald_w, FALSE, FALSE,
6438 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6441 Yemerald_wB, FALSE, TRUE,
6442 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6445 Yemerald_blank, FALSE, FALSE,
6446 EL_EMERALD, ACTION_COLLECTING, -1
6450 Xdiamond, TRUE, FALSE,
6454 Xdiamond_pause, FALSE, FALSE,
6458 Xdiamond_fall, FALSE, FALSE,
6462 Xdiamond_shine, FALSE, FALSE,
6463 EL_DIAMOND, ACTION_TWINKLING, -1
6466 Ydiamond_s, FALSE, FALSE,
6467 EL_DIAMOND, ACTION_FALLING, -1
6470 Ydiamond_sB, FALSE, TRUE,
6471 EL_DIAMOND, ACTION_FALLING, -1
6474 Ydiamond_e, FALSE, FALSE,
6475 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6478 Ydiamond_eB, FALSE, TRUE,
6479 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6482 Ydiamond_w, FALSE, FALSE,
6483 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6486 Ydiamond_wB, FALSE, TRUE,
6487 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6490 Ydiamond_blank, FALSE, FALSE,
6491 EL_DIAMOND, ACTION_COLLECTING, -1
6494 Ydiamond_stone, FALSE, FALSE,
6495 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6499 Xstone, TRUE, FALSE,
6503 Xstone_pause, FALSE, FALSE,
6507 Xstone_fall, FALSE, FALSE,
6511 Ystone_s, FALSE, FALSE,
6512 EL_ROCK, ACTION_FALLING, -1
6515 Ystone_sB, FALSE, TRUE,
6516 EL_ROCK, ACTION_FALLING, -1
6519 Ystone_e, FALSE, FALSE,
6520 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6523 Ystone_eB, FALSE, TRUE,
6524 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6527 Ystone_w, FALSE, FALSE,
6528 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6531 Ystone_wB, FALSE, TRUE,
6532 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6540 Xbomb_pause, FALSE, FALSE,
6544 Xbomb_fall, FALSE, FALSE,
6548 Ybomb_s, FALSE, FALSE,
6549 EL_BOMB, ACTION_FALLING, -1
6552 Ybomb_sB, FALSE, TRUE,
6553 EL_BOMB, ACTION_FALLING, -1
6556 Ybomb_e, FALSE, FALSE,
6557 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6560 Ybomb_eB, FALSE, TRUE,
6561 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6564 Ybomb_w, FALSE, FALSE,
6565 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6568 Ybomb_wB, FALSE, TRUE,
6569 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6572 Ybomb_blank, FALSE, FALSE,
6573 EL_BOMB, ACTION_ACTIVATING, -1
6581 Xnut_pause, FALSE, FALSE,
6585 Xnut_fall, FALSE, FALSE,
6589 Ynut_s, FALSE, FALSE,
6590 EL_NUT, ACTION_FALLING, -1
6593 Ynut_sB, FALSE, TRUE,
6594 EL_NUT, ACTION_FALLING, -1
6597 Ynut_e, FALSE, FALSE,
6598 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6601 Ynut_eB, FALSE, TRUE,
6602 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6605 Ynut_w, FALSE, FALSE,
6606 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6609 Ynut_wB, FALSE, TRUE,
6610 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6613 Ynut_stone, FALSE, FALSE,
6614 EL_NUT, ACTION_BREAKING, -1
6618 Xspring, TRUE, FALSE,
6622 Xspring_pause, FALSE, FALSE,
6626 Xspring_e, TRUE, FALSE,
6627 EL_SPRING_RIGHT, -1, -1
6630 Xspring_w, TRUE, FALSE,
6631 EL_SPRING_LEFT, -1, -1
6634 Xspring_fall, FALSE, FALSE,
6638 Yspring_s, FALSE, FALSE,
6639 EL_SPRING, ACTION_FALLING, -1
6642 Yspring_sB, FALSE, TRUE,
6643 EL_SPRING, ACTION_FALLING, -1
6646 Yspring_e, FALSE, FALSE,
6647 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6650 Yspring_eB, FALSE, TRUE,
6651 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6654 Yspring_w, FALSE, FALSE,
6655 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6658 Yspring_wB, FALSE, TRUE,
6659 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6662 Yspring_alien_e, FALSE, FALSE,
6663 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6666 Yspring_alien_eB, FALSE, TRUE,
6667 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6670 Yspring_alien_w, FALSE, FALSE,
6671 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6674 Yspring_alien_wB, FALSE, TRUE,
6675 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6679 Xpush_emerald_e, FALSE, FALSE,
6680 EL_EMERALD, -1, MV_BIT_RIGHT
6683 Xpush_emerald_w, FALSE, FALSE,
6684 EL_EMERALD, -1, MV_BIT_LEFT
6687 Xpush_diamond_e, FALSE, FALSE,
6688 EL_DIAMOND, -1, MV_BIT_RIGHT
6691 Xpush_diamond_w, FALSE, FALSE,
6692 EL_DIAMOND, -1, MV_BIT_LEFT
6695 Xpush_stone_e, FALSE, FALSE,
6696 EL_ROCK, -1, MV_BIT_RIGHT
6699 Xpush_stone_w, FALSE, FALSE,
6700 EL_ROCK, -1, MV_BIT_LEFT
6703 Xpush_bomb_e, FALSE, FALSE,
6704 EL_BOMB, -1, MV_BIT_RIGHT
6707 Xpush_bomb_w, FALSE, FALSE,
6708 EL_BOMB, -1, MV_BIT_LEFT
6711 Xpush_nut_e, FALSE, FALSE,
6712 EL_NUT, -1, MV_BIT_RIGHT
6715 Xpush_nut_w, FALSE, FALSE,
6716 EL_NUT, -1, MV_BIT_LEFT
6719 Xpush_spring_e, FALSE, FALSE,
6720 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6723 Xpush_spring_w, FALSE, FALSE,
6724 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6728 Xdynamite, TRUE, FALSE,
6729 EL_EM_DYNAMITE, -1, -1
6732 Ydynamite_blank, FALSE, FALSE,
6733 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6736 Xdynamite_1, TRUE, FALSE,
6737 EL_EM_DYNAMITE_ACTIVE, -1, -1
6740 Xdynamite_2, FALSE, FALSE,
6741 EL_EM_DYNAMITE_ACTIVE, -1, -1
6744 Xdynamite_3, FALSE, FALSE,
6745 EL_EM_DYNAMITE_ACTIVE, -1, -1
6748 Xdynamite_4, FALSE, FALSE,
6749 EL_EM_DYNAMITE_ACTIVE, -1, -1
6753 Xkey_1, TRUE, FALSE,
6757 Xkey_2, TRUE, FALSE,
6761 Xkey_3, TRUE, FALSE,
6765 Xkey_4, TRUE, FALSE,
6769 Xkey_5, TRUE, FALSE,
6770 EL_EMC_KEY_5, -1, -1
6773 Xkey_6, TRUE, FALSE,
6774 EL_EMC_KEY_6, -1, -1
6777 Xkey_7, TRUE, FALSE,
6778 EL_EMC_KEY_7, -1, -1
6781 Xkey_8, TRUE, FALSE,
6782 EL_EMC_KEY_8, -1, -1
6786 Xdoor_1, TRUE, FALSE,
6787 EL_EM_GATE_1, -1, -1
6790 Xdoor_2, TRUE, FALSE,
6791 EL_EM_GATE_2, -1, -1
6794 Xdoor_3, TRUE, FALSE,
6795 EL_EM_GATE_3, -1, -1
6798 Xdoor_4, TRUE, FALSE,
6799 EL_EM_GATE_4, -1, -1
6802 Xdoor_5, TRUE, FALSE,
6803 EL_EMC_GATE_5, -1, -1
6806 Xdoor_6, TRUE, FALSE,
6807 EL_EMC_GATE_6, -1, -1
6810 Xdoor_7, TRUE, FALSE,
6811 EL_EMC_GATE_7, -1, -1
6814 Xdoor_8, TRUE, FALSE,
6815 EL_EMC_GATE_8, -1, -1
6819 Xfake_door_1, TRUE, FALSE,
6820 EL_EM_GATE_1_GRAY, -1, -1
6823 Xfake_door_2, TRUE, FALSE,
6824 EL_EM_GATE_2_GRAY, -1, -1
6827 Xfake_door_3, TRUE, FALSE,
6828 EL_EM_GATE_3_GRAY, -1, -1
6831 Xfake_door_4, TRUE, FALSE,
6832 EL_EM_GATE_4_GRAY, -1, -1
6835 Xfake_door_5, TRUE, FALSE,
6836 EL_EMC_GATE_5_GRAY, -1, -1
6839 Xfake_door_6, TRUE, FALSE,
6840 EL_EMC_GATE_6_GRAY, -1, -1
6843 Xfake_door_7, TRUE, FALSE,
6844 EL_EMC_GATE_7_GRAY, -1, -1
6847 Xfake_door_8, TRUE, FALSE,
6848 EL_EMC_GATE_8_GRAY, -1, -1
6852 Xballoon, TRUE, FALSE,
6856 Yballoon_n, FALSE, FALSE,
6857 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6860 Yballoon_nB, FALSE, TRUE,
6861 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6864 Yballoon_e, FALSE, FALSE,
6865 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6868 Yballoon_eB, FALSE, TRUE,
6869 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6872 Yballoon_s, FALSE, FALSE,
6873 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6876 Yballoon_sB, FALSE, TRUE,
6877 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6880 Yballoon_w, FALSE, FALSE,
6881 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6884 Yballoon_wB, FALSE, TRUE,
6885 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6889 Xball_1, TRUE, FALSE,
6890 EL_EMC_MAGIC_BALL, -1, -1
6893 Yball_1, FALSE, FALSE,
6894 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6897 Xball_2, FALSE, FALSE,
6898 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6901 Yball_2, FALSE, FALSE,
6902 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6905 Yball_blank, FALSE, FALSE,
6906 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6910 Xamoeba_1, TRUE, FALSE,
6911 EL_AMOEBA_DRY, ACTION_OTHER, -1
6914 Xamoeba_2, FALSE, FALSE,
6915 EL_AMOEBA_DRY, ACTION_OTHER, -1
6918 Xamoeba_3, FALSE, FALSE,
6919 EL_AMOEBA_DRY, ACTION_OTHER, -1
6922 Xamoeba_4, FALSE, FALSE,
6923 EL_AMOEBA_DRY, ACTION_OTHER, -1
6926 Xamoeba_5, TRUE, FALSE,
6927 EL_AMOEBA_WET, ACTION_OTHER, -1
6930 Xamoeba_6, FALSE, FALSE,
6931 EL_AMOEBA_WET, ACTION_OTHER, -1
6934 Xamoeba_7, FALSE, FALSE,
6935 EL_AMOEBA_WET, ACTION_OTHER, -1
6938 Xamoeba_8, FALSE, FALSE,
6939 EL_AMOEBA_WET, ACTION_OTHER, -1
6944 EL_AMOEBA_DROP, ACTION_GROWING, -1
6947 Xdrip_fall, FALSE, FALSE,
6948 EL_AMOEBA_DROP, -1, -1
6951 Xdrip_stretch, FALSE, FALSE,
6952 EL_AMOEBA_DROP, ACTION_FALLING, -1
6955 Xdrip_stretchB, FALSE, TRUE,
6956 EL_AMOEBA_DROP, ACTION_FALLING, -1
6959 Ydrip_1_s, FALSE, FALSE,
6960 EL_AMOEBA_DROP, ACTION_FALLING, -1
6963 Ydrip_1_sB, FALSE, TRUE,
6964 EL_AMOEBA_DROP, ACTION_FALLING, -1
6967 Ydrip_2_s, FALSE, FALSE,
6968 EL_AMOEBA_DROP, ACTION_FALLING, -1
6971 Ydrip_2_sB, FALSE, TRUE,
6972 EL_AMOEBA_DROP, ACTION_FALLING, -1
6976 Xwonderwall, TRUE, FALSE,
6977 EL_MAGIC_WALL, -1, -1
6980 Ywonderwall, FALSE, FALSE,
6981 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6985 Xwheel, TRUE, FALSE,
6986 EL_ROBOT_WHEEL, -1, -1
6989 Ywheel, FALSE, FALSE,
6990 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6994 Xswitch, TRUE, FALSE,
6995 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6998 Yswitch, FALSE, FALSE,
6999 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7003 Xbumper, TRUE, FALSE,
7004 EL_EMC_SPRING_BUMPER, -1, -1
7007 Ybumper, FALSE, FALSE,
7008 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7012 Xacid_nw, TRUE, FALSE,
7013 EL_ACID_POOL_TOPLEFT, -1, -1
7016 Xacid_ne, TRUE, FALSE,
7017 EL_ACID_POOL_TOPRIGHT, -1, -1
7020 Xacid_sw, TRUE, FALSE,
7021 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7024 Xacid_s, TRUE, FALSE,
7025 EL_ACID_POOL_BOTTOM, -1, -1
7028 Xacid_se, TRUE, FALSE,
7029 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7033 Xfake_blank, TRUE, FALSE,
7034 EL_INVISIBLE_WALL, -1, -1
7037 Yfake_blank, FALSE, FALSE,
7038 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7042 Xfake_grass, TRUE, FALSE,
7043 EL_EMC_FAKE_GRASS, -1, -1
7046 Yfake_grass, FALSE, FALSE,
7047 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7051 Xfake_amoeba, TRUE, FALSE,
7052 EL_EMC_DRIPPER, -1, -1
7055 Yfake_amoeba, FALSE, FALSE,
7056 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7060 Xlenses, TRUE, FALSE,
7061 EL_EMC_LENSES, -1, -1
7065 Xmagnify, TRUE, FALSE,
7066 EL_EMC_MAGNIFIER, -1, -1
7071 EL_QUICKSAND_EMPTY, -1, -1
7074 Xsand_stone, TRUE, FALSE,
7075 EL_QUICKSAND_FULL, -1, -1
7078 Xsand_stonein_1, FALSE, TRUE,
7079 EL_ROCK, ACTION_FILLING, -1
7082 Xsand_stonein_2, FALSE, TRUE,
7083 EL_ROCK, ACTION_FILLING, -1
7086 Xsand_stonein_3, FALSE, TRUE,
7087 EL_ROCK, ACTION_FILLING, -1
7090 Xsand_stonein_4, FALSE, TRUE,
7091 EL_ROCK, ACTION_FILLING, -1
7094 Xsand_sandstone_1, FALSE, FALSE,
7095 EL_QUICKSAND_FILLING, -1, -1
7098 Xsand_sandstone_2, FALSE, FALSE,
7099 EL_QUICKSAND_FILLING, -1, -1
7102 Xsand_sandstone_3, FALSE, FALSE,
7103 EL_QUICKSAND_FILLING, -1, -1
7106 Xsand_sandstone_4, FALSE, FALSE,
7107 EL_QUICKSAND_FILLING, -1, -1
7110 Xsand_stonesand_1, FALSE, FALSE,
7111 EL_QUICKSAND_EMPTYING, -1, -1
7114 Xsand_stonesand_2, FALSE, FALSE,
7115 EL_QUICKSAND_EMPTYING, -1, -1
7118 Xsand_stonesand_3, FALSE, FALSE,
7119 EL_QUICKSAND_EMPTYING, -1, -1
7122 Xsand_stonesand_4, FALSE, FALSE,
7123 EL_QUICKSAND_EMPTYING, -1, -1
7126 Xsand_stoneout_1, FALSE, FALSE,
7127 EL_ROCK, ACTION_EMPTYING, -1
7130 Xsand_stoneout_2, FALSE, FALSE,
7131 EL_ROCK, ACTION_EMPTYING, -1
7134 Xsand_stonesand_quickout_1, FALSE, FALSE,
7135 EL_QUICKSAND_EMPTYING, -1, -1
7138 Xsand_stonesand_quickout_2, FALSE, FALSE,
7139 EL_QUICKSAND_EMPTYING, -1, -1
7143 Xslide_ns, TRUE, FALSE,
7144 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7147 Yslide_ns_blank, FALSE, FALSE,
7148 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7151 Xslide_ew, TRUE, FALSE,
7152 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7155 Yslide_ew_blank, FALSE, FALSE,
7156 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7160 Xwind_n, TRUE, FALSE,
7161 EL_BALLOON_SWITCH_UP, -1, -1
7164 Xwind_e, TRUE, FALSE,
7165 EL_BALLOON_SWITCH_RIGHT, -1, -1
7168 Xwind_s, TRUE, FALSE,
7169 EL_BALLOON_SWITCH_DOWN, -1, -1
7172 Xwind_w, TRUE, FALSE,
7173 EL_BALLOON_SWITCH_LEFT, -1, -1
7176 Xwind_any, TRUE, FALSE,
7177 EL_BALLOON_SWITCH_ANY, -1, -1
7180 Xwind_stop, TRUE, FALSE,
7181 EL_BALLOON_SWITCH_NONE, -1, -1
7186 EL_EM_EXIT_CLOSED, -1, -1
7189 Xexit_1, TRUE, FALSE,
7190 EL_EM_EXIT_OPEN, -1, -1
7193 Xexit_2, FALSE, FALSE,
7194 EL_EM_EXIT_OPEN, -1, -1
7197 Xexit_3, FALSE, FALSE,
7198 EL_EM_EXIT_OPEN, -1, -1
7202 Xpause, FALSE, FALSE,
7207 Xwall_1, TRUE, FALSE,
7211 Xwall_2, TRUE, FALSE,
7212 EL_EMC_WALL_14, -1, -1
7215 Xwall_3, TRUE, FALSE,
7216 EL_EMC_WALL_15, -1, -1
7219 Xwall_4, TRUE, FALSE,
7220 EL_EMC_WALL_16, -1, -1
7224 Xroundwall_1, TRUE, FALSE,
7225 EL_WALL_SLIPPERY, -1, -1
7228 Xroundwall_2, TRUE, FALSE,
7229 EL_EMC_WALL_SLIPPERY_2, -1, -1
7232 Xroundwall_3, TRUE, FALSE,
7233 EL_EMC_WALL_SLIPPERY_3, -1, -1
7236 Xroundwall_4, TRUE, FALSE,
7237 EL_EMC_WALL_SLIPPERY_4, -1, -1
7241 Xsteel_1, TRUE, FALSE,
7242 EL_STEELWALL, -1, -1
7245 Xsteel_2, TRUE, FALSE,
7246 EL_EMC_STEELWALL_2, -1, -1
7249 Xsteel_3, TRUE, FALSE,
7250 EL_EMC_STEELWALL_3, -1, -1
7253 Xsteel_4, TRUE, FALSE,
7254 EL_EMC_STEELWALL_4, -1, -1
7258 Xdecor_1, TRUE, FALSE,
7259 EL_EMC_WALL_8, -1, -1
7262 Xdecor_2, TRUE, FALSE,
7263 EL_EMC_WALL_6, -1, -1
7266 Xdecor_3, TRUE, FALSE,
7267 EL_EMC_WALL_4, -1, -1
7270 Xdecor_4, TRUE, FALSE,
7271 EL_EMC_WALL_7, -1, -1
7274 Xdecor_5, TRUE, FALSE,
7275 EL_EMC_WALL_5, -1, -1
7278 Xdecor_6, TRUE, FALSE,
7279 EL_EMC_WALL_9, -1, -1
7282 Xdecor_7, TRUE, FALSE,
7283 EL_EMC_WALL_10, -1, -1
7286 Xdecor_8, TRUE, FALSE,
7287 EL_EMC_WALL_1, -1, -1
7290 Xdecor_9, TRUE, FALSE,
7291 EL_EMC_WALL_2, -1, -1
7294 Xdecor_10, TRUE, FALSE,
7295 EL_EMC_WALL_3, -1, -1
7298 Xdecor_11, TRUE, FALSE,
7299 EL_EMC_WALL_11, -1, -1
7302 Xdecor_12, TRUE, FALSE,
7303 EL_EMC_WALL_12, -1, -1
7307 Xalpha_0, TRUE, FALSE,
7308 EL_CHAR('0'), -1, -1
7311 Xalpha_1, TRUE, FALSE,
7312 EL_CHAR('1'), -1, -1
7315 Xalpha_2, TRUE, FALSE,
7316 EL_CHAR('2'), -1, -1
7319 Xalpha_3, TRUE, FALSE,
7320 EL_CHAR('3'), -1, -1
7323 Xalpha_4, TRUE, FALSE,
7324 EL_CHAR('4'), -1, -1
7327 Xalpha_5, TRUE, FALSE,
7328 EL_CHAR('5'), -1, -1
7331 Xalpha_6, TRUE, FALSE,
7332 EL_CHAR('6'), -1, -1
7335 Xalpha_7, TRUE, FALSE,
7336 EL_CHAR('7'), -1, -1
7339 Xalpha_8, TRUE, FALSE,
7340 EL_CHAR('8'), -1, -1
7343 Xalpha_9, TRUE, FALSE,
7344 EL_CHAR('9'), -1, -1
7347 Xalpha_excla, TRUE, FALSE,
7348 EL_CHAR('!'), -1, -1
7351 Xalpha_apost, TRUE, FALSE,
7352 EL_CHAR('\''), -1, -1
7355 Xalpha_comma, TRUE, FALSE,
7356 EL_CHAR(','), -1, -1
7359 Xalpha_minus, TRUE, FALSE,
7360 EL_CHAR('-'), -1, -1
7363 Xalpha_perio, TRUE, FALSE,
7364 EL_CHAR('.'), -1, -1
7367 Xalpha_colon, TRUE, FALSE,
7368 EL_CHAR(':'), -1, -1
7371 Xalpha_quest, TRUE, FALSE,
7372 EL_CHAR('?'), -1, -1
7375 Xalpha_a, TRUE, FALSE,
7376 EL_CHAR('A'), -1, -1
7379 Xalpha_b, TRUE, FALSE,
7380 EL_CHAR('B'), -1, -1
7383 Xalpha_c, TRUE, FALSE,
7384 EL_CHAR('C'), -1, -1
7387 Xalpha_d, TRUE, FALSE,
7388 EL_CHAR('D'), -1, -1
7391 Xalpha_e, TRUE, FALSE,
7392 EL_CHAR('E'), -1, -1
7395 Xalpha_f, TRUE, FALSE,
7396 EL_CHAR('F'), -1, -1
7399 Xalpha_g, TRUE, FALSE,
7400 EL_CHAR('G'), -1, -1
7403 Xalpha_h, TRUE, FALSE,
7404 EL_CHAR('H'), -1, -1
7407 Xalpha_i, TRUE, FALSE,
7408 EL_CHAR('I'), -1, -1
7411 Xalpha_j, TRUE, FALSE,
7412 EL_CHAR('J'), -1, -1
7415 Xalpha_k, TRUE, FALSE,
7416 EL_CHAR('K'), -1, -1
7419 Xalpha_l, TRUE, FALSE,
7420 EL_CHAR('L'), -1, -1
7423 Xalpha_m, TRUE, FALSE,
7424 EL_CHAR('M'), -1, -1
7427 Xalpha_n, TRUE, FALSE,
7428 EL_CHAR('N'), -1, -1
7431 Xalpha_o, TRUE, FALSE,
7432 EL_CHAR('O'), -1, -1
7435 Xalpha_p, TRUE, FALSE,
7436 EL_CHAR('P'), -1, -1
7439 Xalpha_q, TRUE, FALSE,
7440 EL_CHAR('Q'), -1, -1
7443 Xalpha_r, TRUE, FALSE,
7444 EL_CHAR('R'), -1, -1
7447 Xalpha_s, TRUE, FALSE,
7448 EL_CHAR('S'), -1, -1
7451 Xalpha_t, TRUE, FALSE,
7452 EL_CHAR('T'), -1, -1
7455 Xalpha_u, TRUE, FALSE,
7456 EL_CHAR('U'), -1, -1
7459 Xalpha_v, TRUE, FALSE,
7460 EL_CHAR('V'), -1, -1
7463 Xalpha_w, TRUE, FALSE,
7464 EL_CHAR('W'), -1, -1
7467 Xalpha_x, TRUE, FALSE,
7468 EL_CHAR('X'), -1, -1
7471 Xalpha_y, TRUE, FALSE,
7472 EL_CHAR('Y'), -1, -1
7475 Xalpha_z, TRUE, FALSE,
7476 EL_CHAR('Z'), -1, -1
7479 Xalpha_arrow_e, TRUE, FALSE,
7480 EL_CHAR('>'), -1, -1
7483 Xalpha_arrow_w, TRUE, FALSE,
7484 EL_CHAR('<'), -1, -1
7487 Xalpha_copyr, TRUE, FALSE,
7488 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7492 Ykey_1_blank, FALSE, FALSE,
7493 EL_EM_KEY_1, ACTION_COLLECTING, -1
7496 Ykey_2_blank, FALSE, FALSE,
7497 EL_EM_KEY_2, ACTION_COLLECTING, -1
7500 Ykey_3_blank, FALSE, FALSE,
7501 EL_EM_KEY_3, ACTION_COLLECTING, -1
7504 Ykey_4_blank, FALSE, FALSE,
7505 EL_EM_KEY_4, ACTION_COLLECTING, -1
7508 Ykey_5_blank, FALSE, FALSE,
7509 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7512 Ykey_6_blank, FALSE, FALSE,
7513 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7516 Ykey_7_blank, FALSE, FALSE,
7517 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7520 Ykey_8_blank, FALSE, FALSE,
7521 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7524 Ylenses_blank, FALSE, FALSE,
7525 EL_EMC_LENSES, ACTION_COLLECTING, -1
7528 Ymagnify_blank, FALSE, FALSE,
7529 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7532 Ygrass_blank, FALSE, FALSE,
7533 EL_EMC_GRASS, ACTION_SNAPPING, -1
7536 Ydirt_blank, FALSE, FALSE,
7537 EL_SAND, ACTION_SNAPPING, -1
7546 static struct Mapping_EM_to_RND_player
7555 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7559 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7563 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7567 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7571 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7575 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7579 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7583 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7587 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7591 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7595 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7599 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7603 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7607 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7611 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7615 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7619 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7623 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7627 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7631 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7635 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7639 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7643 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7647 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7651 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7655 EL_PLAYER_1, ACTION_DEFAULT, -1,
7659 EL_PLAYER_2, ACTION_DEFAULT, -1,
7663 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7667 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7671 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7675 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7679 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7683 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7687 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7691 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7695 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7699 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7703 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7707 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7711 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7715 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7719 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7723 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7727 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7731 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7735 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7739 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7743 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7747 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7751 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7755 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7759 EL_PLAYER_3, ACTION_DEFAULT, -1,
7763 EL_PLAYER_4, ACTION_DEFAULT, -1,
7772 int map_element_RND_to_EM_cave(int element_rnd)
7774 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7775 static boolean mapping_initialized = FALSE;
7777 if (!mapping_initialized)
7781 // return "Xalpha_quest" for all undefined elements in mapping array
7782 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7783 mapping_RND_to_EM[i] = Xalpha_quest;
7785 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7786 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7787 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7788 em_object_mapping_list[i].element_em;
7790 mapping_initialized = TRUE;
7793 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7795 Warn("invalid RND level element %d", element_rnd);
7800 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7803 int map_element_EM_to_RND_cave(int element_em_cave)
7805 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7806 static boolean mapping_initialized = FALSE;
7808 if (!mapping_initialized)
7812 // return "EL_UNKNOWN" for all undefined elements in mapping array
7813 for (i = 0; i < GAME_TILE_MAX; i++)
7814 mapping_EM_to_RND[i] = EL_UNKNOWN;
7816 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7817 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7818 em_object_mapping_list[i].element_rnd;
7820 mapping_initialized = TRUE;
7823 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7825 Warn("invalid EM cave element %d", element_em_cave);
7830 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7833 int map_element_EM_to_RND_game(int element_em_game)
7835 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7836 static boolean mapping_initialized = FALSE;
7838 if (!mapping_initialized)
7842 // return "EL_UNKNOWN" for all undefined elements in mapping array
7843 for (i = 0; i < GAME_TILE_MAX; i++)
7844 mapping_EM_to_RND[i] = EL_UNKNOWN;
7846 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7847 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7848 em_object_mapping_list[i].element_rnd;
7850 mapping_initialized = TRUE;
7853 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7855 Warn("invalid EM game element %d", element_em_game);
7860 return mapping_EM_to_RND[element_em_game];
7863 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7865 struct LevelInfo_EM *level_em = level->native_em_level;
7866 struct CAVE *cav = level_em->cav;
7869 for (i = 0; i < GAME_TILE_MAX; i++)
7870 cav->android_array[i] = Cblank;
7872 for (i = 0; i < level->num_android_clone_elements; i++)
7874 int element_rnd = level->android_clone_element[i];
7875 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7877 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7878 if (em_object_mapping_list[j].element_rnd == element_rnd)
7879 cav->android_array[em_object_mapping_list[j].element_em] =
7884 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7886 struct LevelInfo_EM *level_em = level->native_em_level;
7887 struct CAVE *cav = level_em->cav;
7890 level->num_android_clone_elements = 0;
7892 for (i = 0; i < GAME_TILE_MAX; i++)
7894 int element_em_cave = cav->android_array[i];
7896 boolean element_found = FALSE;
7898 if (element_em_cave == Cblank)
7901 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7903 for (j = 0; j < level->num_android_clone_elements; j++)
7904 if (level->android_clone_element[j] == element_rnd)
7905 element_found = TRUE;
7909 level->android_clone_element[level->num_android_clone_elements++] =
7912 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7917 if (level->num_android_clone_elements == 0)
7919 level->num_android_clone_elements = 1;
7920 level->android_clone_element[0] = EL_EMPTY;
7924 int map_direction_RND_to_EM(int direction)
7926 return (direction == MV_UP ? 0 :
7927 direction == MV_RIGHT ? 1 :
7928 direction == MV_DOWN ? 2 :
7929 direction == MV_LEFT ? 3 :
7933 int map_direction_EM_to_RND(int direction)
7935 return (direction == 0 ? MV_UP :
7936 direction == 1 ? MV_RIGHT :
7937 direction == 2 ? MV_DOWN :
7938 direction == 3 ? MV_LEFT :
7942 int map_element_RND_to_SP(int element_rnd)
7944 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7946 if (element_rnd >= EL_SP_START &&
7947 element_rnd <= EL_SP_END)
7948 element_sp = element_rnd - EL_SP_START;
7949 else if (element_rnd == EL_EMPTY_SPACE)
7951 else if (element_rnd == EL_INVISIBLE_WALL)
7957 int map_element_SP_to_RND(int element_sp)
7959 int element_rnd = EL_UNKNOWN;
7961 if (element_sp >= 0x00 &&
7963 element_rnd = EL_SP_START + element_sp;
7964 else if (element_sp == 0x28)
7965 element_rnd = EL_INVISIBLE_WALL;
7970 int map_action_SP_to_RND(int action_sp)
7974 case actActive: return ACTION_ACTIVE;
7975 case actImpact: return ACTION_IMPACT;
7976 case actExploding: return ACTION_EXPLODING;
7977 case actDigging: return ACTION_DIGGING;
7978 case actSnapping: return ACTION_SNAPPING;
7979 case actCollecting: return ACTION_COLLECTING;
7980 case actPassing: return ACTION_PASSING;
7981 case actPushing: return ACTION_PUSHING;
7982 case actDropping: return ACTION_DROPPING;
7984 default: return ACTION_DEFAULT;
7988 int map_element_RND_to_MM(int element_rnd)
7990 return (element_rnd >= EL_MM_START_1 &&
7991 element_rnd <= EL_MM_END_1 ?
7992 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7994 element_rnd >= EL_MM_START_2 &&
7995 element_rnd <= EL_MM_END_2 ?
7996 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7998 element_rnd >= EL_CHAR_START &&
7999 element_rnd <= EL_CHAR_END ?
8000 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8002 element_rnd >= EL_MM_RUNTIME_START &&
8003 element_rnd <= EL_MM_RUNTIME_END ?
8004 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8006 element_rnd >= EL_MM_DUMMY_START &&
8007 element_rnd <= EL_MM_DUMMY_END ?
8008 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8010 EL_MM_EMPTY_NATIVE);
8013 int map_element_MM_to_RND(int element_mm)
8015 return (element_mm == EL_MM_EMPTY_NATIVE ||
8016 element_mm == EL_DF_EMPTY_NATIVE ?
8019 element_mm >= EL_MM_START_1_NATIVE &&
8020 element_mm <= EL_MM_END_1_NATIVE ?
8021 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8023 element_mm >= EL_MM_START_2_NATIVE &&
8024 element_mm <= EL_MM_END_2_NATIVE ?
8025 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8027 element_mm >= EL_MM_CHAR_START_NATIVE &&
8028 element_mm <= EL_MM_CHAR_END_NATIVE ?
8029 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8031 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8032 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8033 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8035 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8036 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8037 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8042 int map_action_MM_to_RND(int action_mm)
8044 // all MM actions are defined to exactly match their RND counterparts
8048 int map_sound_MM_to_RND(int sound_mm)
8052 case SND_MM_GAME_LEVELTIME_CHARGING:
8053 return SND_GAME_LEVELTIME_CHARGING;
8055 case SND_MM_GAME_HEALTH_CHARGING:
8056 return SND_GAME_HEALTH_CHARGING;
8059 return SND_UNDEFINED;
8063 int map_mm_wall_element(int element)
8065 return (element >= EL_MM_STEEL_WALL_START &&
8066 element <= EL_MM_STEEL_WALL_END ?
8069 element >= EL_MM_WOODEN_WALL_START &&
8070 element <= EL_MM_WOODEN_WALL_END ?
8073 element >= EL_MM_ICE_WALL_START &&
8074 element <= EL_MM_ICE_WALL_END ?
8077 element >= EL_MM_AMOEBA_WALL_START &&
8078 element <= EL_MM_AMOEBA_WALL_END ?
8081 element >= EL_DF_STEEL_WALL_START &&
8082 element <= EL_DF_STEEL_WALL_END ?
8085 element >= EL_DF_WOODEN_WALL_START &&
8086 element <= EL_DF_WOODEN_WALL_END ?
8092 int map_mm_wall_element_editor(int element)
8096 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8097 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8098 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8099 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8100 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8101 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8103 default: return element;
8107 int get_next_element(int element)
8111 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8112 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8113 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8114 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8115 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8116 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8117 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8118 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8119 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8120 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8121 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8123 default: return element;
8127 int el2img_mm(int element_mm)
8129 return el2img(map_element_MM_to_RND(element_mm));
8132 int el_act_dir2img(int element, int action, int direction)
8134 element = GFX_ELEMENT(element);
8135 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8137 // direction_graphic[][] == graphic[] for undefined direction graphics
8138 return element_info[element].direction_graphic[action][direction];
8141 static int el_act_dir2crm(int element, int action, int direction)
8143 element = GFX_ELEMENT(element);
8144 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8146 // direction_graphic[][] == graphic[] for undefined direction graphics
8147 return element_info[element].direction_crumbled[action][direction];
8150 int el_act2img(int element, int action)
8152 element = GFX_ELEMENT(element);
8154 return element_info[element].graphic[action];
8157 int el_act2crm(int element, int action)
8159 element = GFX_ELEMENT(element);
8161 return element_info[element].crumbled[action];
8164 int el_dir2img(int element, int direction)
8166 element = GFX_ELEMENT(element);
8168 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8171 int el2baseimg(int element)
8173 return element_info[element].graphic[ACTION_DEFAULT];
8176 int el2img(int element)
8178 element = GFX_ELEMENT(element);
8180 return element_info[element].graphic[ACTION_DEFAULT];
8183 int el2edimg(int element)
8185 element = GFX_ELEMENT(element);
8187 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8190 int el2preimg(int element)
8192 element = GFX_ELEMENT(element);
8194 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8197 int el2panelimg(int element)
8199 element = GFX_ELEMENT(element);
8201 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8204 int font2baseimg(int font_nr)
8206 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8209 int getBeltNrFromBeltElement(int element)
8211 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8212 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8213 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8216 int getBeltNrFromBeltActiveElement(int element)
8218 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8219 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8220 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8223 int getBeltNrFromBeltSwitchElement(int element)
8225 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8226 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8227 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8230 int getBeltDirNrFromBeltElement(int element)
8232 static int belt_base_element[4] =
8234 EL_CONVEYOR_BELT_1_LEFT,
8235 EL_CONVEYOR_BELT_2_LEFT,
8236 EL_CONVEYOR_BELT_3_LEFT,
8237 EL_CONVEYOR_BELT_4_LEFT
8240 int belt_nr = getBeltNrFromBeltElement(element);
8241 int belt_dir_nr = element - belt_base_element[belt_nr];
8243 return (belt_dir_nr % 3);
8246 int getBeltDirNrFromBeltSwitchElement(int element)
8248 static int belt_base_element[4] =
8250 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8251 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8252 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8253 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8256 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8257 int belt_dir_nr = element - belt_base_element[belt_nr];
8259 return (belt_dir_nr % 3);
8262 int getBeltDirFromBeltElement(int element)
8264 static int belt_move_dir[3] =
8271 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8273 return belt_move_dir[belt_dir_nr];
8276 int getBeltDirFromBeltSwitchElement(int element)
8278 static int belt_move_dir[3] =
8285 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8287 return belt_move_dir[belt_dir_nr];
8290 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8292 static int belt_base_element[4] =
8294 EL_CONVEYOR_BELT_1_LEFT,
8295 EL_CONVEYOR_BELT_2_LEFT,
8296 EL_CONVEYOR_BELT_3_LEFT,
8297 EL_CONVEYOR_BELT_4_LEFT
8300 return belt_base_element[belt_nr] + belt_dir_nr;
8303 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8305 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8307 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8310 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8312 static int belt_base_element[4] =
8314 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8315 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8316 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8317 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8320 return belt_base_element[belt_nr] + belt_dir_nr;
8323 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8325 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8327 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8330 boolean swapTiles_EM(boolean is_pre_emc_cave)
8332 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8335 boolean getTeamMode_EM(void)
8337 return game.team_mode || network_playing;
8340 boolean isActivePlayer_EM(int player_nr)
8342 return stored_player[player_nr].active;
8345 unsigned int InitRND(int seed)
8347 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8348 return InitEngineRandom_EM(seed);
8349 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8350 return InitEngineRandom_SP(seed);
8351 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8352 return InitEngineRandom_MM(seed);
8354 return InitEngineRandom_RND(seed);
8357 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8358 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8360 static int get_effective_element_EM(int tile, int frame_em)
8362 int element = object_mapping[tile].element_rnd;
8363 int action = object_mapping[tile].action;
8364 boolean is_backside = object_mapping[tile].is_backside;
8365 boolean action_removing = (action == ACTION_DIGGING ||
8366 action == ACTION_SNAPPING ||
8367 action == ACTION_COLLECTING);
8375 return (frame_em > 5 ? EL_EMPTY : element);
8381 else // frame_em == 7
8392 case Ydiamond_stone:
8396 case Xdrip_stretchB:
8412 case Ymagnify_blank:
8415 case Xsand_stonein_1:
8416 case Xsand_stonein_2:
8417 case Xsand_stonein_3:
8418 case Xsand_stonein_4:
8422 return (is_backside || action_removing ? EL_EMPTY : element);
8427 static boolean check_linear_animation_EM(int tile)
8431 case Xsand_stonesand_1:
8432 case Xsand_stonesand_quickout_1:
8433 case Xsand_sandstone_1:
8434 case Xsand_stonein_1:
8435 case Xsand_stoneout_1:
8463 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8464 boolean has_crumbled_graphics,
8465 int crumbled, int sync_frame)
8467 // if element can be crumbled, but certain action graphics are just empty
8468 // space (like instantly snapping sand to empty space in 1 frame), do not
8469 // treat these empty space graphics as crumbled graphics in EMC engine
8470 if (crumbled == IMG_EMPTY_SPACE)
8471 has_crumbled_graphics = FALSE;
8473 if (has_crumbled_graphics)
8475 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8476 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8477 g_crumbled->anim_delay,
8478 g_crumbled->anim_mode,
8479 g_crumbled->anim_start_frame,
8482 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8483 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8485 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8486 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8488 g_em->has_crumbled_graphics = TRUE;
8492 g_em->crumbled_bitmap = NULL;
8493 g_em->crumbled_src_x = 0;
8494 g_em->crumbled_src_y = 0;
8495 g_em->crumbled_border_size = 0;
8496 g_em->crumbled_tile_size = 0;
8498 g_em->has_crumbled_graphics = FALSE;
8503 void ResetGfxAnimation_EM(int x, int y, int tile)
8509 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8510 int tile, int frame_em, int x, int y)
8512 int action = object_mapping[tile].action;
8513 int direction = object_mapping[tile].direction;
8514 int effective_element = get_effective_element_EM(tile, frame_em);
8515 int graphic = (direction == MV_NONE ?
8516 el_act2img(effective_element, action) :
8517 el_act_dir2img(effective_element, action, direction));
8518 struct GraphicInfo *g = &graphic_info[graphic];
8520 boolean action_removing = (action == ACTION_DIGGING ||
8521 action == ACTION_SNAPPING ||
8522 action == ACTION_COLLECTING);
8523 boolean action_moving = (action == ACTION_FALLING ||
8524 action == ACTION_MOVING ||
8525 action == ACTION_PUSHING ||
8526 action == ACTION_EATING ||
8527 action == ACTION_FILLING ||
8528 action == ACTION_EMPTYING);
8529 boolean action_falling = (action == ACTION_FALLING ||
8530 action == ACTION_FILLING ||
8531 action == ACTION_EMPTYING);
8533 // special case: graphic uses "2nd movement tile" and has defined
8534 // 7 frames for movement animation (or less) => use default graphic
8535 // for last (8th) frame which ends the movement animation
8536 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8538 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8539 graphic = (direction == MV_NONE ?
8540 el_act2img(effective_element, action) :
8541 el_act_dir2img(effective_element, action, direction));
8543 g = &graphic_info[graphic];
8546 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8550 else if (action_moving)
8552 boolean is_backside = object_mapping[tile].is_backside;
8556 int direction = object_mapping[tile].direction;
8557 int move_dir = (action_falling ? MV_DOWN : direction);
8562 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8563 if (g->double_movement && frame_em == 0)
8567 if (move_dir == MV_LEFT)
8568 GfxFrame[x - 1][y] = GfxFrame[x][y];
8569 else if (move_dir == MV_RIGHT)
8570 GfxFrame[x + 1][y] = GfxFrame[x][y];
8571 else if (move_dir == MV_UP)
8572 GfxFrame[x][y - 1] = GfxFrame[x][y];
8573 else if (move_dir == MV_DOWN)
8574 GfxFrame[x][y + 1] = GfxFrame[x][y];
8581 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8582 if (tile == Xsand_stonesand_quickout_1 ||
8583 tile == Xsand_stonesand_quickout_2)
8587 if (graphic_info[graphic].anim_global_sync)
8588 sync_frame = FrameCounter;
8589 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8590 sync_frame = GfxFrame[x][y];
8592 sync_frame = 0; // playfield border (pseudo steel)
8594 SetRandomAnimationValue(x, y);
8596 int frame = getAnimationFrame(g->anim_frames,
8599 g->anim_start_frame,
8602 g_em->unique_identifier =
8603 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8606 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8607 int tile, int frame_em, int x, int y)
8609 int action = object_mapping[tile].action;
8610 int direction = object_mapping[tile].direction;
8611 boolean is_backside = object_mapping[tile].is_backside;
8612 int effective_element = get_effective_element_EM(tile, frame_em);
8613 int effective_action = action;
8614 int graphic = (direction == MV_NONE ?
8615 el_act2img(effective_element, effective_action) :
8616 el_act_dir2img(effective_element, effective_action,
8618 int crumbled = (direction == MV_NONE ?
8619 el_act2crm(effective_element, effective_action) :
8620 el_act_dir2crm(effective_element, effective_action,
8622 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8623 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8624 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8625 struct GraphicInfo *g = &graphic_info[graphic];
8628 // special case: graphic uses "2nd movement tile" and has defined
8629 // 7 frames for movement animation (or less) => use default graphic
8630 // for last (8th) frame which ends the movement animation
8631 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8633 effective_action = ACTION_DEFAULT;
8634 graphic = (direction == MV_NONE ?
8635 el_act2img(effective_element, effective_action) :
8636 el_act_dir2img(effective_element, effective_action,
8638 crumbled = (direction == MV_NONE ?
8639 el_act2crm(effective_element, effective_action) :
8640 el_act_dir2crm(effective_element, effective_action,
8643 g = &graphic_info[graphic];
8646 if (graphic_info[graphic].anim_global_sync)
8647 sync_frame = FrameCounter;
8648 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8649 sync_frame = GfxFrame[x][y];
8651 sync_frame = 0; // playfield border (pseudo steel)
8653 SetRandomAnimationValue(x, y);
8655 int frame = getAnimationFrame(g->anim_frames,
8658 g->anim_start_frame,
8661 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8662 g->double_movement && is_backside);
8664 // (updating the "crumbled" graphic definitions is probably not really needed,
8665 // as animations for crumbled graphics can't be longer than one EMC cycle)
8666 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8670 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8671 int player_nr, int anim, int frame_em)
8673 int element = player_mapping[player_nr][anim].element_rnd;
8674 int action = player_mapping[player_nr][anim].action;
8675 int direction = player_mapping[player_nr][anim].direction;
8676 int graphic = (direction == MV_NONE ?
8677 el_act2img(element, action) :
8678 el_act_dir2img(element, action, direction));
8679 struct GraphicInfo *g = &graphic_info[graphic];
8682 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8684 stored_player[player_nr].StepFrame = frame_em;
8686 sync_frame = stored_player[player_nr].Frame;
8688 int frame = getAnimationFrame(g->anim_frames,
8691 g->anim_start_frame,
8694 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8695 &g_em->src_x, &g_em->src_y, FALSE);
8698 void InitGraphicInfo_EM(void)
8702 // always start with reliable default values
8703 for (i = 0; i < GAME_TILE_MAX; i++)
8705 object_mapping[i].element_rnd = EL_UNKNOWN;
8706 object_mapping[i].is_backside = FALSE;
8707 object_mapping[i].action = ACTION_DEFAULT;
8708 object_mapping[i].direction = MV_NONE;
8711 // always start with reliable default values
8712 for (p = 0; p < MAX_PLAYERS; p++)
8714 for (i = 0; i < PLY_MAX; i++)
8716 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8717 player_mapping[p][i].action = ACTION_DEFAULT;
8718 player_mapping[p][i].direction = MV_NONE;
8722 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8724 int e = em_object_mapping_list[i].element_em;
8726 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8727 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8729 if (em_object_mapping_list[i].action != -1)
8730 object_mapping[e].action = em_object_mapping_list[i].action;
8732 if (em_object_mapping_list[i].direction != -1)
8733 object_mapping[e].direction =
8734 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8737 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8739 int a = em_player_mapping_list[i].action_em;
8740 int p = em_player_mapping_list[i].player_nr;
8742 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8744 if (em_player_mapping_list[i].action != -1)
8745 player_mapping[p][a].action = em_player_mapping_list[i].action;
8747 if (em_player_mapping_list[i].direction != -1)
8748 player_mapping[p][a].direction =
8749 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8752 for (i = 0; i < GAME_TILE_MAX; i++)
8754 int element = object_mapping[i].element_rnd;
8755 int action = object_mapping[i].action;
8756 int direction = object_mapping[i].direction;
8757 boolean is_backside = object_mapping[i].is_backside;
8758 boolean action_exploding = ((action == ACTION_EXPLODING ||
8759 action == ACTION_SMASHED_BY_ROCK ||
8760 action == ACTION_SMASHED_BY_SPRING) &&
8761 element != EL_DIAMOND);
8762 boolean action_active = (action == ACTION_ACTIVE);
8763 boolean action_other = (action == ACTION_OTHER);
8765 for (j = 0; j < 8; j++)
8767 int effective_element = get_effective_element_EM(i, j);
8768 int effective_action = (j < 7 ? action :
8769 i == Xdrip_stretch ? action :
8770 i == Xdrip_stretchB ? action :
8771 i == Ydrip_1_s ? action :
8772 i == Ydrip_1_sB ? action :
8773 i == Yball_1 ? action :
8774 i == Xball_2 ? action :
8775 i == Yball_2 ? action :
8776 i == Yball_blank ? action :
8777 i == Ykey_1_blank ? action :
8778 i == Ykey_2_blank ? action :
8779 i == Ykey_3_blank ? action :
8780 i == Ykey_4_blank ? action :
8781 i == Ykey_5_blank ? action :
8782 i == Ykey_6_blank ? action :
8783 i == Ykey_7_blank ? action :
8784 i == Ykey_8_blank ? action :
8785 i == Ylenses_blank ? action :
8786 i == Ymagnify_blank ? action :
8787 i == Ygrass_blank ? action :
8788 i == Ydirt_blank ? action :
8789 i == Xsand_stonein_1 ? action :
8790 i == Xsand_stonein_2 ? action :
8791 i == Xsand_stonein_3 ? action :
8792 i == Xsand_stonein_4 ? action :
8793 i == Xsand_stoneout_1 ? action :
8794 i == Xsand_stoneout_2 ? action :
8795 i == Xboom_android ? ACTION_EXPLODING :
8796 action_exploding ? ACTION_EXPLODING :
8797 action_active ? action :
8798 action_other ? action :
8800 int graphic = (el_act_dir2img(effective_element, effective_action,
8802 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8804 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8805 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8806 boolean has_action_graphics = (graphic != base_graphic);
8807 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8808 struct GraphicInfo *g = &graphic_info[graphic];
8809 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8812 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8813 boolean special_animation = (action != ACTION_DEFAULT &&
8814 g->anim_frames == 3 &&
8815 g->anim_delay == 2 &&
8816 g->anim_mode & ANIM_LINEAR);
8817 int sync_frame = (i == Xdrip_stretch ? 7 :
8818 i == Xdrip_stretchB ? 7 :
8819 i == Ydrip_2_s ? j + 8 :
8820 i == Ydrip_2_sB ? j + 8 :
8829 i == Xfake_acid_1 ? 0 :
8830 i == Xfake_acid_2 ? 10 :
8831 i == Xfake_acid_3 ? 20 :
8832 i == Xfake_acid_4 ? 30 :
8833 i == Xfake_acid_5 ? 40 :
8834 i == Xfake_acid_6 ? 50 :
8835 i == Xfake_acid_7 ? 60 :
8836 i == Xfake_acid_8 ? 70 :
8837 i == Xfake_acid_1_player ? 0 :
8838 i == Xfake_acid_2_player ? 10 :
8839 i == Xfake_acid_3_player ? 20 :
8840 i == Xfake_acid_4_player ? 30 :
8841 i == Xfake_acid_5_player ? 40 :
8842 i == Xfake_acid_6_player ? 50 :
8843 i == Xfake_acid_7_player ? 60 :
8844 i == Xfake_acid_8_player ? 70 :
8846 i == Yball_2 ? j + 8 :
8847 i == Yball_blank ? j + 1 :
8848 i == Ykey_1_blank ? j + 1 :
8849 i == Ykey_2_blank ? j + 1 :
8850 i == Ykey_3_blank ? j + 1 :
8851 i == Ykey_4_blank ? j + 1 :
8852 i == Ykey_5_blank ? j + 1 :
8853 i == Ykey_6_blank ? j + 1 :
8854 i == Ykey_7_blank ? j + 1 :
8855 i == Ykey_8_blank ? j + 1 :
8856 i == Ylenses_blank ? j + 1 :
8857 i == Ymagnify_blank ? j + 1 :
8858 i == Ygrass_blank ? j + 1 :
8859 i == Ydirt_blank ? j + 1 :
8860 i == Xamoeba_1 ? 0 :
8861 i == Xamoeba_2 ? 1 :
8862 i == Xamoeba_3 ? 2 :
8863 i == Xamoeba_4 ? 3 :
8864 i == Xamoeba_5 ? 0 :
8865 i == Xamoeba_6 ? 1 :
8866 i == Xamoeba_7 ? 2 :
8867 i == Xamoeba_8 ? 3 :
8868 i == Xexit_2 ? j + 8 :
8869 i == Xexit_3 ? j + 16 :
8870 i == Xdynamite_1 ? 0 :
8871 i == Xdynamite_2 ? 8 :
8872 i == Xdynamite_3 ? 16 :
8873 i == Xdynamite_4 ? 24 :
8874 i == Xsand_stonein_1 ? j + 1 :
8875 i == Xsand_stonein_2 ? j + 9 :
8876 i == Xsand_stonein_3 ? j + 17 :
8877 i == Xsand_stonein_4 ? j + 25 :
8878 i == Xsand_stoneout_1 && j == 0 ? 0 :
8879 i == Xsand_stoneout_1 && j == 1 ? 0 :
8880 i == Xsand_stoneout_1 && j == 2 ? 1 :
8881 i == Xsand_stoneout_1 && j == 3 ? 2 :
8882 i == Xsand_stoneout_1 && j == 4 ? 2 :
8883 i == Xsand_stoneout_1 && j == 5 ? 3 :
8884 i == Xsand_stoneout_1 && j == 6 ? 4 :
8885 i == Xsand_stoneout_1 && j == 7 ? 4 :
8886 i == Xsand_stoneout_2 && j == 0 ? 5 :
8887 i == Xsand_stoneout_2 && j == 1 ? 6 :
8888 i == Xsand_stoneout_2 && j == 2 ? 7 :
8889 i == Xsand_stoneout_2 && j == 3 ? 8 :
8890 i == Xsand_stoneout_2 && j == 4 ? 9 :
8891 i == Xsand_stoneout_2 && j == 5 ? 11 :
8892 i == Xsand_stoneout_2 && j == 6 ? 13 :
8893 i == Xsand_stoneout_2 && j == 7 ? 15 :
8894 i == Xboom_bug && j == 1 ? 2 :
8895 i == Xboom_bug && j == 2 ? 2 :
8896 i == Xboom_bug && j == 3 ? 4 :
8897 i == Xboom_bug && j == 4 ? 4 :
8898 i == Xboom_bug && j == 5 ? 2 :
8899 i == Xboom_bug && j == 6 ? 2 :
8900 i == Xboom_bug && j == 7 ? 0 :
8901 i == Xboom_tank && j == 1 ? 2 :
8902 i == Xboom_tank && j == 2 ? 2 :
8903 i == Xboom_tank && j == 3 ? 4 :
8904 i == Xboom_tank && j == 4 ? 4 :
8905 i == Xboom_tank && j == 5 ? 2 :
8906 i == Xboom_tank && j == 6 ? 2 :
8907 i == Xboom_tank && j == 7 ? 0 :
8908 i == Xboom_android && j == 7 ? 6 :
8909 i == Xboom_1 && j == 1 ? 2 :
8910 i == Xboom_1 && j == 2 ? 2 :
8911 i == Xboom_1 && j == 3 ? 4 :
8912 i == Xboom_1 && j == 4 ? 4 :
8913 i == Xboom_1 && j == 5 ? 6 :
8914 i == Xboom_1 && j == 6 ? 6 :
8915 i == Xboom_1 && j == 7 ? 8 :
8916 i == Xboom_2 && j == 0 ? 8 :
8917 i == Xboom_2 && j == 1 ? 8 :
8918 i == Xboom_2 && j == 2 ? 10 :
8919 i == Xboom_2 && j == 3 ? 10 :
8920 i == Xboom_2 && j == 4 ? 10 :
8921 i == Xboom_2 && j == 5 ? 12 :
8922 i == Xboom_2 && j == 6 ? 12 :
8923 i == Xboom_2 && j == 7 ? 12 :
8924 special_animation && j == 4 ? 3 :
8925 effective_action != action ? 0 :
8927 int frame = getAnimationFrame(g->anim_frames,
8930 g->anim_start_frame,
8933 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8934 g->double_movement && is_backside);
8936 g_em->bitmap = src_bitmap;
8937 g_em->src_x = src_x;
8938 g_em->src_y = src_y;
8939 g_em->src_offset_x = 0;
8940 g_em->src_offset_y = 0;
8941 g_em->dst_offset_x = 0;
8942 g_em->dst_offset_y = 0;
8943 g_em->width = TILEX;
8944 g_em->height = TILEY;
8946 g_em->preserve_background = FALSE;
8948 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8951 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8952 effective_action == ACTION_MOVING ||
8953 effective_action == ACTION_PUSHING ||
8954 effective_action == ACTION_EATING)) ||
8955 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8956 effective_action == ACTION_EMPTYING)))
8959 (effective_action == ACTION_FALLING ||
8960 effective_action == ACTION_FILLING ||
8961 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8962 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8963 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8964 int num_steps = (i == Ydrip_1_s ? 16 :
8965 i == Ydrip_1_sB ? 16 :
8966 i == Ydrip_2_s ? 16 :
8967 i == Ydrip_2_sB ? 16 :
8968 i == Xsand_stonein_1 ? 32 :
8969 i == Xsand_stonein_2 ? 32 :
8970 i == Xsand_stonein_3 ? 32 :
8971 i == Xsand_stonein_4 ? 32 :
8972 i == Xsand_stoneout_1 ? 16 :
8973 i == Xsand_stoneout_2 ? 16 : 8);
8974 int cx = ABS(dx) * (TILEX / num_steps);
8975 int cy = ABS(dy) * (TILEY / num_steps);
8976 int step_frame = (i == Ydrip_2_s ? j + 8 :
8977 i == Ydrip_2_sB ? j + 8 :
8978 i == Xsand_stonein_2 ? j + 8 :
8979 i == Xsand_stonein_3 ? j + 16 :
8980 i == Xsand_stonein_4 ? j + 24 :
8981 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8982 int step = (is_backside ? step_frame : num_steps - step_frame);
8984 if (is_backside) // tile where movement starts
8986 if (dx < 0 || dy < 0)
8988 g_em->src_offset_x = cx * step;
8989 g_em->src_offset_y = cy * step;
8993 g_em->dst_offset_x = cx * step;
8994 g_em->dst_offset_y = cy * step;
8997 else // tile where movement ends
8999 if (dx < 0 || dy < 0)
9001 g_em->dst_offset_x = cx * step;
9002 g_em->dst_offset_y = cy * step;
9006 g_em->src_offset_x = cx * step;
9007 g_em->src_offset_y = cy * step;
9011 g_em->width = TILEX - cx * step;
9012 g_em->height = TILEY - cy * step;
9015 // create unique graphic identifier to decide if tile must be redrawn
9016 /* bit 31 - 16 (16 bit): EM style graphic
9017 bit 15 - 12 ( 4 bit): EM style frame
9018 bit 11 - 6 ( 6 bit): graphic width
9019 bit 5 - 0 ( 6 bit): graphic height */
9020 g_em->unique_identifier =
9021 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9025 for (i = 0; i < GAME_TILE_MAX; i++)
9027 for (j = 0; j < 8; j++)
9029 int element = object_mapping[i].element_rnd;
9030 int action = object_mapping[i].action;
9031 int direction = object_mapping[i].direction;
9032 boolean is_backside = object_mapping[i].is_backside;
9033 int graphic_action = el_act_dir2img(element, action, direction);
9034 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9036 if ((action == ACTION_SMASHED_BY_ROCK ||
9037 action == ACTION_SMASHED_BY_SPRING ||
9038 action == ACTION_EATING) &&
9039 graphic_action == graphic_default)
9041 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9042 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9043 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9044 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9047 // no separate animation for "smashed by rock" -- use rock instead
9048 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9049 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9051 g_em->bitmap = g_xx->bitmap;
9052 g_em->src_x = g_xx->src_x;
9053 g_em->src_y = g_xx->src_y;
9054 g_em->src_offset_x = g_xx->src_offset_x;
9055 g_em->src_offset_y = g_xx->src_offset_y;
9056 g_em->dst_offset_x = g_xx->dst_offset_x;
9057 g_em->dst_offset_y = g_xx->dst_offset_y;
9058 g_em->width = g_xx->width;
9059 g_em->height = g_xx->height;
9060 g_em->unique_identifier = g_xx->unique_identifier;
9063 g_em->preserve_background = TRUE;
9068 for (p = 0; p < MAX_PLAYERS; p++)
9070 for (i = 0; i < PLY_MAX; i++)
9072 int element = player_mapping[p][i].element_rnd;
9073 int action = player_mapping[p][i].action;
9074 int direction = player_mapping[p][i].direction;
9076 for (j = 0; j < 8; j++)
9078 int effective_element = element;
9079 int effective_action = action;
9080 int graphic = (direction == MV_NONE ?
9081 el_act2img(effective_element, effective_action) :
9082 el_act_dir2img(effective_element, effective_action,
9084 struct GraphicInfo *g = &graphic_info[graphic];
9085 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9089 int frame = getAnimationFrame(g->anim_frames,
9092 g->anim_start_frame,
9095 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9097 g_em->bitmap = src_bitmap;
9098 g_em->src_x = src_x;
9099 g_em->src_y = src_y;
9100 g_em->src_offset_x = 0;
9101 g_em->src_offset_y = 0;
9102 g_em->dst_offset_x = 0;
9103 g_em->dst_offset_y = 0;
9104 g_em->width = TILEX;
9105 g_em->height = TILEY;
9111 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9112 boolean any_player_moving,
9113 boolean any_player_snapping,
9114 boolean any_player_dropping)
9116 if (frame == 7 && !any_player_dropping)
9118 if (!local_player->was_waiting)
9120 if (!CheckSaveEngineSnapshotToList())
9123 local_player->was_waiting = TRUE;
9126 else if (any_player_moving || any_player_snapping || any_player_dropping)
9128 local_player->was_waiting = FALSE;
9132 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9133 boolean murphy_is_dropping)
9135 if (murphy_is_waiting)
9137 if (!local_player->was_waiting)
9139 if (!CheckSaveEngineSnapshotToList())
9142 local_player->was_waiting = TRUE;
9147 local_player->was_waiting = FALSE;
9151 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9152 boolean button_released)
9154 if (button_released)
9156 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9157 CheckSaveEngineSnapshotToList();
9159 else if (element_clicked)
9161 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9162 CheckSaveEngineSnapshotToList();
9164 game.snapshot.changed_action = TRUE;
9168 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9169 boolean any_player_moving,
9170 boolean any_player_snapping,
9171 boolean any_player_dropping)
9173 if (tape.single_step && tape.recording && !tape.pausing)
9174 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9175 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9177 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9178 any_player_snapping, any_player_dropping);
9180 return tape.pausing;
9183 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9184 boolean murphy_is_dropping)
9186 boolean murphy_starts_dropping = FALSE;
9189 for (i = 0; i < MAX_PLAYERS; i++)
9190 if (stored_player[i].force_dropping)
9191 murphy_starts_dropping = TRUE;
9193 if (tape.single_step && tape.recording && !tape.pausing)
9194 if (murphy_is_waiting && !murphy_starts_dropping)
9195 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9197 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9200 void CheckSingleStepMode_MM(boolean element_clicked,
9201 boolean button_released)
9203 if (tape.single_step && tape.recording && !tape.pausing)
9204 if (button_released)
9205 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9207 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9210 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9211 int graphic, int sync_frame, int x, int y)
9213 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9215 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9218 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9220 return (IS_NEXT_FRAME(sync_frame, graphic));
9223 int getGraphicInfo_Delay(int graphic)
9225 return graphic_info[graphic].anim_delay;
9228 void PlayMenuSoundExt(int sound)
9230 if (sound == SND_UNDEFINED)
9233 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9234 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9237 if (IS_LOOP_SOUND(sound))
9238 PlaySoundLoop(sound);
9243 void PlayMenuSound(void)
9245 PlayMenuSoundExt(menu.sound[game_status]);
9248 void PlayMenuSoundStereo(int sound, int stereo_position)
9250 if (sound == SND_UNDEFINED)
9253 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9254 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9257 if (IS_LOOP_SOUND(sound))
9258 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9260 PlaySoundStereo(sound, stereo_position);
9263 void PlayMenuSoundIfLoopExt(int sound)
9265 if (sound == SND_UNDEFINED)
9268 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9269 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9272 if (IS_LOOP_SOUND(sound))
9273 PlaySoundLoop(sound);
9276 void PlayMenuSoundIfLoop(void)
9278 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9281 void PlayMenuMusicExt(int music)
9283 if (music == MUS_UNDEFINED)
9286 if (!setup.sound_music)
9289 if (IS_LOOP_MUSIC(music))
9290 PlayMusicLoop(music);
9295 void PlayMenuMusic(void)
9297 char *curr_music = getCurrentlyPlayingMusicFilename();
9298 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9300 if (!strEqual(curr_music, next_music))
9301 PlayMenuMusicExt(menu.music[game_status]);
9304 void PlayMenuSoundsAndMusic(void)
9310 static void FadeMenuSounds(void)
9315 static void FadeMenuMusic(void)
9317 char *curr_music = getCurrentlyPlayingMusicFilename();
9318 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9320 if (!strEqual(curr_music, next_music))
9324 void FadeMenuSoundsAndMusic(void)
9330 void PlaySoundActivating(void)
9333 PlaySound(SND_MENU_ITEM_ACTIVATING);
9337 void PlaySoundSelecting(void)
9340 PlaySound(SND_MENU_ITEM_SELECTING);
9344 void ToggleFullscreenIfNeeded(void)
9346 // if setup and video fullscreen state are already matching, nothing do do
9347 if (setup.fullscreen == video.fullscreen_enabled ||
9348 !video.fullscreen_available)
9351 SDLSetWindowFullscreen(setup.fullscreen);
9353 // set setup value according to successfully changed fullscreen mode
9354 setup.fullscreen = video.fullscreen_enabled;
9357 void ChangeWindowScalingIfNeeded(void)
9359 // if setup and video window scaling are already matching, nothing do do
9360 if (setup.window_scaling_percent == video.window_scaling_percent ||
9361 video.fullscreen_enabled)
9364 SDLSetWindowScaling(setup.window_scaling_percent);
9366 // set setup value according to successfully changed window scaling
9367 setup.window_scaling_percent = video.window_scaling_percent;
9370 void ChangeVsyncModeIfNeeded(void)
9372 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9373 int video_vsync_mode = video.vsync_mode;
9375 // if setup and video vsync mode are already matching, nothing do do
9376 if (setup_vsync_mode == video_vsync_mode)
9379 // if renderer is using OpenGL, vsync mode can directly be changed
9380 SDLSetScreenVsyncMode(setup.vsync_mode);
9382 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9383 if (video.vsync_mode == video_vsync_mode)
9385 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9387 // save backbuffer content which gets lost when re-creating screen
9388 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9390 // force re-creating screen and renderer to set new vsync mode
9391 video.fullscreen_enabled = !setup.fullscreen;
9393 // when creating new renderer, destroy textures linked to old renderer
9394 FreeAllImageTextures(); // needs old renderer to free the textures
9396 // re-create screen and renderer (including change of vsync mode)
9397 ChangeVideoModeIfNeeded(setup.fullscreen);
9399 // set setup value according to successfully changed fullscreen mode
9400 setup.fullscreen = video.fullscreen_enabled;
9402 // restore backbuffer content from temporary backbuffer backup bitmap
9403 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9404 FreeBitmap(tmp_backbuffer);
9406 // update visible window/screen
9407 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9409 // when changing vsync mode, re-create textures for new renderer
9410 InitImageTextures();
9413 // set setup value according to successfully changed vsync mode
9414 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9417 static void JoinRectangles(int *x, int *y, int *width, int *height,
9418 int x2, int y2, int width2, int height2)
9420 // do not join with "off-screen" rectangle
9421 if (x2 == -1 || y2 == -1)
9426 *width = MAX(*width, width2);
9427 *height = MAX(*height, height2);
9430 void SetAnimStatus(int anim_status_new)
9432 if (anim_status_new == GAME_MODE_MAIN)
9433 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9434 else if (anim_status_new == GAME_MODE_NAMES)
9435 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9436 else if (anim_status_new == GAME_MODE_SCORES)
9437 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9439 global.anim_status_next = anim_status_new;
9441 // directly set screen modes that are entered without fading
9442 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9443 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9444 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9445 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9446 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9447 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9448 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9449 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9450 global.anim_status = global.anim_status_next;
9453 void SetGameStatus(int game_status_new)
9455 if (game_status_new != game_status)
9456 game_status_last_screen = game_status;
9458 game_status = game_status_new;
9460 SetAnimStatus(game_status_new);
9463 void SetFontStatus(int game_status_new)
9465 static int last_game_status = -1;
9467 if (game_status_new != -1)
9469 // set game status for font use after storing last game status
9470 last_game_status = game_status;
9471 game_status = game_status_new;
9475 // reset game status after font use from last stored game status
9476 game_status = last_game_status;
9480 void ResetFontStatus(void)
9485 void SetLevelSetInfo(char *identifier, int level_nr)
9487 setString(&levelset.identifier, identifier);
9489 levelset.level_nr = level_nr;
9492 boolean CheckIfAllViewportsHaveChanged(void)
9494 // if game status has not changed, viewports have not changed either
9495 if (game_status == game_status_last)
9498 // check if all viewports have changed with current game status
9500 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9501 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9502 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9503 int new_real_sx = vp_playfield->x;
9504 int new_real_sy = vp_playfield->y;
9505 int new_full_sxsize = vp_playfield->width;
9506 int new_full_sysize = vp_playfield->height;
9507 int new_dx = vp_door_1->x;
9508 int new_dy = vp_door_1->y;
9509 int new_dxsize = vp_door_1->width;
9510 int new_dysize = vp_door_1->height;
9511 int new_vx = vp_door_2->x;
9512 int new_vy = vp_door_2->y;
9513 int new_vxsize = vp_door_2->width;
9514 int new_vysize = vp_door_2->height;
9516 boolean playfield_viewport_has_changed =
9517 (new_real_sx != REAL_SX ||
9518 new_real_sy != REAL_SY ||
9519 new_full_sxsize != FULL_SXSIZE ||
9520 new_full_sysize != FULL_SYSIZE);
9522 boolean door_1_viewport_has_changed =
9525 new_dxsize != DXSIZE ||
9526 new_dysize != DYSIZE);
9528 boolean door_2_viewport_has_changed =
9531 new_vxsize != VXSIZE ||
9532 new_vysize != VYSIZE ||
9533 game_status_last == GAME_MODE_EDITOR);
9535 return (playfield_viewport_has_changed &&
9536 door_1_viewport_has_changed &&
9537 door_2_viewport_has_changed);
9540 boolean CheckFadeAll(void)
9542 return (CheckIfGlobalBorderHasChanged() ||
9543 CheckIfAllViewportsHaveChanged());
9546 void ChangeViewportPropertiesIfNeeded(void)
9548 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9549 FALSE : setup.small_game_graphics);
9550 int gfx_game_mode = game_status;
9551 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9553 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9554 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9555 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9556 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9557 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9558 int new_win_xsize = vp_window->width;
9559 int new_win_ysize = vp_window->height;
9560 int border_left = vp_playfield->border_left;
9561 int border_right = vp_playfield->border_right;
9562 int border_top = vp_playfield->border_top;
9563 int border_bottom = vp_playfield->border_bottom;
9564 int new_sx = vp_playfield->x + border_left;
9565 int new_sy = vp_playfield->y + border_top;
9566 int new_sxsize = vp_playfield->width - border_left - border_right;
9567 int new_sysize = vp_playfield->height - border_top - border_bottom;
9568 int new_real_sx = vp_playfield->x;
9569 int new_real_sy = vp_playfield->y;
9570 int new_full_sxsize = vp_playfield->width;
9571 int new_full_sysize = vp_playfield->height;
9572 int new_dx = vp_door_1->x;
9573 int new_dy = vp_door_1->y;
9574 int new_dxsize = vp_door_1->width;
9575 int new_dysize = vp_door_1->height;
9576 int new_vx = vp_door_2->x;
9577 int new_vy = vp_door_2->y;
9578 int new_vxsize = vp_door_2->width;
9579 int new_vysize = vp_door_2->height;
9580 int new_ex = vp_door_3->x;
9581 int new_ey = vp_door_3->y;
9582 int new_exsize = vp_door_3->width;
9583 int new_eysize = vp_door_3->height;
9584 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9585 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9586 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9587 int new_scr_fieldx = new_sxsize / tilesize;
9588 int new_scr_fieldy = new_sysize / tilesize;
9589 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9590 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9591 boolean init_gfx_buffers = FALSE;
9592 boolean init_video_buffer = FALSE;
9593 boolean init_gadgets_and_anims = FALSE;
9594 boolean init_em_graphics = FALSE;
9596 if (new_win_xsize != WIN_XSIZE ||
9597 new_win_ysize != WIN_YSIZE)
9599 WIN_XSIZE = new_win_xsize;
9600 WIN_YSIZE = new_win_ysize;
9602 init_video_buffer = TRUE;
9603 init_gfx_buffers = TRUE;
9604 init_gadgets_and_anims = TRUE;
9606 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9609 if (new_scr_fieldx != SCR_FIELDX ||
9610 new_scr_fieldy != SCR_FIELDY)
9612 // this always toggles between MAIN and GAME when using small tile size
9614 SCR_FIELDX = new_scr_fieldx;
9615 SCR_FIELDY = new_scr_fieldy;
9617 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9628 new_sxsize != SXSIZE ||
9629 new_sysize != SYSIZE ||
9630 new_dxsize != DXSIZE ||
9631 new_dysize != DYSIZE ||
9632 new_vxsize != VXSIZE ||
9633 new_vysize != VYSIZE ||
9634 new_exsize != EXSIZE ||
9635 new_eysize != EYSIZE ||
9636 new_real_sx != REAL_SX ||
9637 new_real_sy != REAL_SY ||
9638 new_full_sxsize != FULL_SXSIZE ||
9639 new_full_sysize != FULL_SYSIZE ||
9640 new_tilesize_var != TILESIZE_VAR
9643 // ------------------------------------------------------------------------
9644 // determine next fading area for changed viewport definitions
9645 // ------------------------------------------------------------------------
9647 // start with current playfield area (default fading area)
9650 FADE_SXSIZE = FULL_SXSIZE;
9651 FADE_SYSIZE = FULL_SYSIZE;
9653 // add new playfield area if position or size has changed
9654 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9655 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9657 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9658 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9661 // add current and new door 1 area if position or size has changed
9662 if (new_dx != DX || new_dy != DY ||
9663 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9665 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9666 DX, DY, DXSIZE, DYSIZE);
9667 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9668 new_dx, new_dy, new_dxsize, new_dysize);
9671 // add current and new door 2 area if position or size has changed
9672 if (new_vx != VX || new_vy != VY ||
9673 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9675 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9676 VX, VY, VXSIZE, VYSIZE);
9677 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9678 new_vx, new_vy, new_vxsize, new_vysize);
9681 // ------------------------------------------------------------------------
9682 // handle changed tile size
9683 // ------------------------------------------------------------------------
9685 if (new_tilesize_var != TILESIZE_VAR)
9687 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9689 // changing tile size invalidates scroll values of engine snapshots
9690 FreeEngineSnapshotSingle();
9692 // changing tile size requires update of graphic mapping for EM engine
9693 init_em_graphics = TRUE;
9704 SXSIZE = new_sxsize;
9705 SYSIZE = new_sysize;
9706 DXSIZE = new_dxsize;
9707 DYSIZE = new_dysize;
9708 VXSIZE = new_vxsize;
9709 VYSIZE = new_vysize;
9710 EXSIZE = new_exsize;
9711 EYSIZE = new_eysize;
9712 REAL_SX = new_real_sx;
9713 REAL_SY = new_real_sy;
9714 FULL_SXSIZE = new_full_sxsize;
9715 FULL_SYSIZE = new_full_sysize;
9716 TILESIZE_VAR = new_tilesize_var;
9718 init_gfx_buffers = TRUE;
9719 init_gadgets_and_anims = TRUE;
9721 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9722 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9725 if (init_gfx_buffers)
9727 // Debug("tools:viewport", "init_gfx_buffers");
9729 SCR_FIELDX = new_scr_fieldx_buffers;
9730 SCR_FIELDY = new_scr_fieldy_buffers;
9734 SCR_FIELDX = new_scr_fieldx;
9735 SCR_FIELDY = new_scr_fieldy;
9737 SetDrawDeactivationMask(REDRAW_NONE);
9738 SetDrawBackgroundMask(REDRAW_FIELD);
9741 if (init_video_buffer)
9743 // Debug("tools:viewport", "init_video_buffer");
9745 FreeAllImageTextures(); // needs old renderer to free the textures
9747 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9748 InitImageTextures();
9751 if (init_gadgets_and_anims)
9753 // Debug("tools:viewport", "init_gadgets_and_anims");
9756 InitGlobalAnimations();
9759 if (init_em_graphics)
9761 InitGraphicInfo_EM();