1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 /* graphic position values for game controls */
19 #define ENERGY_XSIZE 32
20 #define ENERGY_YSIZE MAX_LASER_ENERGY
21 #define OVERLOAD_XSIZE ENERGY_XSIZE
22 #define OVERLOAD_YSIZE MAX_LASER_OVERLOAD
24 /* values for Explode_MM() */
25 #define EX_PHASE_START 0
30 /* special positions in the game control window (relative to control window) */
39 #define XX_OVERLOAD 60
40 #define YY_OVERLOAD YY_ENERGY
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL (DX + XX_LEVEL)
44 #define DY_LEVEL (DY + YY_LEVEL)
45 #define DX_KETTLES (DX + XX_KETTLES)
46 #define DY_KETTLES (DY + YY_KETTLES)
47 #define DX_SCORE (DX + XX_SCORE)
48 #define DY_SCORE (DY + YY_SCORE)
49 #define DX_ENERGY (DX + XX_ENERGY)
50 #define DY_ENERGY (DY + YY_ENERGY)
51 #define DX_OVERLOAD (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD (DY + YY_OVERLOAD)
54 #define IS_LOOP_SOUND(s) ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT 0
59 #define GAME_CTRL_ID_MIDDLE 1
60 #define GAME_CTRL_ID_RIGHT 2
62 #define NUM_GAME_BUTTONS 3
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED 0
66 #define DL_LASER_ENABLED 1
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_FIRST 12 /* delay (frames) after first click */
70 #define CLICK_DELAY 6 /* delay (frames) for pressed butten */
72 #define AUTO_ROTATE_DELAY CLICK_DELAY
73 #define INIT_GAME_ACTIONS_DELAY (ONE_SECOND_DELAY / GAME_FRAME_DELAY)
74 #define NUM_INIT_CYCLE_STEPS 16
75 #define PACMAN_MOVE_DELAY 12
76 #define ENERGY_DELAY (ONE_SECOND_DELAY / GAME_FRAME_DELAY)
77 #define HEALTH_DEC_DELAY 3
78 #define HEALTH_INC_DELAY 9
79 #define HEALTH_DELAY(x) ((x) ? HEALTH_DEC_DELAY : HEALTH_INC_DELAY)
81 #define BEGIN_NO_HEADLESS \
83 boolean last_headless = program.headless; \
85 program.headless = FALSE; \
87 #define END_NO_HEADLESS \
88 program.headless = last_headless; \
91 /* forward declaration for internal use */
92 static int MovingOrBlocked2Element_MM(int, int);
93 static void Bang_MM(int, int);
94 static void RaiseScore_MM(int);
95 static void RemoveMovingField_MM(int, int);
96 static void InitMovingField_MM(int, int, int);
97 static void ContinueMoving_MM(int, int);
98 static void Moving2Blocked_MM(int, int, int *, int *);
100 /* bitmap for laser beam detection */
101 static Bitmap *laser_bitmap = NULL;
103 /* variables for laser control */
104 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
105 static int hold_x = -1, hold_y = -1;
107 /* variables for pacman control */
108 static int pacman_nr = -1;
110 /* various game engine delay counters */
111 static unsigned int rotate_delay = 0;
112 static unsigned int pacman_delay = 0;
113 static unsigned int energy_delay = 0;
114 static unsigned int overload_delay = 0;
116 /* element masks for scanning pixels of MM elements */
117 static const char mm_masks[10][16][16 + 1] =
301 static int get_element_angle(int element)
303 int element_phase = get_element_phase(element);
305 if (IS_MIRROR_FIXED(element) ||
306 IS_MCDUFFIN(element) ||
308 IS_RECEIVER(element))
309 return 4 * element_phase;
311 return element_phase;
314 static int get_opposite_angle(int angle)
316 int opposite_angle = angle + ANG_RAY_180;
318 /* make sure "opposite_angle" is in valid interval [0, 15] */
319 return (opposite_angle + 16) % 16;
322 static int get_mirrored_angle(int laser_angle, int mirror_angle)
324 int reflected_angle = 16 - laser_angle + mirror_angle;
326 /* make sure "reflected_angle" is in valid interval [0, 15] */
327 return (reflected_angle + 16) % 16;
330 static void DrawLaserLines(struct XY *points, int num_points, int mode)
332 Pixel pixel_drawto = (mode == DL_LASER_ENABLED ? pen_ray : pen_bg);
333 Pixel pixel_buffer = (mode == DL_LASER_ENABLED ? WHITE_PIXEL : BLACK_PIXEL);
335 DrawLines(drawto, points, num_points, pixel_drawto);
339 DrawLines(laser_bitmap, points, num_points, pixel_buffer);
344 static boolean CheckLaserPixel(int x, int y)
350 pixel = ReadPixel(laser_bitmap, x, y);
354 return (pixel == WHITE_PIXEL);
357 static void CheckExitMM()
359 int exit_element = EL_EMPTY;
363 static int xy[4][2] =
371 for (y = 0; y < lev_fieldy; y++)
373 for (x = 0; x < lev_fieldx; x++)
375 if (Feld[x][y] == EL_EXIT_CLOSED)
377 /* initiate opening animation of exit door */
378 Feld[x][y] = EL_EXIT_OPENING;
380 exit_element = EL_EXIT_OPEN;
384 else if (IS_RECEIVER(Feld[x][y]))
386 /* remove field that blocks receiver */
387 int phase = Feld[x][y] - EL_RECEIVER_START;
388 int blocking_x, blocking_y;
390 blocking_x = x + xy[phase][0];
391 blocking_y = y + xy[phase][1];
393 if (IN_LEV_FIELD(blocking_x, blocking_y))
395 Feld[blocking_x][blocking_y] = EL_EMPTY;
397 DrawField_MM(blocking_x, blocking_y);
400 exit_element = EL_RECEIVER;
407 if (exit_element != EL_EMPTY)
408 PlayLevelSound_MM(exit_x, exit_y, exit_element, MM_ACTION_OPENING);
411 static void InitMovDir_MM(int x, int y)
413 int element = Feld[x][y];
414 static int direction[3][4] =
416 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
417 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
418 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
423 case EL_PACMAN_RIGHT:
427 Feld[x][y] = EL_PACMAN;
428 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
436 static void InitField(int x, int y, boolean init_game)
438 int element = Feld[x][y];
443 Feld[x][y] = EL_EMPTY;
448 if (native_mm_level.auto_count_kettles)
449 game_mm.kettles_still_needed++;
452 case EL_LIGHTBULB_OFF:
453 game_mm.lights_still_needed++;
457 if (IS_MIRROR(element) ||
458 IS_BEAMER_OLD(element) ||
459 IS_BEAMER(element) ||
461 IS_POLAR_CROSS(element) ||
462 IS_DF_MIRROR(element) ||
463 IS_DF_MIRROR_AUTO(element) ||
464 IS_GRID_STEEL_AUTO(element) ||
465 IS_GRID_WOOD_AUTO(element) ||
466 IS_FIBRE_OPTIC(element))
468 if (IS_BEAMER_OLD(element))
470 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
471 element = Feld[x][y];
474 if (!IS_FIBRE_OPTIC(element))
476 static int steps_grid_auto = 0;
478 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
479 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
481 if (IS_GRID_STEEL_AUTO(element) ||
482 IS_GRID_WOOD_AUTO(element))
483 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
485 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
487 game_mm.cycle[game_mm.num_cycle].x = x;
488 game_mm.cycle[game_mm.num_cycle].y = y;
492 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
494 int beamer_nr = BEAMER_NR(element);
495 int nr = laser.beamer[beamer_nr][0].num;
497 laser.beamer[beamer_nr][nr].x = x;
498 laser.beamer[beamer_nr][nr].y = y;
499 laser.beamer[beamer_nr][nr].num = 1;
502 else if (IS_PACMAN(element))
506 else if (IS_MCDUFFIN(element) || IS_LASER(element))
508 laser.start_edge.x = x;
509 laser.start_edge.y = y;
510 laser.start_angle = get_element_angle(element);
517 static void InitCycleElements_RotateSingleStep()
521 if (game_mm.num_cycle == 0) /* no elements to cycle */
524 for (i = 0; i < game_mm.num_cycle; i++)
526 int x = game_mm.cycle[i].x;
527 int y = game_mm.cycle[i].y;
528 int step = SIGN(game_mm.cycle[i].steps);
529 int last_element = Feld[x][y];
530 int next_element = get_rotated_element(last_element, step);
532 if (!game_mm.cycle[i].steps)
535 Feld[x][y] = next_element;
538 game_mm.cycle[i].steps -= step;
542 static void InitLaser()
544 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
545 int step = (IS_LASER(start_element) ? 4 : 0);
547 LX = laser.start_edge.x * TILEX;
548 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
551 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
553 LY = laser.start_edge.y * TILEY;
554 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
555 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
559 XS = 2 * Step[laser.start_angle].x;
560 YS = 2 * Step[laser.start_angle].y;
562 laser.current_angle = laser.start_angle;
564 laser.num_damages = 0;
566 laser.num_beamers = 0;
567 laser.beamer_edge[0] = 0;
569 laser.dest_element = EL_EMPTY;
572 AddLaserEdge(LX, LY); /* set laser starting edge */
574 pen_ray = GetPixelFromRGB(window,
575 native_mm_level.laser_red * 0xFF,
576 native_mm_level.laser_green * 0xFF,
577 native_mm_level.laser_blue * 0xFF);
580 void InitGameEngine_MM()
586 /* initialize laser bitmap to current playfield (screen) size */
587 ReCreateBitmap(&laser_bitmap, drawto->width, drawto->height);
588 ClearRectangle(laser_bitmap, 0, 0, drawto->width, drawto->height);
592 /* set global game control values */
593 game_mm.num_cycle = 0;
594 game_mm.num_pacman = 0;
597 game_mm.energy_left = 0; // later set to "native_mm_level.time"
598 game_mm.kettles_still_needed =
599 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
600 game_mm.lights_still_needed = 0;
601 game_mm.num_keys = 0;
603 game_mm.level_solved = FALSE;
604 game_mm.game_over = FALSE;
605 game_mm.game_over_cause = 0;
607 game_mm.laser_overload_value = 0;
609 /* set global laser control values (must be set before "InitLaser()") */
610 laser.start_edge.x = 0;
611 laser.start_edge.y = 0;
612 laser.start_angle = 0;
614 for (i = 0; i < MAX_NUM_BEAMERS; i++)
615 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
617 laser.overloaded = FALSE;
618 laser.overload_value = 0;
619 laser.fuse_off = FALSE;
620 laser.fuse_x = laser.fuse_y = -1;
622 laser.dest_element = EL_EMPTY;
641 ClickElement(-1, -1, -1);
643 for (x = 0; x < lev_fieldx; x++)
645 for (y = 0; y < lev_fieldy; y++)
647 Feld[x][y] = Ur[x][y];
648 Hit[x][y] = Box[x][y] = 0;
650 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
651 Store[x][y] = Store2[x][y] = 0;
655 InitField(x, y, TRUE);
660 CloseDoor(DOOR_CLOSE_1);
666 void InitGameActions_MM()
668 int num_init_game_frames = INIT_GAME_ACTIONS_DELAY;
669 int cycle_steps_done = 0;
675 /* copy default game door content to main double buffer */
676 BlitBitmap(pix[PIX_DOOR], drawto,
677 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
681 DrawText(DX_LEVEL, DY_LEVEL,
682 int2str(level_nr, 2), FONT_TEXT_2);
683 DrawText(DX_KETTLES, DY_KETTLES,
684 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
685 DrawText(DX_SCORE, DY_SCORE,
686 int2str(game_mm.score, 4), FONT_TEXT_2);
695 /* copy actual game door content to door double buffer for OpenDoor() */
696 BlitBitmap(drawto, pix[PIX_DB_DOOR],
697 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
701 OpenDoor(DOOR_OPEN_ALL);
704 for (i = 0; i <= num_init_game_frames; i++)
706 if (i == num_init_game_frames)
707 StopSound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
708 else if (setup.sound_loops)
709 PlaySoundLoop_MM(SND_MM_GAME_LEVELTIME_CHARGING);
711 PlaySound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
713 game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
715 UpdateAndDisplayGameControlValues();
717 while (cycle_steps_done < NUM_INIT_CYCLE_STEPS * i / num_init_game_frames)
719 InitCycleElements_RotateSingleStep();
729 if (setup.quick_doors)
735 if (setup.sound_music && num_bg_loops)
736 PlayMusic(level_nr % num_bg_loops);
741 if (game_mm.kettles_still_needed == 0)
745 void AddLaserEdge(int lx, int ly)
747 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
749 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
754 laser.edge[laser.num_edges].x = SX + 2 + lx;
755 laser.edge[laser.num_edges].y = SY + 2 + ly;
761 void AddDamagedField(int ex, int ey)
763 laser.damage[laser.num_damages].is_mirror = FALSE;
764 laser.damage[laser.num_damages].angle = laser.current_angle;
765 laser.damage[laser.num_damages].edge = laser.num_edges;
766 laser.damage[laser.num_damages].x = ex;
767 laser.damage[laser.num_damages].y = ey;
777 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
778 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
780 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
786 static int getMaskFromElement(int element)
788 if (IS_GRID(element))
789 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
790 else if (IS_MCDUFFIN(element))
791 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
792 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
793 return IMG_MM_MASK_RECTANGLE;
795 return IMG_MM_MASK_CIRCLE;
803 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
804 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
807 /* follow laser beam until it hits something (at least the screen border) */
808 while (hit_mask == HIT_MASK_NO_HIT)
814 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
815 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
817 printf("ScanPixel: touched screen border!\n");
823 for (i = 0; i < 4; i++)
825 int px = LX + (i % 2) * 2;
826 int py = LY + (i / 2) * 2;
829 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
830 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
833 if (IN_LEV_FIELD(lx, ly))
835 int element = Feld[lx][ly];
837 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
841 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
843 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
845 pixel = ((element & (1 << pos)) ? 1 : 0);
849 int pos = getMaskFromElement(element) - IMG_MM_MASK_MCDUFFIN_RIGHT;
851 pixel = (mm_masks[pos][dy / 2][dx / 2] == 'X' ? 1 : 0);
856 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
857 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
860 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
861 hit_mask |= (1 << i);
864 if (hit_mask == HIT_MASK_NO_HIT)
866 /* hit nothing -- go on with another step */
878 int end = 0, rf = laser.num_edges;
880 /* do not scan laser again after the game was lost for whatever reason */
881 if (game_mm.game_over)
884 laser.overloaded = FALSE;
885 laser.stops_inside_element = FALSE;
887 DrawLaser(0, DL_LASER_ENABLED);
890 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
898 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
901 laser.overloaded = TRUE;
906 hit_mask = ScanPixel();
909 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
913 /* hit something -- check out what it was */
914 ELX = (LX + XS) / TILEX;
915 ELY = (LY + YS) / TILEY;
918 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
919 hit_mask, LX, LY, ELX, ELY);
922 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
925 laser.dest_element = element;
930 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
932 /* we have hit the top-right and bottom-left element --
933 choose the bottom-left one */
934 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
935 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
936 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
937 ELX = (LX - 2) / TILEX;
938 ELY = (LY + 2) / TILEY;
941 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
943 /* we have hit the top-left and bottom-right element --
944 choose the top-left one */
945 /* !!! SEE ABOVE !!! */
946 ELX = (LX - 2) / TILEX;
947 ELY = (LY - 2) / TILEY;
951 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
952 hit_mask, LX, LY, ELX, ELY);
955 element = Feld[ELX][ELY];
956 laser.dest_element = element;
959 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
962 LX % TILEX, LY % TILEY,
967 if (!IN_LEV_FIELD(ELX, ELY))
968 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
971 if (element == EL_EMPTY)
973 if (!HitOnlyAnEdge(element, hit_mask))
976 else if (element == EL_FUSE_ON)
978 if (HitPolarizer(element, hit_mask))
981 else if (IS_GRID(element) || IS_DF_GRID(element))
983 if (HitPolarizer(element, hit_mask))
986 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
987 element == EL_GATE_STONE || element == EL_GATE_WOOD)
989 if (HitBlock(element, hit_mask))
996 else if (IS_MCDUFFIN(element))
998 if (HitLaserSource(element, hit_mask))
1001 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
1002 IS_RECEIVER(element))
1004 if (HitLaserDestination(element, hit_mask))
1007 else if (IS_WALL(element))
1009 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
1011 if (HitReflectingWalls(element, hit_mask))
1016 if (HitAbsorbingWalls(element, hit_mask))
1022 if (HitElement(element, hit_mask))
1027 DrawLaser(rf - 1, DL_LASER_ENABLED);
1028 rf = laser.num_edges;
1032 if (laser.dest_element != Feld[ELX][ELY])
1034 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
1035 laser.dest_element, Feld[ELX][ELY]);
1039 if (!end && !laser.stops_inside_element && !StepBehind())
1042 printf("ScanLaser: Go one step back\n");
1048 AddLaserEdge(LX, LY);
1052 DrawLaser(rf - 1, DL_LASER_ENABLED);
1054 Ct = CT = FrameCounter;
1057 if (!IN_LEV_FIELD(ELX, ELY))
1058 printf("WARNING! (2) %d, %d\n", ELX, ELY);
1062 void DrawLaserExt(int start_edge, int num_edges, int mode)
1068 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
1069 start_edge, num_edges, mode);
1074 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
1081 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
1087 if (mode == DL_LASER_DISABLED)
1089 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
1093 /* now draw the laser to the backbuffer and (if enabled) to the screen */
1094 DrawLaserLines(&laser.edge[start_edge], num_edges, mode);
1096 redraw_mask |= REDRAW_FIELD;
1098 if (mode == DL_LASER_ENABLED)
1101 /* after the laser was deleted, the "damaged" graphics must be restored */
1102 if (laser.num_damages)
1104 int damage_start = 0;
1107 /* determine the starting edge, from which graphics need to be restored */
1110 for (i = 0; i < laser.num_damages; i++)
1112 if (laser.damage[i].edge == start_edge + 1)
1121 /* restore graphics from this starting edge to the end of damage list */
1122 for (i = damage_start; i < laser.num_damages; i++)
1124 int lx = laser.damage[i].x;
1125 int ly = laser.damage[i].y;
1126 int element = Feld[lx][ly];
1128 if (Hit[lx][ly] == laser.damage[i].edge)
1129 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1132 if (Box[lx][ly] == laser.damage[i].edge)
1135 if (IS_DRAWABLE(element))
1136 DrawField_MM(lx, ly);
1139 elx = laser.damage[damage_start].x;
1140 ely = laser.damage[damage_start].y;
1141 element = Feld[elx][ely];
1144 if (IS_BEAMER(element))
1148 for (i = 0; i < laser.num_beamers; i++)
1149 printf("-> %d\n", laser.beamer_edge[i]);
1150 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
1151 mode, elx, ely, Hit[elx][ely], start_edge);
1152 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
1153 get_element_angle(element), laser.damage[damage_start].angle);
1157 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1158 laser.num_beamers > 0 &&
1159 start_edge == laser.beamer_edge[laser.num_beamers - 1])
1161 /* element is outgoing beamer */
1162 laser.num_damages = damage_start + 1;
1164 if (IS_BEAMER(element))
1165 laser.current_angle = get_element_angle(element);
1169 /* element is incoming beamer or other element */
1170 laser.num_damages = damage_start;
1171 laser.current_angle = laser.damage[laser.num_damages].angle;
1176 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
1178 elx = laser.start_edge.x;
1179 ely = laser.start_edge.y;
1180 element = Feld[elx][ely];
1183 laser.num_edges = start_edge + 1;
1184 if (start_edge == 0)
1185 laser.current_angle = laser.start_angle;
1187 LX = laser.edge[start_edge].x - (SX + 2);
1188 LY = laser.edge[start_edge].y - (SY + 2);
1189 XS = 2 * Step[laser.current_angle].x;
1190 YS = 2 * Step[laser.current_angle].y;
1193 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
1199 if (IS_BEAMER(element) ||
1200 IS_FIBRE_OPTIC(element) ||
1201 IS_PACMAN(element) ||
1202 IS_POLAR(element) ||
1203 IS_POLAR_CROSS(element) ||
1204 element == EL_FUSE_ON)
1209 printf("element == %d\n", element);
1212 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
1213 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
1217 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
1218 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1219 (laser.num_beamers == 0 ||
1220 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
1222 /* element is incoming beamer or other element */
1223 step_size = -step_size;
1228 if (IS_BEAMER(element))
1230 printf("start_edge == %d, laser.beamer_edge == %d\n",
1231 start_edge, laser.beamer_edge);
1235 LX += step_size * XS;
1236 LY += step_size * YS;
1238 else if (element != EL_EMPTY)
1247 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
1252 void DrawLaser(int start_edge, int mode)
1254 if (laser.num_edges - start_edge < 0)
1256 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
1261 /* check if laser is interrupted by beamer element */
1262 if (laser.num_beamers > 0 &&
1263 start_edge < laser.beamer_edge[laser.num_beamers - 1])
1265 if (mode == DL_LASER_ENABLED)
1268 int tmp_start_edge = start_edge;
1270 /* draw laser segments forward from the start to the last beamer */
1271 for (i = 0; i < laser.num_beamers; i++)
1273 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
1275 if (tmp_num_edges <= 0)
1279 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
1280 i, laser.beamer_edge[i], tmp_start_edge);
1283 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
1285 tmp_start_edge = laser.beamer_edge[i];
1288 /* draw last segment from last beamer to the end */
1289 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
1295 int last_num_edges = laser.num_edges;
1296 int num_beamers = laser.num_beamers;
1298 /* delete laser segments backward from the end to the first beamer */
1299 for (i = num_beamers - 1; i >= 0; i--)
1301 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
1303 if (laser.beamer_edge[i] - start_edge <= 0)
1306 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1308 last_num_edges = laser.beamer_edge[i];
1309 laser.num_beamers--;
1313 if (last_num_edges - start_edge <= 0)
1314 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1315 last_num_edges, start_edge);
1318 // special case when rotating first beamer: delete laser edge on beamer
1319 // (but do not start scanning on previous edge to prevent mirror sound)
1320 if (last_num_edges - start_edge == 1 && start_edge > 0)
1321 DrawLaserLines(&laser.edge[start_edge - 1], 2, DL_LASER_DISABLED);
1323 /* delete first segment from start to the first beamer */
1324 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1329 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1333 boolean HitElement(int element, int hit_mask)
1335 if (HitOnlyAnEdge(element, hit_mask))
1338 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1339 element = MovingOrBlocked2Element_MM(ELX, ELY);
1342 printf("HitElement (1): element == %d\n", element);
1346 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1347 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1349 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1352 AddDamagedField(ELX, ELY);
1354 /* this is more precise: check if laser would go through the center */
1355 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1357 /* skip the whole element before continuing the scan */
1363 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1365 if (LX/TILEX > ELX || LY/TILEY > ELY)
1367 /* skipping scan positions to the right and down skips one scan
1368 position too much, because this is only the top left scan position
1369 of totally four scan positions (plus one to the right, one to the
1370 bottom and one to the bottom right) */
1380 printf("HitElement (2): element == %d\n", element);
1383 if (LX + 5 * XS < 0 ||
1393 printf("HitElement (3): element == %d\n", element);
1396 if (IS_POLAR(element) &&
1397 ((element - EL_POLAR_START) % 2 ||
1398 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1400 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1402 laser.num_damages--;
1407 if (IS_POLAR_CROSS(element) &&
1408 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1410 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1412 laser.num_damages--;
1417 if (!IS_BEAMER(element) &&
1418 !IS_FIBRE_OPTIC(element) &&
1419 !IS_GRID_WOOD(element) &&
1420 element != EL_FUEL_EMPTY)
1423 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1424 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1426 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1429 LX = ELX * TILEX + 14;
1430 LY = ELY * TILEY + 14;
1432 AddLaserEdge(LX, LY);
1435 if (IS_MIRROR(element) ||
1436 IS_MIRROR_FIXED(element) ||
1437 IS_POLAR(element) ||
1438 IS_POLAR_CROSS(element) ||
1439 IS_DF_MIRROR(element) ||
1440 IS_DF_MIRROR_AUTO(element) ||
1441 element == EL_PRISM ||
1442 element == EL_REFRACTOR)
1444 int current_angle = laser.current_angle;
1447 laser.num_damages--;
1449 AddDamagedField(ELX, ELY);
1451 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1454 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1456 if (IS_MIRROR(element) ||
1457 IS_MIRROR_FIXED(element) ||
1458 IS_DF_MIRROR(element) ||
1459 IS_DF_MIRROR_AUTO(element))
1460 laser.current_angle = get_mirrored_angle(laser.current_angle,
1461 get_element_angle(element));
1463 if (element == EL_PRISM || element == EL_REFRACTOR)
1464 laser.current_angle = RND(16);
1466 XS = 2 * Step[laser.current_angle].x;
1467 YS = 2 * Step[laser.current_angle].y;
1469 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1474 LX += step_size * XS;
1475 LY += step_size * YS;
1478 /* draw sparkles on mirror */
1479 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1480 current_angle != laser.current_angle)
1482 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1486 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1487 current_angle != laser.current_angle)
1488 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1491 (get_opposite_angle(laser.current_angle) ==
1492 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1494 return (laser.overloaded ? TRUE : FALSE);
1497 if (element == EL_FUEL_FULL)
1499 laser.stops_inside_element = TRUE;
1504 if (element == EL_BOMB || element == EL_MINE)
1506 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1508 if (element == EL_MINE)
1509 laser.overloaded = TRUE;
1512 if (element == EL_KETTLE ||
1513 element == EL_CELL ||
1514 element == EL_KEY ||
1515 element == EL_LIGHTBALL ||
1516 element == EL_PACMAN ||
1519 if (!IS_PACMAN(element))
1522 if (element == EL_PACMAN)
1525 if (element == EL_KETTLE || element == EL_CELL)
1527 if (game_mm.kettles_still_needed > 0)
1528 game_mm.kettles_still_needed--;
1532 if (game_mm.kettles_still_needed == 0)
1536 DrawLaser(0, DL_LASER_ENABLED);
1539 else if (element == EL_KEY)
1543 else if (element == EL_LIGHTBALL)
1547 else if (IS_PACMAN(element))
1549 DeletePacMan(ELX, ELY);
1556 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1558 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1560 DrawLaser(0, DL_LASER_ENABLED);
1562 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1564 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1565 game_mm.lights_still_needed--;
1569 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1570 game_mm.lights_still_needed++;
1573 DrawField_MM(ELX, ELY);
1574 DrawLaser(0, DL_LASER_ENABLED);
1579 laser.stops_inside_element = TRUE;
1585 printf("HitElement (4): element == %d\n", element);
1588 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1589 laser.num_beamers < MAX_NUM_BEAMERS &&
1590 laser.beamer[BEAMER_NR(element)][1].num)
1592 int beamer_angle = get_element_angle(element);
1593 int beamer_nr = BEAMER_NR(element);
1597 printf("HitElement (BEAMER): element == %d\n", element);
1600 laser.num_damages--;
1602 if (IS_FIBRE_OPTIC(element) ||
1603 laser.current_angle == get_opposite_angle(beamer_angle))
1607 LX = ELX * TILEX + 14;
1608 LY = ELY * TILEY + 14;
1610 AddLaserEdge(LX, LY);
1611 AddDamagedField(ELX, ELY);
1613 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1616 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1618 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1619 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1620 ELX = laser.beamer[beamer_nr][pos].x;
1621 ELY = laser.beamer[beamer_nr][pos].y;
1622 LX = ELX * TILEX + 14;
1623 LY = ELY * TILEY + 14;
1625 if (IS_BEAMER(element))
1627 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1628 XS = 2 * Step[laser.current_angle].x;
1629 YS = 2 * Step[laser.current_angle].y;
1632 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1634 AddLaserEdge(LX, LY);
1635 AddDamagedField(ELX, ELY);
1637 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1640 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1642 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1647 LX += step_size * XS;
1648 LY += step_size * YS;
1650 laser.num_beamers++;
1659 boolean HitOnlyAnEdge(int element, int hit_mask)
1661 /* check if the laser hit only the edge of an element and, if so, go on */
1664 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1667 if ((hit_mask == HIT_MASK_TOPLEFT ||
1668 hit_mask == HIT_MASK_TOPRIGHT ||
1669 hit_mask == HIT_MASK_BOTTOMLEFT ||
1670 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1671 laser.current_angle % 4) /* angle is not 90° */
1675 if (hit_mask == HIT_MASK_TOPLEFT)
1680 else if (hit_mask == HIT_MASK_TOPRIGHT)
1685 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1690 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1696 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1702 printf("[HitOnlyAnEdge() == TRUE]\n");
1709 printf("[HitOnlyAnEdge() == FALSE]\n");
1715 boolean HitPolarizer(int element, int hit_mask)
1717 if (HitOnlyAnEdge(element, hit_mask))
1720 if (IS_DF_GRID(element))
1722 int grid_angle = get_element_angle(element);
1725 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1726 grid_angle, laser.current_angle);
1729 AddLaserEdge(LX, LY);
1730 AddDamagedField(ELX, ELY);
1733 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1735 if (laser.current_angle == grid_angle ||
1736 laser.current_angle == get_opposite_angle(grid_angle))
1738 /* skip the whole element before continuing the scan */
1744 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1746 if (LX/TILEX > ELX || LY/TILEY > ELY)
1748 /* skipping scan positions to the right and down skips one scan
1749 position too much, because this is only the top left scan position
1750 of totally four scan positions (plus one to the right, one to the
1751 bottom and one to the bottom right) */
1757 AddLaserEdge(LX, LY);
1763 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1765 LX / TILEX, LY / TILEY,
1766 LX % TILEX, LY % TILEY);
1771 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1773 return HitReflectingWalls(element, hit_mask);
1777 return HitAbsorbingWalls(element, hit_mask);
1780 else if (IS_GRID_STEEL(element))
1782 return HitReflectingWalls(element, hit_mask);
1784 else /* IS_GRID_WOOD */
1786 return HitAbsorbingWalls(element, hit_mask);
1792 boolean HitBlock(int element, int hit_mask)
1794 boolean check = FALSE;
1796 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1797 game_mm.num_keys == 0)
1800 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1803 int ex = ELX * TILEX + 14;
1804 int ey = ELY * TILEY + 14;
1808 for (i = 1; i < 32; i++)
1813 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1818 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1819 return HitAbsorbingWalls(element, hit_mask);
1823 AddLaserEdge(LX - XS, LY - YS);
1824 AddDamagedField(ELX, ELY);
1827 Box[ELX][ELY] = laser.num_edges;
1829 return HitReflectingWalls(element, hit_mask);
1832 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1834 int xs = XS / 2, ys = YS / 2;
1835 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1836 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1838 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1839 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1841 laser.overloaded = (element == EL_GATE_STONE);
1846 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1847 (hit_mask == HIT_MASK_TOP ||
1848 hit_mask == HIT_MASK_LEFT ||
1849 hit_mask == HIT_MASK_RIGHT ||
1850 hit_mask == HIT_MASK_BOTTOM))
1851 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1852 hit_mask == HIT_MASK_BOTTOM),
1853 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1854 hit_mask == HIT_MASK_RIGHT));
1855 AddLaserEdge(LX, LY);
1861 if (element == EL_GATE_STONE && Box[ELX][ELY])
1863 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1875 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1877 int xs = XS / 2, ys = YS / 2;
1878 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1879 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1881 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1882 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1884 laser.overloaded = (element == EL_BLOCK_STONE);
1889 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1890 (hit_mask == HIT_MASK_TOP ||
1891 hit_mask == HIT_MASK_LEFT ||
1892 hit_mask == HIT_MASK_RIGHT ||
1893 hit_mask == HIT_MASK_BOTTOM))
1894 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1895 hit_mask == HIT_MASK_BOTTOM),
1896 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1897 hit_mask == HIT_MASK_RIGHT));
1898 AddDamagedField(ELX, ELY);
1900 LX = ELX * TILEX + 14;
1901 LY = ELY * TILEY + 14;
1903 AddLaserEdge(LX, LY);
1905 laser.stops_inside_element = TRUE;
1913 boolean HitLaserSource(int element, int hit_mask)
1915 if (HitOnlyAnEdge(element, hit_mask))
1918 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1920 laser.overloaded = TRUE;
1925 boolean HitLaserDestination(int element, int hit_mask)
1927 if (HitOnlyAnEdge(element, hit_mask))
1930 if (element != EL_EXIT_OPEN &&
1931 !(IS_RECEIVER(element) &&
1932 game_mm.kettles_still_needed == 0 &&
1933 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1935 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1940 if (IS_RECEIVER(element) ||
1941 (IS_22_5_ANGLE(laser.current_angle) &&
1942 (ELX != (LX + 6 * XS) / TILEX ||
1943 ELY != (LY + 6 * YS) / TILEY ||
1952 LX = ELX * TILEX + 14;
1953 LY = ELY * TILEY + 14;
1955 laser.stops_inside_element = TRUE;
1958 AddLaserEdge(LX, LY);
1959 AddDamagedField(ELX, ELY);
1961 if (game_mm.lights_still_needed == 0)
1962 game_mm.level_solved = TRUE;
1967 boolean HitReflectingWalls(int element, int hit_mask)
1969 /* check if laser hits side of a wall with an angle that is not 90° */
1970 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1971 hit_mask == HIT_MASK_LEFT ||
1972 hit_mask == HIT_MASK_RIGHT ||
1973 hit_mask == HIT_MASK_BOTTOM))
1975 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1980 if (!IS_DF_GRID(element))
1981 AddLaserEdge(LX, LY);
1983 /* check if laser hits wall with an angle of 45° */
1984 if (!IS_22_5_ANGLE(laser.current_angle))
1986 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1989 laser.current_angle = get_mirrored_angle(laser.current_angle,
1992 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1995 laser.current_angle = get_mirrored_angle(laser.current_angle,
1999 AddLaserEdge(LX, LY);
2001 XS = 2 * Step[laser.current_angle].x;
2002 YS = 2 * Step[laser.current_angle].y;
2006 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
2008 laser.current_angle = get_mirrored_angle(laser.current_angle,
2013 if (!IS_DF_GRID(element))
2014 AddLaserEdge(LX, LY);
2019 if (!IS_DF_GRID(element))
2020 AddLaserEdge(LX, LY + YS / 2);
2023 if (!IS_DF_GRID(element))
2024 AddLaserEdge(LX, LY);
2027 YS = 2 * Step[laser.current_angle].y;
2031 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
2033 laser.current_angle = get_mirrored_angle(laser.current_angle,
2038 if (!IS_DF_GRID(element))
2039 AddLaserEdge(LX, LY);
2044 if (!IS_DF_GRID(element))
2045 AddLaserEdge(LX + XS / 2, LY);
2048 if (!IS_DF_GRID(element))
2049 AddLaserEdge(LX, LY);
2052 XS = 2 * Step[laser.current_angle].x;
2058 /* reflection at the edge of reflecting DF style wall */
2059 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
2061 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
2062 hit_mask == HIT_MASK_TOPRIGHT) ||
2063 ((laser.current_angle == 5 || laser.current_angle == 7) &&
2064 hit_mask == HIT_MASK_TOPLEFT) ||
2065 ((laser.current_angle == 9 || laser.current_angle == 11) &&
2066 hit_mask == HIT_MASK_BOTTOMLEFT) ||
2067 ((laser.current_angle == 13 || laser.current_angle == 15) &&
2068 hit_mask == HIT_MASK_BOTTOMRIGHT))
2071 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
2072 ANG_MIRROR_135 : ANG_MIRROR_45);
2074 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2076 AddDamagedField(ELX, ELY);
2077 AddLaserEdge(LX, LY);
2079 laser.current_angle = get_mirrored_angle(laser.current_angle,
2087 AddLaserEdge(LX, LY);
2093 /* reflection inside an edge of reflecting DF style wall */
2094 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
2096 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
2097 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
2098 ((laser.current_angle == 5 || laser.current_angle == 7) &&
2099 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
2100 ((laser.current_angle == 9 || laser.current_angle == 11) &&
2101 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
2102 ((laser.current_angle == 13 || laser.current_angle == 15) &&
2103 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
2106 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
2107 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
2108 ANG_MIRROR_135 : ANG_MIRROR_45);
2110 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2113 AddDamagedField(ELX, ELY);
2116 AddLaserEdge(LX - XS, LY - YS);
2117 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
2118 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
2120 laser.current_angle = get_mirrored_angle(laser.current_angle,
2128 AddLaserEdge(LX, LY);
2134 /* check if laser hits DF style wall with an angle of 90° */
2135 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
2137 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
2138 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
2139 (IS_VERT_ANGLE(laser.current_angle) &&
2140 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
2142 /* laser at last step touched nothing or the same side of the wall */
2143 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
2145 AddDamagedField(ELX, ELY);
2152 last_hit_mask = hit_mask;
2159 if (!HitOnlyAnEdge(element, hit_mask))
2161 laser.overloaded = TRUE;
2169 boolean HitAbsorbingWalls(int element, int hit_mask)
2171 if (HitOnlyAnEdge(element, hit_mask))
2175 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
2177 AddLaserEdge(LX - XS, LY - YS);
2184 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
2186 AddLaserEdge(LX - XS, LY - YS);
2192 if (IS_WALL_WOOD(element) ||
2193 IS_DF_WALL_WOOD(element) ||
2194 IS_GRID_WOOD(element) ||
2195 IS_GRID_WOOD_FIXED(element) ||
2196 IS_GRID_WOOD_AUTO(element) ||
2197 element == EL_FUSE_ON ||
2198 element == EL_BLOCK_WOOD ||
2199 element == EL_GATE_WOOD)
2201 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2206 if (IS_WALL_ICE(element))
2210 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
2211 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
2213 /* check if laser hits wall with an angle of 90° */
2214 if (IS_90_ANGLE(laser.current_angle))
2215 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2217 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
2221 for (i = 0; i < 4; i++)
2223 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
2224 mask = 15 - (8 >> i);
2225 else if (ABS(XS) == 4 &&
2227 (XS > 0) == (i % 2) &&
2228 (YS < 0) == (i / 2))
2229 mask = 3 + (i / 2) * 9;
2230 else if (ABS(YS) == 4 &&
2232 (XS < 0) == (i % 2) &&
2233 (YS > 0) == (i / 2))
2234 mask = 5 + (i % 2) * 5;
2238 laser.wall_mask = mask;
2240 else if (IS_WALL_AMOEBA(element))
2242 int elx = (LX - 2 * XS) / TILEX;
2243 int ely = (LY - 2 * YS) / TILEY;
2244 int element2 = Feld[elx][ely];
2247 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
2249 laser.dest_element = EL_EMPTY;
2257 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
2258 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
2260 if (IS_90_ANGLE(laser.current_angle))
2261 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2263 laser.dest_element = element2 | EL_WALL_AMOEBA;
2265 laser.wall_mask = mask;
2271 void OpenExit(int x, int y)
2275 if (!MovDelay[x][y]) /* next animation frame */
2276 MovDelay[x][y] = 4 * delay;
2278 if (MovDelay[x][y]) /* wait some time before next frame */
2283 phase = MovDelay[x][y] / delay;
2285 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2286 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2288 if (!MovDelay[x][y])
2290 Feld[x][y] = EL_EXIT_OPEN;
2296 void OpenSurpriseBall(int x, int y)
2300 if (!MovDelay[x][y]) /* next animation frame */
2301 MovDelay[x][y] = 50 * delay;
2303 if (MovDelay[x][y]) /* wait some time before next frame */
2307 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2310 int graphic = el2gfx(Store[x][y]);
2312 int dx = RND(26), dy = RND(26);
2314 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2316 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2317 SX + x * TILEX + dx, SY + y * TILEY + dy);
2319 MarkTileDirty(x, y);
2322 if (!MovDelay[x][y])
2324 Feld[x][y] = Store[x][y];
2333 void MeltIce(int x, int y)
2338 if (!MovDelay[x][y]) /* next animation frame */
2339 MovDelay[x][y] = frames * delay;
2341 if (MovDelay[x][y]) /* wait some time before next frame */
2344 int wall_mask = Store2[x][y];
2345 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2348 phase = frames - MovDelay[x][y] / delay - 1;
2350 if (!MovDelay[x][y])
2354 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2355 Store[x][y] = Store2[x][y] = 0;
2357 DrawWalls_MM(x, y, Feld[x][y]);
2359 if (Feld[x][y] == EL_WALL_ICE)
2360 Feld[x][y] = EL_EMPTY;
2362 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2363 if (laser.damage[i].is_mirror)
2367 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2369 DrawLaser(0, DL_LASER_DISABLED);
2373 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2375 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2377 laser.redraw = TRUE;
2382 void GrowAmoeba(int x, int y)
2387 if (!MovDelay[x][y]) /* next animation frame */
2388 MovDelay[x][y] = frames * delay;
2390 if (MovDelay[x][y]) /* wait some time before next frame */
2393 int wall_mask = Store2[x][y];
2394 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2397 phase = MovDelay[x][y] / delay;
2399 if (!MovDelay[x][y])
2401 Feld[x][y] = real_element;
2402 Store[x][y] = Store2[x][y] = 0;
2404 DrawWalls_MM(x, y, Feld[x][y]);
2405 DrawLaser(0, DL_LASER_ENABLED);
2407 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2409 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2414 static void Explode_MM(int x, int y, int phase, int mode)
2416 int num_phase = 9, delay = 2;
2417 int last_phase = num_phase * delay;
2418 int half_phase = (num_phase / 2) * delay;
2420 laser.redraw = TRUE;
2422 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2424 int center_element = Feld[x][y];
2426 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2428 /* put moving element to center field (and let it explode there) */
2429 center_element = MovingOrBlocked2Element_MM(x, y);
2430 RemoveMovingField_MM(x, y);
2432 Feld[x][y] = center_element;
2435 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2436 Store[x][y] = center_element;
2438 Store[x][y] = EL_EMPTY;
2440 Store2[x][y] = mode;
2441 Feld[x][y] = EL_EXPLODING_OPAQUE;
2442 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2448 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2450 if (phase == half_phase)
2452 Feld[x][y] = EL_EXPLODING_TRANSP;
2454 if (x == ELX && y == ELY)
2458 if (phase == last_phase)
2460 if (Store[x][y] == EL_BOMB)
2462 DrawLaser(0, DL_LASER_DISABLED);
2465 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2466 Store[x][y] = EL_EMPTY;
2468 game_mm.game_over = TRUE;
2469 game_mm.game_over_cause = GAME_OVER_BOMB;
2471 laser.overloaded = FALSE;
2473 else if (IS_MCDUFFIN(Store[x][y]))
2475 Store[x][y] = EL_EMPTY;
2478 Feld[x][y] = Store[x][y];
2479 Store[x][y] = Store2[x][y] = 0;
2480 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2482 InitField(x, y, FALSE);
2485 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2487 int graphic = IMG_MM_DEFAULT_EXPLODING;
2488 int graphic_phase = (phase / delay - 1);
2492 if (Store2[x][y] == EX_KETTLE)
2494 if (graphic_phase < 3)
2496 graphic = IMG_MM_KETTLE_EXPLODING;
2498 else if (graphic_phase < 5)
2504 graphic = IMG_EMPTY;
2508 else if (Store2[x][y] == EX_SHORT)
2510 if (graphic_phase < 4)
2516 graphic = IMG_EMPTY;
2521 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2523 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2524 FX + x * TILEX, FY + y * TILEY);
2526 MarkTileDirty(x, y);
2530 static void Bang_MM(int x, int y)
2532 int element = Feld[x][y];
2533 int mode = EX_NORMAL;
2536 DrawLaser(0, DL_LASER_ENABLED);
2555 if (IS_PACMAN(element))
2556 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2557 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2558 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2559 else if (element == EL_KEY)
2560 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2562 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2564 Explode_MM(x, y, EX_PHASE_START, mode);
2567 void TurnRound(int x, int y)
2579 { 0, 0 }, { 0, 0 }, { 0, 0 },
2584 int left, right, back;
2588 { MV_DOWN, MV_UP, MV_RIGHT },
2589 { MV_UP, MV_DOWN, MV_LEFT },
2591 { MV_LEFT, MV_RIGHT, MV_DOWN },
2592 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2593 { MV_RIGHT, MV_LEFT, MV_UP }
2596 int element = Feld[x][y];
2597 int old_move_dir = MovDir[x][y];
2598 int right_dir = turn[old_move_dir].right;
2599 int back_dir = turn[old_move_dir].back;
2600 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2601 int right_x = x + right_dx, right_y = y + right_dy;
2603 if (element == EL_PACMAN)
2605 boolean can_turn_right = FALSE;
2607 if (IN_LEV_FIELD(right_x, right_y) &&
2608 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2609 can_turn_right = TRUE;
2612 MovDir[x][y] = right_dir;
2614 MovDir[x][y] = back_dir;
2620 static void StartMoving_MM(int x, int y)
2622 int element = Feld[x][y];
2627 if (CAN_MOVE(element))
2631 if (MovDelay[x][y]) /* wait some time before next movement */
2639 /* now make next step */
2641 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2643 if (element == EL_PACMAN &&
2644 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2645 !ObjHit(newx, newy, HIT_POS_CENTER))
2647 Store[newx][newy] = Feld[newx][newy];
2648 Feld[newx][newy] = EL_EMPTY;
2650 DrawField_MM(newx, newy);
2652 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2653 ObjHit(newx, newy, HIT_POS_CENTER))
2655 /* object was running against a wall */
2662 InitMovingField_MM(x, y, MovDir[x][y]);
2666 ContinueMoving_MM(x, y);
2669 static void ContinueMoving_MM(int x, int y)
2671 int element = Feld[x][y];
2672 int direction = MovDir[x][y];
2673 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2674 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2675 int horiz_move = (dx!=0);
2676 int newx = x + dx, newy = y + dy;
2677 int step = (horiz_move ? dx : dy) * TILEX / 8;
2679 MovPos[x][y] += step;
2681 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2683 Feld[x][y] = EL_EMPTY;
2684 Feld[newx][newy] = element;
2686 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2687 MovDelay[newx][newy] = 0;
2689 if (!CAN_MOVE(element))
2690 MovDir[newx][newy] = 0;
2693 DrawField_MM(newx, newy);
2695 Stop[newx][newy] = TRUE;
2697 if (element == EL_PACMAN)
2699 if (Store[newx][newy] == EL_BOMB)
2700 Bang_MM(newx, newy);
2702 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2703 (LX + 2 * XS) / TILEX == newx &&
2704 (LY + 2 * YS) / TILEY == newy)
2711 else /* still moving on */
2716 laser.redraw = TRUE;
2719 void ClickElement(int x, int y, int button)
2721 static unsigned int click_delay = 0;
2722 static int click_delay_value = CLICK_DELAY;
2723 static boolean new_button = TRUE;
2728 /* initialize static variables */
2730 click_delay_value = CLICK_DELAY;
2736 /* do not rotate objects hit by the laser after the game was solved */
2737 if (game_mm.level_solved && Hit[x][y])
2740 if (button == MB_RELEASED)
2743 click_delay_value = CLICK_DELAY;
2745 /* release eventually hold auto-rotating mirror */
2746 RotateMirror(x, y, MB_RELEASED);
2751 if (!FrameReached(&click_delay, click_delay_value) && !new_button)
2754 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2757 if (!IN_LEV_FIELD(x, y))
2760 if (Feld[x][y] == EL_EMPTY)
2763 element = Feld[x][y];
2765 if (IS_MIRROR(element) ||
2766 IS_BEAMER(element) ||
2767 IS_POLAR(element) ||
2768 IS_POLAR_CROSS(element) ||
2769 IS_DF_MIRROR(element) ||
2770 IS_DF_MIRROR_AUTO(element))
2772 RotateMirror(x, y, button);
2774 else if (IS_MCDUFFIN(element))
2776 if (!laser.fuse_off)
2778 DrawLaser(0, DL_LASER_DISABLED);
2785 element = get_rotated_element(element, BUTTON_ROTATION(button));
2786 laser.start_angle = get_element_angle(element);
2790 Feld[x][y] = element;
2797 if (!laser.fuse_off)
2800 else if (element == EL_FUSE_ON && laser.fuse_off)
2802 if (x != laser.fuse_x || y != laser.fuse_y)
2805 laser.fuse_off = FALSE;
2806 laser.fuse_x = laser.fuse_y = -1;
2808 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2811 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2813 laser.fuse_off = TRUE;
2816 laser.overloaded = FALSE;
2818 DrawLaser(0, DL_LASER_DISABLED);
2819 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2821 else if (element == EL_LIGHTBALL)
2825 DrawLaser(0, DL_LASER_ENABLED);
2828 click_delay_value = (new_button ? CLICK_DELAY_FIRST : CLICK_DELAY);
2832 void RotateMirror(int x, int y, int button)
2834 if (button == MB_RELEASED)
2836 /* release eventually hold auto-rotating mirror */
2843 if (IS_MIRROR(Feld[x][y]) ||
2844 IS_POLAR_CROSS(Feld[x][y]) ||
2845 IS_POLAR(Feld[x][y]) ||
2846 IS_BEAMER(Feld[x][y]) ||
2847 IS_DF_MIRROR(Feld[x][y]) ||
2848 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2849 IS_GRID_WOOD_AUTO(Feld[x][y]))
2851 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2853 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2855 if (button == MB_LEFTBUTTON)
2857 /* left mouse button only for manual adjustment, no auto-rotating;
2858 freeze mirror for until mouse button released */
2862 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2864 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2868 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2870 int edge = Hit[x][y];
2876 DrawLaser(edge - 1, DL_LASER_DISABLED);
2880 else if (ObjHit(x, y, HIT_POS_CENTER))
2882 int edge = Hit[x][y];
2886 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2890 DrawLaser(edge - 1, DL_LASER_DISABLED);
2897 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2902 if ((IS_BEAMER(Feld[x][y]) ||
2903 IS_POLAR(Feld[x][y]) ||
2904 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2908 if (IS_BEAMER(Feld[x][y]))
2911 printf("TEST (%d, %d) [%d] [%d]\n",
2913 laser.beamer_edge, laser.beamer[1].num);
2923 DrawLaser(0, DL_LASER_ENABLED);
2927 void AutoRotateMirrors()
2931 if (!FrameReached(&rotate_delay, AUTO_ROTATE_DELAY))
2934 for (x = 0; x < lev_fieldx; x++)
2936 for (y = 0; y < lev_fieldy; y++)
2938 int element = Feld[x][y];
2940 /* do not rotate objects hit by the laser after the game was solved */
2941 if (game_mm.level_solved && Hit[x][y])
2944 if (IS_DF_MIRROR_AUTO(element) ||
2945 IS_GRID_WOOD_AUTO(element) ||
2946 IS_GRID_STEEL_AUTO(element) ||
2947 element == EL_REFRACTOR)
2948 RotateMirror(x, y, MB_RIGHTBUTTON);
2953 boolean ObjHit(int obx, int oby, int bits)
2960 if (bits & HIT_POS_CENTER)
2962 if (CheckLaserPixel(SX + obx + 15,
2967 if (bits & HIT_POS_EDGE)
2969 for (i = 0; i < 4; i++)
2970 if (CheckLaserPixel(SX + obx + 31 * (i % 2),
2971 SY + oby + 31 * (i / 2)))
2975 if (bits & HIT_POS_BETWEEN)
2977 for (i = 0; i < 4; i++)
2978 if (CheckLaserPixel(SX + 4 + obx + 22 * (i % 2),
2979 SY + 4 + oby + 22 * (i / 2)))
2986 void DeletePacMan(int px, int py)
2992 if (game_mm.num_pacman <= 1)
2994 game_mm.num_pacman = 0;
2998 for (i = 0; i < game_mm.num_pacman; i++)
2999 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
3002 game_mm.num_pacman--;
3004 for (j = i; j < game_mm.num_pacman; j++)
3006 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
3007 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
3008 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
3012 void ColorCycling(void)
3014 static int CC, Cc = 0;
3016 static int color, old = 0xF00, new = 0x010, mult = 1;
3017 static unsigned short red, green, blue;
3019 if (color_status == STATIC_COLORS)
3024 if (CC < Cc || CC > Cc + 2)
3028 color = old + new * mult;
3034 if (ABS(mult) == 16)
3044 red = 0x0e00 * ((color & 0xF00) >> 8);
3045 green = 0x0e00 * ((color & 0x0F0) >> 4);
3046 blue = 0x0e00 * ((color & 0x00F));
3047 SetRGB(pen_magicolor[0], red, green, blue);
3049 red = 0x1111 * ((color & 0xF00) >> 8);
3050 green = 0x1111 * ((color & 0x0F0) >> 4);
3051 blue = 0x1111 * ((color & 0x00F));
3052 SetRGB(pen_magicolor[1], red, green, blue);
3056 static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
3063 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3066 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3068 element = Feld[x][y];
3070 if (!IS_MOVING(x, y) && CAN_MOVE(element))
3071 StartMoving_MM(x, y);
3072 else if (IS_MOVING(x, y))
3073 ContinueMoving_MM(x, y);
3074 else if (IS_EXPLODING(element))
3075 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
3076 else if (element == EL_EXIT_OPENING)
3078 else if (element == EL_GRAY_BALL_OPENING)
3079 OpenSurpriseBall(x, y);
3080 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
3082 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
3086 AutoRotateMirrors();
3089 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
3091 /* redraw after Explode_MM() ... */
3093 DrawLaser(0, DL_LASER_ENABLED);
3094 laser.redraw = FALSE;
3099 if (game_mm.num_pacman && FrameReached(&pacman_delay, PACMAN_MOVE_DELAY))
3103 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
3105 DrawLaser(0, DL_LASER_DISABLED);
3110 if (FrameReached(&energy_delay, ENERGY_DELAY))
3112 if (game_mm.energy_left > 0)
3114 game_mm.energy_left--;
3117 BlitBitmap(pix[PIX_DOOR], drawto,
3118 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3119 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3120 DX_ENERGY, DY_ENERGY);
3122 redraw_mask |= REDRAW_DOOR_1;
3124 else if (setup.time_limit && !game_mm.game_over)
3128 for (i = 15; i >= 0; i--)
3131 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
3133 pen_ray = GetPixelFromRGB(window,
3134 native_mm_level.laser_red * 0x11 * i,
3135 native_mm_level.laser_green * 0x11 * i,
3136 native_mm_level.laser_blue * 0x11 * i);
3138 DrawLaser(0, DL_LASER_ENABLED);
3143 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
3146 DrawLaser(0, DL_LASER_DISABLED);
3147 game_mm.game_over = TRUE;
3148 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
3151 if (Request("Out of magic energy ! Play it again ?",
3152 REQ_ASK | REQ_STAY_CLOSED))
3158 game_status = MAINMENU;
3167 element = laser.dest_element;
3170 if (element != Feld[ELX][ELY])
3172 printf("element == %d, Feld[ELX][ELY] == %d\n",
3173 element, Feld[ELX][ELY]);
3177 if (!laser.overloaded && laser.overload_value == 0 &&
3178 element != EL_BOMB &&
3179 element != EL_MINE &&
3180 element != EL_BALL_GRAY &&
3181 element != EL_BLOCK_STONE &&
3182 element != EL_BLOCK_WOOD &&
3183 element != EL_FUSE_ON &&
3184 element != EL_FUEL_FULL &&
3185 !IS_WALL_ICE(element) &&
3186 !IS_WALL_AMOEBA(element))
3189 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
3190 (!laser.overloaded && laser.overload_value > 0)) &&
3191 FrameReached(&overload_delay, HEALTH_DELAY(laser.overloaded)))
3193 if (laser.overloaded)
3194 laser.overload_value++;
3196 laser.overload_value--;
3198 if (game_mm.cheat_no_overload)
3200 laser.overloaded = FALSE;
3201 laser.overload_value = 0;
3204 game_mm.laser_overload_value = laser.overload_value;
3206 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
3208 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
3209 int color_down = 0xFF - color_up;
3212 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
3213 (15 - (laser.overload_value / 6)) * color_scale);
3216 GetPixelFromRGB(window,
3217 (native_mm_level.laser_red ? 0xFF : color_up),
3218 (native_mm_level.laser_green ? color_down : 0x00),
3219 (native_mm_level.laser_blue ? color_down : 0x00));
3221 DrawLaser(0, DL_LASER_ENABLED);
3227 if (!laser.overloaded)
3228 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
3229 else if (setup.sound_loops)
3230 PlaySoundLoop_MM(SND_MM_GAME_HEALTH_CHARGING);
3232 PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
3234 if (laser.overloaded)
3237 BlitBitmap(pix[PIX_DOOR], drawto,
3238 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
3239 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
3240 - laser.overload_value,
3241 OVERLOAD_XSIZE, laser.overload_value,
3242 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
3243 - laser.overload_value);
3245 redraw_mask |= REDRAW_DOOR_1;
3250 BlitBitmap(pix[PIX_DOOR], drawto,
3251 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
3252 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
3253 DX_OVERLOAD, DY_OVERLOAD);
3255 redraw_mask |= REDRAW_DOOR_1;
3258 if (laser.overload_value == MAX_LASER_OVERLOAD)
3262 for (i = 15; i >= 0; i--)
3265 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
3268 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
3270 DrawLaser(0, DL_LASER_ENABLED);
3275 DrawLaser(0, DL_LASER_DISABLED);
3277 game_mm.game_over = TRUE;
3278 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3281 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3282 REQ_ASK | REQ_STAY_CLOSED))
3288 game_status = MAINMENU;
3302 if (element == EL_BOMB && CT > 75)
3304 if (game_mm.cheat_no_explosion)
3308 laser.num_damages--;
3309 DrawLaser(0, DL_LASER_DISABLED);
3310 laser.num_edges = 0;
3315 laser.dest_element = EL_EXPLODING_OPAQUE;
3319 laser.num_damages--;
3320 DrawLaser(0, DL_LASER_DISABLED);
3322 laser.num_edges = 0;
3323 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3325 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3326 REQ_ASK | REQ_STAY_CLOSED))
3332 game_status = MAINMENU;
3340 if (element == EL_FUSE_ON && CT > 25)
3342 laser.fuse_off = TRUE;
3346 DrawLaser(0, DL_LASER_DISABLED);
3347 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3350 if (element == EL_BALL_GRAY && CT > 75)
3352 static int new_elements[] =
3355 EL_MIRROR_FIXED_START,
3357 EL_POLAR_CROSS_START,
3363 int num_new_elements = sizeof(new_elements) / sizeof(int);
3364 int new_element = new_elements[RND(num_new_elements)];
3366 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3367 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3369 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3380 element = EL_MIRROR_START + RND(16);
3386 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3393 element = (rnd == 0 ? EL_FUSE_ON :
3394 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3395 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3396 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3397 EL_MIRROR_FIXED_START + rnd - 25);
3402 graphic = el2gfx(element);
3404 for (i = 0; i < 50; i++)
3410 BlitBitmap(pix[PIX_BACK], drawto,
3411 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3412 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3413 SX + ELX * TILEX + x,
3414 SY + ELY * TILEY + y);
3416 MarkTileDirty(ELX, ELY);
3419 DrawLaser(0, DL_LASER_ENABLED);
3424 Feld[ELX][ELY] = element;
3425 DrawField_MM(ELX, ELY);
3428 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3431 /* above stuff: GRAY BALL -> PRISM !!! */
3433 LX = ELX * TILEX + 14;
3434 LY = ELY * TILEY + 14;
3435 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3442 laser.num_edges -= 2;
3443 laser.num_damages--;
3447 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3448 if (laser.damage[i].is_mirror)
3452 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3454 DrawLaser(0, DL_LASER_DISABLED);
3456 DrawLaser(0, DL_LASER_DISABLED);
3462 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3469 if (IS_WALL_ICE(element) && CT > 50)
3471 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
3474 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3475 Store[ELX][ELY] = EL_WALL_ICE;
3476 Store2[ELX][ELY] = laser.wall_mask;
3478 laser.dest_element = Feld[ELX][ELY];
3483 for (i = 0; i < 5; i++)
3489 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3493 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3498 if (Feld[ELX][ELY] == EL_WALL_ICE)
3499 Feld[ELX][ELY] = EL_EMPTY;
3503 LX = laser.edge[laser.num_edges].x - (SX + 2);
3504 LY = laser.edge[laser.num_edges].y - (SY + 2);
3507 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3508 if (laser.damage[i].is_mirror)
3512 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3514 DrawLaser(0, DL_LASER_DISABLED);
3521 if (IS_WALL_AMOEBA(element) && CT > 60)
3523 int k1, k2, k3, dx, dy, de, dm;
3524 int element2 = Feld[ELX][ELY];
3526 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3529 for (i = laser.num_damages - 1; i >= 0; i--)
3530 if (laser.damage[i].is_mirror)
3533 r = laser.num_edges;
3534 d = laser.num_damages;
3541 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3544 DrawLaser(0, DL_LASER_ENABLED);
3547 x = laser.damage[k1].x;
3548 y = laser.damage[k1].y;
3553 for (i = 0; i < 4; i++)
3555 if (laser.wall_mask & (1 << i))
3557 if (CheckLaserPixel(SX + ELX * TILEX + 14 + (i % 2) * 2,
3558 SY + ELY * TILEY + 31 * (i / 2)))
3561 if (CheckLaserPixel(SX + ELX * TILEX + 31 * (i % 2),
3562 SY + ELY * TILEY + 14 + (i / 2) * 2))
3569 for (i = 0; i < 4; i++)
3571 if (laser.wall_mask & (1 << i))
3573 if (CheckLaserPixel(SX + ELX * TILEX + 31 * (i % 2),
3574 SY + ELY * TILEY + 31 * (i / 2)))
3581 if (laser.num_beamers > 0 ||
3582 k1 < 1 || k2 < 4 || k3 < 4 ||
3583 CheckLaserPixel(SX + ELX * TILEX + 14,
3584 SY + ELY * TILEY + 14))
3586 laser.num_edges = r;
3587 laser.num_damages = d;
3589 DrawLaser(0, DL_LASER_DISABLED);
3592 Feld[ELX][ELY] = element | laser.wall_mask;
3596 de = Feld[ELX][ELY];
3597 dm = laser.wall_mask;
3601 int x = ELX, y = ELY;
3602 int wall_mask = laser.wall_mask;
3605 DrawLaser(0, DL_LASER_ENABLED);
3607 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3609 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3610 Store[x][y] = EL_WALL_AMOEBA;
3611 Store2[x][y] = wall_mask;
3617 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3619 DrawLaser(0, DL_LASER_ENABLED);
3621 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3623 for (i = 4; i >= 0; i--)
3625 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3631 DrawLaser(0, DL_LASER_ENABLED);
3636 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3637 laser.stops_inside_element && CT > 75)
3642 if (ABS(XS) > ABS(YS))
3649 for (i = 0; i < 4; i++)
3656 x = ELX + Step[k * 4].x;
3657 y = ELY + Step[k * 4].y;
3659 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3662 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3670 laser.overloaded = (element == EL_BLOCK_STONE);
3675 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
3678 Feld[x][y] = element;
3680 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3683 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3685 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3686 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3694 if (element == EL_FUEL_FULL && CT > 10)
3696 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3699 BlitBitmap(pix[PIX_DOOR], drawto,
3700 DOOR_GFX_PAGEX4 + XX_ENERGY,
3701 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3702 ENERGY_XSIZE, i, DX_ENERGY,
3703 DY_ENERGY + ENERGY_YSIZE - i);
3706 redraw_mask |= REDRAW_DOOR_1;
3712 game_mm.energy_left = MAX_LASER_ENERGY;
3713 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3714 DrawField_MM(ELX, ELY);
3716 DrawLaser(0, DL_LASER_ENABLED);
3724 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3726 ClickElement(action.lx, action.ly, action.button);
3728 GameActions_MM_Ext(action, warp_mode);
3733 int mx, my, ox, oy, nx, ny;
3737 if (++pacman_nr >= game_mm.num_pacman)
3740 game_mm.pacman[pacman_nr].dir--;
3742 for (l = 1; l < 5; l++)
3744 game_mm.pacman[pacman_nr].dir++;
3746 if (game_mm.pacman[pacman_nr].dir > 4)
3747 game_mm.pacman[pacman_nr].dir = 1;
3749 if (game_mm.pacman[pacman_nr].dir % 2)
3752 my = game_mm.pacman[pacman_nr].dir - 2;
3757 mx = 3 - game_mm.pacman[pacman_nr].dir;
3760 ox = game_mm.pacman[pacman_nr].x;
3761 oy = game_mm.pacman[pacman_nr].y;
3764 element = Feld[nx][ny];
3766 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3769 if (!IS_EATABLE4PACMAN(element))
3772 if (ObjHit(nx, ny, HIT_POS_CENTER))
3775 Feld[ox][oy] = EL_EMPTY;
3777 EL_PACMAN_RIGHT - 1 +
3778 (game_mm.pacman[pacman_nr].dir - 1 +
3779 (game_mm.pacman[pacman_nr].dir % 2) * 2);
3781 game_mm.pacman[pacman_nr].x = nx;
3782 game_mm.pacman[pacman_nr].y = ny;
3784 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3786 if (element != EL_EMPTY)
3788 int graphic = el2gfx(Feld[nx][ny]);
3793 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3796 ox = SX + ox * TILEX;
3797 oy = SY + oy * TILEY;
3799 for (i = 1; i < 33; i += 2)
3800 BlitBitmap(bitmap, window,
3801 src_x, src_y, TILEX, TILEY,
3802 ox + i * mx, oy + i * my);
3803 Ct = Ct + FrameCounter - CT;
3806 DrawField_MM(nx, ny);
3809 if (!laser.fuse_off)
3811 DrawLaser(0, DL_LASER_ENABLED);
3813 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3815 AddDamagedField(nx, ny);
3817 laser.damage[laser.num_damages - 1].edge = 0;
3821 if (element == EL_BOMB)
3822 DeletePacMan(nx, ny);
3824 if (IS_WALL_AMOEBA(element) &&
3825 (LX + 2 * XS) / TILEX == nx &&
3826 (LY + 2 * YS) / TILEY == ny)
3839 boolean raise_level = FALSE;
3842 if (local_player->MovPos)
3845 local_player->LevelSolved = FALSE;
3848 if (game_mm.energy_left)
3850 if (setup.sound_loops)
3851 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3852 SND_CTRL_PLAY_LOOP);
3854 while (game_mm.energy_left > 0)
3856 if (!setup.sound_loops)
3857 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3860 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3861 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3866 game_mm.energy_left--;
3867 if (game_mm.energy_left >= 0)
3870 BlitBitmap(pix[PIX_DOOR], drawto,
3871 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3872 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3873 DX_ENERGY, DY_ENERGY);
3875 redraw_mask |= REDRAW_DOOR_1;
3882 if (setup.sound_loops)
3883 StopSound(SND_SIRR);
3885 else if (native_mm_level.time == 0) /* level without time limit */
3887 if (setup.sound_loops)
3888 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3889 SND_CTRL_PLAY_LOOP);
3891 while (TimePlayed < 999)
3893 if (!setup.sound_loops)
3894 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3895 if (TimePlayed < 999 && !(TimePlayed % 10))
3896 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3897 if (TimePlayed < 900 && !(TimePlayed % 10))
3903 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3910 if (setup.sound_loops)
3911 StopSound(SND_SIRR);
3918 CloseDoor(DOOR_CLOSE_1);
3920 Request("Level solved !", REQ_CONFIRM);
3922 if (level_nr == leveldir_current->handicap_level)
3924 leveldir_current->handicap_level++;
3925 SaveLevelSetup_SeriesInfo();
3928 if (level_editor_test_game)
3929 game_mm.score = -1; /* no highscore when playing from editor */
3930 else if (level_nr < leveldir_current->last_level)
3931 raise_level = TRUE; /* advance to next level */
3933 if ((hi_pos = NewHiScore_MM()) >= 0)
3935 game_status = HALLOFFAME;
3937 // DrawHallOfFame(hi_pos);
3944 game_status = MAINMENU;
3960 // LoadScore(level_nr);
3962 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3963 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3966 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3968 if (game_mm.score > highscore[k].Score)
3970 /* player has made it to the hall of fame */
3972 if (k < MAX_SCORE_ENTRIES - 1)
3974 int m = MAX_SCORE_ENTRIES - 1;
3977 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3978 if (!strcmp(setup.player_name, highscore[l].Name))
3980 if (m == k) /* player's new highscore overwrites his old one */
3984 for (l = m; l>k; l--)
3986 strcpy(highscore[l].Name, highscore[l - 1].Name);
3987 highscore[l].Score = highscore[l - 1].Score;
3994 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3995 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3996 highscore[k].Score = game_mm.score;
4003 else if (!strncmp(setup.player_name, highscore[k].Name,
4004 MAX_PLAYER_NAME_LEN))
4005 break; /* player already there with a higher score */
4010 // if (position >= 0)
4011 // SaveScore(level_nr);
4016 static void InitMovingField_MM(int x, int y, int direction)
4018 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4019 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4021 MovDir[x][y] = direction;
4022 MovDir[newx][newy] = direction;
4024 if (Feld[newx][newy] == EL_EMPTY)
4025 Feld[newx][newy] = EL_BLOCKED;
4028 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
4030 int direction = MovDir[x][y];
4031 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4032 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4038 static void Blocked2Moving_MM(int x, int y,
4039 int *comes_from_x, int *comes_from_y)
4041 int oldx = x, oldy = y;
4042 int direction = MovDir[x][y];
4044 if (direction == MV_LEFT)
4046 else if (direction == MV_RIGHT)
4048 else if (direction == MV_UP)
4050 else if (direction == MV_DOWN)
4053 *comes_from_x = oldx;
4054 *comes_from_y = oldy;
4057 static int MovingOrBlocked2Element_MM(int x, int y)
4059 int element = Feld[x][y];
4061 if (element == EL_BLOCKED)
4065 Blocked2Moving_MM(x, y, &oldx, &oldy);
4067 return Feld[oldx][oldy];
4074 static void RemoveField(int x, int y)
4076 Feld[x][y] = EL_EMPTY;
4083 static void RemoveMovingField_MM(int x, int y)
4085 int oldx = x, oldy = y, newx = x, newy = y;
4087 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
4090 if (IS_MOVING(x, y))
4092 Moving2Blocked_MM(x, y, &newx, &newy);
4093 if (Feld[newx][newy] != EL_BLOCKED)
4096 else if (Feld[x][y] == EL_BLOCKED)
4098 Blocked2Moving_MM(x, y, &oldx, &oldy);
4099 if (!IS_MOVING(oldx, oldy))
4103 Feld[oldx][oldy] = EL_EMPTY;
4104 Feld[newx][newy] = EL_EMPTY;
4105 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
4106 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
4108 DrawLevelField_MM(oldx, oldy);
4109 DrawLevelField_MM(newx, newy);
4112 void PlaySoundLevel(int x, int y, int sound_nr)
4114 int sx = SCREENX(x), sy = SCREENY(y);
4116 int silence_distance = 8;
4118 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
4119 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
4122 if (!IN_LEV_FIELD(x, y) ||
4123 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
4124 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
4127 volume = SOUND_MAX_VOLUME;
4130 stereo = (sx - SCR_FIELDX/2) * 12;
4132 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
4133 if (stereo > SOUND_MAX_RIGHT)
4134 stereo = SOUND_MAX_RIGHT;
4135 if (stereo < SOUND_MAX_LEFT)
4136 stereo = SOUND_MAX_LEFT;
4139 if (!IN_SCR_FIELD(sx, sy))
4141 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
4142 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
4144 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
4147 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
4150 static void RaiseScore_MM(int value)
4152 game_mm.score += value;
4155 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
4160 void RaiseScoreElement_MM(int element)
4165 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
4169 RaiseScore_MM(native_mm_level.score[SC_KEY]);